diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3e527b6aafa1f..eb72ebee4e41b 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -31,14 +31,14 @@ use tracing::debug; use crate::Namespace::{MacroNS, TypeNS, ValueNS}; use crate::def_collector::DefCollector; -use crate::diagnostics::StructCtor; +use crate::error_helper::StructCtor; use crate::imports::{ImportData, ImportKind, OnUnknownData}; use crate::macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef}; use crate::ref_mut::CmCell; use crate::{ BindingKey, Decl, DeclData, DeclKind, DelayedVisResolutionError, ExternModule, ExternPreludeEntry, Finalize, IdentKey, LocalModule, Module, ModuleKind, ModuleOrUniformRoot, - ParentScope, PathResult, Res, Resolver, Segment, Used, VisResolutionError, errors, + ParentScope, PathResult, Res, Resolver, Segment, Used, VisResolutionError, diagnostics, }; impl<'ra, 'tcx> Resolver<'ra, 'tcx> { @@ -706,9 +706,9 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { // Deny importing path-kw without renaming if rename.is_none() && ident.is_path_segment_keyword() { let ident = use_tree.ident(); - self.r.dcx().emit_err(errors::UnnamedImport { + self.r.dcx().emit_err(diagnostics::UnnamedImport { span: ident.span, - sugg: errors::UnnamedImportSugg { span: ident.span, ident }, + sugg: diagnostics::UnnamedImportSugg { span: ident.span, ident }, }); return; } @@ -996,7 +996,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { let expansion = parent_scope.expansion; let (used, module, decl) = if orig_name.is_none() && orig_ident.name == kw::SelfLower { - self.r.dcx().emit_err(errors::ExternCrateSelfRequiresRenaming { span: sp }); + self.r.dcx().emit_err(diagnostics::ExternCrateSelfRequiresRenaming { span: sp }); return; } else if orig_name == Some(kw::SelfLower) { Some(self.r.graph_root.to_module()) @@ -1054,7 +1054,9 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { && entry.item_decl.is_none() { self.r.dcx().emit_err( - errors::MacroExpandedExternCrateCannotShadowExternArguments { span: item.span }, + diagnostics::MacroExpandedExternCrateCannotShadowExternArguments { + span: item.span, + }, ); } @@ -1125,7 +1127,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { allow_shadowing: bool, ) { if self.r.macro_use_prelude.insert(name, decl).is_some() && !allow_shadowing { - self.r.dcx().emit_err(errors::MacroUseNameAlreadyInUse { span, name }); + self.r.dcx().emit_err(diagnostics::MacroUseNameAlreadyInUse { span, name }); } } @@ -1137,14 +1139,14 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { AttributeParser::parse_limited(self.r.tcx.sess, &item.attrs, &[sym::macro_use]) { if self.parent_scope.module.expect_local().parent.is_some() { - self.r - .dcx() - .emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { span: item.span }); + self.r.dcx().emit_err(diagnostics::ExternCrateLoadingMacroNotAtCrateRoot { + span: item.span, + }); } if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind && orig_name == kw::SelfLower { - self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span }); + self.r.dcx().emit_err(diagnostics::MacroUseExternCrateSelf { span }); } match arguments { @@ -1208,7 +1210,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { let import_decl = self.r.new_import_decl(binding, import); self.add_macro_use_decl(ident.name, import_decl, ident.span, allow_shadowing); } else { - self.r.dcx().emit_err(errors::ImportedMacroNotFound { span: ident.span }); + self.r.dcx().emit_err(diagnostics::ImportedMacroNotFound { span: ident.span }); } } } @@ -1220,15 +1222,16 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { for attr in attrs { if attr.has_name(sym::macro_escape) { let inner_attribute = matches!(attr.style, ast::AttrStyle::Inner); - self.r - .dcx() - .emit_warn(errors::MacroExternDeprecated { span: attr.span, inner_attribute }); + self.r.dcx().emit_warn(diagnostics::MacroExternDeprecated { + span: attr.span, + inner_attribute, + }); } else if !attr.has_name(sym::macro_use) { continue; } if !attr.is_word() { - self.r.dcx().emit_err(errors::ArgumentsMacroUseNotAllowed { span: attr.span }); + self.r.dcx().emit_err(diagnostics::ArgumentsMacroUseNotAllowed { span: attr.span }); } return true; } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 3fd9adc10c882..32e4bb9262fa8 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -38,7 +38,7 @@ use rustc_session::lint::builtin::{ use rustc_span::{DUMMY_SP, Ident, Span, kw}; use crate::imports::{Import, ImportKind}; -use crate::{DeclKind, IdentKey, LateDecl, Resolver, errors, module_to_string}; +use crate::{DeclKind, IdentKey, LateDecl, Resolver, diagnostics, module_to_string}; struct UnusedImport { use_tree: ast::UseTree, @@ -170,7 +170,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { UNUSED_EXTERN_CRATES, extern_crate.id, span, - crate::errors::UnusedExternCrate { + crate::diagnostics::UnusedExternCrate { span: extern_crate.span, removal_span: extern_crate.span_with_attributes, }, @@ -234,7 +234,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { UNUSED_EXTERN_CRATES, extern_crate.id, extern_crate.span, - crate::errors::ExternCrateNotIdiomatic { + crate::diagnostics::ExternCrateNotIdiomatic { span: vis_span.between(ident_span), code: if vis_span.is_empty() { "use " } else { " use " }, }, @@ -426,7 +426,7 @@ impl Resolver<'_, '_> { MACRO_USE_EXTERN_CRATE, import.root_id, import.span, - crate::errors::MacroUseDeprecated, + crate::diagnostics::MacroUseDeprecated, ); } } @@ -450,7 +450,7 @@ impl Resolver<'_, '_> { UNUSED_IMPORTS, import.root_id, import.span, - crate::errors::UnusedMacroUse, + crate::diagnostics::UnusedMacroUse, ); } _ => {} @@ -524,9 +524,12 @@ impl Resolver<'_, '_> { move |dcx, level, sess| { let sugg = can_suggest_removal.then(|| { if remove_whole_use { - errors::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } + diagnostics::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } } else { - errors::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } + diagnostics::UnusedImportsSugg::RemoveImports { + remove_spans, + num_to_remove, + } } }); let test_module_span = test_module_span.map(|span| { @@ -536,7 +539,7 @@ impl Resolver<'_, '_> { .guess_head_span(span) }); - errors::UnusedImports { + diagnostics::UnusedImports { sugg, test_module_span, num_snippets: span_snippets.len(), @@ -593,7 +596,7 @@ impl Resolver<'_, '_> { UNUSED_QUALIFICATIONS, unn_qua.node_id, unn_qua.path_span, - errors::UnusedQualifications { removal_span: unn_qua.removal_span }, + diagnostics::UnusedQualifications { removal_span: unn_qua.removal_span }, ); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index aef8d7ed7c54e..2baf423e296d6 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,3892 +1,1778 @@ -// ignore-tidy-filelength -use std::mem; -use std::ops::ControlFlow; - -use itertools::Itertools as _; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{ - self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path, - join_path_idents, -}; -use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, - struct_span_code_err, -}; -use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::{CfgEntry, StrippedCfgItem}; -use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; -use rustc_hir::{PrimTy, Stability, StabilityLevel, find_attr}; -use rustc_middle::bug; -use rustc_middle::ty::{TyCtxt, Visibility}; -use rustc_session::Session; -use rustc_session::lint::builtin::{ - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, - AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, ElidedLifetimeInPathSubdiag, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, }; -use rustc_session::utils::was_invoked_from_cargo; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::SourceMap; -use rustc_span::{ - BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym, -}; -use thin_vec::{ThinVec, thin_vec}; -use tracing::{debug, instrument}; +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::{Ident, Span, Spanned, Symbol}; + +use crate::Res; +use crate::late::PatternSource; + +#[derive(Diagnostic)] +#[diag("can't use {$is_self -> + [true] `Self` + *[false] generic parameters + } from outer item", code = E0401)] +#[note( + "nested items are independent from their parent item for everything except for privacy and name resolution" +)] +pub(crate) struct GenericParamsFromOuterItem { + #[primary_span] + #[label( + "use of {$is_self -> + [true] `Self` + *[false] generic parameter + } from outer item" + )] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) label: Option, + #[subdiagnostic] + pub(crate) refer_to_type_directly: Option, + #[subdiagnostic] + pub(crate) use_let: Option, + #[subdiagnostic] + pub(crate) sugg: Option, + #[subdiagnostic] + pub(crate) static_or_const: Option, + pub(crate) is_self: bool, + #[subdiagnostic] + pub(crate) item: Option, +} -use crate::errors::{ - self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, - ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, - MaybeMissingMacroRulesName, -}; -use crate::hygiene::Macros20NormalizedSyntaxContext; -use crate::imports::{Import, ImportKind}; -use crate::late::{DiagMetadata, PatternSource, Rib}; -use crate::{ - AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, - DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, - LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, - PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, - VisResolutionError, errors as errs, path_names_to_string, -}; +#[derive(Subdiagnostic)] +#[label( + "{$is_self -> + [true] `Self` + *[false] generic parameter + } used in this inner {$descr}" +)] +pub(crate) struct GenericParamsFromOuterItemInnerItem { + #[primary_span] + pub(crate) span: Span, + pub(crate) descr: String, + pub(crate) is_self: bool, +} + +#[derive(Subdiagnostic)] +pub(crate) enum GenericParamsFromOuterItemStaticOrConst { + #[note("a `static` is a separate item from the item that contains it")] + Static, + #[note("a `const` is a separate item from the item that contains it")] + Const, +} -/// A vector of spans and replacements, a message and applicability. -pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); +#[derive(Subdiagnostic)] +pub(crate) enum GenericParamsFromOuterItemLabel { + #[label("can't use `Self` here")] + SelfTyParam(#[primary_span] Span), + #[label("`Self` type implicitly declared here, by this `impl`")] + SelfTyAlias(#[primary_span] Span), + #[label("type parameter from outer item")] + TyParam(#[primary_span] Span), + #[label("const parameter from outer item")] + ConstParam(#[primary_span] Span), +} -/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a -/// similarly named label and whether or not it is reachable. -pub(crate) type LabelSuggestion = (Ident, bool); +#[derive(Subdiagnostic)] +#[suggestion( + "try introducing a local generic parameter here", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemSugg { + #[primary_span] + pub(crate) span: Span, + pub(crate) snippet: String, +} -#[derive(Clone)] -pub(crate) struct StructCtor { - pub res: Res, - pub vis: Visibility, - pub field_visibilities: Vec>, +#[derive(Subdiagnostic)] +#[suggestion( + "try using a local `let` binding instead", + code = "let", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemUseLet { + #[primary_span] + pub(crate) span: Span, } -impl StructCtor { - pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool { - self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m)) - } +#[derive(Subdiagnostic)] +#[suggestion( + "refer to the type directly here instead", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UseTypeDirectly { + #[primary_span] + pub(crate) span: Span, + pub(crate) snippet: String, } -#[derive(Debug)] -pub(crate) enum SuggestionTarget { - /// The target has a similar name as the name used by the programmer (probably a typo) - SimilarlyNamed, - /// The target is the only valid item that can be used in the corresponding context - SingleItem, +#[derive(Diagnostic)] +#[diag("the name `{$name}` is already used for a generic parameter in this item's generic parameters", code = E0403)] +pub(crate) struct NameAlreadyUsedInParameterList { + #[primary_span] + #[label("already used")] + pub(crate) span: Span, + #[label("first use of `{$name}`")] + pub(crate) first_use_span: Span, + pub(crate) name: Ident, +} + +#[derive(Diagnostic)] +#[diag("method `{$method}` is not a member of trait `{$trait_}`", code = E0407)] +pub(crate) struct MethodNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) method: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated function with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedFnWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} + +#[derive(Diagnostic)] +#[diag("type `{$type_}` is not a member of trait `{$trait_}`", code = E0437)] +pub(crate) struct TypeNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) type_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated type with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedTypeWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} + +#[derive(Diagnostic)] +#[diag("const `{$const_}` is not a member of trait `{$trait_}`", code = E0438)] +pub(crate) struct ConstNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) const_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated constant with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedConstWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} + +#[derive(Diagnostic)] +#[diag("variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`", code = E0409)] +pub(crate) struct VariableBoundWithDifferentMode { + #[primary_span] + #[label("bound in different ways")] + pub(crate) span: Span, + #[label("first binding")] + pub(crate) first_binding_span: Span, + pub(crate) variable_name: Ident, +} + +#[derive(Diagnostic)] +#[diag("identifier `{$identifier}` is bound more than once in this parameter list", code = E0415)] +pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { + #[primary_span] + #[label("used as parameter more than once")] + pub(crate) span: Span, + pub(crate) identifier: Ident, +} + +#[derive(Diagnostic)] +#[diag("identifier `{$identifier}` is bound more than once in the same pattern", code = E0416)] +pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { + #[primary_span] + #[label("used in a pattern more than once")] + pub(crate) span: Span, + pub(crate) identifier: Ident, +} + +#[derive(Diagnostic)] +#[diag("use of undeclared label `{$name}`", code = E0426)] +pub(crate) struct UndeclaredLabel { + #[primary_span] + #[label("undeclared label `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) sub_reachable: Option, + #[subdiagnostic] + pub(crate) sub_reachable_suggestion: Option, + #[subdiagnostic] + pub(crate) sub_unreachable: Option, +} + +#[derive(Subdiagnostic)] +#[label("a label with a similar name is reachable")] +pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); + +#[derive(Subdiagnostic)] +#[suggestion( + "try using similarly named label", + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct TryUsingSimilarlyNamedLabel { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[label("a label with a similar name exists but is unreachable")] +pub(crate) struct UnreachableLabelWithSimilarNameExists { + #[primary_span] + pub(crate) ident_span: Span, +} + +#[derive(Diagnostic)] +#[diag("can't capture dynamic environment in a fn item", code = E0434)] +#[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] +pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("attempt to use a non-constant value in a constant", code = E0435)] +pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) with: Option>, + #[subdiagnostic] + pub(crate) with_label: Option, + #[subdiagnostic] + pub(crate) without: Option>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "consider using `{$suggestion}` instead of `{$current}`", + style = "verbose", + applicability = "has-placeholders" +)] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { + // #[primary_span] + #[suggestion_part(code = "{suggestion} ")] + pub(crate) span: Span, + pub(crate) suggestion: &'a str, + #[suggestion_part(code = ": /* Type */")] + pub(crate) type_span: Option, + pub(crate) current: &'a str, +} + +#[derive(Subdiagnostic)] +#[label("non-constant value")] +pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[label("this would need to be a `{$suggestion}`")] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { + #[primary_span] + pub(crate) ident_span: Span, + pub(crate) suggestion: &'a str, +} + +#[derive(Diagnostic)] +#[diag("{$shadowing_binding}s cannot shadow {$shadowed_binding}s", code = E0530)] +pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { + #[primary_span] + #[label("cannot be named the same as {$article} {$shadowed_binding}")] + pub(crate) span: Span, + pub(crate) shadowing_binding: PatternSource, + pub(crate) shadowed_binding: Res, + pub(crate) article: &'a str, + #[subdiagnostic] + pub(crate) sub_suggestion: Option, + #[label("the {$shadowed_binding} `{$name}` is {$participle} here")] + pub(crate) shadowed_binding_span: Span, + pub(crate) participle: &'a str, + pub(crate) name: Symbol, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "try specify the pattern arguments", + code = "{name}(..)", + applicability = "unspecified" +)] +pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("generic parameter defaults cannot reference parameters before they are declared", code = E0128)] +pub(crate) struct ForwardDeclaredGenericParam { + #[primary_span] + #[label("cannot reference `{$param}` before it is declared")] + pub(crate) span: Span, + pub(crate) param: Symbol, +} + +#[derive(Diagnostic)] +#[diag("const parameter types cannot reference parameters before they are declared")] +pub(crate) struct ForwardDeclaredGenericInConstParamTy { + #[primary_span] + #[label("const parameter type cannot reference `{$param}` before it is declared")] + pub(crate) span: Span, + pub(crate) param: Symbol, +} + +#[derive(Diagnostic)] +#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] +pub(crate) struct ParamInTyOfConstParam { + #[primary_span] + #[label("the type must not depend on the parameter `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] +pub(crate) struct SelfInGenericParamDefault { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("cannot use `Self` in const parameter type")] +pub(crate) struct SelfInConstGenericTy { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "{$is_gca -> + [true] generic parameters in const blocks are not allowed; use a named `const` item instead + *[false] generic parameters may not be used in const operations +}" +)] +pub(crate) struct ParamInNonTrivialAnonConst { + #[primary_span] + #[label("cannot perform const operation using `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) param_kind: ParamKindInNonTrivialAnonConst, + #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] + pub(crate) help: bool, + pub(crate) is_gca: bool, + #[help( + "consider factoring the expression into a `type const` item and use it as the const argument instead" + )] + pub(crate) help_gca: bool, } #[derive(Debug)] -pub(crate) struct TypoSuggestion { - pub candidate: Symbol, - /// The source location where the name is defined; None if the name is not defined - /// in source e.g. primitives - pub span: Option, - pub res: Res, - pub target: SuggestionTarget, -} - -impl TypoSuggestion { - pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { - Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } - } - pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { - Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } - } - pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { - Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } - } +#[derive(Subdiagnostic)] +pub(crate) enum ParamKindInNonTrivialAnonConst { + #[note("type parameters may not be used in const expressions")] + Type, + #[help("const parameters may only be used as standalone arguments here, i.e. `{$name}`")] + Const { name: Symbol }, + #[note("lifetime parameters may not be used in const expressions")] + Lifetime, } -/// A free importable items suggested in case of resolution failure. -#[derive(Debug, Clone)] -pub(crate) struct ImportSuggestion { - pub did: Option, - pub descr: &'static str, - pub path: Path, - pub accessible: bool, - // false if the path traverses a foreign `#[doc(hidden)]` item. - pub doc_visible: bool, - pub via_import: bool, - /// An extra note that should be issued if this item is suggested - pub note: Option, - pub is_stable: bool, -} - -/// Adjust the impl span so that just the `impl` keyword is taken by removing -/// everything after `<` (`"impl Iterator for A {}" -> "impl"`) and -/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). -/// -/// *Attention*: the method used is very fragile since it essentially duplicates the work of the -/// parser. If you need to use this function or something similar, please consider updating the -/// `source_map` functions and this function to something more robust. -fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { - let impl_span = sm.span_until_char(impl_span, '<'); - sm.span_until_whitespace(impl_span) -} - -impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { - self.tcx.dcx() - } +#[derive(Diagnostic)] +#[diag("use of unreachable label `{$name}`", code = E0767)] +#[note("labels are unreachable through functions, closures, async blocks and modules")] +pub(crate) struct UnreachableLabel { + #[primary_span] + #[label("unreachable label `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[label("unreachable label defined here")] + pub(crate) definition_span: Span, + #[subdiagnostic] + pub(crate) sub_suggestion: Option, + #[subdiagnostic] + pub(crate) sub_suggestion_label: Option, + #[subdiagnostic] + pub(crate) sub_unreachable_label: Option, +} - pub(crate) fn report_errors(&mut self, krate: &Crate) { - self.report_delayed_vis_resolution_errors(); - self.report_with_use_injections(krate); - - for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { - self.lint_buffer.buffer_lint( - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - CRATE_NODE_ID, - span_use, - errors::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }, - ); - } +#[derive(Subdiagnostic)] +#[suggestion( + "try using similarly named label", + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct UnreachableLabelSubSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} - for ambiguity_error in &self.ambiguity_errors { - let mut diag = self.ambiguity_diagnostic(ambiguity_error); - - if let Some(ambiguity_warning) = ambiguity_error.warning { - let node_id = match ambiguity_error.b1.0.kind { - DeclKind::Import { import, .. } => import.root_id, - DeclKind::Def(_) => CRATE_NODE_ID, - }; - - let lint = match ambiguity_warning { - _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, - AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, - AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, - }; - - self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); - } else { - diag.is_error = true; - self.dcx().emit_err(diag); - } - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name is reachable")] +pub(crate) struct UnreachableLabelSubLabel { + #[primary_span] + pub(crate) ident_span: Span, +} - let mut reported_spans = FxHashSet::default(); - for error in mem::take(&mut self.privacy_errors) { - if reported_spans.insert(error.dedup_span) { - self.report_privacy_error(&error); - } - } - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name exists but is also unreachable")] +pub(crate) struct UnreachableLabelSubLabelUnreachable { + #[primary_span] + pub(crate) ident_span: Span, +} - fn report_delayed_vis_resolution_errors(&mut self) { - for DelayedVisResolutionError { vis, parent_scope, error } in - mem::take(&mut self.delayed_vis_resolution_errors) - { - match self.try_resolve_visibility(&parent_scope, &vis, true) { - Ok(_) => self.report_vis_error(error), - Err(error) => self.report_vis_error(error), - }; - } - } +#[derive(Diagnostic)] +#[diag("invalid `sym` operand")] +#[help("`sym` operands must refer to either a function or a static")] +pub(crate) struct InvalidAsmSym { + #[primary_span] + #[label("is a local variable")] + pub(crate) span: Span, +} - fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in - mem::take(&mut self.use_injections) - { - let (span, found_use) = if node_id != DUMMY_NODE_ID { - UsePlacementFinder::check(krate, node_id) - } else { - (None, FoundUse::No) - }; - - if !candidates.is_empty() { - show_candidates( - self.tcx, - &mut err, - span, - &candidates, - if instead { Instead::Yes } else { Instead::No }, - found_use, - DiagMode::Normal, - path, - "", - ); - err.emit(); - } else if let Some((span, msg, sugg, appl)) = suggestion { - err.span_suggestion_verbose(span, msg, sugg, appl); - err.emit(); - } else if let [segment] = path.as_slice() - && is_call - { - err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); - } else { - err.emit(); - } - } - } +#[derive(Diagnostic)] +#[diag("attempt to use a non-constant value in a constant")] +pub(crate) struct LowercaseSelf { + #[primary_span] + #[suggestion( + "try using `Self`", + code = "Self", + applicability = "maybe-incorrect", + style = "short" + )] + pub(crate) span: Span, +} - pub(crate) fn report_conflict( - &mut self, - ident: IdentKey, - ns: Namespace, - old_binding: Decl<'ra>, - new_binding: Decl<'ra>, - ) { - // Error on the second of two conflicting names - if old_binding.span.lo() > new_binding.span.lo() { - return self.report_conflict(ident, ns, new_binding, old_binding); - } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag("never patterns cannot contain variable bindings")] +pub(crate) struct BindingInNeverPattern { + #[primary_span] + #[suggestion( + "use a wildcard `_` instead", + code = "_", + applicability = "machine-applicable", + style = "short" + )] + pub(crate) span: Span, +} - let container = match old_binding.parent_module.unwrap().expect_local().kind { - // Avoid using TyCtxt::def_kind_descr in the resolver, because it - // indirectly *calls* the resolver, and would cause a query cycle. - ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), - ModuleKind::Block => "block", - }; +#[derive(Diagnostic)] +#[diag("duplicate definitions with name `{$name}`:", code = E0201)] +pub(crate) struct TraitImplDuplicate { + #[primary_span] + #[label("duplicate definition")] + pub(crate) span: Span, + #[label("previous definition here")] + pub(crate) old_span: Span, + #[label("item in trait")] + pub(crate) trait_item_span: Span, + pub(crate) name: Ident, +} - let (name, span) = - (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); +#[derive(Diagnostic)] +#[diag("relative paths are not supported in visibilities in 2018 edition or later")] +pub(crate) struct Relative2018 { + #[primary_span] + pub(crate) span: Span, + #[suggestion("try", code = "crate::{path_str}", applicability = "maybe-incorrect")] + pub(crate) path_span: Span, + pub(crate) path_str: String, +} - if self.name_already_seen.get(&name) == Some(&span) { - return; - } +#[derive(Diagnostic)] +#[diag("visibilities can only be restricted to ancestor modules", code = E0742)] +pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("expected module, found {$res} `{$path_str}`", code = E0577)] +pub(crate) struct ExpectedModuleFound { + #[primary_span] + #[label("not a module")] + pub(crate) span: Span, + pub(crate) res: Res, + pub(crate) path_str: String, +} - let old_kind = match (ns, old_binding.res()) { - (ValueNS, _) => "value", - (MacroNS, _) => "macro", - (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", - (TypeNS, Res::Def(DefKind::Mod, _)) => "module", - (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", - (TypeNS, _) => "type", - }; - - let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { - (true, true) => E0259, - (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { - true => E0254, - false => E0260, - }, - _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { - (false, false) => E0428, - (true, true) => E0252, - _ => E0255, - }, - }; - - let label = match new_binding.is_import_user_facing() { - true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name }, - false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name }, - }; - - let old_binding_label = - (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { - let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); - match old_binding.is_import_user_facing() { - true => errors::NameDefinedMultipleTimeOldBindingLabel::Import { - span, - old_kind, - name, - }, - false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition { - span, - old_kind, - name, - }, - } - }); - - let mut err = self - .dcx() - .create_err(errors::NameDefinedMultipleTime { - span, - name, - descr: ns.descr(), - container, - label, - old_binding_label, - }) - .with_code(code); - - // See https://github.com/rust-lang/rust/issues/32354 - use DeclKind::Import; - let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| { - !binding.span.is_dummy() - && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) - }; - let import = match (&new_binding.kind, &old_binding.kind) { - // If there are two imports where one or both have attributes then prefer removing the - // import without attributes. - (Import { import: new, .. }, Import { import: old, .. }) - if { - (new.has_attributes || old.has_attributes) - && can_suggest(old_binding, *old) - && can_suggest(new_binding, *new) - } => - { - if old.has_attributes { - Some((*new, new_binding.span, true)) - } else { - Some((*old, old_binding.span, true)) - } - } - // Otherwise prioritize the new binding. - (Import { import, .. }, other) if can_suggest(new_binding, *import) => { - Some((*import, new_binding.span, other.is_import())) - } - (other, Import { import, .. }) if can_suggest(old_binding, *import) => { - Some((*import, old_binding.span, other.is_import())) - } - _ => None, - }; - - // Check if the target of the use for both bindings is the same. - let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); - let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); - let from_item = - self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); - // Only suggest removing an import if both bindings are to the same def, if both spans - // aren't dummy spans. Further, if both bindings are imports, then the ident must have - // been introduced by an item. - let should_remove_import = duplicate - && !has_dummy_span - && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); - - match import { - Some((import, span, true)) if should_remove_import && import.is_nested() => { - self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); - } - Some((import, _, true)) if should_remove_import && !import.is_glob() => { - // Simple case - remove the entire import. Due to the above match arm, this can - // only be a single use so just remove it entirely. - err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }); - } - Some((import, span, _)) => { - self.add_suggestion_for_rename_of_use(&mut err, name, import, span); - } - _ => {} - } +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the visibility", code = E0578)] +pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("trait implementation can only be restricted to ancestor modules")] +pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("cannot use a tool module through an import")] +pub(crate) struct ToolModuleImported { + #[primary_span] + pub(crate) span: Span, + #[note("the tool module imported here")] + pub(crate) import: Span, +} - err.emit(); - self.name_already_seen.insert(name, span); - } +#[derive(Diagnostic)] +#[diag("visibility must resolve to a module")] +pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("expected {$expected}, found {$found} `{$macro_path}`")] +pub(crate) struct MacroExpectedFound<'a> { + #[primary_span] + #[label("not {$article} {$expected}")] + pub(crate) span: Span, + pub(crate) found: &'a str, + pub(crate) article: &'static str, + pub(crate) expected: &'a str, + pub(crate) macro_path: &'a str, + #[subdiagnostic] + pub(crate) remove_surrounding_derive: Option, + #[subdiagnostic] + pub(crate) add_as_non_derive: Option>, +} - /// This function adds a suggestion to change the binding name of a new import that conflicts - /// with an existing import. - /// - /// ```text,ignore (diagnostic) - /// help: you can use `as` to change the binding name of the import - /// | - /// LL | use foo::bar as other_bar; - /// | ^^^^^^^^^^^^^^^^^^^^^ - /// ``` - fn add_suggestion_for_rename_of_use( - &self, - err: &mut Diag<'_>, - name: Symbol, - import: Import<'_>, - binding_span: Span, - ) { - let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { - format!("Other{name}") - } else { - format!("other_{name}") - }; - - let mut suggestion = None; - let mut span = binding_span; - match import.kind { - ImportKind::Single { source, .. } => { - if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) - && pos as usize <= snippet.len() - { - span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( - binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), - ); - suggestion = Some(format!(" as {suggested_name}")); - } - } - ImportKind::ExternCrate { source, target, .. } => { - suggestion = Some(format!( - "extern crate {} as {};", - source.unwrap_or(target.name), - suggested_name, - )) - } - _ => unreachable!(), - } +#[derive(Subdiagnostic)] +#[help("remove from the surrounding `derive()`")] +pub(crate) struct RemoveSurroundingDerive { + #[primary_span] + pub(crate) span: Span, +} - if let Some(suggestion) = suggestion { - err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion }); - } else { - err.subdiagnostic(ChangeImportBinding { span }); - } - } +#[derive(Subdiagnostic)] +#[help( + " + add as non-Derive macro + `#[{$macro_path}]`" +)] +pub(crate) struct AddAsNonDerive<'a> { + pub(crate) macro_path: &'a str, +} - /// This function adds a suggestion to remove an unnecessary binding from an import that is - /// nested. In the following example, this function will be invoked to remove the `a` binding - /// in the second use statement: - /// - /// ```ignore (diagnostic) - /// use issue_52891::a; - /// use issue_52891::{d, a, e}; - /// ``` - /// - /// The following suggestion will be added: - /// - /// ```ignore (diagnostic) - /// use issue_52891::{d, a, e}; - /// ^-- help: remove unnecessary import - /// ``` - /// - /// If the nested use contains only one import then the suggestion will remove the entire - /// line. - /// - /// It is expected that the provided import is nested - this isn't checked by the - /// function. If this invariant is not upheld, this function's behaviour will be unexpected - /// as characters expected by span manipulations won't be present. - fn add_suggestion_for_duplicate_nested_use( - &self, - err: &mut Diag<'_>, - import: Import<'_>, - binding_span: Span, - ) { - assert!(import.is_nested()); - - // Two examples will be used to illustrate the span manipulations we're doing: - // - // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, a, e};`. - // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, e, a};`. - - let (found_closing_brace, span) = - find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span); - - // If there was a closing brace then identify the span to remove any trailing commas from - // previous imports. - if found_closing_brace { - if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { - err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport { span }); - } else { - // Remove the entire line if we cannot extend the span back, this indicates an - // `issue_52891::{self}` case. - err.subdiagnostic(errors::RemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }); - } +#[derive(Diagnostic)] +#[diag("can't use a procedural macro from the same crate that defines it")] +pub(crate) struct ProcMacroSameCrate { + #[primary_span] + pub(crate) span: Span, + #[help("you can define integration tests in a directory named `tests`")] + pub(crate) is_test: bool, +} - return; - } +#[derive(Diagnostic)] +#[diag("cannot find {$ns_descr} `{$ident}` in this scope")] +pub(crate) struct ProcMacroDeriveResolutionFallback { + #[label("names from parent modules are not accessible without an explicit import")] + pub span: Span, + pub ns_descr: &'static str, + pub ident: Symbol, +} - err.subdiagnostic(errors::RemoveUnnecessaryImport { span }); - } +#[derive(Diagnostic)] +#[diag( + "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" +)] +pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note("the macro is defined here")] + pub definition: Span, +} - pub(crate) fn lint_if_path_starts_with_module( - &mut self, - finalize: Finalize, - path: &[Segment], - second_binding: Option>, - ) { - let Finalize { node_id, root_span, .. } = finalize; - - let first_name = match path.get(0) { - // In the 2018 edition this lint is a hard error, so nothing to do - Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => { - seg.ident.name - } - _ => return, - }; +#[derive(Diagnostic)] +#[diag("`#[macro_use]` is not supported on `extern crate self`")] +pub(crate) struct MacroUseExternCrateSelf { + #[primary_span] + pub(crate) span: Span, +} - // We're only interested in `use` paths which should start with - // `{{root}}` currently. - if first_name != kw::PathRoot { - return; - } +#[derive(Diagnostic)] +#[diag("not sure whether the path is accessible or not")] +#[note("the type may have associated items, but we are currently not checking them")] +pub(crate) struct CfgAccessibleUnsure { + #[primary_span] + pub(crate) span: Span, +} - match path.get(1) { - // If this import looks like `crate::...` it's already good - Some(Segment { ident, .. }) if ident.name == kw::Crate => return, - // Otherwise go below to see if it's an extern crate - Some(_) => {} - // If the path has length one (and it's `PathRoot` most likely) - // then we don't know whether we're gonna be importing a crate or an - // item in our crate. Defer this lint to elsewhere - None => return, - } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag("generic parameters may not be used in enum discriminant values")] +pub(crate) struct ParamInEnumDiscriminant { + #[primary_span] + #[label("cannot perform const operation using `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) param_kind: ParamKindInEnumDiscriminant, +} - // If the first element of our path was actually resolved to an - // `ExternCrate` (also used for `crate::...`) then no need to issue a - // warning, this looks all good! - if let Some(binding) = second_binding - && let DeclKind::Import { import, .. } = binding.kind - // Careful: we still want to rewrite paths from renamed extern crates. - && let ImportKind::ExternCrate { source: None, .. } = import.kind - { - return; - } +#[derive(Debug)] +#[derive(Subdiagnostic)] +pub(crate) enum ParamKindInEnumDiscriminant { + #[note("type parameters may not be used in enum discriminant values")] + Type, + #[note("const parameters may not be used in enum discriminant values")] + Const, + #[note("lifetime parameters may not be used in enum discriminant values")] + Lifetime, +} - self.lint_buffer.dyn_buffer_lint_any( - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - node_id, - root_span, - move |dcx, level, sess| { - let (replacement, applicability) = match sess - .downcast_ref::() - .expect("expected a `Session`") - .source_map() - .span_to_snippet(root_span) - { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - errors::AbsPathWithModule { - sugg: errors::AbsPathWithModuleSugg { - span: root_span, - applicability, - replacement, - }, - } - .into_diag(dcx, level) - }, - ); - } +#[derive(Subdiagnostic)] +#[label("you can use `as` to change the binding name of the import")] +pub(crate) struct ChangeImportBinding { + #[primary_span] + pub(crate) span: Span, +} - pub(crate) fn add_module_candidates( - &self, - module: Module<'ra>, - names: &mut Vec, - filter_fn: &impl Fn(Res) -> bool, - ctxt: Option, - ) { - module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { - let res = binding.res(); - if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { - names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); - } - }); - } +#[derive(Subdiagnostic)] +#[suggestion( + "you can use `as` to change the binding name of the import", + code = "{suggestion}", + applicability = "maybe-incorrect" +)] +pub(crate) struct ChangeImportBindingSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) suggestion: String, +} - /// Combines an error with provided span and emits it. - /// - /// This takes the error provided, combines it with the span and any additional spans inside the - /// error and emits it. - pub(crate) fn report_error( - &mut self, - span: Span, - resolution_error: ResolutionError<'ra>, - ) -> ErrorGuaranteed { - self.into_struct_error(span, resolution_error).emit() - } +#[derive(Diagnostic)] +#[diag("imports cannot refer to {$what}")] +pub(crate) struct ImportsCannotReferTo<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) what: &'a str, +} + +#[derive(Diagnostic)] +#[diag("cannot find {$expected} `{$ident}` in this scope")] +pub(crate) struct CannotFindIdentInThisScope<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) expected: &'a str, + pub(crate) ident: Ident, +} + +#[derive(Subdiagnostic)] +#[note("unsafe traits like `{$ident}` should be implemented explicitly")] +pub(crate) struct ExplicitUnsafeTraits { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Subdiagnostic)] +#[note("a macro with the same name exists, but it appears later")] +pub(crate) struct MacroDefinedLater { + #[primary_span] + pub(crate) span: Span, +} - pub(crate) fn into_struct_error( - &mut self, +#[derive(Subdiagnostic)] +#[label("consider moving the definition of `{$ident}` before this call")] +pub(crate) struct MacroSuggMovePosition { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Subdiagnostic)] +pub(crate) enum MacroRulesNot { + #[label("`{$ident}` exists, but has no rules for function-like invocation")] + Func { + #[primary_span] span: Span, - resolution_error: ResolutionError<'ra>, - ) -> Diag<'_> { - match resolution_error { - ResolutionError::GenericParamsFromOuterItem { - outer_res, - has_generic_params, - def_kind, - inner_item, - current_self_ty, - } => { - use errs::GenericParamsFromOuterItemLabel as Label; - let static_or_const = match def_kind { - DefKind::Static { .. } => { - Some(errs::GenericParamsFromOuterItemStaticOrConst::Static) - } - DefKind::Const { .. } => { - Some(errs::GenericParamsFromOuterItemStaticOrConst::Const) - } - _ => None, - }; - let is_self = - matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); - let mut err = errs::GenericParamsFromOuterItem { - span, - label: None, - refer_to_type_directly: None, - use_let: None, - sugg: None, - static_or_const, - is_self, - item: inner_item.as_ref().map(|(label_span, _, kind)| { - errs::GenericParamsFromOuterItemInnerItem { - span: *label_span, - descr: kind.descr().to_string(), - is_self, - } - }), - }; - - let sm = self.tcx.sess.source_map(); - // Note: do not early return for missing def_id here, - // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. - let def_id = match outer_res { - Res::SelfTyParam { .. } => { - err.label = Some(Label::SelfTyParam(span)); - None - } - Res::SelfTyAlias { alias_to: def_id, .. } => { - err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( - sm, - self.def_span(def_id), - ))); - err.refer_to_type_directly = - current_self_ty.map(|snippet| errs::UseTypeDirectly { span, snippet }); - None - } - Res::Def(DefKind::TyParam, def_id) => { - err.label = Some(Label::TyParam(self.def_span(def_id))); - Some(def_id) - } - Res::Def(DefKind::ConstParam, def_id) => { - err.label = Some(Label::ConstParam(self.def_span(def_id))); - Some(def_id) - } - _ => { - bug!( - "GenericParamsFromOuterItem should only be used with \ - Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ - DefKind::ConstParam" - ); - } - }; - - if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { - err.use_let = Some(errs::GenericParamsFromOuterItemUseLet { - span: sm.span_until_whitespace(*item_span), - }); - } - - if let Some(def_id) = def_id - && let HasGenericParams::Yes(span) = has_generic_params - && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) - { - let name = self.tcx.item_name(def_id); - let (span, snippet) = if span.is_empty() { - let snippet = format!("<{name}>"); - (span, snippet) - } else { - let span = sm.span_through_char(span, '<').shrink_to_hi(); - let snippet = format!("{name}, "); - (span, snippet) - }; - err.sugg = Some(errs::GenericParamsFromOuterItemSugg { span, snippet }); - } - - self.dcx().create_err(err) - } - ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => self - .dcx() - .create_err(errs::NameAlreadyUsedInParameterList { span, first_use_span, name }), - ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { - self.dcx().create_err(errs::MethodNotMemberOfTrait { - span, - method, - trait_, - sub: candidate.map(|c| errs::AssociatedFnWithSimilarNameExists { - span: method.span, - candidate: c, - }), - }) - } - ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { - self.dcx().create_err(errs::TypeNotMemberOfTrait { - span, - type_, - trait_, - sub: candidate.map(|c| errs::AssociatedTypeWithSimilarNameExists { - span: type_.span, - candidate: c, - }), - }) - } - ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { - self.dcx().create_err(errs::ConstNotMemberOfTrait { - span, - const_, - trait_, - sub: candidate.map(|c| errs::AssociatedConstWithSimilarNameExists { - span: const_.span, - candidate: c, - }), - }) - } - ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { - let BindingError { name, target, origin, could_be_path } = binding_error; - - let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); - target_sp.sort(); - target_sp.dedup(); - let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); - origin_sp.sort(); - origin_sp.dedup(); - - let msp = MultiSpan::from_spans(target_sp.clone()); - let mut err = self - .dcx() - .create_err(errors::VariableIsNotBoundInAllPatterns { multispan: msp, name }); - for sp in target_sp { - err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name }); - } - for sp in &origin_sp { - err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp }); - } - let mut suggested_typo = false; - if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) - && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) - { - // The check above is so that when we encounter `match foo { (a | b) => {} }`, - // we don't suggest `(a | a) => {}`, which would never be what the user wants. - let mut target_visitor = BindingVisitor::default(); - for pat in &target { - target_visitor.visit_pat(pat); - } - target_visitor.identifiers.sort(); - target_visitor.identifiers.dedup(); - let mut origin_visitor = BindingVisitor::default(); - for (_, pat) in &origin { - origin_visitor.visit_pat(pat); - } - origin_visitor.identifiers.sort(); - origin_visitor.identifiers.dedup(); - // Find if the binding could have been a typo - if let Some(typo) = - find_best_match_for_name(&target_visitor.identifiers, name.name, None) - && !origin_visitor.identifiers.contains(&typo) - { - err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo }); - suggested_typo = true; - } - } - if could_be_path { - let import_suggestions = self.lookup_import_candidates( - name, - Namespace::ValueNS, - &parent_scope, - &|res: Res| { - matches!( - res, - Res::Def( - DefKind::Ctor(CtorOf::Variant, CtorKind::Const) - | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) - | DefKind::Const { .. } - | DefKind::AssocConst { .. }, - _, - ) - ) - }, - ); - - if import_suggestions.is_empty() && !suggested_typo { - let kind_matches: [fn(DefKind) -> bool; 4] = [ - |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), - |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), - |kind| matches!(kind, DefKind::Const { .. }), - |kind| matches!(kind, DefKind::AssocConst { .. }), - ]; - let mut local_names = vec![]; - self.add_module_candidates( - parent_scope.module, - &mut local_names, - &|res| matches!(res, Res::Def(_, _)), - None, - ); - let local_names: FxHashSet<_> = local_names - .into_iter() - .filter_map(|s| match s.res { - Res::Def(_, def_id) => Some(def_id), - _ => None, - }) - .collect(); - - let mut local_suggestions = vec![]; - let mut suggestions = vec![]; - for matches_kind in kind_matches { - if let Some(suggestion) = self.early_lookup_typo_candidate( - ScopeSet::All(Namespace::ValueNS), - &parent_scope, - name, - &|res: Res| match res { - Res::Def(k, _) => matches_kind(k), - _ => false, - }, - ) && let Res::Def(kind, mut def_id) = suggestion.res - { - if let DefKind::Ctor(_, _) = kind { - def_id = self.tcx.parent(def_id); - } - let kind = kind.descr(def_id); - if local_names.contains(&def_id) { - // The item is available in the current scope. Very likely to - // be a typo. Don't use the full path. - local_suggestions.push(( - suggestion.candidate, - suggestion.candidate.to_string(), - kind, - )); - } else { - suggestions.push(( - suggestion.candidate, - self.def_path_str(def_id), - kind, - )); - } - } - } - let suggestions = if !local_suggestions.is_empty() { - // There is at least one item available in the current scope that is a - // likely typo. We only show those. - local_suggestions - } else { - suggestions - }; - for (name, sugg, kind) in suggestions { - err.span_suggestion_verbose( - span, - format!( - "you might have meant to use the similarly named {kind} `{name}`", - ), - sugg, - Applicability::MaybeIncorrect, - ); - suggested_typo = true; - } - } - if import_suggestions.is_empty() && !suggested_typo { - let help_msg = format!( - "if you meant to match on a unit struct, unit variant or a `const` \ - item, consider making the path in the pattern qualified: \ - `path::to::ModOrType::{name}`", - ); - err.span_help(span, help_msg); - } - show_candidates( - self.tcx, - &mut err, - Some(span), - &import_suggestions, - Instead::No, - FoundUse::Yes, - DiagMode::Pattern, - vec![], - "", - ); - } - err - } - ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { - self.dcx().create_err(errs::VariableBoundWithDifferentMode { - span, - first_binding_span, - variable_name, - }) - } - ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => self - .dcx() - .create_err(errs::IdentifierBoundMoreThanOnceInParameterList { span, identifier }), - ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => self - .dcx() - .create_err(errs::IdentifierBoundMoreThanOnceInSamePattern { span, identifier }), - ResolutionError::UndeclaredLabel { name, suggestion } => { - let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion - { - // A reachable label with a similar name exists. - Some((ident, true)) => ( - ( - Some(errs::LabelWithSimilarNameReachable(ident.span)), - Some(errs::TryUsingSimilarlyNamedLabel { - span, - ident_name: ident.name, - }), - ), - None, - ), - // An unreachable label with a similar name exists. - Some((ident, false)) => ( - (None, None), - Some(errs::UnreachableLabelWithSimilarNameExists { - ident_span: ident.span, - }), - ), - // No similarly-named labels exist. - None => ((None, None), None), - }; - self.dcx().create_err(errs::UndeclaredLabel { - span, - name, - sub_reachable, - sub_reachable_suggestion, - sub_unreachable, - }) - } - ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { - let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); - err.span_label(span, label); - - if let Some((suggestions, msg, applicability)) = suggestion { - if suggestions.is_empty() { - err.help(msg); - return err; - } - err.multipart_suggestion(msg, suggestions, applicability); - } - - let module = match module { - Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, - _ => CRATE_DEF_ID.to_def_id(), - }; - self.find_cfg_stripped(&mut err, &segment, module); - - err - } - ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { - self.dcx().create_err(errs::CannotCaptureDynamicEnvironmentInFnItem { span }) - } - ResolutionError::AttemptToUseNonConstantValueInConstant { - ident, - suggestion, - current, - type_span, - } => { - // let foo =... - // ^^^ given this Span - // ------- get this Span to have an applicable suggestion - - // edit: - // only do this if the const and usage of the non-constant value are on the same line - // the further the two are apart, the higher the chance of the suggestion being wrong - - let sp = self - .tcx - .sess - .source_map() - .span_extend_to_prev_str(ident.span, current, true, false); - - let (with, with_label, without) = match sp { - Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => { - let sp = sp - .with_lo(BytePos(sp.lo().0 - (current.len() as u32))) - .until(ident.span); - - // Only suggest replacing the binding keyword if this is a simple - // binding. - // - // Note: this approach still incorrectly suggests for irrefutable - // patterns like `if let x = 1 { const { x } }`, since the text - // between `let` and the identifier is just whitespace. - // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs - let is_simple_binding = - self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| { - let after_keyword = snippet[current.len()..].trim(); - after_keyword.is_empty() || after_keyword == "mut" - }); - - if is_simple_binding { - ( - Some(errs::AttemptToUseNonConstantValueInConstantWithSuggestion { - span: sp, - suggestion, - current, - type_span, - }), - Some(errs::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), - None, - ) - } else { - ( - None, - Some(errs::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), - None, - ) - } - } - _ => ( - None, - None, - Some(errs::AttemptToUseNonConstantValueInConstantWithoutSuggestion { - ident_span: ident.span, - suggestion, - }), - ), - }; - - self.dcx().create_err(errs::AttemptToUseNonConstantValueInConstant { - span, - with, - with_label, - without, - }) - } - ResolutionError::BindingShadowsSomethingUnacceptable { - shadowing_binding, - name, - participle, - article, - shadowed_binding, - shadowed_binding_span, - } => self.dcx().create_err(errs::BindingShadowsSomethingUnacceptable { - span, - shadowing_binding, - shadowed_binding, - article, - sub_suggestion: match (shadowing_binding, shadowed_binding) { - ( - PatternSource::Match, - Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), - ) => Some(errs::BindingShadowsSomethingUnacceptableSuggestion { span, name }), - _ => None, - }, - shadowed_binding_span, - participle, - name, - }), - ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason { - ForwardGenericParamBanReason::Default => { - self.dcx().create_err(errs::ForwardDeclaredGenericParam { param, span }) - } - ForwardGenericParamBanReason::ConstParamTy => self - .dcx() - .create_err(errs::ForwardDeclaredGenericInConstParamTy { param, span }), - }, - ResolutionError::ParamInTyOfConstParam { name } => { - self.dcx().create_err(errs::ParamInTyOfConstParam { span, name }) - } - ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { - self.dcx().create_err(errs::ParamInNonTrivialAnonConst { - span, - name, - param_kind: is_type, - help: self.tcx.sess.is_nightly_build(), - is_gca, - help_gca: is_gca, - }) - } - ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => self - .dcx() - .create_err(errs::ParamInEnumDiscriminant { span, name, param_kind: is_type }), - ResolutionError::ForwardDeclaredSelf(reason) => match reason { - ForwardGenericParamBanReason::Default => { - self.dcx().create_err(errs::SelfInGenericParamDefault { span }) - } - ForwardGenericParamBanReason::ConstParamTy => { - self.dcx().create_err(errs::SelfInConstGenericTy { span }) - } - }, - ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { - let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = - match suggestion { - // A reachable label with a similar name exists. - Some((ident, true)) => ( - ( - Some(errs::UnreachableLabelSubLabel { ident_span: ident.span }), - Some(errs::UnreachableLabelSubSuggestion { - span, - // intentionally taking 'ident.name' instead of 'ident' itself, as this - // could be used in suggestion context - ident_name: ident.name, - }), - ), - None, - ), - // An unreachable label with a similar name exists. - Some((ident, false)) => ( - (None, None), - Some(errs::UnreachableLabelSubLabelUnreachable { - ident_span: ident.span, - }), - ), - // No similarly-named labels exist. - None => ((None, None), None), - }; - self.dcx().create_err(errs::UnreachableLabel { - span, - name, - definition_span, - sub_suggestion, - sub_suggestion_label, - sub_unreachable_label, - }) - } - ResolutionError::TraitImplMismatch { - name, - kind, - code, - trait_item_span, - trait_path, - } => self - .dcx() - .create_err(errors::TraitImplMismatch { - span, - name, - kind, - trait_path, - trait_item_span, - }) - .with_code(code), - ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self - .dcx() - .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }), - ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }), - ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }), - ResolutionError::BindingInNeverPattern => { - self.dcx().create_err(errs::BindingInNeverPattern { span }) - } - } - } + ident: Ident, + }, + #[label("`{$ident}` exists, but has no `attr` rules")] + Attr { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("`{$ident}` exists, but has no `derive` rules")] + Derive { + #[primary_span] + span: Span, + ident: Ident, + }, +} - pub(crate) fn report_vis_error( - &mut self, - vis_resolution_error: VisResolutionError, - ) -> ErrorGuaranteed { - match vis_resolution_error { - VisResolutionError::Relative2018(span, path) => { - self.dcx().create_err(errs::Relative2018 { - span, - path_span: path.span, - // intentionally converting to String, as the text would also be used as - // in suggestion context - path_str: pprust::path_to_string(&path), - }) - } - VisResolutionError::AncestorOnly(span) => { - self.dcx().create_err(errs::AncestorOnly(span)) - } - VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self - .into_struct_error( - span, - ResolutionError::FailedToResolve { - segment, - label, - suggestion, - module: None, - message, - }, - ), - VisResolutionError::ExpectedFound(span, path_str, res) => { - self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str }) - } - VisResolutionError::Indeterminate(span) => { - self.dcx().create_err(errs::Indeterminate(span)) - } - VisResolutionError::ModuleOnly(span) => self.dcx().create_err(errs::ModuleOnly(span)), - } - .emit() - } +#[derive(Subdiagnostic)] +#[note("maybe you have forgotten to define a name for this `macro_rules!`")] +pub(crate) struct MaybeMissingMacroRulesName { + #[primary_span] + pub(crate) spans: MultiSpan, +} - pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String { - // We can't use `def_path_str` in resolve. - let mut path = vec![def_id]; - while let Some(parent) = self.tcx.opt_parent(def_id) { - def_id = parent; - path.push(def_id); - if def_id.is_top_level_module() { - break; - } - } - // We will only suggest importing directly if it is accessible through that path. - path.into_iter() - .rev() - .map(|def_id| { - self.tcx - .opt_item_name(def_id) - .map(|name| { - match ( - def_id.is_top_level_module(), - def_id.is_local(), - self.tcx.sess.edition(), - ) { - (true, true, Edition::Edition2015) => String::new(), - (true, true, _) => kw::Crate.to_string(), - (true, false, _) | (false, _, _) => name.to_string(), - } - }) - .unwrap_or_else(|| "_".to_string()) - }) - .collect::>() - .join("::") - } +#[derive(Subdiagnostic)] +#[help("have you added the `#[macro_use]` on the module/import?")] +pub(crate) struct AddedMacroUse; - pub(crate) fn add_scope_set_candidates( - &mut self, - suggestions: &mut Vec, - scope_set: ScopeSet<'ra>, - ps: &ParentScope<'ra>, - sp: Span, - filter_fn: &impl Fn(Res) -> bool, - ) { - let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); - self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { - match scope { - Scope::DeriveHelpers(expn_id) => { - let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); - if filter_fn(res) { - suggestions.extend( - this.helper_attrs.get(&expn_id).into_iter().flatten().map( - |&(ident, orig_ident_span, _)| { - TypoSuggestion::new(ident.name, orig_ident_span, res) - }, - ), - ); - } - } - Scope::DeriveHelpersCompat => { - // Never recommend deprecated helper attributes. - } - Scope::MacroRules(macro_rules_scope) => { - if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { - let res = macro_rules_def.decl.res(); - if filter_fn(res) { - suggestions.push(TypoSuggestion::new( - macro_rules_def.ident.name, - macro_rules_def.orig_ident_span, - res, - )) - } - } - } - Scope::ModuleNonGlobs(module, _) => { - this.add_module_candidates(module, suggestions, filter_fn, None); - } - Scope::ModuleGlobs(..) => { - // Already handled in `ModuleNonGlobs`. - } - Scope::MacroUsePrelude => { - suggestions.extend(this.macro_use_prelude.iter().filter_map( - |(name, binding)| { - let res = binding.res(); - filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) - }, - )); - } - Scope::BuiltinAttrs => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy)); - if filter_fn(res) { - suggestions.extend( - BUILTIN_ATTRIBUTES - .iter() - // These trace attributes are compiler-generated and have - // deliberately invalid names. - .filter(|attr| { - !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) - }) - .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), - ); - } - } - Scope::ExternPreludeItems => { - // Add idents from both item and flag scopes. - suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { - let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); - filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) - })); - } - Scope::ExternPreludeFlags => {} - Scope::ToolPrelude => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); - suggestions.extend( - this.registered_tools - .iter() - .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), - ); - } - Scope::StdLibPrelude => { - if let Some(prelude) = this.prelude { - let mut tmp_suggestions = Vec::new(); - this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); - suggestions.extend( - tmp_suggestions - .into_iter() - .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)), - ); - } - } - Scope::BuiltinTypes => { - suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { - let res = Res::PrimTy(*prim_ty); - filter_fn(res) - .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) - })) - } - } +#[derive(Subdiagnostic)] +#[suggestion("consider adding a derive", code = "{suggestion}", applicability = "maybe-incorrect")] +pub(crate) struct ConsiderAddingADerive { + #[primary_span] + pub(crate) span: Span, + pub(crate) suggestion: String, +} - ControlFlow::<()>::Continue(()) - }); - } +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the import")] +pub(crate) struct CannotDetermineImportResolution { + #[primary_span] + pub(crate) span: Span, +} - /// Lookup typo candidate in scope for a macro or import. - fn early_lookup_typo_candidate( - &mut self, - scope_set: ScopeSet<'ra>, - parent_scope: &ParentScope<'ra>, - ident: Ident, - filter_fn: &impl Fn(Res) -> bool, - ) -> Option { - let mut suggestions = Vec::new(); - self.add_scope_set_candidates( - &mut suggestions, - scope_set, - parent_scope, - ident.span, - filter_fn, - ); - - // Make sure error reporting is deterministic. - suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); - - match find_best_match_for_name( - &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), - ident.name, - None, - ) { - Some(found) if found != ident.name => { - suggestions.into_iter().find(|suggestion| suggestion.candidate == found) - } - _ => None, - } - } +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the {$kind} `{$path}`")] +#[note("import resolution is stuck, try simplifying macro imports")] +pub(crate) struct CannotDetermineMacroResolution { + #[primary_span] + pub(crate) span: Span, + pub(crate) kind: &'static str, + pub(crate) path: String, +} - fn lookup_import_candidates_from_module( - &self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - start_module: Module<'ra>, - crate_path: ThinVec, - filter_fn: FilterFn, - ) -> Vec - where - FilterFn: Fn(Res) -> bool, - { - let mut candidates = Vec::new(); - let mut seen_modules = FxHashSet::default(); - let start_did = start_module.def_id(); - let mut worklist = vec![( - start_module, - ThinVec::::new(), - true, - start_did.is_local() || !self.tcx.is_doc_hidden(start_did), - true, - )]; - let mut worklist_via_import = vec![]; - - while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) = - match worklist.pop() { - None => worklist_via_import.pop(), - Some(x) => Some(x), - } - { - let in_module_is_extern = !in_module.def_id().is_local(); - in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { - // Avoid non-importable candidates. - if name_binding.is_assoc_item() - && !this.tcx.features().import_trait_associated_functions() - { - return; - } - - if ident.name == kw::Underscore { - return; - } - - let child_accessible = - accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); - - // do not venture inside inaccessible items of other crates - if in_module_is_extern && !child_accessible { - return; - } - - let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); - - // There is an assumption elsewhere that paths of variants are in the enum's - // declaration and not imported. With this assumption, the variant component is - // chopped and the rest of the path is assumed to be the enum's own path. For - // errors where a variant is used as the type instead of the enum, this causes - // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. - if via_import && name_binding.is_possibly_imported_variant() { - return; - } - - // #90113: Do not count an inaccessible reexported item as a candidate. - if let DeclKind::Import { source_decl, .. } = name_binding.kind - && this.is_accessible_from(source_decl.vis(), parent_scope.module) - && !this.is_accessible_from(name_binding.vis(), parent_scope.module) - { - return; - } - - let res = name_binding.res(); - let did = match res { - Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), - _ => res.opt_def_id(), - }; - let child_doc_visible = doc_visible - && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); - - // collect results based on the filter function - // avoid suggesting anything from the same module in which we are resolving - // avoid suggesting anything with a hygienic name - if ident.name == lookup_ident.name - && ns == namespace - && in_module != parent_scope.module - && ident.ctxt.is_root() - && filter_fn(res) - { - // create the path - let mut segms = if lookup_ident.span.at_least_rust_2018() { - // crate-local absolute paths start with `crate::` in edition 2018 - // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) - crate_path.clone() - } else { - ThinVec::new() - }; - segms.append(&mut path_segments.clone()); - - segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - let path = Path { span: name_binding.span, segments: segms, tokens: None }; - - if child_accessible - // Remove invisible match if exists - && let Some(idx) = candidates - .iter() - .position(|v: &ImportSuggestion| v.did == did && !v.accessible) - { - candidates.remove(idx); - } - - let is_stable = if is_stable - && let Some(did) = did - && this.is_stable(did, path.span) - { - true - } else { - false - }; - - // Rreplace unstable suggestions if we meet a new stable one, - // and do nothing if any other situation. For example, if we - // meet `std::ops::Range` after `std::range::legacy::Range`, - // we will remove the latter and then insert the former. - if is_stable - && let Some(idx) = candidates - .iter() - .position(|v: &ImportSuggestion| v.did == did && !v.is_stable) - { - candidates.remove(idx); - } - - if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { - // See if we're recommending TryFrom, TryInto, or FromIterator and add - // a note about editions - let note = if let Some(did) = did { - let requires_note = !did.is_local() - && find_attr!( - this.tcx, - did, - RustcDiagnosticItem( - sym::TryInto | sym::TryFrom | sym::FromIterator - ) - ); - requires_note.then(|| { - format!( - "'{}' is included in the prelude starting in Edition 2021", - path_names_to_string(&path) - ) - }) - } else { - None - }; - - candidates.push(ImportSuggestion { - did, - descr: res.descr(), - path, - accessible: child_accessible, - doc_visible: child_doc_visible, - note, - via_import, - is_stable, - }); - } - } - - // collect submodules to explore - if let Some(def_id) = name_binding.res().module_like_def_id() { - // form the path - let mut path_segments = path_segments.clone(); - path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - - let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind - && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind - && import.parent_scope.expansion == parent_scope.expansion - { - true - } else { - false - }; - - let is_extern_crate_that_also_appears_in_prelude = - name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); - - if !is_extern_crate_that_also_appears_in_prelude || alias_import { - // add the module to the lookup - if seen_modules.insert(def_id) { - if via_import { &mut worklist_via_import } else { &mut worklist }.push( - ( - this.expect_module(def_id), - path_segments, - child_accessible, - child_doc_visible, - is_stable && this.is_stable(def_id, name_binding.span), - ), - ); - } - } - } - }) - } +#[derive(Diagnostic)] +#[diag("`{$ident}` is private, and cannot be re-exported", code = E0364)] +pub(crate) struct CannotBeReexportedPrivate { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - candidates - } +#[derive(Diagnostic)] +#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0364)] +pub(crate) struct CannotBeReexportedCratePublic { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - fn is_stable(&self, did: DefId, span: Span) -> bool { - if did.is_local() { - return true; - } +#[derive(Diagnostic)] +#[diag("`{$ident}` is private, and cannot be re-exported", code = E0365)] +#[note("consider declaring type or module `{$ident}` with `pub`")] +pub(crate) struct CannotBeReexportedPrivateNS { + #[primary_span] + #[label("re-export of private `{$ident}`")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - match self.tcx.lookup_stability(did) { - Some(Stability { - level: StabilityLevel::Unstable { implied_by, .. }, feature, .. - }) => { - if span.allows_unstable(feature) { - true - } else if self.tcx.features().enabled(feature) { - true - } else if let Some(implied_by) = implied_by - && self.tcx.features().enabled(implied_by) - { - true - } else { - false - } - } - Some(_) => true, - None => false, - } - } +#[derive(Diagnostic)] +#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0365)] +#[note("consider declaring type or module `{$ident}` with `pub`")] +pub(crate) struct CannotBeReexportedCratePublicNS { + #[primary_span] + #[label("re-export of crate public `{$ident}`")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - /// When name resolution fails, this method can be used to look up candidate - /// entities with the expected name. It allows filtering them using the - /// supplied predicate (which should be used to only accept the types of - /// definitions expected, e.g., traits). The lookup spans across all crates. - /// - /// N.B., the method does not look into imports, but this is not a problem, - /// since we report the definitions (thus, the de-aliased imports). - pub(crate) fn lookup_import_candidates( - &mut self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - filter_fn: FilterFn, - ) -> Vec - where - FilterFn: Fn(Res) -> bool, - { - let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; - let mut suggestions = self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - self.graph_root.to_module(), - crate_path, - &filter_fn, - ); - - if lookup_ident.span.at_least_rust_2018() { - for (ident, entry) in &self.extern_prelude { - if entry.span().from_expansion() { - // Idents are adjusted to the root context before being - // resolved in the extern prelude, so reporting this to the - // user is no help. This skips the injected - // `extern crate std` in the 2018 edition, which would - // otherwise cause duplicate suggestions. - continue; - } - let Some(crate_id) = - self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) - else { - continue; - }; - - let crate_def_id = crate_id.as_def_id(); - let crate_root = self.expect_module(crate_def_id); - - // Check if there's already an item in scope with the same name as the crate. - // If so, we have to disambiguate the potential import suggestions by making - // the paths *global* (i.e., by prefixing them with `::`). - let needs_disambiguation = - self.resolutions(parent_scope.module).borrow().iter().any( - |(key, name_resolution)| { - if key.ns == TypeNS - && key.ident == *ident - && let Some(decl) = name_resolution.borrow().best_decl() - { - match decl.res() { - // No disambiguation needed if the identically named item we - // found in scope actually refers to the crate in question. - Res::Def(_, def_id) => def_id != crate_def_id, - Res::PrimTy(_) => true, - _ => false, - } - } else { - false - } - }, - ); - let mut crate_path = ThinVec::new(); - if needs_disambiguation { - crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); - } - crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); - - suggestions.extend(self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - crate_root, - crate_path, - &filter_fn, - )); - } - } +#[derive(Diagnostic)] +#[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] +pub(crate) struct PrivateExternCrateReexport { + pub ident: Ident, + #[suggestion( + "consider making the `extern crate` item publicly accessible", + code = "pub ", + style = "verbose", + applicability = "maybe-incorrect" + )] + pub sugg: Span, +} - suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build()); - suggestions - } +#[derive(Subdiagnostic)] +#[help("consider adding a `#[macro_export]` to the macro in the imported module")] +pub(crate) struct ConsiderAddingMacroExport { + #[primary_span] + pub(crate) span: Span, +} - pub(crate) fn unresolved_macro_suggestions( - &mut self, - err: &mut Diag<'_>, - macro_kind: MacroKind, - parent_scope: &ParentScope<'ra>, - ident: Ident, - krate: &Crate, - sugg_span: Option, - ) { - // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for - // suggestions. - self.register_macros_for_all_crates(); - - let is_expected = - &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); - let suggestion = self.early_lookup_typo_candidate( - ScopeSet::Macro(macro_kind), - parent_scope, - ident, - is_expected, - ); - if !self.add_typo_suggestion(err, suggestion, ident.span) { - self.detect_derive_attribute(err, ident, parent_scope, sugg_span); - } +#[derive(Subdiagnostic)] +#[suggestion( + "in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`", + code = "pub(crate)", + applicability = "maybe-incorrect" +)] +pub(crate) struct ConsiderMarkingAsPubCrate { + #[primary_span] + pub(crate) vis_span: Span, +} - let import_suggestions = - self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); - let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() { - DUMMY_NODE_ID => (None, FoundUse::No), - node_id => UsePlacementFinder::check(krate, node_id), - }; - show_candidates( - self.tcx, - err, - span, - &import_suggestions, - Instead::No, - found_use, - DiagMode::Normal, - vec![], - "", - ); - - if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { - let label_span = ident.span.shrink_to_hi(); - let mut spans = MultiSpan::from_span(label_span); - spans.push_span_label(label_span, "put a macro name here"); - err.subdiagnostic(MaybeMissingMacroRulesName { spans }); - return; - } +#[derive(Subdiagnostic)] +#[note("consider marking `{$ident}` as `pub` in the imported module")] +pub(crate) struct ConsiderMarkingAsPub { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { - err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); - return; - } +#[derive(Diagnostic)] +#[diag("cannot glob-import all possible crates")] +pub(crate) struct CannotGlobImportAllCrates { + #[primary_span] + pub(crate) span: Span, +} - let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { - if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } - }); - - if let Some((def_id, unused_ident)) = unused_macro { - let scope = self.local_macro_def_scopes[&def_id]; - let parent_nearest = parent_scope.module.nearest_parent_mod(); - let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds(); - if !unused_macro_kinds.contains(macro_kind.into()) { - match macro_kind { - MacroKind::Bang => { - err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident }); - } - MacroKind::Attr => { - err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident }); - } - MacroKind::Derive => { - err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident }); - } - } - return; - } - if Some(parent_nearest) == scope.opt_def_id() { - err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); - err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); - return; - } - } +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to write a const parameter here", + code = "const ", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyToConstParamSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, +} - if ident.name == kw::Default - && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind - { - let span = self.def_span(def_id); - let source_map = self.tcx.sess.source_map(); - let head_span = source_map.guess_head_span(span); - err.subdiagnostic(ConsiderAddingADerive { - span: head_span.shrink_to_lo(), - suggestion: "#[derive(Default)]\n".to_string(), - }); - } - for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { - let Ok(binding) = self.cm().resolve_ident_in_scope_set( - ident, - ScopeSet::All(ns), - parent_scope, - None, - None, - None, - ) else { - continue; - }; - - let desc = match binding.res() { - Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { - "a function-like macro".to_string() - } - Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { - format!("an attribute: `#[{ident}]`") - } - Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { - format!("a derive macro: `#[derive({ident})]`") - } - Res::Def(DefKind::Macro(kinds), _) => { - format!("{} {}", kinds.article(), kinds.descr()) - } - Res::ToolMod | Res::OpenMod(..) => { - // Don't confuse the user with tool modules or open modules. - continue; - } - Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { - "only a trait, without a derive macro".to_string() - } - res => format!( - "{} {}, not {} {}", - res.article(), - res.descr(), - macro_kind.article(), - macro_kind.descr_expected(), - ), - }; - if let crate::DeclKind::Import { import, .. } = binding.kind - && !import.span.is_dummy() - { - let note = errors::IdentImporterHereButItIsDesc { - span: import.span, - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - // Silence the 'unused import' warning we might get, - // since this diagnostic already covers that import. - self.record_use(ident, binding, Used::Other); - return; - } - let note = errors::IdentInScopeButItIsDesc { - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - return; - } +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", + code = "{snippet}", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct UnexpectedMissingConstParameter { + #[primary_span] + pub span: Span, + pub snippet: String, + pub item_name: String, + pub item_location: String, +} - if self.macro_names.contains(&IdentKey::new(ident)) { - err.subdiagnostic(AddedMacroUse); - return; - } - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "you might have meant to write a const parameter here", + applicability = "has-placeholders", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyParamToConstParamSugg { + #[suggestion_part(code = "const ")] + pub before: Span, + #[suggestion_part(code = ": /* Type */")] + pub after: Span, +} - /// Given an attribute macro that failed to be resolved, look for `derive` macros that could - /// provide it, either as-is or with small typos. - fn detect_derive_attribute( - &self, - err: &mut Diag<'_>, - ident: Ident, - parent_scope: &ParentScope<'ra>, - sugg_span: Option, - ) { - // Find all of the `derive`s in scope and collect their corresponding declared - // attributes. - // FIXME: this only works if the crate that owns the macro that has the helper_attr - // has already been imported. - let mut derives = vec![]; - let mut all_attrs: UnordMap> = UnordMap::default(); - // We're collecting these in a hashmap, and handle ordering the output further down. - #[allow(rustc::potential_query_instability)] - for (def_id, ext) in self - .local_macro_map - .iter() - .map(|(local_id, ext)| (local_id.to_def_id(), ext)) - .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) - { - for helper_attr in &ext.helper_attrs { - let item_name = self.tcx.item_name(def_id); - all_attrs.entry(*helper_attr).or_default().push(item_name); - if helper_attr == &ident.name { - derives.push(item_name); - } - } - } - let kind = MacroKind::Derive.descr(); - if !derives.is_empty() { - // We found an exact match for the missing attribute in a `derive` macro. Suggest it. - let mut derives: Vec = derives.into_iter().map(|d| d.to_string()).collect(); - derives.sort(); - derives.dedup(); - let msg = match &derives[..] { - [derive] => format!(" `{derive}`"), - [start @ .., last] => format!( - "s {} and `{last}`", - start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") - ), - [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), - }; - let msg = format!( - "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ - missing a `derive` attribute", - ident.name, - ); - let sugg_span = - if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind { - let span = self.def_span(id); - if span.from_expansion() { - None - } else { - // For enum variants sugg_span is empty but we can get the enum's Span. - Some(span.shrink_to_lo()) - } - } else { - // For items this `Span` will be populated, everything else it'll be None. - sugg_span - }; - match sugg_span { - Some(span) => { - err.span_suggestion_verbose( - span, - msg, - format!("#[derive({})]\n", derives.join(", ")), - Applicability::MaybeIncorrect, - ); - } - None => { - err.note(msg); - } - } - } else { - // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. - let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); - if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) - && let Some(macros) = all_attrs.get(&best_match) - { - let mut macros: Vec = macros.into_iter().map(|d| d.to_string()).collect(); - macros.sort(); - macros.dedup(); - let msg = match ¯os[..] { - [] => return, - [name] => format!(" `{name}` accepts"), - [start @ .., end] => format!( - "s {} and `{end}` accept", - start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), - ), - }; - let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); - err.span_suggestion_verbose( - ident.span, - msg, - best_match, - Applicability::MaybeIncorrect, - ); - } - } - } +#[derive(Subdiagnostic)] +#[suggestion( + "if you meant to collect the rest of the slice in `{$ident}`, use the at operator", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { + #[primary_span] + pub span: Span, + pub ident: Ident, + pub snippet: String, +} - pub(crate) fn add_typo_suggestion( - &self, - err: &mut Diag<'_>, - suggestion: Option, - span: Span, - ) -> bool { - let suggestion = match suggestion { - None => return false, - // We shouldn't suggest underscore. - Some(suggestion) if suggestion.candidate == kw::Underscore => return false, - Some(suggestion) => suggestion, - }; - - let mut did_label_def_span = false; - - if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { - if span.overlaps(def_span) { - // Don't suggest typo suggestion for itself like in the following: - // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` - // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:14 - // | - // LL | struct X {} - // | ----------- `X` defined here - // LL | - // LL | const Y: X = X("ö"); - // | -------------^^^^^^- similarly named constant `Y` defined here - // | - // help: use struct literal syntax instead - // | - // LL | const Y: X = X {}; - // | ^^^^ - // help: a constant with a similar name exists - // | - // LL | const Y: X = Y("ö"); - // | ^ - return false; - } - let span = self.tcx.sess.source_map().guess_head_span(def_span); - let candidate_descr = suggestion.res.descr(); - let candidate = suggestion.candidate; - let label = match suggestion.target { - SuggestionTarget::SimilarlyNamed => { - errors::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } - } - SuggestionTarget::SingleItem => { - errors::DefinedHere::SingleItem { span, candidate_descr, candidate } - } - }; - did_label_def_span = true; - err.subdiagnostic(label); - } +#[derive(Diagnostic)] +#[diag("an `extern crate` loading macros must be at the crate root", code = E0468)] +pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { + #[primary_span] + pub(crate) span: Span, +} - let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && let Some(span) = suggestion.span - && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') - && snippet == candidate - { - let candidate = suggestion.candidate; - // When the suggested binding change would be from `x` to `_x`, suggest changing the - // original binding definition instead. (#60164) - let msg = format!( - "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" - ); - if !did_label_def_span { - err.span_label(span, format!("`{candidate}` defined here")); - } - (span, msg, snippet) - } else { - let msg = match suggestion.target { - SuggestionTarget::SimilarlyNamed => format!( - "{} {} with a similar name exists", - suggestion.res.article(), - suggestion.res.descr() - ), - SuggestionTarget::SingleItem => { - format!("maybe you meant this {}", suggestion.res.descr()) - } - }; - (span, msg, suggestion.candidate.to_ident_string()) - }; - err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); - true - } +#[derive(Diagnostic)] +#[diag("`extern crate self;` requires renaming")] +pub(crate) struct ExternCrateSelfRequiresRenaming { + #[primary_span] + #[suggestion( + "rename the `self` crate to be able to import it", + code = "extern crate self as name;", + applicability = "has-placeholders" + )] + pub(crate) span: Span, +} - fn decl_description(&self, b: Decl<'_>, ident: Ident, scope: Scope<'_>) -> String { - let res = b.res(); - if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) { - let (built_in, from) = match scope { - Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"), - Scope::ExternPreludeFlags - if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() - || matches!(res, Res::OpenMod(..)) => - { - ("", " passed with `--extern`") - } - _ => { - if matches!(res, Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod) { - // These already contain the "built-in" prefix or look bad with it. - ("", "") - } else { - (" built-in", "") - } - } - }; - - let a = if built_in.is_empty() { res.article() } else { "a" }; - format!("{a}{built_in} {thing}{from}", thing = res.descr()) - } else { - let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; - format!("the {thing} {introduced} here", thing = res.descr()) - } - } +#[derive(Diagnostic)] +#[diag("`{$name}` is already in scope")] +#[note("macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)")] +pub(crate) struct MacroUseNameAlreadyInUse { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} - fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors::Ambiguity { - let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = - *ambiguity_error; - let extern_prelude_ambiguity = || { - // Note: b1 may come from a module scope, as an extern crate item in module. - matches!(scope2, Scope::ExternPreludeFlags) - && self - .extern_prelude - .get(&IdentKey::new(ident)) - .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) - }; - let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { - // We have to print the span-less alternative first, otherwise formatting looks bad. - (b2, b1, scope2, scope1, true) - } else { - (b1, b2, scope1, scope2, false) - }; - - let could_refer_to = |b: Decl<'_>, scope: Scope<'ra>, also: &str| { - let what = self.decl_description(b, ident, scope); - let note_msg = format!("`{ident}` could{also} refer to {what}"); - - let thing = b.res().descr(); - let mut help_msgs = Vec::new(); - if b.is_glob_import() - && (kind == AmbiguityKind::GlobVsGlob - || kind == AmbiguityKind::GlobVsExpanded - || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) - { - help_msgs.push(format!( - "consider adding an explicit import of `{ident}` to disambiguate" - )) - } - if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity() - { - help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) - } +#[derive(Diagnostic)] +#[diag("imported macro not found", code = E0469)] +pub(crate) struct ImportedMacroNotFound { + #[primary_span] + pub(crate) span: Span, +} - if kind != AmbiguityKind::GlobVsGlob { - if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { - if module == self.graph_root.to_module() { - help_msgs.push(format!( - "use `crate::{ident}` to refer to this {thing} unambiguously" - )); - } else if module.is_normal() { - help_msgs.push(format!( - "use `self::{ident}` to refer to this {thing} unambiguously" - )); - } - } - } +#[derive(Diagnostic)] +#[diag("`#[macro_escape]` is a deprecated synonym for `#[macro_use]`")] +pub(crate) struct MacroExternDeprecated { + #[primary_span] + pub(crate) span: Span, + #[help("try an outer attribute: `#[macro_use]`")] + pub inner_attribute: bool, +} - ( - Spanned { node: note_msg, span: b.span }, - help_msgs - .iter() - .enumerate() - .map(|(i, help_msg)| { - let or = if i == 0 { "" } else { "or " }; - format!("{or}{help_msg}") - }) - .collect::>(), - ) - }; - let (b1_note, b1_help_msgs) = could_refer_to(b1, scope1, ""); - let (b2_note, b2_help_msgs) = could_refer_to(b2, scope2, " also"); - let help = if kind == AmbiguityKind::GlobVsGlob - && b1 - .parent_module - .and_then(|m| m.opt_def_id()) - .map(|d| !d.is_local()) - .unwrap_or_default() - { - Some(&[ - "consider updating this dependency to resolve this error", - "if updating the dependency does not resolve the problem report the problem to the author of the relevant crate", - ] as &[_]) - } else { - None - }; - - let ambig_vis = ambig_vis.map(|(vis1, vis2)| { - format!( - "{} or {}", - vis1.to_string(CRATE_DEF_ID, self.tcx), - vis2.to_string(CRATE_DEF_ID, self.tcx) - ) - }); - - errors::Ambiguity { - ident, - help, - ambig_vis, - kind: kind.descr(), - b1_note, - b1_help_msgs, - b2_note, - b2_help_msgs, - is_error: false, - } - } +#[derive(Diagnostic)] +#[diag("arguments to `macro_use` are not allowed here")] +pub(crate) struct ArgumentsMacroUseNotAllowed { + #[primary_span] + pub(crate) span: Span, +} - /// If the binding refers to a tuple struct constructor with fields, - /// returns the span of its fields. - fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { - let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = - decl.kind - else { - return None; - }; - - let def_id = self.tcx.parent(ctor_def_id); - self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "try renaming it with a name", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnnamedImportSugg { + #[suggestion_part(code = "{ident} as name")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. - /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. - /// Returns `None` for block modules that don't have a `DefId`. - fn module_path_names(&self, module: Module<'ra>) -> Option> { - let mut path = Vec::new(); - let mut def_id = module.opt_def_id()?; - while let Some(parent) = self.tcx.opt_parent(def_id) { - if let Some(name) = self.tcx.opt_item_name(def_id) { - path.push(name); - } - if parent.is_top_level_module() { - break; - } - def_id = parent; - } - path.reverse(); - path.insert(0, kw::Crate); - Some(path) - } +#[derive(Diagnostic)] +#[diag("imports need to be explicitly named")] +pub(crate) struct UnnamedImport { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) sugg: UnnamedImportSugg, +} - /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) - /// relative to the current scope, if possible. Only applies to crate-local items and - /// only when the resulting path is actually shorter than the original. - fn shorten_candidate_path( - &self, - suggestion: &mut ImportSuggestion, - current_module: Module<'ra>, - ) { - const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; - - // Only shorten local items. - if suggestion.did.is_none_or(|did| !did.is_local()) { - return; - } +#[derive(Diagnostic)] +#[diag("macro-expanded `extern crate` items cannot shadow names passed with `--extern`")] +pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { + #[primary_span] + pub(crate) span: Span, +} - // Build current module path: [Crate, foo, bar, ...]. - let Some(current_mod_path) = self.module_path_names(current_module) else { - return; - }; - - // Normalise candidate path: filter out `PathRoot` (`::`), and if the path - // doesn't start with `Crate`, prepend it (edition 2015 paths are relative - // to the crate root without an explicit `crate::` prefix). - let candidate_names = { - let filtered_segments: Vec<_> = suggestion - .path - .segments - .iter() - .filter(|segment| segment.ident.name != kw::PathRoot) - .collect(); - - let mut candidate_names: Vec = - filtered_segments.iter().map(|segment| segment.ident.name).collect(); - if candidate_names.first() != Some(&kw::Crate) { - candidate_names.insert(0, kw::Crate); - } - if candidate_names.len() < 2 { - return; - } - candidate_names - }; - - // The candidate's module path is everything except the last segment (the item name). - let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; - - // Find the longest common prefix between the current module and candidate module paths. - let common_prefix_length = current_mod_path - .iter() - .zip(candidate_mod_names.iter()) - .take_while(|(current, candidate)| current == candidate) - .count(); - - // Non-crate-local item; keep the full absolute path. - if common_prefix_length == 0 { - return; - } +#[derive(Diagnostic)] +#[diag("`&` without an explicit lifetime name cannot be used here", code = E0637)] +pub(crate) struct ElidedAnonymousLifetimeReportError { + #[primary_span] + #[label("explicit lifetime name needed here")] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) suggestion: Option, +} - let super_count = current_mod_path.len() - common_prefix_length; +#[derive(Diagnostic)] +#[diag( + "associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" +)] +pub(crate) struct LendingIteratorReportError { + #[primary_span] + pub(crate) lifetime: Span, + #[note( + "you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type" + )] + pub(crate) ty: Span, +} - // At the crate root, `use` paths resolve from the crate root anyway, so we can - // drop the `crate::` prefix entirely instead of replacing it with `self::`. - let at_crate_root = current_mod_path.len() == 1; +#[derive(Diagnostic)] +#[diag("missing lifetime in associated type")] +pub(crate) struct AnonymousLifetimeNonGatReportError { + #[primary_span] + #[label("this lifetime must come from the implemented type")] + pub(crate) lifetime: Span, + #[note( + "in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" + )] + pub(crate) decl: MultiSpan, +} - let mut new_segments = if super_count == 0 && at_crate_root { - ThinVec::new() - } else { - let prefix_keyword = match super_count { - 0 => kw::SelfLower, - 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, - _ => return, // Too many `super` levels; keep the full absolute path. - }; - thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] - }; - for &name in &candidate_names[common_prefix_length..] { - new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "consider introducing a higher-ranked lifetime here", + applicability = "machine-applicable" +)] +pub(crate) struct ElidedAnonymousLifetimeReportErrorSuggestion { + #[suggestion_part(code = "for<'a> ")] + pub(crate) lo: Span, + #[suggestion_part(code = "'a ")] + pub(crate) hi: Span, +} - // Only apply if the result is strictly shorter than the original path. - if new_segments.len() >= suggestion.path.segments.len() { - return; - } +#[derive(Diagnostic)] +#[diag("`'_` cannot be used here", code = E0637)] +pub(crate) struct ExplicitAnonymousLifetimeReportError { + #[primary_span] + #[label("`'_` is a reserved lifetime name")] + pub(crate) span: Span, +} - suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; - } +#[derive(Diagnostic)] +#[diag("implicit elided lifetime not allowed here", code = E0726)] +pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) subdiag: ElidedLifetimeInPathSubdiag, +} - fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { - let PrivacyError { - ident, - decl, - outermost_res, - parent_scope, - single_nested, - dedup_span, - ref source, - } = *privacy_error; - - let res = decl.res(); - let ctor_fields_span = self.ctor_fields_span(decl); - let plain_descr = res.descr().to_string(); - let nonimport_descr = - if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; - let import_descr = nonimport_descr.clone() + " import"; - let get_descr = |b: Decl<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; - - // Print the primary message. - let ident_descr = get_descr(decl); - let mut err = - self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident }); - - self.mention_default_field_values(source, ident, &mut err); - - let shown_candidates = if let Some((this_res, outer_ident)) = outermost_res { - let mut import_suggestions = self.lookup_import_candidates( - outer_ident, - this_res.ns().unwrap_or(Namespace::TypeNS), - &parent_scope, - &|res: Res| res == this_res, - ); - // Shorten candidate paths using `super::` or `self::` when possible. - for suggestion in &mut import_suggestions { - self.shorten_candidate_path(suggestion, parent_scope.module); - } - let point_to_def = !show_candidates( - self.tcx, - &mut err, - Some(dedup_span.until(outer_ident.span.shrink_to_hi())), - &import_suggestions, - Instead::Yes, - FoundUse::Yes, - DiagMode::Import { append: single_nested, unresolved_import: false }, - vec![], - "", - ); - // If we suggest importing a public re-export, don't point at the definition. - if point_to_def && ident.span != outer_ident.span { - let label = errors::OuterIdentIsNotPubliclyReexported { - span: outer_ident.span, - outer_ident_descr: this_res.descr(), - outer_ident, - }; - err.subdiagnostic(label); - } - !point_to_def - } else { - false - }; - - let mut non_exhaustive = None; - // If an ADT is foreign and marked as `non_exhaustive`, then that's - // probably why we have the privacy error. - // Otherwise, point out if the struct has any private fields. - if let Some(def_id) = res.opt_def_id() - && !def_id.is_local() - && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) - { - non_exhaustive = Some(attr_span); - } else if let Some(span) = ctor_fields_span { - let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; - err.subdiagnostic(label); - if let Res::Def(_, d) = res - && let Some(fields) = self.field_visibility_spans.get(&d) - { - let spans = fields.iter().map(|span| *span).collect(); - let sugg = - errors::ConsiderMakingTheFieldPublic { spans, number_of_fields: fields.len() }; - err.subdiagnostic(sugg); - } - } +#[derive(Diagnostic)] +#[diag("`'_` cannot be used here", code = E0637)] +#[help("use another lifetime specifier")] +pub(crate) struct UnderscoreLifetimeIsReserved { + #[primary_span] + #[label("`'_` is a reserved lifetime name")] + pub(crate) span: Span, +} - let mut sugg_paths: Vec<(Vec, bool)> = vec![]; - if let Some(mut def_id) = res.opt_def_id() { - // We can't use `def_path_str` in resolve. - let mut path = vec![def_id]; - while let Some(parent) = self.tcx.opt_parent(def_id) { - def_id = parent; - if !def_id.is_top_level_module() { - path.push(def_id); - } else { - break; - } - } - // We will only suggest importing directly if it is accessible through that path. - let path_names: Option> = path - .iter() - .rev() - .map(|def_id| { - self.tcx.opt_item_name(*def_id).map(|name| { - Ident::with_dummy_span(if def_id.is_top_level_module() { - kw::Crate - } else { - name - }) - }) - }) - .collect(); - if let Some(&def_id) = path.get(0) - && let Some(path) = path_names - { - if let Some(def_id) = def_id.as_local() { - if self.effective_visibilities.is_directly_public(def_id) { - sugg_paths.push((path, false)); - } - } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) - { - sugg_paths.push((path, false)); - } - } - } +#[derive(Diagnostic)] +#[diag("invalid lifetime parameter name: `{$lifetime}`", code = E0262)] +pub(crate) struct StaticLifetimeIsReserved { + #[primary_span] + #[label("'static is a reserved lifetime name")] + pub(crate) span: Span, + pub(crate) lifetime: Ident, +} - // Print the whole import chain to make it easier to see what happens. - let first_binding = decl; - let mut next_binding = Some(decl); - let mut next_ident = ident; - while let Some(binding) = next_binding { - let name = next_ident; - next_binding = match binding.kind { - _ if res == Res::Err => None, - DeclKind::Import { source_decl, import, .. } => match import.kind { - _ if source_decl.span.is_dummy() => None, - ImportKind::Single { source, .. } => { - next_ident = source; - Some(source_decl) - } - ImportKind::Glob { .. } - | ImportKind::MacroUse { .. } - | ImportKind::MacroExport => Some(source_decl), - ImportKind::ExternCrate { .. } => None, - }, - _ => None, - }; - - match binding.kind { - DeclKind::Import { source_decl, import, .. } => { - // Don't include `{{root}}` in suggestions - it's an internal symbol - // that should never be shown to users. - let path = import - .module_path - .iter() - .filter(|seg| seg.ident.name != kw::PathRoot) - .map(|seg| seg.ident.clone()) - .chain(std::iter::once(ident)) - .collect::>(); - let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); - sugg_paths.push((path, through_reexport)); - } - DeclKind::Def(_) => {} - } - let first = binding == first_binding; - let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); - let mut note_span = MultiSpan::from_span(def_span); - if !first && binding.vis().is_public() { - let desc = match binding.kind { - DeclKind::Import { .. } => "re-export", - _ => "directly", - }; - note_span.push_span_label(def_span, format!("you could import this {desc}")); - } - // Final step in the import chain, point out if the ADT is `non_exhaustive` - // which is probably why this privacy violation occurred. - if next_binding.is_none() - && let Some(span) = non_exhaustive - { - note_span.push_span_label( - span, - "cannot be constructed because it is `#[non_exhaustive]`", - ); - } - let note = errors::NoteAndRefersToTheItemDefinedHere { - span: note_span, - binding_descr: get_descr(binding), - binding_name: name, - first, - dots: next_binding.is_some(), - }; - err.subdiagnostic(note); - } - // The suggestion replaces `dedup_span` with a path reaching the failing ident. - // That's valid only when - // 1) the failing ident is the imported leaf, otherwise `as` renames and trailing segments - // get dropped, and - // 2) the use isn't nested, otherwise `dedup_span` is one ident in `{...}`. - // - // See issue #156060. - let can_replace_use = !shown_candidates - && !single_nested - && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span); - if can_replace_use { - // We prioritize shorter paths, non-core imports and direct imports over the - // alternatives. - sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); - for (sugg, reexport) in sugg_paths { - if sugg.len() <= 1 { - // A single path segment suggestion is wrong. This happens on circular - // imports. `tests/ui/imports/issue-55884-2.rs` - continue; - } - let path = join_path_idents(sugg); - let sugg = if reexport { - errors::ImportIdent::ThroughReExport { span: dedup_span, ident, path } - } else { - errors::ImportIdent::Directly { span: dedup_span, ident, path } - }; - err.subdiagnostic(sugg); - break; - } - } +#[derive(Diagnostic)] +#[diag("variable `{$name}` is not bound in all patterns", code = E0408)] +pub(crate) struct VariableIsNotBoundInAllPatterns { + #[primary_span] + pub(crate) multispan: MultiSpan, + pub(crate) name: Ident, +} - err.emit(); - } +#[derive(Subdiagnostic, Debug, Clone)] +#[label("pattern doesn't bind `{$name}`")] +pub(crate) struct PatternDoesntBindName { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Ident, +} - /// When a private field is being set that has a default field value, we suggest using `..` and - /// setting the value of that field implicitly with its default. - /// - /// If we encounter code like - /// ```text - /// struct Priv; - /// pub struct S { - /// pub field: Priv = Priv, - /// } - /// ``` - /// which is used from a place where `Priv` isn't accessible - /// ```text - /// let _ = S { field: m::Priv1 {} }; - /// // ^^^^^ private struct - /// ``` - /// we will suggest instead using the `default_field_values` syntax instead: - /// ```text - /// let _ = S { .. }; - /// ``` - fn mention_default_field_values( - &self, - source: &Option, - ident: Ident, - err: &mut Diag<'_>, - ) { - let Some(expr) = source else { return }; - let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; - // We don't have to handle type-relative paths because they're forbidden in ADT - // expressions, but that would change with `#[feature(more_qualified_paths)]`. - let Some(segment) = struct_expr.path.segments.last() else { return }; - let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; - let Some(Res::Def(_, def_id)) = partial_res.full_res() else { - return; - }; - let Some(default_fields) = self.field_defaults(def_id) else { return }; - if struct_expr.fields.is_empty() { - return; - } - let last_span = struct_expr.fields.iter().last().unwrap().span; - let mut iter = struct_expr.fields.iter().peekable(); - let mut prev: Option = None; - while let Some(field) = iter.next() { - if field.expr.span.overlaps(ident.span) { - err.span_label(field.ident.span, "while setting this field"); - if default_fields.contains(&field.ident.name) { - let sugg = if last_span == field.span { - vec![(field.span, "..".to_string())] - } else { - vec![ - ( - // Account for trailing commas and ensure we remove them. - match (prev, iter.peek()) { - (_, Some(next)) => field.span.with_hi(next.span.lo()), - (Some(prev), _) => field.span.with_lo(prev.hi()), - (None, None) => field.span, - }, - String::new(), - ), - (last_span.shrink_to_hi(), ", ..".to_string()), - ] - }; - err.multipart_suggestion( - format!( - "the type `{ident}` of field `{}` is private, but you can construct \ - the default value defined for it in `{}` using `..` in the struct \ - initializer expression", - field.ident, - self.tcx.item_name(def_id), - ), - sugg, - Applicability::MachineApplicable, - ); - break; - } - } - prev = Some(field.span); - } - } +#[derive(Subdiagnostic, Debug, Clone)] +#[label("variable not in all patterns")] +pub(crate) struct VariableNotInAllPatterns { + #[primary_span] + pub(crate) span: Span, +} - pub(crate) fn find_similarly_named_module_or_crate( - &self, - ident: Symbol, - current_module: Module<'ra>, - ) -> Option { - let mut candidates = self - .extern_prelude - .keys() - .map(|ident| ident.name) - .chain( - self.local_module_map - .iter() - .filter(|(_, module)| { - let module = module.to_module(); - current_module.is_ancestor_of(module) && current_module != module - }) - .flat_map(|(_, module)| module.name()), - ) - .chain( - self.extern_module_map - .borrow() - .iter() - .filter(|(_, module)| { - let module = module.to_module(); - current_module.is_ancestor_of(module) && current_module != module - }) - .flat_map(|(_, module)| module.name()), - ) - .filter(|c| !c.to_string().is_empty()) - .collect::>(); - candidates.sort(); - candidates.dedup(); - find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "you might have meant to use the similarly named previously used binding `{$typo}`", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct PatternBindingTypo { + #[suggestion_part(code = "{typo}")] + pub(crate) spans: Vec, + pub(crate) typo: Symbol, +} - pub(crate) fn report_path_resolution_error( - &mut self, - path: &[Segment], - opt_ns: Option, // `None` indicates a module path in import - parent_scope: &ParentScope<'ra>, - ribs: Option<&PerNS>>>, - ignore_decl: Option>, - ignore_import: Option>, - module: Option>, - failed_segment_idx: usize, - ident: Ident, - diag_metadata: Option<&DiagMetadata<'_>>, - ) -> (String, String, Option) { - let is_last = failed_segment_idx == path.len() - 1; - let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - let module_def_id = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), - _ => None, - }; - let scope = match &path[..failed_segment_idx] { - [.., prev] => { - if prev.ident.name == kw::PathRoot { - format!("the crate root") - } else { - format!("`{}`", prev.ident) - } - } - _ => format!("this scope"), - }; - let message = format!("cannot find `{ident}` in {scope}"); - - if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { - let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); - let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); - candidates - .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); - if let Some(candidate) = candidates.get(0) { - let path = { - // remove the possible common prefix of the path - let len = candidate.path.segments.len(); - let start_index = (0..=failed_segment_idx.min(len - 1)) - .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) - .unwrap_or_default(); - let segments = - (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); - Path { segments, span: Span::default(), tokens: None } - }; - ( - message, - String::from("unresolved import"), - Some(( - vec![(ident.span, pprust::path_to_string(&path))], - String::from("a similar path exists"), - Applicability::MaybeIncorrect, - )), - ) - } else if ident.name == sym::core { - ( - message, - format!("you might be missing crate `{ident}`"), - Some(( - vec![(ident.span, "std".to_string())], - "try using `std` instead of `core`".to_string(), - Applicability::MaybeIncorrect, - )), - ) - } else if ident.name == kw::Underscore { - ( - "invalid crate or module name `_`".to_string(), - "`_` is not a valid crate or module name".to_string(), - None, - ) - } else if self.tcx.sess.is_rust_2015() { - ( - format!("cannot find module or crate `{ident}` in {scope}"), - format!("use of unresolved module or unlinked crate `{ident}`"), - Some(( - vec![( - self.current_crate_outer_attr_insert_span, - format!("extern crate {ident};\n"), - )], - if was_invoked_from_cargo() { - format!( - "if you wanted to use a crate named `{ident}`, use `cargo add \ - {ident}` to add it to your `Cargo.toml` and import it in your \ - code", - ) - } else { - format!( - "you might be missing a crate named `{ident}`, add it to your \ - project and import it in your code", - ) - }, - Applicability::MaybeIncorrect, - )), - ) - } else { - (message, format!("could not find `{ident}` in the crate root"), None) - } - } else if failed_segment_idx > 0 { - let parent = path[failed_segment_idx - 1].ident.name; - let parent = match parent { - // ::foo is mounted at the crate root for 2015, and is the extern - // prelude for 2018+ - kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => { - "the list of imported crates".to_owned() - } - kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => format!("`{parent}`"), - }; - - let mut msg = format!("could not find `{ident}` in {parent}"); - if ns == TypeNS || ns == ValueNS { - let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; - let binding = if let Some(module) = module { - self.cm() - .resolve_ident_in_module( - module, - ident, - ns_to_try, - parent_scope, - None, - ignore_decl, - ignore_import, - ) - .ok() - } else if let Some(ribs) = ribs - && let Some(TypeNS | ValueNS) = opt_ns - { - assert!(ignore_import.is_none()); - match self.resolve_ident_in_lexical_scope( - ident, - ns_to_try, - parent_scope, - None, - &ribs[ns_to_try], - ignore_decl, - diag_metadata, - ) { - // we found a locally-imported or available item/module - Some(LateDecl::Decl(binding)) => Some(binding), - _ => None, - } - } else { - self.cm() - .resolve_ident_in_scope_set( - ident, - ScopeSet::All(ns_to_try), - parent_scope, - None, - ignore_decl, - ignore_import, - ) - .ok() - }; - if let Some(binding) = binding { - msg = format!( - "expected {}, found {} `{ident}` in {parent}", - ns.descr(), - binding.res().descr(), - ); - }; - } - (message, msg, None) - } else if ident.name == kw::SelfUpper { - // As mentioned above, `opt_ns` being `None` indicates a module path in import. - // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an - // impl - if opt_ns.is_none() { - (message, "`Self` cannot be used in imports".to_string(), None) - } else { - ( - message, - "`Self` is only available in impls, traits, and type definitions".to_string(), - None, - ) - } - } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { - // Check whether the name refers to an item in the value namespace. - let binding = if let Some(ribs) = ribs { - assert!(ignore_import.is_none()); - self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - None, - &ribs[ValueNS], - ignore_decl, - diag_metadata, - ) - } else { - None - }; - let match_span = match binding { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LateDecl::RibDef(Res::Local(id))) => { - Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) - } - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub const Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LateDecl::Decl(name_binding)) => Some(( - name_binding.span, - name_binding.res().article(), - name_binding.res().descr(), - )), - _ => None, - }; - - let message = format!("cannot find type `{ident}` in {scope}"); - let label = if let Some((span, article, descr)) = match_span { - format!( - "`{ident}` is declared as {article} {descr} at `{}`, not a type", - self.tcx - .sess - .source_map() - .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) - ) - } else { - format!("use of undeclared type `{ident}`") - }; - (message, label, None) - } else { - let mut suggestion = None; - if ident.name == sym::alloc { - suggestion = Some(( - vec![], - String::from("add `extern crate alloc` to use the `alloc` crate"), - Applicability::MaybeIncorrect, - )) - } +#[derive(Diagnostic)] +#[diag("the name `{$name}` is defined multiple times")] +#[note("`{$name}` must be defined only once in the {$descr} namespace of this {$container}")] +pub(crate) struct NameDefinedMultipleTime { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, + pub(crate) descr: &'static str, + pub(crate) container: &'static str, + #[subdiagnostic] + pub(crate) label: NameDefinedMultipleTimeLabel, + #[subdiagnostic] + pub(crate) old_binding_label: Option, +} - suggestion = suggestion.or_else(|| { - self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map( - |sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from("there is a crate or module with a similar name"), - Applicability::MaybeIncorrect, - ) - }, - ) - }); - if let Ok(binding) = self.cm().resolve_ident_in_scope_set( - ident, - ScopeSet::All(ValueNS), - parent_scope, - None, - ignore_decl, - ignore_import, - ) { - let descr = binding.res().descr(); - let message = format!("cannot find module or crate `{ident}` in {scope}"); - (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) - } else { - let suggestion = if suggestion.is_some() { - suggestion - } else if let Some(m) = self.undeclared_module_exists(ident) { - self.undeclared_module_suggest_declare(ident, m) - } else if was_invoked_from_cargo() { - Some(( - vec![], - format!( - "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ - to add it to your `Cargo.toml`", - ), - Applicability::MaybeIncorrect, - )) - } else { - Some(( - vec![], - format!("you might be missing a crate named `{ident}`",), - Applicability::MaybeIncorrect, - )) - }; - let message = format!("cannot find module or crate `{ident}` in {scope}"); - ( - message, - format!("use of unresolved module or unlinked crate `{ident}`"), - suggestion, - ) - } - } - } +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeLabel { + #[label("`{$name}` reimported here")] + Reimported { + #[primary_span] + span: Span, + name: Symbol, + }, + #[label("`{$name}` redefined here")] + Redefined { + #[primary_span] + span: Span, + name: Symbol, + }, +} - fn undeclared_module_suggest_declare( - &self, - ident: Ident, - path: std::path::PathBuf, - ) -> Option<(Vec<(Span, String)>, String, Applicability)> { - Some(( - vec![(self.current_crate_outer_attr_insert_span, format!("mod {ident};\n"))], - format!( - "to make use of source file {}, use `mod {ident}` \ - in this file to declare the module", - path.display() - ), - Applicability::MaybeIncorrect, - )) - } +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { + #[label("previous import of the {$old_kind} `{$name}` here")] + Import { + #[primary_span] + span: Span, + old_kind: &'static str, + name: Symbol, + }, + #[label("previous definition of the {$old_kind} `{$name}` here")] + Definition { + #[primary_span] + span: Span, + old_kind: &'static str, + name: Symbol, + }, +} - fn undeclared_module_exists(&self, ident: Ident) -> Option { - let map = self.tcx.sess.source_map(); - - let src = map.span_to_filename(ident.span).into_local_path()?; - let i = ident.as_str(); - // FIXME: add case where non parent using undeclared module (hard?) - let dir = src.parent()?; - let src = src.file_stem()?.to_str()?; - for file in [ - // …/x.rs - dir.join(i).with_extension("rs"), - // …/x/mod.rs - dir.join(i).join("mod.rs"), - ] { - if file.exists() { - return Some(file); - } - } - if !matches!(src, "main" | "lib" | "mod") { - for file in [ - // …/x/y.rs - dir.join(src).join(i).with_extension("rs"), - // …/x/y/mod.rs - dir.join(src).join(i).join("mod.rs"), - ] { - if file.exists() { - return Some(file); - } - } - } - None - } +#[derive(Diagnostic)] +#[diag("{$ident_descr} `{$ident}` is private", code = E0603)] +pub(crate) struct IsPrivate<'a> { + #[primary_span] + #[label("private {$ident_descr}")] + pub(crate) span: Span, + pub(crate) ident_descr: &'a str, + pub(crate) ident: Ident, +} - /// Adds suggestions for a path that cannot be resolved. - #[instrument(level = "debug", skip(self, parent_scope))] - pub(crate) fn make_path_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - match path[..] { - // `{{root}}::ident::...` on both editions. - // On 2015 `{{root}}` is usually added implicitly. - [first, second, ..] - if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} - // `ident::...` on 2018. - [first, ..] - if first.ident.span.at_least_rust_2018() - && !first.ident.is_path_segment_keyword() => - { - // Insert a placeholder that's later replaced by `self`/`super`/etc. - path.insert(0, Segment::from_ident(Ident::dummy())); - } - _ => return None, - } +#[derive(Diagnostic)] +#[diag("generic arguments in macro path")] +pub(crate) struct GenericArgumentsInMacroPath { + #[primary_span] + pub(crate) span: Span, +} - self.make_missing_self_suggestion(path.clone(), parent_scope) - .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) - .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) - .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) - } +#[derive(Diagnostic)] +#[diag("attributes starting with `rustc` are reserved for use by the `rustc` compiler")] +pub(crate) struct AttributesStartingWithRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} - /// Suggest a missing `self::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `self::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_self_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `self` and check if that is valid. - path[0].ident.name = kw::SelfLower; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { Some((path, None)) } else { None } - } +#[derive(Diagnostic)] +#[diag( + "attributes containing a segment starting with `rustc` are reserved for use by the `rustc` compiler" +)] +pub(crate) struct AttributesContainingRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} - /// Suggests a missing `crate::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `crate::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_crate_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `crate` and check if that is valid. - path[0].ident.name = kw::Crate; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { - Some(( - path, - Some( - "`use` statements changed in Rust 2018; read more at \ - " - .to_string(), - ), - )) - } else { - None - } - } +#[derive(Diagnostic)] +#[diag("cannot use {$article} {$descr} through an import")] +pub(crate) struct CannotUseThroughAnImport { + #[primary_span] + pub(crate) span: Span, + pub(crate) article: &'static str, + pub(crate) descr: &'static str, + #[note("the {$descr} imported here")] + pub(crate) binding_span: Option, +} - /// Suggests a missing `super::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `super::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_super_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `crate` and check if that is valid. - path[0].ident.name = kw::Super; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { Some((path, None)) } else { None } - } +#[derive(Diagnostic)] +#[diag("name `{$ident}` is reserved in attribute namespace")] +pub(crate) struct NameReservedInAttributeNamespace { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Symbol, +} - /// Suggests a missing external crate name if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foobar::Baz; - /// | ^^^^^^ did you mean `baz::foobar`? - /// ``` - /// - /// Used when importing a submodule of an external crate but missing that crate's - /// name as the first part of path. - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_external_crate_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - if path[1].ident.span.is_rust_2015() { - return None; - } +#[derive(Diagnostic)] +#[diag("cannot find a built-in macro with name `{$ident}`")] +pub(crate) struct CannotFindBuiltinMacroWithName { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - // Sort extern crate names in *reverse* order to get - // 1) some consistent ordering for emitted diagnostics, and - // 2) `std` suggestions before `core` suggestions. - let mut extern_crate_names = - self.extern_prelude.keys().map(|ident| ident.name).collect::>(); - extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); - - for name in extern_crate_names.into_iter() { - // Replace first ident with a crate name and check if that is valid. - path[0].ident.name = name; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?name, ?result); - if let PathResult::Module(..) = result { - return Some((path, None)); - } - } +#[derive(Diagnostic)] +#[diag("tool `{$tool}` was already registered")] +pub(crate) struct ToolWasAlreadyRegistered { + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Ident, + #[label("already registered here")] + pub(crate) old_ident_span: Span, +} - None - } +#[derive(Subdiagnostic)] +pub(crate) enum DefinedHere { + #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] + SimilarlyNamed { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, + #[label("{$candidate_descr} `{$candidate}` defined here")] + SingleItem { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, +} - /// Suggests importing a macro from the root of the crate rather than a module within - /// the crate. - /// - /// ```text - /// help: a macro with this name exists at the root of the crate - /// | - /// LL | use issue_59764::makro; - /// | ^^^^^^^^^^^^^^^^^^ - /// | - /// = note: this could be because a macro annotated with `#[macro_export]` will be exported - /// at the root of the crate instead of the module where it is defined - /// ``` - pub(crate) fn check_for_module_export_macro( - &mut self, - import: Import<'ra>, - module: ModuleOrUniformRoot<'ra>, - ident: Ident, - ) -> Option<(Option, Option)> { - let ModuleOrUniformRoot::Module(mut crate_module) = module else { - return None; - }; +#[derive(Subdiagnostic)] +#[label("{$outer_ident_descr} `{$outer_ident}` is not publicly re-exported")] +pub(crate) struct OuterIdentIsNotPubliclyReexported { + #[primary_span] + pub(crate) span: Span, + pub(crate) outer_ident_descr: &'static str, + pub(crate) outer_ident: Ident, +} - while let Some(parent) = crate_module.parent { - crate_module = parent; - } +#[derive(Subdiagnostic)] +#[label("a constructor is private if any of the fields is private")] +pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { + #[primary_span] + pub(crate) span: Span, +} - if module == ModuleOrUniformRoot::Module(crate_module) { - // Don't make a suggestion if the import was already from the root of the crate. - return None; - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "{ $number_of_fields -> + [one] consider making the field publicly accessible + *[other] consider making the fields publicly accessible + }", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct ConsiderMakingTheFieldPublic { + #[suggestion_part(code = "pub ")] + pub(crate) spans: Vec, + pub(crate) number_of_fields: usize, +} - let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); - let binding = self.resolution(crate_module, binding_key)?.best_decl()?; - let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { - return None; - }; - if !kinds.contains(MacroKinds::BANG) { - return None; - } - let module_name = crate_module.name().unwrap_or(kw::Crate); - let import_snippet = match import.kind { - ImportKind::Single { source, target, .. } if source != target => { - format!("{source} as {target}") - } - _ => format!("{ident}"), - }; - - let mut corrections: Vec<(Span, String)> = Vec::new(); - if !import.is_nested() { - // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove - // intermediate segments. - corrections.push((import.span, format!("{module_name}::{import_snippet}"))); - } else { - // Find the binding span (and any trailing commas and spaces). - // i.e. `use a::b::{c, d, e};` - // ^^^ - let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( - self.tcx.sess, - import.span, - import.use_span, - ); - debug!(found_closing_brace, ?binding_span); - - let mut removal_span = binding_span; - - // If the binding span ended with a closing brace, as in the below example: - // i.e. `use a::b::{c, d};` - // ^ - // Then expand the span of characters to remove to include the previous - // binding's trailing comma. - // i.e. `use a::b::{c, d};` - // ^^^ - if found_closing_brace - && let Some(previous_span) = - extend_span_to_previous_binding(self.tcx.sess, binding_span) - { - debug!(?previous_span); - removal_span = removal_span.with_lo(previous_span.lo()); - } - debug!(?removal_span); - - // Remove the `removal_span`. - corrections.push((removal_span, "".to_string())); - - // Find the span after the crate name and if it has nested imports immediately - // after the crate name already. - // i.e. `use a::b::{c, d};` - // ^^^^^^^^^ - // or `use a::{b, c, d}};` - // ^^^^^^^^^^^ - let (has_nested, after_crate_name) = - find_span_immediately_after_crate_name(self.tcx.sess, import.use_span); - debug!(has_nested, ?after_crate_name); - - let source_map = self.tcx.sess.source_map(); - - // Make sure this is actually crate-relative. - let is_definitely_crate = import - .module_path - .first() - .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); - - // Add the import to the start, with a `{` if required. - let start_point = source_map.start_point(after_crate_name); - if is_definitely_crate - && let Ok(start_snippet) = source_map.span_to_snippet(start_point) - { - corrections.push(( - start_point, - if has_nested { - // In this case, `start_snippet` must equal '{'. - format!("{start_snippet}{import_snippet}, ") - } else { - // In this case, add a `{`, then the moved import, then whatever - // was there before. - format!("{{{import_snippet}, {start_snippet}") - }, - )); - - // Add a `};` to the end if nested, matching the `{` added at the start. - if !has_nested { - corrections.push((source_map.end_point(after_crate_name), "};".to_string())); - } - } else { - // If the root import is module-relative, add the import separately - corrections.push(( - import.use_span.shrink_to_lo(), - format!("use {module_name}::{import_snippet};\n"), - )); - } - } +#[derive(Subdiagnostic)] +pub(crate) enum ImportIdent { + #[suggestion( + "import `{$ident}` through the re-export", + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + ThroughReExport { + #[primary_span] + span: Span, + ident: Ident, + path: String, + }, + #[suggestion( + "import `{$ident}` directly", + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + Directly { + #[primary_span] + span: Span, + ident: Ident, + path: String, + }, +} - let suggestion = Some(( - corrections, - String::from("a macro with this name exists at the root of the crate"), - Applicability::MaybeIncorrect, - )); - Some(( - suggestion, - Some( - "this could be because a macro annotated with `#[macro_export]` will be exported \ - at the root of the crate instead of the module where it is defined" - .to_string(), - ), - )) - } +#[derive(Subdiagnostic)] +#[note( + "{$first -> + [true] {$dots -> + [true] the {$binding_descr} `{$binding_name}` is defined here... + *[false] the {$binding_descr} `{$binding_name}` is defined here + } + *[false] {$dots -> + [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... + *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here + } + }" +)] +pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { + #[primary_span] + pub(crate) span: MultiSpan, + pub(crate) binding_descr: &'a str, + pub(crate) binding_name: Ident, + pub(crate) first: bool, + pub(crate) dots: bool, +} - /// Finds a cfg-ed out item inside `module` with the matching name. - pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { - let local_items; - let symbols = if module.is_local() { - local_items = self - .stripped_cfg_items - .iter() - .filter_map(|item| { - let parent_scope = self.local_modules.iter().find_map(|m| match m.kind { - ModuleKind::Def(_, def_id, node_id, _) if node_id == item.parent_scope => { - Some(def_id) - } - _ => None, - })?; - Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg.clone() }) - }) - .collect::>(); - local_items.as_slice() - } else { - self.tcx.stripped_cfg_items(module.krate) - }; +#[derive(Subdiagnostic)] +#[suggestion("remove unnecessary import", code = "", applicability = "maybe-incorrect")] +pub(crate) struct RemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} - for &StrippedCfgItem { parent_scope, ident, ref cfg } in symbols { - if ident.name != *segment { - continue; - } +#[derive(Subdiagnostic)] +#[suggestion( + "remove unnecessary import", + code = "", + applicability = "maybe-incorrect", + style = "tool-only" +)] +pub(crate) struct ToolOnlyRemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} - let parent_module = self.get_nearest_non_block_module(parent_scope).def_id(); - - fn comes_from_same_module_for_glob( - r: &Resolver<'_, '_>, - parent_module: DefId, - module: DefId, - visited: &mut FxHashMap, - ) -> bool { - if let Some(&cached) = visited.get(&parent_module) { - // this branch is prevent from being called recursively infinity, - // because there has some cycles in globs imports, - // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` - return cached; - } - visited.insert(parent_module, false); - let mut res = false; - let m = r.expect_module(parent_module); - if m.is_local() { - for importer in m.glob_importers.borrow().iter() { - if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() - { - if next_parent_module == module - || comes_from_same_module_for_glob( - r, - next_parent_module, - module, - visited, - ) - { - res = true; - break; - } - } - } - } - visited.insert(parent_module, res); - res - } +#[derive(Subdiagnostic)] +#[note("`{$imported_ident}` is imported here, but it is {$imported_ident_desc}")] +pub(crate) struct IdentImporterHereButItIsDesc<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} - let comes_from_same_module = parent_module == module - || comes_from_same_module_for_glob( - self, - parent_module, - module, - &mut Default::default(), - ); - if !comes_from_same_module { - continue; - } +#[derive(Subdiagnostic)] +#[note("`{$imported_ident}` is in scope, but it is {$imported_ident_desc}")] +pub(crate) struct IdentInScopeButItIsDesc<'a> { + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} - let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { - errors::ItemWas::BehindFeature { feature, span: cfg.1 } - } else { - errors::ItemWas::CfgOut { span: cfg.1 } - }; - let note = errors::FoundItemConfigureOut { span: ident.span, item_was }; - err.subdiagnostic(note); - } - } +pub(crate) struct FoundItemConfigureOut { + pub(crate) span: Span, + pub(crate) item_was: ItemWas, +} + +pub(crate) enum ItemWas { + BehindFeature { feature: Symbol, span: Span }, + CfgOut { span: Span }, +} - pub(crate) fn struct_ctor(&self, def_id: DefId) -> Option { - match def_id.as_local() { - Some(def_id) => self.struct_ctors.get(&def_id).cloned(), - None => { - self.cstore().ctor_untracked(self.tcx, def_id).map(|(ctor_kind, ctor_def_id)| { - let res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); - let vis = self.tcx.visibility(ctor_def_id); - let field_visibilities = self - .tcx - .associated_item_def_ids(def_id) - .iter() - .map(|&field_id| self.tcx.visibility(field_id)) - .collect(); - StructCtor { res, vis, field_visibilities } - }) +impl Subdiagnostic for FoundItemConfigureOut { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + let mut multispan: MultiSpan = self.span.into(); + match self.item_was { + ItemWas::BehindFeature { feature, span } => { + let value = feature.into_diag_arg(&mut None); + let msg = msg!("the item is gated behind the `{$feature}` feature") + .arg("feature", value) + .format(); + multispan.push_span_label(span, msg); + } + ItemWas::CfgOut { span } => { + multispan.push_span_label(span, msg!("the item is gated here")); } } + diag.span_note(multispan, msg!("found an item that was configured out")); } } -/// Given a `binding_span` of a binding within a use statement: -/// -/// ```ignore (illustrative) -/// use foo::{a, b, c}; -/// // ^ -/// ``` -/// -/// then return the span until the next binding or the end of the statement: -/// -/// ```ignore (illustrative) -/// use foo::{a, b, c}; -/// // ^^^ -/// ``` -fn find_span_of_binding_until_next_binding( - sess: &Session, - binding_span: Span, - use_span: Span, -) -> (bool, Span) { - let source_map = sess.source_map(); - - // Find the span of everything after the binding. - // i.e. `a, e};` or `a};` - let binding_until_end = binding_span.with_hi(use_span.hi()); - - // Find everything after the binding but not including the binding. - // i.e. `, e};` or `};` - let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); - - // Keep characters in the span until we encounter something that isn't a comma or - // whitespace. - // i.e. `, ` or ``. - // - // Also note whether a closing brace character was encountered. If there - // was, then later go backwards to remove any trailing commas that are left. - let mut found_closing_brace = false; - let after_binding_until_next_binding = - source_map.span_take_while(after_binding_until_end, |&ch| { - if ch == '}' { - found_closing_brace = true; - } - ch == ' ' || ch == ',' - }); - - // Combine the two spans. - // i.e. `a, ` or `a`. - // - // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` - let span = binding_span.with_hi(after_binding_until_next_binding.hi()); - - (found_closing_brace, span) -} - -/// Given a `binding_span`, return the span through to the comma or opening brace of the previous -/// binding. -/// -/// ```ignore (illustrative) -/// use foo::a::{a, b, c}; -/// // ^^--- binding span -/// // | -/// // returned span -/// -/// use foo::{a, b, c}; -/// // --- binding span -/// ``` -fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { - let source_map = sess.source_map(); - - // `prev_source` will contain all of the source that came before the span. - // Then split based on a command and take the first (i.e. closest to our span) - // snippet. In the example, this is a space. - let prev_source = source_map.span_to_prev_source(binding_span).ok()?; - - let prev_comma = prev_source.rsplit(',').collect::>(); - let prev_starting_brace = prev_source.rsplit('{').collect::>(); - if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { - return None; - } +#[derive(Diagnostic)] +#[diag("item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}`")] +pub(crate) struct TraitImplMismatch { + #[primary_span] + #[label("does not match trait")] + pub(crate) span: Span, + pub(crate) name: Ident, + pub(crate) kind: &'static str, + pub(crate) trait_path: String, + #[label("item in trait")] + pub(crate) trait_item_span: Span, +} - let prev_comma = prev_comma.first().unwrap(); - let prev_starting_brace = prev_starting_brace.first().unwrap(); +#[derive(Diagnostic)] +#[diag("derive helper attribute is used before it is introduced")] +pub(crate) struct LegacyDeriveHelpers { + #[label("the attribute is introduced here")] + pub span: Span, +} - // If the amount of source code before the comma is greater than - // the amount of source code before the starting brace then we've only - // got one item in the nested item (eg. `issue_52891::{self}`). - if prev_comma.len() > prev_starting_brace.len() { - return None; - } +#[derive(Diagnostic)] +#[diag("unused extern crate")] +pub(crate) struct UnusedExternCrate { + #[label("unused")] + pub span: Span, + #[suggestion( + "remove the unused `extern crate`", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + pub removal_span: Span, +} - Some(binding_span.with_lo(BytePos( - // Take away the number of bytes for the characters we've found and an - // extra for the comma. - binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, - ))) -} - -/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if -/// it is a nested use tree. -/// -/// ```ignore (illustrative) -/// use foo::a::{b, c}; -/// // ^^^^^^^^^^ -- false -/// -/// use foo::{a, b, c}; -/// // ^^^^^^^^^^ -- true -/// -/// use foo::{a, b::{c, d}}; -/// // ^^^^^^^^^^^^^^^ -- true -/// ``` -#[instrument(level = "debug", skip(sess))] -fn find_span_immediately_after_crate_name(sess: &Session, use_span: Span) -> (bool, Span) { - let source_map = sess.source_map(); - - // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. - let mut num_colons = 0; - // Find second colon.. `use issue_59764:` - let until_second_colon = source_map.span_take_while(use_span, |c| { - if *c == ':' { - num_colons += 1; - } - !matches!(c, ':' if num_colons == 2) - }); - // Find everything after the second colon.. `foo::{baz, makro};` - let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); - - let mut found_a_non_whitespace_character = false; - // Find the first non-whitespace character in `from_second_colon`.. `f` - let after_second_colon = source_map.span_take_while(from_second_colon, |c| { - if found_a_non_whitespace_character { - return false; - } - if !c.is_whitespace() { - found_a_non_whitespace_character = true; - } - true - }); +#[derive(Diagnostic)] +#[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] +pub(crate) struct ReexportPrivateDependency { + pub name: Symbol, + pub kind: &'static str, + pub krate: Symbol, +} + +#[derive(Diagnostic)] +#[diag("unused label")] +pub(crate) struct UnusedLabel; - // Find the first `{` in from_second_colon.. `foo::{` - let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); +#[derive(Diagnostic)] +#[diag("unused `#[macro_use]` import")] +pub(crate) struct UnusedMacroUse; - (next_left_bracket == after_second_colon, from_second_colon) +#[derive(Diagnostic)] +#[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] +#[help("remove it and import macros at use sites with a `use` item instead")] +pub(crate) struct MacroUseDeprecated; + +#[derive(Diagnostic)] +#[diag("macro `{$ident}` is private")] +pub(crate) struct MacroIsPrivate { + pub ident: Ident, } -/// A suggestion has already been emitted, change the wording slightly to clarify that both are -/// independent options. -enum Instead { - Yes, - No, +#[derive(Diagnostic)] +#[diag("unused macro definition: `{$name}`")] +pub(crate) struct UnusedMacroDefinition { + pub name: Symbol, } -/// Whether an existing place with an `use` item was found. -enum FoundUse { - Yes, - No, +#[derive(Diagnostic)] +#[diag("rule #{$n} of macro `{$name}` is never used")] +pub(crate) struct MacroRuleNeverUsed { + pub n: usize, + pub name: Symbol, } -/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. -pub(crate) enum DiagMode { - Normal, - /// The binding is part of a pattern - Pattern, - /// The binding is part of a use statement - Import { - /// `true` means diagnostics is for unresolved import - unresolved_import: bool, - /// `true` mean add the tips afterward for case `use a::{b,c}`, - /// rather than replacing within. - append: bool, +#[derive(Diagnostic)] +#[diag("`extern crate` is not idiomatic in the new edition")] +pub(crate) struct ExternCrateNotIdiomatic { + #[suggestion( + "convert it to a `use`", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] + pub span: Span, + pub code: &'static str, +} + +#[derive(Diagnostic)] +#[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] +#[help("import `macro_rules` with `use` to make it callable above its definition")] +pub(crate) struct OutOfScopeMacroCalls { + #[label("not found from {$location}")] + pub span: Span, + pub path: String, + pub location: String, +} + +#[derive(Diagnostic)] +#[diag( + "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" +)] +pub(crate) struct RedundantImportVisibility { + #[note("the most public imported item is `{$max_vis}`")] + pub span: Span, + #[help("reduce the glob import's visibility or increase visibility of imported items")] + pub help: (), + pub import_vis: String, + pub max_vis: String, +} + +#[derive(Diagnostic)] +#[diag("unknown diagnostic attribute")] +pub(crate) struct UnknownDiagnosticAttribute { + #[subdiagnostic] + pub help: Option, +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnknownDiagnosticAttributeHelp { + #[suggestion( + "an attribute with a similar name exists", + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" + )] + Typo { + #[primary_span] + span: Span, + typo_name: Symbol, }, + #[help("add `#![feature({$feature})]` to the crate attributes to enable")] + UseFeature { feature: Symbol }, } -pub(crate) fn import_candidates( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - // This is `None` if all placement locations are inside expansions - use_placement_span: Option, - candidates: &[ImportSuggestion], - mode: DiagMode, - append: &str, -) { - show_candidates( - tcx, - err, - use_placement_span, - candidates, - Instead::Yes, - FoundUse::Yes, - mode, - vec![], - append, - ); -} - -type PathString<'a> = (String, &'a str, Option, &'a Option, bool); - -/// When an entity with a given name is not available in scope, we search for -/// entities with that name in all crates. This method allows outputting the -/// results of this search in a programmer-friendly way. If any entities are -/// found and suggested, returns `true`, otherwise returns `false`. -fn show_candidates( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - // This is `None` if all placement locations are inside expansions - use_placement_span: Option, - candidates: &[ImportSuggestion], - instead: Instead, - found_use: FoundUse, - mode: DiagMode, - path: Vec, - append: &str, -) -> bool { - if candidates.is_empty() { - return false; - } +// FIXME: Make this properly translatable. +pub(crate) struct Ambiguity { + pub ident: Ident, + pub ambig_vis: Option, + pub kind: &'static str, + pub help: Option<&'static [&'static str]>, + pub b1_note: Spanned, + pub b1_help_msgs: Vec, + pub b2_note: Spanned, + pub b2_help_msgs: Vec, + /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. + pub is_error: bool, +} - let mut showed = false; - let mut accessible_path_strings: Vec> = Vec::new(); - let mut inaccessible_path_strings: Vec> = Vec::new(); - - candidates.iter().for_each(|c| { - if c.accessible { - // Don't suggest `#[doc(hidden)]` items from other crates - if c.doc_visible { - accessible_path_strings.push(( - pprust::path_to_string(&c.path), - c.descr, - c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), - &c.note, - c.via_import, - )) - } - } else { - inaccessible_path_strings.push(( - pprust::path_to_string(&c.path), - c.descr, - c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), - &c.note, - c.via_import, - )) +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + let Self { + ident, + ambig_vis, + kind, + help, + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error, + } = self; + + let mut diag = Diag::new(dcx, level, "").with_span(ident.span); + if is_error { + diag.code(E0659); } - }); - - // we want consistent results across executions, but candidates are produced - // by iterating through a hash map, so make sure they are ordered: - for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { - path_strings.sort_by(|a, b| a.0.cmp(&b.0)); - path_strings.dedup_by(|a, b| a.0 == b.0); - let core_path_strings = - path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); - let std_path_strings = - path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); - let foreign_crate_path_strings = - path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); - - // We list the `crate` local paths first. - // Then we list the `std`/`core` paths. - if std_path_strings.len() == core_path_strings.len() { - // Do not list `core::` paths if we are already listing the `std::` ones. - path_strings.extend(std_path_strings); + if let Some(ambig_vis) = ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); } else { - path_strings.extend(std_path_strings); - path_strings.extend(core_path_strings); + diag.primary_message(format!("`{}` is ambiguous", ident)); + diag.span_label(ident.span, "ambiguous name"); } - // List all paths from foreign crates last. - path_strings.extend(foreign_crate_path_strings); + diag.note(format!("ambiguous because of {}", kind)); + diag.span_note(b1_note.span, b1_note.node); + if let Some(help) = help { + for help in help { + diag.help(*help); + } + } + for help_msg in b1_help_msgs { + diag.help(help_msg); + } + diag.span_note(b2_note.span, b2_note.node); + for help_msg in b2_help_msgs { + diag.help(help_msg); + } + diag } +} - if !accessible_path_strings.is_empty() { - let (determiner, kind, s, name, through) = - if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { - ( - "this", - *descr, - "", - format!(" `{name}`"), - if *via_import { " through its public re-export" } else { "" }, - ) - } else { - // Get the unique item kinds and if there's only one, we use the right kind name - // instead of the more generic "items". - let kinds = accessible_path_strings - .iter() - .map(|(_, descr, _, _, _)| *descr) - .collect::>(); - let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; - let s = if kind.ends_with('s') { "es" } else { "s" }; - - ("one of these", kind, s, String::new(), "") - }; - - let instead = if let Instead::Yes = instead { " instead" } else { "" }; - let mut msg = if let DiagMode::Pattern = mode { - format!( - "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ - pattern", - ) - } else { - format!("consider importing {determiner} {kind}{s}{through}{instead}") - }; +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` never used")] +pub(crate) struct UnusedLifetime { + #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] + pub deletion_span: Option, - for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { - err.note(note.clone()); - } + pub ident: Ident, +} - let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { - msg.push(':'); +#[derive(Diagnostic)] +#[diag("ambiguous glob re-exports")] +pub(crate) struct AmbiguousGlobReexports { + #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] + pub first_reexport: Span, + #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] + pub duplicate_reexport: Span, - for candidate in accessible_path_strings { - msg.push('\n'); - msg.push_str(&candidate.0); - } - }; - - if let Some(span) = use_placement_span { - let (add_use, trailing) = match mode { - DiagMode::Pattern => { - err.span_suggestions( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - ); - return true; - } - DiagMode::Import { .. } => ("", ""), - DiagMode::Normal => ("use ", ";\n"), - }; - for candidate in &mut accessible_path_strings { - // produce an additional newline to separate the new use statement - // from the directly following item. - let additional_newline = if let FoundUse::No = found_use - && let DiagMode::Normal = mode - { - "\n" - } else { - "" - }; - candidate.0 = - format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); - } + pub name: String, + pub namespace: String, +} - match mode { - DiagMode::Import { append: true, .. } => { - append_candidates(&mut msg, accessible_path_strings); - err.span_help(span, msg); - } - _ => { - err.span_suggestions_with_style( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, - ); - } - } +#[derive(Diagnostic)] +#[diag("private item shadows public glob re-export")] +pub(crate) struct HiddenGlobReexports { + #[note( + "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" + )] + pub glob_reexport: Span, + #[note("but the private item here shadows it")] + pub private_item: Span, + + pub name: String, + pub namespace: String, +} - if let [first, .., last] = &path[..] { - let sp = first.ident.span.until(last.ident.span); - // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). - // Can happen for derive-generated spans. - if sp.can_be_used_for_suggestions() && !sp.is_empty() { - err.span_suggestion_verbose( - sp, - format!("if you import `{}`, refer to it directly", last.ident), - "", - Applicability::Unspecified, - ); - } - } - } else { - append_candidates(&mut msg, accessible_path_strings); - err.help(msg); - } - showed = true; - } - if !inaccessible_path_strings.is_empty() - && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. })) - { - let prefix = - if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; - if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] { - let msg = format!( - "{prefix}{descr} `{name}`{} exists but is inaccessible", - if let DiagMode::Pattern = mode { ", which" } else { "" } - ); - - if let Some(source_span) = source_span { - let span = tcx.sess.source_map().guess_head_span(*source_span); - let mut multi_span = MultiSpan::from_span(span); - multi_span.push_span_label(span, "not accessible"); - err.span_note(multi_span, msg); - } else { - err.note(msg); - } - if let Some(note) = (*note).as_deref() { - err.note(note.to_string()); - } - } else { - let descr = inaccessible_path_strings - .iter() - .map(|&(_, descr, _, _, _)| descr) - .all_equal_value() - .unwrap_or("item"); - let plural_descr = - if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; - - let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible"); - let mut has_colon = false; - - let mut spans = Vec::new(); - for (name, _, source_span, _, _) in &inaccessible_path_strings { - if let Some(source_span) = source_span { - let span = tcx.sess.source_map().guess_head_span(*source_span); - spans.push((name, span)); - } else { - if !has_colon { - msg.push(':'); - has_colon = true; - } - msg.push('\n'); - msg.push_str(name); - } - } +#[derive(Diagnostic)] +#[diag("the item `{$ident}` is imported redundantly")] +pub(crate) struct RedundantImport { + #[subdiagnostic] + pub subs: Vec, + pub ident: Ident, +} - let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); - for (name, span) in spans { - multi_span.push_span_label(span, format!("`{name}`: not accessible")); - } +#[derive(Subdiagnostic)] +pub(crate) enum RedundantImportSub { + #[label("the item `{$ident}` is already imported here")] + ImportedHere { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already defined here")] + DefinedHere { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already imported by the extern prelude")] + ImportedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already defined by the extern prelude")] + DefinedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, +} - for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { - err.note(note.clone()); - } +#[derive(Diagnostic)] +#[diag("unnecessary qualification")] +pub(crate) struct UnusedQualifications { + #[suggestion( + "remove the unnecessary path segments", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] + pub removal_span: Span, +} - err.span_note(multi_span, msg); - } - showed = true; - } - showed +#[derive(Diagnostic)] +#[diag( + "{$elided -> + [true] `&` without an explicit lifetime name cannot be used here + *[false] `'_` cannot be used here + }" +)] +pub(crate) struct AssociatedConstElidedLifetime { + #[suggestion( + "use the `'static` lifetime", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] + pub span: Span, + + pub code: &'static str, + pub elided: bool, + #[note("cannot automatically infer `'static` because of other lifetimes in scope")] + pub lifetimes_in_scope: MultiSpan, } -#[derive(Debug)] -struct UsePlacementFinder { - target_module: NodeId, - first_legal_span: Option, - first_use_span: Option, -} - -impl UsePlacementFinder { - fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { - let mut finder = - UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; - finder.visit_crate(krate); - if let Some(use_span) = finder.first_use_span { - (Some(use_span), FoundUse::Yes) - } else { - (finder.first_legal_span, FoundUse::No) - } - } +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` only used once")] +pub(crate) struct SingleUseLifetime { + #[label("this lifetime...")] + pub param_span: Span, + #[label("...is used only here")] + pub use_span: Span, + #[subdiagnostic] + pub suggestion: Option, + + pub ident: Ident, } -impl<'tcx> Visitor<'tcx> for UsePlacementFinder { - fn visit_crate(&mut self, c: &Crate) { - if self.target_module == CRATE_NODE_ID { - let inject = c.spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(&c.items); - } else { - visit::walk_crate(self, c); - } - } +#[derive(Subdiagnostic)] +#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] +pub(crate) struct SingleUseLifetimeSugg { + #[suggestion_part(code = "")] + pub deletion_span: Option, + #[suggestion_part(code = "{replace_lt}")] + pub use_span: Span, - fn visit_item(&mut self, item: &'tcx ast::Item) { - if self.target_module == item.id { - if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { - let inject = mod_spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(items); - } - } else { - visit::walk_item(self, item); - } - } + pub replace_lt: String, } -#[derive(Default)] -struct BindingVisitor { - identifiers: Vec, - spans: FxHashMap>, +#[derive(Diagnostic)] +#[diag( + "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" +)] +pub(crate) struct AbsPathWithModule { + #[subdiagnostic] + pub sugg: AbsPathWithModuleSugg, } -impl<'tcx> Visitor<'tcx> for BindingVisitor { - fn visit_pat(&mut self, pat: &ast::Pat) { - if let ast::PatKind::Ident(_, ident, _) = pat.kind { - self.identifiers.push(ident.name); - self.spans.entry(ident.name).or_default().push(ident.span); - } - visit::walk_pat(self, pat); - } +#[derive(Subdiagnostic)] +#[suggestion("use `crate`", code = "{replacement}")] +pub(crate) struct AbsPathWithModuleSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, + pub replacement: String, } -fn search_for_any_use_in_items(items: &[Box]) -> Option { - for item in items { - if let ItemKind::Use(..) = item.kind - && is_span_suitable_for_use_injection(item.span) - { - let mut lo = item.span.lo(); - for attr in &item.attrs { - if attr.span.eq_ctxt(item.span) { - lo = std::cmp::min(lo, attr.span.lo()); - } - } - return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); - } - } - None +#[derive(Diagnostic)] +#[diag("hidden lifetime parameters in types are deprecated")] +pub(crate) struct ElidedLifetimesInPaths { + #[subdiagnostic] + pub subdiag: rustc_errors::ElidedLifetimeInPathSubdiag, +} + +#[derive(Diagnostic)] +#[diag( + "{$num_snippets -> + [one] unused import: {$span_snippets} + *[other] unused imports: {$span_snippets} + }" +)] +pub(crate) struct UnusedImports { + #[subdiagnostic] + pub sugg: Option, + #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] + pub test_module_span: Option, + + pub span_snippets: DiagArgValue, + pub num_snippets: usize, } -fn is_span_suitable_for_use_injection(s: Span) -> bool { - // don't suggest placing a use before the prelude - // import or other generated ones - !s.from_expansion() +#[derive(Subdiagnostic)] +pub(crate) enum UnusedImportsSugg { + #[suggestion( + "remove the whole `use` item", + applicability = "machine-applicable", + code = "", + style = "tool-only" + )] + RemoveWholeUse { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + "{$num_to_remove -> + [one] remove the unused import + *[other] remove the unused imports + }", + applicability = "machine-applicable", + style = "tool-only" + )] + RemoveImports { + #[suggestion_part(code = "")] + remove_spans: Vec, + num_to_remove: usize, + }, } diff --git a/compiler/rustc_resolve/src/error_helper.rs b/compiler/rustc_resolve/src/error_helper.rs new file mode 100644 index 0000000000000..3f62f3b2df3bd --- /dev/null +++ b/compiler/rustc_resolve/src/error_helper.rs @@ -0,0 +1,3935 @@ +// ignore-tidy-filelength +use std::mem; +use std::ops::ControlFlow; + +use itertools::Itertools as _; +use rustc_ast::visit::{self, Visitor}; +use rustc_ast::{ + self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path, + join_path_idents, +}; +use rustc_ast_pretty::pprust; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_errors::codes::*; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, + struct_span_code_err, +}; +use rustc_feature::BUILTIN_ATTRIBUTES; +use rustc_hir::attrs::{CfgEntry, StrippedCfgItem}; +use rustc_hir::def::Namespace::{self, *}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::{PrimTy, Stability, StabilityLevel, find_attr}; +use rustc_middle::bug; +use rustc_middle::ty::{TyCtxt, Visibility}; +use rustc_session::Session; +use rustc_session::lint::builtin::{ + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, + AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, +}; +use rustc_session::utils::was_invoked_from_cargo; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::edition::Edition; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::SourceMap; +use rustc_span::{ + BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym, +}; +use thin_vec::{ThinVec, thin_vec}; +use tracing::{debug, instrument}; + +use crate::diagnostics::{ + self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, + ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, + MaybeMissingMacroRulesName, +}; +use crate::hygiene::Macros20NormalizedSyntaxContext; +use crate::imports::{Import, ImportKind}; +use crate::late::{DiagMetadata, PatternSource, Rib}; +use crate::{ + AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, + DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, + LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, + PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, + VisResolutionError, path_names_to_string, +}; + +/// A vector of spans and replacements, a message and applicability. +pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); + +/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a +/// similarly named label and whether or not it is reachable. +pub(crate) type LabelSuggestion = (Ident, bool); + +#[derive(Clone)] +pub(crate) struct StructCtor { + pub res: Res, + pub vis: Visibility, + pub field_visibilities: Vec>, +} + +impl StructCtor { + pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool { + self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m)) + } +} + +#[derive(Debug)] +pub(crate) enum SuggestionTarget { + /// The target has a similar name as the name used by the programmer (probably a typo) + SimilarlyNamed, + /// The target is the only valid item that can be used in the corresponding context + SingleItem, +} + +#[derive(Debug)] +pub(crate) struct TypoSuggestion { + pub candidate: Symbol, + /// The source location where the name is defined; None if the name is not defined + /// in source e.g. primitives + pub span: Option, + pub res: Res, + pub target: SuggestionTarget, +} + +impl TypoSuggestion { + pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } + } + pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { + Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } + } + pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } + } +} + +/// A free importable items suggested in case of resolution failure. +#[derive(Debug, Clone)] +pub(crate) struct ImportSuggestion { + pub did: Option, + pub descr: &'static str, + pub path: Path, + pub accessible: bool, + // false if the path traverses a foreign `#[doc(hidden)]` item. + pub doc_visible: bool, + pub via_import: bool, + /// An extra note that should be issued if this item is suggested + pub note: Option, + pub is_stable: bool, +} + +/// Adjust the impl span so that just the `impl` keyword is taken by removing +/// everything after `<` (`"impl Iterator for A {}" -> "impl"`) and +/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). +/// +/// *Attention*: the method used is very fragile since it essentially duplicates the work of the +/// parser. If you need to use this function or something similar, please consider updating the +/// `source_map` functions and this function to something more robust. +fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { + let impl_span = sm.span_until_char(impl_span, '<'); + sm.span_until_whitespace(impl_span) +} + +impl<'ra, 'tcx> Resolver<'ra, 'tcx> { + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { + self.tcx.dcx() + } + + pub(crate) fn report_errors(&mut self, krate: &Crate) { + self.report_delayed_vis_resolution_errors(); + self.report_with_use_injections(krate); + + for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { + self.lint_buffer.buffer_lint( + MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + CRATE_NODE_ID, + span_use, + diagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths { + definition: span_def, + }, + ); + } + + for ambiguity_error in &self.ambiguity_errors { + let mut diag = self.ambiguity_diagnostic(ambiguity_error); + + if let Some(ambiguity_warning) = ambiguity_error.warning { + let node_id = match ambiguity_error.b1.0.kind { + DeclKind::Import { import, .. } => import.root_id, + DeclKind::Def(_) => CRATE_NODE_ID, + }; + + let lint = match ambiguity_warning { + _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, + AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, + AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, + }; + + self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); + } else { + diag.is_error = true; + self.dcx().emit_err(diag); + } + } + + let mut reported_spans = FxHashSet::default(); + for error in mem::take(&mut self.privacy_errors) { + if reported_spans.insert(error.dedup_span) { + self.report_privacy_error(&error); + } + } + } + + fn report_delayed_vis_resolution_errors(&mut self) { + for DelayedVisResolutionError { vis, parent_scope, error } in + mem::take(&mut self.delayed_vis_resolution_errors) + { + match self.try_resolve_visibility(&parent_scope, &vis, true) { + Ok(_) => self.report_vis_error(error), + Err(error) => self.report_vis_error(error), + }; + } + } + + fn report_with_use_injections(&mut self, krate: &Crate) { + for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in + mem::take(&mut self.use_injections) + { + let (span, found_use) = if node_id != DUMMY_NODE_ID { + UsePlacementFinder::check(krate, node_id) + } else { + (None, FoundUse::No) + }; + + if !candidates.is_empty() { + show_candidates( + self.tcx, + &mut err, + span, + &candidates, + if instead { Instead::Yes } else { Instead::No }, + found_use, + DiagMode::Normal, + path, + "", + ); + err.emit(); + } else if let Some((span, msg, sugg, appl)) = suggestion { + err.span_suggestion_verbose(span, msg, sugg, appl); + err.emit(); + } else if let [segment] = path.as_slice() + && is_call + { + err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); + } else { + err.emit(); + } + } + } + + pub(crate) fn report_conflict( + &mut self, + ident: IdentKey, + ns: Namespace, + old_binding: Decl<'ra>, + new_binding: Decl<'ra>, + ) { + // Error on the second of two conflicting names + if old_binding.span.lo() > new_binding.span.lo() { + return self.report_conflict(ident, ns, new_binding, old_binding); + } + + let container = match old_binding.parent_module.unwrap().expect_local().kind { + // Avoid using TyCtxt::def_kind_descr in the resolver, because it + // indirectly *calls* the resolver, and would cause a query cycle. + ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), + ModuleKind::Block => "block", + }; + + let (name, span) = + (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); + + if self.name_already_seen.get(&name) == Some(&span) { + return; + } + + let old_kind = match (ns, old_binding.res()) { + (ValueNS, _) => "value", + (MacroNS, _) => "macro", + (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", + (TypeNS, Res::Def(DefKind::Mod, _)) => "module", + (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", + (TypeNS, _) => "type", + }; + + let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { + (true, true) => E0259, + (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { + true => E0254, + false => E0260, + }, + _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { + (false, false) => E0428, + (true, true) => E0252, + _ => E0255, + }, + }; + + let label = match new_binding.is_import_user_facing() { + true => diagnostics::NameDefinedMultipleTimeLabel::Reimported { span, name }, + false => diagnostics::NameDefinedMultipleTimeLabel::Redefined { span, name }, + }; + + let old_binding_label = + (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { + let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); + match old_binding.is_import_user_facing() { + true => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Import { + span, + old_kind, + name, + }, + false => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Definition { + span, + old_kind, + name, + }, + } + }); + + let mut err = self + .dcx() + .create_err(diagnostics::NameDefinedMultipleTime { + span, + name, + descr: ns.descr(), + container, + label, + old_binding_label, + }) + .with_code(code); + + // See https://github.com/rust-lang/rust/issues/32354 + use DeclKind::Import; + let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| { + !binding.span.is_dummy() + && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) + }; + let import = match (&new_binding.kind, &old_binding.kind) { + // If there are two imports where one or both have attributes then prefer removing the + // import without attributes. + (Import { import: new, .. }, Import { import: old, .. }) + if { + (new.has_attributes || old.has_attributes) + && can_suggest(old_binding, *old) + && can_suggest(new_binding, *new) + } => + { + if old.has_attributes { + Some((*new, new_binding.span, true)) + } else { + Some((*old, old_binding.span, true)) + } + } + // Otherwise prioritize the new binding. + (Import { import, .. }, other) if can_suggest(new_binding, *import) => { + Some((*import, new_binding.span, other.is_import())) + } + (other, Import { import, .. }) if can_suggest(old_binding, *import) => { + Some((*import, old_binding.span, other.is_import())) + } + _ => None, + }; + + // Check if the target of the use for both bindings is the same. + let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); + let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); + let from_item = + self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); + // Only suggest removing an import if both bindings are to the same def, if both spans + // aren't dummy spans. Further, if both bindings are imports, then the ident must have + // been introduced by an item. + let should_remove_import = duplicate + && !has_dummy_span + && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); + + match import { + Some((import, span, true)) if should_remove_import && import.is_nested() => { + self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); + } + Some((import, _, true)) if should_remove_import && !import.is_glob() => { + // Simple case - remove the entire import. Due to the above match arm, this can + // only be a single use so just remove it entirely. + err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); + } + Some((import, span, _)) => { + self.add_suggestion_for_rename_of_use(&mut err, name, import, span); + } + _ => {} + } + + err.emit(); + self.name_already_seen.insert(name, span); + } + + /// This function adds a suggestion to change the binding name of a new import that conflicts + /// with an existing import. + /// + /// ```text,ignore (diagnostic) + /// help: you can use `as` to change the binding name of the import + /// | + /// LL | use foo::bar as other_bar; + /// | ^^^^^^^^^^^^^^^^^^^^^ + /// ``` + fn add_suggestion_for_rename_of_use( + &self, + err: &mut Diag<'_>, + name: Symbol, + import: Import<'_>, + binding_span: Span, + ) { + let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { + format!("Other{name}") + } else { + format!("other_{name}") + }; + + let mut suggestion = None; + let mut span = binding_span; + match import.kind { + ImportKind::Single { source, .. } => { + if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) + && pos as usize <= snippet.len() + { + span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( + binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), + ); + suggestion = Some(format!(" as {suggested_name}")); + } + } + ImportKind::ExternCrate { source, target, .. } => { + suggestion = Some(format!( + "extern crate {} as {};", + source.unwrap_or(target.name), + suggested_name, + )) + } + _ => unreachable!(), + } + + if let Some(suggestion) = suggestion { + err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion }); + } else { + err.subdiagnostic(ChangeImportBinding { span }); + } + } + + /// This function adds a suggestion to remove an unnecessary binding from an import that is + /// nested. In the following example, this function will be invoked to remove the `a` binding + /// in the second use statement: + /// + /// ```ignore (diagnostic) + /// use issue_52891::a; + /// use issue_52891::{d, a, e}; + /// ``` + /// + /// The following suggestion will be added: + /// + /// ```ignore (diagnostic) + /// use issue_52891::{d, a, e}; + /// ^-- help: remove unnecessary import + /// ``` + /// + /// If the nested use contains only one import then the suggestion will remove the entire + /// line. + /// + /// It is expected that the provided import is nested - this isn't checked by the + /// function. If this invariant is not upheld, this function's behaviour will be unexpected + /// as characters expected by span manipulations won't be present. + fn add_suggestion_for_duplicate_nested_use( + &self, + err: &mut Diag<'_>, + import: Import<'_>, + binding_span: Span, + ) { + assert!(import.is_nested()); + + // Two examples will be used to illustrate the span manipulations we're doing: + // + // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, a, e};`. + // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, e, a};`. + + let (found_closing_brace, span) = + find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span); + + // If there was a closing brace then identify the span to remove any trailing commas from + // previous imports. + if found_closing_brace { + if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { + err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { span }); + } else { + // Remove the entire line if we cannot extend the span back, this indicates an + // `issue_52891::{self}` case. + err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); + } + + return; + } + + err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { span }); + } + + pub(crate) fn lint_if_path_starts_with_module( + &mut self, + finalize: Finalize, + path: &[Segment], + second_binding: Option>, + ) { + let Finalize { node_id, root_span, .. } = finalize; + + let first_name = match path.get(0) { + // In the 2018 edition this lint is a hard error, so nothing to do + Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => { + seg.ident.name + } + _ => return, + }; + + // We're only interested in `use` paths which should start with + // `{{root}}` currently. + if first_name != kw::PathRoot { + return; + } + + match path.get(1) { + // If this import looks like `crate::...` it's already good + Some(Segment { ident, .. }) if ident.name == kw::Crate => return, + // Otherwise go below to see if it's an extern crate + Some(_) => {} + // If the path has length one (and it's `PathRoot` most likely) + // then we don't know whether we're gonna be importing a crate or an + // item in our crate. Defer this lint to elsewhere + None => return, + } + + // If the first element of our path was actually resolved to an + // `ExternCrate` (also used for `crate::...`) then no need to issue a + // warning, this looks all good! + if let Some(binding) = second_binding + && let DeclKind::Import { import, .. } = binding.kind + // Careful: we still want to rewrite paths from renamed extern crates. + && let ImportKind::ExternCrate { source: None, .. } = import.kind + { + return; + } + + self.lint_buffer.dyn_buffer_lint_any( + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + node_id, + root_span, + move |dcx, level, sess| { + let (replacement, applicability) = match sess + .downcast_ref::() + .expect("expected a `Session`") + .source_map() + .span_to_snippet(root_span) + { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + diagnostics::AbsPathWithModule { + sugg: diagnostics::AbsPathWithModuleSugg { + span: root_span, + applicability, + replacement, + }, + } + .into_diag(dcx, level) + }, + ); + } + + pub(crate) fn add_module_candidates( + &self, + module: Module<'ra>, + names: &mut Vec, + filter_fn: &impl Fn(Res) -> bool, + ctxt: Option, + ) { + module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { + let res = binding.res(); + if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { + names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); + } + }); + } + + /// Combines an error with provided span and emits it. + /// + /// This takes the error provided, combines it with the span and any additional spans inside the + /// error and emits it. + pub(crate) fn report_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'ra>, + ) -> ErrorGuaranteed { + self.into_struct_error(span, resolution_error).emit() + } + + pub(crate) fn into_struct_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'ra>, + ) -> Diag<'_> { + match resolution_error { + ResolutionError::GenericParamsFromOuterItem { + outer_res, + has_generic_params, + def_kind, + inner_item, + current_self_ty, + } => { + use diagnostics::GenericParamsFromOuterItemLabel as Label; + let static_or_const = match def_kind { + DefKind::Static { .. } => { + Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Static) + } + DefKind::Const { .. } => { + Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Const) + } + _ => None, + }; + let is_self = + matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); + let mut err = diagnostics::GenericParamsFromOuterItem { + span, + label: None, + refer_to_type_directly: None, + use_let: None, + sugg: None, + static_or_const, + is_self, + item: inner_item.as_ref().map(|(label_span, _, kind)| { + diagnostics::GenericParamsFromOuterItemInnerItem { + span: *label_span, + descr: kind.descr().to_string(), + is_self, + } + }), + }; + + let sm = self.tcx.sess.source_map(); + // Note: do not early return for missing def_id here, + // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. + let def_id = match outer_res { + Res::SelfTyParam { .. } => { + err.label = Some(Label::SelfTyParam(span)); + None + } + Res::SelfTyAlias { alias_to: def_id, .. } => { + err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( + sm, + self.def_span(def_id), + ))); + err.refer_to_type_directly = current_self_ty + .map(|snippet| diagnostics::UseTypeDirectly { span, snippet }); + None + } + Res::Def(DefKind::TyParam, def_id) => { + err.label = Some(Label::TyParam(self.def_span(def_id))); + Some(def_id) + } + Res::Def(DefKind::ConstParam, def_id) => { + err.label = Some(Label::ConstParam(self.def_span(def_id))); + Some(def_id) + } + _ => { + bug!( + "GenericParamsFromOuterItem should only be used with \ + Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ + DefKind::ConstParam" + ); + } + }; + + if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { + err.use_let = Some(diagnostics::GenericParamsFromOuterItemUseLet { + span: sm.span_until_whitespace(*item_span), + }); + } + + if let Some(def_id) = def_id + && let HasGenericParams::Yes(span) = has_generic_params + && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) + { + let name = self.tcx.item_name(def_id); + let (span, snippet) = if span.is_empty() { + let snippet = format!("<{name}>"); + (span, snippet) + } else { + let span = sm.span_through_char(span, '<').shrink_to_hi(); + let snippet = format!("{name}, "); + (span, snippet) + }; + err.sugg = Some(diagnostics::GenericParamsFromOuterItemSugg { span, snippet }); + } + + self.dcx().create_err(err) + } + ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { + self.dcx().create_err(diagnostics::NameAlreadyUsedInParameterList { + span, + first_use_span, + name, + }) + } + ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { + self.dcx().create_err(diagnostics::MethodNotMemberOfTrait { + span, + method, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedFnWithSimilarNameExists { + span: method.span, + candidate: c, + }), + }) + } + ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { + self.dcx().create_err(diagnostics::TypeNotMemberOfTrait { + span, + type_, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedTypeWithSimilarNameExists { + span: type_.span, + candidate: c, + }), + }) + } + ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { + self.dcx().create_err(diagnostics::ConstNotMemberOfTrait { + span, + const_, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedConstWithSimilarNameExists { + span: const_.span, + candidate: c, + }), + }) + } + ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { + let BindingError { name, target, origin, could_be_path } = binding_error; + + let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); + target_sp.sort(); + target_sp.dedup(); + let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); + origin_sp.sort(); + origin_sp.dedup(); + + let msp = MultiSpan::from_spans(target_sp.clone()); + let mut err = self.dcx().create_err(diagnostics::VariableIsNotBoundInAllPatterns { + multispan: msp, + name, + }); + for sp in target_sp { + err.subdiagnostic(diagnostics::PatternDoesntBindName { span: sp, name }); + } + for sp in &origin_sp { + err.subdiagnostic(diagnostics::VariableNotInAllPatterns { span: *sp }); + } + let mut suggested_typo = false; + if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) + && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) + { + // The check above is so that when we encounter `match foo { (a | b) => {} }`, + // we don't suggest `(a | a) => {}`, which would never be what the user wants. + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(diagnostics::PatternBindingTypo { + spans: origin_sp, + typo, + }); + suggested_typo = true; + } + } + if could_be_path { + let import_suggestions = self.lookup_import_candidates( + name, + Namespace::ValueNS, + &parent_scope, + &|res: Res| { + matches!( + res, + Res::Def( + DefKind::Ctor(CtorOf::Variant, CtorKind::Const) + | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) + | DefKind::Const { .. } + | DefKind::AssocConst { .. }, + _, + ) + ) + }, + ); + + if import_suggestions.is_empty() && !suggested_typo { + let kind_matches: [fn(DefKind) -> bool; 4] = [ + |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), + |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), + |kind| matches!(kind, DefKind::Const { .. }), + |kind| matches!(kind, DefKind::AssocConst { .. }), + ]; + let mut local_names = vec![]; + self.add_module_candidates( + parent_scope.module, + &mut local_names, + &|res| matches!(res, Res::Def(_, _)), + None, + ); + let local_names: FxHashSet<_> = local_names + .into_iter() + .filter_map(|s| match s.res { + Res::Def(_, def_id) => Some(def_id), + _ => None, + }) + .collect(); + + let mut local_suggestions = vec![]; + let mut suggestions = vec![]; + for matches_kind in kind_matches { + if let Some(suggestion) = self.early_lookup_typo_candidate( + ScopeSet::All(Namespace::ValueNS), + &parent_scope, + name, + &|res: Res| match res { + Res::Def(k, _) => matches_kind(k), + _ => false, + }, + ) && let Res::Def(kind, mut def_id) = suggestion.res + { + if let DefKind::Ctor(_, _) = kind { + def_id = self.tcx.parent(def_id); + } + let kind = kind.descr(def_id); + if local_names.contains(&def_id) { + // The item is available in the current scope. Very likely to + // be a typo. Don't use the full path. + local_suggestions.push(( + suggestion.candidate, + suggestion.candidate.to_string(), + kind, + )); + } else { + suggestions.push(( + suggestion.candidate, + self.def_path_str(def_id), + kind, + )); + } + } + } + let suggestions = if !local_suggestions.is_empty() { + // There is at least one item available in the current scope that is a + // likely typo. We only show those. + local_suggestions + } else { + suggestions + }; + for (name, sugg, kind) in suggestions { + err.span_suggestion_verbose( + span, + format!( + "you might have meant to use the similarly named {kind} `{name}`", + ), + sugg, + Applicability::MaybeIncorrect, + ); + suggested_typo = true; + } + } + if import_suggestions.is_empty() && !suggested_typo { + let help_msg = format!( + "if you meant to match on a unit struct, unit variant or a `const` \ + item, consider making the path in the pattern qualified: \ + `path::to::ModOrType::{name}`", + ); + err.span_help(span, help_msg); + } + show_candidates( + self.tcx, + &mut err, + Some(span), + &import_suggestions, + Instead::No, + FoundUse::Yes, + DiagMode::Pattern, + vec![], + "", + ); + } + err + } + ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { + self.dcx().create_err(diagnostics::VariableBoundWithDifferentMode { + span, + first_binding_span, + variable_name, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { + self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInParameterList { + span, + identifier, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { + self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInSamePattern { + span, + identifier, + }) + } + ResolutionError::UndeclaredLabel { name, suggestion } => { + let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion + { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(diagnostics::LabelWithSimilarNameReachable(ident.span)), + Some(diagnostics::TryUsingSimilarlyNamedLabel { + span, + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(diagnostics::UnreachableLabelWithSimilarNameExists { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.dcx().create_err(diagnostics::UndeclaredLabel { + span, + name, + sub_reachable, + sub_reachable_suggestion, + sub_unreachable, + }) + } + ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { + let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); + err.span_label(span, label); + + if let Some((suggestions, msg, applicability)) = suggestion { + if suggestions.is_empty() { + err.help(msg); + return err; + } + err.multipart_suggestion(msg, suggestions, applicability); + } + + let module = match module { + Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, + _ => CRATE_DEF_ID.to_def_id(), + }; + self.find_cfg_stripped(&mut err, &segment, module); + + err + } + ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { + self.dcx().create_err(diagnostics::CannotCaptureDynamicEnvironmentInFnItem { span }) + } + ResolutionError::AttemptToUseNonConstantValueInConstant { + ident, + suggestion, + current, + type_span, + } => { + // let foo =... + // ^^^ given this Span + // ------- get this Span to have an applicable suggestion + + // edit: + // only do this if the const and usage of the non-constant value are on the same line + // the further the two are apart, the higher the chance of the suggestion being wrong + + let sp = self + .tcx + .sess + .source_map() + .span_extend_to_prev_str(ident.span, current, true, false); + + let (with, with_label, without) = match sp { + Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => { + let sp = sp + .with_lo(BytePos(sp.lo().0 - (current.len() as u32))) + .until(ident.span); + + // Only suggest replacing the binding keyword if this is a simple + // binding. + // + // Note: this approach still incorrectly suggests for irrefutable + // patterns like `if let x = 1 { const { x } }`, since the text + // between `let` and the identifier is just whitespace. + // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs + let is_simple_binding = + self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| { + let after_keyword = snippet[current.len()..].trim(); + after_keyword.is_empty() || after_keyword == "mut" + }); + + if is_simple_binding { + ( + Some(diagnostics::AttemptToUseNonConstantValueInConstantWithSuggestion { + span: sp, + suggestion, + current, + type_span, + }), + Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), + None, + ) + } else { + ( + None, + Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), + None, + ) + } + } + _ => ( + None, + None, + Some( + diagnostics::AttemptToUseNonConstantValueInConstantWithoutSuggestion { + ident_span: ident.span, + suggestion, + }, + ), + ), + }; + + self.dcx().create_err(diagnostics::AttemptToUseNonConstantValueInConstant { + span, + with, + with_label, + without, + }) + } + ResolutionError::BindingShadowsSomethingUnacceptable { + shadowing_binding, + name, + participle, + article, + shadowed_binding, + shadowed_binding_span, + } => self.dcx().create_err(diagnostics::BindingShadowsSomethingUnacceptable { + span, + shadowing_binding, + shadowed_binding, + article, + sub_suggestion: match (shadowing_binding, shadowed_binding) { + ( + PatternSource::Match, + Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), + ) => Some(diagnostics::BindingShadowsSomethingUnacceptableSuggestion { + span, + name, + }), + _ => None, + }, + shadowed_binding_span, + participle, + name, + }), + ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason { + ForwardGenericParamBanReason::Default => { + self.dcx().create_err(diagnostics::ForwardDeclaredGenericParam { param, span }) + } + ForwardGenericParamBanReason::ConstParamTy => self + .dcx() + .create_err(diagnostics::ForwardDeclaredGenericInConstParamTy { param, span }), + }, + ResolutionError::ParamInTyOfConstParam { name } => { + self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name }) + } + ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { + self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { + span, + name, + param_kind: is_type, + help: self.tcx.sess.is_nightly_build(), + is_gca, + help_gca: is_gca, + }) + } + ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => { + self.dcx().create_err(diagnostics::ParamInEnumDiscriminant { + span, + name, + param_kind: is_type, + }) + } + ResolutionError::ForwardDeclaredSelf(reason) => match reason { + ForwardGenericParamBanReason::Default => { + self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) + } + ForwardGenericParamBanReason::ConstParamTy => { + self.dcx().create_err(diagnostics::SelfInConstGenericTy { span }) + } + }, + ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { + let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = + match suggestion { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(diagnostics::UnreachableLabelSubLabel { + ident_span: ident.span, + }), + Some(diagnostics::UnreachableLabelSubSuggestion { + span, + // intentionally taking 'ident.name' instead of 'ident' itself, as this + // could be used in suggestion context + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(diagnostics::UnreachableLabelSubLabelUnreachable { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.dcx().create_err(diagnostics::UnreachableLabel { + span, + name, + definition_span, + sub_suggestion, + sub_suggestion_label, + sub_unreachable_label, + }) + } + ResolutionError::TraitImplMismatch { + name, + kind, + code, + trait_item_span, + trait_path, + } => self + .dcx() + .create_err(diagnostics::TraitImplMismatch { + span, + name, + kind, + trait_path, + trait_item_span, + }) + .with_code(code), + ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { + self.dcx().create_err(diagnostics::TraitImplDuplicate { + span, + name, + trait_item_span, + old_span, + }) + } + ResolutionError::InvalidAsmSym => { + self.dcx().create_err(diagnostics::InvalidAsmSym { span }) + } + ResolutionError::LowercaseSelf => { + self.dcx().create_err(diagnostics::LowercaseSelf { span }) + } + ResolutionError::BindingInNeverPattern => { + self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) + } + } + } + + pub(crate) fn report_vis_error( + &mut self, + vis_resolution_error: VisResolutionError, + ) -> ErrorGuaranteed { + match vis_resolution_error { + VisResolutionError::Relative2018(span, path) => { + self.dcx().create_err(diagnostics::Relative2018 { + span, + path_span: path.span, + // intentionally converting to String, as the text would also be used as + // in suggestion context + path_str: pprust::path_to_string(&path), + }) + } + VisResolutionError::AncestorOnly(span) => { + self.dcx().create_err(diagnostics::AncestorOnly(span)) + } + VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self + .into_struct_error( + span, + ResolutionError::FailedToResolve { + segment, + label, + suggestion, + module: None, + message, + }, + ), + VisResolutionError::ExpectedFound(span, path_str, res) => { + self.dcx().create_err(diagnostics::ExpectedModuleFound { span, res, path_str }) + } + VisResolutionError::Indeterminate(span) => { + self.dcx().create_err(diagnostics::Indeterminate(span)) + } + VisResolutionError::ModuleOnly(span) => { + self.dcx().create_err(diagnostics::ModuleOnly(span)) + } + } + .emit() + } + + pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + path.push(def_id); + if def_id.is_top_level_module() { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + path.into_iter() + .rev() + .map(|def_id| { + self.tcx + .opt_item_name(def_id) + .map(|name| { + match ( + def_id.is_top_level_module(), + def_id.is_local(), + self.tcx.sess.edition(), + ) { + (true, true, Edition::Edition2015) => String::new(), + (true, true, _) => kw::Crate.to_string(), + (true, false, _) | (false, _, _) => name.to_string(), + } + }) + .unwrap_or_else(|| "_".to_string()) + }) + .collect::>() + .join("::") + } + + pub(crate) fn add_scope_set_candidates( + &mut self, + suggestions: &mut Vec, + scope_set: ScopeSet<'ra>, + ps: &ParentScope<'ra>, + sp: Span, + filter_fn: &impl Fn(Res) -> bool, + ) { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { + match scope { + Scope::DeriveHelpers(expn_id) => { + let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); + if filter_fn(res) { + suggestions.extend( + this.helper_attrs.get(&expn_id).into_iter().flatten().map( + |&(ident, orig_ident_span, _)| { + TypoSuggestion::new(ident.name, orig_ident_span, res) + }, + ), + ); + } + } + Scope::DeriveHelpersCompat => { + // Never recommend deprecated helper attributes. + } + Scope::MacroRules(macro_rules_scope) => { + if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { + let res = macro_rules_def.decl.res(); + if filter_fn(res) { + suggestions.push(TypoSuggestion::new( + macro_rules_def.ident.name, + macro_rules_def.orig_ident_span, + res, + )) + } + } + } + Scope::ModuleNonGlobs(module, _) => { + this.add_module_candidates(module, suggestions, filter_fn, None); + } + Scope::ModuleGlobs(..) => { + // Already handled in `ModuleNonGlobs`. + } + Scope::MacroUsePrelude => { + suggestions.extend(this.macro_use_prelude.iter().filter_map( + |(name, binding)| { + let res = binding.res(); + filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) + }, + )); + } + Scope::BuiltinAttrs => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy)); + if filter_fn(res) { + suggestions.extend( + BUILTIN_ATTRIBUTES + .iter() + // These trace attributes are compiler-generated and have + // deliberately invalid names. + .filter(|attr| { + !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) + }) + .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), + ); + } + } + Scope::ExternPreludeItems => { + // Add idents from both item and flag scopes. + suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { + let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); + filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) + })); + } + Scope::ExternPreludeFlags => {} + Scope::ToolPrelude => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); + suggestions.extend( + this.registered_tools + .iter() + .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), + ); + } + Scope::StdLibPrelude => { + if let Some(prelude) = this.prelude { + let mut tmp_suggestions = Vec::new(); + this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); + suggestions.extend( + tmp_suggestions + .into_iter() + .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)), + ); + } + } + Scope::BuiltinTypes => { + suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { + let res = Res::PrimTy(*prim_ty); + filter_fn(res) + .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) + })) + } + } + + ControlFlow::<()>::Continue(()) + }); + } + + /// Lookup typo candidate in scope for a macro or import. + fn early_lookup_typo_candidate( + &mut self, + scope_set: ScopeSet<'ra>, + parent_scope: &ParentScope<'ra>, + ident: Ident, + filter_fn: &impl Fn(Res) -> bool, + ) -> Option { + let mut suggestions = Vec::new(); + self.add_scope_set_candidates( + &mut suggestions, + scope_set, + parent_scope, + ident.span, + filter_fn, + ); + + // Make sure error reporting is deterministic. + suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); + + match find_best_match_for_name( + &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), + ident.name, + None, + ) { + Some(found) if found != ident.name => { + suggestions.into_iter().find(|suggestion| suggestion.candidate == found) + } + _ => None, + } + } + + fn lookup_import_candidates_from_module( + &self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + start_module: Module<'ra>, + crate_path: ThinVec, + filter_fn: FilterFn, + ) -> Vec + where + FilterFn: Fn(Res) -> bool, + { + let mut candidates = Vec::new(); + let mut seen_modules = FxHashSet::default(); + let start_did = start_module.def_id(); + let mut worklist = vec![( + start_module, + ThinVec::::new(), + true, + start_did.is_local() || !self.tcx.is_doc_hidden(start_did), + true, + )]; + let mut worklist_via_import = vec![]; + + while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) = + match worklist.pop() { + None => worklist_via_import.pop(), + Some(x) => Some(x), + } + { + let in_module_is_extern = !in_module.def_id().is_local(); + in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { + // Avoid non-importable candidates. + if name_binding.is_assoc_item() + && !this.tcx.features().import_trait_associated_functions() + { + return; + } + + if ident.name == kw::Underscore { + return; + } + + let child_accessible = + accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); + + // do not venture inside inaccessible items of other crates + if in_module_is_extern && !child_accessible { + return; + } + + let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); + + // There is an assumption elsewhere that paths of variants are in the enum's + // declaration and not imported. With this assumption, the variant component is + // chopped and the rest of the path is assumed to be the enum's own path. For + // errors where a variant is used as the type instead of the enum, this causes + // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. + if via_import && name_binding.is_possibly_imported_variant() { + return; + } + + // #90113: Do not count an inaccessible reexported item as a candidate. + if let DeclKind::Import { source_decl, .. } = name_binding.kind + && this.is_accessible_from(source_decl.vis(), parent_scope.module) + && !this.is_accessible_from(name_binding.vis(), parent_scope.module) + { + return; + } + + let res = name_binding.res(); + let did = match res { + Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), + _ => res.opt_def_id(), + }; + let child_doc_visible = doc_visible + && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); + + // collect results based on the filter function + // avoid suggesting anything from the same module in which we are resolving + // avoid suggesting anything with a hygienic name + if ident.name == lookup_ident.name + && ns == namespace + && in_module != parent_scope.module + && ident.ctxt.is_root() + && filter_fn(res) + { + // create the path + let mut segms = if lookup_ident.span.at_least_rust_2018() { + // crate-local absolute paths start with `crate::` in edition 2018 + // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) + crate_path.clone() + } else { + ThinVec::new() + }; + segms.append(&mut path_segments.clone()); + + segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + let path = Path { span: name_binding.span, segments: segms, tokens: None }; + + if child_accessible + // Remove invisible match if exists + && let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.accessible) + { + candidates.remove(idx); + } + + let is_stable = if is_stable + && let Some(did) = did + && this.is_stable(did, path.span) + { + true + } else { + false + }; + + // Rreplace unstable suggestions if we meet a new stable one, + // and do nothing if any other situation. For example, if we + // meet `std::ops::Range` after `std::range::legacy::Range`, + // we will remove the latter and then insert the former. + if is_stable + && let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.is_stable) + { + candidates.remove(idx); + } + + if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { + // See if we're recommending TryFrom, TryInto, or FromIterator and add + // a note about editions + let note = if let Some(did) = did { + let requires_note = !did.is_local() + && find_attr!( + this.tcx, + did, + RustcDiagnosticItem( + sym::TryInto | sym::TryFrom | sym::FromIterator + ) + ); + requires_note.then(|| { + format!( + "'{}' is included in the prelude starting in Edition 2021", + path_names_to_string(&path) + ) + }) + } else { + None + }; + + candidates.push(ImportSuggestion { + did, + descr: res.descr(), + path, + accessible: child_accessible, + doc_visible: child_doc_visible, + note, + via_import, + is_stable, + }); + } + } + + // collect submodules to explore + if let Some(def_id) = name_binding.res().module_like_def_id() { + // form the path + let mut path_segments = path_segments.clone(); + path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + + let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind + && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind + && import.parent_scope.expansion == parent_scope.expansion + { + true + } else { + false + }; + + let is_extern_crate_that_also_appears_in_prelude = + name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); + + if !is_extern_crate_that_also_appears_in_prelude || alias_import { + // add the module to the lookup + if seen_modules.insert(def_id) { + if via_import { &mut worklist_via_import } else { &mut worklist }.push( + ( + this.expect_module(def_id), + path_segments, + child_accessible, + child_doc_visible, + is_stable && this.is_stable(def_id, name_binding.span), + ), + ); + } + } + } + }) + } + + candidates + } + + fn is_stable(&self, did: DefId, span: Span) -> bool { + if did.is_local() { + return true; + } + + match self.tcx.lookup_stability(did) { + Some(Stability { + level: StabilityLevel::Unstable { implied_by, .. }, feature, .. + }) => { + if span.allows_unstable(feature) { + true + } else if self.tcx.features().enabled(feature) { + true + } else if let Some(implied_by) = implied_by + && self.tcx.features().enabled(implied_by) + { + true + } else { + false + } + } + Some(_) => true, + None => false, + } + } + + /// When name resolution fails, this method can be used to look up candidate + /// entities with the expected name. It allows filtering them using the + /// supplied predicate (which should be used to only accept the types of + /// definitions expected, e.g., traits). The lookup spans across all crates. + /// + /// N.B., the method does not look into imports, but this is not a problem, + /// since we report the definitions (thus, the de-aliased imports). + pub(crate) fn lookup_import_candidates( + &mut self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + filter_fn: FilterFn, + ) -> Vec + where + FilterFn: Fn(Res) -> bool, + { + let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; + let mut suggestions = self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + self.graph_root.to_module(), + crate_path, + &filter_fn, + ); + + if lookup_ident.span.at_least_rust_2018() { + for (ident, entry) in &self.extern_prelude { + if entry.span().from_expansion() { + // Idents are adjusted to the root context before being + // resolved in the extern prelude, so reporting this to the + // user is no help. This skips the injected + // `extern crate std` in the 2018 edition, which would + // otherwise cause duplicate suggestions. + continue; + } + let Some(crate_id) = + self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) + else { + continue; + }; + + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == *ident + && let Some(decl) = name_resolution.borrow().best_decl() + { + match decl.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); + } + crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); + + suggestions.extend(self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + crate_root, + crate_path, + &filter_fn, + )); + } + } + + suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build()); + suggestions + } + + pub(crate) fn unresolved_macro_suggestions( + &mut self, + err: &mut Diag<'_>, + macro_kind: MacroKind, + parent_scope: &ParentScope<'ra>, + ident: Ident, + krate: &Crate, + sugg_span: Option, + ) { + // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for + // suggestions. + self.register_macros_for_all_crates(); + + let is_expected = + &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); + let suggestion = self.early_lookup_typo_candidate( + ScopeSet::Macro(macro_kind), + parent_scope, + ident, + is_expected, + ); + if !self.add_typo_suggestion(err, suggestion, ident.span) { + self.detect_derive_attribute(err, ident, parent_scope, sugg_span); + } + + let import_suggestions = + self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); + let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() { + DUMMY_NODE_ID => (None, FoundUse::No), + node_id => UsePlacementFinder::check(krate, node_id), + }; + show_candidates( + self.tcx, + err, + span, + &import_suggestions, + Instead::No, + found_use, + DiagMode::Normal, + vec![], + "", + ); + + if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { + let label_span = ident.span.shrink_to_hi(); + let mut spans = MultiSpan::from_span(label_span); + spans.push_span_label(label_span, "put a macro name here"); + err.subdiagnostic(MaybeMissingMacroRulesName { spans }); + return; + } + + if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { + err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); + return; + } + + let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { + if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } + }); + + if let Some((def_id, unused_ident)) = unused_macro { + let scope = self.local_macro_def_scopes[&def_id]; + let parent_nearest = parent_scope.module.nearest_parent_mod(); + let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds(); + if !unused_macro_kinds.contains(macro_kind.into()) { + match macro_kind { + MacroKind::Bang => { + err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident }); + } + MacroKind::Attr => { + err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident }); + } + MacroKind::Derive => { + err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident }); + } + } + return; + } + if Some(parent_nearest) == scope.opt_def_id() { + err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); + err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); + return; + } + } + + if ident.name == kw::Default + && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind + { + let span = self.def_span(def_id); + let source_map = self.tcx.sess.source_map(); + let head_span = source_map.guess_head_span(span); + err.subdiagnostic(ConsiderAddingADerive { + span: head_span.shrink_to_lo(), + suggestion: "#[derive(Default)]\n".to_string(), + }); + } + for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { + let Ok(binding) = self.cm().resolve_ident_in_scope_set( + ident, + ScopeSet::All(ns), + parent_scope, + None, + None, + None, + ) else { + continue; + }; + + let desc = match binding.res() { + Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { + "a function-like macro".to_string() + } + Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { + format!("an attribute: `#[{ident}]`") + } + Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { + format!("a derive macro: `#[derive({ident})]`") + } + Res::Def(DefKind::Macro(kinds), _) => { + format!("{} {}", kinds.article(), kinds.descr()) + } + Res::ToolMod | Res::OpenMod(..) => { + // Don't confuse the user with tool modules or open modules. + continue; + } + Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { + "only a trait, without a derive macro".to_string() + } + res => format!( + "{} {}, not {} {}", + res.article(), + res.descr(), + macro_kind.article(), + macro_kind.descr_expected(), + ), + }; + if let crate::DeclKind::Import { import, .. } = binding.kind + && !import.span.is_dummy() + { + let note = diagnostics::IdentImporterHereButItIsDesc { + span: import.span, + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + // Silence the 'unused import' warning we might get, + // since this diagnostic already covers that import. + self.record_use(ident, binding, Used::Other); + return; + } + let note = diagnostics::IdentInScopeButItIsDesc { + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + return; + } + + if self.macro_names.contains(&IdentKey::new(ident)) { + err.subdiagnostic(AddedMacroUse); + return; + } + } + + /// Given an attribute macro that failed to be resolved, look for `derive` macros that could + /// provide it, either as-is or with small typos. + fn detect_derive_attribute( + &self, + err: &mut Diag<'_>, + ident: Ident, + parent_scope: &ParentScope<'ra>, + sugg_span: Option, + ) { + // Find all of the `derive`s in scope and collect their corresponding declared + // attributes. + // FIXME: this only works if the crate that owns the macro that has the helper_attr + // has already been imported. + let mut derives = vec![]; + let mut all_attrs: UnordMap> = UnordMap::default(); + // We're collecting these in a hashmap, and handle ordering the output further down. + #[allow(rustc::potential_query_instability)] + for (def_id, ext) in self + .local_macro_map + .iter() + .map(|(local_id, ext)| (local_id.to_def_id(), ext)) + .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) + { + for helper_attr in &ext.helper_attrs { + let item_name = self.tcx.item_name(def_id); + all_attrs.entry(*helper_attr).or_default().push(item_name); + if helper_attr == &ident.name { + derives.push(item_name); + } + } + } + let kind = MacroKind::Derive.descr(); + if !derives.is_empty() { + // We found an exact match for the missing attribute in a `derive` macro. Suggest it. + let mut derives: Vec = derives.into_iter().map(|d| d.to_string()).collect(); + derives.sort(); + derives.dedup(); + let msg = match &derives[..] { + [derive] => format!(" `{derive}`"), + [start @ .., last] => format!( + "s {} and `{last}`", + start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") + ), + [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), + }; + let msg = format!( + "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ + missing a `derive` attribute", + ident.name, + ); + let sugg_span = + if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind { + let span = self.def_span(id); + if span.from_expansion() { + None + } else { + // For enum variants sugg_span is empty but we can get the enum's Span. + Some(span.shrink_to_lo()) + } + } else { + // For items this `Span` will be populated, everything else it'll be None. + sugg_span + }; + match sugg_span { + Some(span) => { + err.span_suggestion_verbose( + span, + msg, + format!("#[derive({})]\n", derives.join(", ")), + Applicability::MaybeIncorrect, + ); + } + None => { + err.note(msg); + } + } + } else { + // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. + let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); + if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) + && let Some(macros) = all_attrs.get(&best_match) + { + let mut macros: Vec = macros.into_iter().map(|d| d.to_string()).collect(); + macros.sort(); + macros.dedup(); + let msg = match ¯os[..] { + [] => return, + [name] => format!(" `{name}` accepts"), + [start @ .., end] => format!( + "s {} and `{end}` accept", + start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), + ), + }; + let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); + err.span_suggestion_verbose( + ident.span, + msg, + best_match, + Applicability::MaybeIncorrect, + ); + } + } + } + + pub(crate) fn add_typo_suggestion( + &self, + err: &mut Diag<'_>, + suggestion: Option, + span: Span, + ) -> bool { + let suggestion = match suggestion { + None => return false, + // We shouldn't suggest underscore. + Some(suggestion) if suggestion.candidate == kw::Underscore => return false, + Some(suggestion) => suggestion, + }; + + let mut did_label_def_span = false; + + if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { + if span.overlaps(def_span) { + // Don't suggest typo suggestion for itself like in the following: + // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` + // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:14 + // | + // LL | struct X {} + // | ----------- `X` defined here + // LL | + // LL | const Y: X = X("ö"); + // | -------------^^^^^^- similarly named constant `Y` defined here + // | + // help: use struct literal syntax instead + // | + // LL | const Y: X = X {}; + // | ^^^^ + // help: a constant with a similar name exists + // | + // LL | const Y: X = Y("ö"); + // | ^ + return false; + } + let span = self.tcx.sess.source_map().guess_head_span(def_span); + let candidate_descr = suggestion.res.descr(); + let candidate = suggestion.candidate; + let label = match suggestion.target { + SuggestionTarget::SimilarlyNamed => { + diagnostics::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } + } + SuggestionTarget::SingleItem => { + diagnostics::DefinedHere::SingleItem { span, candidate_descr, candidate } + } + }; + did_label_def_span = true; + err.subdiagnostic(label); + } + + let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Some(span) = suggestion.span + && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') + && snippet == candidate + { + let candidate = suggestion.candidate; + // When the suggested binding change would be from `x` to `_x`, suggest changing the + // original binding definition instead. (#60164) + let msg = format!( + "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" + ); + if !did_label_def_span { + err.span_label(span, format!("`{candidate}` defined here")); + } + (span, msg, snippet) + } else { + let msg = match suggestion.target { + SuggestionTarget::SimilarlyNamed => format!( + "{} {} with a similar name exists", + suggestion.res.article(), + suggestion.res.descr() + ), + SuggestionTarget::SingleItem => { + format!("maybe you meant this {}", suggestion.res.descr()) + } + }; + (span, msg, suggestion.candidate.to_ident_string()) + }; + err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); + true + } + + fn decl_description(&self, b: Decl<'_>, ident: Ident, scope: Scope<'_>) -> String { + let res = b.res(); + if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) { + let (built_in, from) = match scope { + Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"), + Scope::ExternPreludeFlags + if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() + || matches!(res, Res::OpenMod(..)) => + { + ("", " passed with `--extern`") + } + _ => { + if matches!(res, Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod) { + // These already contain the "built-in" prefix or look bad with it. + ("", "") + } else { + (" built-in", "") + } + } + }; + + let a = if built_in.is_empty() { res.article() } else { "a" }; + format!("{a}{built_in} {thing}{from}", thing = res.descr()) + } else { + let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; + format!("the {thing} {introduced} here", thing = res.descr()) + } + } + + fn ambiguity_diagnostic( + &self, + ambiguity_error: &AmbiguityError<'ra>, + ) -> diagnostics::Ambiguity { + let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = + *ambiguity_error; + let extern_prelude_ambiguity = || { + // Note: b1 may come from a module scope, as an extern crate item in module. + matches!(scope2, Scope::ExternPreludeFlags) + && self + .extern_prelude + .get(&IdentKey::new(ident)) + .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) + }; + let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { + // We have to print the span-less alternative first, otherwise formatting looks bad. + (b2, b1, scope2, scope1, true) + } else { + (b1, b2, scope1, scope2, false) + }; + + let could_refer_to = |b: Decl<'_>, scope: Scope<'ra>, also: &str| { + let what = self.decl_description(b, ident, scope); + let note_msg = format!("`{ident}` could{also} refer to {what}"); + + let thing = b.res().descr(); + let mut help_msgs = Vec::new(); + if b.is_glob_import() + && (kind == AmbiguityKind::GlobVsGlob + || kind == AmbiguityKind::GlobVsExpanded + || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) + { + help_msgs.push(format!( + "consider adding an explicit import of `{ident}` to disambiguate" + )) + } + if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity() + { + help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) + } + + if kind != AmbiguityKind::GlobVsGlob { + if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { + if module == self.graph_root.to_module() { + help_msgs.push(format!( + "use `crate::{ident}` to refer to this {thing} unambiguously" + )); + } else if module.is_normal() { + help_msgs.push(format!( + "use `self::{ident}` to refer to this {thing} unambiguously" + )); + } + } + } + + ( + Spanned { node: note_msg, span: b.span }, + help_msgs + .iter() + .enumerate() + .map(|(i, help_msg)| { + let or = if i == 0 { "" } else { "or " }; + format!("{or}{help_msg}") + }) + .collect::>(), + ) + }; + let (b1_note, b1_help_msgs) = could_refer_to(b1, scope1, ""); + let (b2_note, b2_help_msgs) = could_refer_to(b2, scope2, " also"); + let help = if kind == AmbiguityKind::GlobVsGlob + && b1 + .parent_module + .and_then(|m| m.opt_def_id()) + .map(|d| !d.is_local()) + .unwrap_or_default() + { + Some(&[ + "consider updating this dependency to resolve this error", + "if updating the dependency does not resolve the problem report the problem to the author of the relevant crate", + ] as &[_]) + } else { + None + }; + + let ambig_vis = ambig_vis.map(|(vis1, vis2)| { + format!( + "{} or {}", + vis1.to_string(CRATE_DEF_ID, self.tcx), + vis2.to_string(CRATE_DEF_ID, self.tcx) + ) + }); + + diagnostics::Ambiguity { + ident, + help, + ambig_vis, + kind: kind.descr(), + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error: false, + } + } + + /// If the binding refers to a tuple struct constructor with fields, + /// returns the span of its fields. + fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { + let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = + decl.kind + else { + return None; + }; + + let def_id = self.tcx.parent(ctor_def_id); + self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` + } + + /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. + /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. + /// Returns `None` for block modules that don't have a `DefId`. + fn module_path_names(&self, module: Module<'ra>) -> Option> { + let mut path = Vec::new(); + let mut def_id = module.opt_def_id()?; + while let Some(parent) = self.tcx.opt_parent(def_id) { + if let Some(name) = self.tcx.opt_item_name(def_id) { + path.push(name); + } + if parent.is_top_level_module() { + break; + } + def_id = parent; + } + path.reverse(); + path.insert(0, kw::Crate); + Some(path) + } + + /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) + /// relative to the current scope, if possible. Only applies to crate-local items and + /// only when the resulting path is actually shorter than the original. + fn shorten_candidate_path( + &self, + suggestion: &mut ImportSuggestion, + current_module: Module<'ra>, + ) { + const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; + + // Only shorten local items. + if suggestion.did.is_none_or(|did| !did.is_local()) { + return; + } + + // Build current module path: [Crate, foo, bar, ...]. + let Some(current_mod_path) = self.module_path_names(current_module) else { + return; + }; + + // Normalise candidate path: filter out `PathRoot` (`::`), and if the path + // doesn't start with `Crate`, prepend it (edition 2015 paths are relative + // to the crate root without an explicit `crate::` prefix). + let candidate_names = { + let filtered_segments: Vec<_> = suggestion + .path + .segments + .iter() + .filter(|segment| segment.ident.name != kw::PathRoot) + .collect(); + + let mut candidate_names: Vec = + filtered_segments.iter().map(|segment| segment.ident.name).collect(); + if candidate_names.first() != Some(&kw::Crate) { + candidate_names.insert(0, kw::Crate); + } + if candidate_names.len() < 2 { + return; + } + candidate_names + }; + + // The candidate's module path is everything except the last segment (the item name). + let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; + + // Find the longest common prefix between the current module and candidate module paths. + let common_prefix_length = current_mod_path + .iter() + .zip(candidate_mod_names.iter()) + .take_while(|(current, candidate)| current == candidate) + .count(); + + // Non-crate-local item; keep the full absolute path. + if common_prefix_length == 0 { + return; + } + + let super_count = current_mod_path.len() - common_prefix_length; + + // At the crate root, `use` paths resolve from the crate root anyway, so we can + // drop the `crate::` prefix entirely instead of replacing it with `self::`. + let at_crate_root = current_mod_path.len() == 1; + + let mut new_segments = if super_count == 0 && at_crate_root { + ThinVec::new() + } else { + let prefix_keyword = match super_count { + 0 => kw::SelfLower, + 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, + _ => return, // Too many `super` levels; keep the full absolute path. + }; + thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] + }; + for &name in &candidate_names[common_prefix_length..] { + new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); + } + + // Only apply if the result is strictly shorter than the original path. + if new_segments.len() >= suggestion.path.segments.len() { + return; + } + + suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; + } + + fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { + let PrivacyError { + ident, + decl, + outermost_res, + parent_scope, + single_nested, + dedup_span, + ref source, + } = *privacy_error; + + let res = decl.res(); + let ctor_fields_span = self.ctor_fields_span(decl); + let plain_descr = res.descr().to_string(); + let nonimport_descr = + if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; + let import_descr = nonimport_descr.clone() + " import"; + let get_descr = |b: Decl<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; + + // Print the primary message. + let ident_descr = get_descr(decl); + let mut err = + self.dcx().create_err(diagnostics::IsPrivate { span: ident.span, ident_descr, ident }); + + self.mention_default_field_values(source, ident, &mut err); + + let shown_candidates = if let Some((this_res, outer_ident)) = outermost_res { + let mut import_suggestions = self.lookup_import_candidates( + outer_ident, + this_res.ns().unwrap_or(Namespace::TypeNS), + &parent_scope, + &|res: Res| res == this_res, + ); + // Shorten candidate paths using `super::` or `self::` when possible. + for suggestion in &mut import_suggestions { + self.shorten_candidate_path(suggestion, parent_scope.module); + } + let point_to_def = !show_candidates( + self.tcx, + &mut err, + Some(dedup_span.until(outer_ident.span.shrink_to_hi())), + &import_suggestions, + Instead::Yes, + FoundUse::Yes, + DiagMode::Import { append: single_nested, unresolved_import: false }, + vec![], + "", + ); + // If we suggest importing a public re-export, don't point at the definition. + if point_to_def && ident.span != outer_ident.span { + let label = diagnostics::OuterIdentIsNotPubliclyReexported { + span: outer_ident.span, + outer_ident_descr: this_res.descr(), + outer_ident, + }; + err.subdiagnostic(label); + } + !point_to_def + } else { + false + }; + + let mut non_exhaustive = None; + // If an ADT is foreign and marked as `non_exhaustive`, then that's + // probably why we have the privacy error. + // Otherwise, point out if the struct has any private fields. + if let Some(def_id) = res.opt_def_id() + && !def_id.is_local() + && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) + { + non_exhaustive = Some(attr_span); + } else if let Some(span) = ctor_fields_span { + let label = diagnostics::ConstructorPrivateIfAnyFieldPrivate { span }; + err.subdiagnostic(label); + if let Res::Def(_, d) = res + && let Some(fields) = self.field_visibility_spans.get(&d) + { + let spans = fields.iter().map(|span| *span).collect(); + let sugg = diagnostics::ConsiderMakingTheFieldPublic { + spans, + number_of_fields: fields.len(), + }; + err.subdiagnostic(sugg); + } + } + + let mut sugg_paths: Vec<(Vec, bool)> = vec![]; + if let Some(mut def_id) = res.opt_def_id() { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + if !def_id.is_top_level_module() { + path.push(def_id); + } else { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + let path_names: Option> = path + .iter() + .rev() + .map(|def_id| { + self.tcx.opt_item_name(*def_id).map(|name| { + Ident::with_dummy_span(if def_id.is_top_level_module() { + kw::Crate + } else { + name + }) + }) + }) + .collect(); + if let Some(&def_id) = path.get(0) + && let Some(path) = path_names + { + if let Some(def_id) = def_id.as_local() { + if self.effective_visibilities.is_directly_public(def_id) { + sugg_paths.push((path, false)); + } + } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) + { + sugg_paths.push((path, false)); + } + } + } + + // Print the whole import chain to make it easier to see what happens. + let first_binding = decl; + let mut next_binding = Some(decl); + let mut next_ident = ident; + while let Some(binding) = next_binding { + let name = next_ident; + next_binding = match binding.kind { + _ if res == Res::Err => None, + DeclKind::Import { source_decl, import, .. } => match import.kind { + _ if source_decl.span.is_dummy() => None, + ImportKind::Single { source, .. } => { + next_ident = source; + Some(source_decl) + } + ImportKind::Glob { .. } + | ImportKind::MacroUse { .. } + | ImportKind::MacroExport => Some(source_decl), + ImportKind::ExternCrate { .. } => None, + }, + _ => None, + }; + + match binding.kind { + DeclKind::Import { source_decl, import, .. } => { + // Don't include `{{root}}` in suggestions - it's an internal symbol + // that should never be shown to users. + let path = import + .module_path + .iter() + .filter(|seg| seg.ident.name != kw::PathRoot) + .map(|seg| seg.ident.clone()) + .chain(std::iter::once(ident)) + .collect::>(); + let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); + sugg_paths.push((path, through_reexport)); + } + DeclKind::Def(_) => {} + } + let first = binding == first_binding; + let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); + let mut note_span = MultiSpan::from_span(def_span); + if !first && binding.vis().is_public() { + let desc = match binding.kind { + DeclKind::Import { .. } => "re-export", + _ => "directly", + }; + note_span.push_span_label(def_span, format!("you could import this {desc}")); + } + // Final step in the import chain, point out if the ADT is `non_exhaustive` + // which is probably why this privacy violation occurred. + if next_binding.is_none() + && let Some(span) = non_exhaustive + { + note_span.push_span_label( + span, + "cannot be constructed because it is `#[non_exhaustive]`", + ); + } + let note = diagnostics::NoteAndRefersToTheItemDefinedHere { + span: note_span, + binding_descr: get_descr(binding), + binding_name: name, + first, + dots: next_binding.is_some(), + }; + err.subdiagnostic(note); + } + // The suggestion replaces `dedup_span` with a path reaching the failing ident. + // That's valid only when + // 1) the failing ident is the imported leaf, otherwise `as` renames and trailing segments + // get dropped, and + // 2) the use isn't nested, otherwise `dedup_span` is one ident in `{...}`. + // + // See issue #156060. + let can_replace_use = !shown_candidates + && !single_nested + && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span); + if can_replace_use { + // We prioritize shorter paths, non-core imports and direct imports over the + // alternatives. + sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); + for (sugg, reexport) in sugg_paths { + if sugg.len() <= 1 { + // A single path segment suggestion is wrong. This happens on circular + // imports. `tests/ui/imports/issue-55884-2.rs` + continue; + } + let path = join_path_idents(sugg); + let sugg = if reexport { + diagnostics::ImportIdent::ThroughReExport { span: dedup_span, ident, path } + } else { + diagnostics::ImportIdent::Directly { span: dedup_span, ident, path } + }; + err.subdiagnostic(sugg); + break; + } + } + + err.emit(); + } + + /// When a private field is being set that has a default field value, we suggest using `..` and + /// setting the value of that field implicitly with its default. + /// + /// If we encounter code like + /// ```text + /// struct Priv; + /// pub struct S { + /// pub field: Priv = Priv, + /// } + /// ``` + /// which is used from a place where `Priv` isn't accessible + /// ```text + /// let _ = S { field: m::Priv1 {} }; + /// // ^^^^^ private struct + /// ``` + /// we will suggest instead using the `default_field_values` syntax instead: + /// ```text + /// let _ = S { .. }; + /// ``` + fn mention_default_field_values( + &self, + source: &Option, + ident: Ident, + err: &mut Diag<'_>, + ) { + let Some(expr) = source else { return }; + let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; + // We don't have to handle type-relative paths because they're forbidden in ADT + // expressions, but that would change with `#[feature(more_qualified_paths)]`. + let Some(segment) = struct_expr.path.segments.last() else { return }; + let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; + let Some(Res::Def(_, def_id)) = partial_res.full_res() else { + return; + }; + let Some(default_fields) = self.field_defaults(def_id) else { return }; + if struct_expr.fields.is_empty() { + return; + } + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion( + format!( + "the type `{ident}` of field `{}` is private, but you can construct \ + the default value defined for it in `{}` using `..` in the struct \ + initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } + + pub(crate) fn find_similarly_named_module_or_crate( + &self, + ident: Symbol, + current_module: Module<'ra>, + ) -> Option { + let mut candidates = self + .extern_prelude + .keys() + .map(|ident| ident.name) + .chain( + self.local_module_map + .iter() + .filter(|(_, module)| { + let module = module.to_module(); + current_module.is_ancestor_of(module) && current_module != module + }) + .flat_map(|(_, module)| module.name()), + ) + .chain( + self.extern_module_map + .borrow() + .iter() + .filter(|(_, module)| { + let module = module.to_module(); + current_module.is_ancestor_of(module) && current_module != module + }) + .flat_map(|(_, module)| module.name()), + ) + .filter(|c| !c.to_string().is_empty()) + .collect::>(); + candidates.sort(); + candidates.dedup(); + find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) + } + + pub(crate) fn report_path_resolution_error( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'ra>, + ribs: Option<&PerNS>>>, + ignore_decl: Option>, + ignore_import: Option>, + module: Option>, + failed_segment_idx: usize, + ident: Ident, + diag_metadata: Option<&DiagMetadata<'_>>, + ) -> (String, String, Option) { + let is_last = failed_segment_idx == path.len() - 1; + let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; + let module_def_id = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), + _ => None, + }; + let scope = match &path[..failed_segment_idx] { + [.., prev] => { + if prev.ident.name == kw::PathRoot { + format!("the crate root") + } else { + format!("`{}`", prev.ident) + } + } + _ => format!("this scope"), + }; + let message = format!("cannot find `{ident}` in {scope}"); + + if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); + let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); + candidates + .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); + if let Some(candidate) = candidates.get(0) { + let path = { + // remove the possible common prefix of the path + let len = candidate.path.segments.len(); + let start_index = (0..=failed_segment_idx.min(len - 1)) + .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) + .unwrap_or_default(); + let segments = + (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); + Path { segments, span: Span::default(), tokens: None } + }; + ( + message, + String::from("unresolved import"), + Some(( + vec![(ident.span, pprust::path_to_string(&path))], + String::from("a similar path exists"), + Applicability::MaybeIncorrect, + )), + ) + } else if ident.name == sym::core { + ( + message, + format!("you might be missing crate `{ident}`"), + Some(( + vec![(ident.span, "std".to_string())], + "try using `std` instead of `core`".to_string(), + Applicability::MaybeIncorrect, + )), + ) + } else if ident.name == kw::Underscore { + ( + "invalid crate or module name `_`".to_string(), + "`_` is not a valid crate or module name".to_string(), + None, + ) + } else if self.tcx.sess.is_rust_2015() { + ( + format!("cannot find module or crate `{ident}` in {scope}"), + format!("use of unresolved module or unlinked crate `{ident}`"), + Some(( + vec![( + self.current_crate_outer_attr_insert_span, + format!("extern crate {ident};\n"), + )], + if was_invoked_from_cargo() { + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add \ + {ident}` to add it to your `Cargo.toml` and import it in your \ + code", + ) + } else { + format!( + "you might be missing a crate named `{ident}`, add it to your \ + project and import it in your code", + ) + }, + Applicability::MaybeIncorrect, + )), + ) + } else { + (message, format!("could not find `{ident}` in the crate root"), None) + } + } else if failed_segment_idx > 0 { + let parent = path[failed_segment_idx - 1].ident.name; + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => format!("`{parent}`"), + }; + + let mut msg = format!("could not find `{ident}` in {parent}"); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + let binding = if let Some(module) = module { + self.cm() + .resolve_ident_in_module( + module, + ident, + ns_to_try, + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .ok() + } else if let Some(ribs) = ribs + && let Some(TypeNS | ValueNS) = opt_ns + { + assert!(ignore_import.is_none()); + match self.resolve_ident_in_lexical_scope( + ident, + ns_to_try, + parent_scope, + None, + &ribs[ns_to_try], + ignore_decl, + diag_metadata, + ) { + // we found a locally-imported or available item/module + Some(LateDecl::Decl(binding)) => Some(binding), + _ => None, + } + } else { + self.cm() + .resolve_ident_in_scope_set( + ident, + ScopeSet::All(ns_to_try), + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .ok() + }; + if let Some(binding) = binding { + msg = format!( + "expected {}, found {} `{ident}` in {parent}", + ns.descr(), + binding.res().descr(), + ); + }; + } + (message, msg, None) + } else if ident.name == kw::SelfUpper { + // As mentioned above, `opt_ns` being `None` indicates a module path in import. + // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an + // impl + if opt_ns.is_none() { + (message, "`Self` cannot be used in imports".to_string(), None) + } else { + ( + message, + "`Self` is only available in impls, traits, and type definitions".to_string(), + None, + ) + } + } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { + // Check whether the name refers to an item in the value namespace. + let binding = if let Some(ribs) = ribs { + assert!(ignore_import.is_none()); + self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + None, + &ribs[ValueNS], + ignore_decl, + diag_metadata, + ) + } else { + None + }; + let match_span = match binding { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LateDecl::RibDef(Res::Local(id))) => { + Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) + } + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub const Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LateDecl::Decl(name_binding)) => Some(( + name_binding.span, + name_binding.res().article(), + name_binding.res().descr(), + )), + _ => None, + }; + + let message = format!("cannot find type `{ident}` in {scope}"); + let label = if let Some((span, article, descr)) = match_span { + format!( + "`{ident}` is declared as {article} {descr} at `{}`, not a type", + self.tcx + .sess + .source_map() + .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) + ) + } else { + format!("use of undeclared type `{ident}`") + }; + (message, label, None) + } else { + let mut suggestion = None; + if ident.name == sym::alloc { + suggestion = Some(( + vec![], + String::from("add `extern crate alloc` to use the `alloc` crate"), + Applicability::MaybeIncorrect, + )) + } + + suggestion = suggestion.or_else(|| { + self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map( + |sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from("there is a crate or module with a similar name"), + Applicability::MaybeIncorrect, + ) + }, + ) + }); + if let Ok(binding) = self.cm().resolve_ident_in_scope_set( + ident, + ScopeSet::All(ValueNS), + parent_scope, + None, + ignore_decl, + ignore_import, + ) { + let descr = binding.res().descr(); + let message = format!("cannot find module or crate `{ident}` in {scope}"); + (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) + } else { + let suggestion = if suggestion.is_some() { + suggestion + } else if let Some(m) = self.undeclared_module_exists(ident) { + self.undeclared_module_suggest_declare(ident, m) + } else if was_invoked_from_cargo() { + Some(( + vec![], + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml`", + ), + Applicability::MaybeIncorrect, + )) + } else { + Some(( + vec![], + format!("you might be missing a crate named `{ident}`",), + Applicability::MaybeIncorrect, + )) + }; + let message = format!("cannot find module or crate `{ident}` in {scope}"); + ( + message, + format!("use of unresolved module or unlinked crate `{ident}`"), + suggestion, + ) + } + } + } + + fn undeclared_module_suggest_declare( + &self, + ident: Ident, + path: std::path::PathBuf, + ) -> Option<(Vec<(Span, String)>, String, Applicability)> { + Some(( + vec![(self.current_crate_outer_attr_insert_span, format!("mod {ident};\n"))], + format!( + "to make use of source file {}, use `mod {ident}` \ + in this file to declare the module", + path.display() + ), + Applicability::MaybeIncorrect, + )) + } + + fn undeclared_module_exists(&self, ident: Ident) -> Option { + let map = self.tcx.sess.source_map(); + + let src = map.span_to_filename(ident.span).into_local_path()?; + let i = ident.as_str(); + // FIXME: add case where non parent using undeclared module (hard?) + let dir = src.parent()?; + let src = src.file_stem()?.to_str()?; + for file in [ + // …/x.rs + dir.join(i).with_extension("rs"), + // …/x/mod.rs + dir.join(i).join("mod.rs"), + ] { + if file.exists() { + return Some(file); + } + } + if !matches!(src, "main" | "lib" | "mod") { + for file in [ + // …/x/y.rs + dir.join(src).join(i).with_extension("rs"), + // …/x/y/mod.rs + dir.join(src).join(i).join("mod.rs"), + ] { + if file.exists() { + return Some(file); + } + } + } + None + } + + /// Adds suggestions for a path that cannot be resolved. + #[instrument(level = "debug", skip(self, parent_scope))] + pub(crate) fn make_path_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + match path[..] { + // `{{root}}::ident::...` on both editions. + // On 2015 `{{root}}` is usually added implicitly. + [first, second, ..] + if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} + // `ident::...` on 2018. + [first, ..] + if first.ident.span.at_least_rust_2018() + && !first.ident.is_path_segment_keyword() => + { + // Insert a placeholder that's later replaced by `self`/`super`/etc. + path.insert(0, Segment::from_ident(Ident::dummy())); + } + _ => return None, + } + + self.make_missing_self_suggestion(path.clone(), parent_scope) + .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) + } + + /// Suggest a missing `self::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `self::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_self_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `self` and check if that is valid. + path[0].ident.name = kw::SelfLower; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { Some((path, None)) } else { None } + } + + /// Suggests a missing `crate::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `crate::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_crate_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `crate` and check if that is valid. + path[0].ident.name = kw::Crate; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { + Some(( + path, + Some( + "`use` statements changed in Rust 2018; read more at \ + " + .to_string(), + ), + )) + } else { + None + } + } + + /// Suggests a missing `super::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `super::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_super_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `crate` and check if that is valid. + path[0].ident.name = kw::Super; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { Some((path, None)) } else { None } + } + + /// Suggests a missing external crate name if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foobar::Baz; + /// | ^^^^^^ did you mean `baz::foobar`? + /// ``` + /// + /// Used when importing a submodule of an external crate but missing that crate's + /// name as the first part of path. + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_external_crate_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + if path[1].ident.span.is_rust_2015() { + return None; + } + + // Sort extern crate names in *reverse* order to get + // 1) some consistent ordering for emitted diagnostics, and + // 2) `std` suggestions before `core` suggestions. + let mut extern_crate_names = + self.extern_prelude.keys().map(|ident| ident.name).collect::>(); + extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); + + for name in extern_crate_names.into_iter() { + // Replace first ident with a crate name and check if that is valid. + path[0].ident.name = name; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?name, ?result); + if let PathResult::Module(..) = result { + return Some((path, None)); + } + } + + None + } + + /// Suggests importing a macro from the root of the crate rather than a module within + /// the crate. + /// + /// ```text + /// help: a macro with this name exists at the root of the crate + /// | + /// LL | use issue_59764::makro; + /// | ^^^^^^^^^^^^^^^^^^ + /// | + /// = note: this could be because a macro annotated with `#[macro_export]` will be exported + /// at the root of the crate instead of the module where it is defined + /// ``` + pub(crate) fn check_for_module_export_macro( + &mut self, + import: Import<'ra>, + module: ModuleOrUniformRoot<'ra>, + ident: Ident, + ) -> Option<(Option, Option)> { + let ModuleOrUniformRoot::Module(mut crate_module) = module else { + return None; + }; + + while let Some(parent) = crate_module.parent { + crate_module = parent; + } + + if module == ModuleOrUniformRoot::Module(crate_module) { + // Don't make a suggestion if the import was already from the root of the crate. + return None; + } + + let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); + let binding = self.resolution(crate_module, binding_key)?.best_decl()?; + let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { + return None; + }; + if !kinds.contains(MacroKinds::BANG) { + return None; + } + let module_name = crate_module.name().unwrap_or(kw::Crate); + let import_snippet = match import.kind { + ImportKind::Single { source, target, .. } if source != target => { + format!("{source} as {target}") + } + _ => format!("{ident}"), + }; + + let mut corrections: Vec<(Span, String)> = Vec::new(); + if !import.is_nested() { + // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove + // intermediate segments. + corrections.push((import.span, format!("{module_name}::{import_snippet}"))); + } else { + // Find the binding span (and any trailing commas and spaces). + // i.e. `use a::b::{c, d, e};` + // ^^^ + let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( + self.tcx.sess, + import.span, + import.use_span, + ); + debug!(found_closing_brace, ?binding_span); + + let mut removal_span = binding_span; + + // If the binding span ended with a closing brace, as in the below example: + // i.e. `use a::b::{c, d};` + // ^ + // Then expand the span of characters to remove to include the previous + // binding's trailing comma. + // i.e. `use a::b::{c, d};` + // ^^^ + if found_closing_brace + && let Some(previous_span) = + extend_span_to_previous_binding(self.tcx.sess, binding_span) + { + debug!(?previous_span); + removal_span = removal_span.with_lo(previous_span.lo()); + } + debug!(?removal_span); + + // Remove the `removal_span`. + corrections.push((removal_span, "".to_string())); + + // Find the span after the crate name and if it has nested imports immediately + // after the crate name already. + // i.e. `use a::b::{c, d};` + // ^^^^^^^^^ + // or `use a::{b, c, d}};` + // ^^^^^^^^^^^ + let (has_nested, after_crate_name) = + find_span_immediately_after_crate_name(self.tcx.sess, import.use_span); + debug!(has_nested, ?after_crate_name); + + let source_map = self.tcx.sess.source_map(); + + // Make sure this is actually crate-relative. + let is_definitely_crate = import + .module_path + .first() + .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); + + // Add the import to the start, with a `{` if required. + let start_point = source_map.start_point(after_crate_name); + if is_definitely_crate + && let Ok(start_snippet) = source_map.span_to_snippet(start_point) + { + corrections.push(( + start_point, + if has_nested { + // In this case, `start_snippet` must equal '{'. + format!("{start_snippet}{import_snippet}, ") + } else { + // In this case, add a `{`, then the moved import, then whatever + // was there before. + format!("{{{import_snippet}, {start_snippet}") + }, + )); + + // Add a `};` to the end if nested, matching the `{` added at the start. + if !has_nested { + corrections.push((source_map.end_point(after_crate_name), "};".to_string())); + } + } else { + // If the root import is module-relative, add the import separately + corrections.push(( + import.use_span.shrink_to_lo(), + format!("use {module_name}::{import_snippet};\n"), + )); + } + } + + let suggestion = Some(( + corrections, + String::from("a macro with this name exists at the root of the crate"), + Applicability::MaybeIncorrect, + )); + Some(( + suggestion, + Some( + "this could be because a macro annotated with `#[macro_export]` will be exported \ + at the root of the crate instead of the module where it is defined" + .to_string(), + ), + )) + } + + /// Finds a cfg-ed out item inside `module` with the matching name. + pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { + let local_items; + let symbols = if module.is_local() { + local_items = self + .stripped_cfg_items + .iter() + .filter_map(|item| { + let parent_scope = self.local_modules.iter().find_map(|m| match m.kind { + ModuleKind::Def(_, def_id, node_id, _) if node_id == item.parent_scope => { + Some(def_id) + } + _ => None, + })?; + Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg.clone() }) + }) + .collect::>(); + local_items.as_slice() + } else { + self.tcx.stripped_cfg_items(module.krate) + }; + + for &StrippedCfgItem { parent_scope, ident, ref cfg } in symbols { + if ident.name != *segment { + continue; + } + + let parent_module = self.get_nearest_non_block_module(parent_scope).def_id(); + + fn comes_from_same_module_for_glob( + r: &Resolver<'_, '_>, + parent_module: DefId, + module: DefId, + visited: &mut FxHashMap, + ) -> bool { + if let Some(&cached) = visited.get(&parent_module) { + // this branch is prevent from being called recursively infinity, + // because there has some cycles in globs imports, + // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` + return cached; + } + visited.insert(parent_module, false); + let mut res = false; + let m = r.expect_module(parent_module); + if m.is_local() { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() + { + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + res = true; + break; + } + } + } + } + visited.insert(parent_module, res); + res + } + + let comes_from_same_module = parent_module == module + || comes_from_same_module_for_glob( + self, + parent_module, + module, + &mut Default::default(), + ); + if !comes_from_same_module { + continue; + } + + let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { + diagnostics::ItemWas::BehindFeature { feature, span: cfg.1 } + } else { + diagnostics::ItemWas::CfgOut { span: cfg.1 } + }; + let note = diagnostics::FoundItemConfigureOut { span: ident.span, item_was }; + err.subdiagnostic(note); + } + } + + pub(crate) fn struct_ctor(&self, def_id: DefId) -> Option { + match def_id.as_local() { + Some(def_id) => self.struct_ctors.get(&def_id).cloned(), + None => { + self.cstore().ctor_untracked(self.tcx, def_id).map(|(ctor_kind, ctor_def_id)| { + let res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); + let vis = self.tcx.visibility(ctor_def_id); + let field_visibilities = self + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|&field_id| self.tcx.visibility(field_id)) + .collect(); + StructCtor { res, vis, field_visibilities } + }) + } + } + } +} + +/// Given a `binding_span` of a binding within a use statement: +/// +/// ```ignore (illustrative) +/// use foo::{a, b, c}; +/// // ^ +/// ``` +/// +/// then return the span until the next binding or the end of the statement: +/// +/// ```ignore (illustrative) +/// use foo::{a, b, c}; +/// // ^^^ +/// ``` +fn find_span_of_binding_until_next_binding( + sess: &Session, + binding_span: Span, + use_span: Span, +) -> (bool, Span) { + let source_map = sess.source_map(); + + // Find the span of everything after the binding. + // i.e. `a, e};` or `a};` + let binding_until_end = binding_span.with_hi(use_span.hi()); + + // Find everything after the binding but not including the binding. + // i.e. `, e};` or `};` + let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); + + // Keep characters in the span until we encounter something that isn't a comma or + // whitespace. + // i.e. `, ` or ``. + // + // Also note whether a closing brace character was encountered. If there + // was, then later go backwards to remove any trailing commas that are left. + let mut found_closing_brace = false; + let after_binding_until_next_binding = + source_map.span_take_while(after_binding_until_end, |&ch| { + if ch == '}' { + found_closing_brace = true; + } + ch == ' ' || ch == ',' + }); + + // Combine the two spans. + // i.e. `a, ` or `a`. + // + // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` + let span = binding_span.with_hi(after_binding_until_next_binding.hi()); + + (found_closing_brace, span) +} + +/// Given a `binding_span`, return the span through to the comma or opening brace of the previous +/// binding. +/// +/// ```ignore (illustrative) +/// use foo::a::{a, b, c}; +/// // ^^--- binding span +/// // | +/// // returned span +/// +/// use foo::{a, b, c}; +/// // --- binding span +/// ``` +fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { + let source_map = sess.source_map(); + + // `prev_source` will contain all of the source that came before the span. + // Then split based on a command and take the first (i.e. closest to our span) + // snippet. In the example, this is a space. + let prev_source = source_map.span_to_prev_source(binding_span).ok()?; + + let prev_comma = prev_source.rsplit(',').collect::>(); + let prev_starting_brace = prev_source.rsplit('{').collect::>(); + if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { + return None; + } + + let prev_comma = prev_comma.first().unwrap(); + let prev_starting_brace = prev_starting_brace.first().unwrap(); + + // If the amount of source code before the comma is greater than + // the amount of source code before the starting brace then we've only + // got one item in the nested item (eg. `issue_52891::{self}`). + if prev_comma.len() > prev_starting_brace.len() { + return None; + } + + Some(binding_span.with_lo(BytePos( + // Take away the number of bytes for the characters we've found and an + // extra for the comma. + binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, + ))) +} + +/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if +/// it is a nested use tree. +/// +/// ```ignore (illustrative) +/// use foo::a::{b, c}; +/// // ^^^^^^^^^^ -- false +/// +/// use foo::{a, b, c}; +/// // ^^^^^^^^^^ -- true +/// +/// use foo::{a, b::{c, d}}; +/// // ^^^^^^^^^^^^^^^ -- true +/// ``` +#[instrument(level = "debug", skip(sess))] +fn find_span_immediately_after_crate_name(sess: &Session, use_span: Span) -> (bool, Span) { + let source_map = sess.source_map(); + + // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. + let mut num_colons = 0; + // Find second colon.. `use issue_59764:` + let until_second_colon = source_map.span_take_while(use_span, |c| { + if *c == ':' { + num_colons += 1; + } + !matches!(c, ':' if num_colons == 2) + }); + // Find everything after the second colon.. `foo::{baz, makro};` + let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); + + let mut found_a_non_whitespace_character = false; + // Find the first non-whitespace character in `from_second_colon`.. `f` + let after_second_colon = source_map.span_take_while(from_second_colon, |c| { + if found_a_non_whitespace_character { + return false; + } + if !c.is_whitespace() { + found_a_non_whitespace_character = true; + } + true + }); + + // Find the first `{` in from_second_colon.. `foo::{` + let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); + + (next_left_bracket == after_second_colon, from_second_colon) +} + +/// A suggestion has already been emitted, change the wording slightly to clarify that both are +/// independent options. +enum Instead { + Yes, + No, +} + +/// Whether an existing place with an `use` item was found. +enum FoundUse { + Yes, + No, +} + +/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. +pub(crate) enum DiagMode { + Normal, + /// The binding is part of a pattern + Pattern, + /// The binding is part of a use statement + Import { + /// `true` means diagnostics is for unresolved import + unresolved_import: bool, + /// `true` mean add the tips afterward for case `use a::{b,c}`, + /// rather than replacing within. + append: bool, + }, +} + +pub(crate) fn import_candidates( + tcx: TyCtxt<'_>, + err: &mut Diag<'_>, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option, + candidates: &[ImportSuggestion], + mode: DiagMode, + append: &str, +) { + show_candidates( + tcx, + err, + use_placement_span, + candidates, + Instead::Yes, + FoundUse::Yes, + mode, + vec![], + append, + ); +} + +type PathString<'a> = (String, &'a str, Option, &'a Option, bool); + +/// When an entity with a given name is not available in scope, we search for +/// entities with that name in all crates. This method allows outputting the +/// results of this search in a programmer-friendly way. If any entities are +/// found and suggested, returns `true`, otherwise returns `false`. +fn show_candidates( + tcx: TyCtxt<'_>, + err: &mut Diag<'_>, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option, + candidates: &[ImportSuggestion], + instead: Instead, + found_use: FoundUse, + mode: DiagMode, + path: Vec, + append: &str, +) -> bool { + if candidates.is_empty() { + return false; + } + + let mut showed = false; + let mut accessible_path_strings: Vec> = Vec::new(); + let mut inaccessible_path_strings: Vec> = Vec::new(); + + candidates.iter().for_each(|c| { + if c.accessible { + // Don't suggest `#[doc(hidden)]` items from other crates + if c.doc_visible { + accessible_path_strings.push(( + pprust::path_to_string(&c.path), + c.descr, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), + &c.note, + c.via_import, + )) + } + } else { + inaccessible_path_strings.push(( + pprust::path_to_string(&c.path), + c.descr, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), + &c.note, + c.via_import, + )) + } + }); + + // we want consistent results across executions, but candidates are produced + // by iterating through a hash map, so make sure they are ordered: + for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { + path_strings.sort_by(|a, b| a.0.cmp(&b.0)); + path_strings.dedup_by(|a, b| a.0 == b.0); + let core_path_strings = + path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); + let std_path_strings = + path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); + let foreign_crate_path_strings = + path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); + + // We list the `crate` local paths first. + // Then we list the `std`/`core` paths. + if std_path_strings.len() == core_path_strings.len() { + // Do not list `core::` paths if we are already listing the `std::` ones. + path_strings.extend(std_path_strings); + } else { + path_strings.extend(std_path_strings); + path_strings.extend(core_path_strings); + } + // List all paths from foreign crates last. + path_strings.extend(foreign_crate_path_strings); + } + + if !accessible_path_strings.is_empty() { + let (determiner, kind, s, name, through) = + if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { + ( + "this", + *descr, + "", + format!(" `{name}`"), + if *via_import { " through its public re-export" } else { "" }, + ) + } else { + // Get the unique item kinds and if there's only one, we use the right kind name + // instead of the more generic "items". + let kinds = accessible_path_strings + .iter() + .map(|(_, descr, _, _, _)| *descr) + .collect::>(); + let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; + let s = if kind.ends_with('s') { "es" } else { "s" }; + + ("one of these", kind, s, String::new(), "") + }; + + let instead = if let Instead::Yes = instead { " instead" } else { "" }; + let mut msg = if let DiagMode::Pattern = mode { + format!( + "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ + pattern", + ) + } else { + format!("consider importing {determiner} {kind}{s}{through}{instead}") + }; + + for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { + err.note(note.clone()); + } + + let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { + msg.push(':'); + + for candidate in accessible_path_strings { + msg.push('\n'); + msg.push_str(&candidate.0); + } + }; + + if let Some(span) = use_placement_span { + let (add_use, trailing) = match mode { + DiagMode::Pattern => { + err.span_suggestions( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + ); + return true; + } + DiagMode::Import { .. } => ("", ""), + DiagMode::Normal => ("use ", ";\n"), + }; + for candidate in &mut accessible_path_strings { + // produce an additional newline to separate the new use statement + // from the directly following item. + let additional_newline = if let FoundUse::No = found_use + && let DiagMode::Normal = mode + { + "\n" + } else { + "" + }; + candidate.0 = + format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); + } + + match mode { + DiagMode::Import { append: true, .. } => { + append_candidates(&mut msg, accessible_path_strings); + err.span_help(span, msg); + } + _ => { + err.span_suggestions_with_style( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + } + } + + if let [first, .., last] = &path[..] { + let sp = first.ident.span.until(last.ident.span); + // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). + // Can happen for derive-generated spans. + if sp.can_be_used_for_suggestions() && !sp.is_empty() { + err.span_suggestion_verbose( + sp, + format!("if you import `{}`, refer to it directly", last.ident), + "", + Applicability::Unspecified, + ); + } + } + } else { + append_candidates(&mut msg, accessible_path_strings); + err.help(msg); + } + showed = true; + } + if !inaccessible_path_strings.is_empty() + && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. })) + { + let prefix = + if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; + if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] { + let msg = format!( + "{prefix}{descr} `{name}`{} exists but is inaccessible", + if let DiagMode::Pattern = mode { ", which" } else { "" } + ); + + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); + let mut multi_span = MultiSpan::from_span(span); + multi_span.push_span_label(span, "not accessible"); + err.span_note(multi_span, msg); + } else { + err.note(msg); + } + if let Some(note) = (*note).as_deref() { + err.note(note.to_string()); + } + } else { + let descr = inaccessible_path_strings + .iter() + .map(|&(_, descr, _, _, _)| descr) + .all_equal_value() + .unwrap_or("item"); + let plural_descr = + if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; + + let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible"); + let mut has_colon = false; + + let mut spans = Vec::new(); + for (name, _, source_span, _, _) in &inaccessible_path_strings { + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); + spans.push((name, span)); + } else { + if !has_colon { + msg.push(':'); + has_colon = true; + } + msg.push('\n'); + msg.push_str(name); + } + } + + let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); + for (name, span) in spans { + multi_span.push_span_label(span, format!("`{name}`: not accessible")); + } + + for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { + err.note(note.clone()); + } + + err.span_note(multi_span, msg); + } + showed = true; + } + showed +} + +#[derive(Debug)] +struct UsePlacementFinder { + target_module: NodeId, + first_legal_span: Option, + first_use_span: Option, +} + +impl UsePlacementFinder { + fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { + let mut finder = + UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; + finder.visit_crate(krate); + if let Some(use_span) = finder.first_use_span { + (Some(use_span), FoundUse::Yes) + } else { + (finder.first_legal_span, FoundUse::No) + } + } +} + +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { + fn visit_crate(&mut self, c: &Crate) { + if self.target_module == CRATE_NODE_ID { + let inject = c.spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(&c.items); + } else { + visit::walk_crate(self, c); + } + } + + fn visit_item(&mut self, item: &'tcx ast::Item) { + if self.target_module == item.id { + if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { + let inject = mod_spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(items); + } + } else { + visit::walk_item(self, item); + } + } +} + +#[derive(Default)] +struct BindingVisitor { + identifiers: Vec, + spans: FxHashMap>, +} + +impl<'tcx> Visitor<'tcx> for BindingVisitor { + fn visit_pat(&mut self, pat: &ast::Pat) { + if let ast::PatKind::Ident(_, ident, _) = pat.kind { + self.identifiers.push(ident.name); + self.spans.entry(ident.name).or_default().push(ident.span); + } + visit::walk_pat(self, pat); + } +} + +fn search_for_any_use_in_items(items: &[Box]) -> Option { + for item in items { + if let ItemKind::Use(..) = item.kind + && is_span_suitable_for_use_injection(item.span) + { + let mut lo = item.span.lo(); + for attr in &item.attrs { + if attr.span.eq_ctxt(item.span) { + lo = std::cmp::min(lo, attr.span.lo()); + } + } + return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); + } + } + None +} + +fn is_span_suitable_for_use_injection(s: Span) -> bool { + // don't suggest placing a use before the prelude + // import or other generated ones + !s.from_expansion() +} diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs deleted file mode 100644 index 2baf423e296d6..0000000000000 --- a/compiler/rustc_resolve/src/errors.rs +++ /dev/null @@ -1,1778 +0,0 @@ -use rustc_errors::codes::*; -use rustc_errors::formatting::DiagMessageAddArg; -use rustc_errors::{ - Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, -}; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::{Ident, Span, Spanned, Symbol}; - -use crate::Res; -use crate::late::PatternSource; - -#[derive(Diagnostic)] -#[diag("can't use {$is_self -> - [true] `Self` - *[false] generic parameters - } from outer item", code = E0401)] -#[note( - "nested items are independent from their parent item for everything except for privacy and name resolution" -)] -pub(crate) struct GenericParamsFromOuterItem { - #[primary_span] - #[label( - "use of {$is_self -> - [true] `Self` - *[false] generic parameter - } from outer item" - )] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) label: Option, - #[subdiagnostic] - pub(crate) refer_to_type_directly: Option, - #[subdiagnostic] - pub(crate) use_let: Option, - #[subdiagnostic] - pub(crate) sugg: Option, - #[subdiagnostic] - pub(crate) static_or_const: Option, - pub(crate) is_self: bool, - #[subdiagnostic] - pub(crate) item: Option, -} - -#[derive(Subdiagnostic)] -#[label( - "{$is_self -> - [true] `Self` - *[false] generic parameter - } used in this inner {$descr}" -)] -pub(crate) struct GenericParamsFromOuterItemInnerItem { - #[primary_span] - pub(crate) span: Span, - pub(crate) descr: String, - pub(crate) is_self: bool, -} - -#[derive(Subdiagnostic)] -pub(crate) enum GenericParamsFromOuterItemStaticOrConst { - #[note("a `static` is a separate item from the item that contains it")] - Static, - #[note("a `const` is a separate item from the item that contains it")] - Const, -} - -#[derive(Subdiagnostic)] -pub(crate) enum GenericParamsFromOuterItemLabel { - #[label("can't use `Self` here")] - SelfTyParam(#[primary_span] Span), - #[label("`Self` type implicitly declared here, by this `impl`")] - SelfTyAlias(#[primary_span] Span), - #[label("type parameter from outer item")] - TyParam(#[primary_span] Span), - #[label("const parameter from outer item")] - ConstParam(#[primary_span] Span), -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try introducing a local generic parameter here", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct GenericParamsFromOuterItemSugg { - #[primary_span] - pub(crate) span: Span, - pub(crate) snippet: String, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try using a local `let` binding instead", - code = "let", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct GenericParamsFromOuterItemUseLet { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "refer to the type directly here instead", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UseTypeDirectly { - #[primary_span] - pub(crate) span: Span, - pub(crate) snippet: String, -} - -#[derive(Diagnostic)] -#[diag("the name `{$name}` is already used for a generic parameter in this item's generic parameters", code = E0403)] -pub(crate) struct NameAlreadyUsedInParameterList { - #[primary_span] - #[label("already used")] - pub(crate) span: Span, - #[label("first use of `{$name}`")] - pub(crate) first_use_span: Span, - pub(crate) name: Ident, -} - -#[derive(Diagnostic)] -#[diag("method `{$method}` is not a member of trait `{$trait_}`", code = E0407)] -pub(crate) struct MethodNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) method: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated function with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedFnWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("type `{$type_}` is not a member of trait `{$trait_}`", code = E0437)] -pub(crate) struct TypeNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) type_: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated type with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedTypeWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("const `{$const_}` is not a member of trait `{$trait_}`", code = E0438)] -pub(crate) struct ConstNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) const_: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated constant with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedConstWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`", code = E0409)] -pub(crate) struct VariableBoundWithDifferentMode { - #[primary_span] - #[label("bound in different ways")] - pub(crate) span: Span, - #[label("first binding")] - pub(crate) first_binding_span: Span, - pub(crate) variable_name: Ident, -} - -#[derive(Diagnostic)] -#[diag("identifier `{$identifier}` is bound more than once in this parameter list", code = E0415)] -pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { - #[primary_span] - #[label("used as parameter more than once")] - pub(crate) span: Span, - pub(crate) identifier: Ident, -} - -#[derive(Diagnostic)] -#[diag("identifier `{$identifier}` is bound more than once in the same pattern", code = E0416)] -pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { - #[primary_span] - #[label("used in a pattern more than once")] - pub(crate) span: Span, - pub(crate) identifier: Ident, -} - -#[derive(Diagnostic)] -#[diag("use of undeclared label `{$name}`", code = E0426)] -pub(crate) struct UndeclaredLabel { - #[primary_span] - #[label("undeclared label `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) sub_reachable: Option, - #[subdiagnostic] - pub(crate) sub_reachable_suggestion: Option, - #[subdiagnostic] - pub(crate) sub_unreachable: Option, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name is reachable")] -pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); - -#[derive(Subdiagnostic)] -#[suggestion( - "try using similarly named label", - code = "{ident_name}", - applicability = "maybe-incorrect" -)] -pub(crate) struct TryUsingSimilarlyNamedLabel { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident_name: Symbol, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name exists but is unreachable")] -pub(crate) struct UnreachableLabelWithSimilarNameExists { - #[primary_span] - pub(crate) ident_span: Span, -} - -#[derive(Diagnostic)] -#[diag("can't capture dynamic environment in a fn item", code = E0434)] -#[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] -pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("attempt to use a non-constant value in a constant", code = E0435)] -pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) with: Option>, - #[subdiagnostic] - pub(crate) with_label: Option, - #[subdiagnostic] - pub(crate) without: Option>, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "consider using `{$suggestion}` instead of `{$current}`", - style = "verbose", - applicability = "has-placeholders" -)] -pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { - // #[primary_span] - #[suggestion_part(code = "{suggestion} ")] - pub(crate) span: Span, - pub(crate) suggestion: &'a str, - #[suggestion_part(code = ": /* Type */")] - pub(crate) type_span: Option, - pub(crate) current: &'a str, -} - -#[derive(Subdiagnostic)] -#[label("non-constant value")] -pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[label("this would need to be a `{$suggestion}`")] -pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { - #[primary_span] - pub(crate) ident_span: Span, - pub(crate) suggestion: &'a str, -} - -#[derive(Diagnostic)] -#[diag("{$shadowing_binding}s cannot shadow {$shadowed_binding}s", code = E0530)] -pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { - #[primary_span] - #[label("cannot be named the same as {$article} {$shadowed_binding}")] - pub(crate) span: Span, - pub(crate) shadowing_binding: PatternSource, - pub(crate) shadowed_binding: Res, - pub(crate) article: &'a str, - #[subdiagnostic] - pub(crate) sub_suggestion: Option, - #[label("the {$shadowed_binding} `{$name}` is {$participle} here")] - pub(crate) shadowed_binding_span: Span, - pub(crate) participle: &'a str, - pub(crate) name: Symbol, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try specify the pattern arguments", - code = "{name}(..)", - applicability = "unspecified" -)] -pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("generic parameter defaults cannot reference parameters before they are declared", code = E0128)] -pub(crate) struct ForwardDeclaredGenericParam { - #[primary_span] - #[label("cannot reference `{$param}` before it is declared")] - pub(crate) span: Span, - pub(crate) param: Symbol, -} - -#[derive(Diagnostic)] -#[diag("const parameter types cannot reference parameters before they are declared")] -pub(crate) struct ForwardDeclaredGenericInConstParamTy { - #[primary_span] - #[label("const parameter type cannot reference `{$param}` before it is declared")] - pub(crate) span: Span, - pub(crate) param: Symbol, -} - -#[derive(Diagnostic)] -#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] -pub(crate) struct ParamInTyOfConstParam { - #[primary_span] - #[label("the type must not depend on the parameter `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] -pub(crate) struct SelfInGenericParamDefault { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("cannot use `Self` in const parameter type")] -pub(crate) struct SelfInConstGenericTy { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag( - "{$is_gca -> - [true] generic parameters in const blocks are not allowed; use a named `const` item instead - *[false] generic parameters may not be used in const operations -}" -)] -pub(crate) struct ParamInNonTrivialAnonConst { - #[primary_span] - #[label("cannot perform const operation using `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) param_kind: ParamKindInNonTrivialAnonConst, - #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] - pub(crate) help: bool, - pub(crate) is_gca: bool, - #[help( - "consider factoring the expression into a `type const` item and use it as the const argument instead" - )] - pub(crate) help_gca: bool, -} - -#[derive(Debug)] -#[derive(Subdiagnostic)] -pub(crate) enum ParamKindInNonTrivialAnonConst { - #[note("type parameters may not be used in const expressions")] - Type, - #[help("const parameters may only be used as standalone arguments here, i.e. `{$name}`")] - Const { name: Symbol }, - #[note("lifetime parameters may not be used in const expressions")] - Lifetime, -} - -#[derive(Diagnostic)] -#[diag("use of unreachable label `{$name}`", code = E0767)] -#[note("labels are unreachable through functions, closures, async blocks and modules")] -pub(crate) struct UnreachableLabel { - #[primary_span] - #[label("unreachable label `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[label("unreachable label defined here")] - pub(crate) definition_span: Span, - #[subdiagnostic] - pub(crate) sub_suggestion: Option, - #[subdiagnostic] - pub(crate) sub_suggestion_label: Option, - #[subdiagnostic] - pub(crate) sub_unreachable_label: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try using similarly named label", - code = "{ident_name}", - applicability = "maybe-incorrect" -)] -pub(crate) struct UnreachableLabelSubSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident_name: Symbol, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name is reachable")] -pub(crate) struct UnreachableLabelSubLabel { - #[primary_span] - pub(crate) ident_span: Span, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name exists but is also unreachable")] -pub(crate) struct UnreachableLabelSubLabelUnreachable { - #[primary_span] - pub(crate) ident_span: Span, -} - -#[derive(Diagnostic)] -#[diag("invalid `sym` operand")] -#[help("`sym` operands must refer to either a function or a static")] -pub(crate) struct InvalidAsmSym { - #[primary_span] - #[label("is a local variable")] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("attempt to use a non-constant value in a constant")] -pub(crate) struct LowercaseSelf { - #[primary_span] - #[suggestion( - "try using `Self`", - code = "Self", - applicability = "maybe-incorrect", - style = "short" - )] - pub(crate) span: Span, -} - -#[derive(Debug)] -#[derive(Diagnostic)] -#[diag("never patterns cannot contain variable bindings")] -pub(crate) struct BindingInNeverPattern { - #[primary_span] - #[suggestion( - "use a wildcard `_` instead", - code = "_", - applicability = "machine-applicable", - style = "short" - )] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("duplicate definitions with name `{$name}`:", code = E0201)] -pub(crate) struct TraitImplDuplicate { - #[primary_span] - #[label("duplicate definition")] - pub(crate) span: Span, - #[label("previous definition here")] - pub(crate) old_span: Span, - #[label("item in trait")] - pub(crate) trait_item_span: Span, - pub(crate) name: Ident, -} - -#[derive(Diagnostic)] -#[diag("relative paths are not supported in visibilities in 2018 edition or later")] -pub(crate) struct Relative2018 { - #[primary_span] - pub(crate) span: Span, - #[suggestion("try", code = "crate::{path_str}", applicability = "maybe-incorrect")] - pub(crate) path_span: Span, - pub(crate) path_str: String, -} - -#[derive(Diagnostic)] -#[diag("visibilities can only be restricted to ancestor modules", code = E0742)] -pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("expected module, found {$res} `{$path_str}`", code = E0577)] -pub(crate) struct ExpectedModuleFound { - #[primary_span] - #[label("not a module")] - pub(crate) span: Span, - pub(crate) res: Res, - pub(crate) path_str: String, -} - -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the visibility", code = E0578)] -pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("trait implementation can only be restricted to ancestor modules")] -pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("cannot use a tool module through an import")] -pub(crate) struct ToolModuleImported { - #[primary_span] - pub(crate) span: Span, - #[note("the tool module imported here")] - pub(crate) import: Span, -} - -#[derive(Diagnostic)] -#[diag("visibility must resolve to a module")] -pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("expected {$expected}, found {$found} `{$macro_path}`")] -pub(crate) struct MacroExpectedFound<'a> { - #[primary_span] - #[label("not {$article} {$expected}")] - pub(crate) span: Span, - pub(crate) found: &'a str, - pub(crate) article: &'static str, - pub(crate) expected: &'a str, - pub(crate) macro_path: &'a str, - #[subdiagnostic] - pub(crate) remove_surrounding_derive: Option, - #[subdiagnostic] - pub(crate) add_as_non_derive: Option>, -} - -#[derive(Subdiagnostic)] -#[help("remove from the surrounding `derive()`")] -pub(crate) struct RemoveSurroundingDerive { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[help( - " - add as non-Derive macro - `#[{$macro_path}]`" -)] -pub(crate) struct AddAsNonDerive<'a> { - pub(crate) macro_path: &'a str, -} - -#[derive(Diagnostic)] -#[diag("can't use a procedural macro from the same crate that defines it")] -pub(crate) struct ProcMacroSameCrate { - #[primary_span] - pub(crate) span: Span, - #[help("you can define integration tests in a directory named `tests`")] - pub(crate) is_test: bool, -} - -#[derive(Diagnostic)] -#[diag("cannot find {$ns_descr} `{$ident}` in this scope")] -pub(crate) struct ProcMacroDeriveResolutionFallback { - #[label("names from parent modules are not accessible without an explicit import")] - pub span: Span, - pub ns_descr: &'static str, - pub ident: Symbol, -} - -#[derive(Diagnostic)] -#[diag( - "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" -)] -pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { - #[note("the macro is defined here")] - pub definition: Span, -} - -#[derive(Diagnostic)] -#[diag("`#[macro_use]` is not supported on `extern crate self`")] -pub(crate) struct MacroUseExternCrateSelf { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("not sure whether the path is accessible or not")] -#[note("the type may have associated items, but we are currently not checking them")] -pub(crate) struct CfgAccessibleUnsure { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Debug)] -#[derive(Diagnostic)] -#[diag("generic parameters may not be used in enum discriminant values")] -pub(crate) struct ParamInEnumDiscriminant { - #[primary_span] - #[label("cannot perform const operation using `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) param_kind: ParamKindInEnumDiscriminant, -} - -#[derive(Debug)] -#[derive(Subdiagnostic)] -pub(crate) enum ParamKindInEnumDiscriminant { - #[note("type parameters may not be used in enum discriminant values")] - Type, - #[note("const parameters may not be used in enum discriminant values")] - Const, - #[note("lifetime parameters may not be used in enum discriminant values")] - Lifetime, -} - -#[derive(Subdiagnostic)] -#[label("you can use `as` to change the binding name of the import")] -pub(crate) struct ChangeImportBinding { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "you can use `as` to change the binding name of the import", - code = "{suggestion}", - applicability = "maybe-incorrect" -)] -pub(crate) struct ChangeImportBindingSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) suggestion: String, -} - -#[derive(Diagnostic)] -#[diag("imports cannot refer to {$what}")] -pub(crate) struct ImportsCannotReferTo<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) what: &'a str, -} - -#[derive(Diagnostic)] -#[diag("cannot find {$expected} `{$ident}` in this scope")] -pub(crate) struct CannotFindIdentInThisScope<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) expected: &'a str, - pub(crate) ident: Ident, -} - -#[derive(Subdiagnostic)] -#[note("unsafe traits like `{$ident}` should be implemented explicitly")] -pub(crate) struct ExplicitUnsafeTraits { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Subdiagnostic)] -#[note("a macro with the same name exists, but it appears later")] -pub(crate) struct MacroDefinedLater { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[label("consider moving the definition of `{$ident}` before this call")] -pub(crate) struct MacroSuggMovePosition { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Subdiagnostic)] -pub(crate) enum MacroRulesNot { - #[label("`{$ident}` exists, but has no rules for function-like invocation")] - Func { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("`{$ident}` exists, but has no `attr` rules")] - Attr { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("`{$ident}` exists, but has no `derive` rules")] - Derive { - #[primary_span] - span: Span, - ident: Ident, - }, -} - -#[derive(Subdiagnostic)] -#[note("maybe you have forgotten to define a name for this `macro_rules!`")] -pub(crate) struct MaybeMissingMacroRulesName { - #[primary_span] - pub(crate) spans: MultiSpan, -} - -#[derive(Subdiagnostic)] -#[help("have you added the `#[macro_use]` on the module/import?")] -pub(crate) struct AddedMacroUse; - -#[derive(Subdiagnostic)] -#[suggestion("consider adding a derive", code = "{suggestion}", applicability = "maybe-incorrect")] -pub(crate) struct ConsiderAddingADerive { - #[primary_span] - pub(crate) span: Span, - pub(crate) suggestion: String, -} - -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the import")] -pub(crate) struct CannotDetermineImportResolution { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the {$kind} `{$path}`")] -#[note("import resolution is stuck, try simplifying macro imports")] -pub(crate) struct CannotDetermineMacroResolution { - #[primary_span] - pub(crate) span: Span, - pub(crate) kind: &'static str, - pub(crate) path: String, -} - -#[derive(Diagnostic)] -#[diag("`{$ident}` is private, and cannot be re-exported", code = E0364)] -pub(crate) struct CannotBeReexportedPrivate { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0364)] -pub(crate) struct CannotBeReexportedCratePublic { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("`{$ident}` is private, and cannot be re-exported", code = E0365)] -#[note("consider declaring type or module `{$ident}` with `pub`")] -pub(crate) struct CannotBeReexportedPrivateNS { - #[primary_span] - #[label("re-export of private `{$ident}`")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0365)] -#[note("consider declaring type or module `{$ident}` with `pub`")] -pub(crate) struct CannotBeReexportedCratePublicNS { - #[primary_span] - #[label("re-export of crate public `{$ident}`")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] -pub(crate) struct PrivateExternCrateReexport { - pub ident: Ident, - #[suggestion( - "consider making the `extern crate` item publicly accessible", - code = "pub ", - style = "verbose", - applicability = "maybe-incorrect" - )] - pub sugg: Span, -} - -#[derive(Subdiagnostic)] -#[help("consider adding a `#[macro_export]` to the macro in the imported module")] -pub(crate) struct ConsiderAddingMacroExport { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`", - code = "pub(crate)", - applicability = "maybe-incorrect" -)] -pub(crate) struct ConsiderMarkingAsPubCrate { - #[primary_span] - pub(crate) vis_span: Span, -} - -#[derive(Subdiagnostic)] -#[note("consider marking `{$ident}` as `pub` in the imported module")] -pub(crate) struct ConsiderMarkingAsPub { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("cannot glob-import all possible crates")] -pub(crate) struct CannotGlobImportAllCrates { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "you might have meant to write a const parameter here", - code = "const ", - style = "verbose" -)] -pub(crate) struct UnexpectedResChangeTyToConstParamSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", - code = "{snippet}", - applicability = "machine-applicable", - style = "verbose" -)] -pub(crate) struct UnexpectedMissingConstParameter { - #[primary_span] - pub span: Span, - pub snippet: String, - pub item_name: String, - pub item_location: String, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "you might have meant to write a const parameter here", - applicability = "has-placeholders", - style = "verbose" -)] -pub(crate) struct UnexpectedResChangeTyParamToConstParamSugg { - #[suggestion_part(code = "const ")] - pub before: Span, - #[suggestion_part(code = ": /* Type */")] - pub after: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "if you meant to collect the rest of the slice in `{$ident}`, use the at operator", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { - #[primary_span] - pub span: Span, - pub ident: Ident, - pub snippet: String, -} - -#[derive(Diagnostic)] -#[diag("an `extern crate` loading macros must be at the crate root", code = E0468)] -pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("`extern crate self;` requires renaming")] -pub(crate) struct ExternCrateSelfRequiresRenaming { - #[primary_span] - #[suggestion( - "rename the `self` crate to be able to import it", - code = "extern crate self as name;", - applicability = "has-placeholders" - )] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("`{$name}` is already in scope")] -#[note("macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)")] -pub(crate) struct MacroUseNameAlreadyInUse { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("imported macro not found", code = E0469)] -pub(crate) struct ImportedMacroNotFound { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("`#[macro_escape]` is a deprecated synonym for `#[macro_use]`")] -pub(crate) struct MacroExternDeprecated { - #[primary_span] - pub(crate) span: Span, - #[help("try an outer attribute: `#[macro_use]`")] - pub inner_attribute: bool, -} - -#[derive(Diagnostic)] -#[diag("arguments to `macro_use` are not allowed here")] -pub(crate) struct ArgumentsMacroUseNotAllowed { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "try renaming it with a name", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UnnamedImportSugg { - #[suggestion_part(code = "{ident} as name")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("imports need to be explicitly named")] -pub(crate) struct UnnamedImport { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) sugg: UnnamedImportSugg, -} - -#[derive(Diagnostic)] -#[diag("macro-expanded `extern crate` items cannot shadow names passed with `--extern`")] -pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("`&` without an explicit lifetime name cannot be used here", code = E0637)] -pub(crate) struct ElidedAnonymousLifetimeReportError { - #[primary_span] - #[label("explicit lifetime name needed here")] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) suggestion: Option, -} - -#[derive(Diagnostic)] -#[diag( - "associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" -)] -pub(crate) struct LendingIteratorReportError { - #[primary_span] - pub(crate) lifetime: Span, - #[note( - "you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type" - )] - pub(crate) ty: Span, -} - -#[derive(Diagnostic)] -#[diag("missing lifetime in associated type")] -pub(crate) struct AnonymousLifetimeNonGatReportError { - #[primary_span] - #[label("this lifetime must come from the implemented type")] - pub(crate) lifetime: Span, - #[note( - "in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" - )] - pub(crate) decl: MultiSpan, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "consider introducing a higher-ranked lifetime here", - applicability = "machine-applicable" -)] -pub(crate) struct ElidedAnonymousLifetimeReportErrorSuggestion { - #[suggestion_part(code = "for<'a> ")] - pub(crate) lo: Span, - #[suggestion_part(code = "'a ")] - pub(crate) hi: Span, -} - -#[derive(Diagnostic)] -#[diag("`'_` cannot be used here", code = E0637)] -pub(crate) struct ExplicitAnonymousLifetimeReportError { - #[primary_span] - #[label("`'_` is a reserved lifetime name")] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("implicit elided lifetime not allowed here", code = E0726)] -pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) subdiag: ElidedLifetimeInPathSubdiag, -} - -#[derive(Diagnostic)] -#[diag("`'_` cannot be used here", code = E0637)] -#[help("use another lifetime specifier")] -pub(crate) struct UnderscoreLifetimeIsReserved { - #[primary_span] - #[label("`'_` is a reserved lifetime name")] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("invalid lifetime parameter name: `{$lifetime}`", code = E0262)] -pub(crate) struct StaticLifetimeIsReserved { - #[primary_span] - #[label("'static is a reserved lifetime name")] - pub(crate) span: Span, - pub(crate) lifetime: Ident, -} - -#[derive(Diagnostic)] -#[diag("variable `{$name}` is not bound in all patterns", code = E0408)] -pub(crate) struct VariableIsNotBoundInAllPatterns { - #[primary_span] - pub(crate) multispan: MultiSpan, - pub(crate) name: Ident, -} - -#[derive(Subdiagnostic, Debug, Clone)] -#[label("pattern doesn't bind `{$name}`")] -pub(crate) struct PatternDoesntBindName { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Ident, -} - -#[derive(Subdiagnostic, Debug, Clone)] -#[label("variable not in all patterns")] -pub(crate) struct VariableNotInAllPatterns { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "you might have meant to use the similarly named previously used binding `{$typo}`", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct PatternBindingTypo { - #[suggestion_part(code = "{typo}")] - pub(crate) spans: Vec, - pub(crate) typo: Symbol, -} - -#[derive(Diagnostic)] -#[diag("the name `{$name}` is defined multiple times")] -#[note("`{$name}` must be defined only once in the {$descr} namespace of this {$container}")] -pub(crate) struct NameDefinedMultipleTime { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, - pub(crate) descr: &'static str, - pub(crate) container: &'static str, - #[subdiagnostic] - pub(crate) label: NameDefinedMultipleTimeLabel, - #[subdiagnostic] - pub(crate) old_binding_label: Option, -} - -#[derive(Subdiagnostic)] -pub(crate) enum NameDefinedMultipleTimeLabel { - #[label("`{$name}` reimported here")] - Reimported { - #[primary_span] - span: Span, - name: Symbol, - }, - #[label("`{$name}` redefined here")] - Redefined { - #[primary_span] - span: Span, - name: Symbol, - }, -} - -#[derive(Subdiagnostic)] -pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { - #[label("previous import of the {$old_kind} `{$name}` here")] - Import { - #[primary_span] - span: Span, - old_kind: &'static str, - name: Symbol, - }, - #[label("previous definition of the {$old_kind} `{$name}` here")] - Definition { - #[primary_span] - span: Span, - old_kind: &'static str, - name: Symbol, - }, -} - -#[derive(Diagnostic)] -#[diag("{$ident_descr} `{$ident}` is private", code = E0603)] -pub(crate) struct IsPrivate<'a> { - #[primary_span] - #[label("private {$ident_descr}")] - pub(crate) span: Span, - pub(crate) ident_descr: &'a str, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("generic arguments in macro path")] -pub(crate) struct GenericArgumentsInMacroPath { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("attributes starting with `rustc` are reserved for use by the `rustc` compiler")] -pub(crate) struct AttributesStartingWithRustcAreReserved { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag( - "attributes containing a segment starting with `rustc` are reserved for use by the `rustc` compiler" -)] -pub(crate) struct AttributesContainingRustcAreReserved { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("cannot use {$article} {$descr} through an import")] -pub(crate) struct CannotUseThroughAnImport { - #[primary_span] - pub(crate) span: Span, - pub(crate) article: &'static str, - pub(crate) descr: &'static str, - #[note("the {$descr} imported here")] - pub(crate) binding_span: Option, -} - -#[derive(Diagnostic)] -#[diag("name `{$ident}` is reserved in attribute namespace")] -pub(crate) struct NameReservedInAttributeNamespace { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Symbol, -} - -#[derive(Diagnostic)] -#[diag("cannot find a built-in macro with name `{$ident}`")] -pub(crate) struct CannotFindBuiltinMacroWithName { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("tool `{$tool}` was already registered")] -pub(crate) struct ToolWasAlreadyRegistered { - #[primary_span] - pub(crate) span: Span, - pub(crate) tool: Ident, - #[label("already registered here")] - pub(crate) old_ident_span: Span, -} - -#[derive(Subdiagnostic)] -pub(crate) enum DefinedHere { - #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] - SimilarlyNamed { - #[primary_span] - span: Span, - candidate_descr: &'static str, - candidate: Symbol, - }, - #[label("{$candidate_descr} `{$candidate}` defined here")] - SingleItem { - #[primary_span] - span: Span, - candidate_descr: &'static str, - candidate: Symbol, - }, -} - -#[derive(Subdiagnostic)] -#[label("{$outer_ident_descr} `{$outer_ident}` is not publicly re-exported")] -pub(crate) struct OuterIdentIsNotPubliclyReexported { - #[primary_span] - pub(crate) span: Span, - pub(crate) outer_ident_descr: &'static str, - pub(crate) outer_ident: Ident, -} - -#[derive(Subdiagnostic)] -#[label("a constructor is private if any of the fields is private")] -pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "{ $number_of_fields -> - [one] consider making the field publicly accessible - *[other] consider making the fields publicly accessible - }", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct ConsiderMakingTheFieldPublic { - #[suggestion_part(code = "pub ")] - pub(crate) spans: Vec, - pub(crate) number_of_fields: usize, -} - -#[derive(Subdiagnostic)] -pub(crate) enum ImportIdent { - #[suggestion( - "import `{$ident}` through the re-export", - code = "{path}", - applicability = "machine-applicable", - style = "verbose" - )] - ThroughReExport { - #[primary_span] - span: Span, - ident: Ident, - path: String, - }, - #[suggestion( - "import `{$ident}` directly", - code = "{path}", - applicability = "machine-applicable", - style = "verbose" - )] - Directly { - #[primary_span] - span: Span, - ident: Ident, - path: String, - }, -} - -#[derive(Subdiagnostic)] -#[note( - "{$first -> - [true] {$dots -> - [true] the {$binding_descr} `{$binding_name}` is defined here... - *[false] the {$binding_descr} `{$binding_name}` is defined here - } - *[false] {$dots -> - [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... - *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here - } - }" -)] -pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { - #[primary_span] - pub(crate) span: MultiSpan, - pub(crate) binding_descr: &'a str, - pub(crate) binding_name: Ident, - pub(crate) first: bool, - pub(crate) dots: bool, -} - -#[derive(Subdiagnostic)] -#[suggestion("remove unnecessary import", code = "", applicability = "maybe-incorrect")] -pub(crate) struct RemoveUnnecessaryImport { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "remove unnecessary import", - code = "", - applicability = "maybe-incorrect", - style = "tool-only" -)] -pub(crate) struct ToolOnlyRemoveUnnecessaryImport { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[note("`{$imported_ident}` is imported here, but it is {$imported_ident_desc}")] -pub(crate) struct IdentImporterHereButItIsDesc<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) imported_ident: Ident, - pub(crate) imported_ident_desc: &'a str, -} - -#[derive(Subdiagnostic)] -#[note("`{$imported_ident}` is in scope, but it is {$imported_ident_desc}")] -pub(crate) struct IdentInScopeButItIsDesc<'a> { - pub(crate) imported_ident: Ident, - pub(crate) imported_ident_desc: &'a str, -} - -pub(crate) struct FoundItemConfigureOut { - pub(crate) span: Span, - pub(crate) item_was: ItemWas, -} - -pub(crate) enum ItemWas { - BehindFeature { feature: Symbol, span: Span }, - CfgOut { span: Span }, -} - -impl Subdiagnostic for FoundItemConfigureOut { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - let mut multispan: MultiSpan = self.span.into(); - match self.item_was { - ItemWas::BehindFeature { feature, span } => { - let value = feature.into_diag_arg(&mut None); - let msg = msg!("the item is gated behind the `{$feature}` feature") - .arg("feature", value) - .format(); - multispan.push_span_label(span, msg); - } - ItemWas::CfgOut { span } => { - multispan.push_span_label(span, msg!("the item is gated here")); - } - } - diag.span_note(multispan, msg!("found an item that was configured out")); - } -} - -#[derive(Diagnostic)] -#[diag("item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}`")] -pub(crate) struct TraitImplMismatch { - #[primary_span] - #[label("does not match trait")] - pub(crate) span: Span, - pub(crate) name: Ident, - pub(crate) kind: &'static str, - pub(crate) trait_path: String, - #[label("item in trait")] - pub(crate) trait_item_span: Span, -} - -#[derive(Diagnostic)] -#[diag("derive helper attribute is used before it is introduced")] -pub(crate) struct LegacyDeriveHelpers { - #[label("the attribute is introduced here")] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag("unused extern crate")] -pub(crate) struct UnusedExternCrate { - #[label("unused")] - pub span: Span, - #[suggestion( - "remove the unused `extern crate`", - code = "", - applicability = "machine-applicable", - style = "verbose" - )] - pub removal_span: Span, -} - -#[derive(Diagnostic)] -#[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] -pub(crate) struct ReexportPrivateDependency { - pub name: Symbol, - pub kind: &'static str, - pub krate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("unused label")] -pub(crate) struct UnusedLabel; - -#[derive(Diagnostic)] -#[diag("unused `#[macro_use]` import")] -pub(crate) struct UnusedMacroUse; - -#[derive(Diagnostic)] -#[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] -#[help("remove it and import macros at use sites with a `use` item instead")] -pub(crate) struct MacroUseDeprecated; - -#[derive(Diagnostic)] -#[diag("macro `{$ident}` is private")] -pub(crate) struct MacroIsPrivate { - pub ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("unused macro definition: `{$name}`")] -pub(crate) struct UnusedMacroDefinition { - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("rule #{$n} of macro `{$name}` is never used")] -pub(crate) struct MacroRuleNeverUsed { - pub n: usize, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("`extern crate` is not idiomatic in the new edition")] -pub(crate) struct ExternCrateNotIdiomatic { - #[suggestion( - "convert it to a `use`", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - pub code: &'static str, -} - -#[derive(Diagnostic)] -#[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] -#[help("import `macro_rules` with `use` to make it callable above its definition")] -pub(crate) struct OutOfScopeMacroCalls { - #[label("not found from {$location}")] - pub span: Span, - pub path: String, - pub location: String, -} - -#[derive(Diagnostic)] -#[diag( - "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" -)] -pub(crate) struct RedundantImportVisibility { - #[note("the most public imported item is `{$max_vis}`")] - pub span: Span, - #[help("reduce the glob import's visibility or increase visibility of imported items")] - pub help: (), - pub import_vis: String, - pub max_vis: String, -} - -#[derive(Diagnostic)] -#[diag("unknown diagnostic attribute")] -pub(crate) struct UnknownDiagnosticAttribute { - #[subdiagnostic] - pub help: Option, -} - -#[derive(Subdiagnostic)] -pub(crate) enum UnknownDiagnosticAttributeHelp { - #[suggestion( - "an attribute with a similar name exists", - style = "verbose", - code = "{typo_name}", - applicability = "machine-applicable" - )] - Typo { - #[primary_span] - span: Span, - typo_name: Symbol, - }, - #[help("add `#![feature({$feature})]` to the crate attributes to enable")] - UseFeature { feature: Symbol }, -} - -// FIXME: Make this properly translatable. -pub(crate) struct Ambiguity { - pub ident: Ident, - pub ambig_vis: Option, - pub kind: &'static str, - pub help: Option<&'static [&'static str]>, - pub b1_note: Spanned, - pub b1_help_msgs: Vec, - pub b2_note: Spanned, - pub b2_help_msgs: Vec, - /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. - pub is_error: bool, -} - -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let Self { - ident, - ambig_vis, - kind, - help, - b1_note, - b1_help_msgs, - b2_note, - b2_help_msgs, - is_error, - } = self; - - let mut diag = Diag::new(dcx, level, "").with_span(ident.span); - if is_error { - diag.code(E0659); - } - if let Some(ambig_vis) = ambig_vis { - diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); - } else { - diag.primary_message(format!("`{}` is ambiguous", ident)); - diag.span_label(ident.span, "ambiguous name"); - } - diag.note(format!("ambiguous because of {}", kind)); - diag.span_note(b1_note.span, b1_note.node); - if let Some(help) = help { - for help in help { - diag.help(*help); - } - } - for help_msg in b1_help_msgs { - diag.help(help_msg); - } - diag.span_note(b2_note.span, b2_note.node); - for help_msg in b2_help_msgs { - diag.help(help_msg); - } - diag - } -} - -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` never used")] -pub(crate) struct UnusedLifetime { - #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] - pub deletion_span: Option, - - pub ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("ambiguous glob re-exports")] -pub(crate) struct AmbiguousGlobReexports { - #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] - pub first_reexport: Span, - #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] - pub duplicate_reexport: Span, - - pub name: String, - pub namespace: String, -} - -#[derive(Diagnostic)] -#[diag("private item shadows public glob re-export")] -pub(crate) struct HiddenGlobReexports { - #[note( - "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" - )] - pub glob_reexport: Span, - #[note("but the private item here shadows it")] - pub private_item: Span, - - pub name: String, - pub namespace: String, -} - -#[derive(Diagnostic)] -#[diag("the item `{$ident}` is imported redundantly")] -pub(crate) struct RedundantImport { - #[subdiagnostic] - pub subs: Vec, - pub ident: Ident, -} - -#[derive(Subdiagnostic)] -pub(crate) enum RedundantImportSub { - #[label("the item `{$ident}` is already imported here")] - ImportedHere { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already defined here")] - DefinedHere { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already imported by the extern prelude")] - ImportedPrelude { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already defined by the extern prelude")] - DefinedPrelude { - #[primary_span] - span: Span, - ident: Ident, - }, -} - -#[derive(Diagnostic)] -#[diag("unnecessary qualification")] -pub(crate) struct UnusedQualifications { - #[suggestion( - "remove the unnecessary path segments", - style = "verbose", - code = "", - applicability = "machine-applicable" - )] - pub removal_span: Span, -} - -#[derive(Diagnostic)] -#[diag( - "{$elided -> - [true] `&` without an explicit lifetime name cannot be used here - *[false] `'_` cannot be used here - }" -)] -pub(crate) struct AssociatedConstElidedLifetime { - #[suggestion( - "use the `'static` lifetime", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - - pub code: &'static str, - pub elided: bool, - #[note("cannot automatically infer `'static` because of other lifetimes in scope")] - pub lifetimes_in_scope: MultiSpan, -} - -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` only used once")] -pub(crate) struct SingleUseLifetime { - #[label("this lifetime...")] - pub param_span: Span, - #[label("...is used only here")] - pub use_span: Span, - #[subdiagnostic] - pub suggestion: Option, - - pub ident: Ident, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] -pub(crate) struct SingleUseLifetimeSugg { - #[suggestion_part(code = "")] - pub deletion_span: Option, - #[suggestion_part(code = "{replace_lt}")] - pub use_span: Span, - - pub replace_lt: String, -} - -#[derive(Diagnostic)] -#[diag( - "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" -)] -pub(crate) struct AbsPathWithModule { - #[subdiagnostic] - pub sugg: AbsPathWithModuleSugg, -} - -#[derive(Subdiagnostic)] -#[suggestion("use `crate`", code = "{replacement}")] -pub(crate) struct AbsPathWithModuleSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, - pub replacement: String, -} - -#[derive(Diagnostic)] -#[diag("hidden lifetime parameters in types are deprecated")] -pub(crate) struct ElidedLifetimesInPaths { - #[subdiagnostic] - pub subdiag: rustc_errors::ElidedLifetimeInPathSubdiag, -} - -#[derive(Diagnostic)] -#[diag( - "{$num_snippets -> - [one] unused import: {$span_snippets} - *[other] unused imports: {$span_snippets} - }" -)] -pub(crate) struct UnusedImports { - #[subdiagnostic] - pub sugg: Option, - #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] - pub test_module_span: Option, - - pub span_snippets: DiagArgValue, - pub num_snippets: usize, -} - -#[derive(Subdiagnostic)] -pub(crate) enum UnusedImportsSugg { - #[suggestion( - "remove the whole `use` item", - applicability = "machine-applicable", - code = "", - style = "tool-only" - )] - RemoveWholeUse { - #[primary_span] - span: Span, - }, - #[multipart_suggestion( - "{$num_to_remove -> - [one] remove the unused import - *[other] remove the unused imports - }", - applicability = "machine-applicable", - style = "tool-only" - )] - RemoveImports { - #[suggestion_part(code = "")] - remove_spans: Vec, - num_to_remove: usize, - }, -} diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 49461651412b2..2e69405811db4 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -14,7 +14,7 @@ use rustc_span::{Ident, Span, kw, sym}; use smallvec::SmallVec; use tracing::{debug, instrument}; -use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use crate::diagnostics::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::{Import, NameResolution}; use crate::late::{ @@ -25,7 +25,7 @@ use crate::{ AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingKey, CmResolver, Decl, DeclKind, Determinacy, Finalize, IdentKey, ImportKind, ImportSummary, LateDecl, LocalModule, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, Res, ResolutionError, - Resolver, Scope, ScopeSet, Segment, Stage, Symbol, Used, errors, + Resolver, Scope, ScopeSet, Segment, Stage, Symbol, Used, diagnostics, }; #[derive(Copy, Clone)] @@ -630,7 +630,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident_span, - errors::ProcMacroDeriveResolutionFallback { + diagnostics::ProcMacroDeriveResolutionFallback { span: orig_ident_span, ns_descr: ns.descr(), ident: ident.name, @@ -681,7 +681,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident_span, - errors::ProcMacroDeriveResolutionFallback { + diagnostics::ProcMacroDeriveResolutionFallback { span: orig_ident_span, ns_descr: ns.descr(), ident: ident.name, @@ -1988,7 +1988,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { record_segment_res(self.reborrow(), finalize, res, id); } else if res == Res::ToolMod && !is_last && opt_ns.is_some() { if binding.is_import() { - self.dcx().emit_err(errors::ToolModuleImported { + self.dcx().emit_err(diagnostics::ToolModuleImported { span: ident.span, import: binding.span, }); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f23cb3dd003a9..a6c7d2fd2c94e 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -33,13 +33,13 @@ use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::debug; use crate::Namespace::{self, *}; -use crate::diagnostics::{DiagMode, Suggestion, import_candidates}; -use crate::errors::{ +use crate::diagnostics::{ self, CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate, CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates, ConsiderAddingMacroExport, ConsiderMarkingAsPub, ConsiderMarkingAsPubCrate, }; +use crate::error_helper::{DiagMode, Suggestion, import_candidates}; use crate::ref_mut::CmCell; use crate::{ AmbiguityError, BindingKey, CmResolver, Decl, DeclData, DeclKind, Determinacy, Finalize, @@ -1014,7 +1014,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { AMBIGUOUS_GLOB_REEXPORTS, import.root_id, import.root_span, - errors::AmbiguousGlobReexports { + diagnostics::AmbiguousGlobReexports { name: key.ident.name.to_string(), namespace: key.ns.descr().to_string(), first_reexport: import.root_span, @@ -1045,7 +1045,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { HIDDEN_GLOB_REEXPORTS, binding_id, binding.span, - errors::HiddenGlobReexports { + diagnostics::HiddenGlobReexports { name: key.ident.name.to_string(), namespace: key.ns.descr().to_owned(), glob_reexport: glob_decl.span, @@ -1069,7 +1069,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { EXPORTED_PRIVATE_DEPENDENCIES, binding_id, binding.span, - crate::errors::ReexportPrivateDependency { + crate::diagnostics::ReexportPrivateDependency { name: key.ident.name, kind: binding.res().descr(), krate: self.tcx.crate_name(reexported_def_id.krate), @@ -1462,7 +1462,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { UNUSED_IMPORTS, id, import.span, - crate::errors::RedundantImportVisibility { + crate::diagnostics::RedundantImportVisibility { span: import.span, help: (), max_vis: max_vis.to_string(def_id, self.tcx), @@ -1778,7 +1778,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { { let ImportKind::Single { id, .. } = import.kind else { unreachable!() }; let sugg = self.tcx.source_span(extern_crate_id).shrink_to_lo(); - let diagnostic = crate::errors::PrivateExternCrateReexport { ident, sugg }; + let diagnostic = crate::diagnostics::PrivateExternCrateReexport { ident, sugg }; return Some(BufferedEarlyLint { lint_id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE), node_id: id, @@ -1889,20 +1889,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .into_iter() .map(|(span, is_imported)| match (span.is_dummy(), is_imported) { (false, true) => { - errors::RedundantImportSub::ImportedHere { span, ident } + diagnostics::RedundantImportSub::ImportedHere { span, ident } } (false, false) => { - errors::RedundantImportSub::DefinedHere { span, ident } + diagnostics::RedundantImportSub::DefinedHere { span, ident } } (true, true) => { - errors::RedundantImportSub::ImportedPrelude { span, ident } + diagnostics::RedundantImportSub::ImportedPrelude { span, ident } } (true, false) => { - errors::RedundantImportSub::DefinedPrelude { span, ident } + diagnostics::RedundantImportSub::DefinedPrelude { span, ident } } }) .collect(); - errors::RedundantImport { subs, ident }.into_diag(dcx, level) + diagnostics::RedundantImport { subs, ident }.into_diag(dcx, level) }, ); return true; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 42738118c271c..5ae2aaadffae7 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -42,7 +42,7 @@ use tracing::{debug, instrument, trace}; use crate::{ BindingError, BindingKey, Decl, DelegationFnSig, Finalize, IdentKey, LateDecl, LocalModule, Module, ModuleOrUniformRoot, ParentScope, PathResult, Res, ResolutionError, Resolver, Segment, - Stage, TyCtxt, UseError, Used, errors, path_names_to_string, rustdoc, with_owner, + Stage, TyCtxt, UseError, Used, path_names_to_string, rustdoc, with_owner, }; mod diagnostics; @@ -1916,7 +1916,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, node_id, lifetime.ident.span, - crate::errors::AssociatedConstElidedLifetime { + crate::diagnostics::AssociatedConstElidedLifetime { elided, code, span: lt_span, @@ -1934,7 +1934,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .. } = rib.kind { - Some(errors::ElidedAnonymousLifetimeReportErrorSuggestion { + Some(crate::diagnostics::ElidedAnonymousLifetimeReportErrorSuggestion { lo: span.shrink_to_lo(), hi: lifetime.ident.span.shrink_to_hi(), }) @@ -1956,10 +1956,12 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { trait_id, &["core", "iter", "traits", "iterator", "Iterator"], ) { - self.r.dcx().emit_err(errors::LendingIteratorReportError { - lifetime: lifetime.ident.span, - ty: ty.span, - }) + self.r.dcx().emit_err( + crate::diagnostics::LendingIteratorReportError { + lifetime: lifetime.ident.span, + ty: ty.span, + }, + ) } else { let decl = if !trait_id.is_local() && let Some(assoc) = self.diag_metadata.current_impl_item @@ -1983,7 +1985,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { DUMMY_SP.into() }; let mut err = self.r.dcx().create_err( - errors::AnonymousLifetimeNonGatReportError { + crate::diagnostics::AnonymousLifetimeNonGatReportError { lifetime: lifetime.ident.span, decl, }, @@ -1992,15 +1994,19 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { err.emit() } } else { - self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError { - span: lifetime.ident.span, - suggestion, - }) + self.r.dcx().emit_err( + crate::diagnostics::ElidedAnonymousLifetimeReportError { + span: lifetime.ident.span, + suggestion, + }, + ) } } else { - self.r.dcx().emit_err(errors::ExplicitAnonymousLifetimeReportError { - span: lifetime.ident.span, - }) + self.r.dcx().emit_err( + crate::diagnostics::ExplicitAnonymousLifetimeReportError { + span: lifetime.ident.span, + }, + ) }; self.record_lifetime_err(lifetime.id, guar); return; @@ -2284,11 +2290,12 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { !segment.has_generic_args, elided_lifetime_span, ); - let guar = - self.r.dcx().emit_err(errors::ImplicitElidedLifetimeNotAllowedHere { + let guar = self.r.dcx().emit_err( + crate::diagnostics::ImplicitElidedLifetimeNotAllowedHere { span: path_span, subdiag, - }); + }, + ); should_lint = false; for id in node_ids { @@ -2360,7 +2367,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .downcast_ref::() .expect("expected a `Session`") .source_map(); - errors::ElidedLifetimesInPaths { + crate::diagnostics::ElidedLifetimesInPaths { subdiag: elided_lifetime_in_path_suggestion( source_map, expected_lifetimes, @@ -2775,7 +2782,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let report_error = |this: &Self, ns| { if this.should_report_errs() { let what = if ns == TypeNS { "type parameters" } else { "local variables" }; - this.r.dcx().emit_err(errors::ImportsCannotReferTo { span: ident.span, what }); + this.r.dcx().emit_err(crate::diagnostics::ImportsCannotReferTo { + span: ident.span, + what, + }); } }; @@ -3165,7 +3175,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let guar = self .r .dcx() - .create_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }) + .create_err(crate::diagnostics::UnderscoreLifetimeIsReserved { + span: param.ident.span, + }) .emit_unless_delay(is_raw_underscore_lifetime); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_err(param.id, guar); @@ -3173,10 +3185,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } if param.ident.name == kw::StaticLifetime { - let guar = self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { - span: param.ident.span, - lifetime: param.ident, - }); + let guar = + self.r.dcx().emit_err(crate::diagnostics::StaticLifetimeIsReserved { + span: param.ident.span, + lifetime: param.ident, + }); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_err(param.id, guar); continue; @@ -4501,7 +4514,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { Visibility::Restricted(def_id), self.parent_scope.module, ) { - self.r.dcx().create_err(errors::RestrictionAncestorOnly(path.span)).emit(); + self.r + .dcx() + .create_err(crate::diagnostics::RestrictionAncestorOnly(path.span)) + .emit(); } } } @@ -5690,7 +5706,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { lint::builtin::UNUSED_LABELS, *id, *span, - errors::UnusedLabel, + crate::diagnostics::UnusedLabel, ); } }) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f089bb170404f..8994a7713c858 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -32,7 +32,7 @@ use thin_vec::ThinVec; use tracing::debug; use super::NoConstantGenericsReason; -use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; +use crate::error_helper::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::{ AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet, QSelf, RibKind, @@ -40,7 +40,7 @@ use crate::late::{ use crate::ty::fast_reject::SimplifiedType; use crate::{ Finalize, Module, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, Resolver, - ScopeSet, Segment, errors, path_names_to_string, + ScopeSet, Segment, diagnostics, path_names_to_string, }; /// A field or associated item from self type suggested in case of resolution failure. @@ -1594,7 +1594,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Side::Start => (segment.ident.span.between(range.span), " @ ".into()), Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)), }; - err.subdiagnostic(errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg { + err.subdiagnostic(diagnostics::UnexpectedResUseAtOpInSlicePatWithRangeSugg { span, ident: segment.ident, snippet, @@ -1758,7 +1758,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }) else { return; }; - err.subdiagnostic(errors::UnexpectedResChangeTyParamToConstParamSugg { + err.subdiagnostic(diagnostics::UnexpectedResChangeTyParamToConstParamSugg { before: span.shrink_to_lo(), after: span.shrink_to_hi(), }); @@ -1801,7 +1801,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }); if let Some(param) = param { - err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg { + err.subdiagnostic(diagnostics::UnexpectedResChangeTyToConstParamSugg { span: param.shrink_to_lo(), applicability, }); @@ -3437,7 +3437,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { format!("corresponding const parameter on the type defined here",), ); - err.subdiagnostic(errors::UnexpectedMissingConstParameter { + err.subdiagnostic(diagnostics::UnexpectedMissingConstParameter { span: insert_span, snippet, item_name: format!("{}", target_ident), @@ -3662,7 +3662,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else { Some(deletion_span) }; - Some(errors::SingleUseLifetimeSugg { + Some(diagnostics::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt, @@ -3670,7 +3670,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else { None }; - errors::SingleUseLifetime { + diagnostics::SingleUseLifetime { suggestion, param_span: param_ident.span, use_span, @@ -3690,7 +3690,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { lint::builtin::UNUSED_LIFETIMES, param.id, param.ident.span, - errors::UnusedLifetime { deletion_span, ident: param.ident }, + diagnostics::UnusedLifetime { deletion_span, ident: param.ident }, ); } } @@ -3907,7 +3907,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ) -> ErrorGuaranteed { self.r .dcx() - .create_err(errors::ParamInTyOfConstParam { + .create_err(diagnostics::ParamInTyOfConstParam { span: lifetime_ref.ident.span, name: lifetime_ref.ident.name, }) @@ -3926,20 +3926,20 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { NoConstantGenericsReason::IsEnumDiscriminant => self .r .dcx() - .create_err(errors::ParamInEnumDiscriminant { + .create_err(diagnostics::ParamInEnumDiscriminant { span: lifetime_ref.ident.span, name: lifetime_ref.ident.name, - param_kind: errors::ParamKindInEnumDiscriminant::Lifetime, + param_kind: diagnostics::ParamKindInEnumDiscriminant::Lifetime, }) .emit(), NoConstantGenericsReason::NonTrivialConstArg => { assert!(!self.r.tcx.features().generic_const_exprs()); self.r .dcx() - .create_err(errors::ParamInNonTrivialAnonConst { + .create_err(diagnostics::ParamInNonTrivialAnonConst { span: lifetime_ref.ident.span, name: lifetime_ref.ident.name, - param_kind: errors::ParamKindInNonTrivialAnonConst::Lifetime, + param_kind: diagnostics::ParamKindInNonTrivialAnonConst::Lifetime, help: self.r.tcx.sess.is_nightly_build(), is_gca: self.r.tcx.features().generic_const_args(), help_gca: self.r.tcx.features().generic_const_args(), diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 769466a112ff8..c7b4686fcd234 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -26,9 +26,9 @@ use std::ops::ControlFlow; use std::sync::Arc; use std::{fmt, mem}; -use diagnostics::{ImportSuggestion, LabelSuggestion, StructCtor, Suggestion}; +use diagnostics::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; use effective_visibilities::EffectiveVisibilitiesVisitor; -use errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use error_helper::{ImportSuggestion, LabelSuggestion, StructCtor, Suggestion}; use hygiene::Macros20NormalizedSyntaxContext; use imports::{Import, ImportData, ImportKind, NameResolution, PendingDecl}; use late::{ @@ -82,7 +82,7 @@ mod check_unused; mod def_collector; mod diagnostics; mod effective_visibilities; -mod errors; +mod error_helper; mod ident; mod imports; mod late; @@ -2273,7 +2273,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PRIVATE_MACRO_USE, import.root_id, ident.span, - errors::MacroIsPrivate { ident }, + diagnostics::MacroIsPrivate { ident }, ); } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 2a5c2e6c2df83..fade23f9cf308 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -35,7 +35,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use crate::Namespace::*; use crate::def_collector::collect_definitions; -use crate::errors::{ +use crate::diagnostics::{ self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive, }; @@ -138,7 +138,7 @@ pub fn registered_tools_ast( { for tool in tools { if let Some(old_tool) = registered_tools.replace(tool) { - dcx.emit_err(errors::ToolWasAlreadyRegistered { + dcx.emit_err(diagnostics::ToolWasAlreadyRegistered { span: tool.span, tool, old_ident_span: old_tool.span, @@ -348,7 +348,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { UNUSED_MACROS, node_id, ident.span, - errors::UnusedMacroDefinition { name: ident.name }, + diagnostics::UnusedMacroDefinition { name: ident.name }, ); // Do not report unused individual rules if the entire macro is unused self.unused_macro_rules.swap_remove(&node_id); @@ -368,7 +368,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { UNUSED_MACRO_RULES, node_id, rule_span, - errors::MacroRuleNeverUsed { n: arm_i + 1, name: ident.name }, + diagnostics::MacroRuleNeverUsed { n: arm_i + 1, name: ident.name }, ); } } @@ -621,15 +621,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Report errors for the resolved macro. for (idx, segment) in path.segments.iter().enumerate() { if let Some(args) = &segment.args { - self.dcx().emit_err(errors::GenericArgumentsInMacroPath { span: args.span() }); + self.dcx().emit_err(diagnostics::GenericArgumentsInMacroPath { span: args.span() }); } if kind == MacroKind::Attr && segment.ident.as_str().starts_with("rustc") { if idx == 0 { - self.dcx().emit_err(errors::AttributesStartingWithRustcAreReserved { + self.dcx().emit_err(diagnostics::AttributesStartingWithRustcAreReserved { span: segment.ident.span, }); } else { - self.dcx().emit_err(errors::AttributesContainingRustcAreReserved { + self.dcx().emit_err(diagnostics::AttributesContainingRustcAreReserved { span: segment.ident.span, }); } @@ -641,7 +641,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(def_id) = def_id.as_local() { self.unused_macros.swap_remove(&def_id); if self.proc_macro_stubs.contains(&def_id) { - self.dcx().emit_err(errors::ProcMacroSameCrate { + self.dcx().emit_err(diagnostics::ProcMacroSameCrate { span: path.span, is_test: self.tcx.sess.is_test_crate(), }); @@ -740,9 +740,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(feature) = *feature && *attr == name { - break 'help Some(errors::UnknownDiagnosticAttributeHelp::UseFeature { - feature, - }); + break 'help Some( + diagnostics::UnknownDiagnosticAttributeHelp::UseFeature { feature }, + ); } } } @@ -755,7 +755,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect::>(); find_best_match_for_name(&candidates, name, None).map(|typo_name| { - errors::UnknownDiagnosticAttributeHelp::Typo { span, typo_name } + diagnostics::UnknownDiagnosticAttributeHelp::Typo { span, typo_name } }) }; @@ -763,7 +763,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { UNKNOWN_DIAGNOSTIC_ATTRIBUTES, span, node_id, - errors::UnknownDiagnosticAttribute { help }, + diagnostics::UnknownDiagnosticAttribute { help }, ); } @@ -1061,7 +1061,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { LEGACY_DERIVE_HELPERS, node_id, ident.span, - errors::LegacyDeriveHelpers { span: binding.span }, + diagnostics::LegacyDeriveHelpers { span: binding.span }, ); } } @@ -1146,7 +1146,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { if let Some(Res::NonMacroAttr(kind)) = res { if kind != NonMacroAttrKind::Tool && decl.is_none_or(|b| b.is_import()) { - self.dcx().emit_err(errors::CannotUseThroughAnImport { + self.dcx().emit_err(diagnostics::CannotUseThroughAnImport { span, article: kind.article(), descr: kind.descr(), @@ -1206,7 +1206,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { OUT_OF_SCOPE_MACRO_CALLS, path.span, node_id, - errors::OutOfScopeMacroCalls { + diagnostics::OutOfScopeMacroCalls { span: path.span, path: pprust::path_to_string(path), location, @@ -1222,7 +1222,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if name == sym::cfg || name == sym::cfg_attr { let macro_kinds = res.macro_kinds(); if macro_kinds.is_some() && sub_namespace_match(macro_kinds, Some(MacroKind::Attr)) { - self.dcx().emit_err(errors::NameReservedInAttributeNamespace { span, ident: name }); + self.dcx() + .emit_err(diagnostics::NameReservedInAttributeNamespace { span, ident: name }); } } } @@ -1257,7 +1258,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // while still taking everything else from the source code. ext.kind = builtin_ext_kind.clone(); } else { - self.dcx().emit_err(errors::CannotFindBuiltinMacroWithName { span, ident }); + self.dcx().emit_err(diagnostics::CannotFindBuiltinMacroWithName { span, ident }); } } @@ -1284,7 +1285,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PathResult::NonModule(..) | // HACK(Urgau): This shouldn't be necessary PathResult::Failed { is_error_from_last_segment: false, .. } => { - self.dcx().emit_err(errors::CfgAccessibleUnsure { span }); + self.dcx().emit_err(diagnostics::CfgAccessibleUnsure { span }); // If we get a partially resolved NonModule in one namespace, we should get the // same result in any other namespaces, so we can return early.