about summary refs log tree commit diff
diff options
context:
space:
mode:
authortrevyn <230691+trevyn@users.noreply.github.com>2024-02-05 18:53:28 -0800
committertrevyn <230691+trevyn@users.noreply.github.com>2024-02-05 18:53:28 -0800
commit0b6af718d85a2639854ad09730f5f39aa5f70f9a (patch)
treee8fc9d10dfd49da17fdeba44ae3dff909c5d2c06
parenta4cb55f2aca24f3cbf33b025c147d7a894bceefd (diff)
downloadrust-0b6af718d85a2639854ad09730f5f39aa5f70f9a.tar.gz
rust-0b6af718d85a2639854ad09730f5f39aa5f70f9a.zip
Create helper `maybe_report_similar_assoc_fn`
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs51
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs52
-rw-r--r--tests/ui/suggestions/issue-109195.rs10
-rw-r--r--tests/ui/suggestions/issue-109195.stderr18
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_errors::{
 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 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         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_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
 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 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     )
                     .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 |     <str as Example>::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 |     <str as Example>::from::utf8_mut();
+   |     ~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 6 previous errors