1use rustc_ast as ast;
11use rustc_ast::visit;
12use rustc_data_structures::fx::FxHashMap;
13use rustc_hir::def_id::{DefId, LocalDefId};
14use rustc_hir::lang_items::GenericRequirement;
15use rustc_hir::{LangItem, LanguageItems, MethodKind, Target};
16use rustc_middle::query::Providers;
17use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
18use rustc_session::cstore::ExternCrate;
19use rustc_span::{Span, Symbol, sym};
20
21use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget};
22use crate::weak_lang_items;
23
24pub(crate) enum Duplicate {
25 Plain,
26 Crate,
27 CrateDepends,
28}
29
30enum CollectWeak {
31 Allowed,
32 Ignore,
33}
34
35struct LanguageItemCollector<'ast, 'tcx> {
36 items: LanguageItems,
37 tcx: TyCtxt<'tcx>,
38 resolver: &'ast ResolverAstLowering<'tcx>,
39 item_spans: FxHashMap<DefId, Span>,
42 parent_item: Option<&'ast ast::Item>,
43}
44
45impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
46 fn new(
47 tcx: TyCtxt<'tcx>,
48 resolver: &'ast ResolverAstLowering<'tcx>,
49 ) -> LanguageItemCollector<'ast, 'tcx> {
50 LanguageItemCollector {
51 tcx,
52 resolver,
53 items: LanguageItems::new(),
54 item_spans: FxHashMap::default(),
55 parent_item: None,
56 }
57 }
58
59 fn check_for_lang(
60 &mut self,
61 actual_target: Target,
62 def_id: LocalDefId,
63 attrs: &'ast [ast::Attribute],
64 item_span: Span,
65 generics: Option<&'ast ast::Generics>,
66 collect_weak: CollectWeak,
67 ) {
68 if let Some((name, attr_span)) = extract_ast(attrs) {
69 match LangItem::from_name(name) {
70 Some(lang_item) => {
72 if actual_target != lang_item.target() {
73 self.tcx
74 .dcx()
75 .delayed_bug("lang item target is checked in attribute parser");
76 return;
77 }
78 if !lang_item.is_weak() || #[allow(non_exhaustive_omitted_patterns)] match collect_weak {
CollectWeak::Allowed => true,
_ => false,
}matches!(collect_weak, CollectWeak::Allowed) {
81 self.collect_item_extended(
82 lang_item,
83 def_id,
84 item_span,
85 attr_span,
86 generics,
87 actual_target,
88 );
89 }
90 }
91 _ => {
93 self.tcx.dcx().delayed_bug("unknown lang item");
94 }
95 }
96 }
97 }
98
99 fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option<Span>) {
100 if let Some(original_def_id) = self.items.get(lang_item)
102 && original_def_id != item_def_id
103 {
104 let lang_item_name = lang_item.name();
105 let crate_name = self.tcx.crate_name(item_def_id.krate);
106 let mut dependency_of = None;
107 let is_local = item_def_id.is_local();
108 let path = if is_local {
109 String::new()
110 } else {
111 self.tcx
112 .crate_extern_paths(item_def_id.krate)
113 .iter()
114 .map(|p| p.display().to_string())
115 .collect::<Vec<_>>()
116 .join(", ")
117 };
118
119 let first_defined_span = self.item_spans.get(&original_def_id).copied();
120 let mut orig_crate_name = None;
121 let mut orig_dependency_of = None;
122 let orig_is_local = original_def_id.is_local();
123 let orig_path = if orig_is_local {
124 String::new()
125 } else {
126 self.tcx
127 .crate_extern_paths(original_def_id.krate)
128 .iter()
129 .map(|p| p.display().to_string())
130 .collect::<Vec<_>>()
131 .join(", ")
132 };
133
134 if first_defined_span.is_none() {
135 orig_crate_name = Some(self.tcx.crate_name(original_def_id.krate));
136 if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
137 self.tcx.extern_crate(original_def_id.krate)
138 {
139 orig_dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
140 }
141 }
142
143 let duplicate = if item_span.is_some() {
144 Duplicate::Plain
145 } else {
146 match self.tcx.extern_crate(item_def_id.krate) {
147 Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
148 dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
149 Duplicate::CrateDepends
150 }
151 _ => Duplicate::Crate,
152 }
153 };
154
155 self.tcx.dcx().emit_fatal(DuplicateLangItem {
159 local_span: item_span,
160 lang_item_name,
161 crate_name,
162 dependency_of,
163 is_local,
164 path,
165 first_defined_span,
166 orig_crate_name,
167 orig_dependency_of,
168 orig_is_local,
169 orig_path,
170 duplicate,
171 });
172 } else {
173 self.items.set(lang_item, item_def_id);
175 if let Some(item_span) = item_span {
177 self.item_spans.insert(item_def_id, item_span);
178 }
179 }
180 }
181
182 fn collect_item_extended(
185 &mut self,
186 lang_item: LangItem,
187 item_def_id: LocalDefId,
188 item_span: Span,
189 attr_span: Span,
190 generics: Option<&'ast ast::Generics>,
191 target: Target,
192 ) {
193 let name = lang_item.name();
194
195 if let Some(generics) = generics {
196 let mut actual_num = generics.params.len();
206 if target.is_associated_item() {
207 actual_num += self
208 .parent_item
209 .unwrap()
210 .opt_generics()
211 .map_or(0, |generics| generics.params.len());
212 }
213
214 let mut at_least = false;
215 let required = match lang_item.required_generics() {
216 GenericRequirement::Exact(num) if num != actual_num => Some(num),
217 GenericRequirement::Minimum(num) if actual_num < num => {
218 at_least = true;
219 Some(num)
220 }
221 _ => None,
223 };
224
225 if let Some(num) = required {
226 self.tcx.dcx().emit_err(IncorrectTarget {
230 span: attr_span,
231 generics_span: generics.span,
232 name: name.as_str(),
233 kind: target.name(),
234 num,
235 actual_num,
236 at_least,
237 });
238
239 return;
241 }
242 }
243
244 if self.tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) {
245 self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span });
246 }
247
248 self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span));
249 }
250}
251
252fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
254 let (resolver, krate) = tcx.resolver_for_lowering();
255 let resolver = &*resolver.borrow();
256 let krate = &*krate.borrow();
257
258 let mut collector = LanguageItemCollector::new(tcx, resolver);
260
261 for &cnum in tcx.used_crates(()).iter() {
263 for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
264 collector.collect_item(lang_item, def_id, None);
265 }
266 }
267
268 visit::Visitor::visit_crate(&mut collector, krate);
270
271 weak_lang_items::check_crate(tcx, &mut collector.items, krate);
273
274 collector.items
276}
277
278impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
279 fn visit_item(&mut self, i: &'ast ast::Item) {
280 let target = match &i.kind {
281 ast::ItemKind::ExternCrate(..) => Target::ExternCrate,
282 ast::ItemKind::Use(_) => Target::Use,
283 ast::ItemKind::Static(_) => Target::Static,
284 ast::ItemKind::Const(_) | ast::ItemKind::ConstBlock(_) => Target::Const,
285 ast::ItemKind::Fn(_) | ast::ItemKind::Delegation(..) => Target::Fn,
286 ast::ItemKind::Mod(..) => Target::Mod,
287 ast::ItemKind::ForeignMod(_) => Target::ForeignFn,
288 ast::ItemKind::GlobalAsm(_) => Target::GlobalAsm,
289 ast::ItemKind::TyAlias(_) => Target::TyAlias,
290 ast::ItemKind::Enum(..) => Target::Enum,
291 ast::ItemKind::Struct(..) => Target::Struct,
292 ast::ItemKind::Union(..) => Target::Union,
293 ast::ItemKind::Trait(_) => Target::Trait,
294 ast::ItemKind::TraitAlias(..) => Target::TraitAlias,
295 ast::ItemKind::Impl(imp_) => Target::Impl { of_trait: imp_.of_trait.is_some() },
296 ast::ItemKind::MacroDef(..) => Target::MacroDef,
297 ast::ItemKind::MacCall(_) | ast::ItemKind::DelegationMac(_) => {
298 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("macros should have been expanded")));
}unreachable!("macros should have been expanded")
299 }
300 };
301
302 self.check_for_lang(
303 target,
304 self.resolver.owners[&i.id].def_id,
305 &i.attrs,
306 i.span,
307 i.opt_generics(),
308 CollectWeak::Allowed,
309 );
310
311 let parent_item = self.parent_item.replace(i);
312 visit::walk_item(self, i);
313 self.parent_item = parent_item;
314 }
315
316 fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) {
317 self.check_for_lang(
318 Target::Fn,
319 self.resolver.owners[&i.id].def_id,
320 &i.attrs,
321 i.span,
322 None,
323 CollectWeak::Ignore,
324 );
325 }
326
327 fn visit_variant(&mut self, variant: &'ast ast::Variant) {
328 self.check_for_lang(
329 Target::Variant,
330 self.resolver.owners[&self.parent_item.unwrap().id].node_id_to_def_id[&variant.id],
331 &variant.attrs,
332 variant.span,
333 None,
334 CollectWeak::Allowed,
335 );
336 }
337
338 fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) {
339 let (target, generics) = match &i.kind {
340 ast::AssocItemKind::Fn(..) | ast::AssocItemKind::Delegation(..) => {
341 let (body, generics) = if let ast::AssocItemKind::Fn(fun) = &i.kind {
342 (fun.body.is_some(), Some(&fun.generics))
343 } else {
344 (true, None)
345 };
346 (
347 match &self.parent_item.unwrap().kind {
348 ast::ItemKind::Impl(i) => {
349 if i.of_trait.is_some() {
350 Target::Method(MethodKind::TraitImpl)
351 } else {
352 Target::Method(MethodKind::Inherent)
353 }
354 }
355 ast::ItemKind::Trait(_) => Target::Method(MethodKind::Trait { body }),
356 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
357 },
358 generics,
359 )
360 }
361 ast::AssocItemKind::Const(ct) => (Target::AssocConst, Some(&ct.generics)),
362 ast::AssocItemKind::Type(ty) => (Target::AssocTy, Some(&ty.generics)),
363 ast::AssocItemKind::MacCall(_) | ast::AssocItemKind::DelegationMac(_) => {
364 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("macros should have been expanded")));
}unreachable!("macros should have been expanded")
365 }
366 };
367
368 self.check_for_lang(
369 target,
370 self.resolver.owners[&i.id].def_id,
371 &i.attrs,
372 i.span,
373 generics,
374 CollectWeak::Allowed,
375 );
376
377 visit::walk_assoc_item(self, i, ctxt);
378 }
379}
380
381pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> {
386 attrs.iter().find_map(|attr| {
387 Some(match attr {
388 _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
389 _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
390 _ => return None,
391 })
392 })
393}
394
395pub(crate) fn provide(providers: &mut Providers) {
396 providers.get_lang_items = get_lang_items;
397}