diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index ec57720387c0..fe85302c2177 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -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(), diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8c57544c54b8..d17a4d6de42f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -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(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 1c999f1ffc93..fcd4cb938bf7 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -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, } #[derive(Diagnostic)] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index bf97bfb1ebbc..2bba1b723353 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -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}; @@ -3008,7 +3008,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)) => { @@ -3070,12 +3072,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)) => { diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs index 71e5a0c728d6..c108b567b18b 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs @@ -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` } 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 f0887f85d6ef..354d11976911 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr @@ -1,5 +1,33 @@ +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; + | + +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:2:27 + --> $DIR/let-binding-init-expr-as-ty.rs:3:27 | LL | let foo: i32::from_be(num); | ^^^ not a type @@ -10,27 +38,122 @@ LL - let foo: i32::from_be(num); LL + let foo = i32::from_be(num); | -error: argument types not allowed with return type notation - --> $DIR/let-binding-init-expr-as-ty.rs:2:26 +error[E0573]: expected type, found function `bar` + --> $DIR/let-binding-init-expr-as-ty.rs:36:12 | -LL | let foo: i32::from_be(num); - | ^^^^^ +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 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`.