From 7190bc3097a3f84c9d0e07d149eba4b00e4f8917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 15 May 2021 14:56:28 -0700 Subject: [PATCH] Account for incorrect `impl Foo {}` syntax Fix #84946 --- compiler/rustc_ast/src/ast.rs | 15 ++++ .../rustc_parse/src/parser/diagnostics.rs | 82 +++++++++++++++-- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/generics.rs | 5 +- compiler/rustc_parse/src/parser/item.rs | 2 +- compiler/rustc_parse/src/parser/path.rs | 88 +++++++++++-------- compiler/rustc_parse/src/parser/ty.rs | 38 ++++++-- ...onst-param-decl-on-type-instead-of-impl.rs | 6 ++ ...-param-decl-on-type-instead-of-impl.stderr | 22 +++++ 9 files changed, 206 insertions(+), 54 deletions(-) create mode 100644 src/test/ui/parser/const-param-decl-on-type-instead-of-impl.rs create mode 100644 src/test/ui/parser/const-param-decl-on-type-instead-of-impl.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index abfe8360987..55b243a84a9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -405,6 +405,21 @@ pub struct GenericParam { pub kind: GenericParamKind, } +impl GenericParam { + pub fn span(&self) -> Span { + match &self.kind { + GenericParamKind::Lifetime | GenericParamKind::Type { default: None } => { + self.ident.span + } + GenericParamKind::Type { default: Some(ty) } => self.ident.span.to(ty.span), + GenericParamKind::Const { kw_span, default: Some(default), .. } => { + kw_span.to(default.value.span) + } + GenericParamKind::Const { kw_span, default: None, ty } => kw_span.to(ty.span), + } + } +} + /// Represents lifetime, type and const parameters attached to a declaration of /// a function, enum, trait, etc. #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 81328e09156..16ab798708c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -6,9 +6,11 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; -use rustc_ast::{AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec}; -use rustc_ast::{BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item}; -use rustc_ast::{ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind}; +use rustc_ast::{ + AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, + BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Mutability, Param, Pat, + PatKind, Path, PathSegment, QSelf, Ty, TyKind, +}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err}; @@ -662,7 +664,7 @@ impl<'a> Parser<'a> { let snapshot = self.clone(); self.bump(); let lo = self.token.span; - match self.parse_angle_args() { + match self.parse_angle_args(None) { Ok(args) => { let span = lo.to(self.prev_token.span); // Detect trailing `>` like in `x.collect::Vec<_>>()`. @@ -719,7 +721,7 @@ impl<'a> Parser<'a> { let x = self.parse_seq_to_before_end( &token::Gt, SeqSep::trailing_allowed(token::Comma), - |p| p.parse_generic_arg(), + |p| p.parse_generic_arg(None), ); match x { Ok((_, _, false)) => { @@ -1103,7 +1105,7 @@ impl<'a> Parser<'a> { self.expect(&token::ModSep)?; let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; + self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; path.span = ty_span.to(self.prev_token.span); let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); @@ -1909,6 +1911,74 @@ impl<'a> Parser<'a> { Ok(expr) } + fn recover_const_param_decl( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option> { + let snapshot = self.clone(); + let param = match self.parse_const_param(vec![]) { + Ok(param) => param, + Err(mut err) => { + err.cancel(); + *self = snapshot; + return Err(err); + } + }; + let mut err = + self.struct_span_err(param.ident.span, "unexpected `const` parameter declaration"); + err.span_label( + param.ident.span, + "expected a `const` expression, not a parameter declaration", + ); + if let (Some(generics), Ok(snippet)) = + (ty_generics, self.sess.source_map().span_to_snippet(param.span())) + { + let (span, sugg) = match &generics.params[..] { + [] => (generics.span, format!("<{}>", snippet)), + [.., generic] => (generic.span().shrink_to_hi(), format!(", {}", snippet)), + }; + err.multipart_suggestion( + "`const` parameters must be declared for the `impl`", + vec![(span, sugg), (param.span(), param.ident.to_string())], + Applicability::MachineApplicable, + ); + } + let value = self.mk_expr_err(param.span()); + err.emit(); + return Ok(Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))); + } + + pub fn recover_const_param_declaration( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option> { + // We have to check for a few different cases. + if let Ok(arg) = self.recover_const_param_decl(ty_generics) { + return Ok(arg); + } + + // We haven't consumed `const` yet. + let start = self.token.span; + self.bump(); // `const` + + // Detect and recover from the old, pre-RFC2000 syntax for const generics. + let mut err = self + .struct_span_err(start, "expected lifetime, type, or constant, found keyword `const`"); + if self.check_const_arg() { + err.span_suggestion_verbose( + start.until(self.token.span), + "the `const` keyword is only needed in the definition of the type", + String::new(), + Applicability::MaybeIncorrect, + ); + err.emit(); + Ok(Some(GenericArg::Const(self.parse_const_arg()?))) + } else { + let after_kw_const = self.token.span; + self.recover_const_arg(after_kw_const, err).map(Some) + } + } + /// Try to recover from possible generic const argument without `{` and `}`. /// /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0c8c45410bd..54f28ea0058 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1150,7 +1150,7 @@ impl<'a> Parser<'a> { } let fn_span_lo = self.token.span; - let mut segment = self.parse_path_segment(PathStyle::Expr)?; + let mut segment = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); self.check_turbofish_missing_angle_brackets(&mut segment); diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index f175c5b50b3..a9ab2cd3f68 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -48,7 +48,10 @@ impl<'a> Parser<'a> { }) } - fn parse_const_param(&mut self, preceding_attrs: Vec) -> PResult<'a, GenericParam> { + crate fn parse_const_param( + &mut self, + preceding_attrs: Vec, + ) -> PResult<'a, GenericParam> { let const_span = self.token.span; self.expect_keyword(kw::Const)?; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 73ca809ab1d..24a8df49ac7 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -514,7 +514,7 @@ impl<'a> Parser<'a> { tokens: None, }) } else { - self.parse_ty()? + self.parse_ty_with_generics_recovery(&generics)? }; // If `for` is missing we try to recover. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index c7d080a80fe..7f8fadb33bd 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -3,10 +3,11 @@ use super::{Parser, TokenType}; use crate::maybe_whole; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; -use rustc_ast::{self as ast, AngleBracketedArg, AngleBracketedArgs, ParenthesizedArgs}; -use rustc_ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; -use rustc_ast::{GenericArg, GenericArgs}; -use rustc_ast::{Path, PathSegment, QSelf}; +use rustc_ast::{ + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocTyConstraint, + AssocTyConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, + Path, PathSegment, QSelf, +}; use rustc_errors::{pluralize, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym, Ident}; @@ -78,7 +79,7 @@ impl<'a> Parser<'a> { } let qself = QSelf { ty, path_span, position: path.segments.len() }; - self.parse_path_segments(&mut path.segments, style)?; + self.parse_path_segments(&mut path.segments, style, None)?; Ok(( qself, @@ -119,6 +120,10 @@ impl<'a> Parser<'a> { true } + pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { + self.parse_path_inner(style, None) + } + /// Parses simple paths. /// /// `path = [::] segment+` @@ -129,7 +134,11 @@ impl<'a> Parser<'a> { /// `a::b::C::` (with disambiguator) /// `Fn(Args)` (without disambiguator) /// `Fn::(Args)` (with disambiguator) - pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { + pub(super) fn parse_path_inner( + &mut self, + style: PathStyle, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Path> { maybe_whole!(self, NtPath, |path| { if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some()) { @@ -152,7 +161,7 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } - self.parse_path_segments(&mut segments, style)?; + self.parse_path_segments(&mut segments, style, ty_generics)?; Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None }) } @@ -161,9 +170,10 @@ impl<'a> Parser<'a> { &mut self, segments: &mut Vec, style: PathStyle, + ty_generics: Option<&Generics>, ) -> PResult<'a, ()> { loop { - let segment = self.parse_path_segment(style)?; + let segment = self.parse_path_segment(style, ty_generics)?; if style == PathStyle::Expr { // In order to check for trailing angle brackets, we must have finished // recursing (`parse_path_segment` can indirectly call this function), @@ -191,7 +201,11 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { + pub(super) fn parse_path_segment( + &mut self, + style: PathStyle, + ty_generics: Option<&Generics>, + ) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; let is_args_start = |token: &Token| { matches!( @@ -229,8 +243,11 @@ impl<'a> Parser<'a> { let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` - let args = - self.parse_angle_args_with_leading_angle_bracket_recovery(style, lo)?; + let args = self.parse_angle_args_with_leading_angle_bracket_recovery( + style, + lo, + ty_generics, + )?; self.expect_gt()?; let span = lo.to(self.prev_token.span); AngleBracketedArgs { args, span }.into() @@ -238,9 +255,9 @@ impl<'a> Parser<'a> { // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; let inputs_span = lo.to(self.prev_token.span); - let span = ident.span.to(self.prev_token.span); let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?; + let span = ident.span.to(self.prev_token.span); ParenthesizedArgs { span, inputs, inputs_span, output }.into() }; @@ -275,6 +292,7 @@ impl<'a> Parser<'a> { &mut self, style: PathStyle, lo: Span, + ty_generics: Option<&Generics>, ) -> PResult<'a, Vec> { // We need to detect whether there are extra leading left angle brackets and produce an // appropriate error and suggestion. This cannot be implemented by looking ahead at @@ -350,7 +368,7 @@ impl<'a> Parser<'a> { let snapshot = if is_first_invocation { Some(self.clone()) } else { None }; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); - match self.parse_angle_args() { + match self.parse_angle_args(ty_generics) { Ok(args) => Ok(args), Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { // Swap `self` with our backup of the parser state before attempting to parse @@ -403,7 +421,7 @@ impl<'a> Parser<'a> { .emit(); // Try again without unmatched angle bracket characters. - self.parse_angle_args() + self.parse_angle_args(ty_generics) } } Err(e) => Err(e), @@ -412,9 +430,12 @@ impl<'a> Parser<'a> { /// Parses (possibly empty) list of generic arguments / associated item constraints, /// possibly including trailing comma. - pub(super) fn parse_angle_args(&mut self) -> PResult<'a, Vec> { + pub(super) fn parse_angle_args( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Vec> { let mut args = Vec::new(); - while let Some(arg) = self.parse_angle_arg()? { + while let Some(arg) = self.parse_angle_arg(ty_generics)? { args.push(arg); if !self.eat(&token::Comma) { if !self.token.kind.should_end_const_arg() { @@ -431,9 +452,12 @@ impl<'a> Parser<'a> { } /// Parses a single argument in the angle arguments `<...>` of a path segment. - fn parse_angle_arg(&mut self) -> PResult<'a, Option> { + fn parse_angle_arg( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option> { let lo = self.token.span; - let arg = self.parse_generic_arg()?; + let arg = self.parse_generic_arg(ty_generics)?; match arg { Some(arg) => { if self.check(&token::Colon) | self.check(&token::Eq) { @@ -476,7 +500,7 @@ impl<'a> Parser<'a> { /// That is, parse `` in `Item = `. /// Right now, this only admits types in ``. fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P> { - let arg = self.parse_generic_arg()?; + let arg = self.parse_generic_arg(None)?; let span = ident.span.to(self.prev_token.span); match arg { Some(GenericArg::Type(ty)) => return Ok(ty), @@ -563,7 +587,10 @@ impl<'a> Parser<'a> { /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. - pub(super) fn parse_generic_arg(&mut self) -> PResult<'a, Option> { + pub(super) fn parse_generic_arg( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option> { let start = self.token.span; let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. @@ -580,25 +607,8 @@ impl<'a> Parser<'a> { return self.recover_const_arg(start, err).map(Some); } } - } else if self.eat_keyword_noexpect(kw::Const) { - // Detect and recover from the old, pre-RFC2000 syntax for const generics. - let mut err = self.struct_span_err( - start, - "expected lifetime, type, or constant, found keyword `const`", - ); - if self.check_const_arg() { - err.span_suggestion_verbose( - start.until(self.token.span), - "the `const` keyword is only needed in the definition of the type", - String::new(), - Applicability::MaybeIncorrect, - ); - err.emit(); - GenericArg::Const(self.parse_const_arg()?) - } else { - let after_kw_const = self.token.span; - return self.recover_const_arg(after_kw_const, err).map(Some); - } + } else if self.token.is_keyword(kw::Const) { + return self.recover_const_param_declaration(ty_generics); } else { return Ok(None); }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index c4c0c17addf..9bfde0e3900 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -4,9 +4,10 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token, TokenKind}; -use rustc_ast::{self as ast, BareFnTy, FnRetTy, GenericParam, Lifetime, MutTy, Ty, TyKind}; -use rustc_ast::{GenericBound, GenericBounds, MacCall, Mutability}; -use rustc_ast::{PolyTraitRef, TraitBoundModifier, TraitObjectSyntax}; +use rustc_ast::{ + self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, + MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, +}; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym}; @@ -98,6 +99,20 @@ impl<'a> Parser<'a> { AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::Yes, + None, + ) + } + + pub(super) fn parse_ty_with_generics_recovery( + &mut self, + ty_params: &Generics, + ) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + Some(ty_params), ) } @@ -110,6 +125,7 @@ impl<'a> Parser<'a> { AllowCVariadic::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes, + None, ) } @@ -125,6 +141,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::Yes, + None, ) } @@ -135,6 +152,7 @@ impl<'a> Parser<'a> { AllowCVariadic::Yes, RecoverQPath::Yes, RecoverReturnSign::OnlyFatArrow, + None, ) } @@ -152,6 +170,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No, recover_qpath, recover_return_sign, + None, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -171,6 +190,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No, recover_qpath, recover_return_sign, + None, )?; FnRetTy::Ty(ty) } else { @@ -184,6 +204,7 @@ impl<'a> Parser<'a> { allow_c_variadic: AllowCVariadic, recover_qpath: RecoverQPath, recover_return_sign: RecoverReturnSign, + ty_generics: Option<&Generics>, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -233,7 +254,7 @@ impl<'a> Parser<'a> { let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.check_path() { - self.parse_path_start_ty(lo, allow_plus)? + self.parse_path_start_ty(lo, allow_plus, ty_generics)? } else if self.can_begin_bound() { self.parse_bare_trait_object(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { @@ -512,9 +533,14 @@ impl<'a> Parser<'a> { /// 1. a type macro, `mac!(...)`, /// 2. a bare trait object, `B0 + ... + Bn`, /// 3. or a path, `path::to::MyType`. - fn parse_path_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { + fn parse_path_start_ty( + &mut self, + lo: Span, + allow_plus: AllowPlus, + ty_generics: Option<&Generics>, + ) -> PResult<'a, TyKind> { // Simple path - let path = self.parse_path(PathStyle::Type)?; + let path = self.parse_path_inner(PathStyle::Type, ty_generics)?; if self.eat(&token::Not) { // Macro invocation in type position Ok(TyKind::MacCall(MacCall { diff --git a/src/test/ui/parser/const-param-decl-on-type-instead-of-impl.rs b/src/test/ui/parser/const-param-decl-on-type-instead-of-impl.rs new file mode 100644 index 00000000000..ecdfce1e19e --- /dev/null +++ b/src/test/ui/parser/const-param-decl-on-type-instead-of-impl.rs @@ -0,0 +1,6 @@ +struct NInts([u8; N]); +impl NInts {} //~ ERROR unexpected `const` parameter declaration + +fn main() { + let _: () = 42; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/const-param-decl-on-type-instead-of-impl.stderr b/src/test/ui/parser/const-param-decl-on-type-instead-of-impl.stderr new file mode 100644 index 00000000000..07c7c69f8d6 --- /dev/null +++ b/src/test/ui/parser/const-param-decl-on-type-instead-of-impl.stderr @@ -0,0 +1,22 @@ +error: unexpected `const` parameter declaration + --> $DIR/const-param-decl-on-type-instead-of-impl.rs:2:18 + | +LL | impl NInts {} + | ^ expected a `const` expression, not a parameter declaration + | +help: `const` parameters must be declared for the `impl` + | +LL | impl NInts {} + | ++++++++++++++++ ~ + +error[E0308]: mismatched types + --> $DIR/const-param-decl-on-type-instead-of-impl.rs:5:17 + | +LL | let _: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`.