Skip to content

Commit

Permalink
Rollup merge of #93571 - compiler-errors:better-where-suggestion, r=lcnr
Browse files Browse the repository at this point in the history
better suggestion for duplicated `where` clause

fixes #93567
  • Loading branch information
matthiaskrgr authored Feb 2, 2022
2 parents b53eaf7 + f35d43c commit ef50086
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 9 deletions.
18 changes: 16 additions & 2 deletions compiler/rustc_parse/src/parser/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_ast::token;
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> {
Expand Down Expand Up @@ -256,7 +256,21 @@ impl<'a> Parser<'a> {
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;
}
}
Expand Down
29 changes: 22 additions & 7 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,7 @@ impl<'a> Parser<'a> {

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)
Expand Down Expand Up @@ -1275,15 +1275,17 @@ impl<'a> Parser<'a> {
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>;`
} else if self.eat(&token::Semi) {
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) {
Expand Down Expand Up @@ -1313,10 +1315,12 @@ impl<'a> Parser<'a> {

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);
Expand All @@ -1332,6 +1336,7 @@ impl<'a> Parser<'a> {
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;
Expand All @@ -1353,9 +1358,19 @@ impl<'a> Parser<'a> {
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);
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/parser/bad-struct-following-where.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
struct A where T: Sized !
//~^ ERROR expected `{` after struct name, found
8 changes: 8 additions & 0 deletions src/test/ui/parser/bad-struct-following-where.stderr
Original file line number Diff line number Diff line change
@@ -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

19 changes: 19 additions & 0 deletions src/test/ui/parser/duplicate-where-clauses.rs
Original file line number Diff line number Diff line change
@@ -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() {}
80 changes: 80 additions & 0 deletions src/test/ui/parser/duplicate-where-clauses.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ef50086

Please sign in to comment.