about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-02-28 20:05:43 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-02-28 21:18:53 +0000
commitadb5ecabdb09ff6f329bbf9b7721036db983f546 (patch)
tree4ca2e3407df092ef48651a0ed7db37d0af5128a4
parentf45d4acf1bb635aa010f19f8a749eed8293203b3 (diff)
downloadrust-adb5ecabdb09ff6f329bbf9b7721036db983f546.tar.gz
rust-adb5ecabdb09ff6f329bbf9b7721036db983f546.zip
Tweak invalid RTN errors
Make suggestions verbose.

When encountering `method(type)` bound, suggest `method(..)` instead of `method()`.

```
error: argument types not allowed with return type notation
  --> $DIR/bad-inputs-and-output.rs:9:23
   |
LL | fn foo<T: Trait<method(i32): Send>>() {}
   |                       ^^^^^
   |
help: remove the input types
   |
LL - fn foo<T: Trait<method(i32): Send>>() {}
LL + fn foo<T: Trait<method(..): Send>>() {}
   |
```

When encountering both return type and arg list that isn't `..`, suggest replacing both.

```
error: return type not allowed with return type notation
  --> $DIR/bad-inputs-and-output.rs:12:25
   |
LL | fn bar<T: Trait<method() -> (): Send>>() {}
   |                         ^^^^^^
   |
help: use the right argument notation and remove the return type
   |
LL - fn bar<T: Trait<method() -> (): Send>>() {}
LL + fn bar<T: Trait<method(..): Send>>() {}
   |
```

When encountering a return type, suggest removing it including the leading whitespace.

