Skip to content

Commit f35d43c

Browse files
better suggestion for duplicated where
1 parent 1ea4851 commit f35d43c

File tree

6 files changed

+147
-9
lines changed

6 files changed

+147
-9
lines changed

compiler/rustc_parse/src/parser/generics.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_ast::token;
44
use rustc_ast::{
55
self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause,
66
};
7-
use rustc_errors::PResult;
7+
use rustc_errors::{Applicability, PResult};
88
use rustc_span::symbol::kw;
99

1010
impl<'a> Parser<'a> {
@@ -256,7 +256,21 @@ impl<'a> Parser<'a> {
256256
break;
257257
}
258258

259-
if !self.eat(&token::Comma) {
259+
let prev_token = self.prev_token.span;
260+
let ate_comma = self.eat(&token::Comma);
261+
262+
if self.eat_keyword_noexpect(kw::Where) {
263+
let msg = &format!("cannot define duplicate `where` clauses on an item");
264+
let mut err = self.struct_span_err(self.token.span, msg);
265+
err.span_label(lo, "previous `where` clause starts here");
266+
err.span_suggestion_verbose(
267+
prev_token.shrink_to_hi().to(self.prev_token.span),
268+
"consider joining the two `where` clauses into one",
269+
",".to_owned(),
270+
Applicability::MaybeIncorrect,
271+
);
272+
err.emit();
273+
} else if !ate_comma {
260274
break;
261275
}
262276
}

compiler/rustc_parse/src/parser/item.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ impl<'a> Parser<'a> {
12211221

12221222
let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
12231223
// Parse a struct variant.
1224-
let (fields, recovered) = this.parse_record_struct_body("struct")?;
1224+
let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
12251225
VariantData::Struct(fields, recovered)
12261226
} else if this.check(&token::OpenDelim(token::Paren)) {
12271227
VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
@@ -1275,15 +1275,17 @@ impl<'a> Parser<'a> {
12751275
VariantData::Unit(DUMMY_NODE_ID)
12761276
} else {
12771277
// If we see: `struct Foo<T> where T: Copy { ... }`
1278-
let (fields, recovered) = self.parse_record_struct_body("struct")?;
1278+
let (fields, recovered) =
1279+
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
12791280
VariantData::Struct(fields, recovered)
12801281
}
12811282
// No `where` so: `struct Foo<T>;`
12821283
} else if self.eat(&token::Semi) {
12831284
VariantData::Unit(DUMMY_NODE_ID)
12841285
// Record-style struct definition
12851286
} else if self.token == token::OpenDelim(token::Brace) {
1286-
let (fields, recovered) = self.parse_record_struct_body("struct")?;
1287+
let (fields, recovered) =
1288+
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
12871289
VariantData::Struct(fields, recovered)
12881290
// Tuple-style struct definition with optional where-clause.
12891291
} else if self.token == token::OpenDelim(token::Paren) {
@@ -1313,10 +1315,12 @@ impl<'a> Parser<'a> {
13131315

13141316
let vdata = if self.token.is_keyword(kw::Where) {
13151317
generics.where_clause = self.parse_where_clause()?;
1316-
let (fields, recovered) = self.parse_record_struct_body("union")?;
1318+
let (fields, recovered) =
1319+
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
13171320
VariantData::Struct(fields, recovered)
13181321
} else if self.token == token::OpenDelim(token::Brace) {
1319-
let (fields, recovered) = self.parse_record_struct_body("union")?;
1322+
let (fields, recovered) =
1323+
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
13201324
VariantData::Struct(fields, recovered)
13211325
} else {
13221326
let token_str = super::token_descr(&self.token);
@@ -1332,6 +1336,7 @@ impl<'a> Parser<'a> {
13321336
fn parse_record_struct_body(
13331337
&mut self,
13341338
adt_ty: &str,
1339+
parsed_where: bool,
13351340
) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
13361341
let mut fields = Vec::new();
13371342
let mut recovered = false;
@@ -1353,9 +1358,19 @@ impl<'a> Parser<'a> {
13531358
self.eat(&token::CloseDelim(token::Brace));
13541359
} else {
13551360
let token_str = super::token_descr(&self.token);
1356-
let msg = &format!("expected `where`, or `{{` after struct name, found {}", token_str);
1361+
let msg = &format!(
1362+
"expected {}`{{` after struct name, found {}",
1363+
if parsed_where { "" } else { "`where`, or " },
1364+
token_str
1365+
);
13571366
let mut err = self.struct_span_err(self.token.span, msg);
1358-
err.span_label(self.token.span, "expected `where`, or `{` after struct name");
1367+
err.span_label(
1368+
self.token.span,
1369+
format!(
1370+
"expected {}`{{` after struct name",
1371+
if parsed_where { "" } else { "`where`, or " }
1372+
),
1373+
);
13591374
return Err(err);
13601375
}
13611376

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
struct A where T: Sized !
2+
//~^ ERROR expected `{` after struct name, found
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected `{` after struct name, found `!`
2+
--> $DIR/bad-struct-following-where.rs:1:25
3+
|
4+
LL | struct A where T: Sized !
5+
| ^ expected `{` after struct name
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct A where (): Sized where (): Sized {}
2+
//~^ ERROR cannot define duplicate `where` clauses on an item
3+
4+
fn b() where (): Sized where (): Sized {}
5+
//~^ ERROR cannot define duplicate `where` clauses on an item
6+
7+
enum C where (): Sized where (): Sized {}
8+
//~^ ERROR cannot define duplicate `where` clauses on an item
9+
10+
struct D where (): Sized, where (): Sized {}
11+
//~^ ERROR cannot define duplicate `where` clauses on an item
12+
13+
fn e() where (): Sized, where (): Sized {}
14+
//~^ ERROR cannot define duplicate `where` clauses on an item
15+
16+
enum F where (): Sized, where (): Sized {}
17+
//~^ ERROR cannot define duplicate `where` clauses on an item
18+
19+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
error: cannot define duplicate `where` clauses on an item
2+
--> $DIR/duplicate-where-clauses.rs:1:32
3+
|
4+
LL | struct A where (): Sized where (): Sized {}
5+
| - ^
6+
| |
7+
| previous `where` clause starts here
8+
|
9+
help: consider joining the two `where` clauses into one
10+
|
11+
LL | struct A where (): Sized, (): Sized {}
12+
| ~
13+
14+
error: cannot define duplicate `where` clauses on an item
15+
--> $DIR/duplicate-where-clauses.rs:4:30
16+
|
17+
LL | fn b() where (): Sized where (): Sized {}
18+
| - ^
19+
| |
20+
| previous `where` clause starts here
21+
|
22+
help: consider joining the two `where` clauses into one
23+
|
24+
LL | fn b() where (): Sized, (): Sized {}
25+
| ~
26+
27+
error: cannot define duplicate `where` clauses on an item
28+
--> $DIR/duplicate-where-clauses.rs:7:30
29+
|
30+
LL | enum C where (): Sized where (): Sized {}
31+
| - ^
32+
| |
33+
| previous `where` clause starts here
34+
|
35+
help: consider joining the two `where` clauses into one
36+
|
37+
LL | enum C where (): Sized, (): Sized {}
38+
| ~
39+
40+
error: cannot define duplicate `where` clauses on an item
41+
--> $DIR/duplicate-where-clauses.rs:10:33
42+
|
43+
LL | struct D where (): Sized, where (): Sized {}
44+
| - ^
45+
| |
46+
| previous `where` clause starts here
47+
|
48+
help: consider joining the two `where` clauses into one
49+
|
50+
LL | struct D where (): Sized, (): Sized {}
51+
| ~
52+
53+
error: cannot define duplicate `where` clauses on an item
54+
--> $DIR/duplicate-where-clauses.rs:13:31
55+
|
56+
LL | fn e() where (): Sized, where (): Sized {}
57+
| - ^
58+
| |
59+
| previous `where` clause starts here
60+
|
61+
help: consider joining the two `where` clauses into one
62+
|
63+
LL | fn e() where (): Sized, (): Sized {}
64+
| ~
65+
66+
error: cannot define duplicate `where` clauses on an item
67+
--> $DIR/duplicate-where-clauses.rs:16:31
68+
|
69+
LL | enum F where (): Sized, where (): Sized {}
70+
| - ^
71+
| |
72+
| previous `where` clause starts here
73+
|
74+
help: consider joining the two `where` clauses into one
75+
|
76+
LL | enum F where (): Sized, (): Sized {}
77+
| ~
78+
79+
error: aborting due to 6 previous errors
80+

0 commit comments

Comments
 (0)