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