Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/rustc_ast_lowering/src/path.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -298,7 +299,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
sym::return_type_notation,
);
}
err.emit();
err.stash(path_span, StashKey::ReturnTypeNotation);
(
GenericArgsCtor {
args: Default::default(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
93 changes: 89 additions & 4 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use rustc_ast::LitKind;
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};
Expand Down Expand Up @@ -3008,7 +3008,9 @@ impl<'tcx> dyn HirTyLowerer<'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)) => {
Expand Down Expand Up @@ -3070,12 +3072,95 @@ impl<'tcx> dyn HirTyLowerer<'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)) => {
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ impl<'a> Parser<'a> {
// 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",
" =",
Expand Down Expand Up @@ -1133,11 +1133,11 @@ impl<'a> Parser<'a> {
} 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,
);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
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",
" = ",
Expand Down
7 changes: 6 additions & 1 deletion tests/ui/parser/recover/array-type-no-semi.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

36 changes: 33 additions & 3 deletions tests/ui/suggestions/let-binding-init-expr-as-ty.rs
Original file line number Diff line number Diff line change
@@ -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`
}
Loading
Loading