From a23566a57e4d53a35827becc2f65ce27951276c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Fri, 28 Feb 2025 21:39:25 +0000
Subject: [PATCH] Provide more context on resolve error caused from incorrect
 RTN

When encountering a resolve E0575 error for an associated method (when a type was expected), see if it could have been an intended return type notation bound.

```
error[E0575]: expected associated type, found associated function `Trait::method`
  --> $DIR/bad-inputs-and-output.rs:31:36
   |
LL | fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
   |
help: you might have meant to use the return type notation syntax
   |
LL - fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
LL + fn foo_qualified<T: Trait>() where T::method(..): Send {}
   |
```
---
 compiler/rustc_ast_lowering/src/lib.rs        |  1 -
 compiler/rustc_ast_lowering/src/path.rs       |  1 -
 compiler/rustc_resolve/src/late.rs            | 18 +++++++--
 .../rustc_resolve/src/late/diagnostics.rs     | 39 +++++++++++++++++-
 .../bad-inputs-and-output.fixed               | 40 +++++++++++++++++++
 .../bad-inputs-and-output.rs                  |  3 +-
 .../bad-inputs-and-output.stderr              | 38 +++++++++++++-----
 7 files changed, 123 insertions(+), 17 deletions(-)
 create mode 100644 tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed

diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 13edcc10c9e2a..2e175405da219 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -926,7 +926,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     if let Some(first_char) = constraint.ident.as_str().chars().next()
                         && first_char.is_ascii_lowercase()
                     {
-                        tracing::info!(?data, ?data.inputs);
                         let err = match (&data.inputs[..], &data.output) {
                             ([_, ..], FnRetTy::Default(_)) => {
                                 errors::BadReturnTypeNotation::Inputs { span: data.inputs_span }
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 6b6244b05aa9b..d00c797755f3f 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -268,7 +268,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 }
                 GenericArgs::Parenthesized(data) => match generic_args_mode {
                     GenericArgsMode::ReturnTypeNotation => {
-                        tracing::info!(?data, ?data.inputs);
                         let err = match (&data.inputs[..], &data.output) {
                             ([_, ..], FnRetTy::Default(_)) => {
                                 BadReturnTypeNotation::Inputs { span: data.inputs_span }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index f119ed55e7d1b..50bc2feb477dc 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -623,7 +623,7 @@ pub(crate) struct UnnecessaryQualification<'ra> {
     pub removal_span: Span,
 }
 
-#[derive(Default)]
+#[derive(Default, Debug)]
 struct DiagMetadata<'ast> {
     /// The current trait's associated items' ident, used for diagnostic suggestions.
     current_trait_assoc_items: Option<&'ast [P<AssocItem>]>,
@@ -3146,6 +3146,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 PathSource::Trait(AliasPossibility::No),
                 Finalize::new(trait_ref.ref_id, trait_ref.path.span),
                 RecordPartialRes::Yes,
+                None,
             );
             self.diag_metadata.currently_processing_impl_trait = None;
             if let Some(def_id) = res.expect_full_res().opt_def_id() {
@@ -4072,6 +4073,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             source,
             Finalize::new(id, path.span),
             RecordPartialRes::Yes,
+            None,
         );
     }
 
@@ -4083,14 +4085,21 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         source: PathSource<'ast>,
         finalize: Finalize,
         record_partial_res: RecordPartialRes,
+        parent_qself: Option<&QSelf>,
     ) -> PartialRes {
         let ns = source.namespace();
 
         let Finalize { node_id, path_span, .. } = finalize;
         let report_errors = |this: &mut Self, res: Option<Res>| {
             if this.should_report_errs() {
-                let (err, candidates) =
-                    this.smart_resolve_report_errors(path, None, path_span, source, res);
+                let (err, candidates) = this.smart_resolve_report_errors(
+                    path,
+                    None,
+                    path_span,
+                    source,
+                    res,
+                    parent_qself,
+                );
 
                 let def_id = this.parent_scope.module.nearest_parent_mod();
                 let instead = res.is_some();
@@ -4159,6 +4168,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     path_span,
                     PathSource::Type,
                     None,
+                    parent_qself,
                 );
 
                 // There are two different error messages user might receive at
@@ -4436,6 +4446,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 PathSource::Trait(AliasPossibility::No),
                 Finalize::new(finalize.node_id, qself.path_span),
                 RecordPartialRes::No,
+                Some(&qself),
             );
 
             if trait_res.expect_full_res() == Res::Err {
@@ -4460,6 +4471,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 PathSource::TraitItem(ns),
                 Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span),
                 RecordPartialRes::No,
+                Some(&qself),
             );
 
             // The remaining segments (the `C` in our example) will
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 0e14e7671b176..035f9abda86e6 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -35,7 +35,7 @@ use super::NoConstantGenericsReason;
 use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
 use crate::late::{
     AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind,
-    LifetimeUseSet, RibKind,
+    LifetimeUseSet, QSelf, RibKind,
 };
 use crate::ty::fast_reject::SimplifiedType;
 use crate::{
@@ -421,6 +421,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         span: Span,
         source: PathSource<'_>,
         res: Option<Res>,
+        qself: Option<&QSelf>,
     ) -> (Diag<'tcx>, Vec<ImportSuggestion>) {
         debug!(?res, ?source);
         let base_error = self.make_base_error(path, span, source, res);
@@ -453,6 +454,15 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
 
         self.suggest_self_or_self_ref(&mut err, path, span);
         self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error);
+        self.detect_rtn_with_fully_qualified_path(
+            &mut err,
+            path,
+            following_seg,
+            span,
+            source,
+            res,
+            qself,
+        );
         if self.suggest_self_ty(&mut err, source, path, span)
             || self.suggest_self_value(&mut err, source, path, span)
         {
@@ -501,6 +511,33 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         (err, candidates)
     }
 
+    fn detect_rtn_with_fully_qualified_path(
+        &self,
+        err: &mut Diag<'_>,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+        span: Span,
+        source: PathSource<'_>,
+        res: Option<Res>,
+        qself: Option<&QSelf>,
+    ) {
+        if let Some(Res::Def(DefKind::AssocFn, _)) = res
+            && let PathSource::TraitItem(TypeNS) = source
+            && let None = following_seg
+            && let Some(qself) = qself
+            && let TyKind::Path(None, ty_path) = &qself.ty.kind
+            && ty_path.segments.len() == 1
+            && self.diag_metadata.current_where_predicate.is_some()
+        {
+            err.span_suggestion_verbose(
+                span,
+                "you might have meant to use the return type notation syntax",
+                format!("{}::{}(..)", ty_path.segments[0].ident, path[path.len() - 1].ident),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
     fn detect_assoc_type_constraint_meant_as_path(
         &self,
         err: &mut Diag<'_>,
diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed
new file mode 100644
index 0000000000000..7af986267aa67
--- /dev/null
+++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed
@@ -0,0 +1,40 @@
+//@ edition: 2021
+//@ run-rustfix
+#![feature(return_type_notation)]
+#![allow(dead_code)]
+
+trait Trait {
+    async fn method() {}
+}
+
+fn foo<T: Trait<method(..): Send>>() {}
+//~^ ERROR argument types not allowed with return type notation
+
+fn bar<T: Trait<method(..): Send>>() {}
+//~^ ERROR return type not allowed with return type notation
+
+fn baz<T: Trait<method(..): Send>>() {}
+//~^ ERROR return type notation arguments must be elided with `..`
+
+fn foo_path<T: Trait>() where T::method(..): Send {}
+//~^ ERROR argument types not allowed with return type notation
+
+fn bar_path<T: Trait>() where T::method(..): Send {}
+//~^ ERROR return type not allowed with return type notation
+
+fn bay_path<T: Trait>() where T::method(..): Send {}
+//~^ ERROR return type not allowed with return type notation
+
+fn baz_path<T: Trait>() where T::method(..): Send {}
+//~^ ERROR return type notation arguments must be elided with `..`
+
+fn foo_qualified<T: Trait>() where T::method(..): Send {}
+//~^ ERROR expected associated type
+
+fn bar_qualified<T: Trait>() where T::method(..): Send {}
+//~^ ERROR expected associated type
+
+fn baz_qualified<T: Trait>() where T::method(..): Send {}
+//~^ ERROR expected associated type
+
+fn main() {}
diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs
index 90fd2c2b4f326..983836e6433f0 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs
+++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs
@@ -1,6 +1,7 @@
 //@ edition: 2021
-
+//@ run-rustfix
 #![feature(return_type_notation)]
+#![allow(dead_code)]
 
 trait Trait {
     async fn method() {}
diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr
index bd02b7d6534a9..e1245a04c7f7d 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr
+++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr
@@ -1,5 +1,5 @@
 error: return type not allowed with return type notation
-  --> $DIR/bad-inputs-and-output.rs:24:45
+  --> $DIR/bad-inputs-and-output.rs:25:45
    |
 LL | fn bay_path<T: Trait>() where T::method(..) -> (): Send {}
    |                                             ^^^^^
@@ -11,25 +11,43 @@ LL + fn bay_path<T: Trait>() where T::method(..): Send {}
    |
 
 error[E0575]: expected associated type, found associated function `Trait::method`
-  --> $DIR/bad-inputs-and-output.rs:30:36
+  --> $DIR/bad-inputs-and-output.rs:31:36
    |
 LL | fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
+   |
+help: you might have meant to use the return type notation syntax
+   |
+LL - fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
+LL + fn foo_qualified<T: Trait>() where T::method(..): Send {}
+   |
 
 error[E0575]: expected associated type, found associated function `Trait::method`
-  --> $DIR/bad-inputs-and-output.rs:33:36
+  --> $DIR/bad-inputs-and-output.rs:34:36
    |
 LL | fn bar_qualified<T: Trait>() where <T as Trait>::method() -> (): Send {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
+   |
+help: you might have meant to use the return type notation syntax
+   |
+LL - fn bar_qualified<T: Trait>() where <T as Trait>::method() -> (): Send {}
+LL + fn bar_qualified<T: Trait>() where T::method(..): Send {}
+   |
 
 error[E0575]: expected associated type, found associated function `Trait::method`
-  --> $DIR/bad-inputs-and-output.rs:36:36
+  --> $DIR/bad-inputs-and-output.rs:37:36
    |
 LL | fn baz_qualified<T: Trait>() where <T as Trait>::method(): Send {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^ not a associated type
+   |
+help: you might have meant to use the return type notation syntax
+   |
+LL - fn baz_qualified<T: Trait>() where <T as Trait>::method(): Send {}
+LL + fn baz_qualified<T: Trait>() where T::method(..): Send {}
+   |
 
 error: argument types not allowed with return type notation
-  --> $DIR/bad-inputs-and-output.rs:9:23
+  --> $DIR/bad-inputs-and-output.rs:10:23
    |
 LL | fn foo<T: Trait<method(i32): Send>>() {}
    |                       ^^^^^
@@ -41,7 +59,7 @@ LL + fn foo<T: Trait<method(..): Send>>() {}
    |
 
 error: return type not allowed with return type notation
-  --> $DIR/bad-inputs-and-output.rs:12:25
+  --> $DIR/bad-inputs-and-output.rs:13:25
    |
 LL | fn bar<T: Trait<method() -> (): Send>>() {}
    |                         ^^^^^^
@@ -53,7 +71,7 @@ LL + fn bar<T: Trait<method(..): Send>>() {}
    |
 
 error: return type notation arguments must be elided with `..`
-  --> $DIR/bad-inputs-and-output.rs:15:23
+  --> $DIR/bad-inputs-and-output.rs:16:23
    |
 LL | fn baz<T: Trait<method(): Send>>() {}
    |                       ^^
@@ -64,7 +82,7 @@ LL | fn baz<T: Trait<method(..): Send>>() {}
    |                        ++
 
 error: argument types not allowed with return type notation
-  --> $DIR/bad-inputs-and-output.rs:18:40
+  --> $DIR/bad-inputs-and-output.rs:19:40
    |
 LL | fn foo_path<T: Trait>() where T::method(i32): Send {}
    |                                        ^^^^^
@@ -76,7 +94,7 @@ LL + fn foo_path<T: Trait>() where T::method(..): Send {}
    |
 
 error: return type not allowed with return type notation
-  --> $DIR/bad-inputs-and-output.rs:21:42
+  --> $DIR/bad-inputs-and-output.rs:22:42
    |
 LL | fn bar_path<T: Trait>() where T::method() -> (): Send {}
    |                                          ^^^^^^
@@ -88,7 +106,7 @@ LL + fn bar_path<T: Trait>() where T::method(..): Send {}
    |
 
 error: return type notation arguments must be elided with `..`
-  --> $DIR/bad-inputs-and-output.rs:27:40
+  --> $DIR/bad-inputs-and-output.rs:28:40
    |
 LL | fn baz_path<T: Trait>() where T::method(): Send {}
    |                                        ^^