]>
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 | ||
ba9703b0 XL |
13 | use rustc_middle::middle::cstore::ExternCrate; |
14 | use rustc_middle::ty::TyCtxt; | |
74b04a01 | 15 | |
17df50a5 | 16 | use rustc_errors::{pluralize, struct_span_err}; |
74b04a01 | 17 | use rustc_hir as hir; |
17df50a5 | 18 | use rustc_hir::def_id::DefId; |
74b04a01 XL |
19 | use rustc_hir::itemlikevisit::ItemLikeVisitor; |
20 | use rustc_hir::lang_items::{extract, ITEM_REFS}; | |
f035d41b | 21 | use rustc_hir::{HirId, LangItem, LanguageItems, Target}; |
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 | ||
31 | impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { | |
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 | ||
53 | impl LanguageItemCollector<'tcx> { | |
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); | |
3dfed10e XL |
60 | let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym); |
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 | |
185 | // with the right number of generic arguments if it is a trait. | |
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 | ||
191 | self.collect_item(item_index, item_def_id); | |
192 | ||
193 | // Now check whether the lang_item has the expected number of generic | |
194 | // arguments if it is a trait. Generally speaking, binary and indexing | |
195 | // operations have one (for the RHS/index), unary operations have none, | |
196 | // and the rest also have none except for the closure traits (one for | |
197 | // the argument list), generators (one for the resume argument), | |
198 | // ordering/equality relations (one for the RHS), and various conversion | |
199 | // traits. | |
200 | ||
201 | let expected_num = match lang_item { | |
202 | // Binary operations | |
203 | LangItem::Add | |
204 | | LangItem::Sub | |
205 | | LangItem::Mul | |
206 | | LangItem::Div | |
207 | | LangItem::Rem | |
208 | | LangItem::BitXor | |
209 | | LangItem::BitAnd | |
210 | | LangItem::BitOr | |
211 | | LangItem::Shl | |
212 | | LangItem::Shr | |
213 | | LangItem::AddAssign | |
214 | | LangItem::SubAssign | |
215 | | LangItem::MulAssign | |
216 | | LangItem::DivAssign | |
217 | | LangItem::RemAssign | |
218 | | LangItem::BitXorAssign | |
219 | | LangItem::BitAndAssign | |
220 | | LangItem::BitOrAssign | |
221 | | LangItem::ShlAssign | |
222 | | LangItem::ShrAssign | |
223 | | LangItem::Index | |
224 | | LangItem::IndexMut | |
225 | ||
226 | // Miscellaneous | |
227 | | LangItem::Unsize | |
228 | | LangItem::CoerceUnsized | |
229 | | LangItem::DispatchFromDyn | |
230 | | LangItem::Fn | |
231 | | LangItem::FnMut | |
232 | | LangItem::FnOnce | |
233 | | LangItem::Generator | |
234 | | LangItem::PartialEq | |
235 | | LangItem::PartialOrd | |
236 | => Some(1), | |
237 | ||
238 | // Unary operations | |
239 | LangItem::Neg | |
240 | | LangItem::Not | |
241 | ||
242 | // Miscellaneous | |
243 | | LangItem::Deref | |
244 | | LangItem::DerefMut | |
245 | | LangItem::Sized | |
246 | | LangItem::StructuralPeq | |
247 | | LangItem::StructuralTeq | |
248 | | LangItem::Copy | |
249 | | LangItem::Clone | |
250 | | LangItem::Sync | |
251 | | LangItem::DiscriminantKind | |
252 | | LangItem::PointeeTrait | |
253 | | LangItem::Freeze | |
254 | | LangItem::Drop | |
255 | | LangItem::Receiver | |
256 | | LangItem::Future | |
257 | | LangItem::Unpin | |
258 | | LangItem::Termination | |
259 | | LangItem::Try | |
17df50a5 XL |
260 | => Some(0), |
261 | ||
262 | // Not a trait | |
263 | _ => None, | |
264 | }; | |
265 | ||
266 | if let Some(expected_num) = expected_num { | |
267 | let (actual_num, generics_span) = match self.tcx.hir().get(hir_id) { | |
268 | hir::Node::Item(hir::Item { | |
269 | kind: hir::ItemKind::Trait(_, _, generics, ..), | |
270 | .. | |
271 | }) => (generics.params.len(), generics.span), | |
272 | _ => bug!("op/index/deref lang item target is not a trait: {:?}", lang_item), | |
273 | }; | |
274 | ||
275 | if expected_num != actual_num { | |
276 | // We are issuing E0718 "incorrect target" here, because while the | |
277 | // item kind of the target is correct, the target is still wrong | |
278 | // because of the wrong number of generic arguments. | |
279 | struct_span_err!( | |
280 | self.tcx.sess, | |
281 | span, | |
282 | E0718, | |
283 | "`{}` language item must be applied to a trait with {} generic argument{}", | |
284 | name, | |
285 | expected_num, | |
286 | pluralize!(expected_num) | |
287 | ) | |
288 | .span_label( | |
289 | generics_span, | |
290 | format!( | |
291 | "this trait has {} generic argument{}, not {}", | |
292 | actual_num, | |
293 | pluralize!(actual_num), | |
294 | expected_num | |
295 | ), | |
296 | ) | |
297 | .emit(); | |
298 | } | |
299 | } | |
300 | } | |
74b04a01 XL |
301 | } |
302 | ||
303 | /// Traverses and collects all the lang items in all crates. | |
17df50a5 | 304 | fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { |
74b04a01 XL |
305 | // Initialize the collector. |
306 | let mut collector = LanguageItemCollector::new(tcx); | |
307 | ||
308 | // Collect lang items in other crates. | |
136023e0 | 309 | for &cnum in tcx.crates(()).iter() { |
74b04a01 XL |
310 | for &(def_id, item_index) in tcx.defined_lang_items(cnum).iter() { |
311 | collector.collect_item(item_index, def_id); | |
312 | } | |
313 | } | |
314 | ||
315 | // Collect lang items in this crate. | |
316 | tcx.hir().krate().visit_all_item_likes(&mut collector); | |
317 | ||
318 | // Extract out the found lang items. | |
319 | let LanguageItemCollector { mut items, .. } = collector; | |
320 | ||
321 | // Find all required but not-yet-defined lang items. | |
322 | weak_lang_items::check_crate(tcx, &mut items); | |
323 | ||
324 | items | |
325 | } | |
326 | ||
f035d41b | 327 | pub fn provide(providers: &mut Providers) { |
17df50a5 | 328 | providers.get_lang_items = get_lang_items; |
74b04a01 | 329 | } |