Rollup merge of #154154 - estebank:issue-134087, r=mati865

Emit fewer errors for incorrect rtn and `=` -> `:` typos in bindings

Make all existing `:` -> `=` typo suggestions verbose and tweak the suggested code.

Stash parse errors when pasing rtn that doesn't use `(..)`.

Emit single error on `Foo::bar(qux)` that looks like rtn in incorrect position and suggest `Foo::bar(..)`.
```
error: return type notation not allowed in this position yet
  --> $DIR/let-binding-init-expr-as-ty.rs:18:10
   |
LL | struct K(S::new(()));
   |          ^^^^^^^^^^
   |
help: furthermore, argument types not allowed with return type notation
   |
LL - struct K(S::new(()));
LL + struct K(S::new(..));
   |
```
On incorrect rtn in let binding type for an existing inherent associated function, suggest `:` -> `=` to turn the "type" into a call expression. (Fix rust-lang/rust#134087)
```
error: expected type, found associated function call
  --> $DIR/let-binding-init-expr-as-ty.rs:29:12
   |
LL |     let x: S::new(());
   |            ^^^^^^^^^^
   |
help: use `=` if you meant to assign
   |
LL -     let x: S::new(());
LL +     let x = S::new(());
   |
```
This commit is contained in:
Jonathan Brouwer
2026-03-21 00:42:46 +01:00
committed by GitHub
11 changed files with 300 additions and 34 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use rustc_ast::{self as ast, *};
use rustc_errors::StashKey;
use rustc_hir::def::{DefKind, PartialRes, PerNS, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, GenericArg};
@@ -298,7 +299,7 @@ pub(crate) fn lower_path_segment(
sym::return_type_notation,
);
}
err.emit();
err.stash(path_span, StashKey::ReturnTypeNotation);
(
GenericArgsCtor {
args: Default::default(),
+1
View File
@@ -386,6 +386,7 @@ pub enum StashKey {
/// it's a method call without parens. If later on in `hir_typeck` we find out that this is
/// the case we suppress this message and we give a better suggestion.
GenericInFieldExpr,
ReturnTypeNotation,
}
fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
@@ -1807,6 +1807,13 @@ pub(crate) struct CmseImplTrait {
pub(crate) struct BadReturnTypeNotation {
#[primary_span]
pub span: Span,
#[suggestion(
"furthermore, argument types not allowed with return type notation",
applicability = "maybe-incorrect",
code = "(..)",
style = "verbose"
)]
pub suggestion: Option<Span>,
}
#[derive(Diagnostic)]
@@ -26,7 +26,7 @@
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level,
Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, StashKey,
struct_span_code_err,
};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -3015,7 +3015,9 @@ pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
let guar = self
.dcx()
.emit_err(BadReturnTypeNotation { span: hir_ty.span, suggestion: None });
Ty::new_error(tcx, guar)
}
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
@@ -3077,12 +3079,95 @@ pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
// If we encounter a type relative path with RTN generics, then it must have
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
// it's certainly in an illegal position.
hir::TyKind::Path(hir::QPath::TypeRelative(_, segment))
hir::TyKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment))
if segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
let guar = if let hir::Node::LetStmt(stmt) = tcx.parent_hir_node(hir_ty.hir_id)
&& let None = stmt.init
&& let hir::TyKind::Path(hir::QPath::Resolved(_, self_ty_path)) =
hir_self_ty.kind
&& let Res::Def(DefKind::Enum | DefKind::Struct | DefKind::Union, def_id) =
self_ty_path.res
&& let Some(_) = tcx
.inherent_impls(def_id)
.iter()
.flat_map(|imp| {
tcx.associated_items(*imp).filter_by_name_unhygienic(segment.ident.name)
})
.filter(|assoc| {
matches!(assoc.kind, ty::AssocKind::Fn { has_self: false, .. })
})
.next()
{
// `let x: S::new(valid_in_ty_ctxt);` -> `let x = S::new(valid_in_ty_ctxt);`
let err = tcx
.dcx()
.struct_span_err(
hir_ty.span,
"expected type, found associated function call",
)
.with_span_suggestion_verbose(
stmt.pat.span.between(hir_ty.span),
"use `=` if you meant to assign",
" = ".to_string(),
Applicability::MaybeIncorrect,
);
self.dcx().try_steal_replace_and_emit_err(
hir_ty.span,
StashKey::ReturnTypeNotation,
err,
)
} else if let hir::Node::LetStmt(stmt) = tcx.parent_hir_node(hir_ty.hir_id)
&& let None = stmt.init
&& let hir::TyKind::Path(hir::QPath::Resolved(_, self_ty_path)) =
hir_self_ty.kind
&& let Res::PrimTy(_) = self_ty_path.res
&& self.dcx().has_stashed_diagnostic(hir_ty.span, StashKey::ReturnTypeNotation)
{
// `let x: i32::something(valid_in_ty_ctxt);` -> `let x = i32::something(valid_in_ty_ctxt);`
// FIXME: Check that `something` is a valid function in `i32`.
let err = tcx
.dcx()
.struct_span_err(
hir_ty.span,
"expected type, found associated function call",
)
.with_span_suggestion_verbose(
stmt.pat.span.between(hir_ty.span),
"use `=` if you meant to assign",
" = ".to_string(),
Applicability::MaybeIncorrect,
);
self.dcx().try_steal_replace_and_emit_err(
hir_ty.span,
StashKey::ReturnTypeNotation,
err,
)
} else {
let suggestion = if self
.dcx()
.has_stashed_diagnostic(hir_ty.span, StashKey::ReturnTypeNotation)
{
// We already created a diagnostic complaining that `foo(bar)` is wrong and
// should have been `foo(..)`. Instead, emit only the current error and
// include that prior suggestion. Changes are that the problems go further,
// but keep the suggestion just in case. Either way, we want a single error
// instead of two.
Some(segment.ident.span.shrink_to_hi().with_hi(hir_ty.span.hi()))
} else {
None
};
let err = self
.dcx()
.create_err(BadReturnTypeNotation { span: hir_ty.span, suggestion });
self.dcx().try_steal_replace_and_emit_err(
hir_ty.span,
StashKey::ReturnTypeNotation,
err,
)
};
Ty::new_error(tcx, guar)
}
hir::TyKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => {
+5 -5
View File
@@ -362,7 +362,7 @@ fn parse_local(&mut self, super_: Option<Span>, attrs: AttrVec) -> PResult<'a, B
// init parsed, ty error
// Could parse the type as if it were the initializer, it is likely there was a
// typo in the code: `:` instead of `=`. Add suggestion and emit the error.
err.span_suggestion_short(
err.span_suggestion_verbose(
colon_sp,
"use `=` if you meant to assign",
" =",
@@ -1133,11 +1133,11 @@ pub fn parse_full_stmt(
} else {
false
};
if suggest_eq {
e.span_suggestion_short(
colon_sp,
if suggest_eq && let Some(ty) = &local.ty {
e.span_suggestion_verbose(
local.pat.span.between(ty.span),
"use `=` if you meant to assign",
"=",
" = ",
Applicability::MaybeIncorrect,
);
}
@@ -1272,7 +1272,7 @@ fn suggest_typo(
Some((pat_sp, Some(ty_sp), None))
if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
{
err.span_suggestion_short(
err.span_suggestion_verbose(
pat_sp.between(ty_sp),
"use `=` if you meant to assign",
" = ",
@@ -17,7 +17,12 @@ LL | let a: [i32, ];
| - ^ expected `;` or `]`
| |
| while parsing the type for `a`
| help: use `=` if you meant to assign
|
help: use `=` if you meant to assign
|
LL - let a: [i32, ];
LL + let a = [i32, ];
|
error: expected `;` or `]`, found `,`
--> $DIR/array-type-no-semi.rs:12:16
@@ -13,7 +13,12 @@ LL | let _: std::env::temp_dir().join("foo");
| - ^ expected one of `!`, `+`, `->`, `::`, `;`, or `=`
| |
| while parsing the type for `_`
| help: use `=` if you meant to assign
|
help: use `=` if you meant to assign
|
LL - let _: std::env::temp_dir().join("foo");
LL + let _ = std::env::temp_dir().join("foo");
|
error: aborting due to 2 previous errors
@@ -1,11 +1,41 @@
pub fn foo(num: i32) -> i32 {
fn foo(num: i32) -> i32 {
// FIXME: This case doesn't really check that `from_be` is a valid function in `i32`.
let foo: i32::from_be(num);
//~^ ERROR expected type, found local variable `num`
//~| ERROR argument types not allowed with return type notation
//~| ERROR return type notation not allowed in this position yet
//~| ERROR expected type, found associated function call
foo
}
struct S;
impl S {
fn new(_: ()) -> S {
S
}
}
// We should still mention that it should be `S::new(..)`, even though rtn is not allowed there:
struct K(S::new(())); //~ ERROR return type notation not allowed in this position yet
fn bar() {}
fn main() {
let _ = foo(42);
// Associated functions (#134087)
let x: Vec::new(); //~ ERROR expected type, found associated function call
let x: Vec<()>::new(); //~ ERROR expected type, found associated function call
let x: S::new(..); //~ ERROR expected type, found associated function call
//~^ ERROR return type notation is experimental
let x: S::new(()); //~ ERROR expected type, found associated function call
// Literals
let x: 42; //~ ERROR expected type, found `42`
let x: ""; //~ ERROR expected type, found `""`
// Functions
let x: bar(); //~ ERROR expected type, found function `bar`
let x: bar; //~ ERROR expected type, found function `bar`
// Locals
let x: x; //~ ERROR expected type, found local variable `x`
}
@@ -1,32 +1,159 @@
error[E0573]: expected type, found local variable `num`
--> $DIR/let-binding-init-expr-as-ty.rs:2:27
error: expected type, found `42`
--> $DIR/let-binding-init-expr-as-ty.rs:32:12
|
LL | let x: 42;
| - ^^ expected type
| |
| while parsing the type for `x`
|
help: use `=` if you meant to assign
|
LL - let x: 42;
LL + let x = 42;
|
LL | let foo: i32::from_be(num);
| -- ^^^ not a type
| |
| help: use `=` if you meant to assign
error: argument types not allowed with return type notation
--> $DIR/let-binding-init-expr-as-ty.rs:2:26
error: expected type, found `""`
--> $DIR/let-binding-init-expr-as-ty.rs:33:12
|
LL | let x: "";
| - ^^ expected type
| |
| while parsing the type for `x`
|
help: use `=` if you meant to assign
|
LL - let x: "";
LL + let x = "";
|
error[E0573]: expected type, found local variable `num`
--> $DIR/let-binding-init-expr-as-ty.rs:3:27
|
LL | let foo: i32::from_be(num);
| ^^^^^
| ^^^ not a type
|
help: use `=` if you meant to assign
|
LL - let foo: i32::from_be(num);
LL + let foo = i32::from_be(num);
|
error[E0573]: expected type, found function `bar`
--> $DIR/let-binding-init-expr-as-ty.rs:36:12
|
LL | let x: bar();
| ^^^^^ not a type
|
help: use `=` if you meant to assign
|
LL - let x: bar();
LL + let x = bar();
|
error[E0573]: expected type, found function `bar`
--> $DIR/let-binding-init-expr-as-ty.rs:37:12
|
LL | let x: bar;
| ^^^ not a type
error[E0573]: expected type, found local variable `x`
--> $DIR/let-binding-init-expr-as-ty.rs:40:12
|
LL | struct K(S::new(()));
| --------------------- similarly named struct `K` defined here
...
LL | let x: x;
| ^
|
help: a struct with a similar name exists
|
LL - let x: x;
LL + let x: K;
|
error[E0658]: return type notation is experimental
--> $DIR/let-binding-init-expr-as-ty.rs:27:18
|
LL | let x: S::new(..);
| ^^^^
|
= 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
--> $DIR/let-binding-init-expr-as-ty.rs:18:10
|
LL | struct K(S::new(()));
| ^^^^^^^^^^
|
help: furthermore, argument types not allowed with return type notation
|
LL - struct K(S::new(()));
LL + struct K(S::new(..));
|
error: expected type, found associated function call
--> $DIR/let-binding-init-expr-as-ty.rs:3:14
|
LL | let foo: i32::from_be(num);
| ^^^^^^^^^^^^^^^^^
|
help: use `=` if you meant to assign
|
LL - let foo: i32::from_be(num);
LL + let foo = i32::from_be(num);
|
error: aborting due to 3 previous errors
error: expected type, found associated function call
--> $DIR/let-binding-init-expr-as-ty.rs:25:12
|
LL | let x: Vec::new();
| ^^^^^^^^^^
|
help: use `=` if you meant to assign
|
LL - let x: Vec::new();
LL + let x = Vec::new();
|
For more information about this error, try `rustc --explain E0573`.
error: expected type, found associated function call
--> $DIR/let-binding-init-expr-as-ty.rs:26:12
|
LL | let x: Vec<()>::new();
| ^^^^^^^^^^^^^^
|
help: use `=` if you meant to assign
|
LL - let x: Vec<()>::new();
LL + let x = Vec<()>::new();
|
error: expected type, found associated function call
--> $DIR/let-binding-init-expr-as-ty.rs:27:12
|
LL | let x: S::new(..);
| ^^^^^^^^^^
|
help: use `=` if you meant to assign
|
LL - let x: S::new(..);
LL + let x = S::new(..);
|
error: expected type, found associated function call
--> $DIR/let-binding-init-expr-as-ty.rs:29:12
|
LL | let x: S::new(());
| ^^^^^^^^^^
|
help: use `=` if you meant to assign
|
LL - let x: S::new(());
LL + let x = S::new(());
|
error: aborting due to 13 previous errors
Some errors have detailed explanations: E0573, E0658.
For more information about an error, try `rustc --explain E0573`.
@@ -5,7 +5,12 @@ LL | let x: Vec::with_capacity(10, 20);
| - ^^ expected type
| |
| while parsing the type for `x`
| help: use `=` if you meant to assign
|
help: use `=` if you meant to assign
|
LL - let x: Vec::with_capacity(10, 20);
LL + let x = Vec::with_capacity(10, 20);
|
error[E0061]: this function takes 1 argument but 2 arguments were supplied
--> $DIR/type-ascription-instead-of-initializer.rs:2:12