Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account for incorrect impl Foo<const N: ty> {} syntax #85346

Merged
merged 3 commits into from
Nov 25, 2021
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
15 changes: 15 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
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
79 changes: 73 additions & 6 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
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,71 @@ 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.span(), "unexpected `const` parameter declaration");
err.span_label(param.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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading