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::attrs::diagnostic::{CustomDiagnostic, FormatArgs};23use rustc_hir::def::Namespace::{self, *};24use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds};25use rustc_hir::def_id::{CRATE_DEF_ID, DefId};26use rustc_hir::{MissingLifetimeKind, PrimTy, find_attr};27use rustc_lint_defs::builtin::{SINGLE_USE_LIFETIMES, UNUSED_LIFETIMES};28use rustc_middle::ty;29use rustc_session::Session;30use rustc_span::edit_distance::{edit_distance, find_best_match_for_name};31use rustc_span::edition::Edition;32use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};33use thin_vec::{ThinVec, thin_vec};34use tracing::debug;3536use super::NoConstantGenericsReason;37use crate::diagnostics::impls::{ImportSuggestion, LabelSuggestion, TypoSuggestion};38use crate::late::{39 AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind,40 LifetimeUseSet, QSelf, RibKind,41};42use crate::ty::fast_reject::SimplifiedType;43use crate::{44 Finalize, Module, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, Resolver,45 ScopeSet, Segment, diagnostics, path_names_to_string,46};4748/// A field or associated item from self type suggested in case of resolution failure.49enum AssocSuggestion {50 Field(Span),51 MethodWithSelf { called: bool },52 AssocFn { called: bool },53 AssocType,54 AssocConst,55}5657impl AssocSuggestion {58 fn action(&self) -> &'static str {59 match self {60 AssocSuggestion::Field(_) => "use the available field",61 AssocSuggestion::MethodWithSelf { called: true } => {62 "call the method with the fully-qualified path"63 }64 AssocSuggestion::MethodWithSelf { called: false } => {65 "refer to the method with the fully-qualified path"66 }67 AssocSuggestion::AssocFn { called: true } => "call the associated function",68 AssocSuggestion::AssocFn { called: false } => "refer to the associated function",69 AssocSuggestion::AssocConst => "use the associated `const`",70 AssocSuggestion::AssocType => "use the associated type",71 }72 }73}7475fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {76 namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper77}7879fn is_self_value(path: &[Segment], namespace: Namespace) -> bool {80 namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower81}8283fn path_to_string_without_assoc_item_bindings(path: &Path) -> String {84 let mut path = path.clone();85 for segment in &mut path.segments {86 let mut remove_args = false;87 if let Some(args) = segment.args.as_deref_mut()88 && let ast::GenericArgs::AngleBracketed(angle_bracketed) = args89 {90 angle_bracketed.args.retain(|arg| matches!(arg, ast::AngleBracketedArg::Arg(_)));91 remove_args = angle_bracketed.args.is_empty();92 }93 if remove_args {94 segment.args = None;95 }96 }97 path_to_string(&path)98}99100/// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.101fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) {102 let variant_path = &suggestion.path;103 let variant_path_string = path_names_to_string(variant_path);104105 let path_len = suggestion.path.segments.len();106 let enum_path = ast::Path {107 span: suggestion.path.span,108 segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(),109 };110 let enum_path_string = path_names_to_string(&enum_path);111112 (variant_path_string, enum_path_string)113}114115/// Description of an elided lifetime.116#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]117pub(super) struct MissingLifetime {118 /// Used to overwrite the resolution with the suggestion, to avoid cascading errors.119 pub id: NodeId,120 /// As we cannot yet emit lints in this crate and have to buffer them instead,121 /// we need to associate each lint with some `NodeId`,122 /// however for some `MissingLifetime`s their `NodeId`s are "fake",123 /// in a sense that they are temporary and not get preserved down the line,124 /// which means that the lints for those nodes will not get emitted.125 /// To combat this, we can try to use some other `NodeId`s as a fallback option.126 pub id_for_lint: NodeId,127 /// Where to suggest adding the lifetime.128 pub span: Span,129 /// How the lifetime was introduced, to have the correct space and comma.130 pub kind: MissingLifetimeKind,131 /// Number of elided lifetimes, used for elision in path.132 pub count: usize,133}134135/// Description of the lifetimes appearing in a function parameter.136/// This is used to provide a literal explanation to the elision failure.137#[derive(Debug)]138pub(super) struct ElisionFnParameter {139 /// The index of the argument in the original definition.140 pub index: usize,141 /// The name of the argument if it's a simple ident.142 pub ident: Option<Ident>,143 /// The number of lifetimes in the parameter.144 pub lifetime_count: usize,145 /// The span of the parameter.146 pub span: Span,147}148149/// Description of lifetimes that appear as candidates for elision.150/// This is used to suggest introducing an explicit lifetime.151#[derive(Clone, Copy, Debug)]152pub(super) enum LifetimeElisionCandidate {153 /// This is not a real lifetime, or it is a named lifetime, in which case we won't suggest anything.154 Ignore,155 Missing(MissingLifetime),156}157158/// Only used for diagnostics.159#[derive(Debug)]160struct BaseError {161 msg: String,162 fallback_label: String,163 span: Span,164 span_label: Option<(Span, &'static str)>,165 could_be_expr: bool,166 suggestion: Option<(Span, &'static str, String)>,167 module: Option<DefId>,168 notes: Vec<String>,169}170171#[derive(Debug)]172enum TypoCandidate {173 Typo(TypoSuggestion),174 Shadowed(Res, Option<Span>),175 None,176}177178impl TypoCandidate {179 fn to_opt_suggestion(self) -> Option<TypoSuggestion> {180 match self {181 TypoCandidate::Typo(sugg) => Some(sugg),182 TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None,183 }184 }185}186187impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {188 fn trait_assoc_type_def_id_by_name(189 &mut self,190 trait_def_id: DefId,191 assoc_name: Symbol,192 ) -> Option<DefId> {193 let module = self.r.get_module(trait_def_id)?;194 self.r.resolutions(module).iter().find_map(|(key, resolution)| {195 if key.ident.name != assoc_name {196 return None;197 }198 let resolution = resolution.borrow(self.r);199 let binding = resolution.best_decl()?;200 match binding.res() {201 Res::Def(DefKind::AssocTy, def_id) => Some(def_id),202 _ => None,203 }204 })205 }206207 /// This does best-effort work to generate suggestions for associated types.208 fn suggest_assoc_type_from_bounds(209 &mut self,210 err: &mut Diag<'_>,211 source: PathSource<'_, 'ast, 'ra>,212 path: &[Segment],213 ident_span: Span,214 ) -> bool {215 // Filter out cases where we cannot emit meaningful suggestions.216 if source.namespace() != TypeNS {217 return false;218 }219 let [segment] = path else { return false };220 if segment.has_generic_args {221 return false;222 }223 if !ident_span.can_be_used_for_suggestions() {224 return false;225 }226 let assoc_name = segment.ident.name;227 if assoc_name == kw::Underscore {228 return false;229 }230231 // Map: type parameter name -> (trait def id -> (assoc type def id, trait paths as written)).232 // We keep a set of paths per trait so we can detect cases like233 // `T: Trait<i32> + Trait<u32>` where suggesting `T::Assoc` would be ambiguous.234 let mut matching_bounds: FxIndexMap<235 Symbol,236 FxIndexMap<DefId, (DefId, FxIndexSet<String>)>,237 > = FxIndexMap::default();238239 let mut record_bound = |this: &mut Self,240 ty_param: Symbol,241 poly_trait_ref: &ast::PolyTraitRef| {242 // Avoid generating suggestions we can't print in a well-formed way.243 if !poly_trait_ref.bound_generic_params.is_empty() {244 return;245 }246 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {247 return;248 }249 let Some(trait_seg) = poly_trait_ref.trait_ref.path.segments.last() else {250 return;251 };252 let Some(partial_res) = this.r.partial_res_map.get(&trait_seg.id) else {253 return;254 };255 let Some(trait_def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) else {256 return;257 };258 let Some(assoc_type_def_id) =259 this.trait_assoc_type_def_id_by_name(trait_def_id, assoc_name)260 else {261 return;262 };263264 // Preserve `::` and generic args so we don't generate broken suggestions like265 // `<T as Foo>::Assoc` for bounds written as `T: ::Foo<'a>`, while stripping266 // associated-item bindings that are rejected in qualified paths.267 let trait_path =268 path_to_string_without_assoc_item_bindings(&poly_trait_ref.trait_ref.path);269 let trait_bounds = matching_bounds.entry(ty_param).or_default();270 let trait_bounds = trait_bounds271 .entry(trait_def_id)272 .or_insert_with(|| (assoc_type_def_id, FxIndexSet::default()));273 debug_assert_eq!(trait_bounds.0, assoc_type_def_id);274 trait_bounds.1.insert(trait_path);275 };276277 let mut record_from_generics = |this: &mut Self, generics: &ast::Generics| {278 for param in &generics.params {279 let ast::GenericParamKind::Type { .. } = param.kind else { continue };280 for bound in ¶m.bounds {281 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };282 record_bound(this, param.ident.name, poly_trait_ref);283 }284 }285286 for predicate in &generics.where_clause.predicates {287 let ast::WherePredicateKind::BoundPredicate(where_bound) = &predicate.kind else {288 continue;289 };290291 let ast::TyKind::Path(None, bounded_path) = &where_bound.bounded_ty.kind else {292 continue;293 };294 let [ast::PathSegment { ident, args: None, .. }] = &bounded_path.segments[..]295 else {296 continue;297 };298299 // Only suggest for bounds that are explicitly on an in-scope type parameter.300 let Some(partial_res) = this.r.partial_res_map.get(&where_bound.bounded_ty.id)301 else {302 continue;303 };304 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {305 continue;306 }307308 for bound in &where_bound.bounds {309 let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue };310 record_bound(this, ident.name, poly_trait_ref);311 }312 }313 };314315 if let Some(item) = self.diag_metadata.current_item316 && let Some(generics) = item.kind.generics()317 {318 record_from_generics(self, generics);319 }320321 if let Some(item) = self.diag_metadata.current_item322 && matches!(item.kind, ItemKind::Impl(..))323 && let Some(assoc) = self.diag_metadata.current_impl_item324 {325 let generics = match &assoc.kind {326 AssocItemKind::Const(ast::ConstItem { generics, .. })327 | AssocItemKind::Fn(ast::Fn { generics, .. })328 | AssocItemKind::Type(ast::TyAlias { generics, .. }) => Some(generics),329 AssocItemKind::Delegation(..)330 | AssocItemKind::MacCall(..)331 | AssocItemKind::DelegationMac(..) => None,332 };333 if let Some(generics) = generics {334 record_from_generics(self, generics);335 }336 }337338 let mut suggestions: FxIndexSet<String> = FxIndexSet::default();339 for (ty_param, traits) in matching_bounds {340 let ty_param = ty_param.to_ident_string();341 let trait_paths_len: usize = traits.values().map(|(_, paths)| paths.len()).sum();342 if traits.len() == 1 && trait_paths_len == 1 {343 let assoc_type_def_id = traits.values().next().unwrap().0;344 let assoc_segment = format!(345 "{}{}",346 assoc_name,347 self.r.item_required_generic_args_suggestion(assoc_type_def_id)348 );349 suggestions.insert(format!("{ty_param}::{assoc_segment}"));350 } else {351 for (assoc_type_def_id, trait_paths) in traits.into_values() {352 let assoc_segment = format!(353 "{}{}",354 assoc_name,355 self.r.item_required_generic_args_suggestion(assoc_type_def_id)356 );357 for trait_path in trait_paths {358 suggestions359 .insert(format!("<{ty_param} as {trait_path}>::{assoc_segment}"));360 }361 }362 }363 }364365 if suggestions.is_empty() {366 return false;367 }368369 let mut suggestions: Vec<String> = suggestions.into_iter().collect();370 suggestions.sort();371372 err.span_suggestions_with_style(373 ident_span,374 "you might have meant to use an associated type of the same name",375 suggestions,376 Applicability::MaybeIncorrect,377 SuggestionStyle::ShowAlways,378 );379380 true381 }382383 fn make_base_error(384 &mut self,385 path: &[Segment],386 span: Span,387 source: PathSource<'_, 'ast, 'ra>,388 res: Option<Res>,389 could_be_expr: bool,390 ) -> BaseError {391 // Make the base error.392 let mut expected = source.descr_expected();393 let path_str = Segment::names_to_string(path);394395 if let Some(res) = res {396 BaseError {397 msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),398 fallback_label: format!("not a {expected}"),399 span,400 span_label: match res {401 Res::Def(DefKind::TyParam, def_id) => {402 Some((self.r.def_span(def_id), "found this type parameter"))403 }404 _ => None,405 },406 could_be_expr,407 suggestion: None,408 module: None,409 notes: Vec::new(),410 }411 } else {412 let mut span_label = None;413 let item_ident = path.last().unwrap().ident;414 let item_span = item_ident.span;415 let (tick, mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {416 debug!(?self.diag_metadata.current_impl_items);417 debug!(?self.diag_metadata.current_function);418 let suggestion = if self.current_trait_ref.is_none()419 && let Some((fn_kind, _)) = self.diag_metadata.current_function420 && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()421 && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = fn_kind422 && let Some(items) = self.diag_metadata.current_impl_items423 && let Some(item) = items.iter().find(|i| {424 i.kind.ident().is_some_and(|ident| {425 // Don't suggest if the item is in Fn signature arguments (#112590).426 ident.name == item_ident.name && !sig.span.contains(item_span)427 })428 }) {429 let sp = item_span.shrink_to_lo();430431 // Account for `Foo { field }` when suggesting `self.field` so we result on432 // `Foo { field: self.field }`.433 let field = match source {434 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. })) => {435 expr.fields.iter().find(|f| f.ident == item_ident)436 }437 _ => None,438 };439 let pre = if let Some(field) = field440 && field.is_shorthand441 {442 format!("{item_ident}: ")443 } else {444 String::new()445 };446 // Ensure we provide a structured suggestion for an assoc fn only for447 // expressions that are actually a fn call.448 let is_call = match field {449 Some(ast::ExprField { expr, .. }) => {450 matches!(expr.kind, ExprKind::Call(..))451 }452 _ => matches!(453 source,454 PathSource::Expr(Some(Expr { kind: ExprKind::Call(..), .. })),455 ),456 };457458 match &item.kind {459 AssocItemKind::Fn(fn_)460 if (!sig.decl.has_self() || !is_call) && fn_.sig.decl.has_self() =>461 {462 // Ensure that we only suggest `self.` if `self` is available,463 // you can't call `fn foo(&self)` from `fn bar()` (#115992).464 // We also want to mention that the method exists.465 span_label = Some((466 fn_.ident.span,467 "a method by that name is available on `Self` here",468 ));469 None470 }471 AssocItemKind::Fn(fn_) if !fn_.sig.decl.has_self() && !is_call => {472 span_label = Some((473 fn_.ident.span,474 "an associated function by that name is available on `Self` here",475 ));476 None477 }478 AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => {479 Some((sp, "consider using the method on `Self`", format!("{pre}self.")))480 }481 AssocItemKind::Fn(_) => Some((482 sp,483 "consider using the associated function on `Self`",484 format!("{pre}Self::"),485 )),486 AssocItemKind::Const(..) => Some((487 sp,488 "consider using the associated constant on `Self`",489 format!("{pre}Self::"),490 )),491 _ => None,492 }493 } else {494 None495 };496 ("", String::new(), "this scope".to_string(), None, suggestion)497 } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {498 if self.r.tcx.sess.edition() > Edition::Edition2015 {499 // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude500 // which overrides all other expectations of item type501 expected = "crate";502 ("", String::new(), "the list of imported crates".to_string(), None, None)503 } else {504 (505 "",506 String::new(),507 "the crate root".to_string(),508 Some(CRATE_DEF_ID.to_def_id()),509 None,510 )511 }512 } else if path.len() == 2 && path[0].ident.name == kw::Crate {513 (514 "",515 String::new(),516 "the crate root".to_string(),517 Some(CRATE_DEF_ID.to_def_id()),518 None,519 )520 } else {521 let mod_path = &path[..path.len() - 1];522 let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source);523 let mod_prefix = match mod_res {524 PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),525 _ => None,526 };527528 let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);529530 let mod_prefix =531 mod_prefix.map_or_else(String::new, |res| format!("{} ", res.descr()));532 ("`", mod_prefix, Segment::names_to_string(mod_path), module_did, None)533 };534535 let suggestion =536 if ["true", "false"].contains(&item_ident.to_string().to_lowercase().as_str()) {537 // check if we are in situation of typo like `True` instead of `true`.538 let item_typo = item_ident.to_string().to_lowercase();539 Some((item_span, "you may want to use a bool value instead", item_typo))540 // FIXME(vincenzopalazzo): make the check smarter,541 // and maybe expand with levenshtein distance checks542 } else if item_ident.as_str() == "printf" {543 Some((544 item_span,545 "you may have meant to use the `print` macro",546 "print!".to_owned(),547 ))548 } else {549 suggestion550 };551 let mut msg = format!(552 "cannot find {expected} `{item_ident}` in {mod_prefix}{tick}{mod_str}{tick}"553 );554 let mut fallback_label = if path_str == "async" && expected.starts_with("struct") {555 "`async` blocks are only allowed in Rust 2018 or later".to_string()556 } else {557 format!("not found in {tick}{mod_str}{tick}")558 };559 let mut notes = Vec::new();560 if let Some(module_def_id) = module561 && let Some(directive) = self.r.on_unknown_data(module_def_id)562 {563 let args = FormatArgs { unresolved: item_ident.to_string(), this: mod_str, .. };564 let CustomDiagnostic {565 message,566 label,567 notes: custom_notes,568 parent_label: _unreachable,569 } = directive.eval(None, &args);570 if let Some(message) = message {571 notes.push(msg);572 msg = message;573 }574 if let Some(label) = label {575 fallback_label = label;576 if let Some((_, span_label)) = span_label.take() {577 notes.push(span_label.to_string());578 }579 }580 notes.extend(custom_notes);581 }582583 BaseError {584 msg,585 fallback_label,586 span: item_span,587 span_label,588 could_be_expr,589 suggestion,590 module,591 notes,592 }593 }594 }595596 fn could_be_expr(&self, res: Res, span: Span) -> bool {597 match res {598 // Verify whether this is a fn call or an Fn used as a type.599 Res::Def(DefKind::Fn, _) => self600 .r601 .tcx602 .sess603 .source_map()604 .span_to_snippet(span)605 .is_ok_and(|snippet| snippet.ends_with(')')),606 Res::Def(607 DefKind::Ctor(..)608 | DefKind::AssocFn609 | DefKind::Const { .. }610 | DefKind::AssocConst { .. },611 _,612 )613 | Res::SelfCtor(_)614 | Res::PrimTy(_)615 | Res::Local(_) => true,616 _ => false,617 }618 }619620 /// Try to suggest for a module path that cannot be resolved.621 /// Such as `fmt::Debug` where `fmt` is not resolved without importing,622 /// here we search with `lookup_import_candidates` for a module named `fmt`623 /// with `TypeNS` as namespace.624 ///625 /// We need a separate function here because we won't suggest for a path with single segment626 /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`627 pub(crate) fn smart_resolve_partial_mod_path_errors(628 &mut self,629 prefix_path: &[Segment],630 following_seg: Option<&Segment>,631 ) -> Vec<ImportSuggestion> {632 if let Some(segment) = prefix_path.last()633 && let Some(following_seg) = following_seg634 {635 let candidates = self.r.lookup_import_candidates(636 segment.ident,637 Namespace::TypeNS,638 &self.parent_scope,639 &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),640 );641 // double check next seg is valid642 candidates643 .into_iter()644 .filter(|candidate| {645 if let Some(def_id) = candidate.did646 && let Some(module) = self.r.get_module(def_id)647 {648 Some(def_id) != self.parent_scope.module.opt_def_id()649 && self650 .r651 .resolutions(module)652 .iter()653 .any(|(key, _r)| key.ident.name == following_seg.ident.name)654 } else {655 false656 }657 })658 .collect::<Vec<_>>()659 } else {660 Vec::new()661 }662 }663664 /// Handles error reporting for `smart_resolve_path_fragment` function.665 /// Creates base error and amends it with one short label and possibly some longer helps/notes.666 #[tracing::instrument(skip(self), level = "debug")]667 pub(crate) fn smart_resolve_report_errors(668 &mut self,669 path: &[Segment],670 following_seg: Option<&Segment>,671 span: Span,672 source: PathSource<'_, 'ast, 'ra>,673 res: Option<Res>,674 qself: Option<&QSelf>,675 ) -> (Diag<'tcx>, Vec<ImportSuggestion>) {676 debug!(?res, ?source);677 let cross_namespace_res = res.filter(|res| !res.matches_ns(source.namespace()));678 let could_be_expr = res.is_some_and(|res| self.could_be_expr(res, span));679 let base_error = self.make_base_error(680 path,681 span,682 source,683 if cross_namespace_res.is_some() { None } else { res },684 could_be_expr,685 );686687 let code = source.error_code(res.is_some());688 let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());689 err.code(code);690691 if let Some(res) = cross_namespace_res {692 err.note(format!(693 "{} {} named `{}` exists in another namespace",694 res.article(),695 res.descr(),696 Segment::names_to_string(path),697 ));698 }699700 // Try to get the span of the identifier within the path's syntax context701 // (if that's different).702 if let Some(within_macro_span) =703 base_error.span.within_macro(span, self.r.tcx.sess.source_map())704 {705 err.span_label(within_macro_span, "due to this macro variable");706 }707708 self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);709 self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);710 self.suggest_range_struct_destructuring(&mut err, path, source);711 self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);712713 if let Some((span, label)) = base_error.span_label {714 err.span_label(span, label);715 }716 for note in &base_error.notes {717 err.note(note.clone());718 }719720 if let Some(ref sugg) = base_error.suggestion {721 err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);722 }723724 self.suggest_changing_type_to_const_param(&mut err, res, source, path, following_seg, span);725 self.explain_functions_in_pattern(&mut err, res, source);726727 if self.suggest_pattern_match_with_let(&mut err, source, span) {728 // Fallback label.729 err.span_label(base_error.span, base_error.fallback_label);730 return (err, Vec::new());731 }732733 self.suggest_self_or_self_ref(&mut err, path, span);734 self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error);735 self.detect_rtn_with_fully_qualified_path(736 &mut err,737 path,738 following_seg,739 span,740 source,741 res,742 qself,743 );744 if self.suggest_self_ty(&mut err, source, path, span)745 || self.suggest_self_value(&mut err, source, path, span)746 {747 return (err, Vec::new());748 }749750 if let Some((did, item)) = self.lookup_doc_alias_name(path, source.namespace()) {751 let item_name = item.name;752 let suggestion_name = self.r.tcx.item_name(did);753 err.span_suggestion(754 item.span,755 format!("`{suggestion_name}` has a name defined in the doc alias attribute as `{item_name}`"),756 suggestion_name,757 Applicability::MaybeIncorrect758 );759760 return (err, Vec::new());761 };762763 let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed(764 &mut err,765 source,766 path,767 following_seg,768 span,769 res,770 &base_error,771 );772 if found {773 return (err, candidates);774 }775776 if self.suggest_shadowed(&mut err, source, path, following_seg, span) {777 // if there is already a shadowed name, don'suggest candidates for importing778 candidates.clear();779 }780781 let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);782 fallback |= self.suggest_typo(783 &mut err,784 source,785 path,786 following_seg,787 span,788 &base_error,789 suggested_candidates,790 );791792 if fallback {793 // Fallback label.794 err.span_label(base_error.span, base_error.fallback_label);795 }796 self.err_code_special_cases(&mut err, source, path, span);797798 let module = base_error.module.unwrap_or_else(|| CRATE_DEF_ID.to_def_id());799 self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);800801 (err, candidates)802 }803804 fn detect_rtn_with_fully_qualified_path(805 &self,806 err: &mut Diag<'_>,807 path: &[Segment],808 following_seg: Option<&Segment>,809 span: Span,810 source: PathSource<'_, '_, '_>,811 res: Option<Res>,812 qself: Option<&QSelf>,813 ) {814 if let Some(Res::Def(DefKind::AssocFn, _)) = res815 && let PathSource::TraitItem(TypeNS, _) = source816 && let None = following_seg817 && let Some(qself) = qself818 && let TyKind::Path(None, ty_path) = &qself.ty.kind819 && ty_path.segments.len() == 1820 && self.diag_metadata.current_where_predicate.is_some()821 {822 err.span_suggestion_verbose(823 span,824 "you might have meant to use the return type notation syntax",825 format!("{}::{}(..)", ty_path.segments[0].ident, path[path.len() - 1].ident),826 Applicability::MaybeIncorrect,827 );828 }829 }830831 fn detect_assoc_type_constraint_meant_as_path(832 &self,833 err: &mut Diag<'_>,834 base_error: &BaseError,835 ) {836 let Some(ty) = self.diag_metadata.current_type_path else {837 return;838 };839 let TyKind::Path(_, path) = &ty.kind else {840 return;841 };842 for segment in &path.segments {843 let Some(params) = &segment.args else {844 continue;845 };846 let ast::GenericArgs::AngleBracketed(params) = params.deref() else {847 continue;848 };849 for param in ¶ms.args {850 let ast::AngleBracketedArg::Constraint(constraint) = param else {851 continue;852 };853 let ast::AssocItemConstraintKind::Bound { bounds } = &constraint.kind else {854 continue;855 };856 for bound in bounds {857 let ast::GenericBound::Trait(trait_ref) = bound else {858 continue;859 };860 if trait_ref.modifiers == ast::TraitBoundModifiers::NONE861 && base_error.span == trait_ref.span862 {863 err.span_suggestion_verbose(864 constraint.ident.span.between(trait_ref.span),865 "you might have meant to write a path instead of an associated type bound",866 "::",867 Applicability::MachineApplicable,868 );869 }870 }871 }872 }873 }874875 fn suggest_self_or_self_ref(&mut self, err: &mut Diag<'_>, path: &[Segment], span: Span) {876 if !self.self_type_is_available() {877 return;878 }879 let Some(path_last_segment) = path.last() else { return };880 let item_str = path_last_segment.ident;881 // Emit help message for fake-self from other languages (e.g., `this` in JavaScript).882 if ["this", "my"].contains(&item_str.as_str()) {883 err.span_suggestion_short(884 span,885 "you might have meant to use `self` here instead",886 "self",887 Applicability::MaybeIncorrect,888 );889 if !self.self_value_is_available(path[0].ident.span) {890 if let Some((FnKind::Fn(_, _, ast::Fn { sig, .. }), fn_span)) =891 &self.diag_metadata.current_function892 {893 let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {894 (param.span.shrink_to_lo(), "&self, ")895 } else {896 (897 self.r898 .tcx899 .sess900 .source_map()901 .span_through_char(*fn_span, '(')902 .shrink_to_hi(),903 "&self",904 )905 };906 err.span_suggestion_verbose(907 span,908 "if you meant to use `self`, you are also missing a `self` receiver \909 argument",910 sugg,911 Applicability::MaybeIncorrect,912 );913 }914 }915 }916 }917918 fn try_lookup_name_relaxed(919 &mut self,920 err: &mut Diag<'_>,921 source: PathSource<'_, '_, '_>,922 path: &[Segment],923 following_seg: Option<&Segment>,924 span: Span,925 res: Option<Res>,926 base_error: &BaseError,927 ) -> (bool, FxHashSet<String>, Vec<ImportSuggestion>) {928 let span = match following_seg {929 Some(_) if path[0].ident.span.eq_ctxt(path[path.len() - 1].ident.span) => {930 // The path `span` that comes in includes any following segments, which we don't931 // want to replace in the suggestions.932 path[0].ident.span.to(path[path.len() - 1].ident.span)933 }934 _ => span,935 };936 let mut suggested_candidates = FxHashSet::default();937 // Try to lookup name in more relaxed fashion for better error reporting.938 let ident = path.last().unwrap().ident;939 let is_expected = &|res| source.is_expected(res);940 let ns = source.namespace();941 let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));942 let path_str = Segment::names_to_string(path);943 let ident_span = path.last().map_or(span, |ident| ident.ident.span);944 let mut candidates = self945 .r946 .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)947 .into_iter()948 .filter(|ImportSuggestion { did, .. }| {949 match (did, res.and_then(|res| res.opt_def_id())) {950 (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,951 _ => true,952 }953 })954 .collect::<Vec<_>>();955 // Try to filter out intrinsics candidates, as long as we have956 // some other candidates to suggest.957 let intrinsic_candidates: Vec<_> = candidates958 .extract_if(.., |sugg| {959 let path = path_names_to_string(&sugg.path);960 path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")961 })962 .collect();963 if candidates.is_empty() {964 // Put them back if we have no more candidates to suggest...965 candidates = intrinsic_candidates;966 }967 let crate_def_id = CRATE_DEF_ID.to_def_id();968 if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {969 let mut enum_candidates: Vec<_> = self970 .r971 .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)972 .into_iter()973 .map(|suggestion| import_candidate_to_enum_paths(&suggestion))974 .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::"))975 .collect();976 if !enum_candidates.is_empty() {977 enum_candidates.sort();978979 // Contextualize for E0425 "cannot find type", but don't belabor the point980 // (that it's a variant) for E0573 "expected type, found variant".981 let preamble = if res.is_none() {982 let others = match enum_candidates.len() {983 1 => String::new(),984 2 => " and 1 other".to_owned(),985 n => format!(" and {n} others"),986 };987 format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others)988 } else {989 String::new()990 };991 let msg = format!("{preamble}try using the variant's enum");992993 suggested_candidates.extend(994 enum_candidates995 .iter()996 .map(|(_variant_path, enum_ty_path)| enum_ty_path.clone()),997 );998 err.span_suggestions(999 span,1000 msg,1001 enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),1002 Applicability::MachineApplicable,1003 );1004 }1005 }10061007 // Try finding a suitable replacement.1008 let typo_sugg = self1009 .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected)1010 .to_opt_suggestion()1011 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));1012 if let [segment] = path1013 && !matches!(source, PathSource::Delegation)1014 && self.self_type_is_available()1015 {1016 if let Some(candidate) =1017 self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())1018 {1019 let self_is_available = self.self_value_is_available(segment.ident.span);1020 // Account for `Foo { field }` when suggesting `self.field` so we result on1021 // `Foo { field: self.field }`.1022 let pre = match source {1023 PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. }))1024 if expr1025 .fields1026 .iter()1027 .any(|f| f.ident == segment.ident && f.is_shorthand) =>1028 {1029 format!("{path_str}: ")1030 }1031 _ => String::new(),1032 };1033 match candidate {1034 AssocSuggestion::Field(field_span) => {1035 if self_is_available {1036 let source_map = self.r.tcx.sess.source_map();1037 let field_is_format_named_arg = matches!(1038 span.desugaring_kind(),1039 Some(DesugaringKind::FormatLiteral { .. })1040 ) && source_map1041 .span_to_source(span, |s, start, _| {1042 Ok(s.get(start.saturating_sub(1)..start) == Some("{"))1043 })1044 .unwrap_or(false);1045 if field_is_format_named_arg {1046 err.help(1047 format!("you might have meant to use the available field in a format string: `\"{{}}\", self.{}`", segment.ident.name),1048 );1049 } else {1050 err.span_suggestion_verbose(1051 span.shrink_to_lo(),1052 "you might have meant to use the available field",1053 format!("{pre}self."),1054 Applicability::MaybeIncorrect,1055 );1056 }1057 } else {1058 err.span_label(field_span, "a field by that name exists in `Self`");1059 }1060 }1061 AssocSuggestion::MethodWithSelf { called } if self_is_available => {1062 let msg = if called {1063 "you might have meant to call the method"1064 } else {1065 "you might have meant to refer to the method"1066 };1067 err.span_suggestion_verbose(1068 span.shrink_to_lo(),1069 msg,1070 "self.",1071 Applicability::MachineApplicable,1072 );1073 }1074 AssocSuggestion::MethodWithSelf { .. }1075 | AssocSuggestion::AssocFn { .. }1076 | AssocSuggestion::AssocConst1077 | AssocSuggestion::AssocType => {1078 err.span_suggestion_verbose(1079 span.shrink_to_lo(),1080 format!("you might have meant to {}", candidate.action()),1081 "Self::",1082 Applicability::MachineApplicable,1083 );1084 }1085 }1086 self.r.add_typo_suggestion(err, typo_sugg, ident_span);1087 return (true, suggested_candidates, candidates);1088 }10891090 // If the first argument in call is `self` suggest calling a method.1091 if let Some((call_span, args_span)) = self.call_has_self_arg(source) {1092 let mut args_snippet = String::new();1093 if let Some(args_span) = args_span1094 && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span)1095 {1096 args_snippet = snippet;1097 }10981099 if let Some(Res::Def(DefKind::Struct, def_id)) = res {1100 if let Some(ctor) = self.r.struct_ctor(def_id)1101 && ctor.has_private_fields(self.parent_scope.module, self.r)1102 {1103 if matches!(1104 ctor.res,1105 Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _)1106 ) {1107 self.update_err_for_private_tuple_struct_fields(err, &source, def_id);1108 }1109 err.note("constructor is not visible here due to private fields");1110 }1111 } else {1112 err.span_suggestion(1113 call_span,1114 format!("try calling `{ident}` as a method"),1115 format!("self.{path_str}({args_snippet})"),1116 Applicability::MachineApplicable,1117 );1118 }11191120 return (true, suggested_candidates, candidates);1121 }1122 }11231124 // Try context-dependent help if relaxed lookup didn't work.1125 if let Some(res) = res {1126 if self.smart_resolve_context_dependent_help(1127 err,1128 span,1129 source,1130 path,1131 res,1132 &path_str,1133 &base_error.fallback_label,1134 ) {1135 // We do this to avoid losing a secondary span when we override the main error span.1136 self.r.add_typo_suggestion(err, typo_sugg, ident_span);1137 return (true, suggested_candidates, candidates);1138 }1139 }11401141 // Try to find in last block rib1142 if let Some(rib) = &self.last_block_rib {1143 for (ident, &res) in &rib.bindings {1144 if let Res::Local(_) = res1145 && path.len() == 11146 && ident.span.eq_ctxt(path[0].ident.span)1147 && ident.name == path[0].ident.name1148 {1149 err.span_help(1150 ident.span,1151 format!("the binding `{path_str}` is available in a different scope in the same function"),1152 );1153 return (true, suggested_candidates, candidates);1154 }1155 }1156 }11571158 if candidates.is_empty() {1159 candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);1160 }11611162 (false, suggested_candidates, candidates)1163 }11641165 fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> {1166 let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| {1167 for resolution in r.resolutions(m).values() {1168 let Some(did) =1169 resolution.borrow(r).best_decl().and_then(|binding| binding.res().opt_def_id())1170 else {1171 continue;1172 };1173 if did.is_local() {1174 // We don't record the doc alias name in the local crate1175 // because the people who write doc alias are usually not1176 // confused by them.1177 continue;1178 }1179 if let Some(d) = hir::find_attr!(r.tcx, did, Doc(d) => d)1180 && d.aliases.contains_key(&item_name)1181 {1182 return Some(did);1183 }1184 }1185 None1186 };11871188 if path.len() == 1 {1189 for rib in self.ribs[ns].iter().rev() {1190 let item = path[0].ident;1191 if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind1192 && let Some(did) = find_doc_alias_name(self.r, module.to_module(), item.name)1193 {1194 return Some((did, item));1195 }1196 }1197 } else {1198 // Finds to the last resolved module item in the path1199 // and searches doc aliases within that module.1200 //1201 // Example: For the path `a::b::last_resolved::not_exist::c::d`,1202 // we will try to find any item has doc aliases named `not_exist`1203 // in `last_resolved` module.1204 //1205 // - Use `skip(1)` because the final segment must remain unresolved.1206 for (idx, seg) in path.iter().enumerate().rev().skip(1) {1207 let Some(id) = seg.id else {1208 continue;1209 };1210 let Some(res) = self.r.partial_res_map.get(&id) else {1211 continue;1212 };1213 if let Res::Def(DefKind::Mod, module) = res.expect_full_res()1214 && let module = self.r.expect_module(module)1215 && let item = path[idx + 1].ident1216 && let Some(did) = find_doc_alias_name(self.r, module, item.name)1217 {1218 return Some((did, item));1219 }1220 break;1221 }1222 }1223 None1224 }12251226 fn suggest_trait_and_bounds(1227 &self,1228 err: &mut Diag<'_>,1229 source: PathSource<'_, '_, '_>,1230 res: Option<Res>,1231 span: Span,1232 base_error: &BaseError,1233 ) -> bool {1234 let is_macro =1235 base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();1236 let mut fallback = false;12371238 if let (1239 PathSource::Trait(AliasPossibility::Maybe),1240 Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),1241 false,1242 ) = (source, res, is_macro)1243 && let Some(bounds @ [first_bound, .., last_bound]) =1244 self.diag_metadata.current_trait_object1245 {1246 fallback = true;1247 let spans: Vec<Span> = bounds1248 .iter()1249 .map(|bound| bound.span())1250 .filter(|&sp| sp != base_error.span)1251 .collect();12521253 let start_span = first_bound.span();1254 // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)1255 let end_span = last_bound.span();1256 // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)1257 let last_bound_span = spans.last().cloned().unwrap();1258 let mut multi_span: MultiSpan = spans.clone().into();1259 for sp in spans {1260 let msg = if sp == last_bound_span {1261 format!(1262 "...because of {these} bound{s}",1263 these = pluralize!("this", bounds.len() - 1),1264 s = pluralize!(bounds.len() - 1),1265 )1266 } else {1267 String::new()1268 };1269 multi_span.push_span_label(sp, msg);1270 }1271 multi_span.push_span_label(base_error.span, "expected this type to be a trait...");1272 err.span_help(1273 multi_span,1274 "`+` is used to constrain a \"trait object\" type with lifetimes or \1275 auto-traits; structs and enums can't be bound in that way",1276 );1277 if bounds.iter().all(|bound| match bound {1278 ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,1279 ast::GenericBound::Trait(tr) => tr.span == base_error.span,1280 }) {1281 let mut sugg = vec![];1282 if base_error.span != start_span {1283 sugg.push((start_span.until(base_error.span), String::new()));1284 }1285 if base_error.span != end_span {1286 sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));1287 }12881289 err.multipart_suggestion(1290 "if you meant to use a type and not a trait here, remove the bounds",1291 sugg,1292 Applicability::MaybeIncorrect,1293 );1294 }1295 }12961297 fallback |= self.restrict_assoc_type_in_where_clause(span, err);1298 fallback1299 }13001301 fn suggest_typo(1302 &mut self,1303 err: &mut Diag<'_>,1304 source: PathSource<'_, 'ast, 'ra>,1305 path: &[Segment],1306 following_seg: Option<&Segment>,1307 span: Span,1308 base_error: &BaseError,1309 suggested_candidates: FxHashSet<String>,1310 ) -> bool {1311 let is_expected = &|res| source.is_expected(res);1312 let ident_span = path.last().map_or(span, |ident| ident.ident.span);13131314 // Prefer suggestions based on associated types from in-scope bounds (e.g. `T::Item`)1315 // over purely edit-distance-based identifier suggestions.1316 // Otherwise suggestions could be verbose.1317 if self.suggest_assoc_type_from_bounds(err, source, path, ident_span) {1318 return false;1319 }13201321 let typo_sugg =1322 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1323 let mut fallback = true;1324 let typo_sugg = typo_sugg1325 .to_opt_suggestion()1326 .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str()));1327 self.r.add_typo_suggestion(err, typo_sugg, ident_span);13281329 match self.diag_metadata.current_let_binding {1330 Some((pat_sp, Some(ty_sp), None))1331 if ty_sp.contains(base_error.span) && base_error.could_be_expr =>1332 {1333 err.span_suggestion_verbose(1334 pat_sp.between(ty_sp),1335 "use `=` if you meant to assign",1336 " = ",1337 Applicability::MaybeIncorrect,1338 );1339 }1340 _ => {}1341 }13421343 // If the trait has a single item (which wasn't matched by the algorithm), suggest it1344 let suggestion = self.get_single_associated_item(path, &source, is_expected);1345 self.r.add_typo_suggestion(err, suggestion, ident_span);13461347 if self.let_binding_suggestion(err, ident_span) {1348 fallback = false;1349 }13501351 fallback1352 }13531354 fn suggest_shadowed(1355 &mut self,1356 err: &mut Diag<'_>,1357 source: PathSource<'_, '_, '_>,1358 path: &[Segment],1359 following_seg: Option<&Segment>,1360 span: Span,1361 ) -> bool {1362 let is_expected = &|res| source.is_expected(res);1363 let typo_sugg =1364 self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);1365 let is_in_same_file = &|sp1, sp2| {1366 let source_map = self.r.tcx.sess.source_map();1367 let file1 = source_map.span_to_filename(sp1);1368 let file2 = source_map.span_to_filename(sp2);1369 file1 == file21370 };1371 // print 'you might have meant' if the candidate is (1) is a shadowed name with1372 // accessible definition and (2) either defined in the same crate as the typo1373 // (could be in a different file) or introduced in the same file as the typo1374 // (could belong to a different crate)1375 if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg1376 && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))1377 {1378 err.span_label(1379 sugg_span,1380 format!("you might have meant to refer to this {}", res.descr()),1381 );1382 return true;1383 }1384 false1385 }13861387 fn err_code_special_cases(1388 &mut self,1389 err: &mut Diag<'_>,1390 source: PathSource<'_, '_, '_>,1391 path: &[Segment],1392 span: Span,1393 ) {1394 if let Some(err_code) = err.code {1395 if err_code == E0425 {1396 for label_rib in &self.label_ribs {1397 for (label_ident, node_id) in &label_rib.bindings {1398 let ident = path.last().unwrap().ident;1399 if format!("'{ident}") == label_ident.to_string() {1400 err.span_label(label_ident.span, "a label with a similar name exists");1401 if let PathSource::Expr(Some(Expr {1402 kind: ExprKind::Break(None, Some(_)),1403 ..1404 })) = source1405 {1406 err.span_suggestion(1407 span,1408 "use the similarly named label",1409 label_ident.name,1410 Applicability::MaybeIncorrect,1411 );1412 // Do not lint against unused label when we suggest them.1413 self.diag_metadata.unused_labels.swap_remove(node_id);1414 }1415 }1416 }1417 }14181419 self.suggest_ident_hidden_by_hygiene(err, path, span);1420 // cannot find type in this scope1421 if let Some(correct) = Self::likely_rust_type(path) {1422 err.span_suggestion(1423 span,1424 "perhaps you intended to use this type",1425 correct,1426 Applicability::MaybeIncorrect,1427 );1428 }1429 }1430 }1431 }14321433 fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], span: Span) {1434 let [segment] = path else { return };14351436 let ident = segment.ident;1437 let callsite_span = span.source_callsite();1438 for rib in self.ribs[ValueNS].iter().rev() {1439 for (binding_ident, _) in &rib.bindings {1440 // Case 1: the identifier is defined in the same scope as the macro is called1441 if binding_ident.name == ident.name1442 && !binding_ident.span.eq_ctxt(span)1443 && !binding_ident.span.from_expansion()1444 && binding_ident.span.lo() < callsite_span.lo()1445 {1446 err.span_help(1447 binding_ident.span,1448 "an identifier with the same name exists, but is not accessible due to macro hygiene",1449 );1450 return;1451 }14521453 // Case 2: the identifier is defined in a macro call in the same scope1454 if binding_ident.name == ident.name1455 && binding_ident.span.from_expansion()1456 && binding_ident.span.source_callsite().eq_ctxt(callsite_span)1457 && binding_ident.span.source_callsite().lo() < callsite_span.lo()1458 {1459 err.span_help(1460 binding_ident.span,1461 "an identifier with the same name is defined here, but is not accessible due to macro hygiene",1462 );1463 return;1464 }1465 }1466 }1467 }14681469 /// Emit special messages for unresolved `Self` and `self`.1470 fn suggest_self_ty(1471 &self,1472 err: &mut Diag<'_>,1473 source: PathSource<'_, '_, '_>,1474 path: &[Segment],1475 span: Span,1476 ) -> bool {1477 if !is_self_type(path, source.namespace()) {1478 return false;1479 }1480 err.code(E0411);1481 err.span_label(span, "`Self` is only available in impls, traits, and type definitions");1482 if let Some(item) = self.diag_metadata.current_item1483 && let Some(ident) = item.kind.ident()1484 {1485 err.span_label(1486 ident.span,1487 format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1488 );1489 }1490 true1491 }14921493 fn suggest_self_value(1494 &mut self,1495 err: &mut Diag<'_>,1496 source: PathSource<'_, '_, '_>,1497 path: &[Segment],1498 span: Span,1499 ) -> bool {1500 if !is_self_value(path, source.namespace()) {1501 return false;1502 }15031504 debug!("smart_resolve_path_fragment: E0424, source={:?}", source);1505 err.code(E0424);1506 err.span_label(1507 span,1508 match source {1509 PathSource::Pat => {1510 "`self` value is a keyword and may not be bound to variables or shadowed"1511 }1512 _ => "`self` value is a keyword only available in methods with a `self` parameter",1513 },1514 );15151516 // using `let self` is wrong even if we're not in an associated method or if we're in a macro expansion.1517 // So, we should return early if we're in a pattern, see issue #143134.1518 if matches!(source, PathSource::Pat) {1519 return true;1520 }15211522 let is_assoc_fn = self.self_type_is_available();1523 let self_from_macro = "a `self` parameter, but a macro invocation can only \1524 access identifiers it receives from parameters";1525 if let Some((fn_kind, fn_span)) = &self.diag_metadata.current_function {1526 // The current function has a `self` parameter, but we were unable to resolve1527 // a reference to `self`. This can only happen if the `self` identifier we1528 // are resolving came from a different hygiene context or a variable binding.1529 // But variable binding error is returned early above.1530 if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) {1531 err.span_label(*fn_span, format!("this function has {self_from_macro}"));1532 } else {1533 let doesnt = if is_assoc_fn {1534 let (span, sugg) = fn_kind1535 .decl()1536 .inputs1537 .get(0)1538 .map(|p| (p.span.shrink_to_lo(), "&self, "))1539 .unwrap_or_else(|| {1540 // Try to look for the "(" after the function name, if possible.1541 // This avoids placing the suggestion into the visibility specifier.1542 let span = fn_kind1543 .ident()1544 .map_or(*fn_span, |ident| fn_span.with_lo(ident.span.hi()));1545 (1546 self.r1547 .tcx1548 .sess1549 .source_map()1550 .span_through_char(span, '(')1551 .shrink_to_hi(),1552 "&self",1553 )1554 });1555 err.span_suggestion_verbose(1556 span,1557 "add a `self` receiver parameter to make the associated `fn` a method",1558 sugg,1559 Applicability::MaybeIncorrect,1560 );1561 "doesn't"1562 } else {1563 "can't"1564 };1565 if let Some(ident) = fn_kind.ident() {1566 err.span_label(1567 ident.span,1568 format!("this function {doesnt} have a `self` parameter"),1569 );1570 }1571 }1572 } else if let Some(item) = self.diag_metadata.current_item {1573 if matches!(item.kind, ItemKind::Delegation(..)) {1574 err.span_label(item.span, format!("delegation supports {self_from_macro}"));1575 } else {1576 let span = if let Some(ident) = item.kind.ident() { ident.span } else { item.span };1577 err.span_label(1578 span,1579 format!("`self` not allowed in {} {}", item.kind.article(), item.kind.descr()),1580 );1581 }1582 }1583 true1584 }15851586 fn detect_missing_binding_available_from_pattern(1587 &self,1588 err: &mut Diag<'_>,1589 path: &[Segment],1590 following_seg: Option<&Segment>,1591 ) {1592 let [segment] = path else { return };1593 let None = following_seg else { return };1594 for rib in self.ribs[ValueNS].iter().rev() {1595 let patterns_with_skipped_bindings =1596 self.r.tcx.with_stable_hashing_context(|mut hcx| {1597 rib.patterns_with_skipped_bindings.to_sorted(&mut hcx, true)1598 });1599 for (def_id, spans) in patterns_with_skipped_bindings {1600 if let DefKind::Struct | DefKind::Variant = self.r.tcx.def_kind(*def_id)1601 && let Some(fields) = self.r.field_idents(*def_id)1602 {1603 for field in fields {1604 if field.name == segment.ident.name {1605 if spans.iter().all(|(_, had_error)| had_error.is_err()) {1606 // This resolution error will likely be fixed by fixing a1607 // syntax error in a pattern, so it is irrelevant to the user.1608 let multispan: MultiSpan =1609 spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into();1610 err.span_note(1611 multispan,1612 "this pattern had a recovered parse error which likely lost \1613 the expected fields",1614 );1615 err.downgrade_to_delayed_bug();1616 }1617 let ty = self.r.tcx.item_name(*def_id);1618 for (span, _) in spans {1619 err.span_label(1620 *span,1621 format!(1622 "this pattern doesn't include `{field}`, which is \1623 available in `{ty}`",1624 ),1625 );1626 }1627 }1628 }1629 }1630 }1631 }1632 }16331634 fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) {1635 let Some(pat) = self.diag_metadata.current_pat else { return };1636 let (bound, side, range) = match &pat.kind {1637 ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range),1638 ast::PatKind::Range(None, Some(bound), range) => (bound, Side::End, range),1639 _ => return,1640 };1641 if let ExprKind::Path(None, range_path) = &bound.kind1642 && let [segment] = &range_path.segments[..]1643 && let [s] = path1644 && segment.ident == s.ident1645 && segment.ident.span.eq_ctxt(range.span)1646 {1647 // We've encountered `[first, rest..]` (#88404) or `[first, ..rest]` (#120591)1648 // where the user might have meant `[first, rest @ ..]`.1649 let (span, snippet) = match side {1650 Side::Start => (segment.ident.span.between(range.span), " @ ".into()),1651 Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),1652 };1653 err.subdiagnostic(diagnostics::UnexpectedResUseAtOpInSlicePatWithRangeSugg {1654 span,1655 ident: segment.ident,1656 snippet,1657 });1658 }16591660 enum Side {1661 Start,1662 End,1663 }1664 }16651666 fn suggest_range_struct_destructuring(1667 &mut self,1668 err: &mut Diag<'_>,1669 path: &[Segment],1670 source: PathSource<'_, '_, '_>,1671 ) {1672 if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) {1673 return;1674 }16751676 let Some(pat) = self.diag_metadata.current_pat else { return };1677 let ast::PatKind::Range(start, end, end_kind) = &pat.kind else { return };16781679 let [segment] = path else { return };1680 let failing_span = segment.ident.span;16811682 let in_start = start.as_ref().is_some_and(|e| e.span.contains(failing_span));1683 let in_end = end.as_ref().is_some_and(|e| e.span.contains(failing_span));16841685 if !in_start && !in_end {1686 return;1687 }16881689 let start_snippet =1690 start.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());1691 let end_snippet =1692 end.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());16931694 let field = |name: &str, val: String| {1695 if val == name { val } else { format!("{name}: {val}") }1696 };16971698 let mut resolve_short_name = |short: Symbol, full: &str| -> String {1699 let ident = Ident::with_dummy_span(short);1700 let path = Segment::from_path(&Path::from_ident(ident));17011702 match self.resolve_path(&path, Some(TypeNS), None, PathSource::Type) {1703 PathResult::NonModule(..) => short.to_string(),1704 _ => full.to_string(),1705 }1706 };1707 // FIXME(new_range): Also account for new range types1708 let (struct_path, fields) = match (start_snippet, end_snippet, &end_kind.node) {1709 (Some(start), Some(end), ast::RangeEnd::Excluded) => (1710 resolve_short_name(sym::Range, "std::ops::Range"),1711 vec![field("start", start), field("end", end)],1712 ),1713 (Some(start), Some(end), ast::RangeEnd::Included(_)) => (1714 resolve_short_name(sym::RangeInclusive, "std::ops::RangeInclusive"),1715 vec![field("start", start), field("end", end)],1716 ),1717 (Some(start), None, _) => (1718 resolve_short_name(sym::RangeFrom, "std::ops::RangeFrom"),1719 vec![field("start", start)],1720 ),1721 (None, Some(end), ast::RangeEnd::Excluded) => {1722 (resolve_short_name(sym::RangeTo, "std::ops::RangeTo"), vec![field("end", end)])1723 }1724 (None, Some(end), ast::RangeEnd::Included(_)) => (1725 resolve_short_name(sym::RangeToInclusive, "std::ops::RangeToInclusive"),1726 vec![field("end", end)],1727 ),1728 _ => return,1729 };17301731 err.span_suggestion_verbose(1732 pat.span,1733 format!("if you meant to destructure a range use a struct pattern"),1734 format!("{} {{ {} }}", struct_path, fields.join(", ")),1735 Applicability::MaybeIncorrect,1736 );17371738 err.note(1739 "range patterns match against the start and end of a range; \1740 to bind the components, use a struct pattern",1741 );1742 }17431744 fn suggest_swapping_misplaced_self_ty_and_trait(1745 &mut self,1746 err: &mut Diag<'_>,1747 source: PathSource<'_, 'ast, 'ra>,1748 res: Option<Res>,1749 span: Span,1750 ) {1751 if let Some((trait_ref, self_ty)) =1752 self.diag_metadata.currently_processing_impl_trait.clone()1753 && let TyKind::Path(_, self_ty_path) = &self_ty.kind1754 && let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1755 self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source)1756 && module.def_kind() == Some(DefKind::Trait)1757 && trait_ref.path.span == span1758 && let PathSource::Trait(_) = source1759 && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res1760 && let Ok(self_ty_str) = self.r.tcx.sess.source_map().span_to_snippet(self_ty.span)1761 && let Ok(trait_ref_str) =1762 self.r.tcx.sess.source_map().span_to_snippet(trait_ref.path.span)1763 {1764 err.multipart_suggestion(1765 "`impl` items mention the trait being implemented first and the type it is being implemented for second",1766 vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],1767 Applicability::MaybeIncorrect,1768 );1769 }1770 }17711772 fn explain_functions_in_pattern(1773 &self,1774 err: &mut Diag<'_>,1775 res: Option<Res>,1776 source: PathSource<'_, '_, '_>,1777 ) {1778 let PathSource::TupleStruct(_, _) = source else { return };1779 let Some(Res::Def(DefKind::Fn, _)) = res else { return };1780 err.primary_message("expected a pattern, found a function call");1781 err.note("function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>");1782 }17831784 fn suggest_changing_type_to_const_param(1785 &self,1786 err: &mut Diag<'_>,1787 res: Option<Res>,1788 source: PathSource<'_, '_, '_>,1789 path: &[Segment],1790 following_seg: Option<&Segment>,1791 span: Span,1792 ) {1793 if let PathSource::Expr(None) = source1794 && let Some(Res::Def(DefKind::TyParam, _)) = res1795 && following_seg.is_none()1796 && let [segment] = path1797 {1798 // We have something like1799 // impl<T, N> From<[T; N]> for VecWrapper<T> {1800 // fn from(slice: [T; N]) -> Self {1801 // VecWrapper(slice.to_vec())1802 // }1803 // }1804 // where `N` is a type param but should likely have been a const param.1805 let Some(item) = self.diag_metadata.current_item else { return };1806 let Some(generics) = item.kind.generics() else { return };1807 let Some(span) = generics.params.iter().find_map(|param| {1808 // Only consider type params with no bounds.1809 if param.bounds.is_empty() && param.ident.name == segment.ident.name {1810 Some(param.ident.span)1811 } else {1812 None1813 }1814 }) else {1815 return;1816 };1817 err.subdiagnostic(diagnostics::UnexpectedResChangeTyParamToConstParamSugg {1818 before: span.shrink_to_lo(),1819 after: span.shrink_to_hi(),1820 });1821 return;1822 }1823 let PathSource::Trait(_) = source else { return };18241825 // We don't include `DefKind::Str` and `DefKind::AssocTy` as they can't be reached here anyway.1826 let applicability = match res {1827 Some(Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_) | PrimTy::Bool | PrimTy::Char)) => {1828 Applicability::MachineApplicable1829 }1830 // FIXME(const_generics): Add `DefKind::TyParam` and `SelfTyParam` once we support generic1831 // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the1832 // benefits of including them here outweighs the small number of false positives.1833 Some(Res::Def(DefKind::Struct | DefKind::Enum, _))1834 if self.r.features.adt_const_params() || self.r.features.min_adt_const_params() =>1835 {1836 Applicability::MaybeIncorrect1837 }1838 _ => return,1839 };18401841 let Some(item) = self.diag_metadata.current_item else { return };1842 let Some(generics) = item.kind.generics() else { return };18431844 let param = generics.params.iter().find_map(|param| {1845 // Only consider type params with exactly one trait bound.1846 if let [bound] = &*param.bounds1847 && let ast::GenericBound::Trait(tref) = bound1848 && tref.modifiers == ast::TraitBoundModifiers::NONE1849 && tref.span == span1850 && param.ident.span.eq_ctxt(span)1851 {1852 Some(param.ident.span)1853 } else {1854 None1855 }1856 });18571858 if let Some(param) = param {1859 err.subdiagnostic(diagnostics::UnexpectedResChangeTyToConstParamSugg {1860 span: param.shrink_to_lo(),1861 applicability,1862 });1863 }1864 }18651866 fn suggest_pattern_match_with_let(1867 &self,1868 err: &mut Diag<'_>,1869 source: PathSource<'_, '_, '_>,1870 span: Span,1871 ) -> bool {1872 if let PathSource::Expr(_) = source1873 && let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }) =1874 self.diag_metadata.in_if_condition1875 {1876 // Icky heuristic so we don't suggest:1877 // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)1878 // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)1879 if lhs.is_approximately_pattern() && lhs.span.contains(span) {1880 err.span_suggestion_verbose(1881 expr_span.shrink_to_lo(),1882 "you might have meant to use pattern matching",1883 "let ",1884 Applicability::MaybeIncorrect,1885 );1886 return true;1887 }1888 }1889 false1890 }18911892 fn get_single_associated_item(1893 &mut self,1894 path: &[Segment],1895 source: &PathSource<'_, 'ast, 'ra>,1896 filter_fn: &impl Fn(Res) -> bool,1897 ) -> Option<TypoSuggestion> {1898 if let crate::PathSource::TraitItem(_, _) = source {1899 let mod_path = &path[..path.len() - 1];1900 if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =1901 self.resolve_path(mod_path, None, None, *source)1902 {1903 let targets: Vec<_> = self1904 .r1905 .resolutions(module)1906 .iter()1907 .filter_map(|(key, resolution)| {1908 let resolution = resolution.borrow(self.r);1909 resolution.best_decl().map(|binding| binding.res()).and_then(|res| {1910 if filter_fn(res) {1911 Some((key.ident.name, resolution.orig_ident_span, res))1912 } else {1913 None1914 }1915 })1916 })1917 .collect();1918 if let &[(name, orig_ident_span, res)] = targets.as_slice() {1919 return Some(TypoSuggestion::single_item(name, orig_ident_span, res));1920 }1921 }1922 }1923 None1924 }19251926 /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.1927 fn restrict_assoc_type_in_where_clause(&self, span: Span, err: &mut Diag<'_>) -> bool {1928 // Detect that we are actually in a `where` predicate.1929 let Some(ast::WherePredicate {1930 kind:1931 ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {1932 bounded_ty,1933 bound_generic_params,1934 bounds,1935 }),1936 span: where_span,1937 ..1938 }) = self.diag_metadata.current_where_predicate1939 else {1940 return false;1941 };1942 if !bound_generic_params.is_empty() {1943 return false;1944 }19451946 // Confirm that the target is an associated type.1947 let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind else { return false };1948 // use this to verify that ident is a type param.1949 let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else { return false };1950 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::AssocTy, _))) {1951 return false;1952 }19531954 let peeled_ty = qself.ty.peel_refs();1955 let ast::TyKind::Path(None, type_param_path) = &peeled_ty.kind else { return false };1956 // Confirm that the `SelfTy` is a type parameter.1957 let Some(partial_res) = self.r.partial_res_map.get(&peeled_ty.id) else {1958 return false;1959 };1960 if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) {1961 return false;1962 }1963 let ([ast::PathSegment { args: None, .. }], [ast::GenericBound::Trait(poly_trait_ref)]) =1964 (&type_param_path.segments[..], &bounds[..])1965 else {1966 return false;1967 };1968 let [ast::PathSegment { ident, args: None, id }] =1969 &poly_trait_ref.trait_ref.path.segments[..]1970 else {1971 return false;1972 };1973 if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE {1974 return false;1975 }1976 if ident.span == span {1977 let Some(partial_res) = self.r.partial_res_map.get(&id) else {1978 return false;1979 };1980 if !matches!(partial_res.full_res(), Some(Res::Def(..))) {1981 return false;1982 }19831984 let Some(new_where_bound_predicate) =1985 mk_where_bound_predicate(path, poly_trait_ref, &qself.ty)1986 else {1987 return false;1988 };1989 err.span_suggestion_verbose(1990 *where_span,1991 format!("constrain the associated type to `{ident}`"),1992 where_bound_predicate_to_string(&new_where_bound_predicate),1993 Applicability::MaybeIncorrect,1994 );1995 }1996 true1997 }19981999 /// Check if the source is call expression and the first argument is `self`. If true,2000 /// return the span of whole call and the span for all arguments expect the first one (`self`).
Findings
✓ No findings reported for this file.