rust-lang/rust · #162103

fix(parser): emit targeted diagnostic for fields inside traits

raushan728 · merged Sep 23, 20264 files · 121 + / 0 −
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+