1// ignore-tidy-file-filelength23use std::borrow::Cow;4use std::iter;5use std::ops::Deref;67use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt, Visitor, walk_ty};8use rustc_ast::{9 self as ast, AngleBracketedArg, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericArg,10 GenericArgs, GenericParam, GenericParamKind, Item, ItemKind, MethodCall, NodeId, Path,11 PathSegment, Ty, TyKind,12};13use rustc_ast_pretty::pprust::{path_to_string, where_bound_predicate_to_string};14use rustc_attr_ir::diagnostic::{CustomDiagnostic, FormatArgs};15use rustc_attr_ir::find_attr;16use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};17use rustc_data_structures::unord::UnordItems;18use rustc_errors::codes::*;19use rustc_errors::{20 Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize,21 struct_span_code_err,22};23use rustc_hir::def::Namespace::{self, *};24use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds};25use rustc_hir::def_id::{CRATE_DEF_ID, DefId};26use rustc_hir::{MissingLifetimeKind, PrimTy};27use rustc_lint_defs::builtin::{SINGLE_USE_LIFETIMES, UNUSED_LIFETIMES};28use rustc_middle::ty;29use rustc_session::Session;30use rustc_span::edit_distance::{edit_distance, find_best_match_for_name};31use rustc_span::edition::Edition;32use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};33use thin_vec::{ThinVec, thin_vec};34use tracing::debug;3536use super::NoConstantGenericsReason;37use crate::diagnostics::impls::{ImportSuggestion, LabelSuggestion, TypoSuggestion};38use crate::late::{39 AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind,40 LifetimeUseSet, QSelf, RibKind,41};42use crate::ty::fast_reject::SimplifiedType;43use crate::{44 Finalize, Module, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, Resolver,45 ScopeSet, Segment, diagnostics, path_names_to_string,46};4748/// A field or associated item from self type suggested in case of resolution failure.49enum AssocSuggestion {50 Field,51 MethodWithSelf { called: bool },52 AssocFn { called: bool },53 AssocType,54 AssocConst,55}5657impl AssocSuggestion {58 fn action(&self) -> &'static str {59 match self {60 AssocSuggestion::Field => "use the available field",61 AssocSuggestion::MethodWithSelf { called: true } => {62 "call the method with the fully-qualified path"63 }64 AssocSuggestion::MethodWithSelf { called: false } => {65 "refer to the method with the fully-qualified path"66 }67 AssocSuggestion::AssocFn { called: true } => "call the associated function",68 AssocSuggestion::AssocFn { called: false } => "refer to the associated function",69 AssocSuggestion::AssocConst => "use the associated `const`",70 AssocSuggestion::AssocType => "use the associated type",71 }72 }73}7475fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {76 namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper77}7879fn is_self_value(path: &[Segment], namespace: Namespace) -> bool {80 namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower81}8283fn path_to_string_without_assoc_item_bindings(path: &Path) -> String {84 let mut path = path.clone();85 for segment in &mut path.segments {86 let mut remove_args = false;87 if let Some(args) = segment.args.as_deref_mut()88 && let ast::GenericArgs::AngleBracketed(angle_bracketed) = args89 {90 angle_bracketed.args.retain(|arg| matches!(arg, ast::AngleBracketedArg::Arg(_)));91 remove_args = angle_bracketed.args.is_empty();92 }93 if remove_args {94 segment.args = None;95 }96 }97 path_to_string(&path)98}99100/// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.101fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) {102 let variant_path = &suggestion.path;103 let variant_path_string = path_names_to_string(variant_path);104105 let path_len = suggestion.path.segments.len();106 let enum_path = ast::Path {107 span: suggestion.path.span,108 segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(),109 };110 let enum_path_string = path_names_to_string(&enum_path);111112 (variant_path_string, enum_path_string)113}114115/// Description of an elided lifetime.116#[derive(Copy, Clone, PartialEq, Eq, Debug)]117pub(super) struct MissingLifetime {118 /// Used to overwrite the resolution with the suggestion, to avoid cascading errors.119 pub id: NodeId,120 /// As we cannot yet emit lints in this crate and have to buffer them instead,121 /// we need to associate each lint with some `NodeId`,122 /// however for some `MissingLifetime`s their `NodeId`s are "fake",123 /// in a sense that they are temporary and not get preserved down the line,124 /// which means that the lints for those nodes will not get emitted.125 /// To combat this, we can try to use some other `NodeId`s as a fallback option.126 pub id_for_lint: NodeId,127 /// Where to suggest adding the lifetime.128 pub span: Span,129 /// How the lifetime was introduced, to have the correct space and comma.130 pub kind: MissingLifetimeKind,131 /// Number of elided lifetimes, used for elision in path.132 pub count: usize,133}134135/// Description of the lifetimes appearing in a function parameter.136/// This is used to provide a literal explanation to the elision failure.137#[derive(Debug)]138pub(super) struct ElisionFnParameter {139 /// The index of the argument in the original definition.140 pub index: usize,141 /// The name of the argument if it's a simple ident.142 pub ident: Option<Ident>,143 /// The number of lifetimes in the parameter.144 pub lifetime_count: usize,145 /// The span of the parameter.146 pub span: Span,147}148149/// Description of lifetimes that appear as candidates for elision.150/// This is used to suggest introducing an explicit lifetime.151#[derive(Clone, Copy, Debug)]152pub(super) enum LifetimeElisionCandidate {153 /// This is not a real lifetime, or it is a named lifetime, in which case we won't suggest anything.154 Ignore,155 Missing(MissingLifetime),156}157158/// Only used for diagnostics.159#[derive(Debug)]160struct BaseError {161 msg: String,162 fallback_label: String,163 span: Span,164 span_label: Option<(Span, &'static str)>,165 could_be_expr: bool,166 suggestion: Option<(Span, &'static str, String)>,167 module: Option<DefId>,168 notes: Vec<String>,169}170171#[derive(Debug)]172enum TypoCandidate {173 Typo(TypoSuggestion),174 Shadowed(Res, Option<Span>),175 None,176}177178impl TypoCandidate {179 fn to_opt_suggestion(self) -> Option<TypoSuggestion> {180 match self {181 TypoCandidate::Typo(sugg) => Some(sugg),182 TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None,183 }184 }185}186187impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {188 fn trait_assoc_type_def_id_by_name(189 &mut self,190 trait_def_id: DefId,191 assoc_name: Symbol,192 ) -> Option<DefId> {193 let module = self.r.get_module(trait_def_id)?;194 self.r.resolutions(module).iter().find_map(|(key, resolution)| {195 if key.ident.name != assoc_name {196 return None;197 }198 let resolution = resolution.borrow(self.r);199 let binding = resolution.best_decl()?;200 match binding.res() {201 Res::Def(DefKind::AssocTy, def_id) => Some(def_id),202 _ => None,203 }204 })205 }206207 /// This does best-effort work to generate suggestions for associated types.208 fn suggest_assoc_type_from_bounds(209 &mut self,210 err: &mut Diag<'_>,211 source: PathSource<'_, 'ast, 'ra>,212 path: &[Segment],213 ident_span: Span,214 ) -> bool {215 // Filter out cases where we cannot emit meaningful suggestions.216 if source.namespace() != TypeNS {217 return false;218 }219 let [segment] = path else { return false };220 if segment.has_generic_args {221 return false;222 }223 if !ident_span.can_be_used_for_suggestions() {224 return false;225 }226 let assoc_name = segment.ident.name;227 if assoc_name == kw::Underscore {228 return false;229 }230231 // Map: type parameter name -> (trait def id -> (assoc type def id, trait paths as written)).232 // We keep a set of paths per trait so we can detect cases like233 // `T: Trait<i32> + Trait<u32>` where suggesting `T::Assoc` would be ambiguous.234 let mut matching_bounds: FxIndexMap<235 Symbol,236 FxIndexMap<DefId, (DefId, FxIndexSet<String>)>,237 > = FxIndexMap::default();238239 let mut record_bound = |this: &mut Self,240 ty_param: Symbol,241 poly_trait_ref: &ast::PolyTraitRef| {242 // Avoid generating suggestions we can't print in a well-formed way.243 if !poly_trait_ref.bound_generic_params.is_empty() {244 return;245 }246 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {247 return;248 }249 let Some(trait_seg) = poly_trait_ref.trait_ref.path.segments.last() else {250 return;251 };252 let Some(partial_res) = this.r.partial_res_map.get(&trait_seg.id) else {253 return;254 };255 let Some(trait_def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) else {256 return;257 };258 let Some(assoc_type_def_id) =259 this.trait_assoc_type_def_id_by_name(trait_def_id, assoc_name)260 else {261 return;262 };263264 // Preserve `::` and generic args so we don't generate broken suggestions like265 // `<T as Foo>::Assoc` for bounds written as `T: ::Foo<'a>`, while stripping266 // associated-item bindings that are rejected in qualified paths.267 let trait_path =268 path_to_string_without_assoc_item_bindings(&poly_trait_ref.trait_ref.path);269 let trait_bounds = matching_bounds.entry(ty_param).or_default();270 let trait_bounds = trait_bounds271 .entry(trait_def_id)272 .or_insert_with(|| (assoc_type_def_id, FxIndexSet::default()));273 debug_assert_eq!(trait_bounds.0, assoc_type_def_id);274 trait_bounds.1.insert(trait_path);275 };276277 let mut record_from_generics = |this: &mut Self, generics: &ast::Generics| {278 for param in &generics.params {279 let ast::GenericParamKind::Type { .. } = param.kind else { continue };280 for bound in ¶m.bounds {281 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };282 record_bound(this, param.ident.name, poly_trait_ref);283 }284 }285286 for predicate in &generics.where_clause.predicates {287 let ast::WherePredicateKind::BoundPredicate(where_bound) = &predicate.kind else {288 continue;289 };290291 let ast::TyKind::Path(None, bounded_path) = &where_bound.bounded_ty.kind else {292 continue;293 };294 let [ast::PathSegment { ident, args: None, .. }] = &bounded_path.segments[..]295 else {296 continue;297 };298299 // Only suggest for bounds that are explicitly on an in-scope type parameter.300 let Some(partial_res) = this.r.partial_res_map.get(&where_bound.bounded_ty.id)301 else {302 continue;303 };304 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {305 continue;306 }307308 for bound in &where_bound.bounds {309 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };310 record_bound(this, ident.name, poly_trait_ref);311 }312 }313 };314315 if let Some(item) = self.diag_metadata.current_item316 && let Some(generics) = item.kind.generics()317 {318 record_from_generics(self, generics);319 }320321 if let Some(item) = self.diag_metadata.current_item322 && matches!(item.kind, ItemKind::Impl(..))323 && let Some(assoc) = self.diag_metadata.current_impl_item324 {325 let generics = match &assoc.kind {326 AssocItemKind::Const(ast::ConstItem { generics, .. })327 | AssocItemKind::Fn(ast::Fn { generics, .. })328 | AssocItemKind::Type(ast::TyAlias { generics, .. }) => Some(generics),329 AssocItemKind::Delegation(..)330 | AssocItemKind::MacCall(..)331 | AssocItemKind::DelegationMac(..) => None,332 };333 if let Some(generics) = generics {334 record_from_generics(self, generics);335 }336 }337338 let mut suggestions: FxIndexSet<String> = FxIndexSet::default();339 for (ty_param, traits) in matching_bounds {340 let ty_param = ty_param.to_ident_string();341 let trait_paths_len: usize = traits.values().map(|(_, paths)| paths.len()).sum();342 if traits.len() == 1 && trait_paths_len == 1 {343 let assoc_type_def_id = traits.values().next().unwrap().0;344 let assoc_segment = format!(345 "{}{}",346 assoc_name,347 self.r.item_required_generic_args_suggestion(assoc_type_def_id)348 );349 suggestions.insert(format!("{ty_param}::{assoc_segment}"));350 } else {351 for (assoc_type_def_id, trait_paths) in traits.into_values() {352 let assoc_segment = format!(353 "{}{}",354 assoc_name,355 self.r.item_required_generic_args_suggestion(assoc_type_def_id)356 );357 for trait_path in trait_paths {358 suggestions359 .insert(format!("<{ty_param} as {trait_path}>::{assoc_segment}"));360 }361 }362 }363 }364365 if suggestions.is_empty() {366 return false;367 }368369 let mut suggestions: Vec<String> = suggestions.into_iter().collect();370 suggestions.sort();371372 err.span_suggestions_with_style(373 ident_span,374 "you might have meant to use an associated type of the same name",375 suggestions,376 Applicability::MaybeIncorrect,377 SuggestionStyle::ShowAlways,378 );379380 true381 }382383 fn make_base_error(384 &mut self,385 path: &[Segment],386 span: Span,387 source: PathSource<'_, 'ast, 'ra>,388 res: Option<Res>,389 could_be_expr: bool,390 ) -> BaseError {391 // Make the base error.392 let mut expected = source.descr_expected();393 let path_str = Segment::names_to_string(path);394395 if let Some(res) = res {396 BaseError {397 msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),398 fallback_label: format!("not a {expected}"),399 span,400 span_label: match res {401 Res::Def(DefKind::TyParam, def_id) => {402 Some((self.r.def_span(def_id), "found this type parameter"))403 }404 _ => None,405 },406 could_be_expr,407 suggestion: None,408 module: None,409 notes: Vec::new(),410 }411 } else {412 let mut span_label = None;413 let item_ident = path.last().unwrap().ident;414 let item_span = item_ident.span;415 let (tick, mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {416 debug!(?self.diag_metadata.current_impl_items);417 debug!(?self.diag_metadata.current_function);418 let suggestion = if self.current_trait_ref.is_none()419 && let Some((fn_kind, _)) = self.diag_metadata.current_function420 && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()421 && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = fn_kind422 && let Some(items) = self.diag_metadata.current_impl_items423 && let Some(item) = items.iter().find(|i| {424 i.kind.ident().is_some_and(|ident| {425 // Don't suggest if the item is in Fn signature arguments (#112590).426 ident.name == item_ident.name && !sig.span.contains(item_span)427 })428 }) {429 let sp = item_span.shrink_to_lo();430431 // Account for `Foo { field }` when suggesting `self.field` so we result on432 // `Foo { field: self.field }`.433 let field = match source {434 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. })) => {435 expr.fields.iter().find(|f| f.ident == item_ident)436 }437 _ => None,438 };439 let pre = if let Some(field) = field440 && field.is_shorthand441 {442 format!("{item_ident}: ")443 } else {444 String::new()445 };446 // Ensure we provide a structured suggestion for an assoc fn only for447 // expressions that are actually a fn call.448 let is_call = match field {449 Some(ast::ExprField { expr, .. }) => {450 matches!(expr.kind, ExprKind::Call(..))451 }452 _ => matches!(453 source,454 PathSource::Expr(Some(Expr { kind: ExprKind::Call(..), .. })),455 ),456 };457458 match &item.kind {459 AssocItemKind::Fn(fn_)460 if (!sig.decl.has_self() || !is_call) && fn_.sig.decl.has_self() =>461 {462 // Ensure that we only suggest `self.` if `self` is available,463 // you can't call `fn foo(&self)` from `fn bar()` (#115992).464 // We also want to mention that the method exists.465 span_label = Some((466 fn_.ident.span,467 "a method by that name is available on `Self` here",468 ));469 None470 }471 AssocItemKind::Fn(fn_) if !fn_.sig.decl.has_self() && !is_call => {472 span_label = Some((473 fn_.ident.span,474 "an associated function by that name is available on `Self` here",475 ));476 None477 }478 AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => {479 Some((sp, "consider using the method on `Self`", format!("{pre}self.")))480 }481 AssocItemKind::Fn(_) => Some((482 sp,483 "consider using the associated function on `Self`",484 format!("{pre}Self::"),485 )),486 AssocItemKind::Const(..) => Some((487 sp,488 "consider using the associated constant on `Self`",489 format!("{pre}Self::"),490 )),491 _ => None,492 }493 } else {494 None495 };496 ("", String::new(), "this scope".to_string(), None, suggestion)497 } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {498 if self.r.tcx.sess.edition() > Edition::Edition2015 {499 // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude500 // which overrides all other expectations of item type501 expected = "crate";502 ("", String::new(), "the list of imported crates".to_string(), None, None)503 } else {504 (505 "",506 String::new(),507 "the crate root".to_string(),508 Some(CRATE_DEF_ID.to_def_id()),509 None,510 )511 }512 } else if path.len() == 2 && path[0].ident.name == kw::Crate {513 (514 "",515 String::new(),516 "the crate root".to_string(),517 Some(CRATE_DEF_ID.to_def_id()),518 None,519 )520 } else {521 let mod_path = &path[..path.len() - 1];522 let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source);523 let mod_prefix = match mod_res {524 PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),525 _ => None,526 };527528 let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);529530 let mod_prefix =531 mod_prefix.map_or_else(String::new, |res| format!("{} ", res.descr()));532 ("`", mod_prefix, Segment::names_to_string(mod_path), module_did, None)533 };534535 let suggestion =536 if ["true", "false"].contains(&item_ident.to_string().to_lowercase().as_str()) {537 // check if we are in situation of typo like `True` instead of `true`.538 let item_typo = item_ident.to_string().to_lowercase();539 Some((item_span, "you may want to use a bool value instead", item_typo))540 // FIXME(vincenzopalazzo): make the check smarter,541 // and maybe expand with levenshtein distance checks542 } else if item_ident.as_str() == "printf" {543 Some((544 item_span,545 "you may have meant to use the `print` macro",546 "print!".to_owned(),547 ))548 } else {549 suggestion550 };551 let mut msg = format!(552 "cannot find {expected} `{item_ident}` in {mod_prefix}{tick}{mod_str}{tick}"553 );554 let mut fallback_label = if path_str == "async" && expected.starts_with("struct") {555 "`async` blocks are only allowed in Rust 2018 or later".to_string()556 } else {557 format!("not found in {tick}{mod_str}{tick}")558 };559 let mut notes = Vec::new();560 if let Some(module_def_id) = module561 && let Some((this, directive)) = self.r.on_unknown_data(module_def_id)562 {563 let args =564 FormatArgs { unresolved: item_ident.to_string(), this: this.to_string(), .. };565 let CustomDiagnostic {566 message,567 label,568 notes: custom_notes,569 parent_label: _unreachable,570 } = directive.eval(None, &args);571 if let Some(message) = message {572 notes.push(msg);573 msg = message;574 }575 if let Some(label) = label {576 fallback_label = label;577 if let Some((_, span_label)) = span_label.take() {578 notes.push(span_label.to_string());579 }580 }581 notes.extend(custom_notes);582 }583584 BaseError {585 msg,586 fallback_label,587 span: item_span,588 span_label,589 could_be_expr,590 suggestion,591 module,592 notes,593 }594 }595 }596597 fn could_be_expr(&self, res: Res, span: Span) -> bool {598 match res {599 // Verify whether this is a fn call or an Fn used as a type.600 Res::Def(DefKind::Fn, _) => self601 .r602 .tcx603 .sess604 .source_map()605 .span_to_snippet(span)606 .is_ok_and(|snippet| snippet.ends_with(')')),607 Res::Def(608 DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst,609 _,610 )611 | Res::SelfCtor(_)612 | Res::PrimTy(_)613 | Res::Local(_) => true,614 _ => false,615 }616 }617618 /// Try to suggest for a module path that cannot be resolved.619 /// Such as `fmt::Debug` where `fmt` is not resolved without importing,620 /// here we search with `lookup_import_candidates` for a module named `fmt`621 /// with `TypeNS` as namespace.622 ///623 /// We need a separate function here because we won't suggest for a path with single segment624 /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`625 pub(crate) fn smart_resolve_partial_mod_path_errors(626 &mut self,627 prefix_path: &[Segment],628 following_seg: Option<&Segment>,629 ) -> Vec<ImportSuggestion> {630 if let Some(segment) = prefix_path.last()631 && let Some(following_seg) = following_seg632 {633 let candidates = self.r.lookup_import_candidates(634 segment.ident,635 Namespace::TypeNS,636 &self.parent_scope,637 &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),638 );639 // double check next seg is valid640 candidates641 .into_iter()642 .filter(|candidate| {643 if let Some(def_id) = candidate.did644 && let Some(module) = self.r.get_module(def_id)645 {646 Some(def_id) != self.parent_scope.module.opt_def_id()647 && self648 .r649 .resolutions(module)650 .iter()651 .any(|(key, _r)| key.ident.name == following_seg.ident.name)652 } else {653 false654 }655 })656 .collect::<Vec<_>>()657 } else {658 Vec::new()659 }660 }661662 /// Handles error reporting for `smart_resolve_path_fragment` function.663 /// Creates base error and amends it with one short label and possibly some longer helps/notes.664 #[tracing::instrument(skip(self), level = "debug")]665 pub(crate) fn smart_resolve_report_errors(666 &mut self,667 path: &[Segment],668 following_seg: Option<&Segment>,669 span: Span,670 mut source: PathSource<'_, 'ast, 'ra>,671 res: Option<Res>,672 qself: Option<&QSelf>,673 ) -> (Diag<'tcx>, Vec<ImportSuggestion>) {674 let cross_namespace_res = res.filter(|res| !res.matches_ns(source.namespace()));675 let could_be_expr = res.is_some_and(|res| self.could_be_expr(res, span));676 let base_error = self.make_base_error(677 path,678 span,679 source,680 if cross_namespace_res.is_some() { None } else { res },681 could_be_expr,682 );683684 let code = source.error_code(res.is_some());685 let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());686 err.code(code);687688 if self.diag_metadata.currently_processing_generic_args689 && let PathSource::Type = source690 && let [segment] = path691 && !segment.has_generic_args692 {693 source = PathSource::TypeParam;694 }695696 if let Some(res) = cross_namespace_res {697 err.note(format!(698 "{} {} named `{}` exists in another namespace",699 res.article(),700 res.descr(),701 Segment::names_to_string(path),702 ));703 }704705 // Try to get the span of the identifier within the path's syntax context706 // (if that's different).707 if let Some(within_macro_span) =708 base_error.span.within_macro(span, self.r.tcx.sess.source_map())709 {710 err.span_label(within_macro_span, "due to this macro variable");711 }712713 self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);714 self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);715 self.suggest_range_struct_destructuring(&mut err, path, source);716 self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);717 self.detect_resolution_error_in_derive(&mut err, base_error.span);718719 if let Some((span, label)) = base_error.span_label {720 err.span_label(span, label);721 }722 for note in &base_error.notes {723 err.note(note.clone());724 }725726 if let Some((span, message, replacement)) = &base_error.suggestion {727 let ident = path.last().unwrap().ident;728 // An insertion adds a qualifier (`Self::CONST`), whereas a replacement729 // replaces the name itself (`True` -> `true`).730 let expression =731 if span.is_empty() { format!("{replacement}{ident}") } else { replacement.clone() };732 if !self.suggest_named_format_argument(733 &mut err,734 source,735 ident,736 &expression,737 Applicability::MaybeIncorrect,738 ) {739 err.span_suggestion_verbose(740 *span,741 *message,742 replacement,743 Applicability::MaybeIncorrect,744 );745 }746 }747748 self.suggest_changing_type_to_const_param(&mut err, res, source, path, following_seg, span);749 self.explain_functions_in_pattern(&mut err, res, source);750751 if self.suggest_pattern_match_with_let(&mut err, source, span) {752 // Fallback label.753 err.span_label(base_error.span, base_error.fallback_label);754 return (err, Vec::new());755 }756757 self.suggest_self_or_self_ref(&mut err, path, span);758 self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error);759 self.detect_rtn_with_fully_qualified_path(760 &mut err,761 path,762 following_seg,763 span,764 source,765 res,766 qself,767 );768 if self.suggest_self_ty(&mut err, source, path, span)769 || self.suggest_self_value(&mut err, source, path, span)770 {771 return (err, Vec::new());772 }773774 if let Some((did, item)) = self.lookup_doc_alias_name(path, source.namespace()) {775 let item_name = item.name;776 let suggestion_name = self.r.tcx.item_name(did);777 err.span_suggestion_verbose(778 item.span,779 format!(780 "`{suggestion_name}` has a name defined in the doc alias attribute as \781 `{item_name}`",782 ),783 suggestion_name,784 Applicability::MaybeIncorrect,785 );786787 return (err, Vec::new());788 };789790 let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed(791 &mut err,792 source,793 path,794 following_seg,795 span,796 res,797 &base_error,798 );799 if found {800 return (err, candidates);801 }802803 if self.suggest_shadowed(&mut err, source, path, following_seg, span) {804 // if there is already a shadowed name, don'suggest candidates for importing805 candidates.clear();806 }807808 let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);809 fallback |= self.suggest_typo(810 &mut err,811 source,812 path,813 following_seg,814 span,815 &base_error,816 suggested_candidates,817 );818819 if fallback {820 // Fallback label.821 err.span_label(base_error.span, base_error.fallback_label);822 }823 self.err_code_special_cases(&mut err, source, path, span);824825 let module = base_error.module.unwrap_or_else(|| CRATE_DEF_ID.to_def_id());826 self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);827828 (err, candidates)829 }830831 /// Captures only accept identifiers, so put the suggested expression in a named argument.832 fn suggest_named_format_argument(833 &self,834 err: &mut Diag<'_>,835 source: PathSource<'_, '_, '_>,836 ident: Ident,837 expression: &str,838 applicability: Applicability,839 ) -> bool {840 let PathSource::Expr(Some(Expr {841 kind: ExprKind::FormatArgs(args),842 span: format_span,843 ..844 })) = source845 else {846 return false;847 };848 let argument = format!("{ident} = {expression}");849 let insertion = args850 .arguments851 .explicit_args()852 .last()853 .map_or(args.span, |arg| arg.original_span)854 .shrink_to_hi();855 // A custom wrapper may not accept another named argument.856 let source_macro = format_span857 .source_callee()858 .and_then(|expn| expn.macro_def_id)859 // Local diagnostic items are not available before HIR lowering.860 .filter(|def_id| !def_id.is_local())861 .and_then(|def_id| self.r.tcx.get_diagnostic_name(def_id));862 if matches!(863 source_macro,864 Some(865 sym::format_macro866 | sym::format_args_macro867 | sym::print_macro868 | sym::println_macro869 | sym::eprint_macro870 | sym::eprintln_macro871 | sym::write_macro872 | sym::writeln_macro873 | sym::assert_macro874 | sym::assert_eq_macro875 | sym::assert_ne_macro876 | sym::debug_assert_macro877 | sym::debug_assert_eq_macro878 | sym::debug_assert_ne_macro879 | sym::core_panic_macro880 | sym::std_panic_macro881 | sym::unreachable_macro882 | sym::todo_macro883 | sym::unimplemented_macro884 )885 ) && !insertion.from_expansion()886 && !args.span.from_expansion()887 {888 err.span_suggestion_verbose(889 insertion,890 "you might have meant to add a named formatting argument",891 format!(", {argument}"),892 applicability,893 );894 } else {895 err.help(format!(896 "you might have meant to add a named formatting argument: `{argument}`"897 ));898 }899 true900 }901902 fn detect_rtn_with_fully_qualified_path(903 &self,904 err: &mut Diag<'_>,905 path: &[Segment],906 following_seg: Option<&Segment>,907 span: Span,908 source: PathSource<'_, '_, '_>,909 res: Option<Res>,910 qself: Option<&QSelf>,911 ) {912 if let Some(Res::Def(DefKind::AssocFn, _)) = res913 && let PathSource::TraitItem(TypeNS, _) = source914 && let None = following_seg915 && let Some(qself) = qself916 && let TyKind::Path(None, ty_path) = &qself.ty.kind917 && ty_path.segments.len() == 1918 && self.diag_metadata.current_where_predicate.is_some()919 {920 err.span_suggestion_verbose(921 span,922 "you might have meant to use the return type notation syntax",923 format!("{}::{}(..)", ty_path.segments[0].ident, path[path.len() - 1].ident),924 Applicability::MaybeIncorrect,925 );926 }927 }928929 fn detect_assoc_type_constraint_meant_as_path(930 &self,931 err: &mut Diag<'_>,932 base_error: &BaseError,933 ) {934 let Some(ty) = self.diag_metadata.current_type_path else {935 return;936 };937 let TyKind::Path(_, path) = &ty.kind else {938 return;939 };940 for segment in &path.segments {941 let Some(params) = &segment.args else {942 continue;943 };944 let ast::GenericArgs::AngleBracketed(params) = params.deref() else {945 continue;946 };947 for param in ¶ms.args {948 let ast::AngleBracketedArg::Constraint(constraint) = param else {949 continue;950 };951 let ast::AssocItemConstraintKind::Bound { bounds } = &constraint.kind else {952 continue;953 };954 for bound in bounds {955 let ast::GenericBound::Trait(trait_ref) = bound else {956 continue;957 };958 if trait_ref.modifiers == ast::TraitBoundModifiers::NONE959 && base_error.span == trait_ref.span960 {961 err.span_suggestion_verbose(962 constraint.ident.span.between(trait_ref.span),963 "you might have meant to write a path instead of an associated type bound",964 "::",965 Applicability::MachineApplicable,966 );967 }968 }969 }970 }971 }972973 fn suggest_self_or_self_ref(&mut self, err: &mut Diag<'_>, path: &[Segment], span: Span) {974 if !self.self_type_is_available() {975 return;976 }977 let Some(path_last_segment) = path.last() else { return };978 let item_str = path_last_segment.ident;979 // Emit help message for fake-self from other languages (e.g., `this` in JavaScript).980 if ["this", "my"].contains(&item_str.as_str()) {981 err.span_suggestion_short(982 span,983 "you might have meant to use `self` here instead",984 "self",985 Applicability::MaybeIncorrect,986 );987 if !self.self_value_is_available(path[0].ident.span) {988 if let Some((FnKind::Fn(_, _, ast::Fn { sig, .. }), fn_span)) =989 &self.diag_metadata.current_function990 {991 let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {992 (param.span.shrink_to_lo(), "&self, ")993 } else {994 (995 self.r996 .tcx997 .sess998 .source_map()999 .span_through_char(*fn_span, '(')1000 .shrink_to_hi(),1001 "&self",1002 )1003 };1004 err.span_suggestion_verbose(1005 span,1006 "if you meant to use `self`, you are also missing a `self` receiver \1007 argument",1008 sugg,1009 Applicability::MaybeIncorrect,1010 );1011 }1012 }1013 }1014 }10151016 fn try_lookup_name_relaxed(1017 &mut self,1018 err: &mut Diag<'_>,1019 source: PathSource<'_, '_, '_>,1020 path: &[Segment],1021 following_seg: Option<&Segment>,1022 span: Span,1023 res: Option<Res>,1024 base_error: &BaseError,1025 ) -> (bool, FxHashSet<String>, Vec<ImportSuggestion>) {1026 let span = match following_seg {1027 Some(_) if path[0].ident.span.eq_ctxt(path[path.len() - 1].ident.span) => {1028 // The path `span` that comes in includes any following segments, which we don't1029 // want to replace in the suggestions.1030 path[0].ident.span.to(path[path.len() - 1].ident.span)1031 }1032 _ => span,1033 };1034 let mut suggested_candidates = FxHashSet::default();1035 // Try to lookup name in more relaxed fashion for better error reporting.1036 let ident = path.last().unwrap().ident;1037 let is_expected = &|res| source.is_expected(res);1038 let ns = source.namespace();1039 let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));1040 let path_str = Segment::names_to_string(path);1041 let ident_span = path.last().map_or(span, |ident| ident.ident.span);1042 let mut candidates = self1043 .r1044 .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)1045 .into_iter()1046 .filter(|ImportSuggestion { did, .. }| {1047 match (did, res.and_then(|res| res.opt_def_id())) {1048 (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,1049 _ => true,1050 }1051 })1052 .collect::<Vec<_>>();1053 // Try to filter out intrinsics candidates, as long as we have1054 // some other candidates to suggest.1055 let intrinsic_candidates: Vec<_> = candidates1056 .extract_if(.., |sugg| {1057 let path = path_names_to_string(&sugg.path);1058 path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")1059 })1060 .collect();1061 if candidates.is_empty() {1062 // Put them back if we have no more candidates to suggest...1063 candidates = intrinsic_candidates;1064 }1065 let crate_def_id = CRATE_DEF_ID.to_def_id();1066 if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {1067 let mut enum_candidates: Vec<_> = self1068 .r1069 .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)1070 .into_iter()1071 .map(|suggestion| import_candidate_to_enum_paths(&suggestion))1072 .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::"))1073 .collect();1074 if !enum_candidates.is_empty() {1075 enum_candidates.sort();10761077 // Contextualize for E0425 "cannot find type", but don't belabor the point1078 // (that it's a variant) for E0573 "expected type, found variant".1079 let preamble = if res.is_none() {1080 let others = match enum_candidates.len() {1081 1 => String::new(),1082 2 => " and 1 other".to_owned(),1083 n => format!(" and {n} others"),1084 };1085 format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others)1086 } else {1087 String::new()1088 };1089 let msg = format!("{preamble}try using the variant's enum");10901091 suggested_candidates.extend(1092 enum_candidates1093 .iter()1094 .map(|(_variant_path, enum_ty_path)| enum_ty_path.clone()),1095 );1096 err.span_suggestions(1097 span,1098 msg,1099 enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),1100 Applicability::MachineApplicable,1101 );1102 }1103 }11041105 // Try finding a suitable replacement.1106 let typo_sugg = self1107 .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected)1108 .to_opt_suggestion()1109 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));1110 if let [segment] = path1111 && !matches!(source, PathSource::Delegation)1112 && self.self_type_is_available()1113 {1114 if let Some((candidate, def_span)) =1115 self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())1116 {1117 let self_is_available = self.self_value_is_available(segment.ident.span);1118 // Account for `Foo { field }` when suggesting `self.field` so we result on1119 // `Foo { field: self.field }`.1120 let pre = match source {1121 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. }))1122 if expr1123 .fields1124 .iter()1125 .any(|f| f.ident == segment.ident && f.is_shorthand) =>1126 {1127 format!("{path_str}: ")1128 }1129 _ => String::new(),1130 };1131 match candidate {1132 AssocSuggestion::Field => {1133 if self_is_available {1134 if !self.suggest_named_format_argument(1135 err,1136 source,1137 segment.ident,1138 &format!("self.{}", segment.ident),1139 Applicability::MaybeIncorrect,1140 ) {1141 err.span_suggestion_verbose(1142 span.shrink_to_lo(),1143 "you might have meant to use the available field",1144 format!("{pre}self."),1145 Applicability::MaybeIncorrect,1146 );1147 }1148 } else {1149 err.span_label(def_span, "a field by that name exists in `Self`");1150 }1151 }1152 AssocSuggestion::MethodWithSelf { .. }1153 | AssocSuggestion::AssocFn { .. }1154 | AssocSuggestion::AssocType1155 if matches!(1156 source,1157 PathSource::Expr(Some(Expr { kind: ExprKind::FormatArgs(_), .. }))1158 ) =>1159 {1160 let kind = match candidate {1161 AssocSuggestion::MethodWithSelf { .. } => "a method",1162 AssocSuggestion::AssocFn { .. } => "an associated function",1163 AssocSuggestion::AssocType => "an associated type",1164 _ => unreachable!(),1165 };1166 err.span_label(1167 def_span,1168 format!("{kind} by that name is available on `Self` here"),1169 );1170 }1171 AssocSuggestion::MethodWithSelf { called } if self_is_available => {1172 let msg = if called {1173 "you might have meant to call the method"1174 } else {1175 "you might have meant to refer to the method"1176 };1177 err.span_suggestion_verbose(1178 span.shrink_to_lo(),1179 msg,1180 "self.",1181 Applicability::MachineApplicable,1182 );1183 }1184 AssocSuggestion::MethodWithSelf { .. }1185 | AssocSuggestion::AssocFn { .. }1186 | AssocSuggestion::AssocConst1187 | AssocSuggestion::AssocType => {1188 if !matches!(candidate, AssocSuggestion::AssocConst)1189 || !self.suggest_named_format_argument(1190 err,1191 source,1192 segment.ident,1193 &format!("Self::{}", segment.ident),1194 Applicability::MaybeIncorrect,1195 )1196 {1197 err.span_suggestion_verbose(1198 span.shrink_to_lo(),1199 format!("you might have meant to {}", candidate.action()),1200 "Self::",1201 Applicability::MachineApplicable,1202 );1203 }1204 }1205 }1206 self.r.add_typo_suggestion(err, typo_sugg, ident_span, None);1207 return (true, suggested_candidates, candidates);1208 }12091210 // If the first argument in call is `self` suggest calling a method.1211 if let Some((call_span, args_span)) = self.call_has_self_arg(source) {1212 let mut args_snippet = String::new();1213 if let Some(args_span) = args_span1214 && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span)1215 {1216 args_snippet = snippet;1217 }12181219 if let Some(Res::Def(DefKind::Struct, def_id)) = res {1220 if let Some(ctor) = self.r.struct_ctor(def_id)1221 && ctor.has_private_fields(self.parent_scope.module, self.r)1222 {1223 if matches!(1224 ctor.res,1225 Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _)1226 ) {1227 self.update_err_for_private_tuple_struct_fields(err, &source, def_id);1228 }1229 err.note("constructor is not visible here due to private fields");1230 }1231 } else {1232 err.span_suggestion_verbose(1233 call_span,1234 format!("try calling `{ident}` as a method"),1235 format!("self.{path_str}({args_snippet})"),1236 Applicability::MachineApplicable,1237 );1238 }12391240 return (true, suggested_candidates, candidates);1241 }1242 }12431244 // Try context-dependent help if relaxed lookup didn't work.1245 if let Some(res) = res {1246 if self.smart_resolve_context_dependent_help(1247 err,1248 span,1249 source,1250 path,1251 res,1252 &path_str,1253 &base_error.fallback_label,1254 ) {1255 // We do this to avoid losing a secondary span when we override the main error span.1256 self.r.add_typo_suggestion(err, typo_sugg, ident_span, None);1257 return (true, suggested_candidates, candidates);1258 }1259 }12601261 // Try to find in last block rib1262 if let Some(rib) = &self.last_block_rib {1263 for (ident, &res) in &rib.bindings {1264 if let Res::Local(_) = res1265 && path.len() == 11266 && ident.span.eq_ctxt(path[0].ident.span)1267 && ident.name == path[0].ident.name1268 {1269 err.span_help(1270 ident.span,1271 format!("the binding `{path_str}` is available in a different scope in the same function"),1272 );1273 return (true, suggested_candidates, candidates);1274 }1275 }1276 }12771278 if candidates.is_empty() {1279 candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);1280 }12811282 (false, suggested_candidates, candidates)1283 }12841285 fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> {1286 let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| {1287 for resolution in r.resolutions(m).values() {1288 let Some(did) =1289 resolution.borrow(r).best_decl().and_then(|binding| binding.res().opt_def_id())1290 else {1291 continue;1292 };1293 if did.is_local() {1294 // We don't record the doc alias name in the local crate1295 // because the people who write doc alias are usually not1296 // confused by them.1297 continue;1298 }1299 if let Some(d) = find_attr!(r.tcx, did, Doc(d) => d)1300 && d.aliases.contains_key(&item_name)1301 {1302 return Some(did);1303 }1304 }1305 None1306 };13071308 if path.len() == 1 {1309 for rib in self.ribs[ns].iter().rev() {1310 let item = path[0].ident;1311 if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind1312 && let Some(did) = find_doc_alias_name(self.r, module.to_module(), item.name)1313 {1314 return Some((did, item));1315 }1316 }1317 } else {1318 // Finds to the last resolved module item in the path1319 // and searches doc aliases within that module.1320 //1321 // Example: For the path `a::b::last_resolved::not_exist::c::d`,1322 // we will try to find any item has doc aliases named `not_exist`1323 // in `last_resolved` module.1324 //1325 // - Use `skip(1)` because the final segment must remain unresolved.1326 for (idx, seg) in path.iter().enumerate().rev().skip(1) {1327 let Some(id) = seg.id else {1328 continue;1329 };1330 let Some(res) = self.r.partial_res_map.get(&id) else {1331 continue;1332 };1333 if let Res::Def(DefKind::Mod, module) = res.expect_full_res()1334 && let module = self.r.expect_module(module)1335 && let item = path[idx + 1].ident1336 && let Some(did) = find_doc_alias_name(self.r, module, item.name)1337 {1338 return Some((did, item));1339 }1340 break;1341 }1342 }1343 None1344 }13451346 fn suggest_trait_and_bounds(1347 &self,1348 err: &mut Diag<'_>,1349 source: PathSource<'_, '_, '_>,1350 res: Option<Res>,1351 span: Span,1352 base_error: &BaseError,1353 ) -> bool {1354 let is_macro =1355 base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();1356 let mut fallback = false;13571358 if let (1359 PathSource::Trait(AliasPossibility::Maybe),1360 Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),1361 false,1362 ) = (source, res, is_macro)1363 && let Some(bounds @ [first_bound, .., last_bound]) =1364 self.diag_metadata.current_trait_object1365 {1366 fallback = true;1367 let spans: Vec<Span> = bounds1368 .iter()1369 .map(|bound| bound.span())1370 .filter(|&sp| sp != base_error.span)1371 .collect();13721373 let start_span = first_bound.span();1374 // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)1375 let end_span = last_bound.span();1376 // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)1377 let last_bound_span = spans.last().cloned().unwrap();1378 let mut multi_span: MultiSpan = spans.clone().into();1379 for sp in spans {1380 let msg = if sp == last_bound_span {1381 format!(1382 "...because of {these} bound{s}",1383 these = pluralize!("this", bounds.len() - 1),1384 s = pluralize!(bounds.len() - 1),1385 )1386 } else {1387 String::new()1388 };1389 multi_span.push_span_label(sp, msg);1390 }1391 multi_span.push_span_label(base_error.span, "expected this type to be a trait...");1392 err.span_help(1393 multi_span,1394 "`+` is used to constrain a \"trait object\" type with lifetimes or \1395 auto-traits; structs and enums can't be bound in that way",1396 );1397 if bounds.iter().all(|bound| match bound {1398 ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,1399 ast::GenericBound::Trait(tr) => tr.span == base_error.span,1400 }) {1401 let mut sugg = vec![];1402 if base_error.span != start_span {1403 sugg.push((start_span.until(base_error.span), String::new()));1404 }1405 if base_error.span != end_span {1406 sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));1407 }14081409 err.multipart_suggestion(1410 "if you meant to use a type and not a trait here, remove the bounds",1411 sugg,1412 Applicability::MaybeIncorrect,1413 );1414 }1415 }14161417 fallback |= self.restrict_assoc_type_in_where_clause(span, err);1418 fallback1419 }14201421 fn suggest_typo(1422 &mut self,1423 err: &mut Diag<'_>,1424 source: PathSource<'_, 'ast, 'ra>,1425 path: &[Segment],1426 following_seg: Option<&Segment>,1427 span: Span,1428 base_error: &BaseError,1429 suggested_candidates: FxHashSet<String>,1430 ) -> bool {1431 let is_expected = &|res| source.is_expected(res);1432 let ident_span = path.last().map_or(span, |ident| ident.ident.span);14331434 // Prefer suggestions based on associated types from in-scope bounds (e.g. `T::Item`)1435 // over purely edit-distance-based identifier suggestions.1436 // Otherwise suggestions could be verbose.1437 if self.suggest_assoc_type_from_bounds(err, source, path, ident_span) {1438 return false;1439 }14401441 // Preserve the field name for struct field shorthands to avoid suggesting invalid shorthands.1442 let mut prefix = None;1443 if let PathSource::Expr(Some(ast::Expr { kind: ExprKind::Struct(expr), .. })) = source1444 && let Some(ident) = path.last().map(|seg| seg.ident)1445 && expr.fields.iter().any(|f| f.ident == ident && f.is_shorthand)1446 {1447 prefix = Some(ident);1448 }14491450 let typo_sugg =1451 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1452 let mut fallback = true;1453 let typo_sugg = typo_sugg1454 .to_opt_suggestion()1455 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));1456 self.r.add_typo_suggestion(err, typo_sugg, ident_span, prefix);14571458 match self.diag_metadata.current_let_binding {1459 Some((pat_sp, Some(ty_sp), None))1460 if ty_sp.contains(base_error.span) && base_error.could_be_expr =>1461 {1462 err.span_suggestion_verbose(1463 pat_sp.between(ty_sp),1464 "use `=` if you meant to assign",1465 " = ",1466 Applicability::MaybeIncorrect,1467 );1468 }1469 _ => {}1470 }14711472 // If the trait has a single item (which wasn't matched by the algorithm), suggest it1473 let suggestion = self.get_single_associated_item(path, &source, is_expected);1474 self.r.add_typo_suggestion(err, suggestion, ident_span, prefix);14751476 if self.let_binding_suggestion(err, ident_span) {1477 fallback = false;1478 }14791480 fallback1481 }14821483 fn suggest_shadowed(1484 &mut self,1485 err: &mut Diag<'_>,1486 source: PathSource<'_, '_, '_>,1487 path: &[Segment],1488 following_seg: Option<&Segment>,1489 span: Span,1490 ) -> bool {1491 let is_expected = &|res| source.is_expected(res);1492 let typo_sugg =1493 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1494 let is_in_same_file = &|sp1, sp2| {1495 let source_map = self.r.tcx.sess.source_map();1496 let file1 = source_map.span_to_filename(sp1);1497 let file2 = source_map.span_to_filename(sp2);1498 file1 == file21499 };1500 // print 'you might have meant' if the candidate is (1) is a shadowed name with1501 // accessible definition and (2) either defined in the same crate as the typo1502 // (could be in a different file) or introduced in the same file as the typo1503 // (could belong to a different crate)1504 if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg1505 && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))1506 {1507 err.span_label(1508 sugg_span,1509 format!("you might have meant to refer to this {}", res.descr()),1510 );1511 return true;1512 }1513 false1514 }15151516 fn err_code_special_cases(1517 &mut self,1518 err: &mut Diag<'_>,1519 source: PathSource<'_, '_, '_>,1520 path: &[Segment],1521 span: Span,1522 ) {1523 if let Some(err_code) = err.code {1524 if err_code == E0425 {1525 for label_rib in &self.label_ribs {1526 for (label_ident, node_id) in &label_rib.bindings {1527 let ident = path.last().unwrap().ident;1528 if format!("'{ident}") == label_ident.to_string() {1529 err.span_label(label_ident.span, "a label with a similar name exists");1530 if let PathSource::Expr(Some(Expr {1531 kind: ExprKind::Break(None, Some(_)),1532 ..1533 })) = source1534 {1535 err.span_suggestion_verbose(1536 span,1537 "use the similarly named label",1538 label_ident.name,1539 Applicability::MaybeIncorrect,1540 );1541 // Do not lint against unused label when we suggest them.1542 self.diag_metadata.unused_labels.swap_remove(node_id);1543 }1544 }1545 }1546 }15471548 self.suggest_ident_hidden_by_hygiene(err, path, span);1549 // cannot find type in this scope1550 if let Some(correct) = Self::likely_rust_type(path) {1551 err.span_suggestion_verbose(1552 span,1553 format!("you might have intended to use the `{correct}` primitive type"),1554 correct,1555 Applicability::MaybeIncorrect,1556 );1557 }1558 }1559 }1560 }15611562 fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], span: Span) {1563 let [segment] = path else { return };15641565 let ident = segment.ident;1566 let callsite_span = span.source_callsite();1567 for rib in self.ribs[ValueNS].iter().rev() {1568 for (binding_ident, _) in &rib.bindings {1569 // Case 1: the identifier is defined in the same scope as the macro is called1570 if binding_ident.name == ident.name1571 && !binding_ident.span.eq_ctxt(span)1572 && !binding_ident.span.from_expansion()1573 && binding_ident.span.lo() < callsite_span.lo()1574 {1575 err.span_help(1576 binding_ident.span,1577 "an identifier with the same name exists, but is not accessible due to macro hygiene",1578 );1579 return;1580 }15811582 // Case 2: the identifier is defined in a macro call in the same scope1583 if binding_ident.name == ident.name1584 && binding_ident.span.from_expansion()1585 && binding_ident.span.source_callsite().eq_ctxt(callsite_span)1586 && binding_ident.span.source_callsite().lo() < callsite_span.lo()1587 {1588 err.span_help(1589 binding_ident.span,1590 "an identifier with the same name is defined here, but is not accessible due to macro hygiene",1591 );1592 return;1593 }1594 }1595 }1596 }15971598 /// Emit special messages for unresolved `Self` and `self`.1599 fn suggest_self_ty(1600 &self,1601 err: &mut Diag<'_>,1602 source: PathSource<'_, '_, '_>,1603 path: &[Segment],1604 span: Span,1605 ) -> bool {1606 if !is_self_type(path, source.namespace()) {1607 return false;1608 }1609 err.code(E0411);1610 err.span_label(span, "`Self` is only available in impls, traits, and type definitions");1611 if let Some(item) = self.diag_metadata.current_item1612 && let Some(ident) = item.kind.ident()1613 {1614 err.span_label(1615 ident.span,1616 format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1617 );1618 }1619 true1620 }16211622 fn suggest_self_value(1623 &mut self,1624 err: &mut Diag<'_>,1625 source: PathSource<'_, '_, '_>,1626 path: &[Segment],1627 span: Span,1628 ) -> bool {1629 if !is_self_value(path, source.namespace()) {1630 return false;1631 }16321633 debug!("smart_resolve_path_fragment: E0424, source={:?}", source);1634 err.code(E0424);1635 err.span_label(1636 span,1637 match source {1638 PathSource::Pat => {1639 "`self` value is a keyword and may not be bound to variables or shadowed"1640 }1641 _ => "`self` value is a keyword only available in methods with a `self` parameter",1642 },1643 );16441645 // using `let self` is wrong even if we're not in an associated method or if we're in a macro expansion.1646 // So, we should return early if we're in a pattern, see issue #143134.1647 if matches!(source, PathSource::Pat) {1648 return true;1649 }16501651 let is_assoc_fn = self.self_type_is_available();1652 let self_from_macro = "a `self` parameter, but a macro invocation can only \1653 access identifiers it receives from parameters";1654 if let Some((fn_kind, fn_span)) = &self.diag_metadata.current_function {1655 // The current function has a `self` parameter, but we were unable to resolve1656 // a reference to `self`. This can only happen if the `self` identifier we1657 // are resolving came from a different hygiene context or a variable binding.1658 // But variable binding error is returned early above.1659 if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) {1660 err.span_label(*fn_span, format!("this function has {self_from_macro}"));1661 } else {1662 let doesnt = if is_assoc_fn {1663 let (span, sugg) = fn_kind1664 .decl()1665 .inputs1666 .get(0)1667 .map(|p| (p.span.shrink_to_lo(), "&self, "))1668 .unwrap_or_else(|| {1669 // Try to look for the "(" after the function name, if possible.1670 // This avoids placing the suggestion into the visibility specifier.1671 let span = fn_kind1672 .ident()1673 .map_or(*fn_span, |ident| fn_span.with_lo(ident.span.hi()));1674 (1675 self.r1676 .tcx1677 .sess1678 .source_map()1679 .span_through_char(span, '(')1680 .shrink_to_hi(),1681 "&self",1682 )1683 });1684 err.span_suggestion_verbose(1685 span,1686 "add a `self` receiver parameter to make the associated `fn` a method",1687 sugg,1688 Applicability::MaybeIncorrect,1689 );1690 "doesn't"1691 } else {1692 "can't"1693 };1694 if let Some(ident) = fn_kind.ident() {1695 err.span_label(1696 ident.span,1697 format!("this function {doesnt} have a `self` parameter"),1698 );1699 }1700 }1701 } else if let Some(item) = self.diag_metadata.current_item {1702 if matches!(item.kind, ItemKind::Delegation(..)) {1703 err.span_label(item.span, format!("delegation supports {self_from_macro}"));1704 } else {1705 let span = if let Some(ident) = item.kind.ident() { ident.span } else { item.span };1706 err.span_label(1707 span,1708 format!("`self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1709 );1710 }1711 }1712 true1713 }17141715 fn detect_missing_binding_available_from_pattern(1716 &self,1717 err: &mut Diag<'_>,1718 path: &[Segment],1719 following_seg: Option<&Segment>,1720 ) {1721 let [segment] = path else { return };1722 let None = following_seg else { return };1723 for rib in self.ribs[ValueNS].iter().rev() {1724 let patterns_with_skipped_bindings =1725 self.r.tcx.with_stable_hashing_context(|mut hcx| {1726 rib.patterns_with_skipped_bindings.to_sorted(&mut hcx, true)1727 });1728 for (def_id, spans) in patterns_with_skipped_bindings {1729 if let DefKind::Struct | DefKind::Variant = self.r.tcx.def_kind(*def_id)1730 && let Some(fields) = self.r.field_idents(*def_id)1731 {1732 for field in fields {1733 if field.name == segment.ident.name {1734 if spans.iter().all(|(.., had_error)| had_error.is_err()) {1735 // This resolution error will likely be fixed by fixing a1736 // syntax error in a pattern, so it is irrelevant to the user.1737 let multispan: MultiSpan =1738 spans.iter().map(|(s, ..)| *s).collect::<Vec<_>>().into();1739 err.span_note(1740 multispan,1741 "this pattern had a recovered parse error which likely lost \1742 the expected fields",1743 );1744 err.downgrade_to_delayed_bug();1745 }1746 let ty = self.r.tcx.item_name(*def_id);1747 for (span, rest_span, _) in spans {1748 err.span_label(1749 *span,1750 format!(1751 "this pattern doesn't include `{field}`, which is \1752 available in `{ty}`",1753 ),1754 );1755 if let Some(rest_span) = rest_span {1756 err.span_suggestion_verbose(1757 *rest_span,1758 format!("include `{field}` in the pattern"),1759 format!("{field}, .."),1760 Applicability::MaybeIncorrect,1761 );1762 }1763 }1764 }1765 }1766 }1767 }1768 }1769 }17701771 fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) {1772 let Some(pat) = self.diag_metadata.current_pat else { return };1773 let (bound, side, range) = match &pat.kind {1774 ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range),1775 ast::PatKind::Range(None, Some(bound), range) => (bound, Side::End, range),1776 _ => return,1777 };1778 if let ExprKind::Path(None, range_path) = &bound.kind1779 && let [segment] = &range_path.segments[..]1780 && let [s] = path1781 && segment.ident == s.ident1782 && segment.ident.span.eq_ctxt(range.span)1783 {1784 // We've encountered `[first, rest..]` (#88404) or `[first, ..rest]` (#120591)1785 // where the user might have meant `[first, rest @ ..]`.1786 let (span, snippet) = match side {1787 Side::Start => (segment.ident.span.between(range.span), " @ ".into()),1788 Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),1789 };1790 err.subdiagnostic(diagnostics::UnexpectedResUseAtOpInSlicePatWithRangeSugg {1791 span,1792 ident: segment.ident,1793 snippet,1794 });1795 }17961797 enum Side {1798 Start,1799 End,1800 }1801 }18021803 fn suggest_range_struct_destructuring(1804 &mut self,1805 err: &mut Diag<'_>,1806 path: &[Segment],1807 source: PathSource<'_, '_, '_>,1808 ) {1809 if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) {1810 return;1811 }18121813 let Some(pat) = self.diag_metadata.current_pat else { return };1814 let ast::PatKind::Range(start, end, end_kind) = &pat.kind else { return };18151816 let [segment] = path else { return };1817 let failing_span = segment.ident.span;18181819 let in_start = start.as_ref().is_some_and(|e| e.span.contains(failing_span));1820 let in_end = end.as_ref().is_some_and(|e| e.span.contains(failing_span));18211822 if !in_start && !in_end {1823 return;1824 }18251826 let start_snippet =1827 start.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());1828 let end_snippet =1829 end.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());18301831 let field = |name: &str, val: String| {1832 if val == name { val } else { format!("{name}: {val}") }1833 };18341835 let mut resolve_short_name = |short: Symbol, full: &str| -> String {1836 let ident = Ident::with_dummy_span(short);1837 let path = Segment::from_path(&Path::from_ident(ident));18381839 match self.resolve_path(&path, Some(TypeNS), None, PathSource::Type) {1840 PathResult::NonModule(..) => short.to_string(),1841 _ => full.to_string(),1842 }1843 };1844 // FIXME(new_range): Also account for new range types1845 let (struct_path, fields) = match (start_snippet, end_snippet, &end_kind.node) {1846 (Some(start), Some(end), ast::RangeEnd::Excluded) => (1847 resolve_short_name(sym::Range, "std::ops::Range"),1848 vec![field("start", start), field("end", end)],1849 ),1850 (Some(start), Some(end), ast::RangeEnd::Included(_)) => (1851 resolve_short_name(sym::RangeInclusive, "std::ops::RangeInclusive"),1852 vec![field("start", start), field("end", end)],1853 ),1854 (Some(start), None, _) => (1855 resolve_short_name(sym::RangeFrom, "std::ops::RangeFrom"),1856 vec![field("start", start)],1857 ),1858 (None, Some(end), ast::RangeEnd::Excluded) => {1859 (resolve_short_name(sym::RangeTo, "std::ops::RangeTo"), vec![field("end", end)])1860 }1861 (None, Some(end), ast::RangeEnd::Included(_)) => (1862 resolve_short_name(sym::RangeToInclusive, "std::ops::RangeToInclusive"),1863 vec![field("end", end)],1864 ),1865 _ => return,1866 };18671868 err.span_suggestion_verbose(1869 pat.span,1870 format!("if you meant to destructure a range use a struct pattern"),1871 format!("{} {{ {} }}", struct_path, fields.join(", ")),1872 Applicability::MaybeIncorrect,1873 );18741875 err.note(1876 "range patterns match against the start and end of a range; \1877 to bind the components, use a struct pattern",1878 );1879 }18801881 fn suggest_swapping_misplaced_self_ty_and_trait(1882 &mut self,1883 err: &mut Diag<'_>,1884 source: PathSource<'_, 'ast, 'ra>,1885 res: Option<Res>,1886 span: Span,1887 ) {1888 if let Some((trait_ref, self_ty)) = self.diag_metadata.currently_processing_impl_trait1889 && let TyKind::Path(_, self_ty_path) = &self_ty.kind1890 && let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1891 self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source)1892 && module.def_kind() == Some(DefKind::Trait)1893 && trait_ref.path.span == span1894 && let PathSource::Trait(_) = source1895 && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res1896 && let Ok(self_ty_str) = self.r.tcx.sess.source_map().span_to_snippet(self_ty.span)1897 && let Ok(trait_ref_str) =1898 self.r.tcx.sess.source_map().span_to_snippet(trait_ref.path.span)1899 {1900 err.multipart_suggestion(1901 "`impl` items mention the trait being implemented first and the type it is being implemented for second",1902 vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],1903 Applicability::MaybeIncorrect,1904 );1905 }1906 }19071908 /// If the name resolution error occurs in an ident that has no span context, but the enclosing1909 /// item is within a derive macro, point at the derive, as the error can either be because of an1910 /// invalid derive macro *or* a non-existing item that will already have been reported through1911 /// the annotated item.1912 fn detect_resolution_error_in_derive(&self, err: &mut Diag<'_>, span: Span) {1913 if let Some(item) = self.diag_metadata.current_item1914 && !item.span.eq_ctxt(span)1915 && item.span.in_derive_expansion()1916 {1917 // `item` comes from a `#[derive()]`, but the error `span` doesn't, which means that the1918 // derive is referencing a name coming from the annotated item. If the item exists, then1919 // the derive macro itself is buggy. If the item exists, then an error will have already1920 // been emitted while evaluating the annotated item itself, but we can't silence this1921 // one because one *can* write a derive macro referencing a non-existing item that is1922 // part of the expansion, *not* from the user's code. Because of that, at least for now,1923 // we just add context to the current error.1924 err.span_label(item.span, "in this derive macro");1925 }1926 }19271928 fn explain_functions_in_pattern(1929 &self,1930 err: &mut Diag<'_>,1931 res: Option<Res>,1932 source: PathSource<'_, '_, '_>,1933 ) {1934 let PathSource::TupleStruct(_, _) = source else { return };1935 let Some(Res::Def(DefKind::Fn, _)) = res else { return };1936 err.primary_message("expected a pattern, found a function call");1937 err.note("function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>");1938 }19391940 fn suggest_changing_type_to_const_param(1941 &self,1942 err: &mut Diag<'_>,1943 res: Option<Res>,1944 source: PathSource<'_, '_, '_>,1945 path: &[Segment],1946 following_seg: Option<&Segment>,1947 span: Span,1948 ) {1949 if let PathSource::Expr(None) = source1950 && let Some(Res::Def(DefKind::TyParam, _)) = res1951 && following_seg.is_none()1952 && let [segment] = path1953 {1954 // We have something like1955 // impl<T, N> From<[T; N]> for VecWrapper<T> {1956 // fn from(slice: [T; N]) -> Self {1957 // VecWrapper(slice.to_vec())1958 // }1959 // }1960 // where `N` is a type param but should likely have been a const param.1961 let Some(item) = self.diag_metadata.current_item else { return };1962 let Some(generics) = item.kind.generics() else { return };1963 let Some(span) = generics.params.iter().find_map(|param| {1964 // Only consider type params with no bounds.1965 if param.bounds.is_empty() && param.ident.name == segment.ident.name {1966 Some(param.ident.span)1967 } else {1968 None1969 }1970 }) else {1971 return;1972 };1973 err.subdiagnostic(diagnostics::UnexpectedResChangeTyParamToConstParamSugg {1974 before: span.shrink_to_lo(),1975 after: span.shrink_to_hi(),1976 });1977 return;1978 }1979 let PathSource::Trait(_) = source else { return };19801981 // We don't include `DefKind::Str` and `DefKind::AssocTy` as they can't be reached here anyway.1982 let applicability = match res {1983 Some(Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_) | PrimTy::Bool | PrimTy::Char)) => {1984 Applicability::MachineApplicable1985 }1986 // FIXME(const_generics): Add `DefKind::TyParam` and `SelfTyParam` once we support generic1987 // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the1988 // benefits of including them here outweighs the small number of false positives.1989 Some(Res::Def(DefKind::Struct | DefKind::Enum, _))1990 if self.r.features.adt_const_params() || self.r.features.min_adt_const_params() =>1991 {1992 Applicability::MaybeIncorrect1993 }1994 _ => return,1995 };19961997 let Some(item) = self.diag_metadata.current_item else { return };1998 let Some(generics) = item.kind.generics() else { return };19992000 let param = generics.params.iter().find_map(|param| {
Findings
✓ No findings reported for this file.