diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 13b1a589d9ba9..8e63702b62f27 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2355,6 +2355,19 @@ pub enum SelfKind { Explicit(P, Mutability), } +impl SelfKind { + pub fn as_suggestion(&self) -> String { + match self { + SelfKind::Value(mutbl) => mutbl.prefix_str().to_string(), + SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(), + SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()), + SelfKind::Explicit(_, _) => { + unreachable!("if we had an explicit self, we wouldn't be here") + } + } + } +} + pub type ExplicitSelf = Spanned; impl Param { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 60cc138fd7bc2..2256449b1528c 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -329,6 +329,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 c72b7e2cfa727..b31b343060ede 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2974,3 +2974,22 @@ pub(crate) struct AsyncImpl { #[primary_span] pub span: Span, } + +#[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 79492fe62ed97..c43127d7b163c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2774,6 +2774,33 @@ 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.check_noexpect(&token::Colon) { + this.bump(); + 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.as_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 }); @@ -2787,6 +2814,7 @@ impl<'a> Parser<'a> { let eself_lo = self.token.span; let (eself, eself_ident, eself_hi) = match self.token.uninterpolate().kind { token::BinOp(token::And) => { + let lo = self.token.span; let eself = if is_isolated_self(self, 1) { // `&self` self.bump(); @@ -2811,7 +2839,9 @@ impl<'a> Parser<'a> { // `¬_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, 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 0000000000000..e3d85ab891b5b --- /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 0000000000000..c1ecd3b7fef43 --- /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 +