Skip to main content

rustc_passes/
lang_items.rs

1//! Detecting lang 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
10use 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                // Known lang item
55                Some(lang_item) => {
56                    if actual_target != lang_item.target() {
57                        // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast
58                        // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`.
59                        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                    // Weak lang items are handled separately
71                    if lang_item.is_weak() && actual_target == Target::ForeignFn {
72                        self.items.missing.push(lang_item);
73                    } else {
74                        // Weak only lang items are always handled here
75                        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                // Unknown lang item.
86                _ => {
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        // Check for duplicates.
95        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            // When there's a duplicate lang item, something went very wrong and there's no value
149            // in recovering or doing anything. Give the user the one message to let them debug the
150            // mess they created and then wish them farewell.
151            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            // Matched.
167            self.items.set(lang_item, item_def_id);
168        }
169    }
170
171    // Like collect_item() above, but also checks whether the lang item is declared
172    // with the right number of generic arguments.
173    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            // Now check whether the lang_item has the expected number of generic
186            // arguments. Generally speaking, binary and indexing operations have
187            // one (for the RHS/index), unary operations have none, the closure
188            // traits have one for the argument list, coroutines have one for the
189            // resume argument, and ordering/equality relations have one for the RHS
190            // Some other types like Box and various unsizing-related traits
191            // have minimum requirements.
192
193            // FIXME: This still doesn't count, e.g., elided lifetimes and APITs.
194            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                // If the number matches, or there is no requirement, handle it normally
211                _ => None,
212            };
213
214            if let Some(num) = required {
215                // We are issuing E0718 "incorrect target" here, because while the
216                // item kind of the target is correct, the target is still wrong
217                // because of the wrong number of generic arguments.
218                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 early to not collect the lang item
229                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
241/// Traverses and collects all the lang items in all crates.
242fn 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    // Initialize the collector.
248    let mut collector = LanguageItemCollector::new(tcx, resolver);
249
250    // Collect lang items in other crates.
251    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    // Collect lang items local to this crate.
258    visit::Visitor::visit_crate(&mut collector, krate);
259
260    // Find all required but not-yet-defined lang items.
261    weak_lang_items::check_crate(tcx, &mut collector.items);
262
263    // Return all the lang items that were found.
264    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
314/// Extracts the first `lang = "$name"` out of a list of attributes.
315/// The `#[panic_handler]` attribute is also extracted out when found.
316///
317/// This function is used for `ast::Attribute`, for `hir::Attribute` use the `find_attr!` macro with `AttributeKind::Lang`
318pub(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}