1// ignore-tidy-filelength2use std::mem;3use std::ops::ControlFlow;45use itertools::Itertools as _;6use rustc_ast::visit::{self, Visitor};7use rustc_ast::{8 self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path,9 join_path_idents,10};11use rustc_ast_pretty::pprust;12use rustc_data_structures::fx::{FxHashMap, FxHashSet};13use rustc_data_structures::unord::{UnordMap, UnordSet};14use rustc_errors::codes::*;15use rustc_errors::{16 Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle,17 struct_span_code_err,18};19use rustc_feature::BUILTIN_ATTRIBUTES;20use rustc_hir::attrs::{CfgEntry, StrippedCfgItem};21use rustc_hir::def::Namespace::{self, *};22use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS};23use rustc_hir::def_id::{CRATE_DEF_ID, DefId};24use rustc_hir::{PrimTy, Stability, StabilityLevel, find_attr};25use rustc_middle::bug;26use rustc_middle::ty::{TyCtxt, Visibility};27use rustc_session::Session;28use rustc_session::lint::builtin::{29 ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES,30 AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,31};32use rustc_session::utils::was_invoked_from_cargo;33use rustc_span::edit_distance::find_best_match_for_name;34use rustc_span::edition::Edition;35use rustc_span::hygiene::MacroKind;36use rustc_span::source_map::SourceMap;37use rustc_span::{38 BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym,39};40use thin_vec::{ThinVec, thin_vec};41use tracing::{debug, instrument};4243use crate::errors::{44 self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive,45 ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition,46 MaybeMissingMacroRulesName,47};48use crate::hygiene::Macros20NormalizedSyntaxContext;49use crate::imports::{Import, ImportKind};50use crate::late::{DiagMetadata, PatternSource, Rib};51use crate::{52 AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind,53 DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey,54 LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult,55 PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used,56 VisResolutionError, errors as errs, path_names_to_string,57};5859/// A vector of spans and replacements, a message and applicability.60pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability);6162/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a63/// similarly named label and whether or not it is reachable.64pub(crate) type LabelSuggestion = (Ident, bool);6566#[derive(Clone)]67pub(crate) struct StructCtor {68 pub res: Res,69 pub vis: Visibility<DefId>,70 pub field_visibilities: Vec<Visibility<DefId>>,71}7273impl StructCtor {74 pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool {75 self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m))76 }77}7879#[derive(Debug)]80pub(crate) enum SuggestionTarget {81 /// The target has a similar name as the name used by the programmer (probably a typo)82 SimilarlyNamed,83 /// The target is the only valid item that can be used in the corresponding context84 SingleItem,85}8687#[derive(Debug)]88pub(crate) struct TypoSuggestion {89 pub candidate: Symbol,90 /// The source location where the name is defined; None if the name is not defined91 /// in source e.g. primitives92 pub span: Option<Span>,93 pub res: Res,94 pub target: SuggestionTarget,95}9697impl TypoSuggestion {98 pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion {99 Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed }100 }101 pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion {102 Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed }103 }104 pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion {105 Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem }106 }107}108109/// A free importable items suggested in case of resolution failure.110#[derive(Debug, Clone)]111pub(crate) struct ImportSuggestion {112 pub did: Option<DefId>,113 pub descr: &'static str,114 pub path: Path,115 pub accessible: bool,116 // false if the path traverses a foreign `#[doc(hidden)]` item.117 pub doc_visible: bool,118 pub via_import: bool,119 /// An extra note that should be issued if this item is suggested120 pub note: Option<String>,121 pub is_stable: bool,122}123124/// Adjust the impl span so that just the `impl` keyword is taken by removing125/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and126/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`).127///128/// *Attention*: the method used is very fragile since it essentially duplicates the work of the129/// parser. If you need to use this function or something similar, please consider updating the130/// `source_map` functions and this function to something more robust.131fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span {132 let impl_span = sm.span_until_char(impl_span, '<');133 sm.span_until_whitespace(impl_span)134}135136impl<'ra, 'tcx> Resolver<'ra, 'tcx> {137 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> {138 self.tcx.dcx()139 }140141 pub(crate) fn report_errors(&mut self, krate: &Crate) {142 self.report_delayed_vis_resolution_errors();143 self.report_with_use_injections(krate);144145 for &(span_use, span_def) in &self.macro_expanded_macro_export_errors {146 self.lint_buffer.buffer_lint(147 MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,148 CRATE_NODE_ID,149 span_use,150 errors::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def },151 );152 }153154 for ambiguity_error in &self.ambiguity_errors {155 let mut diag = self.ambiguity_diagnostic(ambiguity_error);156157 if let Some(ambiguity_warning) = ambiguity_error.warning {158 let node_id = match ambiguity_error.b1.0.kind {159 DeclKind::Import { import, .. } => import.root_id,160 DeclKind::Def(_) => CRATE_NODE_ID,161 };162163 let lint = match ambiguity_warning {164 _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES,165 AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS,166 AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS,167 };168169 self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag);170 } else {171 diag.is_error = true;172 self.dcx().emit_err(diag);173 }174 }175176 let mut reported_spans = FxHashSet::default();177 for error in mem::take(&mut self.privacy_errors) {178 if reported_spans.insert(error.dedup_span) {179 self.report_privacy_error(&error);180 }181 }182 }183184 fn report_delayed_vis_resolution_errors(&mut self) {185 for DelayedVisResolutionError { vis, parent_scope, error } in186 mem::take(&mut self.delayed_vis_resolution_errors)187 {188 match self.try_resolve_visibility(&parent_scope, &vis, true) {189 Ok(_) => self.report_vis_error(error),190 Err(error) => self.report_vis_error(error),191 };192 }193 }194195 fn report_with_use_injections(&mut self, krate: &Crate) {196 for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in197 mem::take(&mut self.use_injections)198 {199 let (span, found_use) = if node_id != DUMMY_NODE_ID {200 UsePlacementFinder::check(krate, node_id)201 } else {202 (None, FoundUse::No)203 };204205 if !candidates.is_empty() {206 show_candidates(207 self.tcx,208 &mut err,209 span,210 &candidates,211 if instead { Instead::Yes } else { Instead::No },212 found_use,213 DiagMode::Normal,214 path,215 "",216 );217 err.emit();218 } else if let Some((span, msg, sugg, appl)) = suggestion {219 err.span_suggestion_verbose(span, msg, sugg, appl);220 err.emit();221 } else if let [segment] = path.as_slice()222 && is_call223 {224 err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod);225 } else {226 err.emit();227 }228 }229 }230231 pub(crate) fn report_conflict(232 &mut self,233 ident: IdentKey,234 ns: Namespace,235 old_binding: Decl<'ra>,236 new_binding: Decl<'ra>,237 ) {238 // Error on the second of two conflicting names239 if old_binding.span.lo() > new_binding.span.lo() {240 return self.report_conflict(ident, ns, new_binding, old_binding);241 }242243 let container = match old_binding.parent_module.unwrap().kind {244 // Avoid using TyCtxt::def_kind_descr in the resolver, because it245 // indirectly *calls* the resolver, and would cause a query cycle.246 ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id),247 ModuleKind::Block => "block",248 };249250 let (name, span) =251 (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span));252253 if self.name_already_seen.get(&name) == Some(&span) {254 return;255 }256257 let old_kind = match (ns, old_binding.res()) {258 (ValueNS, _) => "value",259 (MacroNS, _) => "macro",260 (TypeNS, _) if old_binding.is_extern_crate() => "extern crate",261 (TypeNS, Res::Def(DefKind::Mod, _)) => "module",262 (TypeNS, Res::Def(DefKind::Trait, _)) => "trait",263 (TypeNS, _) => "type",264 };265266 let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) {267 (true, true) => E0259,268 (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() {269 true => E0254,270 false => E0260,271 },272 _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) {273 (false, false) => E0428,274 (true, true) => E0252,275 _ => E0255,276 },277 };278279 let label = match new_binding.is_import_user_facing() {280 true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name },281 false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name },282 };283284 let old_binding_label =285 (!old_binding.span.is_dummy() && old_binding.span != span).then(|| {286 let span = self.tcx.sess.source_map().guess_head_span(old_binding.span);287 match old_binding.is_import_user_facing() {288 true => errors::NameDefinedMultipleTimeOldBindingLabel::Import {289 span,290 old_kind,291 name,292 },293 false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition {294 span,295 old_kind,296 name,297 },298 }299 });300301 let mut err = self302 .dcx()303 .create_err(errors::NameDefinedMultipleTime {304 span,305 name,306 descr: ns.descr(),307 container,308 label,309 old_binding_label,310 })311 .with_code(code);312313 // See https://github.com/rust-lang/rust/issues/32354314 use DeclKind::Import;315 let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| {316 !binding.span.is_dummy()317 && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport)318 };319 let import = match (&new_binding.kind, &old_binding.kind) {320 // If there are two imports where one or both have attributes then prefer removing the321 // import without attributes.322 (Import { import: new, .. }, Import { import: old, .. })323 if {324 (new.has_attributes || old.has_attributes)325 && can_suggest(old_binding, *old)326 && can_suggest(new_binding, *new)327 } =>328 {329 if old.has_attributes {330 Some((*new, new_binding.span, true))331 } else {332 Some((*old, old_binding.span, true))333 }334 }335 // Otherwise prioritize the new binding.336 (Import { import, .. }, other) if can_suggest(new_binding, *import) => {337 Some((*import, new_binding.span, other.is_import()))338 }339 (other, Import { import, .. }) if can_suggest(old_binding, *import) => {340 Some((*import, old_binding.span, other.is_import()))341 }342 _ => None,343 };344345 // Check if the target of the use for both bindings is the same.346 let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id();347 let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy();348 let from_item =349 self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item());350 // Only suggest removing an import if both bindings are to the same def, if both spans351 // aren't dummy spans. Further, if both bindings are imports, then the ident must have352 // been introduced by an item.353 let should_remove_import = duplicate354 && !has_dummy_span355 && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item);356357 match import {358 Some((import, span, true)) if should_remove_import && import.is_nested() => {359 self.add_suggestion_for_duplicate_nested_use(&mut err, import, span);360 }361 Some((import, _, true)) if should_remove_import && !import.is_glob() => {362 // Simple case - remove the entire import. Due to the above match arm, this can363 // only be a single use so just remove it entirely.364 err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport {365 span: import.use_span_with_attributes,366 });367 }368 Some((import, span, _)) => {369 self.add_suggestion_for_rename_of_use(&mut err, name, import, span);370 }371 _ => {}372 }373374 err.emit();375 self.name_already_seen.insert(name, span);376 }377378 /// This function adds a suggestion to change the binding name of a new import that conflicts379 /// with an existing import.380 ///381 /// ```text,ignore (diagnostic)382 /// help: you can use `as` to change the binding name of the import383 /// |384 /// LL | use foo::bar as other_bar;385 /// | ^^^^^^^^^^^^^^^^^^^^^386 /// ```387 fn add_suggestion_for_rename_of_use(388 &self,389 err: &mut Diag<'_>,390 name: Symbol,391 import: Import<'_>,392 binding_span: Span,393 ) {394 let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {395 format!("Other{name}")396 } else {397 format!("other_{name}")398 };399400 let mut suggestion = None;401 let mut span = binding_span;402 match import.kind {403 ImportKind::Single { source, .. } => {404 if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0)405 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span)406 && pos as usize <= snippet.len()407 {408 span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi(409 binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }),410 );411 suggestion = Some(format!(" as {suggested_name}"));412 }413 }414 ImportKind::ExternCrate { source, target, .. } => {415 suggestion = Some(format!(416 "extern crate {} as {};",417 source.unwrap_or(target.name),418 suggested_name,419 ))420 }421 _ => unreachable!(),422 }423424 if let Some(suggestion) = suggestion {425 err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion });426 } else {427 err.subdiagnostic(ChangeImportBinding { span });428 }429 }430431 /// This function adds a suggestion to remove an unnecessary binding from an import that is432 /// nested. In the following example, this function will be invoked to remove the `a` binding433 /// in the second use statement:434 ///435 /// ```ignore (diagnostic)436 /// use issue_52891::a;437 /// use issue_52891::{d, a, e};438 /// ```439 ///440 /// The following suggestion will be added:441 ///442 /// ```ignore (diagnostic)443 /// use issue_52891::{d, a, e};444 /// ^-- help: remove unnecessary import445 /// ```446 ///447 /// If the nested use contains only one import then the suggestion will remove the entire448 /// line.449 ///450 /// It is expected that the provided import is nested - this isn't checked by the451 /// function. If this invariant is not upheld, this function's behaviour will be unexpected452 /// as characters expected by span manipulations won't be present.453 fn add_suggestion_for_duplicate_nested_use(454 &self,455 err: &mut Diag<'_>,456 import: Import<'_>,457 binding_span: Span,458 ) {459 assert!(import.is_nested());460461 // Two examples will be used to illustrate the span manipulations we're doing:462 //463 // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is464 // `a` and `import.use_span` is `issue_52891::{d, a, e};`.465 // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is466 // `a` and `import.use_span` is `issue_52891::{d, e, a};`.467468 let (found_closing_brace, span) =469 find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span);470471 // If there was a closing brace then identify the span to remove any trailing commas from472 // previous imports.473 if found_closing_brace {474 if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) {475 err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport { span });476 } else {477 // Remove the entire line if we cannot extend the span back, this indicates an478 // `issue_52891::{self}` case.479 err.subdiagnostic(errors::RemoveUnnecessaryImport {480 span: import.use_span_with_attributes,481 });482 }483484 return;485 }486487 err.subdiagnostic(errors::RemoveUnnecessaryImport { span });488 }489490 pub(crate) fn lint_if_path_starts_with_module(491 &mut self,492 finalize: Finalize,493 path: &[Segment],494 second_binding: Option<Decl<'_>>,495 ) {496 let Finalize { node_id, root_span, .. } = finalize;497498 let first_name = match path.get(0) {499 // In the 2018 edition this lint is a hard error, so nothing to do500 Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => {501 seg.ident.name502 }503 _ => return,504 };505506 // We're only interested in `use` paths which should start with507 // `{{root}}` currently.508 if first_name != kw::PathRoot {509 return;510 }511512 match path.get(1) {513 // If this import looks like `crate::...` it's already good514 Some(Segment { ident, .. }) if ident.name == kw::Crate => return,515 // Otherwise go below to see if it's an extern crate516 Some(_) => {}517 // If the path has length one (and it's `PathRoot` most likely)518 // then we don't know whether we're gonna be importing a crate or an519 // item in our crate. Defer this lint to elsewhere520 None => return,521 }522523 // If the first element of our path was actually resolved to an524 // `ExternCrate` (also used for `crate::...`) then no need to issue a525 // warning, this looks all good!526 if let Some(binding) = second_binding527 && let DeclKind::Import { import, .. } = binding.kind528 // Careful: we still want to rewrite paths from renamed extern crates.529 && let ImportKind::ExternCrate { source: None, .. } = import.kind530 {531 return;532 }533534 self.lint_buffer.dyn_buffer_lint_any(535 ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,536 node_id,537 root_span,538 move |dcx, level, sess| {539 let (replacement, applicability) = match sess540 .downcast_ref::<Session>()541 .expect("expected a `Session`")542 .source_map()543 .span_to_snippet(root_span)544 {545 Ok(ref s) => {546 // FIXME(Manishearth) ideally the emitting code547 // can tell us whether or not this is global548 let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };549550 (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)551 }552 Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),553 };554 errors::AbsPathWithModule {555 sugg: errors::AbsPathWithModuleSugg {556 span: root_span,557 applicability,558 replacement,559 },560 }561 .into_diag(dcx, level)562 },563 );564 }565566 pub(crate) fn add_module_candidates(567 &self,568 module: Module<'ra>,569 names: &mut Vec<TypoSuggestion>,570 filter_fn: &impl Fn(Res) -> bool,571 ctxt: Option<SyntaxContext>,572 ) {573 module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| {574 let res = binding.res();575 if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) {576 names.push(TypoSuggestion::new(ident.name, orig_ident_span, res));577 }578 });579 }580581 /// Combines an error with provided span and emits it.582 ///583 /// This takes the error provided, combines it with the span and any additional spans inside the584 /// error and emits it.585 pub(crate) fn report_error(586 &mut self,587 span: Span,588 resolution_error: ResolutionError<'ra>,589 ) -> ErrorGuaranteed {590 self.into_struct_error(span, resolution_error).emit()591 }592593 pub(crate) fn into_struct_error(594 &mut self,595 span: Span,596 resolution_error: ResolutionError<'ra>,597 ) -> Diag<'_> {598 match resolution_error {599 ResolutionError::GenericParamsFromOuterItem {600 outer_res,601 has_generic_params,602 def_kind,603 inner_item,604 current_self_ty,605 } => {606 use errs::GenericParamsFromOuterItemLabel as Label;607 let static_or_const = match def_kind {608 DefKind::Static { .. } => {609 Some(errs::GenericParamsFromOuterItemStaticOrConst::Static)610 }611 DefKind::Const { .. } => {612 Some(errs::GenericParamsFromOuterItemStaticOrConst::Const)613 }614 _ => None,615 };616 let is_self =617 matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. });618 let mut err = errs::GenericParamsFromOuterItem {619 span,620 label: None,621 refer_to_type_directly: None,622 use_let: None,623 sugg: None,624 static_or_const,625 is_self,626 item: inner_item.as_ref().map(|(label_span, _, kind)| {627 errs::GenericParamsFromOuterItemInnerItem {628 span: *label_span,629 descr: kind.descr().to_string(),630 is_self,631 }632 }),633 };634635 let sm = self.tcx.sess.source_map();636 // Note: do not early return for missing def_id here,637 // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`.638 let def_id = match outer_res {639 Res::SelfTyParam { .. } => {640 err.label = Some(Label::SelfTyParam(span));641 None642 }643 Res::SelfTyAlias { alias_to: def_id, .. } => {644 err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword(645 sm,646 self.def_span(def_id),647 )));648 err.refer_to_type_directly =649 current_self_ty.map(|snippet| errs::UseTypeDirectly { span, snippet });650 None651 }652 Res::Def(DefKind::TyParam, def_id) => {653 err.label = Some(Label::TyParam(self.def_span(def_id)));654 Some(def_id)655 }656 Res::Def(DefKind::ConstParam, def_id) => {657 err.label = Some(Label::ConstParam(self.def_span(def_id)));658 Some(def_id)659 }660 _ => {661 bug!(662 "GenericParamsFromOuterItem should only be used with \663 Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \664 DefKind::ConstParam"665 );666 }667 };668669 if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() {670 err.use_let = Some(errs::GenericParamsFromOuterItemUseLet {671 span: sm.span_until_whitespace(*item_span),672 });673 }674675 if let Some(def_id) = def_id676 && let HasGenericParams::Yes(span) = has_generic_params677 && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..))))678 {679 let name = self.tcx.item_name(def_id);680 let (span, snippet) = if span.is_empty() {681 let snippet = format!("<{name}>");682 (span, snippet)683 } else {684 let span = sm.span_through_char(span, '<').shrink_to_hi();685 let snippet = format!("{name}, ");686 (span, snippet)687 };688 err.sugg = Some(errs::GenericParamsFromOuterItemSugg { span, snippet });689 }690691 self.dcx().create_err(err)692 }693 ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => self694 .dcx()695 .create_err(errs::NameAlreadyUsedInParameterList { span, first_use_span, name }),696 ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {697 self.dcx().create_err(errs::MethodNotMemberOfTrait {698 span,699 method,700 trait_,701 sub: candidate.map(|c| errs::AssociatedFnWithSimilarNameExists {702 span: method.span,703 candidate: c,704 }),705 })706 }707 ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {708 self.dcx().create_err(errs::TypeNotMemberOfTrait {709 span,710 type_,711 trait_,712 sub: candidate.map(|c| errs::AssociatedTypeWithSimilarNameExists {713 span: type_.span,714 candidate: c,715 }),716 })717 }718 ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {719 self.dcx().create_err(errs::ConstNotMemberOfTrait {720 span,721 const_,722 trait_,723 sub: candidate.map(|c| errs::AssociatedConstWithSimilarNameExists {724 span: const_.span,725 candidate: c,726 }),727 })728 }729 ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {730 let BindingError { name, target, origin, could_be_path } = binding_error;731732 let mut target_sp = target.iter().map(|pat| pat.span).collect::<Vec<_>>();733 target_sp.sort();734 target_sp.dedup();735 let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::<Vec<_>>();736 origin_sp.sort();737 origin_sp.dedup();738739 let msp = MultiSpan::from_spans(target_sp.clone());740 let mut err = self741 .dcx()742 .create_err(errors::VariableIsNotBoundInAllPatterns { multispan: msp, name });743 for sp in target_sp {744 err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name });745 }746 for sp in &origin_sp {747 err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp });748 }749 let mut suggested_typo = false;750 if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..)))751 && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..)))752 {753 // The check above is so that when we encounter `match foo { (a | b) => {} }`,754 // we don't suggest `(a | a) => {}`, which would never be what the user wants.755 let mut target_visitor = BindingVisitor::default();756 for pat in &target {757 target_visitor.visit_pat(pat);758 }759 target_visitor.identifiers.sort();760 target_visitor.identifiers.dedup();761 let mut origin_visitor = BindingVisitor::default();762 for (_, pat) in &origin {763 origin_visitor.visit_pat(pat);764 }765 origin_visitor.identifiers.sort();766 origin_visitor.identifiers.dedup();767 // Find if the binding could have been a typo768 if let Some(typo) =769 find_best_match_for_name(&target_visitor.identifiers, name.name, None)770 && !origin_visitor.identifiers.contains(&typo)771 {772 err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo });773 suggested_typo = true;774 }775 }776 if could_be_path {777 let import_suggestions = self.lookup_import_candidates(778 name,779 Namespace::ValueNS,780 &parent_scope,781 &|res: Res| {782 matches!(783 res,784 Res::Def(785 DefKind::Ctor(CtorOf::Variant, CtorKind::Const)786 | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)787 | DefKind::Const { .. }788 | DefKind::AssocConst { .. },789 _,790 )791 )792 },793 );794795 if import_suggestions.is_empty() && !suggested_typo {796 let kind_matches: [fn(DefKind) -> bool; 4] = [797 |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)),798 |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)),799 |kind| matches!(kind, DefKind::Const { .. }),800 |kind| matches!(kind, DefKind::AssocConst { .. }),801 ];802 let mut local_names = vec![];803 self.add_module_candidates(804 parent_scope.module,805 &mut local_names,806 &|res| matches!(res, Res::Def(_, _)),807 None,808 );809 let local_names: FxHashSet<_> = local_names810 .into_iter()811 .filter_map(|s| match s.res {812 Res::Def(_, def_id) => Some(def_id),813 _ => None,814 })815 .collect();816817 let mut local_suggestions = vec![];818 let mut suggestions = vec![];819 for matches_kind in kind_matches {820 if let Some(suggestion) = self.early_lookup_typo_candidate(821 ScopeSet::All(Namespace::ValueNS),822 &parent_scope,823 name,824 &|res: Res| match res {825 Res::Def(k, _) => matches_kind(k),826 _ => false,827 },828 ) && let Res::Def(kind, mut def_id) = suggestion.res829 {830 if let DefKind::Ctor(_, _) = kind {831 def_id = self.tcx.parent(def_id);832 }833 let kind = kind.descr(def_id);834 if local_names.contains(&def_id) {835 // The item is available in the current scope. Very likely to836 // be a typo. Don't use the full path.837 local_suggestions.push((838 suggestion.candidate,839 suggestion.candidate.to_string(),840 kind,841 ));842 } else {843 suggestions.push((844 suggestion.candidate,845 self.def_path_str(def_id),846 kind,847 ));848 }849 }850 }851 let suggestions = if !local_suggestions.is_empty() {852 // There is at least one item available in the current scope that is a853 // likely typo. We only show those.854 local_suggestions855 } else {856 suggestions857 };858 for (name, sugg, kind) in suggestions {859 err.span_suggestion_verbose(860 span,861 format!(862 "you might have meant to use the similarly named {kind} `{name}`",863 ),864 sugg,865 Applicability::MaybeIncorrect,866 );867 suggested_typo = true;868 }869 }870 if import_suggestions.is_empty() && !suggested_typo {871 let help_msg = format!(872 "if you meant to match on a unit struct, unit variant or a `const` \873 item, consider making the path in the pattern qualified: \874 `path::to::ModOrType::{name}`",875 );876 err.span_help(span, help_msg);877 }878 show_candidates(879 self.tcx,880 &mut err,881 Some(span),882 &import_suggestions,883 Instead::No,884 FoundUse::Yes,885 DiagMode::Pattern,886 vec![],887 "",888 );889 }890 err891 }892 ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => {893 self.dcx().create_err(errs::VariableBoundWithDifferentMode {894 span,895 first_binding_span,896 variable_name,897 })898 }899 ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => self900 .dcx()901 .create_err(errs::IdentifierBoundMoreThanOnceInParameterList { span, identifier }),902 ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => self903 .dcx()904 .create_err(errs::IdentifierBoundMoreThanOnceInSamePattern { span, identifier }),905 ResolutionError::UndeclaredLabel { name, suggestion } => {906 let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion907 {908 // A reachable label with a similar name exists.909 Some((ident, true)) => (910 (911 Some(errs::LabelWithSimilarNameReachable(ident.span)),912 Some(errs::TryUsingSimilarlyNamedLabel {913 span,914 ident_name: ident.name,915 }),916 ),917 None,918 ),919 // An unreachable label with a similar name exists.920 Some((ident, false)) => (921 (None, None),922 Some(errs::UnreachableLabelWithSimilarNameExists {923 ident_span: ident.span,924 }),925 ),926 // No similarly-named labels exist.927 None => ((None, None), None),928 };929 self.dcx().create_err(errs::UndeclaredLabel {930 span,931 name,932 sub_reachable,933 sub_reachable_suggestion,934 sub_unreachable,935 })936 }937 ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => {938 let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}");939 err.span_label(span, label);940941 if let Some((suggestions, msg, applicability)) = suggestion {942 if suggestions.is_empty() {943 err.help(msg);944 return err;945 }946 err.multipart_suggestion(msg, suggestions, applicability);947 }948949 let module = match module {950 Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id,951 _ => CRATE_DEF_ID.to_def_id(),952 };953 self.find_cfg_stripped(&mut err, &segment, module);954955 err956 }957 ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {958 self.dcx().create_err(errs::CannotCaptureDynamicEnvironmentInFnItem { span })959 }960 ResolutionError::AttemptToUseNonConstantValueInConstant {961 ident,962 suggestion,963 current,964 type_span,965 } => {966 // let foo =...967 // ^^^ given this Span968 // ------- get this Span to have an applicable suggestion969970 // edit:971 // only do this if the const and usage of the non-constant value are on the same line972 // the further the two are apart, the higher the chance of the suggestion being wrong973974 let sp = self975 .tcx976 .sess977 .source_map()978 .span_extend_to_prev_str(ident.span, current, true, false);979980 let (with, with_label, without) = match sp {981 Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => {982 let sp = sp983 .with_lo(BytePos(sp.lo().0 - (current.len() as u32)))984 .until(ident.span);985986 // Only suggest replacing the binding keyword if this is a simple987 // binding.988 //989 // Note: this approach still incorrectly suggests for irrefutable990 // patterns like `if let x = 1 { const { x } }`, since the text991 // between `let` and the identifier is just whitespace.992 // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs993 let is_simple_binding =994 self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| {995 let after_keyword = snippet[current.len()..].trim();996 after_keyword.is_empty() || after_keyword == "mut"997 });998999 if is_simple_binding {1000 (1001 Some(errs::AttemptToUseNonConstantValueInConstantWithSuggestion {1002 span: sp,1003 suggestion,1004 current,1005 type_span,1006 }),1007 Some(errs::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }),1008 None,1009 )1010 } else {1011 (1012 None,1013 Some(errs::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }),1014 None,1015 )1016 }1017 }1018 _ => (1019 None,1020 None,1021 Some(errs::AttemptToUseNonConstantValueInConstantWithoutSuggestion {1022 ident_span: ident.span,1023 suggestion,1024 }),1025 ),1026 };10271028 self.dcx().create_err(errs::AttemptToUseNonConstantValueInConstant {1029 span,1030 with,1031 with_label,1032 without,1033 })1034 }1035 ResolutionError::BindingShadowsSomethingUnacceptable {1036 shadowing_binding,1037 name,1038 participle,1039 article,1040 shadowed_binding,1041 shadowed_binding_span,1042 } => self.dcx().create_err(errs::BindingShadowsSomethingUnacceptable {1043 span,1044 shadowing_binding,1045 shadowed_binding,1046 article,1047 sub_suggestion: match (shadowing_binding, shadowed_binding) {1048 (1049 PatternSource::Match,1050 Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _),1051 ) => Some(errs::BindingShadowsSomethingUnacceptableSuggestion { span, name }),1052 _ => None,1053 },1054 shadowed_binding_span,1055 participle,1056 name,1057 }),1058 ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason {1059 ForwardGenericParamBanReason::Default => {1060 self.dcx().create_err(errs::ForwardDeclaredGenericParam { param, span })1061 }1062 ForwardGenericParamBanReason::ConstParamTy => self1063 .dcx()1064 .create_err(errs::ForwardDeclaredGenericInConstParamTy { param, span }),1065 },1066 ResolutionError::ParamInTyOfConstParam { name } => {1067 self.dcx().create_err(errs::ParamInTyOfConstParam { span, name })1068 }1069 ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => {1070 self.dcx().create_err(errs::ParamInNonTrivialAnonConst {1071 span,1072 name,1073 param_kind: is_type,1074 help: self.tcx.sess.is_nightly_build(),1075 is_gca,1076 help_gca: is_gca,1077 })1078 }1079 ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => self1080 .dcx()1081 .create_err(errs::ParamInEnumDiscriminant { span, name, param_kind: is_type }),1082 ResolutionError::ForwardDeclaredSelf(reason) => match reason {1083 ForwardGenericParamBanReason::Default => {1084 self.dcx().create_err(errs::SelfInGenericParamDefault { span })1085 }1086 ForwardGenericParamBanReason::ConstParamTy => {1087 self.dcx().create_err(errs::SelfInConstGenericTy { span })1088 }1089 },1090 ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {1091 let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) =1092 match suggestion {1093 // A reachable label with a similar name exists.1094 Some((ident, true)) => (1095 (1096 Some(errs::UnreachableLabelSubLabel { ident_span: ident.span }),1097 Some(errs::UnreachableLabelSubSuggestion {1098 span,1099 // intentionally taking 'ident.name' instead of 'ident' itself, as this1100 // could be used in suggestion context1101 ident_name: ident.name,1102 }),1103 ),1104 None,1105 ),1106 // An unreachable label with a similar name exists.1107 Some((ident, false)) => (1108 (None, None),1109 Some(errs::UnreachableLabelSubLabelUnreachable {1110 ident_span: ident.span,1111 }),1112 ),1113 // No similarly-named labels exist.1114 None => ((None, None), None),1115 };1116 self.dcx().create_err(errs::UnreachableLabel {1117 span,1118 name,1119 definition_span,1120 sub_suggestion,1121 sub_suggestion_label,1122 sub_unreachable_label,1123 })1124 }1125 ResolutionError::TraitImplMismatch {1126 name,1127 kind,1128 code,1129 trait_item_span,1130 trait_path,1131 } => self1132 .dcx()1133 .create_err(errors::TraitImplMismatch {1134 span,1135 name,1136 kind,1137 trait_path,1138 trait_item_span,1139 })1140 .with_code(code),1141 ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self1142 .dcx()1143 .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }),1144 ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }),1145 ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }),1146 ResolutionError::BindingInNeverPattern => {1147 self.dcx().create_err(errs::BindingInNeverPattern { span })1148 }1149 }1150 }11511152 pub(crate) fn report_vis_error(1153 &mut self,1154 vis_resolution_error: VisResolutionError,1155 ) -> ErrorGuaranteed {1156 match vis_resolution_error {1157 VisResolutionError::Relative2018(span, path) => {1158 self.dcx().create_err(errs::Relative2018 {1159 span,1160 path_span: path.span,1161 // intentionally converting to String, as the text would also be used as1162 // in suggestion context1163 path_str: pprust::path_to_string(&path),1164 })1165 }1166 VisResolutionError::AncestorOnly(span) => {1167 self.dcx().create_err(errs::AncestorOnly(span))1168 }1169 VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self1170 .into_struct_error(1171 span,1172 ResolutionError::FailedToResolve {1173 segment,1174 label,1175 suggestion,1176 module: None,1177 message,1178 },1179 ),1180 VisResolutionError::ExpectedFound(span, path_str, res) => {1181 self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str })1182 }1183 VisResolutionError::Indeterminate(span) => {1184 self.dcx().create_err(errs::Indeterminate(span))1185 }1186 VisResolutionError::ModuleOnly(span) => self.dcx().create_err(errs::ModuleOnly(span)),1187 }1188 .emit()1189 }11901191 fn def_path_str(&self, mut def_id: DefId) -> String {1192 // We can't use `def_path_str` in resolve.1193 let mut path = vec![def_id];1194 while let Some(parent) = self.tcx.opt_parent(def_id) {1195 def_id = parent;1196 path.push(def_id);1197 if def_id.is_top_level_module() {1198 break;1199 }1200 }1201 // We will only suggest importing directly if it is accessible through that path.1202 path.into_iter()1203 .rev()1204 .map(|def_id| {1205 self.tcx1206 .opt_item_name(def_id)1207 .map(|name| {1208 match (1209 def_id.is_top_level_module(),1210 def_id.is_local(),1211 self.tcx.sess.edition(),1212 ) {1213 (true, true, Edition::Edition2015) => String::new(),1214 (true, true, _) => kw::Crate.to_string(),1215 (true, false, _) | (false, _, _) => name.to_string(),1216 }1217 })1218 .unwrap_or_else(|| "_".to_string())1219 })1220 .collect::<Vec<String>>()1221 .join("::")1222 }12231224 pub(crate) fn add_scope_set_candidates(1225 &mut self,1226 suggestions: &mut Vec<TypoSuggestion>,1227 scope_set: ScopeSet<'ra>,1228 ps: &ParentScope<'ra>,1229 sp: Span,1230 filter_fn: &impl Fn(Res) -> bool,1231 ) {1232 let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt());1233 self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| {1234 match scope {1235 Scope::DeriveHelpers(expn_id) => {1236 let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);1237 if filter_fn(res) {1238 suggestions.extend(1239 this.helper_attrs.get(&expn_id).into_iter().flatten().map(1240 |&(ident, orig_ident_span, _)| {1241 TypoSuggestion::new(ident.name, orig_ident_span, res)1242 },1243 ),1244 );1245 }1246 }1247 Scope::DeriveHelpersCompat => {1248 // Never recommend deprecated helper attributes.1249 }1250 Scope::MacroRules(macro_rules_scope) => {1251 if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() {1252 let res = macro_rules_def.decl.res();1253 if filter_fn(res) {1254 suggestions.push(TypoSuggestion::new(1255 macro_rules_def.ident.name,1256 macro_rules_def.orig_ident_span,1257 res,1258 ))1259 }1260 }1261 }1262 Scope::ModuleNonGlobs(module, _) => {1263 this.add_module_candidates(module, suggestions, filter_fn, None);1264 }1265 Scope::ModuleGlobs(..) => {1266 // Already handled in `ModuleNonGlobs`.1267 }1268 Scope::MacroUsePrelude => {1269 suggestions.extend(this.macro_use_prelude.iter().filter_map(1270 |(name, binding)| {1271 let res = binding.res();1272 filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res))1273 },1274 ));1275 }1276 Scope::BuiltinAttrs => {1277 let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy));1278 if filter_fn(res) {1279 suggestions.extend(1280 BUILTIN_ATTRIBUTES1281 .iter()1282 // These trace attributes are compiler-generated and have1283 // deliberately invalid names.1284 .filter(|attr| {1285 !matches!(attr.name, sym::cfg_trace | sym::cfg_attr_trace)1286 })1287 .map(|attr| TypoSuggestion::typo_from_name(attr.name, res)),1288 );1289 }1290 }1291 Scope::ExternPreludeItems => {1292 // Add idents from both item and flag scopes.1293 suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| {1294 let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());1295 filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res))1296 }));1297 }1298 Scope::ExternPreludeFlags => {}1299 Scope::ToolPrelude => {1300 let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);1301 suggestions.extend(1302 this.registered_tools1303 .iter()1304 .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)),1305 );1306 }1307 Scope::StdLibPrelude => {1308 if let Some(prelude) = this.prelude {1309 let mut tmp_suggestions = Vec::new();1310 this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None);1311 suggestions.extend(1312 tmp_suggestions1313 .into_iter()1314 .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)),1315 );1316 }1317 }1318 Scope::BuiltinTypes => {1319 suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| {1320 let res = Res::PrimTy(*prim_ty);1321 filter_fn(res)1322 .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res))1323 }))1324 }1325 }13261327 ControlFlow::<()>::Continue(())1328 });1329 }13301331 /// Lookup typo candidate in scope for a macro or import.1332 fn early_lookup_typo_candidate(1333 &mut self,1334 scope_set: ScopeSet<'ra>,1335 parent_scope: &ParentScope<'ra>,1336 ident: Ident,1337 filter_fn: &impl Fn(Res) -> bool,1338 ) -> Option<TypoSuggestion> {1339 let mut suggestions = Vec::new();1340 self.add_scope_set_candidates(1341 &mut suggestions,1342 scope_set,1343 parent_scope,1344 ident.span,1345 filter_fn,1346 );13471348 // Make sure error reporting is deterministic.1349 suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str()));13501351 match find_best_match_for_name(1352 &suggestions.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),1353 ident.name,1354 None,1355 ) {1356 Some(found) if found != ident.name => {1357 suggestions.into_iter().find(|suggestion| suggestion.candidate == found)1358 }1359 _ => None,1360 }1361 }13621363 fn lookup_import_candidates_from_module<FilterFn>(1364 &self,1365 lookup_ident: Ident,1366 namespace: Namespace,1367 parent_scope: &ParentScope<'ra>,1368 start_module: Module<'ra>,1369 crate_path: ThinVec<ast::PathSegment>,1370 filter_fn: FilterFn,1371 ) -> Vec<ImportSuggestion>1372 where1373 FilterFn: Fn(Res) -> bool,1374 {1375 let mut candidates = Vec::new();1376 let mut seen_modules = FxHashSet::default();1377 let start_did = start_module.def_id();1378 let mut worklist = vec![(1379 start_module,1380 ThinVec::<ast::PathSegment>::new(),1381 true,1382 start_did.is_local() || !self.tcx.is_doc_hidden(start_did),1383 true,1384 )];1385 let mut worklist_via_import = vec![];13861387 while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) =1388 match worklist.pop() {1389 None => worklist_via_import.pop(),1390 Some(x) => Some(x),1391 }1392 {1393 let in_module_is_extern = !in_module.def_id().is_local();1394 in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| {1395 // Avoid non-importable candidates.1396 if name_binding.is_assoc_item()1397 && !this.tcx.features().import_trait_associated_functions()1398 {1399 return;1400 }14011402 if ident.name == kw::Underscore {1403 return;1404 }14051406 let child_accessible =1407 accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module);14081409 // do not venture inside inaccessible items of other crates1410 if in_module_is_extern && !child_accessible {1411 return;1412 }14131414 let via_import = name_binding.is_import() && !name_binding.is_extern_crate();14151416 // There is an assumption elsewhere that paths of variants are in the enum's1417 // declaration and not imported. With this assumption, the variant component is1418 // chopped and the rest of the path is assumed to be the enum's own path. For1419 // errors where a variant is used as the type instead of the enum, this causes1420 // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`.1421 if via_import && name_binding.is_possibly_imported_variant() {1422 return;1423 }14241425 // #90113: Do not count an inaccessible reexported item as a candidate.1426 if let DeclKind::Import { source_decl, .. } = name_binding.kind1427 && this.is_accessible_from(source_decl.vis(), parent_scope.module)1428 && !this.is_accessible_from(name_binding.vis(), parent_scope.module)1429 {1430 return;1431 }14321433 let res = name_binding.res();1434 let did = match res {1435 Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did),1436 _ => res.opt_def_id(),1437 };1438 let child_doc_visible = doc_visible1439 && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did));14401441 // collect results based on the filter function1442 // avoid suggesting anything from the same module in which we are resolving1443 // avoid suggesting anything with a hygienic name1444 if ident.name == lookup_ident.name1445 && ns == namespace1446 && in_module != parent_scope.module1447 && ident.ctxt.is_root()1448 && filter_fn(res)1449 {1450 // create the path1451 let mut segms = if lookup_ident.span.at_least_rust_2018() {1452 // crate-local absolute paths start with `crate::` in edition 20181453 // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660)1454 crate_path.clone()1455 } else {1456 ThinVec::new()1457 };1458 segms.append(&mut path_segments.clone());14591460 segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));1461 let path = Path { span: name_binding.span, segments: segms, tokens: None };14621463 if child_accessible1464 // Remove invisible match if exists1465 && let Some(idx) = candidates1466 .iter()1467 .position(|v: &ImportSuggestion| v.did == did && !v.accessible)1468 {1469 candidates.remove(idx);1470 }14711472 let is_stable = if is_stable1473 && let Some(did) = did1474 && this.is_stable(did, path.span)1475 {1476 true1477 } else {1478 false1479 };14801481 // Rreplace unstable suggestions if we meet a new stable one,1482 // and do nothing if any other situation. For example, if we1483 // meet `std::ops::Range` after `std::range::legacy::Range`,1484 // we will remove the latter and then insert the former.1485 if is_stable1486 && let Some(idx) = candidates1487 .iter()1488 .position(|v: &ImportSuggestion| v.did == did && !v.is_stable)1489 {1490 candidates.remove(idx);1491 }14921493 if candidates.iter().all(|v: &ImportSuggestion| v.did != did) {1494 // See if we're recommending TryFrom, TryInto, or FromIterator and add1495 // a note about editions1496 let note = if let Some(did) = did {1497 let requires_note = !did.is_local()1498 && find_attr!(1499 this.tcx,1500 did,1501 RustcDiagnosticItem(1502 sym::TryInto | sym::TryFrom | sym::FromIterator1503 )1504 );1505 requires_note.then(|| {1506 format!(1507 "'{}' is included in the prelude starting in Edition 2021",1508 path_names_to_string(&path)1509 )1510 })1511 } else {1512 None1513 };15141515 candidates.push(ImportSuggestion {1516 did,1517 descr: res.descr(),1518 path,1519 accessible: child_accessible,1520 doc_visible: child_doc_visible,1521 note,1522 via_import,1523 is_stable,1524 });1525 }1526 }15271528 // collect submodules to explore1529 if let Some(def_id) = name_binding.res().module_like_def_id() {1530 // form the path1531 let mut path_segments = path_segments.clone();1532 path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));15331534 let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind1535 && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind1536 && import.parent_scope.expansion == parent_scope.expansion1537 {1538 true1539 } else {1540 false1541 };15421543 let is_extern_crate_that_also_appears_in_prelude =1544 name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018();15451546 if !is_extern_crate_that_also_appears_in_prelude || alias_import {1547 // add the module to the lookup1548 if seen_modules.insert(def_id) {1549 if via_import { &mut worklist_via_import } else { &mut worklist }.push(1550 (1551 this.expect_module(def_id),1552 path_segments,1553 child_accessible,1554 child_doc_visible,1555 is_stable && this.is_stable(def_id, name_binding.span),1556 ),1557 );1558 }1559 }1560 }1561 })1562 }15631564 candidates1565 }15661567 fn is_stable(&self, did: DefId, span: Span) -> bool {1568 if did.is_local() {1569 return true;1570 }15711572 match self.tcx.lookup_stability(did) {1573 Some(Stability {1574 level: StabilityLevel::Unstable { implied_by, .. }, feature, ..1575 }) => {1576 if span.allows_unstable(feature) {1577 true1578 } else if self.tcx.features().enabled(feature) {1579 true1580 } else if let Some(implied_by) = implied_by1581 && self.tcx.features().enabled(implied_by)1582 {1583 true1584 } else {1585 false1586 }1587 }1588 Some(_) => true,1589 None => false,1590 }1591 }15921593 /// When name resolution fails, this method can be used to look up candidate1594 /// entities with the expected name. It allows filtering them using the1595 /// supplied predicate (which should be used to only accept the types of1596 /// definitions expected, e.g., traits). The lookup spans across all crates.1597 ///1598 /// N.B., the method does not look into imports, but this is not a problem,1599 /// since we report the definitions (thus, the de-aliased imports).1600 pub(crate) fn lookup_import_candidates<FilterFn>(1601 &mut self,1602 lookup_ident: Ident,1603 namespace: Namespace,1604 parent_scope: &ParentScope<'ra>,1605 filter_fn: FilterFn,1606 ) -> Vec<ImportSuggestion>1607 where1608 FilterFn: Fn(Res) -> bool,1609 {1610 let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))];1611 let mut suggestions = self.lookup_import_candidates_from_module(1612 lookup_ident,1613 namespace,1614 parent_scope,1615 self.graph_root.to_module(),1616 crate_path,1617 &filter_fn,1618 );16191620 if lookup_ident.span.at_least_rust_2018() {1621 for (ident, entry) in &self.extern_prelude {1622 if entry.span().from_expansion() {1623 // Idents are adjusted to the root context before being1624 // resolved in the extern prelude, so reporting this to the1625 // user is no help. This skips the injected1626 // `extern crate std` in the 2018 edition, which would1627 // otherwise cause duplicate suggestions.1628 continue;1629 }1630 let Some(crate_id) =1631 self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)1632 else {1633 continue;1634 };16351636 let crate_def_id = crate_id.as_def_id();1637 let crate_root = self.expect_module(crate_def_id);16381639 // Check if there's already an item in scope with the same name as the crate.1640 // If so, we have to disambiguate the potential import suggestions by making1641 // the paths *global* (i.e., by prefixing them with `::`).1642 let needs_disambiguation =1643 self.resolutions(parent_scope.module).borrow().iter().any(1644 |(key, name_resolution)| {1645 if key.ns == TypeNS1646 && key.ident == *ident1647 && let Some(decl) = name_resolution.borrow().best_decl()1648 {1649 match decl.res() {1650 // No disambiguation needed if the identically named item we1651 // found in scope actually refers to the crate in question.1652 Res::Def(_, def_id) => def_id != crate_def_id,1653 Res::PrimTy(_) => true,1654 _ => false,1655 }1656 } else {1657 false1658 }1659 },1660 );1661 let mut crate_path = ThinVec::new();1662 if needs_disambiguation {1663 crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));1664 }1665 crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span())));16661667 suggestions.extend(self.lookup_import_candidates_from_module(1668 lookup_ident,1669 namespace,1670 parent_scope,1671 crate_root,1672 crate_path,1673 &filter_fn,1674 ));1675 }1676 }16771678 suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build());1679 suggestions1680 }16811682 pub(crate) fn unresolved_macro_suggestions(1683 &mut self,1684 err: &mut Diag<'_>,1685 macro_kind: MacroKind,1686 parent_scope: &ParentScope<'ra>,1687 ident: Ident,1688 krate: &Crate,1689 sugg_span: Option<Span>,1690 ) {1691 // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for1692 // suggestions.1693 self.register_macros_for_all_crates();16941695 let is_expected =1696 &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into()));1697 let suggestion = self.early_lookup_typo_candidate(1698 ScopeSet::Macro(macro_kind),1699 parent_scope,1700 ident,1701 is_expected,1702 );1703 if !self.add_typo_suggestion(err, suggestion, ident.span) {1704 self.detect_derive_attribute(err, ident, parent_scope, sugg_span);1705 }17061707 let import_suggestions =1708 self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);1709 let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() {1710 DUMMY_NODE_ID => (None, FoundUse::No),1711 node_id => UsePlacementFinder::check(krate, node_id),1712 };1713 show_candidates(1714 self.tcx,1715 err,1716 span,1717 &import_suggestions,1718 Instead::No,1719 found_use,1720 DiagMode::Normal,1721 vec![],1722 "",1723 );17241725 if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {1726 let label_span = ident.span.shrink_to_hi();1727 let mut spans = MultiSpan::from_span(label_span);1728 spans.push_span_label(label_span, "put a macro name here");1729 err.subdiagnostic(MaybeMissingMacroRulesName { spans });1730 return;1731 }17321733 if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {1734 err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });1735 return;1736 }17371738 let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| {1739 if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None }1740 });17411742 if let Some((def_id, unused_ident)) = unused_macro {1743 let scope = self.local_macro_def_scopes[&def_id];1744 let parent_nearest = parent_scope.module.nearest_parent_mod();1745 let unused_macro_kinds = self.local_macro_map[def_id].ext.macro_kinds();1746 if !unused_macro_kinds.contains(macro_kind.into()) {1747 match macro_kind {1748 MacroKind::Bang => {1749 err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident });1750 }1751 MacroKind::Attr => {1752 err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident });1753 }1754 MacroKind::Derive => {1755 err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident });1756 }1757 }1758 return;1759 }1760 if Some(parent_nearest) == scope.opt_def_id() {1761 err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });1762 err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });1763 return;1764 }1765 }17661767 if ident.name == kw::Default1768 && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind1769 {1770 let span = self.def_span(def_id);1771 let source_map = self.tcx.sess.source_map();1772 let head_span = source_map.guess_head_span(span);1773 err.subdiagnostic(ConsiderAddingADerive {1774 span: head_span.shrink_to_lo(),1775 suggestion: "#[derive(Default)]\n".to_string(),1776 });1777 }1778 for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {1779 let Ok(binding) = self.cm().resolve_ident_in_scope_set(1780 ident,1781 ScopeSet::All(ns),1782 parent_scope,1783 None,1784 None,1785 None,1786 ) else {1787 continue;1788 };17891790 let desc = match binding.res() {1791 Res::Def(DefKind::Macro(MacroKinds::BANG), _) => {1792 "a function-like macro".to_string()1793 }1794 Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => {1795 format!("an attribute: `#[{ident}]`")1796 }1797 Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => {1798 format!("a derive macro: `#[derive({ident})]`")1799 }1800 Res::Def(DefKind::Macro(kinds), _) => {1801 format!("{} {}", kinds.article(), kinds.descr())1802 }1803 Res::ToolMod | Res::OpenMod(..) => {1804 // Don't confuse the user with tool modules or open modules.1805 continue;1806 }1807 Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {1808 "only a trait, without a derive macro".to_string()1809 }1810 res => format!(1811 "{} {}, not {} {}",1812 res.article(),1813 res.descr(),1814 macro_kind.article(),1815 macro_kind.descr_expected(),1816 ),1817 };1818 if let crate::DeclKind::Import { import, .. } = binding.kind1819 && !import.span.is_dummy()1820 {1821 let note = errors::IdentImporterHereButItIsDesc {1822 span: import.span,1823 imported_ident: ident,1824 imported_ident_desc: &desc,1825 };1826 err.subdiagnostic(note);1827 // Silence the 'unused import' warning we might get,1828 // since this diagnostic already covers that import.1829 self.record_use(ident, binding, Used::Other);1830 return;1831 }1832 let note = errors::IdentInScopeButItIsDesc {1833 imported_ident: ident,1834 imported_ident_desc: &desc,1835 };1836 err.subdiagnostic(note);1837 return;1838 }18391840 if self.macro_names.contains(&IdentKey::new(ident)) {1841 err.subdiagnostic(AddedMacroUse);1842 return;1843 }1844 }18451846 /// Given an attribute macro that failed to be resolved, look for `derive` macros that could1847 /// provide it, either as-is or with small typos.1848 fn detect_derive_attribute(1849 &self,1850 err: &mut Diag<'_>,1851 ident: Ident,1852 parent_scope: &ParentScope<'ra>,1853 sugg_span: Option<Span>,1854 ) {1855 // Find all of the `derive`s in scope and collect their corresponding declared1856 // attributes.1857 // FIXME: this only works if the crate that owns the macro that has the helper_attr1858 // has already been imported.1859 let mut derives = vec![];1860 let mut all_attrs: UnordMap<Symbol, Vec<_>> = UnordMap::default();1861 // We're collecting these in a hashmap, and handle ordering the output further down.1862 #[allow(rustc::potential_query_instability)]1863 for (def_id, data) in self1864 .local_macro_map1865 .iter()1866 .map(|(local_id, data)| (local_id.to_def_id(), data))1867 .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d)))1868 {1869 for helper_attr in &data.ext.helper_attrs {1870 let item_name = self.tcx.item_name(def_id);1871 all_attrs.entry(*helper_attr).or_default().push(item_name);1872 if helper_attr == &ident.name {1873 derives.push(item_name);1874 }1875 }1876 }1877 let kind = MacroKind::Derive.descr();1878 if !derives.is_empty() {1879 // We found an exact match for the missing attribute in a `derive` macro. Suggest it.1880 let mut derives: Vec<String> = derives.into_iter().map(|d| d.to_string()).collect();1881 derives.sort();1882 derives.dedup();1883 let msg = match &derives[..] {1884 [derive] => format!(" `{derive}`"),1885 [start @ .., last] => format!(1886 "s {} and `{last}`",1887 start.iter().map(|d| format!("`{d}`")).collect::<Vec<_>>().join(", ")1888 ),1889 [] => unreachable!("we checked for this to be non-empty 10 lines above!?"),1890 };1891 let msg = format!(1892 "`{}` is an attribute that can be used by the {kind}{msg}, you might be \1893 missing a `derive` attribute",1894 ident.name,1895 );1896 let sugg_span =1897 if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind {1898 let span = self.def_span(id);1899 if span.from_expansion() {1900 None1901 } else {1902 // For enum variants sugg_span is empty but we can get the enum's Span.1903 Some(span.shrink_to_lo())1904 }1905 } else {1906 // For items this `Span` will be populated, everything else it'll be None.1907 sugg_span1908 };1909 match sugg_span {1910 Some(span) => {1911 err.span_suggestion_verbose(1912 span,1913 msg,1914 format!("#[derive({})]\n", derives.join(", ")),1915 Applicability::MaybeIncorrect,1916 );1917 }1918 None => {1919 err.note(msg);1920 }1921 }1922 } else {1923 // We didn't find an exact match. Look for close matches. If any, suggest fixing typo.1924 let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord();1925 if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None)1926 && let Some(macros) = all_attrs.get(&best_match)1927 {1928 let mut macros: Vec<String> = macros.into_iter().map(|d| d.to_string()).collect();1929 macros.sort();1930 macros.dedup();1931 let msg = match ¯os[..] {1932 [] => return,1933 [name] => format!(" `{name}` accepts"),1934 [start @ .., end] => format!(1935 "s {} and `{end}` accept",1936 start.iter().map(|m| format!("`{m}`")).collect::<Vec<_>>().join(", "),1937 ),1938 };1939 let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute");1940 err.span_suggestion_verbose(1941 ident.span,1942 msg,1943 best_match,1944 Applicability::MaybeIncorrect,1945 );1946 }1947 }1948 }19491950 pub(crate) fn add_typo_suggestion(1951 &self,1952 err: &mut Diag<'_>,1953 suggestion: Option<TypoSuggestion>,1954 span: Span,1955 ) -> bool {1956 let suggestion = match suggestion {1957 None => return false,1958 // We shouldn't suggest underscore.1959 Some(suggestion) if suggestion.candidate == kw::Underscore => return false,1960 Some(suggestion) => suggestion,1961 };19621963 let mut did_label_def_span = false;19641965 if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) {1966 if span.overlaps(def_span) {1967 // Don't suggest typo suggestion for itself like in the following:1968 // error[E0423]: expected function, tuple struct or tuple variant, found struct `X`1969 // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:141970 // |1971 // LL | struct X {}1972 // | ----------- `X` defined here1973 // LL |1974 // LL | const Y: X = X("ö");1975 // | -------------^^^^^^- similarly named constant `Y` defined here1976 // |1977 // help: use struct literal syntax instead1978 // |1979 // LL | const Y: X = X {};1980 // | ^^^^1981 // help: a constant with a similar name exists1982 // |1983 // LL | const Y: X = Y("ö");1984 // | ^1985 return false;1986 }1987 let span = self.tcx.sess.source_map().guess_head_span(def_span);1988 let candidate_descr = suggestion.res.descr();1989 let candidate = suggestion.candidate;1990 let label = match suggestion.target {1991 SuggestionTarget::SimilarlyNamed => {1992 errors::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate }1993 }1994 SuggestionTarget::SingleItem => {1995 errors::DefinedHere::SingleItem { span, candidate_descr, candidate }1996 }1997 };1998 did_label_def_span = true;1999 err.subdiagnostic(label);2000 }
Findings
✓ No findings reported for this file.