Parse const generics

Co-Authored-By: Gabriel Smith <yodaldevoid@users.noreply.github.com>
This commit is contained in:
varkor 2019-02-05 16:49:38 +01:00
parent 8d83521f0b
commit 0a8d98a270
2 changed files with 113 additions and 179 deletions

View File

@ -397,6 +397,7 @@ crate enum TokenType {
Ident,
Path,
Type,
Const,
}
impl TokenType {
@ -409,6 +410,7 @@ impl TokenType {
TokenType::Ident => "identifier".to_string(),
TokenType::Path => "path".to_string(),
TokenType::Type => "type".to_string(),
TokenType::Const => "const".to_string(),
}
}
}
@ -946,6 +948,19 @@ impl<'a> Parser<'a> {
}
}
fn check_const_param(&mut self) -> bool {
self.check_keyword(keywords::Const)
}
fn check_const_arg(&mut self) -> bool {
if self.token.can_begin_const_arg() {
true
} else {
self.expected_tokens.push(TokenType::Const);
false
}
}
/// Expect and consume a `+`. if `+=` is seen, replace it with a `=`
/// and continue. If a `+` is not seen, return false.
///
@ -5482,15 +5497,28 @@ impl<'a> Parser<'a> {
Ok((ident, TraitItemKind::Type(bounds, default), generics))
}
fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
self.expect_keyword(keywords::Const)?;
let ident = self.parse_ident()?;
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
Ok(GenericParam {
ident,
id: ast::DUMMY_NODE_ID,
attrs: preceding_attrs.into(),
bounds: Vec::new(),
kind: GenericParamKind::Const {
ty,
}
})
}
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
/// trailing comma and erroneous trailing attributes.
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
let mut lifetimes = Vec::new();
let mut params = Vec::new();
let mut seen_ty_param: Option<Span> = None;
let mut last_comma_span = None;
let mut bad_lifetime_pos = vec![];
let mut suggestions = vec![];
let mut prev_param: Option<ParamKindOrd> = None;
loop {
let attrs = self.parse_outer_attributes()?;
if self.check_lifetime() {
@ -5501,39 +5529,36 @@ impl<'a> Parser<'a> {
} else {
Vec::new()
};
lifetimes.push(ast::GenericParam {
params.push(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs: attrs.into(),
bounds,
kind: ast::GenericParamKind::Lifetime,
});
if let Some(sp) = seen_ty_param {
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
bad_lifetime_pos.push(self.prev_span);
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
suggestions.push((remove_sp, String::new()));
suggestions.push((
sp.shrink_to_lo(),
format!("{}, ", snippet)));
}
}
prev_param = Some(ParamKindOrd::Lifetime);
} else if self.check_const_param() {
// Parse const parameter.
params.push(self.parse_const_param(attrs)?);
prev_param = Some(ParamKindOrd::Const);
} else if self.check_ident() {
// Parse type parameter.
params.push(self.parse_ty_param(attrs)?);
if seen_ty_param.is_none() {
seen_ty_param = Some(self.prev_span);
}
prev_param = Some(ParamKindOrd::Type);
} else {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
let param_kind = if seen_ty_param.is_some() { "type" } else { "lifetime" };
self.struct_span_err(
attrs[0].span,
&format!("trailing attribute after {} parameters", param_kind),
)
.span_label(attrs[0].span, "attributes must go before parameters")
.emit();
if let Some(prev_param) = prev_param {
self.struct_span_err(
attrs[0].span,
&format!(
"trailing attribute after {} parameter",
prev_param,
),
)
.span_label(attrs[0].span, "attributes must go before parameters")
.emit();
}
}
break
}
@ -5541,24 +5566,8 @@ impl<'a> Parser<'a> {
if !self.eat(&token::Comma) {
break
}
last_comma_span = Some(self.prev_span);
}
if !bad_lifetime_pos.is_empty() {
let mut err = self.struct_span_err(
bad_lifetime_pos,
"lifetime parameters must be declared prior to type parameters",
);
if !suggestions.is_empty() {
err.multipart_suggestion(
"move the lifetime parameter prior to the first type parameter",
suggestions,
Applicability::MachineApplicable,
);
}
err.emit();
}
lifetimes.extend(params); // ensure the correct order of lifetimes and type params
Ok(lifetimes)
Ok(params)
}
/// Parse a set of optional generic type parameter declarations. Where
@ -5740,35 +5749,16 @@ impl<'a> Parser<'a> {
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
let mut args = Vec::new();
let mut bindings = Vec::new();
let mut misplaced_assoc_ty_bindings: Vec<Span> = Vec::new();
let mut assoc_ty_bindings: Vec<Span> = Vec::new();
let mut seen_type = false;
let mut seen_binding = false;
let args_lo = self.span;
let mut last_comma_span = None;
let mut first_type_or_binding_span: Option<Span> = None;
let mut first_binding_span: Option<Span> = None;
let mut bad_lifetime_pos = vec![];
let mut bad_type_pos = vec![];
let mut lifetime_suggestions = vec![];
let mut type_suggestions = vec![];
loop {
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
args.push(GenericArg::Lifetime(self.expect_lifetime()));
if seen_type || seen_binding {
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
bad_lifetime_pos.push(self.prev_span);
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
lifetime_suggestions.push((remove_sp, String::new()));
lifetime_suggestions.push((
first_type_or_binding_span.unwrap().shrink_to_lo(),
format!("{}, ", snippet)));
}
}
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
// Parse associated type binding.
let lo = self.span;
@ -5782,133 +5772,61 @@ impl<'a> Parser<'a> {
ty,
span,
});
seen_binding = true;
if first_type_or_binding_span.is_none() {
first_type_or_binding_span = Some(span);
}
if first_binding_span.is_none() {
first_binding_span = Some(span);
}
assoc_ty_bindings.push(span);
} else if self.check_const_arg() {
// Parse const argument.
let expr = if let token::OpenDelim(token::Brace) = self.token {
self.parse_block_expr(None, self.span, BlockCheckMode::Default, ThinVec::new())?
} else if self.token.can_begin_literal_or_bool() {
let lit = self.parse_lit()?;
self.mk_expr(lit.span, ExprKind::Lit(lit), ThinVec::new())
} else {
// FIXME(const_generics): to distinguish between idents for types and consts,
// we should introduce a GenericArg::Ident in the AST and distinguish when
// lowering to the HIR. For now, idents for const args are not permitted.
return Err(
self.fatal("identifiers may currently not be used for const generics")
);
};
let value = AnonConst {
id: ast::DUMMY_NODE_ID,
value: expr,
};
args.push(GenericArg::Const(value));
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
} else if self.check_type() {
// Parse type argument.
let ty_param = self.parse_ty()?;
if seen_binding {
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
bad_type_pos.push(self.prev_span);
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
type_suggestions.push((remove_sp, String::new()));
type_suggestions.push((
first_binding_span.unwrap().shrink_to_lo(),
format!("{}, ", snippet)));
}
}
if first_type_or_binding_span.is_none() {
first_type_or_binding_span = Some(ty_param.span);
}
args.push(GenericArg::Type(ty_param));
seen_type = true;
args.push(GenericArg::Type(self.parse_ty()?));
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
} else {
break
}
if !self.eat(&token::Comma) {
break
} else {
last_comma_span = Some(self.prev_span);
}
}
self.maybe_report_incorrect_generic_argument_order(
bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions
);
// FIXME: we would like to report this in ast_validation instead, but we currently do not
// preserve ordering of generic parameters with respect to associated type binding, so we
// lose that information after parsing.
if misplaced_assoc_ty_bindings.len() > 0 {
let mut err = self.struct_span_err(
args_lo.to(self.prev_span),
"associated type bindings must be declared after generic parameters",
);
for span in misplaced_assoc_ty_bindings {
err.span_label(
span,
"this associated type binding should be moved after the generic parameters",
);
}
err.emit();
}
Ok((args, bindings))
}
/// Maybe report an error about incorrect generic argument order - "lifetime parameters
/// must be declared before type parameters", "type parameters must be declared before
/// associated type bindings" or both.
fn maybe_report_incorrect_generic_argument_order(
&self,
bad_lifetime_pos: Vec<Span>,
bad_type_pos: Vec<Span>,
lifetime_suggestions: Vec<(Span, String)>,
type_suggestions: Vec<(Span, String)>,
) {
let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() {
let mut positions = bad_lifetime_pos.clone();
positions.extend_from_slice(&bad_type_pos);
self.struct_span_err(
positions,
"generic arguments must declare lifetimes, types and associated type bindings in \
that order",
)
} else if !bad_lifetime_pos.is_empty() {
self.struct_span_err(
bad_lifetime_pos.clone(),
"lifetime parameters must be declared prior to type parameters"
)
} else if !bad_type_pos.is_empty() {
self.struct_span_err(
bad_type_pos.clone(),
"type parameters must be declared prior to associated type bindings"
)
} else {
return;
};
if !bad_lifetime_pos.is_empty() {
for sp in &bad_lifetime_pos {
err.span_label(*sp, "must be declared prior to type parameters");
}
}
if !bad_type_pos.is_empty() {
for sp in &bad_type_pos {
err.span_label(*sp, "must be declared prior to associated type bindings");
}
}
if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() {
let mut suggestions = lifetime_suggestions;
suggestions.extend_from_slice(&type_suggestions);
let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1;
err.multipart_suggestion(
&format!(
"move the parameter{}",
if plural { "s" } else { "" },
),
suggestions,
Applicability::MachineApplicable,
);
} else if !lifetime_suggestions.is_empty() {
err.multipart_suggestion(
&format!(
"move the lifetime parameter{} prior to the first type parameter",
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
),
lifetime_suggestions,
Applicability::MachineApplicable,
);
} else if !type_suggestions.is_empty() {
err.multipart_suggestion(
&format!(
"move the type parameter{} prior to the first associated type binding",
if bad_type_pos.len() > 1 { "s" } else { "" },
),
type_suggestions,
Applicability::MachineApplicable,
);
}
err.emit();
}
/// Parses an optional `where` clause and places it in `generics`.
///
/// ```ignore (only-for-syntax-highlight)
@ -6526,6 +6444,7 @@ impl<'a> Parser<'a> {
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
// `<` const IDENT - generic const parameter
// The only truly ambiguous case is
// `<` IDENT `>` `::` IDENT ...
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
@ -6535,7 +6454,8 @@ impl<'a> Parser<'a> {
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
t == &token::Colon || t == &token::Eq))
t == &token::Colon || t == &token::Eq) ||
self.look_ahead(1, |t| t.is_keyword(keywords::Const)))
}
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {

View File

@ -279,6 +279,20 @@ impl Token {
}
}
/// Returns `true` if the token can appear at the start of a const param.
pub fn can_begin_const_arg(&self) -> bool {
match self {
OpenDelim(Brace) => true,
Interpolated(ref nt) => match nt.0 {
NtExpr(..) => true,
NtBlock(..) => true,
NtLiteral(..) => true,
_ => false,
}
_ => self.can_begin_literal_or_bool(),
}
}
/// Returns `true` if the token can appear at the start of a generic bound.
crate fn can_begin_bound(&self) -> bool {
self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||