1// ignore-tidy-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_errors::codes::*;16use rustc_errors::{17 Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize,18 struct_span_code_err,19};20use rustc_hir as hir;21use rustc_hir::def::Namespace::{self, *};22use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds};23use rustc_hir::def_id::{CRATE_DEF_ID, DefId};24use rustc_hir::{MissingLifetimeKind, PrimTy, find_attr};25use rustc_middle::ty;26use rustc_session::{Session, lint};27use rustc_span::edit_distance::{edit_distance, find_best_match_for_name};28use rustc_span::edition::Edition;29use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};30use thin_vec::ThinVec;31use tracing::debug;3233use super::NoConstantGenericsReason;34use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};35use crate::late::{36 AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind,37 LifetimeUseSet, QSelf, RibKind,38};39use crate::ty::fast_reject::SimplifiedType;40use crate::{41 Finalize, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res,42 Resolver, ScopeSet, Segment, errors, path_names_to_string,43};4445/// A field or associated item from self type suggested in case of resolution failure.46enum AssocSuggestion {47 Field(Span),48 MethodWithSelf { called: bool },49 AssocFn { called: bool },50 AssocType,51 AssocConst,52}5354impl AssocSuggestion {55 fn action(&self) -> &'static str {56 match self {57 AssocSuggestion::Field(_) => "use the available field",58 AssocSuggestion::MethodWithSelf { called: true } => {59 "call the method with the fully-qualified path"60 }61 AssocSuggestion::MethodWithSelf { called: false } => {62 "refer to the method with the fully-qualified path"63 }64 AssocSuggestion::AssocFn { called: true } => "call the associated function",65 AssocSuggestion::AssocFn { called: false } => "refer to the associated function",66 AssocSuggestion::AssocConst => "use the associated `const`",67 AssocSuggestion::AssocType => "use the associated type",68 }69 }70}7172fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {73 namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper74}7576fn is_self_value(path: &[Segment], namespace: Namespace) -> bool {77 namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower78}7980fn path_to_string_without_assoc_item_bindings(path: &Path) -> String {81 let mut path = path.clone();82 for segment in &mut path.segments {83 let mut remove_args = false;84 if let Some(args) = segment.args.as_deref_mut()85 && let ast::GenericArgs::AngleBracketed(angle_bracketed) = args86 {87 angle_bracketed.args.retain(|arg| matches!(arg, ast::AngleBracketedArg::Arg(_)));88 remove_args = angle_bracketed.args.is_empty();89 }90 if remove_args {91 segment.args = None;92 }93 }94 path_to_string(&path)95}9697/// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.98fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) {99 let variant_path = &suggestion.path;100 let variant_path_string = path_names_to_string(variant_path);101102 let path_len = suggestion.path.segments.len();103 let enum_path = ast::Path {104 span: suggestion.path.span,105 segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(),106 tokens: None,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(Clone, 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.152 Ignore,153 /// There is a named lifetime, we won't suggest anything.154 Named,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}169170#[derive(Debug)]171enum TypoCandidate {172 Typo(TypoSuggestion),173 Shadowed(Res, Option<Span>),174 None,175}176177impl TypoCandidate {178 fn to_opt_suggestion(self) -> Option<TypoSuggestion> {179 match self {180 TypoCandidate::Typo(sugg) => Some(sugg),181 TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None,182 }183 }184}185186impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {187 fn trait_assoc_type_def_id_by_name(188 &mut self,189 trait_def_id: DefId,190 assoc_name: Symbol,191 ) -> Option<DefId> {192 let module = self.r.get_module(trait_def_id)?;193 self.r.resolutions(module).borrow().iter().find_map(|(key, resolution)| {194 if key.ident.name != assoc_name {195 return None;196 }197 let resolution = resolution.borrow();198 let binding = resolution.best_decl()?;199 match binding.res() {200 Res::Def(DefKind::AssocTy, def_id) => Some(def_id),201 _ => None,202 }203 })204 }205206 /// This does best-effort work to generate suggestions for associated types.207 fn suggest_assoc_type_from_bounds(208 &mut self,209 err: &mut Diag<'_>,210 source: PathSource<'_, 'ast, 'ra>,211 path: &[Segment],212 ident_span: Span,213 ) -> bool {214 // Filter out cases where we cannot emit meaningful suggestions.215 if source.namespace() != TypeNS {216 return false;217 }218 let [segment] = path else { return false };219 if segment.has_generic_args {220 return false;221 }222 if !ident_span.can_be_used_for_suggestions() {223 return false;224 }225 let assoc_name = segment.ident.name;226 if assoc_name == kw::Underscore {227 return false;228 }229230 // Map: type parameter name -> (trait def id -> (assoc type def id, trait paths as written)).231 // We keep a set of paths per trait so we can detect cases like232 // `T: Trait<i32> + Trait<u32>` where suggesting `T::Assoc` would be ambiguous.233 let mut matching_bounds: FxIndexMap<234 Symbol,235 FxIndexMap<DefId, (DefId, FxIndexSet<String>)>,236 > = FxIndexMap::default();237238 let mut record_bound = |this: &mut Self,239 ty_param: Symbol,240 poly_trait_ref: &ast::PolyTraitRef| {241 // Avoid generating suggestions we can't print in a well-formed way.242 if !poly_trait_ref.bound_generic_params.is_empty() {243 return;244 }245 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {246 return;247 }248 let Some(trait_seg) = poly_trait_ref.trait_ref.path.segments.last() else {249 return;250 };251 let Some(partial_res) = this.r.partial_res_map.get(&trait_seg.id) else {252 return;253 };254 let Some(trait_def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) else {255 return;256 };257 let Some(assoc_type_def_id) =258 this.trait_assoc_type_def_id_by_name(trait_def_id, assoc_name)259 else {260 return;261 };262263 // Preserve `::` and generic args so we don't generate broken suggestions like264 // `<T as Foo>::Assoc` for bounds written as `T: ::Foo<'a>`, while stripping265 // associated-item bindings that are rejected in qualified paths.266 let trait_path =267 path_to_string_without_assoc_item_bindings(&poly_trait_ref.trait_ref.path);268 let trait_bounds = matching_bounds.entry(ty_param).or_default();269 let trait_bounds = trait_bounds270 .entry(trait_def_id)271 .or_insert_with(|| (assoc_type_def_id, FxIndexSet::default()));272 debug_assert_eq!(trait_bounds.0, assoc_type_def_id);273 trait_bounds.1.insert(trait_path);274 };275276 let mut record_from_generics = |this: &mut Self, generics: &ast::Generics| {277 for param in &generics.params {278 let ast::GenericParamKind::Type { .. } = param.kind else { continue };279 for bound in ¶m.bounds {280 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };281 record_bound(this, param.ident.name, poly_trait_ref);282 }283 }284285 for predicate in &generics.where_clause.predicates {286 let ast::WherePredicateKind::BoundPredicate(where_bound) = &predicate.kind else {287 continue;288 };289290 let ast::TyKind::Path(None, bounded_path) = &where_bound.bounded_ty.kind else {291 continue;292 };293 let [ast::PathSegment { ident, args: None, .. }] = &bounded_path.segments[..]294 else {295 continue;296 };297298 // Only suggest for bounds that are explicitly on an in-scope type parameter.299 let Some(partial_res) = this.r.partial_res_map.get(&where_bound.bounded_ty.id)300 else {301 continue;302 };303 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {304 continue;305 }306307 for bound in &where_bound.bounds {308 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };309 record_bound(this, ident.name, poly_trait_ref);310 }311 }312 };313314 if let Some(item) = self.diag_metadata.current_item315 && let Some(generics) = item.kind.generics()316 {317 record_from_generics(self, generics);318 }319320 if let Some(item) = self.diag_metadata.current_item321 && matches!(item.kind, ItemKind::Impl(..))322 && let Some(assoc) = self.diag_metadata.current_impl_item323 {324 let generics = match &assoc.kind {325 AssocItemKind::Const(box ast::ConstItem { generics, .. })326 | AssocItemKind::Fn(box ast::Fn { generics, .. })327 | AssocItemKind::Type(box ast::TyAlias { generics, .. }) => Some(generics),328 AssocItemKind::Delegation(..)329 | AssocItemKind::MacCall(..)330 | AssocItemKind::DelegationMac(..) => None,331 };332 if let Some(generics) = generics {333 record_from_generics(self, generics);334 }335 }336337 let mut suggestions: FxIndexSet<String> = FxIndexSet::default();338 for (ty_param, traits) in matching_bounds {339 let ty_param = ty_param.to_ident_string();340 let trait_paths_len: usize = traits.values().map(|(_, paths)| paths.len()).sum();341 if traits.len() == 1 && trait_paths_len == 1 {342 let assoc_type_def_id = traits.values().next().unwrap().0;343 let assoc_segment = format!(344 "{}{}",345 assoc_name,346 self.r.item_required_generic_args_suggestion(assoc_type_def_id)347 );348 suggestions.insert(format!("{ty_param}::{assoc_segment}"));349 } else {350 for (assoc_type_def_id, trait_paths) in traits.into_values() {351 let assoc_segment = format!(352 "{}{}",353 assoc_name,354 self.r.item_required_generic_args_suggestion(assoc_type_def_id)355 );356 for trait_path in trait_paths {357 suggestions358 .insert(format!("<{ty_param} as {trait_path}>::{assoc_segment}"));359 }360 }361 }362 }363364 if suggestions.is_empty() {365 return false;366 }367368 let mut suggestions: Vec<String> = suggestions.into_iter().collect();369 suggestions.sort();370371 err.span_suggestions_with_style(372 ident_span,373 "you might have meant to use an associated type of the same name",374 suggestions,375 Applicability::MaybeIncorrect,376 SuggestionStyle::ShowAlways,377 );378379 true380 }381382 fn make_base_error(383 &mut self,384 path: &[Segment],385 span: Span,386 source: PathSource<'_, 'ast, 'ra>,387 res: Option<Res>,388 ) -> BaseError {389 // Make the base error.390 let mut expected = source.descr_expected();391 let path_str = Segment::names_to_string(path);392 let item_str = path.last().unwrap().ident;393394 if let Some(res) = res {395 BaseError {396 msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),397 fallback_label: format!("not a {expected}"),398 span,399 span_label: match res {400 Res::Def(DefKind::TyParam, def_id) => {401 Some((self.r.def_span(def_id), "found this type parameter"))402 }403 _ => None,404 },405 could_be_expr: match res {406 Res::Def(DefKind::Fn, _) => {407 // Verify whether this is a fn call or an Fn used as a type.408 self.r409 .tcx410 .sess411 .source_map()412 .span_to_snippet(span)413 .is_ok_and(|snippet| snippet.ends_with(')'))414 }415 Res::Def(416 DefKind::Ctor(..)417 | DefKind::AssocFn418 | DefKind::Const { .. }419 | DefKind::AssocConst { .. },420 _,421 )422 | Res::SelfCtor(_)423 | Res::PrimTy(_)424 | Res::Local(_) => true,425 _ => false,426 },427 suggestion: None,428 module: None,429 }430 } else {431 let mut span_label = None;432 let item_ident = path.last().unwrap().ident;433 let item_span = item_ident.span;434 let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {435 debug!(?self.diag_metadata.current_impl_items);436 debug!(?self.diag_metadata.current_function);437 let suggestion = if self.current_trait_ref.is_none()438 && let Some((fn_kind, _)) = self.diag_metadata.current_function439 && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()440 && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = fn_kind441 && let Some(items) = self.diag_metadata.current_impl_items442 && let Some(item) = items.iter().find(|i| {443 i.kind.ident().is_some_and(|ident| {444 // Don't suggest if the item is in Fn signature arguments (#112590).445 ident.name == item_str.name && !sig.span.contains(item_span)446 })447 }) {448 let sp = item_span.shrink_to_lo();449450 // Account for `Foo { field }` when suggesting `self.field` so we result on451 // `Foo { field: self.field }`.452 let field = match source {453 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. })) => {454 expr.fields.iter().find(|f| f.ident == item_ident)455 }456 _ => None,457 };458 let pre = if let Some(field) = field459 && field.is_shorthand460 {461 format!("{item_ident}: ")462 } else {463 String::new()464 };465 // Ensure we provide a structured suggestion for an assoc fn only for466 // expressions that are actually a fn call.467 let is_call = match field {468 Some(ast::ExprField { expr, .. }) => {469 matches!(expr.kind, ExprKind::Call(..))470 }471 _ => matches!(472 source,473 PathSource::Expr(Some(Expr { kind: ExprKind::Call(..), .. })),474 ),475 };476477 match &item.kind {478 AssocItemKind::Fn(fn_)479 if (!sig.decl.has_self() || !is_call) && fn_.sig.decl.has_self() =>480 {481 // Ensure that we only suggest `self.` if `self` is available,482 // you can't call `fn foo(&self)` from `fn bar()` (#115992).483 // We also want to mention that the method exists.484 span_label = Some((485 fn_.ident.span,486 "a method by that name is available on `Self` here",487 ));488 None489 }490 AssocItemKind::Fn(fn_) if !fn_.sig.decl.has_self() && !is_call => {491 span_label = Some((492 fn_.ident.span,493 "an associated function by that name is available on `Self` here",494 ));495 None496 }497 AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => {498 Some((sp, "consider using the method on `Self`", format!("{pre}self.")))499 }500 AssocItemKind::Fn(_) => Some((501 sp,502 "consider using the associated function on `Self`",503 format!("{pre}Self::"),504 )),505 AssocItemKind::Const(..) => Some((506 sp,507 "consider using the associated constant on `Self`",508 format!("{pre}Self::"),509 )),510 _ => None,511 }512 } else {513 None514 };515 (String::new(), "this scope".to_string(), None, suggestion)516 } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {517 if self.r.tcx.sess.edition() > Edition::Edition2015 {518 // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude519 // which overrides all other expectations of item type520 expected = "crate";521 (String::new(), "the list of imported crates".to_string(), None, None)522 } else {523 (524 String::new(),525 "the crate root".to_string(),526 Some(CRATE_DEF_ID.to_def_id()),527 None,528 )529 }530 } else if path.len() == 2 && path[0].ident.name == kw::Crate {531 (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None)532 } else {533 let mod_path = &path[..path.len() - 1];534 let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source);535 let mod_prefix = match mod_res {536 PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),537 _ => None,538 };539540 let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);541542 let mod_prefix =543 mod_prefix.map_or_else(String::new, |res| format!("{} ", res.descr()));544 (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None)545 };546547 let (fallback_label, suggestion) = if path_str == "async"548 && expected.starts_with("struct")549 {550 ("`async` blocks are only allowed in Rust 2018 or later".to_string(), suggestion)551 } else {552 // check if we are in situation of typo like `True` instead of `true`.553 let override_suggestion =554 if ["true", "false"].contains(&item_str.to_string().to_lowercase().as_str()) {555 let item_typo = item_str.to_string().to_lowercase();556 Some((item_span, "you may want to use a bool value instead", item_typo))557 // FIXME(vincenzopalazzo): make the check smarter,558 // and maybe expand with levenshtein distance checks559 } else if item_str.as_str() == "printf" {560 Some((561 item_span,562 "you may have meant to use the `print` macro",563 "print!".to_owned(),564 ))565 } else {566 suggestion567 };568 (format!("not found in {mod_str}"), override_suggestion)569 };570571 BaseError {572 msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),573 fallback_label,574 span: item_span,575 span_label,576 could_be_expr: false,577 suggestion,578 module,579 }580 }581 }582583 /// Try to suggest for a module path that cannot be resolved.584 /// Such as `fmt::Debug` where `fmt` is not resolved without importing,585 /// here we search with `lookup_import_candidates` for a module named `fmt`586 /// with `TypeNS` as namespace.587 ///588 /// We need a separate function here because we won't suggest for a path with single segment589 /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`590 pub(crate) fn smart_resolve_partial_mod_path_errors(591 &mut self,592 prefix_path: &[Segment],593 following_seg: Option<&Segment>,594 ) -> Vec<ImportSuggestion> {595 if let Some(segment) = prefix_path.last()596 && let Some(following_seg) = following_seg597 {598 let candidates = self.r.lookup_import_candidates(599 segment.ident,600 Namespace::TypeNS,601 &self.parent_scope,602 &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),603 );604 // double check next seg is valid605 candidates606 .into_iter()607 .filter(|candidate| {608 if let Some(def_id) = candidate.did609 && let Some(module) = self.r.get_module(def_id)610 {611 Some(def_id) != self.parent_scope.module.opt_def_id()612 && self613 .r614 .resolutions(module)615 .borrow()616 .iter()617 .any(|(key, _r)| key.ident.name == following_seg.ident.name)618 } else {619 false620 }621 })622 .collect::<Vec<_>>()623 } else {624 Vec::new()625 }626 }627628 /// Handles error reporting for `smart_resolve_path_fragment` function.629 /// Creates base error and amends it with one short label and possibly some longer helps/notes.630 #[tracing::instrument(skip(self), level = "debug")]631 pub(crate) fn smart_resolve_report_errors(632 &mut self,633 path: &[Segment],634 following_seg: Option<&Segment>,635 span: Span,636 source: PathSource<'_, 'ast, 'ra>,637 res: Option<Res>,638 qself: Option<&QSelf>,639 ) -> (Diag<'tcx>, Vec<ImportSuggestion>) {640 debug!(?res, ?source);641 let base_error = self.make_base_error(path, span, source, res);642643 let code = source.error_code(res.is_some());644 let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());645 err.code(code);646647 // Try to get the span of the identifier within the path's syntax context648 // (if that's different).649 if let Some(within_macro_span) =650 base_error.span.within_macro(span, self.r.tcx.sess.source_map())651 {652 err.span_label(within_macro_span, "due to this macro variable");653 }654655 self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);656 self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);657 self.suggest_range_struct_destructuring(&mut err, path, source);658 self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);659660 if let Some((span, label)) = base_error.span_label {661 err.span_label(span, label);662 }663664 if let Some(ref sugg) = base_error.suggestion {665 err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);666 }667668 self.suggest_changing_type_to_const_param(&mut err, res, source, path, following_seg, span);669 self.explain_functions_in_pattern(&mut err, res, source);670671 if self.suggest_pattern_match_with_let(&mut err, source, span) {672 // Fallback label.673 err.span_label(base_error.span, base_error.fallback_label);674 return (err, Vec::new());675 }676677 self.suggest_self_or_self_ref(&mut err, path, span);678 self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error);679 self.detect_rtn_with_fully_qualified_path(680 &mut err,681 path,682 following_seg,683 span,684 source,685 res,686 qself,687 );688 if self.suggest_self_ty(&mut err, source, path, span)689 || self.suggest_self_value(&mut err, source, path, span)690 {691 return (err, Vec::new());692 }693694 if let Some((did, item)) = self.lookup_doc_alias_name(path, source.namespace()) {695 let item_name = item.name;696 let suggestion_name = self.r.tcx.item_name(did);697 err.span_suggestion(698 item.span,699 format!("`{suggestion_name}` has a name defined in the doc alias attribute as `{item_name}`"),700 suggestion_name,701 Applicability::MaybeIncorrect702 );703704 return (err, Vec::new());705 };706707 let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed(708 &mut err,709 source,710 path,711 following_seg,712 span,713 res,714 &base_error,715 );716 if found {717 return (err, candidates);718 }719720 if self.suggest_shadowed(&mut err, source, path, following_seg, span) {721 // if there is already a shadowed name, don'suggest candidates for importing722 candidates.clear();723 }724725 let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);726 fallback |= self.suggest_typo(727 &mut err,728 source,729 path,730 following_seg,731 span,732 &base_error,733 suggested_candidates,734 );735736 if fallback {737 // Fallback label.738 err.span_label(base_error.span, base_error.fallback_label);739 }740 self.err_code_special_cases(&mut err, source, path, span);741742 let module = base_error.module.unwrap_or_else(|| CRATE_DEF_ID.to_def_id());743 self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);744745 (err, candidates)746 }747748 fn detect_rtn_with_fully_qualified_path(749 &self,750 err: &mut Diag<'_>,751 path: &[Segment],752 following_seg: Option<&Segment>,753 span: Span,754 source: PathSource<'_, '_, '_>,755 res: Option<Res>,756 qself: Option<&QSelf>,757 ) {758 if let Some(Res::Def(DefKind::AssocFn, _)) = res759 && let PathSource::TraitItem(TypeNS, _) = source760 && let None = following_seg761 && let Some(qself) = qself762 && let TyKind::Path(None, ty_path) = &qself.ty.kind763 && ty_path.segments.len() == 1764 && self.diag_metadata.current_where_predicate.is_some()765 {766 err.span_suggestion_verbose(767 span,768 "you might have meant to use the return type notation syntax",769 format!("{}::{}(..)", ty_path.segments[0].ident, path[path.len() - 1].ident),770 Applicability::MaybeIncorrect,771 );772 }773 }774775 fn detect_assoc_type_constraint_meant_as_path(776 &self,777 err: &mut Diag<'_>,778 base_error: &BaseError,779 ) {780 let Some(ty) = self.diag_metadata.current_type_path else {781 return;782 };783 let TyKind::Path(_, path) = &ty.kind else {784 return;785 };786 for segment in &path.segments {787 let Some(params) = &segment.args else {788 continue;789 };790 let ast::GenericArgs::AngleBracketed(params) = params.deref() else {791 continue;792 };793 for param in ¶ms.args {794 let ast::AngleBracketedArg::Constraint(constraint) = param else {795 continue;796 };797 let ast::AssocItemConstraintKind::Bound { bounds } = &constraint.kind else {798 continue;799 };800 for bound in bounds {801 let ast::GenericBound::Trait(trait_ref) = bound else {802 continue;803 };804 if trait_ref.modifiers == ast::TraitBoundModifiers::NONE805 && base_error.span == trait_ref.span806 {807 err.span_suggestion_verbose(808 constraint.ident.span.between(trait_ref.span),809 "you might have meant to write a path instead of an associated type bound",810 "::",811 Applicability::MachineApplicable,812 );813 }814 }815 }816 }817 }818819 fn suggest_self_or_self_ref(&mut self, err: &mut Diag<'_>, path: &[Segment], span: Span) {820 if !self.self_type_is_available() {821 return;822 }823 let Some(path_last_segment) = path.last() else { return };824 let item_str = path_last_segment.ident;825 // Emit help message for fake-self from other languages (e.g., `this` in JavaScript).826 if ["this", "my"].contains(&item_str.as_str()) {827 err.span_suggestion_short(828 span,829 "you might have meant to use `self` here instead",830 "self",831 Applicability::MaybeIncorrect,832 );833 if !self.self_value_is_available(path[0].ident.span) {834 if let Some((FnKind::Fn(_, _, ast::Fn { sig, .. }), fn_span)) =835 &self.diag_metadata.current_function836 {837 let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {838 (param.span.shrink_to_lo(), "&self, ")839 } else {840 (841 self.r842 .tcx843 .sess844 .source_map()845 .span_through_char(*fn_span, '(')846 .shrink_to_hi(),847 "&self",848 )849 };850 err.span_suggestion_verbose(851 span,852 "if you meant to use `self`, you are also missing a `self` receiver \853 argument",854 sugg,855 Applicability::MaybeIncorrect,856 );857 }858 }859 }860 }861862 fn try_lookup_name_relaxed(863 &mut self,864 err: &mut Diag<'_>,865 source: PathSource<'_, '_, '_>,866 path: &[Segment],867 following_seg: Option<&Segment>,868 span: Span,869 res: Option<Res>,870 base_error: &BaseError,871 ) -> (bool, FxHashSet<String>, Vec<ImportSuggestion>) {872 let span = match following_seg {873 Some(_) if path[0].ident.span.eq_ctxt(path[path.len() - 1].ident.span) => {874 // The path `span` that comes in includes any following segments, which we don't875 // want to replace in the suggestions.876 path[0].ident.span.to(path[path.len() - 1].ident.span)877 }878 _ => span,879 };880 let mut suggested_candidates = FxHashSet::default();881 // Try to lookup name in more relaxed fashion for better error reporting.882 let ident = path.last().unwrap().ident;883 let is_expected = &|res| source.is_expected(res);884 let ns = source.namespace();885 let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));886 let path_str = Segment::names_to_string(path);887 let ident_span = path.last().map_or(span, |ident| ident.ident.span);888 let mut candidates = self889 .r890 .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)891 .into_iter()892 .filter(|ImportSuggestion { did, .. }| {893 match (did, res.and_then(|res| res.opt_def_id())) {894 (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,895 _ => true,896 }897 })898 .collect::<Vec<_>>();899 // Try to filter out intrinsics candidates, as long as we have900 // some other candidates to suggest.901 let intrinsic_candidates: Vec<_> = candidates902 .extract_if(.., |sugg| {903 let path = path_names_to_string(&sugg.path);904 path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")905 })906 .collect();907 if candidates.is_empty() {908 // Put them back if we have no more candidates to suggest...909 candidates = intrinsic_candidates;910 }911 let crate_def_id = CRATE_DEF_ID.to_def_id();912 if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {913 let mut enum_candidates: Vec<_> = self914 .r915 .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)916 .into_iter()917 .map(|suggestion| import_candidate_to_enum_paths(&suggestion))918 .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::"))919 .collect();920 if !enum_candidates.is_empty() {921 enum_candidates.sort();922923 // Contextualize for E0425 "cannot find type", but don't belabor the point924 // (that it's a variant) for E0573 "expected type, found variant".925 let preamble = if res.is_none() {926 let others = match enum_candidates.len() {927 1 => String::new(),928 2 => " and 1 other".to_owned(),929 n => format!(" and {n} others"),930 };931 format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others)932 } else {933 String::new()934 };935 let msg = format!("{preamble}try using the variant's enum");936937 suggested_candidates.extend(938 enum_candidates939 .iter()940 .map(|(_variant_path, enum_ty_path)| enum_ty_path.clone()),941 );942 err.span_suggestions(943 span,944 msg,945 enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),946 Applicability::MachineApplicable,947 );948 }949 }950951 // Try finding a suitable replacement.952 let typo_sugg = self953 .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected)954 .to_opt_suggestion()955 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));956 if let [segment] = path957 && !matches!(source, PathSource::Delegation)958 && self.self_type_is_available()959 {960 if let Some(candidate) =961 self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())962 {963 let self_is_available = self.self_value_is_available(segment.ident.span);964 // Account for `Foo { field }` when suggesting `self.field` so we result on965 // `Foo { field: self.field }`.966 let pre = match source {967 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. }))968 if expr969 .fields970 .iter()971 .any(|f| f.ident == segment.ident && f.is_shorthand) =>972 {973 format!("{path_str}: ")974 }975 _ => String::new(),976 };977 match candidate {978 AssocSuggestion::Field(field_span) => {979 if self_is_available {980 let source_map = self.r.tcx.sess.source_map();981 let field_is_format_named_arg = matches!(982 span.desugaring_kind(),983 Some(DesugaringKind::FormatLiteral { .. })984 ) && source_map985 .span_to_source(span, |s, start, _| {986 Ok(s.get(start.saturating_sub(1)..start) == Some("{"))987 })988 .unwrap_or(false);989 if field_is_format_named_arg {990 err.help(991 format!("you might have meant to use the available field in a format string: `\"{{}}\", self.{}`", segment.ident.name),992 );993 } else {994 err.span_suggestion_verbose(995 span.shrink_to_lo(),996 "you might have meant to use the available field",997 format!("{pre}self."),998 Applicability::MaybeIncorrect,999 );1000 }1001 } else {1002 err.span_label(field_span, "a field by that name exists in `Self`");1003 }1004 }1005 AssocSuggestion::MethodWithSelf { called } if self_is_available => {1006 let msg = if called {1007 "you might have meant to call the method"1008 } else {1009 "you might have meant to refer to the method"1010 };1011 err.span_suggestion_verbose(1012 span.shrink_to_lo(),1013 msg,1014 "self.",1015 Applicability::MachineApplicable,1016 );1017 }1018 AssocSuggestion::MethodWithSelf { .. }1019 | AssocSuggestion::AssocFn { .. }1020 | AssocSuggestion::AssocConst1021 | AssocSuggestion::AssocType => {1022 err.span_suggestion_verbose(1023 span.shrink_to_lo(),1024 format!("you might have meant to {}", candidate.action()),1025 "Self::",1026 Applicability::MachineApplicable,1027 );1028 }1029 }1030 self.r.add_typo_suggestion(err, typo_sugg, ident_span);1031 return (true, suggested_candidates, candidates);1032 }10331034 // If the first argument in call is `self` suggest calling a method.1035 if let Some((call_span, args_span)) = self.call_has_self_arg(source) {1036 let mut args_snippet = String::new();1037 if let Some(args_span) = args_span1038 && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span)1039 {1040 args_snippet = snippet;1041 }10421043 if let Some(Res::Def(DefKind::Struct, def_id)) = res {1044 if let Some(ctor) = self.r.struct_ctor(def_id)1045 && ctor.has_private_fields(self.parent_scope.module, self.r)1046 {1047 if matches!(1048 ctor.res,1049 Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _)1050 ) {1051 self.update_err_for_private_tuple_struct_fields(err, &source, def_id);1052 }1053 err.note("constructor is not visible here due to private fields");1054 }1055 } else {1056 err.span_suggestion(1057 call_span,1058 format!("try calling `{ident}` as a method"),1059 format!("self.{path_str}({args_snippet})"),1060 Applicability::MachineApplicable,1061 );1062 }10631064 return (true, suggested_candidates, candidates);1065 }1066 }10671068 // Try context-dependent help if relaxed lookup didn't work.1069 if let Some(res) = res {1070 if self.smart_resolve_context_dependent_help(1071 err,1072 span,1073 source,1074 path,1075 res,1076 &path_str,1077 &base_error.fallback_label,1078 ) {1079 // We do this to avoid losing a secondary span when we override the main error span.1080 self.r.add_typo_suggestion(err, typo_sugg, ident_span);1081 return (true, suggested_candidates, candidates);1082 }1083 }10841085 // Try to find in last block rib1086 if let Some(rib) = &self.last_block_rib {1087 for (ident, &res) in &rib.bindings {1088 if let Res::Local(_) = res1089 && path.len() == 11090 && ident.span.eq_ctxt(path[0].ident.span)1091 && ident.name == path[0].ident.name1092 {1093 err.span_help(1094 ident.span,1095 format!("the binding `{path_str}` is available in a different scope in the same function"),1096 );1097 return (true, suggested_candidates, candidates);1098 }1099 }1100 }11011102 if candidates.is_empty() {1103 candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);1104 }11051106 (false, suggested_candidates, candidates)1107 }11081109 fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> {1110 let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| {1111 for resolution in r.resolutions(m).borrow().values() {1112 let Some(did) =1113 resolution.borrow().best_decl().and_then(|binding| binding.res().opt_def_id())1114 else {1115 continue;1116 };1117 if did.is_local() {1118 // We don't record the doc alias name in the local crate1119 // because the people who write doc alias are usually not1120 // confused by them.1121 continue;1122 }1123 if let Some(d) = hir::find_attr!(r.tcx, did, Doc(d) => d)1124 && d.aliases.contains_key(&item_name)1125 {1126 return Some(did);1127 }1128 }1129 None1130 };11311132 if path.len() == 1 {1133 for rib in self.ribs[ns].iter().rev() {1134 let item = path[0].ident;1135 if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind1136 && let Some(did) = find_doc_alias_name(self.r, module.to_module(), item.name)1137 {1138 return Some((did, item));1139 }1140 }1141 } else {1142 // Finds to the last resolved module item in the path1143 // and searches doc aliases within that module.1144 //1145 // Example: For the path `a::b::last_resolved::not_exist::c::d`,1146 // we will try to find any item has doc aliases named `not_exist`1147 // in `last_resolved` module.1148 //1149 // - Use `skip(1)` because the final segment must remain unresolved.1150 for (idx, seg) in path.iter().enumerate().rev().skip(1) {1151 let Some(id) = seg.id else {1152 continue;1153 };1154 let Some(res) = self.r.partial_res_map.get(&id) else {1155 continue;1156 };1157 if let Res::Def(DefKind::Mod, module) = res.expect_full_res()1158 && let module = self.r.expect_module(module)1159 && let item = path[idx + 1].ident1160 && let Some(did) = find_doc_alias_name(self.r, module, item.name)1161 {1162 return Some((did, item));1163 }1164 break;1165 }1166 }1167 None1168 }11691170 fn suggest_trait_and_bounds(1171 &self,1172 err: &mut Diag<'_>,1173 source: PathSource<'_, '_, '_>,1174 res: Option<Res>,1175 span: Span,1176 base_error: &BaseError,1177 ) -> bool {1178 let is_macro =1179 base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();1180 let mut fallback = false;11811182 if let (1183 PathSource::Trait(AliasPossibility::Maybe),1184 Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),1185 false,1186 ) = (source, res, is_macro)1187 && let Some(bounds @ [first_bound, .., last_bound]) =1188 self.diag_metadata.current_trait_object1189 {1190 fallback = true;1191 let spans: Vec<Span> = bounds1192 .iter()1193 .map(|bound| bound.span())1194 .filter(|&sp| sp != base_error.span)1195 .collect();11961197 let start_span = first_bound.span();1198 // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)1199 let end_span = last_bound.span();1200 // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)1201 let last_bound_span = spans.last().cloned().unwrap();1202 let mut multi_span: MultiSpan = spans.clone().into();1203 for sp in spans {1204 let msg = if sp == last_bound_span {1205 format!(1206 "...because of {these} bound{s}",1207 these = pluralize!("this", bounds.len() - 1),1208 s = pluralize!(bounds.len() - 1),1209 )1210 } else {1211 String::new()1212 };1213 multi_span.push_span_label(sp, msg);1214 }1215 multi_span.push_span_label(base_error.span, "expected this type to be a trait...");1216 err.span_help(1217 multi_span,1218 "`+` is used to constrain a \"trait object\" type with lifetimes or \1219 auto-traits; structs and enums can't be bound in that way",1220 );1221 if bounds.iter().all(|bound| match bound {1222 ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,1223 ast::GenericBound::Trait(tr) => tr.span == base_error.span,1224 }) {1225 let mut sugg = vec![];1226 if base_error.span != start_span {1227 sugg.push((start_span.until(base_error.span), String::new()));1228 }1229 if base_error.span != end_span {1230 sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));1231 }12321233 err.multipart_suggestion(1234 "if you meant to use a type and not a trait here, remove the bounds",1235 sugg,1236 Applicability::MaybeIncorrect,1237 );1238 }1239 }12401241 fallback |= self.restrict_assoc_type_in_where_clause(span, err);1242 fallback1243 }12441245 fn suggest_typo(1246 &mut self,1247 err: &mut Diag<'_>,1248 source: PathSource<'_, 'ast, 'ra>,1249 path: &[Segment],1250 following_seg: Option<&Segment>,1251 span: Span,1252 base_error: &BaseError,1253 suggested_candidates: FxHashSet<String>,1254 ) -> bool {1255 let is_expected = &|res| source.is_expected(res);1256 let ident_span = path.last().map_or(span, |ident| ident.ident.span);12571258 // Prefer suggestions based on associated types from in-scope bounds (e.g. `T::Item`)1259 // over purely edit-distance-based identifier suggestions.1260 // Otherwise suggestions could be verbose.1261 if self.suggest_assoc_type_from_bounds(err, source, path, ident_span) {1262 return false;1263 }12641265 let typo_sugg =1266 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1267 let mut fallback = false;1268 let typo_sugg = typo_sugg1269 .to_opt_suggestion()1270 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));1271 if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {1272 fallback = true;1273 match self.diag_metadata.current_let_binding {1274 Some((pat_sp, Some(ty_sp), None))1275 if ty_sp.contains(base_error.span) && base_error.could_be_expr =>1276 {1277 err.span_suggestion_verbose(1278 pat_sp.between(ty_sp),1279 "use `=` if you meant to assign",1280 " = ",1281 Applicability::MaybeIncorrect,1282 );1283 }1284 _ => {}1285 }12861287 // If the trait has a single item (which wasn't matched by the algorithm), suggest it1288 let suggestion = self.get_single_associated_item(path, &source, is_expected);1289 self.r.add_typo_suggestion(err, suggestion, ident_span);1290 }12911292 if self.let_binding_suggestion(err, ident_span) {1293 fallback = false;1294 }12951296 fallback1297 }12981299 fn suggest_shadowed(1300 &mut self,1301 err: &mut Diag<'_>,1302 source: PathSource<'_, '_, '_>,1303 path: &[Segment],1304 following_seg: Option<&Segment>,1305 span: Span,1306 ) -> bool {1307 let is_expected = &|res| source.is_expected(res);1308 let typo_sugg =1309 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1310 let is_in_same_file = &|sp1, sp2| {1311 let source_map = self.r.tcx.sess.source_map();1312 let file1 = source_map.span_to_filename(sp1);1313 let file2 = source_map.span_to_filename(sp2);1314 file1 == file21315 };1316 // print 'you might have meant' if the candidate is (1) is a shadowed name with1317 // accessible definition and (2) either defined in the same crate as the typo1318 // (could be in a different file) or introduced in the same file as the typo1319 // (could belong to a different crate)1320 if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg1321 && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))1322 {1323 err.span_label(1324 sugg_span,1325 format!("you might have meant to refer to this {}", res.descr()),1326 );1327 return true;1328 }1329 false1330 }13311332 fn err_code_special_cases(1333 &mut self,1334 err: &mut Diag<'_>,1335 source: PathSource<'_, '_, '_>,1336 path: &[Segment],1337 span: Span,1338 ) {1339 if let Some(err_code) = err.code {1340 if err_code == E0425 {1341 for label_rib in &self.label_ribs {1342 for (label_ident, node_id) in &label_rib.bindings {1343 let ident = path.last().unwrap().ident;1344 if format!("'{ident}") == label_ident.to_string() {1345 err.span_label(label_ident.span, "a label with a similar name exists");1346 if let PathSource::Expr(Some(Expr {1347 kind: ExprKind::Break(None, Some(_)),1348 ..1349 })) = source1350 {1351 err.span_suggestion(1352 span,1353 "use the similarly named label",1354 label_ident.name,1355 Applicability::MaybeIncorrect,1356 );1357 // Do not lint against unused label when we suggest them.1358 self.diag_metadata.unused_labels.swap_remove(node_id);1359 }1360 }1361 }1362 }13631364 self.suggest_ident_hidden_by_hygiene(err, path, span);1365 // cannot find type in this scope1366 if let Some(correct) = Self::likely_rust_type(path) {1367 err.span_suggestion(1368 span,1369 "perhaps you intended to use this type",1370 correct,1371 Applicability::MaybeIncorrect,1372 );1373 }1374 }1375 }1376 }13771378 fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], span: Span) {1379 let [segment] = path else { return };13801381 let ident = segment.ident;1382 let callsite_span = span.source_callsite();1383 for rib in self.ribs[ValueNS].iter().rev() {1384 for (binding_ident, _) in &rib.bindings {1385 // Case 1: the identifier is defined in the same scope as the macro is called1386 if binding_ident.name == ident.name1387 && !binding_ident.span.eq_ctxt(span)1388 && !binding_ident.span.from_expansion()1389 && binding_ident.span.lo() < callsite_span.lo()1390 {1391 err.span_help(1392 binding_ident.span,1393 "an identifier with the same name exists, but is not accessible due to macro hygiene",1394 );1395 return;1396 }13971398 // Case 2: the identifier is defined in a macro call in the same scope1399 if binding_ident.name == ident.name1400 && binding_ident.span.from_expansion()1401 && binding_ident.span.source_callsite().eq_ctxt(callsite_span)1402 && binding_ident.span.source_callsite().lo() < callsite_span.lo()1403 {1404 err.span_help(1405 binding_ident.span,1406 "an identifier with the same name is defined here, but is not accessible due to macro hygiene",1407 );1408 return;1409 }1410 }1411 }1412 }14131414 /// Emit special messages for unresolved `Self` and `self`.1415 fn suggest_self_ty(1416 &self,1417 err: &mut Diag<'_>,1418 source: PathSource<'_, '_, '_>,1419 path: &[Segment],1420 span: Span,1421 ) -> bool {1422 if !is_self_type(path, source.namespace()) {1423 return false;1424 }1425 err.code(E0411);1426 err.span_label(span, "`Self` is only available in impls, traits, and type definitions");1427 if let Some(item) = self.diag_metadata.current_item1428 && let Some(ident) = item.kind.ident()1429 {1430 err.span_label(1431 ident.span,1432 format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1433 );1434 }1435 true1436 }14371438 fn suggest_self_value(1439 &mut self,1440 err: &mut Diag<'_>,1441 source: PathSource<'_, '_, '_>,1442 path: &[Segment],1443 span: Span,1444 ) -> bool {1445 if !is_self_value(path, source.namespace()) {1446 return false;1447 }14481449 debug!("smart_resolve_path_fragment: E0424, source={:?}", source);1450 err.code(E0424);1451 err.span_label(1452 span,1453 match source {1454 PathSource::Pat => {1455 "`self` value is a keyword and may not be bound to variables or shadowed"1456 }1457 _ => "`self` value is a keyword only available in methods with a `self` parameter",1458 },1459 );14601461 // using `let self` is wrong even if we're not in an associated method or if we're in a macro expansion.1462 // So, we should return early if we're in a pattern, see issue #143134.1463 if matches!(source, PathSource::Pat) {1464 return true;1465 }14661467 let is_assoc_fn = self.self_type_is_available();1468 let self_from_macro = "a `self` parameter, but a macro invocation can only \1469 access identifiers it receives from parameters";1470 if let Some((fn_kind, fn_span)) = &self.diag_metadata.current_function {1471 // The current function has a `self` parameter, but we were unable to resolve1472 // a reference to `self`. This can only happen if the `self` identifier we1473 // are resolving came from a different hygiene context or a variable binding.1474 // But variable binding error is returned early above.1475 if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) {1476 err.span_label(*fn_span, format!("this function has {self_from_macro}"));1477 } else {1478 let doesnt = if is_assoc_fn {1479 let (span, sugg) = fn_kind1480 .decl()1481 .inputs1482 .get(0)1483 .map(|p| (p.span.shrink_to_lo(), "&self, "))1484 .unwrap_or_else(|| {1485 // Try to look for the "(" after the function name, if possible.1486 // This avoids placing the suggestion into the visibility specifier.1487 let span = fn_kind1488 .ident()1489 .map_or(*fn_span, |ident| fn_span.with_lo(ident.span.hi()));1490 (1491 self.r1492 .tcx1493 .sess1494 .source_map()1495 .span_through_char(span, '(')1496 .shrink_to_hi(),1497 "&self",1498 )1499 });1500 err.span_suggestion_verbose(1501 span,1502 "add a `self` receiver parameter to make the associated `fn` a method",1503 sugg,1504 Applicability::MaybeIncorrect,1505 );1506 "doesn't"1507 } else {1508 "can't"1509 };1510 if let Some(ident) = fn_kind.ident() {1511 err.span_label(1512 ident.span,1513 format!("this function {doesnt} have a `self` parameter"),1514 );1515 }1516 }1517 } else if let Some(item) = self.diag_metadata.current_item {1518 if matches!(item.kind, ItemKind::Delegation(..)) {1519 err.span_label(item.span, format!("delegation supports {self_from_macro}"));1520 } else {1521 let span = if let Some(ident) = item.kind.ident() { ident.span } else { item.span };1522 err.span_label(1523 span,1524 format!("`self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1525 );1526 }1527 }1528 true1529 }15301531 fn detect_missing_binding_available_from_pattern(1532 &self,1533 err: &mut Diag<'_>,1534 path: &[Segment],1535 following_seg: Option<&Segment>,1536 ) {1537 let [segment] = path else { return };1538 let None = following_seg else { return };1539 for rib in self.ribs[ValueNS].iter().rev() {1540 let patterns_with_skipped_bindings =1541 self.r.tcx.with_stable_hashing_context(|mut hcx| {1542 rib.patterns_with_skipped_bindings.to_sorted(&mut hcx, true)1543 });1544 for (def_id, spans) in patterns_with_skipped_bindings {1545 if let DefKind::Struct | DefKind::Variant = self.r.tcx.def_kind(*def_id)1546 && let Some(fields) = self.r.field_idents(*def_id)1547 {1548 for field in fields {1549 if field.name == segment.ident.name {1550 if spans.iter().all(|(_, had_error)| had_error.is_err()) {1551 // This resolution error will likely be fixed by fixing a1552 // syntax error in a pattern, so it is irrelevant to the user.1553 let multispan: MultiSpan =1554 spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into();1555 err.span_note(1556 multispan,1557 "this pattern had a recovered parse error which likely lost \1558 the expected fields",1559 );1560 err.downgrade_to_delayed_bug();1561 }1562 let ty = self.r.tcx.item_name(*def_id);1563 for (span, _) in spans {1564 err.span_label(1565 *span,1566 format!(1567 "this pattern doesn't include `{field}`, which is \1568 available in `{ty}`",1569 ),1570 );1571 }1572 }1573 }1574 }1575 }1576 }1577 }15781579 fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) {1580 let Some(pat) = self.diag_metadata.current_pat else { return };1581 let (bound, side, range) = match &pat.kind {1582 ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range),1583 ast::PatKind::Range(None, Some(bound), range) => (bound, Side::End, range),1584 _ => return,1585 };1586 if let ExprKind::Path(None, range_path) = &bound.kind1587 && let [segment] = &range_path.segments[..]1588 && let [s] = path1589 && segment.ident == s.ident1590 && segment.ident.span.eq_ctxt(range.span)1591 {1592 // We've encountered `[first, rest..]` (#88404) or `[first, ..rest]` (#120591)1593 // where the user might have meant `[first, rest @ ..]`.1594 let (span, snippet) = match side {1595 Side::Start => (segment.ident.span.between(range.span), " @ ".into()),1596 Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),1597 };1598 err.subdiagnostic(errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg {1599 span,1600 ident: segment.ident,1601 snippet,1602 });1603 }16041605 enum Side {1606 Start,1607 End,1608 }1609 }16101611 fn suggest_range_struct_destructuring(1612 &mut self,1613 err: &mut Diag<'_>,1614 path: &[Segment],1615 source: PathSource<'_, '_, '_>,1616 ) {1617 if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) {1618 return;1619 }16201621 let Some(pat) = self.diag_metadata.current_pat else { return };1622 let ast::PatKind::Range(start, end, end_kind) = &pat.kind else { return };16231624 let [segment] = path else { return };1625 let failing_span = segment.ident.span;16261627 let in_start = start.as_ref().is_some_and(|e| e.span.contains(failing_span));1628 let in_end = end.as_ref().is_some_and(|e| e.span.contains(failing_span));16291630 if !in_start && !in_end {1631 return;1632 }16331634 let start_snippet =1635 start.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());1636 let end_snippet =1637 end.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());16381639 let field = |name: &str, val: String| {1640 if val == name { val } else { format!("{name}: {val}") }1641 };16421643 let mut resolve_short_name = |short: Symbol, full: &str| -> String {1644 let ident = Ident::with_dummy_span(short);1645 let path = Segment::from_path(&Path::from_ident(ident));16461647 match self.resolve_path(&path, Some(TypeNS), None, PathSource::Type) {1648 PathResult::NonModule(..) => short.to_string(),1649 _ => full.to_string(),1650 }1651 };1652 // FIXME(new_range): Also account for new range types1653 let (struct_path, fields) = match (start_snippet, end_snippet, &end_kind.node) {1654 (Some(start), Some(end), ast::RangeEnd::Excluded) => (1655 resolve_short_name(sym::Range, "std::ops::Range"),1656 vec![field("start", start), field("end", end)],1657 ),1658 (Some(start), Some(end), ast::RangeEnd::Included(_)) => (1659 resolve_short_name(sym::RangeInclusive, "std::ops::RangeInclusive"),1660 vec![field("start", start), field("end", end)],1661 ),1662 (Some(start), None, _) => (1663 resolve_short_name(sym::RangeFrom, "std::ops::RangeFrom"),1664 vec![field("start", start)],1665 ),1666 (None, Some(end), ast::RangeEnd::Excluded) => {1667 (resolve_short_name(sym::RangeTo, "std::ops::RangeTo"), vec![field("end", end)])1668 }1669 (None, Some(end), ast::RangeEnd::Included(_)) => (1670 resolve_short_name(sym::RangeToInclusive, "std::ops::RangeToInclusive"),1671 vec![field("end", end)],1672 ),1673 _ => return,1674 };16751676 err.span_suggestion_verbose(1677 pat.span,1678 format!("if you meant to destructure a range use a struct pattern"),1679 format!("{} {{ {} }}", struct_path, fields.join(", ")),1680 Applicability::MaybeIncorrect,1681 );16821683 err.note(1684 "range patterns match against the start and end of a range; \1685 to bind the components, use a struct pattern",1686 );1687 }16881689 fn suggest_swapping_misplaced_self_ty_and_trait(1690 &mut self,1691 err: &mut Diag<'_>,1692 source: PathSource<'_, 'ast, 'ra>,1693 res: Option<Res>,1694 span: Span,1695 ) {1696 if let Some((trait_ref, self_ty)) =1697 self.diag_metadata.currently_processing_impl_trait.clone()1698 && let TyKind::Path(_, self_ty_path) = &self_ty.kind1699 && let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1700 self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source)1701 && let ModuleKind::Def(DefKind::Trait, ..) = module.kind1702 && trait_ref.path.span == span1703 && let PathSource::Trait(_) = source1704 && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res1705 && let Ok(self_ty_str) = self.r.tcx.sess.source_map().span_to_snippet(self_ty.span)1706 && let Ok(trait_ref_str) =1707 self.r.tcx.sess.source_map().span_to_snippet(trait_ref.path.span)1708 {1709 err.multipart_suggestion(1710 "`impl` items mention the trait being implemented first and the type it is being implemented for second",1711 vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],1712 Applicability::MaybeIncorrect,1713 );1714 }1715 }17161717 fn explain_functions_in_pattern(1718 &self,1719 err: &mut Diag<'_>,1720 res: Option<Res>,1721 source: PathSource<'_, '_, '_>,1722 ) {1723 let PathSource::TupleStruct(_, _) = source else { return };1724 let Some(Res::Def(DefKind::Fn, _)) = res else { return };1725 err.primary_message("expected a pattern, found a function call");1726 err.note("function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>");1727 }17281729 fn suggest_changing_type_to_const_param(1730 &self,1731 err: &mut Diag<'_>,1732 res: Option<Res>,1733 source: PathSource<'_, '_, '_>,1734 path: &[Segment],1735 following_seg: Option<&Segment>,1736 span: Span,1737 ) {1738 if let PathSource::Expr(None) = source1739 && let Some(Res::Def(DefKind::TyParam, _)) = res1740 && following_seg.is_none()1741 && let [segment] = path1742 {1743 // We have something like1744 // impl<T, N> From<[T; N]> for VecWrapper<T> {1745 // fn from(slice: [T; N]) -> Self {1746 // VecWrapper(slice.to_vec())1747 // }1748 // }1749 // where `N` is a type param but should likely have been a const param.1750 let Some(item) = self.diag_metadata.current_item else { return };1751 let Some(generics) = item.kind.generics() else { return };1752 let Some(span) = generics.params.iter().find_map(|param| {1753 // Only consider type params with no bounds.1754 if param.bounds.is_empty() && param.ident.name == segment.ident.name {1755 Some(param.ident.span)1756 } else {1757 None1758 }1759 }) else {1760 return;1761 };1762 err.subdiagnostic(errors::UnexpectedResChangeTyParamToConstParamSugg {1763 before: span.shrink_to_lo(),1764 after: span.shrink_to_hi(),1765 });1766 return;1767 }1768 let PathSource::Trait(_) = source else { return };17691770 // We don't include `DefKind::Str` and `DefKind::AssocTy` as they can't be reached here anyway.1771 let applicability = match res {1772 Some(Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_) | PrimTy::Bool | PrimTy::Char)) => {1773 Applicability::MachineApplicable1774 }1775 // FIXME(const_generics): Add `DefKind::TyParam` and `SelfTyParam` once we support generic1776 // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the1777 // benefits of including them here outweighs the small number of false positives.1778 Some(Res::Def(DefKind::Struct | DefKind::Enum, _))1779 if self.r.tcx.features().adt_const_params()1780 || self.r.tcx.features().min_adt_const_params() =>1781 {1782 Applicability::MaybeIncorrect1783 }1784 _ => return,1785 };17861787 let Some(item) = self.diag_metadata.current_item else { return };1788 let Some(generics) = item.kind.generics() else { return };17891790 let param = generics.params.iter().find_map(|param| {1791 // Only consider type params with exactly one trait bound.1792 if let [bound] = &*param.bounds1793 && let ast::GenericBound::Trait(tref) = bound1794 && tref.modifiers == ast::TraitBoundModifiers::NONE1795 && tref.span == span1796 && param.ident.span.eq_ctxt(span)1797 {1798 Some(param.ident.span)1799 } else {1800 None1801 }1802 });18031804 if let Some(param) = param {1805 err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg {1806 span: param.shrink_to_lo(),1807 applicability,1808 });1809 }1810 }18111812 fn suggest_pattern_match_with_let(1813 &self,1814 err: &mut Diag<'_>,1815 source: PathSource<'_, '_, '_>,1816 span: Span,1817 ) -> bool {1818 if let PathSource::Expr(_) = source1819 && let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }) =1820 self.diag_metadata.in_if_condition1821 {1822 // Icky heuristic so we don't suggest:1823 // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)1824 // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)1825 if lhs.is_approximately_pattern() && lhs.span.contains(span) {1826 err.span_suggestion_verbose(1827 expr_span.shrink_to_lo(),1828 "you might have meant to use pattern matching",1829 "let ",1830 Applicability::MaybeIncorrect,1831 );1832 return true;1833 }1834 }1835 false1836 }18371838 fn get_single_associated_item(1839 &mut self,1840 path: &[Segment],1841 source: &PathSource<'_, 'ast, 'ra>,1842 filter_fn: &impl Fn(Res) -> bool,1843 ) -> Option<TypoSuggestion> {1844 if let crate::PathSource::TraitItem(_, _) = source {1845 let mod_path = &path[..path.len() - 1];1846 if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1847 self.resolve_path(mod_path, None, None, *source)1848 {1849 let targets: Vec<_> = self1850 .r1851 .resolutions(module)1852 .borrow()1853 .iter()1854 .filter_map(|(key, resolution)| {1855 let resolution = resolution.borrow();1856 resolution.best_decl().map(|binding| binding.res()).and_then(|res| {1857 if filter_fn(res) {1858 Some((key.ident.name, resolution.orig_ident_span, res))1859 } else {1860 None1861 }1862 })1863 })1864 .collect();1865 if let &[(name, orig_ident_span, res)] = targets.as_slice() {1866 return Some(TypoSuggestion::single_item(name, orig_ident_span, res));1867 }1868 }1869 }1870 None1871 }18721873 /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.1874 fn restrict_assoc_type_in_where_clause(&self, span: Span, err: &mut Diag<'_>) -> bool {1875 // Detect that we are actually in a `where` predicate.1876 let Some(ast::WherePredicate {1877 kind:1878 ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {1879 bounded_ty,1880 bound_generic_params,1881 bounds,1882 }),1883 span: where_span,1884 ..1885 }) = self.diag_metadata.current_where_predicate1886 else {1887 return false;1888 };1889 if !bound_generic_params.is_empty() {1890 return false;1891 }18921893 // Confirm that the target is an associated type.1894 let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind else { return false };1895 // use this to verify that ident is a type param.1896 let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else { return false };1897 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::AssocTy, _))) {1898 return false;1899 }19001901 let peeled_ty = qself.ty.peel_refs();1902 let ast::TyKind::Path(None, type_param_path) = &peeled_ty.kind else { return false };1903 // Confirm that the `SelfTy` is a type parameter.1904 let Some(partial_res) = self.r.partial_res_map.get(&peeled_ty.id) else {1905 return false;1906 };1907 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {1908 return false;1909 }1910 let ([ast::PathSegment { args: None, .. }], [ast::GenericBound::Trait(poly_trait_ref)]) =1911 (&type_param_path.segments[..], &bounds[..])1912 else {1913 return false;1914 };1915 let [ast::PathSegment { ident, args: None, id }] =1916 &poly_trait_ref.trait_ref.path.segments[..]1917 else {1918 return false;1919 };1920 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {1921 return false;1922 }1923 if ident.span == span {1924 let Some(partial_res) = self.r.partial_res_map.get(&id) else {1925 return false;1926 };1927 if !matches!(partial_res.full_res(), Some(Res::Def(..))) {1928 return false;1929 }19301931 let Some(new_where_bound_predicate) =1932 mk_where_bound_predicate(path, poly_trait_ref, &qself.ty)1933 else {1934 return false;1935 };1936 err.span_suggestion_verbose(1937 *where_span,1938 format!("constrain the associated type to `{ident}`"),1939 where_bound_predicate_to_string(&new_where_bound_predicate),1940 Applicability::MaybeIncorrect,1941 );1942 }1943 true1944 }19451946 /// Check if the source is call expression and the first argument is `self`. If true,1947 /// return the span of whole call and the span for all arguments expect the first one (`self`).1948 fn call_has_self_arg(&self, source: PathSource<'_, '_, '_>) -> Option<(Span, Option<Span>)> {1949 let mut has_self_arg = None;1950 if let PathSource::Expr(Some(parent)) = source1951 && let ExprKind::Call(_, args) = &parent.kind1952 && !args.is_empty()1953 {1954 let mut expr_kind = &args[0].kind;1955 loop {1956 match expr_kind {1957 ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => {1958 if arg_name.segments[0].ident.name == kw::SelfLower {1959 let call_span = parent.span;1960 let tail_args_span = if args.len() > 1 {1961 Some(Span::new(1962 args[1].span.lo(),1963 args.last().unwrap().span.hi(),1964 call_span.ctxt(),1965 None,1966 ))1967 } else {1968 None1969 };1970 has_self_arg = Some((call_span, tail_args_span));1971 }1972 break;1973 }1974 ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind,1975 _ => break,1976 }1977 }1978 }1979 has_self_arg1980 }19811982 fn followed_by_brace(&self, span: Span) -> (bool, Option<Span>) {1983 // HACK(estebank): find a better way to figure out that this was a1984 // parser issue where a struct literal is being used on an expression1985 // where a brace being opened means a block is being started. Look1986 // ahead for the next text to see if `span` is followed by a `{`.1987 let sm = self.r.tcx.sess.source_map();1988 if let Some(open_brace_span) = sm.span_followed_by(span, "{") {1989 // In case this could be a struct literal that needs to be surrounded1990 // by parentheses, find the appropriate span.1991 let close_brace_span =1992 sm.span_to_next_source(open_brace_span).ok().and_then(|next_source| {1993 // Find the matching `}` accounting for nested braces.1994 let mut depth: u32 = 1;1995 let offset = next_source.char_indices().find_map(|(i, c)| {1996 match c {1997 '{' => depth += 1,1998 '}' if depth == 1 => return Some(i),1999 '}' => depth -= 1,2000 _ => {}
Findings
✓ No findings reported for this file.