1use rustc_ast as ast;
11use rustc_ast::visit;
12use rustc_hir::def_id::{DefId, LocalDefId};
13use rustc_hir::lang_items::GenericRequirement;
14use rustc_hir::{LangItem, LanguageItems, Target};
15use rustc_middle::query::Providers;
16use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
17use rustc_session::cstore::ExternCrate;
18use rustc_span::{Span, Symbol, sym};
19
20use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget};
21use crate::weak_lang_items;
22
23pub(crate) enum Duplicate {
24 Plain,
25 Crate,
26 CrateDepends,
27}
28
29struct LanguageItemCollector<'ast, 'tcx> {
30 items: LanguageItems,
31 tcx: TyCtxt<'tcx>,
32 resolver: &'ast ResolverAstLowering<'tcx>,
33 parent_item: Option<&'ast ast::Item>,
34}
35
36impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
37 fn new(
38 tcx: TyCtxt<'tcx>,
39 resolver: &'ast ResolverAstLowering<'tcx>,
40 ) -> LanguageItemCollector<'ast, 'tcx> {
41 LanguageItemCollector { tcx, resolver, items: LanguageItems::new(), parent_item: None }
42 }
43
44 fn check_for_lang(
45 &mut self,
46 actual_target: Target,
47 def_id: LocalDefId,
48 attrs: &'ast [ast::Attribute],
49 item_span: Span,
50 generics: Option<&'ast ast::Generics>,
51 ) {
52 if let Some((name, attr_span)) = extract_ast(attrs) {
53 match LangItem::from_name(name) {
54 Some(lang_item) => {
56 if actual_target != lang_item.target() {
57 if !(lang_item.is_weak()
60 && actual_target == Target::Fn
61 && lang_item.target() == Target::ForeignFn
62 && #[allow(non_exhaustive_omitted_patterns)] match lang_item {
LangItem::PanicImpl => true,
_ => false,
}matches!(lang_item, LangItem::PanicImpl))
63 {
64 self.tcx
65 .dcx()
66 .delayed_bug(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("lang item target is checked in attribute parser: {0:?} has {1} but expected {2}",
def_id, actual_target, lang_item.target()))
})format!("lang item target is checked in attribute parser: {:?} has {} but expected {}", def_id, actual_target, lang_item.target()));
67 return;
68 }
69 }
70 if lang_item.is_weak() && actual_target == Target::ForeignFn {
72 self.items.missing.push(lang_item);
73 } else {
74 self.collect_item_extended(
76 lang_item,
77 def_id,
78 item_span,
79 attr_span,
80 generics,
81 actual_target,
82 );
83 }
84 }
85 _ => {
87 self.tcx.dcx().delayed_bug("unknown lang item");
88 }
89 }
90 }
91 }
92
93 fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option<Span>) {
94 if let Some(original_def_id) = self.items.get(lang_item)
96 && original_def_id != item_def_id
97 {
98 let lang_item_name = lang_item.name();
99 let crate_name = self.tcx.crate_name(item_def_id.krate);
100 let mut dependency_of = None;
101 let is_local = item_def_id.is_local();
102 let path = if is_local {
103 String::new()
104 } else {
105 self.tcx
106 .crate_extern_paths(item_def_id.krate)
107 .iter()
108 .map(|p| p.display().to_string())
109 .collect::<Vec<_>>()
110 .join(", ")
111 };
112
113 let mut orig_crate_name = None;
114 let mut orig_dependency_of = None;
115 let orig_is_local = original_def_id.is_local();
116 let orig_path = if orig_is_local {
117 String::new()
118 } else {
119 self.tcx
120 .crate_extern_paths(original_def_id.krate)
121 .iter()
122 .map(|p| p.display().to_string())
123 .collect::<Vec<_>>()
124 .join(", ")
125 };
126
127 if !original_def_id.is_local() {
128 orig_crate_name = Some(self.tcx.crate_name(original_def_id.krate));
129 if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
130 self.tcx.extern_crate(original_def_id.krate)
131 {
132 orig_dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
133 }
134 }
135
136 let duplicate = if item_span.is_some() {
137 Duplicate::Plain
138 } else {
139 match self.tcx.extern_crate(item_def_id.krate) {
140 Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
141 dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
142 Duplicate::CrateDepends
143 }
144 _ => Duplicate::Crate,
145 }
146 };
147
148 self.tcx.dcx().emit_fatal(DuplicateLangItem {
152 local_span: item_span,
153 lang_item_name,
154 crate_name,
155 dependency_of,
156 is_local,
157 path,
158 first_defined_span: original_def_id.as_local().map(|did| self.tcx.source_span(did)),
159 orig_crate_name,
160 orig_dependency_of,
161 orig_is_local,
162 orig_path,
163 duplicate,
164 });
165 } else {
166 self.items.set(lang_item, item_def_id);
168 }
169 }
170
171 fn collect_item_extended(
174 &mut self,
175 lang_item: LangItem,
176 item_def_id: LocalDefId,
177 item_span: Span,
178 attr_span: Span,
179 generics: Option<&'ast ast::Generics>,
180 target: Target,
181 ) {
182 let name = lang_item.name();
183
184 if let Some(generics) = generics {
185 let mut actual_num = generics.params.len();
195 if target.is_associated_item() {
196 actual_num += self
197 .parent_item
198 .unwrap()
199 .opt_generics()
200 .map_or(0, |generics| generics.params.len());
201 }
202
203 let mut at_least = false;
204 let required = match lang_item.required_generics() {
205 GenericRequirement::Exact(num) if num != actual_num => Some(num),
206 GenericRequirement::Minimum(num) if actual_num < num => {
207 at_least = true;
208 Some(num)
209 }
210 _ => None,
212 };
213
214 if let Some(num) = required {
215 self.tcx.dcx().emit_err(IncorrectTarget {
219 span: attr_span,
220 generics_span: generics.span,
221 name: name.as_str(),
222 kind: target.name(),
223 num,
224 actual_num,
225 at_least,
226 });
227
228 return;
230 }
231 }
232
233 if self.tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) {
234 self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span });
235 }
236
237 self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span));
238 }
239}
240
241fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
243 let (resolver, krate) = tcx.resolver_for_lowering();
244 let resolver = &*resolver.borrow();
245 let krate = &*krate.borrow();
246
247 let mut collector = LanguageItemCollector::new(tcx, resolver);
249
250 for &cnum in tcx.used_crates(()).iter() {
252 for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
253 collector.collect_item(lang_item, def_id, None);
254 }
255 }
256
257 visit::Visitor::visit_crate(&mut collector, krate);
259
260 weak_lang_items::check_crate(tcx, &mut collector.items);
262
263 collector.items
265}
266
267impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
268 fn visit_item(&mut self, i: &'ast ast::Item) {
269 let target = Target::from_ast_item(i);
270
271 self.check_for_lang(
272 target,
273 self.resolver.owners[&i.id].def_id,
274 &i.attrs,
275 i.span,
276 i.opt_generics(),
277 );
278
279 let parent_item = self.parent_item.replace(i);
280 visit::walk_item(self, i);
281 self.parent_item = parent_item;
282 }
283
284 fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) {
285 self.check_for_lang(
286 Target::from_foreign_item_kind(&i.kind),
287 self.resolver.owners[&i.id].def_id,
288 &i.attrs,
289 i.span,
290 None,
291 );
292 }
293
294 fn visit_variant(&mut self, variant: &'ast ast::Variant) {
295 self.check_for_lang(
296 Target::Variant,
297 self.resolver.owners[&self.parent_item.unwrap().id].node_id_to_def_id[&variant.id],
298 &variant.attrs,
299 variant.span,
300 None,
301 );
302 }
303
304 fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) {
305 let target = Target::from_assoc_item_kind(&i.kind, ctxt);
306 let generics = i.opt_generics();
307
308 self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics);
309
310 visit::walk_assoc_item(self, i, ctxt);
311 }
312}
313
314pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> {
319 attrs.iter().find_map(|attr| {
320 Some(match attr {
321 _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
322 _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
323 _ => return None,
324 })
325 })
326}
327
328pub(crate) fn provide(providers: &mut Providers) {
329 providers.get_lang_items = get_lang_items;
330}