about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-03-08 00:02:11 +0000
committerMichael Goulet <michael@errs.io>2024-12-04 19:52:53 +0000
commit05c34cc5ed35dbf32f83e2eaa0ebbda4c4953294 (patch)
treec90ab2db52cef4b472db5291b58ed28038700053
parent96e51d9482405e400dec53750f3b263d45784ada (diff)
downloadrust-05c34cc5ed35dbf32f83e2eaa0ebbda4c4953294.tar.gz
rust-05c34cc5ed35dbf32f83e2eaa0ebbda4c4953294.zip
Fix suggestion when shorthand self has erroneous type
-rw-r--r--compiler/rustc_ast/src/ast.rs12
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs19
-rw-r--r--compiler/rustc_parse/src/parser/item.rs30
-rw-r--r--tests/ui/parser/typed-self-param.rs14
-rw-r--r--tests/ui/parser/typed-self-param.stderr50
6 files changed, 127 insertions, 1 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index ed2a3a507c8..2f55a9eaeda 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2566,6 +2566,18 @@ pub enum SelfKind {
     Explicit(P<Ty>, Mutability),
 }
 
+impl SelfKind {
+    pub fn to_ref_suggestion(&self) -> String {
+        match self {
+            SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
+            SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
+            SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
+                unreachable!("if we had an explicit self, we wouldn't be here")
+            }
+        }
+    }
+}
+
 pub type ExplicitSelf = Spanned<SelfKind>;
 
 impl Param {
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index b9a325eddd8..e5cd4622dae 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -343,6 +343,9 @@ parse_incorrect_semicolon =
     .suggestion = remove this semicolon
     .help = {$name} declarations are not followed by a semicolon
 
+parse_incorrect_type_on_self = type not allowed for shorthand `self` parameter
+    .suggestion = move the modifiers on `self` to the type
+
 parse_incorrect_use_of_await = incorrect use of `await`
     .parentheses_suggestion = `await` is not a method call, remove the parentheses
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 2579e4c1f25..14f2dd32e92 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3409,3 +3409,22 @@ pub(crate) struct PolarityAndModifiers {
     pub polarity: &'static str,
     pub modifiers_concatenated: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_incorrect_type_on_self)]
+pub(crate) struct IncorrectTypeOnSelf {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub move_self_modifier: MoveSelfModifier,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
+pub(crate) struct MoveSelfModifier {
+    #[suggestion_part(code = "")]
+    pub removal_span: Span,
+    #[suggestion_part(code = "{modifier}")]
+    pub insertion_span: Span,
+    pub modifier: String,
+}
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 26e81b7676b..475cd09147f 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2941,6 +2941,32 @@ impl<'a> Parser<'a> {
             };
             Ok((eself, eself_ident, eself_hi))
         };
+        let expect_self_ident_not_typed =
+            |this: &mut Self, modifier: &SelfKind, modifier_span: Span| {
+                let eself_ident = expect_self_ident(this);
+
+                // Recover `: Type` after a qualified self
+                if this.may_recover() && this.eat_noexpect(&token::Colon) {
+                    let snap = this.create_snapshot_for_diagnostic();
+                    match this.parse_ty() {
+                        Ok(ty) => {
+                            this.dcx().emit_err(errors::IncorrectTypeOnSelf {
+                                span: ty.span,
+                                move_self_modifier: errors::MoveSelfModifier {
+                                    removal_span: modifier_span,
+                                    insertion_span: ty.span.shrink_to_lo(),
+                                    modifier: modifier.to_ref_suggestion(),
+                                },
+                            });
+                        }
+                        Err(diag) => {
+                            diag.cancel();
+                            this.restore_snapshot(snap);
+                        }
+                    }
+                }
+                eself_ident
+            };
         // Recover for the grammar `*self`, `*const self`, and `*mut self`.
         let recover_self_ptr = |this: &mut Self| {
             this.dcx().emit_err(errors::SelfArgumentPointer { span: this.token.span });
@@ -2978,7 +3004,9 @@ impl<'a> Parser<'a> {
                     // `&not_self`
                     return Ok(None);
                 };
-                (eself, expect_self_ident(self), self.prev_token.span)
+                let hi = self.token.span;
+                let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi));
+                (eself, self_ident, hi)
             }
             // `*self`
             token::BinOp(token::Star) if is_isolated_self(self, 1) => {
diff --git a/tests/ui/parser/typed-self-param.rs b/tests/ui/parser/typed-self-param.rs
new file mode 100644
index 00000000000..e3d85ab891b
--- /dev/null
+++ b/tests/ui/parser/typed-self-param.rs
@@ -0,0 +1,14 @@
+struct S;
+
+impl S {
+    fn a(&self: Self) {}
+    //~^ ERROR type not allowed for shorthand `self` parameter
+    fn b(&mut self: Self) {}
+    //~^ ERROR type not allowed for shorthand `self` parameter
+    fn c<'c>(&'c mut self: Self) {}
+    //~^ ERROR type not allowed for shorthand `self` parameter
+    fn d<'d>(&'d self: Self) {}
+    //~^ ERROR type not allowed for shorthand `self` parameter
+}
+
+fn main() {}
diff --git a/tests/ui/parser/typed-self-param.stderr b/tests/ui/parser/typed-self-param.stderr
new file mode 100644
index 00000000000..c1ecd3b7fef
--- /dev/null
+++ b/tests/ui/parser/typed-self-param.stderr
@@ -0,0 +1,50 @@
+error: type not allowed for shorthand `self` parameter
+  --> $DIR/typed-self-param.rs:4:17
+   |
+LL |     fn a(&self: Self) {}
+   |                 ^^^^
+   |
+help: move the modifiers on `self` to the type
+   |
+LL -     fn a(&self: Self) {}
+LL +     fn a(self: &Self) {}
+   |
+
+error: type not allowed for shorthand `self` parameter
+  --> $DIR/typed-self-param.rs:6:21
+   |
+LL |     fn b(&mut self: Self) {}
+   |                     ^^^^
+   |
+help: move the modifiers on `self` to the type
+   |
+LL -     fn b(&mut self: Self) {}
+LL +     fn b(self: &mut Self) {}
+   |
+
+error: type not allowed for shorthand `self` parameter
+  --> $DIR/typed-self-param.rs:8:28
+   |
+LL |     fn c<'c>(&'c mut self: Self) {}
+   |                            ^^^^
+   |
+help: move the modifiers on `self` to the type
+   |
+LL -     fn c<'c>(&'c mut self: Self) {}
+LL +     fn c<'c>(self: &'c mut Self) {}
+   |
+
+error: type not allowed for shorthand `self` parameter
+  --> $DIR/typed-self-param.rs:10:24
+   |
+LL |     fn d<'d>(&'d self: Self) {}
+   |                        ^^^^
+   |
+help: move the modifiers on `self` to the type
+   |
+LL -     fn d<'d>(&'d self: Self) {}
+LL +     fn d<'d>(self: &'d Self) {}
+   |
+
+error: aborting due to 4 previous errors
+