parser: recover from where clauses placed before tuple struct bodies
This commit is contained in:
parent
b22c152958
commit
70ddde76df
11 changed files with 266 additions and 17 deletions
|
@ -368,3 +368,9 @@ parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of
|
||||||
|
|
||||||
parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn`
|
parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn`
|
||||||
.suggestion = use `Fn` to refer to the trait
|
.suggestion = use `Fn` to refer to the trait
|
||||||
|
|
||||||
|
parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies
|
||||||
|
.label = unexpected where clause
|
||||||
|
.name_label = while parsing this tuple struct
|
||||||
|
.body_label = the struct body
|
||||||
|
.suggestion = move the body before the where clause
|
||||||
|
|
|
@ -1237,3 +1237,27 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword {
|
||||||
#[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")]
|
#[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")]
|
||||||
pub fn_token_span: Span,
|
pub fn_token_span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parse_where_clause_before_tuple_struct_body)]
|
||||||
|
pub(crate) struct WhereClauseBeforeTupleStructBody {
|
||||||
|
#[primary_span]
|
||||||
|
#[label]
|
||||||
|
pub span: Span,
|
||||||
|
#[label(name_label)]
|
||||||
|
pub name: Span,
|
||||||
|
#[label(body_label)]
|
||||||
|
pub body: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
pub sugg: Option<WhereClauseBeforeTupleStructBodySugg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
|
||||||
|
pub(crate) struct WhereClauseBeforeTupleStructBodySugg {
|
||||||
|
#[suggestion_part(code = "{snippet}")]
|
||||||
|
pub left: Span,
|
||||||
|
pub snippet: String,
|
||||||
|
#[suggestion_part(code = "")]
|
||||||
|
pub right: Span,
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
|
use crate::errors::{WhereClauseBeforeTupleStructBody, WhereClauseBeforeTupleStructBodySugg};
|
||||||
|
|
||||||
use super::{ForceCollect, Parser, TrailingToken};
|
use super::{ForceCollect, Parser, TrailingToken};
|
||||||
|
|
||||||
|
use ast::token::Delimiter;
|
||||||
use rustc_ast::token;
|
use rustc_ast::token;
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause,
|
self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause,
|
||||||
};
|
};
|
||||||
use rustc_errors::{Applicability, PResult};
|
use rustc_errors::{Applicability, PResult};
|
||||||
use rustc_span::symbol::kw;
|
use rustc_span::symbol::{kw, Ident};
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
enum PredicateOrStructBody {
|
||||||
|
Predicate(ast::WherePredicate),
|
||||||
|
StructBody(Vec<ast::FieldDef>),
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
|
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
|
||||||
|
@ -240,23 +249,39 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an optional where-clause and places it in `generics`.
|
/// Parses an optional where-clause.
|
||||||
///
|
///
|
||||||
/// ```ignore (only-for-syntax-highlight)
|
/// ```ignore (only-for-syntax-highlight)
|
||||||
/// where T : Trait<U, V> + 'b, 'a : 'b
|
/// where T : Trait<U, V> + 'b, 'a : 'b
|
||||||
/// ```
|
/// ```
|
||||||
pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
|
pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
|
||||||
|
self.parse_where_clause_common(None).map(|(clause, _)| clause)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse_struct_where_clause(
|
||||||
|
&mut self,
|
||||||
|
struct_name: Ident,
|
||||||
|
body_insertion_point: Span,
|
||||||
|
) -> PResult<'a, (WhereClause, Option<Vec<ast::FieldDef>>)> {
|
||||||
|
self.parse_where_clause_common(Some((struct_name, body_insertion_point)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_where_clause_common(
|
||||||
|
&mut self,
|
||||||
|
struct_: Option<(Ident, Span)>,
|
||||||
|
) -> PResult<'a, (WhereClause, Option<Vec<ast::FieldDef>>)> {
|
||||||
let mut where_clause = WhereClause {
|
let mut where_clause = WhereClause {
|
||||||
has_where_token: false,
|
has_where_token: false,
|
||||||
predicates: Vec::new(),
|
predicates: Vec::new(),
|
||||||
span: self.prev_token.span.shrink_to_hi(),
|
span: self.prev_token.span.shrink_to_hi(),
|
||||||
};
|
};
|
||||||
|
let mut tuple_struct_body = None;
|
||||||
|
|
||||||
if !self.eat_keyword(kw::Where) {
|
if !self.eat_keyword(kw::Where) {
|
||||||
return Ok(where_clause);
|
return Ok((where_clause, None));
|
||||||
}
|
}
|
||||||
where_clause.has_where_token = true;
|
where_clause.has_where_token = true;
|
||||||
let lo = self.prev_token.span;
|
let where_lo = self.prev_token.span;
|
||||||
|
|
||||||
// We are considering adding generics to the `where` keyword as an alternative higher-rank
|
// We are considering adding generics to the `where` keyword as an alternative higher-rank
|
||||||
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
|
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
|
||||||
|
@ -272,7 +297,8 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let lo = self.token.span;
|
let where_sp = where_lo.to(self.prev_token.span);
|
||||||
|
let pred_lo = self.token.span;
|
||||||
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
|
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
|
||||||
let lifetime = self.expect_lifetime();
|
let lifetime = self.expect_lifetime();
|
||||||
// Bounds starting with a colon are mandatory, but possibly empty.
|
// Bounds starting with a colon are mandatory, but possibly empty.
|
||||||
|
@ -280,13 +306,21 @@ impl<'a> Parser<'a> {
|
||||||
let bounds = self.parse_lt_param_bounds();
|
let bounds = self.parse_lt_param_bounds();
|
||||||
where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
|
where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
|
||||||
ast::WhereRegionPredicate {
|
ast::WhereRegionPredicate {
|
||||||
span: lo.to(self.prev_token.span),
|
span: pred_lo.to(self.prev_token.span),
|
||||||
lifetime,
|
lifetime,
|
||||||
bounds,
|
bounds,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else if self.check_type() {
|
} else if self.check_type() {
|
||||||
where_clause.predicates.push(self.parse_ty_where_predicate()?);
|
match self.parse_ty_where_predicate_or_recover_tuple_struct_body(
|
||||||
|
struct_, pred_lo, where_sp,
|
||||||
|
)? {
|
||||||
|
PredicateOrStructBody::Predicate(pred) => where_clause.predicates.push(pred),
|
||||||
|
PredicateOrStructBody::StructBody(body) => {
|
||||||
|
tuple_struct_body = Some(body);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -297,7 +331,7 @@ impl<'a> Parser<'a> {
|
||||||
if self.eat_keyword_noexpect(kw::Where) {
|
if self.eat_keyword_noexpect(kw::Where) {
|
||||||
let msg = "cannot define duplicate `where` clauses on an item";
|
let msg = "cannot define duplicate `where` clauses on an item";
|
||||||
let mut err = self.struct_span_err(self.token.span, msg);
|
let mut err = self.struct_span_err(self.token.span, msg);
|
||||||
err.span_label(lo, "previous `where` clause starts here");
|
err.span_label(pred_lo, "previous `where` clause starts here");
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
prev_token.shrink_to_hi().to(self.prev_token.span),
|
prev_token.shrink_to_hi().to(self.prev_token.span),
|
||||||
"consider joining the two `where` clauses into one",
|
"consider joining the two `where` clauses into one",
|
||||||
|
@ -310,8 +344,72 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
where_clause.span = lo.to(self.prev_token.span);
|
where_clause.span = where_lo.to(self.prev_token.span);
|
||||||
Ok(where_clause)
|
Ok((where_clause, tuple_struct_body))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ty_where_predicate_or_recover_tuple_struct_body(
|
||||||
|
&mut self,
|
||||||
|
struct_: Option<(Ident, Span)>,
|
||||||
|
pred_lo: Span,
|
||||||
|
where_sp: Span,
|
||||||
|
) -> PResult<'a, PredicateOrStructBody> {
|
||||||
|
let mut snapshot = None;
|
||||||
|
|
||||||
|
if let Some(struct_) = struct_
|
||||||
|
&& self.may_recover()
|
||||||
|
&& self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
|
||||||
|
{
|
||||||
|
snapshot = Some((struct_, self.create_snapshot_for_diagnostic()));
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.parse_ty_where_predicate() {
|
||||||
|
Ok(pred) => Ok(PredicateOrStructBody::Predicate(pred)),
|
||||||
|
Err(type_err) => {
|
||||||
|
let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else {
|
||||||
|
return Err(type_err);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if we might have encountered an out of place tuple struct body.
|
||||||
|
match snapshot.parse_tuple_struct_body() {
|
||||||
|
// Since we don't know the exact reason why we failed to parse the
|
||||||
|
// predicate (we might have stumbled upon something bogus like `(T): ?`),
|
||||||
|
// employ a simple heuristic to weed out some pathological cases:
|
||||||
|
// Look for a semicolon (strong indicator) or anything that might mark
|
||||||
|
// the end of the item (weak indicator) following the body.
|
||||||
|
Ok(body)
|
||||||
|
if matches!(snapshot.token.kind, token::Semi | token::Eof)
|
||||||
|
|| snapshot.token.can_begin_item() =>
|
||||||
|
{
|
||||||
|
type_err.cancel();
|
||||||
|
|
||||||
|
let body_sp = pred_lo.to(snapshot.prev_token.span);
|
||||||
|
let map = self.sess.source_map();
|
||||||
|
|
||||||
|
self.sess.emit_err(WhereClauseBeforeTupleStructBody {
|
||||||
|
span: where_sp,
|
||||||
|
name: struct_name.span,
|
||||||
|
body: body_sp,
|
||||||
|
sugg: map.span_to_snippet(body_sp).ok().map(|body| {
|
||||||
|
WhereClauseBeforeTupleStructBodySugg {
|
||||||
|
left: body_insertion_point.shrink_to_hi(),
|
||||||
|
snippet: body,
|
||||||
|
right: map.end_point(where_sp).to(body_sp),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.restore_snapshot(snapshot);
|
||||||
|
Ok(PredicateOrStructBody::StructBody(body))
|
||||||
|
}
|
||||||
|
Ok(_) => Err(type_err),
|
||||||
|
Err(body_err) => {
|
||||||
|
body_err.cancel();
|
||||||
|
Err(type_err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
|
fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
|
||||||
|
|
|
@ -1454,8 +1454,16 @@ impl<'a> Parser<'a> {
|
||||||
// struct.
|
// struct.
|
||||||
|
|
||||||
let vdata = if self.token.is_keyword(kw::Where) {
|
let vdata = if self.token.is_keyword(kw::Where) {
|
||||||
generics.where_clause = self.parse_where_clause()?;
|
let tuple_struct_body;
|
||||||
if self.eat(&token::Semi) {
|
(generics.where_clause, tuple_struct_body) =
|
||||||
|
self.parse_struct_where_clause(class_name, generics.span)?;
|
||||||
|
|
||||||
|
if let Some(body) = tuple_struct_body {
|
||||||
|
// If we see a misplaced tuple struct body: `struct Foo<T> where T: Copy, (T);`
|
||||||
|
let body = VariantData::Tuple(body, DUMMY_NODE_ID);
|
||||||
|
self.expect_semi()?;
|
||||||
|
body
|
||||||
|
} else if self.eat(&token::Semi) {
|
||||||
// If we see a: `struct Foo<T> where T: Copy;` style decl.
|
// If we see a: `struct Foo<T> where T: Copy;` style decl.
|
||||||
VariantData::Unit(DUMMY_NODE_ID)
|
VariantData::Unit(DUMMY_NODE_ID)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1575,7 +1583,7 @@ impl<'a> Parser<'a> {
|
||||||
Ok((fields, recovered))
|
Ok((fields, recovered))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> {
|
pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> {
|
||||||
// This is the case where we find `struct Foo<T>(T) where T: Copy;`
|
// This is the case where we find `struct Foo<T>(T) where T: Copy;`
|
||||||
// Unit like structs are handled in parse_item_struct function
|
// Unit like structs are handled in parse_item_struct function
|
||||||
self.parse_paren_comma_seq(|p| {
|
self.parse_paren_comma_seq(|p| {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
// compile-flags: -Zparse-only
|
||||||
|
|
||||||
struct Baz<U> where U: Eq(U); //This is parsed as the new Fn* style parenthesis syntax.
|
struct Baz<U> where U: Eq(U); //This is parsed as the new Fn* style parenthesis syntax.
|
||||||
struct Baz<U> where U: Eq(U) -> R; // Notice this parses as well.
|
struct Baz<U> where U: Eq(U) -> R; // Notice this parses as well.
|
||||||
struct Baz<U>(U) where U: Eq; // This rightfully signals no error as well.
|
struct Baz<U>(U) where U: Eq; // This rightfully signals no error as well.
|
||||||
struct Foo<T> where T: Copy, (T); //~ ERROR expected one of `:`, `==`, or `=`, found `;`
|
struct Foo<T> where T: Copy, (T); //~ ERROR where clauses are not allowed before tuple struct bodies
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
error: expected one of `:`, `==`, or `=`, found `;`
|
error: where clauses are not allowed before tuple struct bodies
|
||||||
--> $DIR/issue-17904.rs:4:33
|
--> $DIR/issue-17904.rs:6:15
|
||||||
|
|
|
|
||||||
LL | struct Foo<T> where T: Copy, (T);
|
LL | struct Foo<T> where T: Copy, (T);
|
||||||
| ^ expected one of `:`, `==`, or `=`
|
| --- ^^^^^^^^^^^^^^ --- the struct body
|
||||||
|
| | |
|
||||||
|
| | unexpected where clause
|
||||||
|
| while parsing this tuple struct
|
||||||
|
|
|
||||||
|
help: move the body before the where clause
|
||||||
|
|
|
||||||
|
LL - struct Foo<T> where T: Copy, (T);
|
||||||
|
LL + struct Foo<T>(T) where T: Copy;
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Regression test for issues #100790 and #106439.
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
pub struct Example(usize)
|
||||||
|
where
|
||||||
|
(): Sized;
|
||||||
|
//~^^^ ERROR where clauses are not allowed before tuple struct bodies
|
||||||
|
|
||||||
|
struct _Demo(pub usize, usize)
|
||||||
|
where
|
||||||
|
(): Sized,
|
||||||
|
String: Clone;
|
||||||
|
//~^^^^ ERROR where clauses are not allowed before tuple struct bodies
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Regression test for issues #100790 and #106439.
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
pub struct Example
|
||||||
|
where
|
||||||
|
(): Sized,
|
||||||
|
(usize);
|
||||||
|
//~^^^ ERROR where clauses are not allowed before tuple struct bodies
|
||||||
|
|
||||||
|
struct _Demo
|
||||||
|
where
|
||||||
|
(): Sized,
|
||||||
|
String: Clone,
|
||||||
|
(pub usize, usize);
|
||||||
|
//~^^^^ ERROR where clauses are not allowed before tuple struct bodies
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,40 @@
|
||||||
|
error: where clauses are not allowed before tuple struct bodies
|
||||||
|
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
|
||||||
|
|
|
||||||
|
LL | pub struct Example
|
||||||
|
| ------- while parsing this tuple struct
|
||||||
|
LL | / where
|
||||||
|
LL | | (): Sized,
|
||||||
|
| |______________^ unexpected where clause
|
||||||
|
LL | (usize);
|
||||||
|
| ------- the struct body
|
||||||
|
|
|
||||||
|
help: move the body before the where clause
|
||||||
|
|
|
||||||
|
LL ~ pub struct Example(usize)
|
||||||
|
LL | where
|
||||||
|
LL ~ (): Sized;
|
||||||
|
|
|
||||||
|
|
||||||
|
error: where clauses are not allowed before tuple struct bodies
|
||||||
|
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
|
||||||
|
|
|
||||||
|
LL | struct _Demo
|
||||||
|
| ----- while parsing this tuple struct
|
||||||
|
LL | / where
|
||||||
|
LL | | (): Sized,
|
||||||
|
LL | | String: Clone,
|
||||||
|
| |__________________^ unexpected where clause
|
||||||
|
LL | (pub usize, usize);
|
||||||
|
| ------------------ the struct body
|
||||||
|
|
|
||||||
|
help: move the body before the where clause
|
||||||
|
|
|
||||||
|
LL ~ struct _Demo(pub usize, usize)
|
||||||
|
LL | where
|
||||||
|
LL | (): Sized,
|
||||||
|
LL ~ String: Clone;
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Regression test for issues #100790 and #106439.
|
||||||
|
|
||||||
|
// Make sure that we still show a helpful error message even if the trailing semicolon is missing.
|
||||||
|
|
||||||
|
struct Foo<T> where T: MyTrait, (T)
|
||||||
|
//~^ ERROR where clauses are not allowed before tuple struct bodies
|
||||||
|
//~| ERROR expected `;`, found `<eof>`
|
|
@ -0,0 +1,23 @@
|
||||||
|
error: where clauses are not allowed before tuple struct bodies
|
||||||
|
--> $DIR/recover-where-clause-before-tuple-struct-body-1.rs:5:15
|
||||||
|
|
|
||||||
|
LL | struct Foo<T> where T: MyTrait, (T)
|
||||||
|
| --- ^^^^^^^^^^^^^^^^^ --- the struct body
|
||||||
|
| | |
|
||||||
|
| | unexpected where clause
|
||||||
|
| while parsing this tuple struct
|
||||||
|
|
|
||||||
|
help: move the body before the where clause
|
||||||
|
|
|
||||||
|
LL - struct Foo<T> where T: MyTrait, (T)
|
||||||
|
LL + struct Foo<T>(T) where T: MyTrait
|
||||||
|
|
|
||||||
|
|
||||||
|
error: expected `;`, found `<eof>`
|
||||||
|
--> $DIR/recover-where-clause-before-tuple-struct-body-1.rs:5:35
|
||||||
|
|
|
||||||
|
LL | struct Foo<T> where T: MyTrait, (T)
|
||||||
|
| ^ expected `;`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue