From 0b6af718d85a2639854ad09730f5f39aa5f70f9a Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:53:28 -0800 Subject: [PATCH] Create helper `maybe_report_similar_assoc_fn` --- .../rustc_hir_analysis/src/astconv/errors.rs | 51 ++++++++++++++++++ .../rustc_hir_analysis/src/astconv/mod.rs | 52 +------------------ tests/ui/suggestions/issue-109195.rs | 10 ++-- tests/ui/suggestions/issue-109195.stderr | 18 +++---- 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 407517b15ef..869e2184006 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -14,6 +14,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; +use rustc_middle::query::Key; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; @@ -859,6 +860,56 @@ pub(crate) fn complain_about_missing_associated_types( self.set_tainted_by_errors(err.emit()); } + + /// On ambiguous associated type, look for an associated function whose name matches the + /// extended path and, if found, emit an E0223 error with a structured suggestion. + /// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195) + pub(crate) fn maybe_report_similar_assoc_fn( + &self, + span: Span, + qself_ty: Ty<'tcx>, + qself: &hir::Ty<'_>, + ) -> Result<(), ErrorGuaranteed> { + let tcx = self.tcx(); + if let Some((_, node)) = tcx.hir().parent_iter(qself.hir_id).skip(1).next() + && let hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Path(hir::QPath::TypeRelative( + hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::TypeRelative( + _, + hir::PathSegment { ident: ident2, .. }, + )), + .. + }, + hir::PathSegment { ident: ident3, .. }, + )), + .. + }) = node + && let Some(ty_def_id) = qself_ty.ty_def_id() + && let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id) + && let name = format!("{ident2}_{ident3}") + && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx + .associated_items(inherent_impl) + .filter_by_name_unhygienic(Symbol::intern(&name)) + .next() + { + let reported = + struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type") + .with_span_suggestion_verbose( + ident2.span.to(ident3.span), + format!("there is an associated function with a similar name: `{name}`"), + name, + Applicability::MaybeIncorrect, + ) + .emit(); + self.set_tainted_by_errors(reported); + Err(reported) + } else { + Ok(()) + } + } } /// Emits an error regarding forbidden type binding associations diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index d69f366a66c..cfd38fb48f4 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -29,7 +29,6 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::query::Key; use rustc_middle::ty::{ self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, IsSuggestable, ParamEnv, Ty, TyCtxt, TypeVisitableExt, @@ -1374,56 +1373,7 @@ pub fn associated_path_to_ty( ) .emit() // Already reported in an earlier stage. } else { - // suggest methods that look similar to the path - // e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195) - for (_, node) in tcx.hir().parent_iter(qself.hir_id) { - if let hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::Path(hir::QPath::TypeRelative( - hir::Ty { - kind: - hir::TyKind::Path(hir::QPath::TypeRelative( - _, - hir::PathSegment { ident: ident2, .. }, - )), - .. - }, - hir::PathSegment { ident: ident3, .. }, - )), - .. - }) = node - { - let name = format!("{ident2}_{ident3}"); - if if let Some(ty_def_id) = qself_ty.ty_def_id() - && let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id) - && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx - .associated_items(inherent_impl) - .filter_by_name_unhygienic(Symbol::intern(&name)) - .next() - { - true - } else { - qself_ty.is_str() - && ["from_utf8", "from_utf8_mut"].contains(&name.as_str()) - } { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0223, - "ambiguous associated type" - ) - .with_span_suggestion_verbose( - ident2.span.to(ident3.span), - format!("you might have meant to use `{name}`"), - name, - Applicability::MaybeIncorrect, - ) - .emit(); - self.set_tainted_by_errors(reported); - return Err(reported); - } - } - } + self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?; let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); diff --git a/tests/ui/suggestions/issue-109195.rs b/tests/ui/suggestions/issue-109195.rs index badb859dbb7..cc499b0d776 100644 --- a/tests/ui/suggestions/issue-109195.rs +++ b/tests/ui/suggestions/issue-109195.rs @@ -1,20 +1,20 @@ fn main() { String::from::utf8; //~^ ERROR ambiguous associated type [E0223] - //~| HELP you might have meant to use `from_utf8` + //~| HELP there is an associated function with a similar name: `from_utf8` String::from::utf8(); //~^ ERROR ambiguous associated type [E0223] - //~| HELP you might have meant to use `from_utf8` + //~| HELP there is an associated function with a similar name: `from_utf8` String::from::utf16(); //~^ ERROR ambiguous associated type [E0223] - //~| HELP you might have meant to use `from_utf16` + //~| HELP there is an associated function with a similar name: `from_utf16` String::from::method_that_doesnt_exist(); //~^ ERROR ambiguous associated type [E0223] //~| HELP if there were a trait named `Example` with associated type `from` str::from::utf8(); //~^ ERROR ambiguous associated type [E0223] - //~| HELP you might have meant to use `from_utf8` + //~| HELP if there were a trait named `Example` with associated type `from` str::from::utf8_mut(); //~^ ERROR ambiguous associated type [E0223] - //~| HELP you might have meant to use `from_utf8_mut` + //~| HELP if there were a trait named `Example` with associated type `from` } diff --git a/tests/ui/suggestions/issue-109195.stderr b/tests/ui/suggestions/issue-109195.stderr index 5c9ef9f98f5..10cf9cfd28c 100644 --- a/tests/ui/suggestions/issue-109195.stderr +++ b/tests/ui/suggestions/issue-109195.stderr @@ -4,7 +4,7 @@ error[E0223]: ambiguous associated type LL | String::from::utf8; | ^^^^^^^^^^^^ | -help: you might have meant to use `from_utf8` +help: there is an associated function with a similar name: `from_utf8` | LL | String::from_utf8; | ~~~~~~~~~ @@ -15,7 +15,7 @@ error[E0223]: ambiguous associated type LL | String::from::utf8(); | ^^^^^^^^^^^^ | -help: you might have meant to use `from_utf8` +help: there is an associated function with a similar name: `from_utf8` | LL | String::from_utf8(); | ~~~~~~~~~ @@ -26,7 +26,7 @@ error[E0223]: ambiguous associated type LL | String::from::utf16(); | ^^^^^^^^^^^^ | -help: you might have meant to use `from_utf16` +help: there is an associated function with a similar name: `from_utf16` | LL | String::from_utf16(); | ~~~~~~~~~~ @@ -48,10 +48,10 @@ error[E0223]: ambiguous associated type LL | str::from::utf8(); | ^^^^^^^^^ | -help: you might have meant to use `from_utf8` +help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path | -LL | str::from_utf8(); - | ~~~~~~~~~ +LL | ::from::utf8(); + | ~~~~~~~~~~~~~~~~~~~~~~ error[E0223]: ambiguous associated type --> $DIR/issue-109195.rs:17:5 @@ -59,10 +59,10 @@ error[E0223]: ambiguous associated type LL | str::from::utf8_mut(); | ^^^^^^^^^ | -help: you might have meant to use `from_utf8_mut` +help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path | -LL | str::from_utf8_mut(); - | ~~~~~~~~~~~~~ +LL | ::from::utf8_mut(); + | ~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 6 previous errors