better suggestion for duplicated where

This commit is contained in:
Michael Goulet 2022-02-01 21:44:44 -08:00
parent 1ea4851715
commit f35d43cdf0
6 changed files with 147 additions and 9 deletions

View File

@ -4,7 +4,7 @@
use rustc_ast::{
self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause,
};
use rustc_errors::PResult;
use rustc_errors::{Applicability, PResult};
use rustc_span::symbol::kw;
impl<'a> Parser<'a> {
@ -256,7 +256,21 @@ pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
break;
}
if !self.eat(&token::Comma) {
let prev_token = self.prev_token.span;
let ate_comma = self.eat(&token::Comma);
if self.eat_keyword_noexpect(kw::Where) {
let msg = &format!("cannot define duplicate `where` clauses on an item");
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(lo, "previous `where` clause starts here");
err.span_suggestion_verbose(
prev_token.shrink_to_hi().to(self.prev_token.span),
"consider joining the two `where` clauses into one",
",".to_owned(),
Applicability::MaybeIncorrect,
);
err.emit();
} else if !ate_comma {
break;
}
}

View File

@ -1221,7 +1221,7 @@ fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
// Parse a struct variant.
let (fields, recovered) = this.parse_record_struct_body("struct")?;
let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
VariantData::Struct(fields, recovered)
} else if this.check(&token::OpenDelim(token::Paren)) {
VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
@ -1275,7 +1275,8 @@ fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
VariantData::Unit(DUMMY_NODE_ID)
} else {
// If we see: `struct Foo<T> where T: Copy { ... }`
let (fields, recovered) = self.parse_record_struct_body("struct")?;
let (fields, recovered) =
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
}
// No `where` so: `struct Foo<T>;`
@ -1283,7 +1284,8 @@ fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
VariantData::Unit(DUMMY_NODE_ID)
// Record-style struct definition
} else if self.token == token::OpenDelim(token::Brace) {
let (fields, recovered) = self.parse_record_struct_body("struct")?;
let (fields, recovered) =
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
// Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(token::Paren) {
@ -1313,10 +1315,12 @@ fn parse_item_union(&mut self) -> PResult<'a, ItemInfo> {
let vdata = if self.token.is_keyword(kw::Where) {
generics.where_clause = self.parse_where_clause()?;
let (fields, recovered) = self.parse_record_struct_body("union")?;
let (fields, recovered) =
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
} else if self.token == token::OpenDelim(token::Brace) {
let (fields, recovered) = self.parse_record_struct_body("union")?;
let (fields, recovered) =
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
} else {
let token_str = super::token_descr(&self.token);
@ -1332,6 +1336,7 @@ fn parse_item_union(&mut self) -> PResult<'a, ItemInfo> {
fn parse_record_struct_body(
&mut self,
adt_ty: &str,
parsed_where: bool,
) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
let mut fields = Vec::new();
let mut recovered = false;
@ -1353,9 +1358,19 @@ fn parse_record_struct_body(
self.eat(&token::CloseDelim(token::Brace));
} else {
let token_str = super::token_descr(&self.token);
let msg = &format!("expected `where`, or `{{` after struct name, found {}", token_str);
let msg = &format!(
"expected {}`{{` after struct name, found {}",
if parsed_where { "" } else { "`where`, or " },
token_str
);
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected `where`, or `{` after struct name");
err.span_label(
self.token.span,
format!(
"expected {}`{{` after struct name",
if parsed_where { "" } else { "`where`, or " }
),
);
return Err(err);
}

View File

@ -0,0 +1,2 @@
struct A where T: Sized !
//~^ ERROR expected `{` after struct name, found

View File

@ -0,0 +1,8 @@
error: expected `{` after struct name, found `!`
--> $DIR/bad-struct-following-where.rs:1:25
|
LL | struct A where T: Sized !
| ^ expected `{` after struct name
error: aborting due to previous error

View File

@ -0,0 +1,19 @@
struct A where (): Sized where (): Sized {}
//~^ ERROR cannot define duplicate `where` clauses on an item
fn b() where (): Sized where (): Sized {}
//~^ ERROR cannot define duplicate `where` clauses on an item
enum C where (): Sized where (): Sized {}
//~^ ERROR cannot define duplicate `where` clauses on an item
struct D where (): Sized, where (): Sized {}
//~^ ERROR cannot define duplicate `where` clauses on an item
fn e() where (): Sized, where (): Sized {}
//~^ ERROR cannot define duplicate `where` clauses on an item
enum F where (): Sized, where (): Sized {}
//~^ ERROR cannot define duplicate `where` clauses on an item
fn main() {}

View File

@ -0,0 +1,80 @@
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clauses.rs:1:32
|
LL | struct A where (): Sized where (): Sized {}
| - ^
| |
| previous `where` clause starts here
|
help: consider joining the two `where` clauses into one
|
LL | struct A where (): Sized, (): Sized {}
| ~
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clauses.rs:4:30
|
LL | fn b() where (): Sized where (): Sized {}
| - ^
| |
| previous `where` clause starts here
|
help: consider joining the two `where` clauses into one
|
LL | fn b() where (): Sized, (): Sized {}
| ~
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clauses.rs:7:30
|
LL | enum C where (): Sized where (): Sized {}
| - ^
| |
| previous `where` clause starts here
|
help: consider joining the two `where` clauses into one
|
LL | enum C where (): Sized, (): Sized {}
| ~
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clauses.rs:10:33
|
LL | struct D where (): Sized, where (): Sized {}
| - ^
| |
| previous `where` clause starts here
|
help: consider joining the two `where` clauses into one
|
LL | struct D where (): Sized, (): Sized {}
| ~
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clauses.rs:13:31
|
LL | fn e() where (): Sized, where (): Sized {}
| - ^
| |
| previous `where` clause starts here
|
help: consider joining the two `where` clauses into one
|
LL | fn e() where (): Sized, (): Sized {}
| ~
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clauses.rs:16:31
|
LL | enum F where (): Sized, where (): Sized {}
| - ^
| |
| previous `where` clause starts here
|
help: consider joining the two `where` clauses into one
|
LL | enum F where (): Sized, (): Sized {}
| ~
error: aborting due to 6 previous errors