From 7298174cd5905e73b60900110b8cd5e015e45d4f Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sun, 7 Sep 2025 17:07:25 +0000 Subject: [PATCH] add parser check for multi-reference self --- compiler/rustc_ast/src/ast.rs | 9 ++ .../rustc_parse/src/parser/diagnostics.rs | 39 ++++++++- tests/ui/self/lot-of-references-self.rs | 28 ++++++ tests/ui/self/lot-of-references-self.stderr | 86 +++++++++++++++++++ 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 tests/ui/self/lot-of-references-self.rs create mode 100644 tests/ui/self/lot-of-references-self.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 802a6fa324984..422fcabe41b08 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -715,6 +715,15 @@ impl Pat { } } + /// Strip off all reference patterns (`&`, `&mut`) and return the inner pattern. + pub fn peel_refs(&self) -> &Pat { + let mut current = self; + while let PatKind::Ref(inner, _) = ¤t.kind { + current = inner; + } + current + } + /// Is this a `..` pattern? pub fn is_rest(&self) -> bool { matches!(self.kind, PatKind::Rest) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a28af7833c387..300b543381dec 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2,13 +2,12 @@ use std::mem::take; use std::ops::{Deref, DerefMut}; use ast::token::IdentIsRaw; -use rustc_ast as ast; use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ - AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, - BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind, - Path, PathSegment, QSelf, Recovered, Ty, TyKind, + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, + Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, + PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -2290,6 +2289,38 @@ impl<'a> Parser<'a> { pat.span.shrink_to_hi(), pat.span.shrink_to_lo(), ), + PatKind::Ref(ref inner_pat, _) + // Fix suggestions for multi-reference `self` parameters (e.g. `&&&self`) + // cc: https://github.com/rust-lang/rust/pull/146305 + if let PatKind::Ref(_, _) = &inner_pat.kind + && let PatKind::Path(_, path) = &pat.peel_refs().kind + && let [a, ..] = path.segments.as_slice() + && a.ident.name == kw::SelfLower => + { + let mut inner = inner_pat; + let mut span_vec = vec![pat.span]; + + while let PatKind::Ref(ref inner_type, _) = inner.kind { + inner = inner_type; + span_vec.push(inner.span.shrink_to_lo()); + } + + let span = match span_vec.len() { + // Should be unreachable: match guard ensures at least 2 references + 0 | 1 => unreachable!(), + 2 => span_vec[0].until(inner_pat.span.shrink_to_lo()), + _ => span_vec[0].until(span_vec[span_vec.len() - 2].shrink_to_lo()), + }; + + err.span_suggestion_verbose( + span, + "`self` should be `self`, `&self` or `&mut self`, consider removing extra references", + "".to_string(), + Applicability::MachineApplicable, + ); + + return None; + } // Also catches `fn foo(&a)`. PatKind::Ref(ref inner_pat, mutab) if let PatKind::Ident(_, ident, _) = inner_pat.clone().kind => diff --git a/tests/ui/self/lot-of-references-self.rs b/tests/ui/self/lot-of-references-self.rs new file mode 100644 index 0000000000000..8f3e1a30616b0 --- /dev/null +++ b/tests/ui/self/lot-of-references-self.rs @@ -0,0 +1,28 @@ +struct A ; + +impl A { + fn a(&&self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references + fn b(&&&&&&self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references + fn c(&self) {} + fn d(&mut &self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references + fn e(&mut &&&self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references + fn f(&mut &mut &mut self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references + fn g(&mut & &mut self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references + fn h(&mut & & & && & & self) {} + //~^ ERROR expected one of + //~| HELP `self` should be `self`, `&self` or `&mut self`, consider removing extra references +} + +fn main() {} diff --git a/tests/ui/self/lot-of-references-self.stderr b/tests/ui/self/lot-of-references-self.stderr new file mode 100644 index 0000000000000..41a401eb13102 --- /dev/null +++ b/tests/ui/self/lot-of-references-self.stderr @@ -0,0 +1,86 @@ +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:4:16 + | +LL | fn a(&&self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn a(&&self) {} +LL + fn a(&self) {} + | + +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:7:20 + | +LL | fn b(&&&&&&self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn b(&&&&&&self) {} +LL + fn b(&self) {} + | + +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:11:20 + | +LL | fn d(&mut &self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn d(&mut &self) {} +LL + fn d(&self) {} + | + +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:14:22 + | +LL | fn e(&mut &&&self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn e(&mut &&&self) {} +LL + fn e(&self) {} + | + +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:17:29 + | +LL | fn f(&mut &mut &mut self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn f(&mut &mut &mut self) {} +LL + fn f(&mut self) {} + | + +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:20:26 + | +LL | fn g(&mut & &mut self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn g(&mut & &mut self) {} +LL + fn g(&mut self) {} + | + +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/lot-of-references-self.rs:23:39 + | +LL | fn h(&mut & & & && & & self) {} + | ^ expected one of 9 possible tokens + | +help: `self` should be `self`, `&self` or `&mut self`, consider removing extra references + | +LL - fn h(&mut & & & && & & self) {} +LL + fn h(& self) {} + | + +error: aborting due to 7 previous errors +