```
error: return type not allowed with return type notation
  --> $DIR/bad-inputs-and-output.rs:24:45
   |
LL | fn bay_path<T: Trait>() where T::method(..) -> (): Send {}
   |                                             ^^^^^
   |
help: remove the return type
   |
LL - fn bay_path<T: Trait>() where T::method(..) -> (): Send {}
LL + fn bay_path<T: Trait>() where T::method(..): Send {}
   |
```
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl4
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs21
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs32
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs34
-rw-r--r--compiler/rustc_parse/src/errors.rs3
-rw-r--r--compiler/rustc_parse/src/parser/path.rs5
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs3
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr68
-rw-r--r--tests/ui/suggestions/let-binding-init-expr-as-ty.stderr7
9 files changed, 133 insertions, 44 deletions
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 1b91c33742d..5f1726f7f0a 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -37,11 +37,11 @@ ast_lowering_bad_return_type_notation_inputs =
     .suggestion = remove the input types
 
 ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..`
-    .suggestion = add `..`
+    .suggestion = use the correct syntax by adding `..` to the arguments
 
 ast_lowering_bad_return_type_notation_output =
     return type not allowed with return type notation
-    .suggestion = remove the return type
+ast_lowering_bad_return_type_notation_output_suggestion = use the right argument notation and remove the return type
 
 ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
 
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index f31e2db051d..d402c5abc19 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -372,24 +372,39 @@ pub(crate) struct InclusiveRangeWithNoEnd {
     pub span: Span,
 }
 
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    ast_lowering_bad_return_type_notation_output_suggestion,
+    applicability = "machine-applicable",
+    style = "verbose"
+)]
+/// Given `T: Tr<m() -> Ret>` or `T: Tr<m(Ty) -> Ret>`, suggest `T: Tr<m(..)>`.
+pub(crate) struct RTNSuggestion {
+    #[suggestion_part(code = "")]
+    pub output: Span,
+    #[suggestion_part(code = "(..)")]
+    pub input: Span,
+}
+
 #[derive(Diagnostic)]
 pub(crate) enum BadReturnTypeNotation {
     #[diag(ast_lowering_bad_return_type_notation_inputs)]
     Inputs {
         #[primary_span]
-        #[suggestion(code = "()", applicability = "maybe-incorrect")]
+        #[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
         span: Span,
     },
     #[diag(ast_lowering_bad_return_type_notation_output)]
     Output {
         #[primary_span]
-        #[suggestion(code = "", applicability = "maybe-incorrect")]
         span: Span,
+        #[subdiagnostic]
+        suggestion: RTNSuggestion,
     },
     #[diag(ast_lowering_bad_return_type_notation_needs_dots)]
     NeedsDots {
         #[primary_span]
-        #[suggestion(code = "(..)", applicability = "maybe-incorrect")]
+        #[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
         span: Span,
     },
     #[diag(ast_lowering_bad_return_type_notation_position)]
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 1c69937eed0..13edcc10c9e 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -926,19 +926,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     if let Some(first_char) = constraint.ident.as_str().chars().next()
                         && first_char.is_ascii_lowercase()
                     {
-                        let mut err = if !data.inputs.is_empty() {
-                            self.dcx().create_err(errors::BadReturnTypeNotation::Inputs {
-                                span: data.inputs_span,
-                            })
-                        } else if let FnRetTy::Ty(ty) = &data.output {
-                            self.dcx().create_err(errors::BadReturnTypeNotation::Output {
-                                span: data.inputs_span.shrink_to_hi().to(ty.span),
-                            })
-                        } else {
-                            self.dcx().create_err(errors::BadReturnTypeNotation::NeedsDots {
-                                span: data.inputs_span,
-                            })
+                        tracing::info!(?data, ?data.inputs);
+                        let err = match (&data.inputs[..], &data.output) {
+                            ([_, ..], FnRetTy::Default(_)) => {
+                                errors::BadReturnTypeNotation::Inputs { span: data.inputs_span }
+                            }
+                            ([], FnRetTy::Default(_)) => {
+                                errors::BadReturnTypeNotation::NeedsDots { span: data.inputs_span }
+                            }
+                            // The case `T: Trait<method(..) -> Ret>` is handled in the parser.
+                            (_, FnRetTy::Ty(ty)) => {
+                                let span = data.inputs_span.shrink_to_hi().to(ty.span);
+                                errors::BadReturnTypeNotation::Output {
+                                    span,
+                                    suggestion: errors::RTNSuggestion {
+                                        output: span,
+                                        input: data.inputs_span,
+                                    },
+                                }
+                            }
                         };
+                        let mut err = self.dcx().create_err(err);
                         if !self.tcx.features().return_type_notation()
                             && self.tcx.sess.is_nightly_build()
                         {
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 55d06477313..6b6244b05aa 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -13,7 +13,7 @@ use tracing::{debug, instrument};
 
 use super::errors::{
     AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation,
-    GenericTypeWithParentheses, UseAngleBrackets,
+    GenericTypeWithParentheses, RTNSuggestion, UseAngleBrackets,
 };
 use super::{
     AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition,
@@ -268,19 +268,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 }
                 GenericArgs::Parenthesized(data) => match generic_args_mode {
                     GenericArgsMode::ReturnTypeNotation => {
-                        let mut err = if !data.inputs.is_empty() {
-                            self.dcx().create_err(BadReturnTypeNotation::Inputs {
-                                span: data.inputs_span,
-                            })
-                        } else if let FnRetTy::Ty(ty) = &data.output {
-                            self.dcx().create_err(BadReturnTypeNotation::Output {
-                                span: data.inputs_span.shrink_to_hi().to(ty.span),
-                            })
-                        } else {
-                            self.dcx().create_err(BadReturnTypeNotation::NeedsDots {
-                                span: data.inputs_span,
-                            })
+                        tracing::info!(?data, ?data.inputs);
+                        let err = match (&data.inputs[..], &data.output) {
+                            ([_, ..], FnRetTy::Default(_)) => {
+                                BadReturnTypeNotation::Inputs { span: data.inputs_span }
+                            }
+                            ([], FnRetTy::Default(_)) => {
+                                BadReturnTypeNotation::NeedsDots { span: data.inputs_span }
+                            }
+                            // The case `T: Trait<method(..) -> Ret>` is handled in the parser.
+                            (_, FnRetTy::Ty(ty)) => {
+                                let span = data.inputs_span.shrink_to_hi().to(ty.span);
+                                BadReturnTypeNotation::Output {
+                                    span,
+                                    suggestion: RTNSuggestion {
+                                        output: span,
+                                        input: data.inputs_span,
+                                    },
+                                }
+                            }
                         };
+                        let mut err = self.dcx().create_err(err);
                         if !self.tcx.features().return_type_notation()
                             && self.tcx.sess.is_nightly_build()
                         {
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 173c68b3a72..5bf1a11954e 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2922,8 +2922,9 @@ pub(crate) struct AddBoxNew {
 #[diag(parse_bad_return_type_notation_output)]
 pub(crate) struct BadReturnTypeNotationOutput {
     #[primary_span]
-    #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")]
     pub span: Span,
+    #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")]
+    pub suggestion: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index c24305ea9a8..7698df3b554 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -378,11 +378,14 @@ impl<'a> Parser<'a> {
 
                     self.psess.gated_spans.gate(sym::return_type_notation, span);
 
+                    let prev_lo = self.prev_token.span.shrink_to_hi();
                     if self.eat_noexpect(&token::RArrow) {
                         let lo = self.prev_token.span;
                         let ty = self.parse_ty()?;
+                        let span = lo.to(ty.span);
+                        let suggestion = prev_lo.to(ty.span);
                         self.dcx()
-                            .emit_err(errors::BadReturnTypeNotationOutput { span: lo.to(ty.span) });
+                            .emit_err(errors::BadReturnTypeNotationOutput { span, suggestion });
                     }
 
                     P(ast::GenericArgs::ParenthesizedElided(span))
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 f00aaec1a8c..90fd2c2b4f3 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
@@ -21,6 +21,9 @@ fn foo_path<T: Trait>() where T::method(i32): Send {}
 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 `..`
 
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 c6b9f3eff90..bd02b7d6534 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,17 +1,29 @@
+error: return type not allowed with return type notation
+  --> $DIR/bad-inputs-and-output.rs:24:45
+   |
+LL | fn bay_path<T: Trait>() where T::method(..) -> (): Send {}
+   |                                             ^^^^^
+   |
+help: remove the return type
+   |
+LL - fn bay_path<T: Trait>() where T::method(..) -> (): Send {}
+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:27:36
+  --> $DIR/bad-inputs-and-output.rs:30:36
    |
 LL | fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
 
 error[E0575]: expected associated type, found associated function `Trait::method`
-  --> $DIR/bad-inputs-and-output.rs:30:36
+  --> $DIR/bad-inputs-and-output.rs:33:36
    |
 LL | fn bar_qualified<T: Trait>() where <T as Trait>::method() -> (): Send {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
 
 error[E0575]: expected associated type, found associated function `Trait::method`
-  --> $DIR/bad-inputs-and-output.rs:33:36
+  --> $DIR/bad-inputs-and-output.rs:36:36
    |
 LL | fn baz_qualified<T: Trait>() where <T as Trait>::method(): Send {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^ not a associated type
@@ -20,38 +32,72 @@ error: argument types not allowed with return type notation
   --> $DIR/bad-inputs-and-output.rs:9:23
    |
 LL | fn foo<T: Trait<method(i32): Send>>() {}
-   |                       ^^^^^ help: remove the input types: `()`
+   |                       ^^^^^
+   |
+help: remove the input types
+   |
+LL - fn foo<T: Trait<method(i32): Send>>() {}
+LL + fn foo<T: Trait<method(..): Send>>() {}
+   |
 
 error: return type not allowed with return type notation
   --> $DIR/bad-inputs-and-output.rs:12:25
    |
 LL | fn bar<T: Trait<method() -> (): Send>>() {}
-   |                         ^^^^^^ help: remove the return type
+   |                         ^^^^^^
+   |
+help: use the right argument notation and remove the return type
+   |
+LL - fn bar<T: Trait<method() -> (): Send>>() {}
+LL + fn bar<T: Trait<method(..): Send>>() {}
+   |
 
 error: return type notation arguments must be elided with `..`
   --> $DIR/bad-inputs-and-output.rs:15:23
    |
 LL | fn baz<T: Trait<method(): Send>>() {}
-   |                       ^^ help: add `..`: `(..)`
+   |                       ^^
+   |
+help: use the correct syntax by adding `..` to the arguments
+   |
+LL | fn baz<T: Trait<method(..): Send>>() {}
+   |                        ++
 
 error: argument types not allowed with return type notation
   --> $DIR/bad-inputs-and-output.rs:18:40
    |
 LL | fn foo_path<T: Trait>() where T::method(i32): Send {}
-   |                                        ^^^^^ help: remove the input types: `()`
+   |                                        ^^^^^
+   |
+help: remove the input types
+   |
+LL - fn foo_path<T: Trait>() where T::method(i32): Send {}
+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
    |
 LL | fn bar_path<T: Trait>() where T::method() -> (): Send {}
-   |                                          ^^^^^^ help: remove the return type
+   |                                          ^^^^^^
+   |
+help: use the right argument notation and remove the return type
+   |
+LL - fn bar_path<T: Trait>() where T::method() -> (): Send {}
+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:24:40
+  --> $DIR/bad-inputs-and-output.rs:27:40
    |
 LL | fn baz_path<T: Trait>() where T::method(): Send {}
-   |                                        ^^ help: add `..`: `(..)`
+   |                                        ^^
+   |
+help: use the correct syntax by adding `..` to the arguments
+   |
+LL | fn baz_path<T: Trait>() where T::method(..): Send {}
+   |                                         ++
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
 For more information about this error, try `rustc --explain E0575`.
diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr
index 83a5441e3c0..19a0e4b17d0 100644
--- a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr
+++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr
@@ -10,11 +10,16 @@ error: argument types not allowed with return type notation
   --> $DIR/let-binding-init-expr-as-ty.rs:2:26
    |
 LL |     let foo: i32::from_be(num);
-   |                          ^^^^^ help: remove the input types: `()`
+   |                          ^^^^^
    |
    = note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
    = help: add `#![feature(return_type_notation)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+help: remove the input types
+   |
+LL -     let foo: i32::from_be(num);
+LL +     let foo: i32::from_be(..);
+   |
 
 error: return type notation not allowed in this position yet
   --> $DIR/let-binding-init-expr-as-ty.rs:2:14