add parser check for multi-reference self

This commit is contained in:
Kivooeo 2025-09-07 17:07:25 +00:00
parent 1ed3cd7030
commit 7298174cd5
4 changed files with 158 additions and 4 deletions

View file

@ -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, _) = &current.kind {
current = inner;
}
current
}
/// Is this a `..` pattern?
pub fn is_rest(&self) -> bool {
matches!(self.kind, PatKind::Rest)

View file

@ -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 =>

View file

@ -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() {}

View file

@ -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