]>
Commit | Line | Data |
---|---|---|
74b04a01 XL |
1 | //! Detecting language items. |
2 | //! | |
3 | //! Language items are items that represent concepts intrinsic to the language | |
4 | //! itself. Examples are: | |
5 | //! | |
6 | //! * Traits that specify "kinds"; e.g., `Sync`, `Send`. | |
7 | //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. | |
8 | //! * Functions called by the compiler itself. | |
9 | ||
f035d41b | 10 | use crate::check_attr::target_from_impl_item; |
74b04a01 XL |
11 | use crate::weak_lang_items; |
12 | ||
94222f64 | 13 | use rustc_ast::Attribute; |
17df50a5 | 14 | use rustc_errors::{pluralize, struct_span_err}; |
74b04a01 | 15 | use rustc_hir as hir; |
17df50a5 | 16 | use rustc_hir::def_id::DefId; |
74b04a01 | 17 | use rustc_hir::itemlikevisit::ItemLikeVisitor; |
94222f64 | 18 | use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS}; |
f035d41b | 19 | use rustc_hir::{HirId, LangItem, LanguageItems, Target}; |
c295e0f8 XL |
20 | use rustc_middle::ty::TyCtxt; |
21 | use rustc_session::cstore::ExternCrate; | |
17df50a5 | 22 | use rustc_span::Span; |
74b04a01 | 23 | |
ba9703b0 | 24 | use rustc_middle::ty::query::Providers; |
74b04a01 XL |
25 | |
26 | struct LanguageItemCollector<'tcx> { | |
27 | items: LanguageItems, | |
28 | tcx: TyCtxt<'tcx>, | |
29 | } | |
30 | ||
a2a8927a | 31 | impl<'v, 'tcx> ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { |
74b04a01 | 32 | fn visit_item(&mut self, item: &hir::Item<'_>) { |
6a06907d | 33 | self.check_for_lang(Target::from_item(item), item.hir_id()); |
3dfed10e XL |
34 | |
35 | if let hir::ItemKind::Enum(def, ..) = &item.kind { | |
36 | for variant in def.variants { | |
6a06907d | 37 | self.check_for_lang(Target::Variant, variant.id); |
3dfed10e XL |
38 | } |
39 | } | |
f035d41b XL |
40 | } |
41 | ||
42 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { | |
6a06907d | 43 | self.check_for_lang(Target::from_trait_item(trait_item), trait_item.hir_id()) |
f035d41b XL |
44 | } |
45 | ||
46 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { | |
6a06907d | 47 | self.check_for_lang(target_from_impl_item(self.tcx, impl_item), impl_item.hir_id()) |
f035d41b | 48 | } |
fc512014 XL |
49 | |
50 | fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {} | |
f035d41b XL |
51 | } |
52 | ||
a2a8927a | 53 | impl<'tcx> LanguageItemCollector<'tcx> { |
f035d41b XL |
54 | fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> { |
55 | LanguageItemCollector { tcx, items: LanguageItems::new() } | |
56 | } | |
57 | ||
6a06907d XL |
58 | fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) { |
59 | let attrs = self.tcx.hir().attrs(hir_id); | |
94222f64 | 60 | let check_name = |attr: &Attribute, sym| attr.has_name(sym); |
3dfed10e XL |
61 | if let Some((value, span)) = extract(check_name, &attrs) { |
62 | match ITEM_REFS.get(&value).cloned() { | |
74b04a01 XL |
63 | // Known lang item with attribute on correct target. |
64 | Some((item_index, expected_target)) if actual_target == expected_target => { | |
17df50a5 | 65 | self.collect_item_extended(item_index, hir_id, span); |
74b04a01 XL |
66 | } |
67 | // Known lang item with attribute on incorrect target. | |
68 | Some((_, expected_target)) => { | |
69 | struct_span_err!( | |
70 | self.tcx.sess, | |
71 | span, | |
72 | E0718, | |
73 | "`{}` language item must be applied to a {}", | |
74 | value, | |
75 | expected_target, | |
76 | ) | |
77 | .span_label( | |
78 | span, | |
79 | format!( | |
80 | "attribute should be applied to a {}, not a {}", | |
81 | expected_target, actual_target, | |
82 | ), | |
83 | ) | |
84 | .emit(); | |
85 | } | |
86 | // Unknown lang item. | |
87 | _ => { | |
88 | struct_span_err!( | |
89 | self.tcx.sess, | |
90 | span, | |
91 | E0522, | |
92 | "definition of an unknown language item: `{}`", | |
93 | value | |
94 | ) | |
95 | .span_label(span, format!("definition of unknown language item `{}`", value)) | |
96 | .emit(); | |
97 | } | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
74b04a01 XL |
102 | fn collect_item(&mut self, item_index: usize, item_def_id: DefId) { |
103 | // Check for duplicates. | |
104 | if let Some(original_def_id) = self.items.items[item_index] { | |
105 | if original_def_id != item_def_id { | |
f035d41b XL |
106 | let lang_item = LangItem::from_u32(item_index as u32).unwrap(); |
107 | let name = lang_item.name(); | |
74b04a01 XL |
108 | let mut err = match self.tcx.hir().span_if_local(item_def_id) { |
109 | Some(span) => struct_span_err!( | |
110 | self.tcx.sess, | |
111 | span, | |
112 | E0152, | |
113 | "found duplicate lang item `{}`", | |
114 | name | |
115 | ), | |
116 | None => match self.tcx.extern_crate(item_def_id) { | |
117 | Some(ExternCrate { dependency_of, .. }) => { | |
118 | self.tcx.sess.struct_err(&format!( | |
119 | "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.", | |
120 | self.tcx.crate_name(item_def_id.krate), | |
121 | self.tcx.crate_name(*dependency_of), | |
122 | name | |
123 | )) | |
124 | } | |
125 | _ => self.tcx.sess.struct_err(&format!( | |
126 | "duplicate lang item in crate `{}`: `{}`.", | |
127 | self.tcx.crate_name(item_def_id.krate), | |
128 | name | |
129 | )), | |
130 | }, | |
131 | }; | |
132 | if let Some(span) = self.tcx.hir().span_if_local(original_def_id) { | |
133 | err.span_note(span, "the lang item is first defined here"); | |
134 | } else { | |
135 | match self.tcx.extern_crate(original_def_id) { | |
136 | Some(ExternCrate { dependency_of, .. }) => { | |
137 | err.note(&format!( | |
138 | "the lang item is first defined in crate `{}` (which `{}` depends on)", | |
139 | self.tcx.crate_name(original_def_id.krate), | |
140 | self.tcx.crate_name(*dependency_of) | |
141 | )); | |
142 | } | |
143 | _ => { | |
144 | err.note(&format!( | |
145 | "the lang item is first defined in crate `{}`.", | |
146 | self.tcx.crate_name(original_def_id.krate) | |
147 | )); | |
148 | } | |
149 | } | |
f035d41b XL |
150 | let mut note_def = |which, def_id: DefId| { |
151 | let crate_name = self.tcx.crate_name(def_id.krate); | |
152 | let note = if def_id.is_local() { | |
153 | format!("{} definition in the local crate (`{}`)", which, crate_name) | |
154 | } else { | |
155 | let paths: Vec<_> = self | |
156 | .tcx | |
157 | .crate_extern_paths(def_id.krate) | |
158 | .iter() | |
159 | .map(|p| p.display().to_string()) | |
160 | .collect(); | |
161 | format!( | |
162 | "{} definition in `{}` loaded from {}", | |
163 | which, | |
164 | crate_name, | |
165 | paths.join(", ") | |
166 | ) | |
167 | }; | |
168 | err.note(¬e); | |
169 | }; | |
170 | note_def("first", original_def_id); | |
171 | note_def("second", item_def_id); | |
74b04a01 XL |
172 | } |
173 | err.emit(); | |
174 | } | |
175 | } | |
176 | ||
177 | // Matched. | |
178 | self.items.items[item_index] = Some(item_def_id); | |
f035d41b XL |
179 | if let Some(group) = LangItem::from_u32(item_index as u32).unwrap().group() { |
180 | self.items.groups[group as usize].push(item_def_id); | |
181 | } | |
74b04a01 | 182 | } |
17df50a5 XL |
183 | |
184 | // Like collect_item() above, but also checks whether the lang item is declared | |
94222f64 | 185 | // with the right number of generic arguments. |
17df50a5 XL |
186 | fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) { |
187 | let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); | |
188 | let lang_item = LangItem::from_u32(item_index as u32).unwrap(); | |
189 | let name = lang_item.name(); | |
190 | ||
17df50a5 | 191 | // Now check whether the lang_item has the expected number of generic |
94222f64 XL |
192 | // arguments. Generally speaking, binary and indexing operations have |
193 | // one (for the RHS/index), unary operations have none, the closure | |
194 | // traits have one for the argument list, generators have one for the | |
195 | // resume argument, and ordering/equality relations have one for the RHS | |
196 | // Some other types like Box and various functions like drop_in_place | |
197 | // have minimum requirements. | |
17df50a5 | 198 | |
94222f64 XL |
199 | if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) = self.tcx.hir().get(hir_id) |
200 | { | |
201 | let (actual_num, generics_span) = match kind.generics() { | |
202 | Some(generics) => (generics.params.len(), generics.span), | |
203 | None => (0, *item_span), | |
204 | }; | |
17df50a5 | 205 | |
94222f64 XL |
206 | let required = match lang_item.required_generics() { |
207 | GenericRequirement::Exact(num) if num != actual_num => { | |
208 | Some((format!("{}", num), pluralize!(num))) | |
209 | } | |
210 | GenericRequirement::Minimum(num) if actual_num < num => { | |
211 | Some((format!("at least {}", num), pluralize!(num))) | |
212 | } | |
213 | // If the number matches, or there is no requirement, handle it normally | |
214 | _ => None, | |
17df50a5 XL |
215 | }; |
216 | ||
94222f64 | 217 | if let Some((range_str, pluralized)) = required { |
17df50a5 XL |
218 | // We are issuing E0718 "incorrect target" here, because while the |
219 | // item kind of the target is correct, the target is still wrong | |
220 | // because of the wrong number of generic arguments. | |
221 | struct_span_err!( | |
222 | self.tcx.sess, | |
223 | span, | |
224 | E0718, | |
94222f64 | 225 | "`{}` language item must be applied to a {} with {} generic argument{}", |
17df50a5 | 226 | name, |
94222f64 XL |
227 | kind.descr(), |
228 | range_str, | |
229 | pluralized, | |
17df50a5 XL |
230 | ) |
231 | .span_label( | |
232 | generics_span, | |
233 | format!( | |
94222f64 XL |
234 | "this {} has {} generic argument{}", |
235 | kind.descr(), | |
17df50a5 XL |
236 | actual_num, |
237 | pluralize!(actual_num), | |
17df50a5 XL |
238 | ), |
239 | ) | |
240 | .emit(); | |
94222f64 XL |
241 | |
242 | // return early to not collect the lang item | |
243 | return; | |
17df50a5 XL |
244 | } |
245 | } | |
94222f64 XL |
246 | |
247 | self.collect_item(item_index, item_def_id); | |
17df50a5 | 248 | } |
74b04a01 XL |
249 | } |
250 | ||
251 | /// Traverses and collects all the lang items in all crates. | |
17df50a5 | 252 | fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { |
74b04a01 XL |
253 | // Initialize the collector. |
254 | let mut collector = LanguageItemCollector::new(tcx); | |
255 | ||
256 | // Collect lang items in other crates. | |
136023e0 | 257 | for &cnum in tcx.crates(()).iter() { |
74b04a01 XL |
258 | for &(def_id, item_index) in tcx.defined_lang_items(cnum).iter() { |
259 | collector.collect_item(item_index, def_id); | |
260 | } | |
261 | } | |
262 | ||
263 | // Collect lang items in this crate. | |
c295e0f8 | 264 | tcx.hir().visit_all_item_likes(&mut collector); |
74b04a01 XL |
265 | |
266 | // Extract out the found lang items. | |
267 | let LanguageItemCollector { mut items, .. } = collector; | |
268 | ||
269 | // Find all required but not-yet-defined lang items. | |
270 | weak_lang_items::check_crate(tcx, &mut items); | |
271 | ||
272 | items | |
273 | } | |
274 | ||
f035d41b | 275 | pub fn provide(providers: &mut Providers) { |
17df50a5 | 276 | providers.get_lang_items = get_lang_items; |
74b04a01 | 277 | } |