Skip to content

Commit

Permalink
Account for incorrect impl Foo<const N: ty> {} syntax
Browse files Browse the repository at this point in the history
Fix #84946
  • Loading branch information
estebank committed Nov 24, 2021
1 parent 311fa1f commit 7190bc3
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 54 deletions.
15 changes: 15 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Expand Up @@ -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)]
Expand Down
82 changes: 76 additions & 6 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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<_>>()`.
Expand Down Expand Up @@ -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)) => {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -1909,6 +1911,74 @@ impl<'a> Parser<'a> {
Ok(expr)
}

fn recover_const_param_decl(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<GenericArg>> {
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<GenericArg>> {
// 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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Expand Up @@ -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);

Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_parse/src/parser/generics.rs
Expand Up @@ -48,7 +48,10 @@ impl<'a> Parser<'a> {
})
}

fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
crate fn parse_const_param(
&mut self,
preceding_attrs: Vec<Attribute>,
) -> PResult<'a, GenericParam> {
let const_span = self.token.span;

self.expect_keyword(kw::Const)?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/item.rs
Expand Up @@ -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.
Expand Down
88 changes: 49 additions & 39 deletions compiler/rustc_parse/src/parser/path.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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+`
Expand All @@ -129,7 +134,11 @@ impl<'a> Parser<'a> {
/// `a::b::C::<D>` (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())
{
Expand All @@ -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 })
}
Expand All @@ -161,9 +170,10 @@ impl<'a> Parser<'a> {
&mut self,
segments: &mut Vec<PathSegment>,
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),
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -229,18 +243,21 @@ 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()
} else {
// `(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()
};

Expand Down Expand Up @@ -275,6 +292,7 @@ impl<'a> Parser<'a> {
&mut self,
style: PathStyle,
lo: Span,
ty_generics: Option<&Generics>,
) -> PResult<'a, Vec<AngleBracketedArg>> {
// 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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -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<AngleBracketedArg>> {
pub(super) fn parse_angle_args(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Vec<AngleBracketedArg>> {
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() {
Expand All @@ -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<AngleBracketedArg>> {
fn parse_angle_arg(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<AngleBracketedArg>> {
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) {
Expand Down Expand Up @@ -476,7 +500,7 @@ impl<'a> Parser<'a> {
/// That is, parse `<term>` in `Item = <term>`.
/// Right now, this only admits types in `<term>`.
fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P<ast::Ty>> {
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),
Expand Down Expand Up @@ -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<GenericArg>> {
pub(super) fn parse_generic_arg(
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<GenericArg>> {
let start = self.token.span;
let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
Expand All @@ -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);
};
Expand Down

0 comments on commit 7190bc3

Please sign in to comment.