]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_passes/src/lang_items.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / compiler / rustc_passes / src / lang_items.rs
index 0ae0c381a110b13129e979795a3f58685677ac81..7e8372439183632490eb937d69f13fa62ea4b59f 100644 (file)
 //! * Functions called by the compiler itself.
 
 use crate::check_attr::target_from_impl_item;
+use crate::errors::{
+    DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem,
+};
 use crate::weak_lang_items;
 
-use rustc_middle::middle::cstore::ExternCrate;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::lang_items::{extract, GenericRequirement};
+use rustc_hir::{LangItem, LanguageItems, Target};
 use rustc_middle::ty::TyCtxt;
+use rustc_session::cstore::ExternCrate;
+use rustc_span::symbol::kw::Empty;
+use rustc_span::{sym, Span};
 
-use rustc_ast::Attribute;
-use rustc_errors::struct_span_err;
-use rustc_hir as hir;
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::lang_items::{extract, ITEM_REFS};
-use rustc_hir::{HirId, LangItem, LanguageItems, Target};
+use rustc_middle::query::Providers;
 
-use rustc_middle::ty::query::Providers;
+pub(crate) enum Duplicate {
+    Plain,
+    Crate,
+    CrateDepends,
+}
 
 struct LanguageItemCollector<'tcx> {
     items: LanguageItems,
     tcx: TyCtxt<'tcx>,
 }
 
-impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> {
-    fn visit_item(&mut self, item: &hir::Item<'_>) {
-        self.check_for_lang(Target::from_item(item), item.hir_id, item.attrs);
-
-        if let hir::ItemKind::Enum(def, ..) = &item.kind {
-            for variant in def.variants {
-                self.check_for_lang(Target::Variant, variant.id, variant.attrs);
-            }
-        }
-    }
-
-    fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
-        self.check_for_lang(
-            Target::from_trait_item(trait_item),
-            trait_item.hir_id,
-            trait_item.attrs,
-        )
-    }
-
-    fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
-        self.check_for_lang(
-            target_from_impl_item(self.tcx, impl_item),
-            impl_item.hir_id,
-            impl_item.attrs,
-        )
-    }
-}
-
-impl LanguageItemCollector<'tcx> {
+impl<'tcx> LanguageItemCollector<'tcx> {
     fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> {
         LanguageItemCollector { tcx, items: LanguageItems::new() }
     }
 
-    fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) {
-        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
-        if let Some((value, span)) = extract(check_name, &attrs) {
-            match ITEM_REFS.get(&value).cloned() {
+    fn check_for_lang(&mut self, actual_target: Target, def_id: LocalDefId) {
+        let attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
+        if let Some((name, span)) = extract(&attrs) {
+            match LangItem::from_name(name) {
                 // Known lang item with attribute on correct target.
-                Some((item_index, expected_target)) if actual_target == expected_target => {
-                    let def_id = self.tcx.hir().local_def_id(hir_id);
-                    self.collect_item(item_index, def_id.to_def_id());
+                Some(lang_item) if actual_target == lang_item.target() => {
+                    self.collect_item_extended(lang_item, def_id, span);
                 }
                 // Known lang item with attribute on incorrect target.
-                Some((_, expected_target)) => {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0718,
-                        "`{}` language item must be applied to a {}",
-                        value,
-                        expected_target,
-                    )
-                    .span_label(
+                Some(lang_item) => {
+                    self.tcx.sess.emit_err(LangItemOnIncorrectTarget {
                         span,
-                        format!(
-                            "attribute should be applied to a {}, not a {}",
-                            expected_target, actual_target,
-                        ),
-                    )
-                    .emit();
+                        name,
+                        expected_target: lang_item.target(),
+                        actual_target,
+                    });
                 }
                 // Unknown lang item.
                 _ => {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0522,
-                        "definition of an unknown language item: `{}`",
-                        value
-                    )
-                    .span_label(span, format!("definition of unknown language item `{}`", value))
-                    .emit();
+                    self.tcx.sess.emit_err(UnknownLangItem { span, name });
                 }
             }
         }
     }
 
-    fn collect_item(&mut self, item_index: usize, item_def_id: DefId) {
+    fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId) {
         // Check for duplicates.
-        if let Some(original_def_id) = self.items.items[item_index] {
+        if let Some(original_def_id) = self.items.get(lang_item) {
             if original_def_id != item_def_id {
-                let lang_item = LangItem::from_u32(item_index as u32).unwrap();
-                let name = lang_item.name();
-                let mut err = match self.tcx.hir().span_if_local(item_def_id) {
-                    Some(span) => struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0152,
-                        "found duplicate lang item `{}`",
-                        name
-                    ),
-                    None => match self.tcx.extern_crate(item_def_id) {
-                        Some(ExternCrate { dependency_of, .. }) => {
-                            self.tcx.sess.struct_err(&format!(
-                                "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.",
-                                self.tcx.crate_name(item_def_id.krate),
-                                self.tcx.crate_name(*dependency_of),
-                                name
-                            ))
-                        }
-                        _ => self.tcx.sess.struct_err(&format!(
-                            "duplicate lang item in crate `{}`: `{}`.",
-                            self.tcx.crate_name(item_def_id.krate),
-                            name
-                        )),
-                    },
+                let local_span = self.tcx.hir().span_if_local(item_def_id);
+                let lang_item_name = lang_item.name();
+                let crate_name = self.tcx.crate_name(item_def_id.krate);
+                let mut dependency_of = Empty;
+                let is_local = item_def_id.is_local();
+                let path = if is_local {
+                    String::new()
+                } else {
+                    self.tcx
+                        .crate_extern_paths(item_def_id.krate)
+                        .iter()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
                 };
-                if let Some(span) = self.tcx.hir().span_if_local(original_def_id) {
-                    err.span_note(span, "the lang item is first defined here");
+                let first_defined_span = self.tcx.hir().span_if_local(original_def_id);
+                let mut orig_crate_name = Empty;
+                let mut orig_dependency_of = Empty;
+                let orig_is_local = original_def_id.is_local();
+                let orig_path = if orig_is_local {
+                    String::new()
                 } else {
-                    match self.tcx.extern_crate(original_def_id) {
-                        Some(ExternCrate { dependency_of, .. }) => {
-                            err.note(&format!(
-                                "the lang item is first defined in crate `{}` (which `{}` depends on)",
-                                self.tcx.crate_name(original_def_id.krate),
-                                self.tcx.crate_name(*dependency_of)
-                            ));
-                        }
-                        _ => {
-                            err.note(&format!(
-                                "the lang item is first defined in crate `{}`.",
-                                self.tcx.crate_name(original_def_id.krate)
-                            ));
-                        }
+                    self.tcx
+                        .crate_extern_paths(original_def_id.krate)
+                        .iter()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                };
+                if first_defined_span.is_none() {
+                    orig_crate_name = self.tcx.crate_name(original_def_id.krate);
+                    if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
+                        self.tcx.extern_crate(original_def_id)
+                    {
+                        orig_dependency_of = self.tcx.crate_name(*inner_dependency_of);
                     }
-                    let mut note_def = |which, def_id: DefId| {
-                        let crate_name = self.tcx.crate_name(def_id.krate);
-                        let note = if def_id.is_local() {
-                            format!("{} definition in the local crate (`{}`)", which, crate_name)
-                        } else {
-                            let paths: Vec<_> = self
-                                .tcx
-                                .crate_extern_paths(def_id.krate)
-                                .iter()
-                                .map(|p| p.display().to_string())
-                                .collect();
-                            format!(
-                                "{} definition in `{}` loaded from {}",
-                                which,
-                                crate_name,
-                                paths.join(", ")
-                            )
-                        };
-                        err.note(&note);
-                    };
-                    note_def("first", original_def_id);
-                    note_def("second", item_def_id);
                 }
-                err.emit();
+
+                let duplicate = if local_span.is_some() {
+                    Duplicate::Plain
+                } else {
+                    match self.tcx.extern_crate(item_def_id) {
+                        Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
+                            dependency_of = self.tcx.crate_name(*inner_dependency_of);
+                            Duplicate::CrateDepends
+                        }
+                        _ => Duplicate::Crate,
+                    }
+                };
+
+                self.tcx.sess.emit_err(DuplicateLangItem {
+                    local_span,
+                    lang_item_name,
+                    crate_name,
+                    dependency_of,
+                    is_local,
+                    path,
+                    first_defined_span,
+                    orig_crate_name,
+                    orig_dependency_of,
+                    orig_is_local,
+                    orig_path,
+                    duplicate,
+                });
             }
         }
 
         // Matched.
-        self.items.items[item_index] = Some(item_def_id);
-        if let Some(group) = LangItem::from_u32(item_index as u32).unwrap().group() {
-            self.items.groups[group as usize].push(item_def_id);
+        self.items.set(lang_item, item_def_id);
+    }
+
+    // Like collect_item() above, but also checks whether the lang item is declared
+    // with the right number of generic arguments.
+    fn collect_item_extended(&mut self, lang_item: LangItem, item_def_id: LocalDefId, span: Span) {
+        let name = lang_item.name();
+
+        // Now check whether the lang_item has the expected number of generic
+        // arguments. Generally speaking, binary and indexing operations have
+        // one (for the RHS/index), unary operations have none, the closure
+        // traits have one for the argument list, generators have one for the
+        // resume argument, and ordering/equality relations have one for the RHS
+        // Some other types like Box and various functions like drop_in_place
+        // have minimum requirements.
+
+        if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) =
+            self.tcx.hir().get_by_def_id(item_def_id)
+        {
+            let (actual_num, generics_span) = match kind.generics() {
+                Some(generics) => (
+                    generics
+                        .params
+                        .iter()
+                        .filter(|p| !self.tcx.has_attr(p.def_id, sym::rustc_host))
+                        .count(),
+                    generics.span,
+                ),
+                None => (0, *item_span),
+            };
+
+            let mut at_least = false;
+            let required = match lang_item.required_generics() {
+                GenericRequirement::Exact(num) if num != actual_num => Some(num),
+                GenericRequirement::Minimum(num) if actual_num < num => {
+                    at_least = true;
+                    Some(num)}
+                ,
+                // If the number matches, or there is no requirement, handle it normally
+                _ => None,
+            };
+
+            if let Some(num) = required {
+                // We are issuing E0718 "incorrect target" here, because while the
+                // item kind of the target is correct, the target is still wrong
+                // because of the wrong number of generic arguments.
+                self.tcx.sess.emit_err(IncorrectTarget {
+                    span,
+                    generics_span,
+                    name: name.as_str(),
+                    kind: kind.descr(),
+                    num,
+                    actual_num,
+                    at_least,
+                });
+
+                // return early to not collect the lang item
+                return;
+            }
         }
+
+        self.collect_item(lang_item, item_def_id.to_def_id());
     }
 }
 
 /// Traverses and collects all the lang items in all crates.
-fn collect(tcx: TyCtxt<'_>) -> LanguageItems {
+fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
     // Initialize the collector.
     let mut collector = LanguageItemCollector::new(tcx);
 
     // Collect lang items in other crates.
-    for &cnum in tcx.crates().iter() {
-        for &(def_id, item_index) in tcx.defined_lang_items(cnum).iter() {
-            collector.collect_item(item_index, def_id);
+    for &cnum in tcx.crates(()).iter() {
+        for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
+            collector.collect_item(lang_item, def_id);
         }
     }
 
     // Collect lang items in this crate.
-    tcx.hir().krate().visit_all_item_likes(&mut collector);
+    let crate_items = tcx.hir_crate_items(());
+
+    for id in crate_items.items() {
+        collector
+            .check_for_lang(Target::from_def_kind(tcx.def_kind(id.owner_id)), id.owner_id.def_id);
+
+        if matches!(tcx.def_kind(id.owner_id), DefKind::Enum) {
+            let item = tcx.hir().item(id);
+            if let hir::ItemKind::Enum(def, ..) = &item.kind {
+                for variant in def.variants {
+                    collector.check_for_lang(Target::Variant, variant.def_id);
+                }
+            }
+        }
+    }
+
+    // FIXME: avoid calling trait_item() when possible
+    for id in crate_items.trait_items() {
+        let item = tcx.hir().trait_item(id);
+        collector.check_for_lang(Target::from_trait_item(item), item.owner_id.def_id)
+    }
+
+    // FIXME: avoid calling impl_item() when possible
+    for id in crate_items.impl_items() {
+        let item = tcx.hir().impl_item(id);
+        collector.check_for_lang(target_from_impl_item(tcx, item), item.owner_id.def_id)
+    }
 
     // Extract out the found lang items.
     let LanguageItemCollector { mut items, .. } = collector;
@@ -213,8 +254,5 @@ fn collect(tcx: TyCtxt<'_>) -> LanguageItems {
 }
 
 pub fn provide(providers: &mut Providers) {
-    providers.get_lang_items = |tcx, id| {
-        assert_eq!(id, LOCAL_CRATE);
-        collect(tcx)
-    };
+    providers.get_lang_items = get_lang_items;
 }