Rollup merge of #146305 - Kivooeo:a-lot-of-references-in-self, r=JonathanBrouwer

Add correct suggestion for multi-references for self type in method

Currently the suggestion for this code

```rust
fn main() {}

struct A {
    field: i32,
}

impl A {
    fn f(&&self) {}
}
```

looks like this, which is incorrect and missleading

```rust
   Compiling playground v0.0.1 (/playground)
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)`
 --> src/main.rs:8:16
  |
8 |     fn f(&&self) {}
  |                ^ expected one of 9 possible tokens
  |
  = note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
help: explicitly ignore the parameter name
  |
8 |     fn f(_: &&self) {}
  |          ++
```

So this fixes it and make more correct suggestions

```rust
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)`
 --> /home/gh-Kivooeo/test_/src/main.rs:8:16
  |
8 |     fn f(&&self) {}
  |                ^ expected one of 9 possible tokens
  |
help: `self` should be `self`, `&self` or `&mut self`, please remove extra references
  |
8 -     fn f(&&self) {}
8 +     fn f(&self) {}
```

Implementation is pretty self-documenting, but if you have suggestions on how to improve this (according to current test, which may be not fully covering all cases, this is works very well) or have some funny edge cases to show, I would appreciate it

r? compiler
This commit is contained in:
Stuart Cook 2025-11-09 13:22:23 +11:00 committed by GitHub
commit 0a7401148e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 158 additions and 4 deletions

View file

@ -714,6 +714,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;
@ -2302,6 +2301,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