Skip to content

Commit eff6598

Browse files
committed
suggest using a appropriate keyword for struct and enum
1 parent 559c019 commit eff6598

File tree

3 files changed

+134
-21
lines changed

3 files changed

+134
-21
lines changed

compiler/rustc_parse/src/parser/item.rs

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,13 @@ impl<'a> Parser<'a> {
273273
// TYPE ITEM
274274
self.parse_type_alias(def())?
275275
} else if self.eat_keyword(kw::Enum) {
276+
let start_span = self.prev_token.span;
276277
// ENUM ITEM
277-
self.parse_item_enum()?
278+
self.parse_item_enum(start_span)?
278279
} else if self.eat_keyword(kw::Struct) {
280+
let start_span = self.prev_token.span;
279281
// STRUCT ITEM
280-
self.parse_item_struct()?
282+
self.parse_item_struct(start_span)?
281283
} else if self.is_kw_followed_by_ident(kw::Union) {
282284
// UNION ITEM
283285
self.bump(); // `union`
@@ -1199,13 +1201,14 @@ impl<'a> Parser<'a> {
11991201
}
12001202

12011203
/// Parses an enum declaration.
1202-
fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
1204+
fn parse_item_enum(&mut self, start_span: Span) -> PResult<'a, ItemInfo> {
12031205
let id = self.parse_ident()?;
12041206
let mut generics = self.parse_generics()?;
12051207
generics.where_clause = self.parse_where_clause()?;
12061208

1207-
let (variants, _) =
1208-
self.parse_delim_comma_seq(token::Brace, |p| p.parse_enum_variant()).map_err(|e| {
1209+
let (variants, _) = self
1210+
.parse_delim_comma_seq(token::Brace, |p| p.parse_enum_variant(start_span))
1211+
.map_err(|e| {
12091212
self.recover_stmt();
12101213
e
12111214
})?;
@@ -1214,7 +1217,7 @@ impl<'a> Parser<'a> {
12141217
Ok((id, ItemKind::Enum(enum_definition, generics)))
12151218
}
12161219

1217-
fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
1220+
fn parse_enum_variant(&mut self, start_span: Span) -> PResult<'a, Option<Variant>> {
12181221
let variant_attrs = self.parse_outer_attributes()?;
12191222
self.collect_tokens_trailing_token(
12201223
variant_attrs,
@@ -1226,11 +1229,36 @@ impl<'a> Parser<'a> {
12261229
if !this.recover_nested_adt_item(kw::Enum)? {
12271230
return Ok((None, TrailingToken::None));
12281231
}
1232+
let enum_field_start_span = this.token.span;
12291233
let ident = this.parse_field_ident("enum", vlo)?;
1234+
if this.token.kind == token::Colon {
1235+
let snapshot = this.clone();
1236+
this.bump();
1237+
match this.parse_ty() {
1238+
Ok(_) => {
1239+
let mut err = this.struct_span_err(
1240+
enum_field_start_span.to(this.prev_token.span),
1241+
"the enum cannot have a struct field declaration",
1242+
);
1243+
err.span_suggestion_verbose(
1244+
start_span,
1245+
"consider using `struct` instead of `enum`",
1246+
"struct".to_string(),
1247+
Applicability::MaybeIncorrect,
1248+
);
1249+
return Err(err);
1250+
}
1251+
Err(e) => {
1252+
e.cancel();
1253+
*this = snapshot;
1254+
}
1255+
};
1256+
}
12301257

12311258
let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
12321259
// Parse a struct variant.
1233-
let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
1260+
let (fields, recovered) =
1261+
this.parse_record_struct_body("struct", false, None)?;
12341262
VariantData::Struct(fields, recovered)
12351263
} else if this.check(&token::OpenDelim(token::Paren)) {
12361264
VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
@@ -1258,7 +1286,7 @@ impl<'a> Parser<'a> {
12581286
}
12591287

12601288
/// Parses `struct Foo { ... }`.
1261-
fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
1289+
fn parse_item_struct(&mut self, start_span: Span) -> PResult<'a, ItemInfo> {
12621290
let class_name = self.parse_ident()?;
12631291

12641292
let mut generics = self.parse_generics()?;
@@ -1284,17 +1312,23 @@ impl<'a> Parser<'a> {
12841312
VariantData::Unit(DUMMY_NODE_ID)
12851313
} else {
12861314
// If we see: `struct Foo<T> where T: Copy { ... }`
1287-
let (fields, recovered) =
1288-
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
1315+
let (fields, recovered) = self.parse_record_struct_body(
1316+
"struct",
1317+
generics.where_clause.has_where_token,
1318+
Some(start_span),
1319+
)?;
12891320
VariantData::Struct(fields, recovered)
12901321
}
12911322
// No `where` so: `struct Foo<T>;`
12921323
} else if self.eat(&token::Semi) {
12931324
VariantData::Unit(DUMMY_NODE_ID)
12941325
// Record-style struct definition
12951326
} else if self.token == token::OpenDelim(token::Brace) {
1296-
let (fields, recovered) =
1297-
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
1327+
let (fields, recovered) = self.parse_record_struct_body(
1328+
"struct",
1329+
generics.where_clause.has_where_token,
1330+
Some(start_span),
1331+
)?;
12981332
VariantData::Struct(fields, recovered)
12991333
// Tuple-style struct definition with optional where-clause.
13001334
} else if self.token == token::OpenDelim(token::Paren) {
@@ -1323,12 +1357,18 @@ impl<'a> Parser<'a> {
13231357

13241358
let vdata = if self.token.is_keyword(kw::Where) {
13251359
generics.where_clause = self.parse_where_clause()?;
1326-
let (fields, recovered) =
1327-
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
1360+
let (fields, recovered) = self.parse_record_struct_body(
1361+
"union",
1362+
generics.where_clause.has_where_token,
1363+
None,
1364+
)?;
13281365
VariantData::Struct(fields, recovered)
13291366
} else if self.token == token::OpenDelim(token::Brace) {
1330-
let (fields, recovered) =
1331-
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
1367+
let (fields, recovered) = self.parse_record_struct_body(
1368+
"union",
1369+
generics.where_clause.has_where_token,
1370+
None,
1371+
)?;
13321372
VariantData::Struct(fields, recovered)
13331373
} else {
13341374
let token_str = super::token_descr(&self.token);
@@ -1345,12 +1385,13 @@ impl<'a> Parser<'a> {
13451385
&mut self,
13461386
adt_ty: &str,
13471387
parsed_where: bool,
1388+
start_span: Option<Span>,
13481389
) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
13491390
let mut fields = Vec::new();
13501391
let mut recovered = false;
13511392
if self.eat(&token::OpenDelim(token::Brace)) {
13521393
while self.token != token::CloseDelim(token::Brace) {
1353-
let field = self.parse_field_def(adt_ty).map_err(|e| {
1394+
let field = self.parse_field_def(adt_ty, start_span).map_err(|e| {
13541395
self.consume_block(token::Brace, ConsumeClosingDelim::No);
13551396
recovered = true;
13561397
e
@@ -1413,12 +1454,15 @@ impl<'a> Parser<'a> {
14131454
}
14141455

14151456
/// Parses an element of a struct declaration.
1416-
fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
1457+
fn parse_field_def(&mut self, adt_ty: &str, start_span: Option<Span>) -> PResult<'a, FieldDef> {
14171458
let attrs = self.parse_outer_attributes()?;
14181459
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
14191460
let lo = this.token.span;
14201461
let vis = this.parse_visibility(FollowedByType::No)?;
1421-
Ok((this.parse_single_struct_field(adt_ty, lo, vis, attrs)?, TrailingToken::None))
1462+
Ok((
1463+
this.parse_single_struct_field(adt_ty, lo, vis, attrs, start_span)?,
1464+
TrailingToken::None,
1465+
))
14221466
})
14231467
}
14241468

@@ -1429,9 +1473,10 @@ impl<'a> Parser<'a> {
14291473
lo: Span,
14301474
vis: Visibility,
14311475
attrs: Vec<Attribute>,
1476+
start_span: Option<Span>,
14321477
) -> PResult<'a, FieldDef> {
14331478
let mut seen_comma: bool = false;
1434-
let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?;
1479+
let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs, start_span)?;
14351480
if self.token == token::Comma {
14361481
seen_comma = true;
14371482
}
@@ -1555,9 +1600,32 @@ impl<'a> Parser<'a> {
15551600
lo: Span,
15561601
vis: Visibility,
15571602
attrs: Vec<Attribute>,
1603+
start_span: Option<Span>,
15581604
) -> PResult<'a, FieldDef> {
1605+
let prev_token_kind = self.prev_token.kind.clone();
15591606
let name = self.parse_field_ident(adt_ty, lo)?;
1560-
self.expect_field_ty_separator()?;
1607+
if let Err(mut error) = self.expect_field_ty_separator() {
1608+
if (matches!(vis.kind, VisibilityKind::Inherited)
1609+
&& (prev_token_kind == token::Comma
1610+
|| prev_token_kind == token::OpenDelim(token::Brace)))
1611+
|| !matches!(vis.kind, VisibilityKind::Inherited)
1612+
{
1613+
let snapshot = self.clone();
1614+
if let Err(err) = self.parse_ty() {
1615+
if let Some(span) = start_span {
1616+
error.span_suggestion_verbose(
1617+
span,
1618+
"consider using `enum` instead of `struct`",
1619+
"enum".to_string(),
1620+
Applicability::MaybeIncorrect,
1621+
);
1622+
}
1623+
err.cancel();
1624+
*self = snapshot;
1625+
}
1626+
}
1627+
return Err(error);
1628+
}
15611629
let ty = self.parse_ty()?;
15621630
if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) {
15631631
self.struct_span_err(self.token.span, "found single colon in a struct field type path")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub struct NotStruct {
2+
Variant
3+
} //~ ERROR expected `:`, found `}`
4+
5+
pub struct NotStruct {
6+
field String //~ ERROR expected `:`, found `String`
7+
}
8+
9+
pub enum NotEnum {
10+
field: u8 //~ ERROR the enum cannot have a struct field declaration
11+
}
12+
13+
fn main() {}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: expected `:`, found `}`
2+
--> $DIR/suggest-using-appropriate-keyword-for-struct-and-enum.rs:3:1
3+
|
4+
LL | Variant
5+
| - expected `:`
6+
LL | }
7+
| ^ unexpected token
8+
|
9+
help: consider using `enum` instead of `struct`
10+
|
11+
LL | pub enum NotStruct {
12+
| ~~~~
13+
14+
error: expected `:`, found `String`
15+
--> $DIR/suggest-using-appropriate-keyword-for-struct-and-enum.rs:6:11
16+
|
17+
LL | field String
18+
| ^^^^^^ expected `:`
19+
20+
error: the enum cannot have a struct field declaration
21+
--> $DIR/suggest-using-appropriate-keyword-for-struct-and-enum.rs:10:5
22+
|
23+
LL | field: u8
24+
| ^^^^^^^^^
25+
|
26+
help: consider using `struct` instead of `enum`
27+
|
28+
LL | pub struct NotEnum {
29+
| ~~~~~~
30+
31+
error: aborting due to 3 previous errors
32+

0 commit comments

Comments
 (0)