Recover extra trailing angle brackets in struct definition

This commit applies the existing 'extra angle bracket recovery' logic
when parsing fields in struct definitions. This allows us to continue
parsing the struct's fields, avoiding spurious 'missing field' errors in
code that tries to use the struct.
This commit is contained in:
Aaron Hill 2020-06-27 11:35:12 -04:00
parent 7750c3d46b
commit 765bd47fa0
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
6 changed files with 66 additions and 8 deletions

View File

@ -376,7 +376,14 @@ impl<'a> Parser<'a> {
/// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
/// ^^ help: remove extra angle brackets
/// ```
pub(super) fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: TokenKind) {
///
/// If `true` is returned, then trailing brackets were recovered, tokens were consumed
/// up until one of the tokens in 'end' was encountered, and an error was emitted.
pub(super) fn check_trailing_angle_brackets(
&mut self,
segment: &PathSegment,
end: &[&TokenKind],
) -> bool {
// This function is intended to be invoked after parsing a path segment where there are two
// cases:
//
@ -409,7 +416,7 @@ impl<'a> Parser<'a> {
parsed_angle_bracket_args,
);
if !parsed_angle_bracket_args {
return;
return false;
}
// Keep the span at the start so we can highlight the sequence of `>` characters to be
@ -447,18 +454,18 @@ impl<'a> Parser<'a> {
number_of_gt, number_of_shr,
);
if number_of_gt < 1 && number_of_shr < 1 {
return;
return false;
}
// Finally, double check that we have our end token as otherwise this is the
// second case.
if self.look_ahead(position, |t| {
trace!("check_trailing_angle_brackets: t={:?}", t);
*t == end
end.contains(&&t.kind)
}) {
// Eat from where we started until the end token so that parsing can continue
// as if we didn't have those extra angle brackets.
self.eat_to_tokens(&[&end]);
self.eat_to_tokens(end);
let span = lo.until(self.token.span);
let total_num_of_gt = number_of_gt + number_of_shr * 2;
@ -473,7 +480,9 @@ impl<'a> Parser<'a> {
Applicability::MachineApplicable,
)
.emit();
return true;
}
false
}
/// Check to see if a pair of chained operators looks like an attempt at chained comparison,

View File

@ -867,7 +867,7 @@ impl<'a> Parser<'a> {
let fn_span_lo = self.token.span;
let segment = self.parse_path_segment(PathStyle::Expr)?;
self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren));
self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]);
if self.check(&token::OpenDelim(token::Paren)) {
// Method call `expr.f()`

View File

@ -9,7 +9,7 @@ use rustc_ast::ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind,
use rustc_ast::ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
use rustc_ast::ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind};
use rustc_ast::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
use rustc_ast::ast::{FnHeader, ForeignItem, PathSegment, Visibility, VisibilityKind};
use rustc_ast::ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
use rustc_ast::ast::{MacArgs, MacCall, MacDelimiter};
use rustc_ast::ptr::P;
use rustc_ast::token::{self, TokenKind};
@ -1262,6 +1262,25 @@ impl<'a> Parser<'a> {
sp,
&format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)),
);
// Try to recover extra trailing angle brackets
let mut recovered = false;
if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind {
if let Some(last_segment) = segments.last() {
recovered = self.check_trailing_angle_brackets(
last_segment,
&[&token::Comma, &token::CloseDelim(token::Brace)],
);
if recovered {
// Handle a case like `Vec<u8>>,` where we can continue parsing fields
// after the comma
self.eat(&token::Comma);
// `check_trailing_angle_brackets` already emitted a nicer error
err.cancel();
}
}
}
if self.token.is_ident() {
// This is likely another field; emit the diagnostic and keep going
err.span_suggestion(
@ -1271,6 +1290,14 @@ impl<'a> Parser<'a> {
Applicability::MachineApplicable,
);
err.emit();
recovered = true;
}
if recovered {
// Make sure an error was emitted (either by recovering an angle bracket,
// or by finding an identifier as the next token), since we're
// going to continue parsing
assert!(self.sess.span_diagnostic.has_errors());
} else {
return Err(err);
}

View File

@ -169,7 +169,7 @@ impl<'a> Parser<'a> {
// `PathStyle::Expr` is only provided at the root invocation and never in
// `parse_path_segment` to recurse and therefore can be checked to maintain
// this invariant.
self.check_trailing_angle_brackets(&segment, token::ModSep);
self.check_trailing_angle_brackets(&segment, &[&token::ModSep]);
}
segments.push(segment);

View File

@ -0,0 +1,14 @@
// Tests that we recover from extra trailing angle brackets
// in a struct field
struct BadStruct {
first: Vec<u8>>, //~ ERROR unmatched angle bracket
second: bool
}
fn bar(val: BadStruct) {
val.first;
val.second;
}
fn main() {}

View File

@ -0,0 +1,8 @@
error: unmatched angle bracket
--> $DIR/recover-field-extra-angle-brackets.rs:5:19
|
LL | first: Vec<u8>>,
| ^ help: remove extra angle bracket
error: aborting due to previous error