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_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};15use rustc_data_structures::unord::UnordItems;16use rustc_errors::codes::*;17use rustc_errors::{18 Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize,19 struct_span_code_err,20};21use rustc_hir as hir;22use rustc_hir::def::Namespace::{self, *};23use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds};24use rustc_hir::def_id::{CRATE_DEF_ID, DefId};25use rustc_hir::{MissingLifetimeKind, PrimTy, find_attr};26use rustc_middle::ty;27use rustc_session::{Session, lint};28use rustc_span::edit_distance::{edit_distance, find_best_match_for_name};29use rustc_span::edition::Edition;30use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};31use thin_vec::{ThinVec, thin_vec};32use tracing::debug;3334use super::NoConstantGenericsReason;35use crate::diagnostics::impls::{ImportSuggestion, LabelSuggestion, TypoSuggestion};36use crate::late::{37 AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind,38 LifetimeUseSet, QSelf, RibKind,39};40use crate::ty::fast_reject::SimplifiedType;41use crate::{42 Finalize, Module, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, Resolver,43 ScopeSet, Segment, diagnostics, path_names_to_string,44};4546/// A field or associated item from self type suggested in case of resolution failure.47enum AssocSuggestion {48 Field(Span),49 MethodWithSelf { called: bool },50 AssocFn { called: bool },51 AssocType,52 AssocConst,53}5455impl AssocSuggestion {56 fn action(&self) -> &'static str {57 match self {58 AssocSuggestion::Field(_) => "use the available field",59 AssocSuggestion::MethodWithSelf { called: true } => {60 "call the method with the fully-qualified path"61 }62 AssocSuggestion::MethodWithSelf { called: false } => {63 "refer to the method with the fully-qualified path"64 }65 AssocSuggestion::AssocFn { called: true } => "call the associated function",66 AssocSuggestion::AssocFn { called: false } => "refer to the associated function",67 AssocSuggestion::AssocConst => "use the associated `const`",68 AssocSuggestion::AssocType => "use the associated type",69 }70 }71}7273fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {74 namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper75}7677fn is_self_value(path: &[Segment], namespace: Namespace) -> bool {78 namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower79}8081fn path_to_string_without_assoc_item_bindings(path: &Path) -> String {82 let mut path = path.clone();83 for segment in &mut path.segments {84 let mut remove_args = false;85 if let Some(args) = segment.args.as_deref_mut()86 && let ast::GenericArgs::AngleBracketed(angle_bracketed) = args87 {88 angle_bracketed.args.retain(|arg| matches!(arg, ast::AngleBracketedArg::Arg(_)));89 remove_args = angle_bracketed.args.is_empty();90 }91 if remove_args {92 segment.args = None;93 }94 }95 path_to_string(&path)96}9798/// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.99fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) {100 let variant_path = &suggestion.path;101 let variant_path_string = path_names_to_string(variant_path);102103 let path_len = suggestion.path.segments.len();104 let enum_path = ast::Path {105 span: suggestion.path.span,106 segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(),107 };108 let enum_path_string = path_names_to_string(&enum_path);109110 (variant_path_string, enum_path_string)111}112113/// Description of an elided lifetime.114#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]115pub(super) struct MissingLifetime {116 /// Used to overwrite the resolution with the suggestion, to avoid cascading errors.117 pub id: NodeId,118 /// As we cannot yet emit lints in this crate and have to buffer them instead,119 /// we need to associate each lint with some `NodeId`,120 /// however for some `MissingLifetime`s their `NodeId`s are "fake",121 /// in a sense that they are temporary and not get preserved down the line,122 /// which means that the lints for those nodes will not get emitted.123 /// To combat this, we can try to use some other `NodeId`s as a fallback option.124 pub id_for_lint: NodeId,125 /// Where to suggest adding the lifetime.126 pub span: Span,127 /// How the lifetime was introduced, to have the correct space and comma.128 pub kind: MissingLifetimeKind,129 /// Number of elided lifetimes, used for elision in path.130 pub count: usize,131}132133/// Description of the lifetimes appearing in a function parameter.134/// This is used to provide a literal explanation to the elision failure.135#[derive(Debug)]136pub(super) struct ElisionFnParameter {137 /// The index of the argument in the original definition.138 pub index: usize,139 /// The name of the argument if it's a simple ident.140 pub ident: Option<Ident>,141 /// The number of lifetimes in the parameter.142 pub lifetime_count: usize,143 /// The span of the parameter.144 pub span: Span,145}146147/// Description of lifetimes that appear as candidates for elision.148/// This is used to suggest introducing an explicit lifetime.149#[derive(Clone, Copy, Debug)]150pub(super) enum LifetimeElisionCandidate {151 /// This is not a real lifetime, or it is a named lifetime, in which case we won't suggest anything.152 Ignore,153 Missing(MissingLifetime),154}155156/// Only used for diagnostics.157#[derive(Debug)]158struct BaseError {159 msg: String,160 fallback_label: String,161 span: Span,162 span_label: Option<(Span, &'static str)>,163 could_be_expr: bool,164 suggestion: Option<(Span, &'static str, String)>,165 module: Option<DefId>,166}167168#[derive(Debug)]169enum TypoCandidate {170 Typo(TypoSuggestion),171 Shadowed(Res, Option<Span>),172 None,173}174175impl TypoCandidate {176 fn to_opt_suggestion(self) -> Option<TypoSuggestion> {177 match self {178 TypoCandidate::Typo(sugg) => Some(sugg),179 TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None,180 }181 }182}183184impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {185 fn trait_assoc_type_def_id_by_name(186 &mut self,187 trait_def_id: DefId,188 assoc_name: Symbol,189 ) -> Option<DefId> {190 let module = self.r.get_module(trait_def_id)?;191 self.r.resolutions(module).borrow().iter().find_map(|(key, resolution)| {192 if key.ident.name != assoc_name {193 return None;194 }195 let resolution = resolution.borrow();196 let binding = resolution.best_decl()?;197 match binding.res() {198 Res::Def(DefKind::AssocTy, def_id) => Some(def_id),199 _ => None,200 }201 })202 }203204 /// This does best-effort work to generate suggestions for associated types.205 fn suggest_assoc_type_from_bounds(206 &mut self,207 err: &mut Diag<'_>,208 source: PathSource<'_, 'ast, 'ra>,209 path: &[Segment],210 ident_span: Span,211 ) -> bool {212 // Filter out cases where we cannot emit meaningful suggestions.213 if source.namespace() != TypeNS {214 return false;215 }216 let [segment] = path else { return false };217 if segment.has_generic_args {218 return false;219 }220 if !ident_span.can_be_used_for_suggestions() {221 return false;222 }223 let assoc_name = segment.ident.name;224 if assoc_name == kw::Underscore {225 return false;226 }227228 // Map: type parameter name -> (trait def id -> (assoc type def id, trait paths as written)).229 // We keep a set of paths per trait so we can detect cases like230 // `T: Trait<i32> + Trait<u32>` where suggesting `T::Assoc` would be ambiguous.231 let mut matching_bounds: FxIndexMap<232 Symbol,233 FxIndexMap<DefId, (DefId, FxIndexSet<String>)>,234 > = FxIndexMap::default();235236 let mut record_bound = |this: &mut Self,237 ty_param: Symbol,238 poly_trait_ref: &ast::PolyTraitRef| {239 // Avoid generating suggestions we can't print in a well-formed way.240 if !poly_trait_ref.bound_generic_params.is_empty() {241 return;242 }243 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {244 return;245 }246 let Some(trait_seg) = poly_trait_ref.trait_ref.path.segments.last() else {247 return;248 };249 let Some(partial_res) = this.r.partial_res_map.get(&trait_seg.id) else {250 return;251 };252 let Some(trait_def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) else {253 return;254 };255 let Some(assoc_type_def_id) =256 this.trait_assoc_type_def_id_by_name(trait_def_id, assoc_name)257 else {258 return;259 };260261 // Preserve `::` and generic args so we don't generate broken suggestions like262 // `<T as Foo>::Assoc` for bounds written as `T: ::Foo<'a>`, while stripping263 // associated-item bindings that are rejected in qualified paths.264 let trait_path =265 path_to_string_without_assoc_item_bindings(&poly_trait_ref.trait_ref.path);266 let trait_bounds = matching_bounds.entry(ty_param).or_default();267 let trait_bounds = trait_bounds268 .entry(trait_def_id)269 .or_insert_with(|| (assoc_type_def_id, FxIndexSet::default()));270 debug_assert_eq!(trait_bounds.0, assoc_type_def_id);271 trait_bounds.1.insert(trait_path);272 };273274 let mut record_from_generics = |this: &mut Self, generics: &ast::Generics| {275 for param in &generics.params {276 let ast::GenericParamKind::Type { .. } = param.kind else { continue };277 for bound in ¶m.bounds {278 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };279 record_bound(this, param.ident.name, poly_trait_ref);280 }281 }282283 for predicate in &generics.where_clause.predicates {284 let ast::WherePredicateKind::BoundPredicate(where_bound) = &predicate.kind else {285 continue;286 };287288 let ast::TyKind::Path(None, bounded_path) = &where_bound.bounded_ty.kind else {289 continue;290 };291 let [ast::PathSegment { ident, args: None, .. }] = &bounded_path.segments[..]292 else {293 continue;294 };295296 // Only suggest for bounds that are explicitly on an in-scope type parameter.297 let Some(partial_res) = this.r.partial_res_map.get(&where_bound.bounded_ty.id)298 else {299 continue;300 };301 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {302 continue;303 }304305 for bound in &where_bound.bounds {306 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };307 record_bound(this, ident.name, poly_trait_ref);308 }309 }310 };311312 if let Some(item) = self.diag_metadata.current_item313 && let Some(generics) = item.kind.generics()314 {315 record_from_generics(self, generics);316 }317318 if let Some(item) = self.diag_metadata.current_item319 && matches!(item.kind, ItemKind::Impl(..))320 && let Some(assoc) = self.diag_metadata.current_impl_item321 {322 let generics = match &assoc.kind {323 AssocItemKind::Const(ast::ConstItem { generics, .. })324 | AssocItemKind::Fn(ast::Fn { generics, .. })325 | AssocItemKind::Type(ast::TyAlias { generics, .. }) => Some(generics),326 AssocItemKind::Delegation(..)327 | AssocItemKind::MacCall(..)328 | AssocItemKind::DelegationMac(..) => None,329 };330 if let Some(generics) = generics {331 record_from_generics(self, generics);332 }333 }334335 let mut suggestions: FxIndexSet<String> = FxIndexSet::default();336 for (ty_param, traits) in matching_bounds {337 let ty_param = ty_param.to_ident_string();338 let trait_paths_len: usize = traits.values().map(|(_, paths)| paths.len()).sum();339 if traits.len() == 1 && trait_paths_len == 1 {340 let assoc_type_def_id = traits.values().next().unwrap().0;341 let assoc_segment = format!(342 "{}{}",343 assoc_name,344 self.r.item_required_generic_args_suggestion(assoc_type_def_id)345 );346 suggestions.insert(format!("{ty_param}::{assoc_segment}"));347 } else {348 for (assoc_type_def_id, trait_paths) in traits.into_values() {349 let assoc_segment = format!(350 "{}{}",351 assoc_name,352 self.r.item_required_generic_args_suggestion(assoc_type_def_id)353 );354 for trait_path in trait_paths {355 suggestions356 .insert(format!("<{ty_param} as {trait_path}>::{assoc_segment}"));357 }358 }359 }360 }361362 if suggestions.is_empty() {363 return false;364 }365366 let mut suggestions: Vec<String> = suggestions.into_iter().collect();367 suggestions.sort();368369 err.span_suggestions_with_style(370 ident_span,371 "you might have meant to use an associated type of the same name",372 suggestions,373 Applicability::MaybeIncorrect,374 SuggestionStyle::ShowAlways,375 );376377 true378 }379380 fn make_base_error(381 &mut self,382 path: &[Segment],383 span: Span,384 source: PathSource<'_, 'ast, 'ra>,385 res: Option<Res>,386 ) -> BaseError {387 // Make the base error.388 let mut expected = source.descr_expected();389 let path_str = Segment::names_to_string(path);390 let item_str = path.last().unwrap().ident;391392 if let Some(res) = res {393 BaseError {394 msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),395 fallback_label: format!("not a {expected}"),396 span,397 span_label: match res {398 Res::Def(DefKind::TyParam, def_id) => {399 Some((self.r.def_span(def_id), "found this type parameter"))400 }401 _ => None,402 },403 could_be_expr: match res {404 Res::Def(DefKind::Fn, _) => {405 // Verify whether this is a fn call or an Fn used as a type.406 self.r407 .tcx408 .sess409 .source_map()410 .span_to_snippet(span)411 .is_ok_and(|snippet| snippet.ends_with(')'))412 }413 Res::Def(414 DefKind::Ctor(..)415 | DefKind::AssocFn416 | DefKind::Const { .. }417 | DefKind::AssocConst { .. },418 _,419 )420 | Res::SelfCtor(_)421 | Res::PrimTy(_)422 | Res::Local(_) => true,423 _ => false,424 },425 suggestion: None,426 module: None,427 }428 } else {429 let mut span_label = None;430 let item_ident = path.last().unwrap().ident;431 let item_span = item_ident.span;432 let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {433 debug!(?self.diag_metadata.current_impl_items);434 debug!(?self.diag_metadata.current_function);435 let suggestion = if self.current_trait_ref.is_none()436 && let Some((fn_kind, _)) = self.diag_metadata.current_function437 && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()438 && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = fn_kind439 && let Some(items) = self.diag_metadata.current_impl_items440 && let Some(item) = items.iter().find(|i| {441 i.kind.ident().is_some_and(|ident| {442 // Don't suggest if the item is in Fn signature arguments (#112590).443 ident.name == item_str.name && !sig.span.contains(item_span)444 })445 }) {446 let sp = item_span.shrink_to_lo();447448 // Account for `Foo { field }` when suggesting `self.field` so we result on449 // `Foo { field: self.field }`.450 let field = match source {451 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. })) => {452 expr.fields.iter().find(|f| f.ident == item_ident)453 }454 _ => None,455 };456 let pre = if let Some(field) = field457 && field.is_shorthand458 {459 format!("{item_ident}: ")460 } else {461 String::new()462 };463 // Ensure we provide a structured suggestion for an assoc fn only for464 // expressions that are actually a fn call.465 let is_call = match field {466 Some(ast::ExprField { expr, .. }) => {467 matches!(expr.kind, ExprKind::Call(..))468 }469 _ => matches!(470 source,471 PathSource::Expr(Some(Expr { kind: ExprKind::Call(..), .. })),472 ),473 };474475 match &item.kind {476 AssocItemKind::Fn(fn_)477 if (!sig.decl.has_self() || !is_call) && fn_.sig.decl.has_self() =>478 {479 // Ensure that we only suggest `self.` if `self` is available,480 // you can't call `fn foo(&self)` from `fn bar()` (#115992).481 // We also want to mention that the method exists.482 span_label = Some((483 fn_.ident.span,484 "a method by that name is available on `Self` here",485 ));486 None487 }488 AssocItemKind::Fn(fn_) if !fn_.sig.decl.has_self() && !is_call => {489 span_label = Some((490 fn_.ident.span,491 "an associated function by that name is available on `Self` here",492 ));493 None494 }495 AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => {496 Some((sp, "consider using the method on `Self`", format!("{pre}self.")))497 }498 AssocItemKind::Fn(_) => Some((499 sp,500 "consider using the associated function on `Self`",501 format!("{pre}Self::"),502 )),503 AssocItemKind::Const(..) => Some((504 sp,505 "consider using the associated constant on `Self`",506 format!("{pre}Self::"),507 )),508 _ => None,509 }510 } else {511 None512 };513 (String::new(), "this scope".to_string(), None, suggestion)514 } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {515 if self.r.tcx.sess.edition() > Edition::Edition2015 {516 // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude517 // which overrides all other expectations of item type518 expected = "crate";519 (String::new(), "the list of imported crates".to_string(), None, None)520 } else {521 (522 String::new(),523 "the crate root".to_string(),524 Some(CRATE_DEF_ID.to_def_id()),525 None,526 )527 }528 } else if path.len() == 2 && path[0].ident.name == kw::Crate {529 (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None)530 } else {531 let mod_path = &path[..path.len() - 1];532 let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source);533 let mod_prefix = match mod_res {534 PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),535 _ => None,536 };537538 let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);539540 let mod_prefix =541 mod_prefix.map_or_else(String::new, |res| format!("{} ", res.descr()));542 (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None)543 };544545 let (fallback_label, suggestion) = if path_str == "async"546 && expected.starts_with("struct")547 {548 ("`async` blocks are only allowed in Rust 2018 or later".to_string(), suggestion)549 } else {550 // check if we are in situation of typo like `True` instead of `true`.551 let override_suggestion =552 if ["true", "false"].contains(&item_str.to_string().to_lowercase().as_str()) {553 let item_typo = item_str.to_string().to_lowercase();554 Some((item_span, "you may want to use a bool value instead", item_typo))555 // FIXME(vincenzopalazzo): make the check smarter,556 // and maybe expand with levenshtein distance checks557 } else if item_str.as_str() == "printf" {558 Some((559 item_span,560 "you may have meant to use the `print` macro",561 "print!".to_owned(),562 ))563 } else {564 suggestion565 };566 (format!("not found in {mod_str}"), override_suggestion)567 };568569 BaseError {570 msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),571 fallback_label,572 span: item_span,573 span_label,574 could_be_expr: false,575 suggestion,576 module,577 }578 }579 }580581 /// Try to suggest for a module path that cannot be resolved.582 /// Such as `fmt::Debug` where `fmt` is not resolved without importing,583 /// here we search with `lookup_import_candidates` for a module named `fmt`584 /// with `TypeNS` as namespace.585 ///586 /// We need a separate function here because we won't suggest for a path with single segment587 /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`588 pub(crate) fn smart_resolve_partial_mod_path_errors(589 &mut self,590 prefix_path: &[Segment],591 following_seg: Option<&Segment>,592 ) -> Vec<ImportSuggestion> {593 if let Some(segment) = prefix_path.last()594 && let Some(following_seg) = following_seg595 {596 let candidates = self.r.lookup_import_candidates(597 segment.ident,598 Namespace::TypeNS,599 &self.parent_scope,600 &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),601 );602 // double check next seg is valid603 candidates604 .into_iter()605 .filter(|candidate| {606 if let Some(def_id) = candidate.did607 && let Some(module) = self.r.get_module(def_id)608 {609 Some(def_id) != self.parent_scope.module.opt_def_id()610 && self611 .r612 .resolutions(module)613 .borrow()614 .iter()615 .any(|(key, _r)| key.ident.name == following_seg.ident.name)616 } else {617 false618 }619 })620 .collect::<Vec<_>>()621 } else {622 Vec::new()623 }624 }625626 /// Handles error reporting for `smart_resolve_path_fragment` function.627 /// Creates base error and amends it with one short label and possibly some longer helps/notes.628 #[tracing::instrument(skip(self), level = "debug")]629 pub(crate) fn smart_resolve_report_errors(630 &mut self,631 path: &[Segment],632 following_seg: Option<&Segment>,633 span: Span,634 source: PathSource<'_, 'ast, 'ra>,635 res: Option<Res>,636 qself: Option<&QSelf>,637 ) -> (Diag<'tcx>, Vec<ImportSuggestion>) {638 debug!(?res, ?source);639 let base_error = self.make_base_error(path, span, source, res);640641 let code = source.error_code(res.is_some());642 let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());643 err.code(code);644645 // Try to get the span of the identifier within the path's syntax context646 // (if that's different).647 if let Some(within_macro_span) =648 base_error.span.within_macro(span, self.r.tcx.sess.source_map())649 {650 err.span_label(within_macro_span, "due to this macro variable");651 }652653 self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);654 self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);655 self.suggest_range_struct_destructuring(&mut err, path, source);656 self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);657658 if let Some((span, label)) = base_error.span_label {659 err.span_label(span, label);660 }661662 if let Some(ref sugg) = base_error.suggestion {663 err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);664 }665666 self.suggest_changing_type_to_const_param(&mut err, res, source, path, following_seg, span);667 self.explain_functions_in_pattern(&mut err, res, source);668669 if self.suggest_pattern_match_with_let(&mut err, source, span) {670 // Fallback label.671 err.span_label(base_error.span, base_error.fallback_label);672 return (err, Vec::new());673 }674675 self.suggest_self_or_self_ref(&mut err, path, span);676 self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error);677 self.detect_rtn_with_fully_qualified_path(678 &mut err,679 path,680 following_seg,681 span,682 source,683 res,684 qself,685 );686 if self.suggest_self_ty(&mut err, source, path, span)687 || self.suggest_self_value(&mut err, source, path, span)688 {689 return (err, Vec::new());690 }691692 if let Some((did, item)) = self.lookup_doc_alias_name(path, source.namespace()) {693 let item_name = item.name;694 let suggestion_name = self.r.tcx.item_name(did);695 err.span_suggestion(696 item.span,697 format!("`{suggestion_name}` has a name defined in the doc alias attribute as `{item_name}`"),698 suggestion_name,699 Applicability::MaybeIncorrect700 );701702 return (err, Vec::new());703 };704705 let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed(706 &mut err,707 source,708 path,709 following_seg,710 span,711 res,712 &base_error,713 );714 if found {715 return (err, candidates);716 }717718 if self.suggest_shadowed(&mut err, source, path, following_seg, span) {719 // if there is already a shadowed name, don'suggest candidates for importing720 candidates.clear();721 }722723 let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);724 fallback |= self.suggest_typo(725 &mut err,726 source,727 path,728 following_seg,729 span,730 &base_error,731 suggested_candidates,732 );733734 if fallback {735 // Fallback label.736 err.span_label(base_error.span, base_error.fallback_label);737 }738 self.err_code_special_cases(&mut err, source, path, span);739740 let module = base_error.module.unwrap_or_else(|| CRATE_DEF_ID.to_def_id());741 self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);742743 (err, candidates)744 }745746 fn detect_rtn_with_fully_qualified_path(747 &self,748 err: &mut Diag<'_>,749 path: &[Segment],750 following_seg: Option<&Segment>,751 span: Span,752 source: PathSource<'_, '_, '_>,753 res: Option<Res>,754 qself: Option<&QSelf>,755 ) {756 if let Some(Res::Def(DefKind::AssocFn, _)) = res757 && let PathSource::TraitItem(TypeNS, _) = source758 && let None = following_seg759 && let Some(qself) = qself760 && let TyKind::Path(None, ty_path) = &qself.ty.kind761 && ty_path.segments.len() == 1762 && self.diag_metadata.current_where_predicate.is_some()763 {764 err.span_suggestion_verbose(765 span,766 "you might have meant to use the return type notation syntax",767 format!("{}::{}(..)", ty_path.segments[0].ident, path[path.len() - 1].ident),768 Applicability::MaybeIncorrect,769 );770 }771 }772773 fn detect_assoc_type_constraint_meant_as_path(774 &self,775 err: &mut Diag<'_>,776 base_error: &BaseError,777 ) {778 let Some(ty) = self.diag_metadata.current_type_path else {779 return;780 };781 let TyKind::Path(_, path) = &ty.kind else {782 return;783 };784 for segment in &path.segments {785 let Some(params) = &segment.args else {786 continue;787 };788 let ast::GenericArgs::AngleBracketed(params) = params.deref() else {789 continue;790 };791 for param in ¶ms.args {792 let ast::AngleBracketedArg::Constraint(constraint) = param else {793 continue;794 };795 let ast::AssocItemConstraintKind::Bound { bounds } = &constraint.kind else {796 continue;797 };798 for bound in bounds {799 let ast::GenericBound::Trait(trait_ref) = bound else {800 continue;801 };802 if trait_ref.modifiers == ast::TraitBoundModifiers::NONE803 && base_error.span == trait_ref.span804 {805 err.span_suggestion_verbose(806 constraint.ident.span.between(trait_ref.span),807 "you might have meant to write a path instead of an associated type bound",808 "::",809 Applicability::MachineApplicable,810 );811 }812 }813 }814 }815 }816817 fn suggest_self_or_self_ref(&mut self, err: &mut Diag<'_>, path: &[Segment], span: Span) {818 if !self.self_type_is_available() {819 return;820 }821 let Some(path_last_segment) = path.last() else { return };822 let item_str = path_last_segment.ident;823 // Emit help message for fake-self from other languages (e.g., `this` in JavaScript).824 if ["this", "my"].contains(&item_str.as_str()) {825 err.span_suggestion_short(826 span,827 "you might have meant to use `self` here instead",828 "self",829 Applicability::MaybeIncorrect,830 );831 if !self.self_value_is_available(path[0].ident.span) {832 if let Some((FnKind::Fn(_, _, ast::Fn { sig, .. }), fn_span)) =833 &self.diag_metadata.current_function834 {835 let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {836 (param.span.shrink_to_lo(), "&self, ")837 } else {838 (839 self.r840 .tcx841 .sess842 .source_map()843 .span_through_char(*fn_span, '(')844 .shrink_to_hi(),845 "&self",846 )847 };848 err.span_suggestion_verbose(849 span,850 "if you meant to use `self`, you are also missing a `self` receiver \851 argument",852 sugg,853 Applicability::MaybeIncorrect,854 );855 }856 }857 }858 }859860 fn try_lookup_name_relaxed(861 &mut self,862 err: &mut Diag<'_>,863 source: PathSource<'_, '_, '_>,864 path: &[Segment],865 following_seg: Option<&Segment>,866 span: Span,867 res: Option<Res>,868 base_error: &BaseError,869 ) -> (bool, FxHashSet<String>, Vec<ImportSuggestion>) {870 let span = match following_seg {871 Some(_) if path[0].ident.span.eq_ctxt(path[path.len() - 1].ident.span) => {872 // The path `span` that comes in includes any following segments, which we don't873 // want to replace in the suggestions.874 path[0].ident.span.to(path[path.len() - 1].ident.span)875 }876 _ => span,877 };878 let mut suggested_candidates = FxHashSet::default();879 // Try to lookup name in more relaxed fashion for better error reporting.880 let ident = path.last().unwrap().ident;881 let is_expected = &|res| source.is_expected(res);882 let ns = source.namespace();883 let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));884 let path_str = Segment::names_to_string(path);885 let ident_span = path.last().map_or(span, |ident| ident.ident.span);886 let mut candidates = self887 .r888 .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)889 .into_iter()890 .filter(|ImportSuggestion { did, .. }| {891 match (did, res.and_then(|res| res.opt_def_id())) {892 (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,893 _ => true,894 }895 })896 .collect::<Vec<_>>();897 // Try to filter out intrinsics candidates, as long as we have898 // some other candidates to suggest.899 let intrinsic_candidates: Vec<_> = candidates900 .extract_if(.., |sugg| {901 let path = path_names_to_string(&sugg.path);902 path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")903 })904 .collect();905 if candidates.is_empty() {906 // Put them back if we have no more candidates to suggest...907 candidates = intrinsic_candidates;908 }909 let crate_def_id = CRATE_DEF_ID.to_def_id();910 if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {911 let mut enum_candidates: Vec<_> = self912 .r913 .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)914 .into_iter()915 .map(|suggestion| import_candidate_to_enum_paths(&suggestion))916 .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::"))917 .collect();918 if !enum_candidates.is_empty() {919 enum_candidates.sort();920921 // Contextualize for E0425 "cannot find type", but don't belabor the point922 // (that it's a variant) for E0573 "expected type, found variant".923 let preamble = if res.is_none() {924 let others = match enum_candidates.len() {925 1 => String::new(),926 2 => " and 1 other".to_owned(),927 n => format!(" and {n} others"),928 };929 format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others)930 } else {931 String::new()932 };933 let msg = format!("{preamble}try using the variant's enum");934935 suggested_candidates.extend(936 enum_candidates937 .iter()938 .map(|(_variant_path, enum_ty_path)| enum_ty_path.clone()),939 );940 err.span_suggestions(941 span,942 msg,943 enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),944 Applicability::MachineApplicable,945 );946 }947 }948949 // Try finding a suitable replacement.950 let typo_sugg = self951 .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected)952 .to_opt_suggestion()953 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));954 if let [segment] = path955 && !matches!(source, PathSource::Delegation)956 && self.self_type_is_available()957 {958 if let Some(candidate) =959 self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())960 {961 let self_is_available = self.self_value_is_available(segment.ident.span);962 // Account for `Foo { field }` when suggesting `self.field` so we result on963 // `Foo { field: self.field }`.964 let pre = match source {965 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. }))966 if expr967 .fields968 .iter()969 .any(|f| f.ident == segment.ident && f.is_shorthand) =>970 {971 format!("{path_str}: ")972 }973 _ => String::new(),974 };975 match candidate {976 AssocSuggestion::Field(field_span) => {977 if self_is_available {978 let source_map = self.r.tcx.sess.source_map();979 let field_is_format_named_arg = matches!(980 span.desugaring_kind(),981 Some(DesugaringKind::FormatLiteral { .. })982 ) && source_map983 .span_to_source(span, |s, start, _| {984 Ok(s.get(start.saturating_sub(1)..start) == Some("{"))985 })986 .unwrap_or(false);987 if field_is_format_named_arg {988 err.help(989 format!("you might have meant to use the available field in a format string: `\"{{}}\", self.{}`", segment.ident.name),990 );991 } else {992 err.span_suggestion_verbose(993 span.shrink_to_lo(),994 "you might have meant to use the available field",995 format!("{pre}self."),996 Applicability::MaybeIncorrect,997 );998 }999 } else {1000 err.span_label(field_span, "a field by that name exists in `Self`");1001 }1002 }1003 AssocSuggestion::MethodWithSelf { called } if self_is_available => {1004 let msg = if called {1005 "you might have meant to call the method"1006 } else {1007 "you might have meant to refer to the method"1008 };1009 err.span_suggestion_verbose(1010 span.shrink_to_lo(),1011 msg,1012 "self.",1013 Applicability::MachineApplicable,1014 );1015 }1016 AssocSuggestion::MethodWithSelf { .. }1017 | AssocSuggestion::AssocFn { .. }1018 | AssocSuggestion::AssocConst1019 | AssocSuggestion::AssocType => {1020 err.span_suggestion_verbose(1021 span.shrink_to_lo(),1022 format!("you might have meant to {}", candidate.action()),1023 "Self::",1024 Applicability::MachineApplicable,1025 );1026 }1027 }1028 self.r.add_typo_suggestion(err, typo_sugg, ident_span);1029 return (true, suggested_candidates, candidates);1030 }10311032 // If the first argument in call is `self` suggest calling a method.1033 if let Some((call_span, args_span)) = self.call_has_self_arg(source) {1034 let mut args_snippet = String::new();1035 if let Some(args_span) = args_span1036 && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span)1037 {1038 args_snippet = snippet;1039 }10401041 if let Some(Res::Def(DefKind::Struct, def_id)) = res {1042 if let Some(ctor) = self.r.struct_ctor(def_id)1043 && ctor.has_private_fields(self.parent_scope.module, self.r)1044 {1045 if matches!(1046 ctor.res,1047 Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _)1048 ) {1049 self.update_err_for_private_tuple_struct_fields(err, &source, def_id);1050 }1051 err.note("constructor is not visible here due to private fields");1052 }1053 } else {1054 err.span_suggestion(1055 call_span,1056 format!("try calling `{ident}` as a method"),1057 format!("self.{path_str}({args_snippet})"),1058 Applicability::MachineApplicable,1059 );1060 }10611062 return (true, suggested_candidates, candidates);1063 }1064 }10651066 // Try context-dependent help if relaxed lookup didn't work.1067 if let Some(res) = res {1068 if self.smart_resolve_context_dependent_help(1069 err,1070 span,1071 source,1072 path,1073 res,1074 &path_str,1075 &base_error.fallback_label,1076 ) {1077 // We do this to avoid losing a secondary span when we override the main error span.1078 self.r.add_typo_suggestion(err, typo_sugg, ident_span);1079 return (true, suggested_candidates, candidates);1080 }1081 }10821083 // Try to find in last block rib1084 if let Some(rib) = &self.last_block_rib {1085 for (ident, &res) in &rib.bindings {1086 if let Res::Local(_) = res1087 && path.len() == 11088 && ident.span.eq_ctxt(path[0].ident.span)1089 && ident.name == path[0].ident.name1090 {1091 err.span_help(1092 ident.span,1093 format!("the binding `{path_str}` is available in a different scope in the same function"),1094 );1095 return (true, suggested_candidates, candidates);1096 }1097 }1098 }10991100 if candidates.is_empty() {1101 candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);1102 }11031104 (false, suggested_candidates, candidates)1105 }11061107 fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> {1108 let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| {1109 for resolution in r.resolutions(m).borrow().values() {1110 let Some(did) =1111 resolution.borrow().best_decl().and_then(|binding| binding.res().opt_def_id())1112 else {1113 continue;1114 };1115 if did.is_local() {1116 // We don't record the doc alias name in the local crate1117 // because the people who write doc alias are usually not1118 // confused by them.1119 continue;1120 }1121 if let Some(d) = hir::find_attr!(r.tcx, did, Doc(d) => d)1122 && d.aliases.contains_key(&item_name)1123 {1124 return Some(did);1125 }1126 }1127 None1128 };11291130 if path.len() == 1 {1131 for rib in self.ribs[ns].iter().rev() {1132 let item = path[0].ident;1133 if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind1134 && let Some(did) = find_doc_alias_name(self.r, module.to_module(), item.name)1135 {1136 return Some((did, item));1137 }1138 }1139 } else {1140 // Finds to the last resolved module item in the path1141 // and searches doc aliases within that module.1142 //1143 // Example: For the path `a::b::last_resolved::not_exist::c::d`,1144 // we will try to find any item has doc aliases named `not_exist`1145 // in `last_resolved` module.1146 //1147 // - Use `skip(1)` because the final segment must remain unresolved.1148 for (idx, seg) in path.iter().enumerate().rev().skip(1) {1149 let Some(id) = seg.id else {1150 continue;1151 };1152 let Some(res) = self.r.partial_res_map.get(&id) else {1153 continue;1154 };1155 if let Res::Def(DefKind::Mod, module) = res.expect_full_res()1156 && let module = self.r.expect_module(module)1157 && let item = path[idx + 1].ident1158 && let Some(did) = find_doc_alias_name(self.r, module, item.name)1159 {1160 return Some((did, item));1161 }1162 break;1163 }1164 }1165 None1166 }11671168 fn suggest_trait_and_bounds(1169 &self,1170 err: &mut Diag<'_>,1171 source: PathSource<'_, '_, '_>,1172 res: Option<Res>,1173 span: Span,1174 base_error: &BaseError,1175 ) -> bool {1176 let is_macro =1177 base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();1178 let mut fallback = false;11791180 if let (1181 PathSource::Trait(AliasPossibility::Maybe),1182 Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),1183 false,1184 ) = (source, res, is_macro)1185 && let Some(bounds @ [first_bound, .., last_bound]) =1186 self.diag_metadata.current_trait_object1187 {1188 fallback = true;1189 let spans: Vec<Span> = bounds1190 .iter()1191 .map(|bound| bound.span())1192 .filter(|&sp| sp != base_error.span)1193 .collect();11941195 let start_span = first_bound.span();1196 // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)1197 let end_span = last_bound.span();1198 // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)1199 let last_bound_span = spans.last().cloned().unwrap();1200 let mut multi_span: MultiSpan = spans.clone().into();1201 for sp in spans {1202 let msg = if sp == last_bound_span {1203 format!(1204 "...because of {these} bound{s}",1205 these = pluralize!("this", bounds.len() - 1),1206 s = pluralize!(bounds.len() - 1),1207 )1208 } else {1209 String::new()1210 };1211 multi_span.push_span_label(sp, msg);1212 }1213 multi_span.push_span_label(base_error.span, "expected this type to be a trait...");1214 err.span_help(1215 multi_span,1216 "`+` is used to constrain a \"trait object\" type with lifetimes or \1217 auto-traits; structs and enums can't be bound in that way",1218 );1219 if bounds.iter().all(|bound| match bound {1220 ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,1221 ast::GenericBound::Trait(tr) => tr.span == base_error.span,1222 }) {1223 let mut sugg = vec![];1224 if base_error.span != start_span {1225 sugg.push((start_span.until(base_error.span), String::new()));1226 }1227 if base_error.span != end_span {1228 sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));1229 }12301231 err.multipart_suggestion(1232 "if you meant to use a type and not a trait here, remove the bounds",1233 sugg,1234 Applicability::MaybeIncorrect,1235 );1236 }1237 }12381239 fallback |= self.restrict_assoc_type_in_where_clause(span, err);1240 fallback1241 }12421243 fn suggest_typo(1244 &mut self,1245 err: &mut Diag<'_>,1246 source: PathSource<'_, 'ast, 'ra>,1247 path: &[Segment],1248 following_seg: Option<&Segment>,1249 span: Span,1250 base_error: &BaseError,1251 suggested_candidates: FxHashSet<String>,1252 ) -> bool {1253 let is_expected = &|res| source.is_expected(res);1254 let ident_span = path.last().map_or(span, |ident| ident.ident.span);12551256 // Prefer suggestions based on associated types from in-scope bounds (e.g. `T::Item`)1257 // over purely edit-distance-based identifier suggestions.1258 // Otherwise suggestions could be verbose.1259 if self.suggest_assoc_type_from_bounds(err, source, path, ident_span) {1260 return false;1261 }12621263 let typo_sugg =1264 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1265 let mut fallback = false;1266 let typo_sugg = typo_sugg1267 .to_opt_suggestion()1268 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));1269 if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {1270 fallback = true;1271 match self.diag_metadata.current_let_binding {1272 Some((pat_sp, Some(ty_sp), None))1273 if ty_sp.contains(base_error.span) && base_error.could_be_expr =>1274 {1275 err.span_suggestion_verbose(1276 pat_sp.between(ty_sp),1277 "use `=` if you meant to assign",1278 " = ",1279 Applicability::MaybeIncorrect,1280 );1281 }1282 _ => {}1283 }12841285 // If the trait has a single item (which wasn't matched by the algorithm), suggest it1286 let suggestion = self.get_single_associated_item(path, &source, is_expected);1287 self.r.add_typo_suggestion(err, suggestion, ident_span);1288 }12891290 if self.let_binding_suggestion(err, ident_span) {1291 fallback = false;1292 }12931294 fallback1295 }12961297 fn suggest_shadowed(1298 &mut self,1299 err: &mut Diag<'_>,1300 source: PathSource<'_, '_, '_>,1301 path: &[Segment],1302 following_seg: Option<&Segment>,1303 span: Span,1304 ) -> bool {1305 let is_expected = &|res| source.is_expected(res);1306 let typo_sugg =1307 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1308 let is_in_same_file = &|sp1, sp2| {1309 let source_map = self.r.tcx.sess.source_map();1310 let file1 = source_map.span_to_filename(sp1);1311 let file2 = source_map.span_to_filename(sp2);1312 file1 == file21313 };1314 // print 'you might have meant' if the candidate is (1) is a shadowed name with1315 // accessible definition and (2) either defined in the same crate as the typo1316 // (could be in a different file) or introduced in the same file as the typo1317 // (could belong to a different crate)1318 if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg1319 && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))1320 {1321 err.span_label(1322 sugg_span,1323 format!("you might have meant to refer to this {}", res.descr()),1324 );1325 return true;1326 }1327 false1328 }13291330 fn err_code_special_cases(1331 &mut self,1332 err: &mut Diag<'_>,1333 source: PathSource<'_, '_, '_>,1334 path: &[Segment],1335 span: Span,1336 ) {1337 if let Some(err_code) = err.code {1338 if err_code == E0425 {1339 for label_rib in &self.label_ribs {1340 for (label_ident, node_id) in &label_rib.bindings {1341 let ident = path.last().unwrap().ident;1342 if format!("'{ident}") == label_ident.to_string() {1343 err.span_label(label_ident.span, "a label with a similar name exists");1344 if let PathSource::Expr(Some(Expr {1345 kind: ExprKind::Break(None, Some(_)),1346 ..1347 })) = source1348 {1349 err.span_suggestion(1350 span,1351 "use the similarly named label",1352 label_ident.name,1353 Applicability::MaybeIncorrect,1354 );1355 // Do not lint against unused label when we suggest them.1356 self.diag_metadata.unused_labels.swap_remove(node_id);1357 }1358 }1359 }1360 }13611362 self.suggest_ident_hidden_by_hygiene(err, path, span);1363 // cannot find type in this scope1364 if let Some(correct) = Self::likely_rust_type(path) {1365 err.span_suggestion(1366 span,1367 "perhaps you intended to use this type",1368 correct,1369 Applicability::MaybeIncorrect,1370 );1371 }1372 }1373 }1374 }13751376 fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], span: Span) {1377 let [segment] = path else { return };13781379 let ident = segment.ident;1380 let callsite_span = span.source_callsite();1381 for rib in self.ribs[ValueNS].iter().rev() {1382 for (binding_ident, _) in &rib.bindings {1383 // Case 1: the identifier is defined in the same scope as the macro is called1384 if binding_ident.name == ident.name1385 && !binding_ident.span.eq_ctxt(span)1386 && !binding_ident.span.from_expansion()1387 && binding_ident.span.lo() < callsite_span.lo()1388 {1389 err.span_help(1390 binding_ident.span,1391 "an identifier with the same name exists, but is not accessible due to macro hygiene",1392 );1393 return;1394 }13951396 // Case 2: the identifier is defined in a macro call in the same scope1397 if binding_ident.name == ident.name1398 && binding_ident.span.from_expansion()1399 && binding_ident.span.source_callsite().eq_ctxt(callsite_span)1400 && binding_ident.span.source_callsite().lo() < callsite_span.lo()1401 {1402 err.span_help(1403 binding_ident.span,1404 "an identifier with the same name is defined here, but is not accessible due to macro hygiene",1405 );1406 return;1407 }1408 }1409 }1410 }14111412 /// Emit special messages for unresolved `Self` and `self`.1413 fn suggest_self_ty(1414 &self,1415 err: &mut Diag<'_>,1416 source: PathSource<'_, '_, '_>,1417 path: &[Segment],1418 span: Span,1419 ) -> bool {1420 if !is_self_type(path, source.namespace()) {1421 return false;1422 }1423 err.code(E0411);1424 err.span_label(span, "`Self` is only available in impls, traits, and type definitions");1425 if let Some(item) = self.diag_metadata.current_item1426 && let Some(ident) = item.kind.ident()1427 {1428 err.span_label(1429 ident.span,1430 format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1431 );1432 }1433 true1434 }14351436 fn suggest_self_value(1437 &mut self,1438 err: &mut Diag<'_>,1439 source: PathSource<'_, '_, '_>,1440 path: &[Segment],1441 span: Span,1442 ) -> bool {1443 if !is_self_value(path, source.namespace()) {1444 return false;1445 }14461447 debug!("smart_resolve_path_fragment: E0424, source={:?}", source);1448 err.code(E0424);1449 err.span_label(1450 span,1451 match source {1452 PathSource::Pat => {1453 "`self` value is a keyword and may not be bound to variables or shadowed"1454 }1455 _ => "`self` value is a keyword only available in methods with a `self` parameter",1456 },1457 );14581459 // using `let self` is wrong even if we're not in an associated method or if we're in a macro expansion.1460 // So, we should return early if we're in a pattern, see issue #143134.1461 if matches!(source, PathSource::Pat) {1462 return true;1463 }14641465 let is_assoc_fn = self.self_type_is_available();1466 let self_from_macro = "a `self` parameter, but a macro invocation can only \1467 access identifiers it receives from parameters";1468 if let Some((fn_kind, fn_span)) = &self.diag_metadata.current_function {1469 // The current function has a `self` parameter, but we were unable to resolve1470 // a reference to `self`. This can only happen if the `self` identifier we1471 // are resolving came from a different hygiene context or a variable binding.1472 // But variable binding error is returned early above.1473 if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) {1474 err.span_label(*fn_span, format!("this function has {self_from_macro}"));1475 } else {1476 let doesnt = if is_assoc_fn {1477 let (span, sugg) = fn_kind1478 .decl()1479 .inputs1480 .get(0)1481 .map(|p| (p.span.shrink_to_lo(), "&self, "))1482 .unwrap_or_else(|| {1483 // Try to look for the "(" after the function name, if possible.1484 // This avoids placing the suggestion into the visibility specifier.1485 let span = fn_kind1486 .ident()1487 .map_or(*fn_span, |ident| fn_span.with_lo(ident.span.hi()));1488 (1489 self.r1490 .tcx1491 .sess1492 .source_map()1493 .span_through_char(span, '(')1494 .shrink_to_hi(),1495 "&self",1496 )1497 });1498 err.span_suggestion_verbose(1499 span,1500 "add a `self` receiver parameter to make the associated `fn` a method",1501 sugg,1502 Applicability::MaybeIncorrect,1503 );1504 "doesn't"1505 } else {1506 "can't"1507 };1508 if let Some(ident) = fn_kind.ident() {1509 err.span_label(1510 ident.span,1511 format!("this function {doesnt} have a `self` parameter"),1512 );1513 }1514 }1515 } else if let Some(item) = self.diag_metadata.current_item {1516 if matches!(item.kind, ItemKind::Delegation(..)) {1517 err.span_label(item.span, format!("delegation supports {self_from_macro}"));1518 } else {1519 let span = if let Some(ident) = item.kind.ident() { ident.span } else { item.span };1520 err.span_label(1521 span,1522 format!("`self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1523 );1524 }1525 }1526 true1527 }15281529 fn detect_missing_binding_available_from_pattern(1530 &self,1531 err: &mut Diag<'_>,1532 path: &[Segment],1533 following_seg: Option<&Segment>,1534 ) {1535 let [segment] = path else { return };1536 let None = following_seg else { return };1537 for rib in self.ribs[ValueNS].iter().rev() {1538 let patterns_with_skipped_bindings =1539 self.r.tcx.with_stable_hashing_context(|mut hcx| {1540 rib.patterns_with_skipped_bindings.to_sorted(&mut hcx, true)1541 });1542 for (def_id, spans) in patterns_with_skipped_bindings {1543 if let DefKind::Struct | DefKind::Variant = self.r.tcx.def_kind(*def_id)1544 && let Some(fields) = self.r.field_idents(*def_id)1545 {1546 for field in fields {1547 if field.name == segment.ident.name {1548 if spans.iter().all(|(_, had_error)| had_error.is_err()) {1549 // This resolution error will likely be fixed by fixing a1550 // syntax error in a pattern, so it is irrelevant to the user.1551 let multispan: MultiSpan =1552 spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into();1553 err.span_note(1554 multispan,1555 "this pattern had a recovered parse error which likely lost \1556 the expected fields",1557 );1558 err.downgrade_to_delayed_bug();1559 }1560 let ty = self.r.tcx.item_name(*def_id);1561 for (span, _) in spans {1562 err.span_label(1563 *span,1564 format!(1565 "this pattern doesn't include `{field}`, which is \1566 available in `{ty}`",1567 ),1568 );1569 }1570 }1571 }1572 }1573 }1574 }1575 }15761577 fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) {1578 let Some(pat) = self.diag_metadata.current_pat else { return };1579 let (bound, side, range) = match &pat.kind {1580 ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range),1581 ast::PatKind::Range(None, Some(bound), range) => (bound, Side::End, range),1582 _ => return,1583 };1584 if let ExprKind::Path(None, range_path) = &bound.kind1585 && let [segment] = &range_path.segments[..]1586 && let [s] = path1587 && segment.ident == s.ident1588 && segment.ident.span.eq_ctxt(range.span)1589 {1590 // We've encountered `[first, rest..]` (#88404) or `[first, ..rest]` (#120591)1591 // where the user might have meant `[first, rest @ ..]`.1592 let (span, snippet) = match side {1593 Side::Start => (segment.ident.span.between(range.span), " @ ".into()),1594 Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),1595 };1596 err.subdiagnostic(diagnostics::UnexpectedResUseAtOpInSlicePatWithRangeSugg {1597 span,1598 ident: segment.ident,1599 snippet,1600 });1601 }16021603 enum Side {1604 Start,1605 End,1606 }1607 }16081609 fn suggest_range_struct_destructuring(1610 &mut self,1611 err: &mut Diag<'_>,1612 path: &[Segment],1613 source: PathSource<'_, '_, '_>,1614 ) {1615 if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) {1616 return;1617 }16181619 let Some(pat) = self.diag_metadata.current_pat else { return };1620 let ast::PatKind::Range(start, end, end_kind) = &pat.kind else { return };16211622 let [segment] = path else { return };1623 let failing_span = segment.ident.span;16241625 let in_start = start.as_ref().is_some_and(|e| e.span.contains(failing_span));1626 let in_end = end.as_ref().is_some_and(|e| e.span.contains(failing_span));16271628 if !in_start && !in_end {1629 return;1630 }16311632 let start_snippet =1633 start.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());1634 let end_snippet =1635 end.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());16361637 let field = |name: &str, val: String| {1638 if val == name { val } else { format!("{name}: {val}") }1639 };16401641 let mut resolve_short_name = |short: Symbol, full: &str| -> String {1642 let ident = Ident::with_dummy_span(short);1643 let path = Segment::from_path(&Path::from_ident(ident));16441645 match self.resolve_path(&path, Some(TypeNS), None, PathSource::Type) {1646 PathResult::NonModule(..) => short.to_string(),1647 _ => full.to_string(),1648 }1649 };1650 // FIXME(new_range): Also account for new range types1651 let (struct_path, fields) = match (start_snippet, end_snippet, &end_kind.node) {1652 (Some(start), Some(end), ast::RangeEnd::Excluded) => (1653 resolve_short_name(sym::Range, "std::ops::Range"),1654 vec![field("start", start), field("end", end)],1655 ),1656 (Some(start), Some(end), ast::RangeEnd::Included(_)) => (1657 resolve_short_name(sym::RangeInclusive, "std::ops::RangeInclusive"),1658 vec![field("start", start), field("end", end)],1659 ),1660 (Some(start), None, _) => (1661 resolve_short_name(sym::RangeFrom, "std::ops::RangeFrom"),1662 vec![field("start", start)],1663 ),1664 (None, Some(end), ast::RangeEnd::Excluded) => {1665 (resolve_short_name(sym::RangeTo, "std::ops::RangeTo"), vec![field("end", end)])1666 }1667 (None, Some(end), ast::RangeEnd::Included(_)) => (1668 resolve_short_name(sym::RangeToInclusive, "std::ops::RangeToInclusive"),1669 vec![field("end", end)],1670 ),1671 _ => return,1672 };16731674 err.span_suggestion_verbose(1675 pat.span,1676 format!("if you meant to destructure a range use a struct pattern"),1677 format!("{} {{ {} }}", struct_path, fields.join(", ")),1678 Applicability::MaybeIncorrect,1679 );16801681 err.note(1682 "range patterns match against the start and end of a range; \1683 to bind the components, use a struct pattern",1684 );1685 }16861687 fn suggest_swapping_misplaced_self_ty_and_trait(1688 &mut self,1689 err: &mut Diag<'_>,1690 source: PathSource<'_, 'ast, 'ra>,1691 res: Option<Res>,1692 span: Span,1693 ) {1694 if let Some((trait_ref, self_ty)) =1695 self.diag_metadata.currently_processing_impl_trait.clone()1696 && let TyKind::Path(_, self_ty_path) = &self_ty.kind1697 && let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1698 self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source)1699 && module.def_kind() == Some(DefKind::Trait)1700 && trait_ref.path.span == span1701 && let PathSource::Trait(_) = source1702 && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res1703 && let Ok(self_ty_str) = self.r.tcx.sess.source_map().span_to_snippet(self_ty.span)1704 && let Ok(trait_ref_str) =1705 self.r.tcx.sess.source_map().span_to_snippet(trait_ref.path.span)1706 {1707 err.multipart_suggestion(1708 "`impl` items mention the trait being implemented first and the type it is being implemented for second",1709 vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],1710 Applicability::MaybeIncorrect,1711 );1712 }1713 }17141715 fn explain_functions_in_pattern(1716 &self,1717 err: &mut Diag<'_>,1718 res: Option<Res>,1719 source: PathSource<'_, '_, '_>,1720 ) {1721 let PathSource::TupleStruct(_, _) = source else { return };1722 let Some(Res::Def(DefKind::Fn, _)) = res else { return };1723 err.primary_message("expected a pattern, found a function call");1724 err.note("function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>");1725 }17261727 fn suggest_changing_type_to_const_param(1728 &self,1729 err: &mut Diag<'_>,1730 res: Option<Res>,1731 source: PathSource<'_, '_, '_>,1732 path: &[Segment],1733 following_seg: Option<&Segment>,1734 span: Span,1735 ) {1736 if let PathSource::Expr(None) = source1737 && let Some(Res::Def(DefKind::TyParam, _)) = res1738 && following_seg.is_none()1739 && let [segment] = path1740 {1741 // We have something like1742 // impl<T, N> From<[T; N]> for VecWrapper<T> {1743 // fn from(slice: [T; N]) -> Self {1744 // VecWrapper(slice.to_vec())1745 // }1746 // }1747 // where `N` is a type param but should likely have been a const param.1748 let Some(item) = self.diag_metadata.current_item else { return };1749 let Some(generics) = item.kind.generics() else { return };1750 let Some(span) = generics.params.iter().find_map(|param| {1751 // Only consider type params with no bounds.1752 if param.bounds.is_empty() && param.ident.name == segment.ident.name {1753 Some(param.ident.span)1754 } else {1755 None1756 }1757 }) else {1758 return;1759 };1760 err.subdiagnostic(diagnostics::UnexpectedResChangeTyParamToConstParamSugg {1761 before: span.shrink_to_lo(),1762 after: span.shrink_to_hi(),1763 });1764 return;1765 }1766 let PathSource::Trait(_) = source else { return };17671768 // We don't include `DefKind::Str` and `DefKind::AssocTy` as they can't be reached here anyway.1769 let applicability = match res {1770 Some(Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_) | PrimTy::Bool | PrimTy::Char)) => {1771 Applicability::MachineApplicable1772 }1773 // FIXME(const_generics): Add `DefKind::TyParam` and `SelfTyParam` once we support generic1774 // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the1775 // benefits of including them here outweighs the small number of false positives.1776 Some(Res::Def(DefKind::Struct | DefKind::Enum, _))1777 if self.r.features.adt_const_params() || self.r.features.min_adt_const_params() =>1778 {1779 Applicability::MaybeIncorrect1780 }1781 _ => return,1782 };17831784 let Some(item) = self.diag_metadata.current_item else { return };1785 let Some(generics) = item.kind.generics() else { return };17861787 let param = generics.params.iter().find_map(|param| {1788 // Only consider type params with exactly one trait bound.1789 if let [bound] = &*param.bounds1790 && let ast::GenericBound::Trait(tref) = bound1791 && tref.modifiers == ast::TraitBoundModifiers::NONE1792 && tref.span == span1793 && param.ident.span.eq_ctxt(span)1794 {1795 Some(param.ident.span)1796 } else {1797 None1798 }1799 });18001801 if let Some(param) = param {1802 err.subdiagnostic(diagnostics::UnexpectedResChangeTyToConstParamSugg {1803 span: param.shrink_to_lo(),1804 applicability,1805 });1806 }1807 }18081809 fn suggest_pattern_match_with_let(1810 &self,1811 err: &mut Diag<'_>,1812 source: PathSource<'_, '_, '_>,1813 span: Span,1814 ) -> bool {1815 if let PathSource::Expr(_) = source1816 && let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }) =1817 self.diag_metadata.in_if_condition1818 {1819 // Icky heuristic so we don't suggest:1820 // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)1821 // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)1822 if lhs.is_approximately_pattern() && lhs.span.contains(span) {1823 err.span_suggestion_verbose(1824 expr_span.shrink_to_lo(),1825 "you might have meant to use pattern matching",1826 "let ",1827 Applicability::MaybeIncorrect,1828 );1829 return true;1830 }1831 }1832 false1833 }18341835 fn get_single_associated_item(1836 &mut self,1837 path: &[Segment],1838 source: &PathSource<'_, 'ast, 'ra>,1839 filter_fn: &impl Fn(Res) -> bool,1840 ) -> Option<TypoSuggestion> {1841 if let crate::PathSource::TraitItem(_, _) = source {1842 let mod_path = &path[..path.len() - 1];1843 if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1844 self.resolve_path(mod_path, None, None, *source)1845 {1846 let targets: Vec<_> = self1847 .r1848 .resolutions(module)1849 .borrow()1850 .iter()1851 .filter_map(|(key, resolution)| {1852 let resolution = resolution.borrow();1853 resolution.best_decl().map(|binding| binding.res()).and_then(|res| {1854 if filter_fn(res) {1855 Some((key.ident.name, resolution.orig_ident_span, res))1856 } else {1857 None1858 }1859 })1860 })1861 .collect();1862 if let &[(name, orig_ident_span, res)] = targets.as_slice() {1863 return Some(TypoSuggestion::single_item(name, orig_ident_span, res));1864 }1865 }1866 }1867 None1868 }18691870 /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.1871 fn restrict_assoc_type_in_where_clause(&self, span: Span, err: &mut Diag<'_>) -> bool {1872 // Detect that we are actually in a `where` predicate.1873 let Some(ast::WherePredicate {1874 kind:1875 ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {1876 bounded_ty,1877 bound_generic_params,1878 bounds,1879 }),1880 span: where_span,1881 ..1882 }) = self.diag_metadata.current_where_predicate1883 else {1884 return false;1885 };1886 if !bound_generic_params.is_empty() {1887 return false;1888 }18891890 // Confirm that the target is an associated type.1891 let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind else { return false };1892 // use this to verify that ident is a type param.1893 let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else { return false };1894 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::AssocTy, _))) {1895 return false;1896 }18971898 let peeled_ty = qself.ty.peel_refs();1899 let ast::TyKind::Path(None, type_param_path) = &peeled_ty.kind else { return false };1900 // Confirm that the `SelfTy` is a type parameter.1901 let Some(partial_res) = self.r.partial_res_map.get(&peeled_ty.id) else {1902 return false;1903 };1904 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {1905 return false;1906 }1907 let ([ast::PathSegment { args: None, .. }], [ast::GenericBound::Trait(poly_trait_ref)]) =1908 (&type_param_path.segments[..], &bounds[..])1909 else {1910 return false;1911 };1912 let [ast::PathSegment { ident, args: None, id }] =1913 &poly_trait_ref.trait_ref.path.segments[..]1914 else {1915 return false;1916 };1917 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {1918 return false;1919 }1920 if ident.span == span {1921 let Some(partial_res) = self.r.partial_res_map.get(&id) else {1922 return false;1923 };1924 if !matches!(partial_res.full_res(), Some(Res::Def(..))) {1925 return false;1926 }19271928 let Some(new_where_bound_predicate) =1929 mk_where_bound_predicate(path, poly_trait_ref, &qself.ty)1930 else {1931 return false;1932 };1933 err.span_suggestion_verbose(1934 *where_span,1935 format!("constrain the associated type to `{ident}`"),1936 where_bound_predicate_to_string(&new_where_bound_predicate),1937 Applicability::MaybeIncorrect,1938 );1939 }1940 true1941 }19421943 /// Check if the source is call expression and the first argument is `self`. If true,1944 /// return the span of whole call and the span for all arguments expect the first one (`self`).1945 fn call_has_self_arg(&self, source: PathSource<'_, '_, '_>) -> Option<(Span, Option<Span>)> {1946 let mut has_self_arg = None;1947 if let PathSource::Expr(Some(parent)) = source1948 && let ExprKind::Call(_, args) = &parent.kind1949 && !args.is_empty()1950 {1951 let mut expr_kind = &args[0].kind;1952 loop {1953 match expr_kind {1954 ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => {1955 if arg_name.segments[0].ident.name == kw::SelfLower {1956 let call_span = parent.span;1957 let tail_args_span = if args.len() > 1 {1958 Some(Span::new(1959 args[1].span.lo(),1960 args.last().unwrap().span.hi(),1961 call_span.ctxt(),1962 None,1963 ))1964 } else {1965 None1966 };1967 has_self_arg = Some((call_span, tail_args_span));1968 }1969 break;1970 }1971 ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind,1972 _ => break,1973 }1974 }1975 }1976 has_self_arg1977 }19781979 fn followed_by_brace(&self, span: Span) -> (bool, Option<Span>) {1980 // HACK(estebank): find a better way to figure out that this was a1981 // parser issue where a struct literal is being used on an expression1982 // where a brace being opened means a block is being started. Look1983 // ahead for the next text to see if `span` is followed by a `{`.1984 let sm = self.r.tcx.sess.source_map();1985 if let Some(open_brace_span) = sm.span_followed_by(span, "{") {1986 // In case this could be a struct literal that needs to be surrounded1987 // by parentheses, find the appropriate span.1988 let close_brace_span =1989 sm.span_to_next_source(open_brace_span).ok().and_then(|next_source| {1990 // Find the matching `}` accounting for nested braces.1991 let mut depth: u32 = 1;1992 let offset = next_source.char_indices().find_map(|(i, c)| {1993 match c {1994 '{' => depth += 1,1995 '}' if depth == 1 => return Some(i),1996 '}' => depth -= 1,1997 _ => {}1998 }1999 None2000 })?;
Findings
✓ No findings reported for this file.