diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index b075568ae13e..9499ad56aa93 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -383,9 +383,6 @@ passes_unknown_lang_item = definition of an unknown language item: `{$name}` .label = definition of unknown language item `{$name}` -passes_local_duplicate_lang_item = - found duplicate lang item `{$name}` - passes_invalid_attr_at_crate_level = `{$name}` attribute cannot be used at crate level .suggestion = perhaps you meant to use an outer attribute @@ -534,3 +531,30 @@ passes_no_main_function = .consider_adding_main_at_crate = consider adding a `main` function at the crate level .teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/ .non_function_main = non-function item at `crate::main` is found + +passes_duplicate_lang_item = + {$message -> + *[duplicate] found duplicate lang item `{$lang_item_name}` + [duplicate_in_crate] duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`. + [duplicate_in_crate_depends] duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`. + } + .first_defined_span = the lang item is first defined here + .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) + .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`. + .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) + .second_definition_local = second definition in the local crate (`{$crate_name}`) + .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} + .second_definition_path = second definition in `{$crate_name}` loaded from {$path} + +passes_incorrect_target = + `{$name}` language item must be applied to a {$kind} with {$at_least -> + [true] at least {$num} + *[false] {$num} + } generic {$num -> + [one] argument + *[other] arguments + } + .label = this {$kind} has {$actual_num} generic {$actual_num -> + [one] argument + *[other] arguments + } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 56e184ab25ee..bf3350129b0b 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1191,3 +1191,77 @@ impl<'a> IntoDiagnostic<'a> for NoMainErr { diag } } + +pub struct DuplicateLangItem<'a> { + pub local_span: Option, + pub lang_item_name: Symbol, + pub crate_name: Symbol, + pub dependency_of: Symbol, + pub is_local: bool, + pub path: String, + pub first_defined_span: Option, + pub orig_crate_name: Symbol, + pub orig_dependency_of: Symbol, + pub orig_is_local: bool, + pub orig_path: String, + pub message: &'a str, +} + +impl<'a, 'b> IntoDiagnostic<'a> for DuplicateLangItem<'b> { + fn into_diagnostic( + self, + handler: &'a rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> { + let mut diag = handler.struct_err_with_code( + rustc_errors::fluent::passes::duplicate_lang_item, + error_code!(E0152), + ); + diag.set_arg("lang_item_name", self.lang_item_name); + diag.set_arg("crate_name", self.crate_name); + diag.set_arg("dependency_of", self.dependency_of); + diag.set_arg("path", self.path); + diag.set_arg("orig_crate_name", self.orig_crate_name); + diag.set_arg("orig_dependency_of", self.orig_dependency_of); + diag.set_arg("orig_path", self.orig_path); + diag.set_arg("message", self.message); + if let Some(span) = self.local_span { + diag.set_span(span); + } + if let Some(span) = self.first_defined_span { + diag.span_note(span, rustc_errors::fluent::passes::first_defined_span); + } else { + if self.orig_dependency_of.is_empty() { + diag.note(rustc_errors::fluent::passes::first_defined_crate); + } else { + diag.note(rustc_errors::fluent::passes::first_defined_crate_depends); + } + + if self.orig_is_local { + diag.note(rustc_errors::fluent::passes::first_definition_local); + } else { + diag.note(rustc_errors::fluent::passes::first_definition_path); + } + + if self.is_local { + diag.note(rustc_errors::fluent::passes::second_definition_local); + } else { + diag.note(rustc_errors::fluent::passes::second_definition_path); + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes::incorrect_target, code = "E0718")] +pub struct IncorrectTarget<'a> { + #[primary_span] + pub span: Span, + #[label] + pub generics_span: Span, + pub name: &'a str, + pub kind: &'static str, + pub num: usize, + pub actual_num: usize, + pub at_least: bool, +} diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 2b090e036fe9..0af7c0cd3fcc 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -8,10 +8,11 @@ //! * Functions called by the compiler itself. use crate::check_attr::target_from_impl_item; -use crate::errors::{LangItemOnIncorrectTarget, UnknownLangItem}; +use crate::errors::{ + DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem, +}; use crate::weak_lang_items; -use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -19,7 +20,7 @@ use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS}; use rustc_hir::{HirId, LangItem, LanguageItems, Target}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::ExternCrate; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use rustc_middle::ty::query::Providers; @@ -62,74 +63,72 @@ impl<'tcx> LanguageItemCollector<'tcx> { // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { 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 - )), - }, - }; - 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 local_span = self.tcx.hir().span_if_local(item_def_id); + let lang_item_name = LangItem::from_u32(item_index as u32).unwrap().name(); + let crate_name = self.tcx.crate_name(item_def_id.krate); + let mut dependency_of = Symbol::intern(""); + let is_local = item_def_id.is_local(); + let path = if 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(item_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + .into() + }; + let first_defined_span = self.tcx.hir().span_if_local(original_def_id); + let mut orig_crate_name = Symbol::intern(""); + let mut orig_dependency_of = Symbol::intern(""); + let orig_is_local = original_def_id.is_local(); + let orig_path = if orig_is_local { + String::new() + } else { + self.tcx + .crate_extern_paths(original_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + .into() + }; + 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(¬e); - }; - note_def("first", original_def_id); - note_def("second", item_def_id); } - err.emit(); + + let message = if local_span.is_some() { + "duplicate" + } 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_in_crate_depends" + } + _ => "duplicate_in_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, + message, + }); } } @@ -162,41 +161,30 @@ impl<'tcx> LanguageItemCollector<'tcx> { None => (0, *item_span), }; + let mut at_least = false; let required = match lang_item.required_generics() { - GenericRequirement::Exact(num) if num != actual_num => { - Some((format!("{}", num), pluralize!(num))) - } + GenericRequirement::Exact(num) if num != actual_num => Some(num), GenericRequirement::Minimum(num) if actual_num < num => { - Some((format!("at least {}", num), pluralize!(num))) - } + at_least = true; + Some(num)} + , // If the number matches, or there is no requirement, handle it normally _ => None, }; - if let Some((range_str, pluralized)) = required { + 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. - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(IncorrectTarget { span, - E0718, - "`{}` language item must be applied to a {} with {} generic argument{}", - name, - kind.descr(), - range_str, - pluralized, - ) - .span_label( generics_span, - format!( - "this {} has {} generic argument{}", - kind.descr(), - actual_num, - pluralize!(actual_num), - ), - ) - .emit(); + name: name.as_str(), + kind: kind.descr(), + num, + actual_num, + at_least, + }); // return early to not collect the lang item return;