rust-lang/rust · #162103
fix(parser): emit targeted diagnostic for fields inside traits
compiler/rustc_parse/src/diagnostics.rs17 + / 0 −
@@ -2495,6 +2495,23 @@ pub(crate) struct AssociatedStaticItemNotAllowed { pub span: Span, } +#[derive(Subdiagnostic)]+pub(crate) enum FieldNotAllowedInTraitSugg {+ #[help("consider using a method instead: `fn {$ident}(&self) -> {$ty};`")]+ Method { ident: String, ty: String },+ #[note("`self` can only appear as a method receiver; consider `fn method(self: {$ty})`")]+ SelfReceiver { ty: String },+}++#[derive(Diagnostic)]+#[diag("fields are not allowed in trait definitions")]+pub(crate) struct FieldNotAllowedInTrait {+ #[primary_span]+ pub span: Span,+ #[subdiagnostic]+ pub sugg: FieldNotAllowedInTraitSugg,+}+ #[derive(Diagnostic)] #[diag("crate name using dashes are not valid in `extern crate` statements")] pub(crate) struct ExternCrateNameWithDashes {compiler/rustc_parse/src/parser/item.rs37 + / 0 −
@@ -404,6 +404,14 @@ impl<'a> Parser<'a> { Case::Insensitive, ); } else if macros_allowed && self.check_path() {+ // Detect `field_name: Type` or `self: Type` inside a trait body and emit+ // a clearer diagnostic before falling through to the macro invocation path.+ let is_field_in_trait = fn_parse_mode.context == FnContext::Trait+ && self.token.is_path_start()+ && self.look_ahead(1, |t| t.kind == token::Colon);+ if is_field_in_trait {+ return self.recover_field_in_trait();+ } if self.isnt_macro_invocation() { self.recover_missing_kw_before_item()?; }@@ -433,6 +441,35 @@ impl<'a> Parser<'a> { } } + fn recover_field_in_trait(&mut self) -> PResult<'a, Option<ItemKind>> {+ let ident_span = self.token.span;+ let ident_str = pprust::token_to_string(&self.token).into_owned();+ let is_self = self.token.is_keyword(kw::SelfLower);++ self.bump(); // identifier or `self`+ self.bump(); // `:`++ let ty_str = match self.parse_ty() {+ Ok(ty) => pprust::ty_to_string(&ty),+ Err(e) => {+ e.cancel();+ String::from("Type")+ }+ };+ // Eat the trailing separator so the parser doesn't trip over it.+ let _ = self.eat(exp!(Comma));+ let _ = self.eat(exp!(Semi));++ let span = ident_span.to(self.prev_token.span);+ let sugg = if is_self {+ diagnostics::FieldNotAllowedInTraitSugg::SelfReceiver { ty: ty_str }+ } else {+ diagnostics::FieldNotAllowedInTraitSugg::Method { ident: ident_str, ty: ty_str }+ };+ let err = self.dcx().create_err(diagnostics::FieldNotAllowedInTrait { span, sugg });+ Err(err)+ }+ fn parse_use_item(&mut self) -> PResult<'a, ItemKind> { let use_token_span = self.prev_token.span; let tree = self.parse_use_tree(use_token_span, None)?;tests/ui/parser/trait-item-field-colon-syntax.rsadded17 + / 0 −
@@ -0,0 +1,17 @@+fn main() {}++trait Trait1 {+ field_name: String, //~ ERROR fields are not allowed in trait definitions+}++trait Trait2 {+ self: String, //~ ERROR fields are not allowed in trait definitions+}++trait Trait3 {+ field_name: String //~ ERROR fields are not allowed in trait definitions+}++trait Trait4 {+ self: String //~ ERROR fields are not allowed in trait definitions+}tests/ui/parser/trait-item-field-colon-syntax.stderradded50 + / 0 −
@@ -0,0 +1,50 @@+error: fields are not allowed in trait definitions+ --> $DIR/trait-item-field-colon-syntax.rs:4:5+ |+LL | trait Trait1 {+ | - while parsing this item list starting here+LL | field_name: String,+ | ^^^^^^^^^^^^^^^^^^^+LL | }+ | - the item list ends here+ |+ = help: consider using a method instead: `fn field_name(&self) -> String;`++error: fields are not allowed in trait definitions+ --> $DIR/trait-item-field-colon-syntax.rs:8:5+ |+LL | trait Trait2 {+ | - while parsing this item list starting here+LL | self: String,+ | ^^^^^^^^^^^^^+LL | }+ | - the item list ends here+ |+ = note: `self` can only appear as a method receiver; consider `fn method(self: String)`++error: fields are not allowed in trait definitions+ --> $DIR/trait-item-field-colon-syntax.rs:12:5+ |+LL | trait Trait3 {+ | - while parsing this item list starting here+LL | field_name: String+ | ^^^^^^^^^^^^^^^^^^+LL | }+ | - the item list ends here+ |+ = help: consider using a method instead: `fn field_name(&self) -> String;`++error: fields are not allowed in trait definitions+ --> $DIR/trait-item-field-colon-syntax.rs:16:5+ |+LL | trait Trait4 {+ | - while parsing this item list starting here+LL | self: String+ | ^^^^^^^^^^^^+LL | }+ | - the item list ends here+ |+ = note: `self` can only appear as a method receiver; consider `fn method(self: String)`++error: aborting due to 4 previous errors+