diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0c850b69ca7f1..c0336cd57603c 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4660,3 +4660,33 @@ pub(crate) struct UseRegularStructSuggestion { #[suggestion_part(code = "")] pub semicolon: Option, } +#[derive(Diagnostic)] +#[diag("expected type parameter, found path `{$path}`")] +pub(crate) struct FoundPathInGenerics { + #[primary_span] + pub span: Span, + pub path: String, +} +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to bind a type parameter to a trait", + applicability = "maybe-incorrect", + code = "T: " +)] + +pub(crate) struct SuggestBindTypeParameter { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "alternatively, you might have meant to introduce type parameter", + applicability = "maybe-incorrect", + code = "{parameters}" +)] +pub(crate) struct SuggestIntroduceTypeParameter { + #[primary_span] + pub span: Span, + pub parameters: String, +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7040fc60e4f1a..28e2841b9f1fe 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, - Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, + Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, MgcaDisambiguation, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; @@ -31,14 +31,16 @@ use crate::errors::{ AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound, - ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, - GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition, - InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, - MisspelledKw, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, - SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, - SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, - TernaryOperatorSuggestion, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, FoundPathInGenerics, + GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, + HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, + IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, MisspelledKw, + PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, + StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, + SuggEscapeIdentifier, SuggRemoveComma, SuggestBindTypeParameter, SuggestIntroduceTypeParameter, + TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::exp; use crate::parser::FnContext; @@ -3164,4 +3166,48 @@ impl<'a> Parser<'a> { } Ok(()) } + pub(super) fn maybe_type_in_generic_parameter(&mut self, origin_error: Diag<'a>) -> Diag<'a> { + if !self.may_recover() { + return origin_error; + } + self.with_recovery(super::Recovery::Forbidden, |snapshot| { + snapshot.bump(); + let lo = snapshot.token.span.shrink_to_lo(); + + let ty = match snapshot.parse_ty() { + Ok(t) => t, + Err(err) => { + err.cancel(); + return origin_error; + } + }; + let TyKind::Path(_, path) = ty.kind else { + return origin_error; + }; + let Some(GenericArgs::AngleBracketed(AngleBracketedArgs { span: _, ref args })) = + path.segments[0].args + else { + return origin_error; + }; + + let path_span = path.span; + let mut new_error = snapshot.dcx().create_err(FoundPathInGenerics { + span: path_span, + path: snapshot.span_to_snippet(path_span).unwrap(), + }); + new_error.subdiagnostic(SuggestBindTypeParameter { span: lo }); + origin_error.cancel(); + + let params = args + .iter() + .map(|arg| snapshot.span_to_snippet(arg.span()).unwrap()) + .collect::>() + .join(", "); + new_error.subdiagnostic(SuggestIntroduceTypeParameter { + span: path_span, + parameters: params, + }); + new_error + }) + } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 16fed9c6c273b..627711cb2f822 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -662,11 +662,20 @@ impl<'a> Parser<'a> { let constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); self.expect_keyword(exp!(Impl))?; - + let mut generics_snapshot = None; // First, parse generic parameters if necessary. let mut generics = if self.choose_generics_over_qpath(0) { self.parse_generics()? } else { + // We might be mistakenly trying to use a generic type as a generic parameter. + // impl> Trait for Y { ... } + if self.look_ahead(0, |t| t == &token::Lt) + && self.look_ahead(1, |t| t.is_ident()) + && self.look_ahead(2, |t| t == &token::Lt) + { + generics_snapshot = Some(self.create_snapshot_for_diagnostic()); + } + let mut generics = Generics::default(); // impl A for B {} // /\ this is where `generics.span` should point when there are no type params. @@ -698,9 +707,13 @@ impl<'a> Parser<'a> { for_span: span.to(self.token.span), })); } else { - self.parse_ty_with_generics_recovery(&generics)? + self.parse_ty_with_generics_recovery(&generics).map_err(|e| { + let Some(mut snapshot) = generics_snapshot else { + return e; + }; + snapshot.maybe_type_in_generic_parameter(e) + })? }; - // If `for` is missing we try to recover. let has_for = self.eat_keyword(exp!(For)); let missing_for_span = self.prev_token.span.between(self.token.span); diff --git a/tests/ui/parser/bare-type-in-impl-parameter.rs b/tests/ui/parser/bare-type-in-impl-parameter.rs new file mode 100644 index 0000000000000..7e71d4d2f283a --- /dev/null +++ b/tests/ui/parser/bare-type-in-impl-parameter.rs @@ -0,0 +1 @@ +impl> Z for X {} //~ ERROR: expected type parameter, found path `Y` diff --git a/tests/ui/parser/bare-type-in-impl-parameter.stderr b/tests/ui/parser/bare-type-in-impl-parameter.stderr new file mode 100644 index 0000000000000..87b65793eec9b --- /dev/null +++ b/tests/ui/parser/bare-type-in-impl-parameter.stderr @@ -0,0 +1,18 @@ +error: expected type parameter, found path `Y` + --> $DIR/bare-type-in-impl-parameter.rs:1:6 + | +LL | impl> Z for X {} + | ^^^^^^^^^^ + | +help: you might have meant to bind a type parameter to a trait + | +LL | impl> Z for X {} + | ++ +help: alternatively, you might have meant to introduce type parameter + | +LL - impl> Z for X {} +LL + impl Z for X {} + | + +error: aborting due to 1 previous error +