Implement &pin patterns and ref pin bindings

This commit is contained in:
Frank King 2025-11-02 19:23:36 +08:00
parent f5ccdae676
commit 276053175d
20 changed files with 44 additions and 39 deletions

View file

@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
| PatKind::Err(_) => false,
PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Expr(_) => true,
}
}

View file

@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(
let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
// Checks if `pat` is a single reference to a binding (`&x`)
let is_ref_to_binding =
matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..)));
matches!(pat.kind, PatKind::Ref(inner, _, _) if matches!(inner.kind, PatKind::Binding(..)));
// If `pat` is not a binding or a reference to a binding (`x` or `&x`)
// we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) {

View file

@ -157,7 +157,7 @@ fn check_iter(
),
);
},
hir::PatKind::Ref(pat, _) => make_span_lint_and_sugg(
hir::PatKind::Ref(pat, _, _) => make_span_lint_and_sugg(
cx,
parent_expr_span,
format!(
@ -196,7 +196,7 @@ fn check_to_owned(
&& let filter_body = cx.tcx.hir_body(closure.body)
&& let [filter_params] = filter_body.params
&& msrv.meets(cx, msrvs::STRING_RETAIN)
&& let hir::PatKind::Ref(pat, _) = filter_params.pat.kind
&& let hir::PatKind::Ref(pat, _, _) = filter_params.pat.kind
{
make_span_lint_and_sugg(
cx,

View file

@ -78,7 +78,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool
.is_lang_item(cx, ResultErr)
== must_match_err
},
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => {
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) => {
is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err)
},
_ => false,

View file

@ -254,7 +254,7 @@ pub(super) fn try_parse_pattern<'tcx>(
) -> Option<OptionPat<'tcx>> {
match pat.kind {
PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
PatKind::Ref(pat, _, _) => f(cx, pat, ref_count + 1, ctxt),
PatKind::Expr(PatExpr {
kind: PatExprKind::Path(qpath),
hir_id,

View file

@ -50,7 +50,7 @@ where
}
let remaining_suggs = pats.filter_map(|pat| {
if let PatKind::Ref(refp, _) = pat.kind {
if let PatKind::Ref(refp, _, _) = pat.kind {
Some((pat.span, snippet(cx, refp.span, "..").to_string()))
} else {
None

View file

@ -264,7 +264,7 @@ impl<'a> NormalizedPat<'a> {
PatKind::Binding(.., Some(pat))
| PatKind::Box(pat)
| PatKind::Deref(pat)
| PatKind::Ref(pat, _)
| PatKind::Ref(pat, _, _)
| PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat),
PatKind::Never => Self::Never,
PatKind::Struct(ref path, fields, _) => {

View file

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv:
&& !pat_contains_disallowed_or(cx, arm.pat, msrv)
{
let pat_span = match (arm.pat.kind, binding.byref_ident) {
(PatKind::Ref(pat, _), Some(_)) => pat.span,
(PatKind::Ref(pat, _, _), Some(_)) => pat.span,
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
_ => arm.pat.span,
};
@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv:
&& !pat_contains_disallowed_or(cx, let_expr.pat, msrv)
{
let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
(PatKind::Ref(pat, _), Some(_)) => pat.span,
(PatKind::Ref(pat, _, _), Some(_)) => pat.span,
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
_ => let_expr.pat.span,
};

View file

@ -186,7 +186,7 @@ fn find_method_sugg_for_if_let<'tcx>(
// also look inside refs
// if we have &None for example, peel it so we can detect "if let None = x"
let check_pat = match let_pat.kind {
PatKind::Ref(inner, _mutability) => inner,
PatKind::Ref(inner, _pinnedness, _mutability) => inner,
_ => let_pat,
};
let op_ty = cx.typeck_results().expr_ty(let_expr);

View file

@ -373,7 +373,7 @@ impl<'a> PatState<'a> {
},
// Patterns for things which can only contain a single sub-pattern.
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
self.add_pat(cx, pat)
},
PatKind::Tuple([sub_pat], pos)

View file

@ -404,7 +404,7 @@ fn is_find_or_filter<'a>(
&& let filter_body = cx.tcx.hir_body(filter_body_id)
&& let [filter_param] = filter_body.params
// optional ref pattern: `filter(|&x| ..)`
&& let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
&& let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _, _) = filter_param.pat.kind {
(ref_pat, true)
} else {
(filter_param.pat, false)

View file

@ -50,7 +50,7 @@ fn is_method(
fn pat_is_recv(ident: Ident, param: &hir::Pat<'_>) -> bool {
match param.kind {
hir::PatKind::Binding(_, _, other, _) => ident == other,
hir::PatKind::Ref(pat, _) => pat_is_recv(ident, pat),
hir::PatKind::Ref(pat, _, _) => pat_is_recv(ident, pat),
_ => false,
}
}

View file

@ -81,7 +81,7 @@ pub(super) fn check<'tcx>(
}
match it.kind {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, _, Mutability::Mut) => {
to_be_discarded = true;
false
},

View file

@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, LangItem};
use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::mir::{Mutability, Pinnedness};
use rustc_middle::ty;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::symbol::Ident;
@ -50,7 +50,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
let closure_body = cx.tcx.hir_body(body);
let closure_expr = peel_blocks(closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, Mutability::Not) => {
hir::PatKind::Ref(inner, Pinnedness::Not, Mutability::Not) => {
if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind
&& ident_eq(name, closure_expr)
{

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind};
use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind, Pinnedness};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -37,7 +37,7 @@ declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) {
if let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind
if let PatKind::Ref(pat, Pinnedness::Not, Mutability::Not) = ref_pat.kind
&& !ref_pat.span.from_expansion()
&& cx
.tcx

View file

@ -7,7 +7,7 @@ use clippy_utils::msrvs::{self, MsrvStack};
use clippy_utils::over;
use rustc_ast::PatKind::*;
use rustc_ast::mut_visit::*;
use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind};
use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind, Pinnedness};
use rustc_ast_pretty::pprust;
use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
use rustc_errors::Applicability;
@ -151,8 +151,8 @@ fn insert_necessary_parens(pat: &mut Box<Pat>) {
walk_pat(self, pat);
let target = match &mut pat.kind {
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
Ref(p, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
Ident(.., Some(p)) | Box(p) | Ref(p, _, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
_ => return,
};
target.kind = Paren(Box::new(take_pat(target)));
@ -241,7 +241,8 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<Pat>, focus_idx: usize
// Skip immutable refs, as grouping them saves few characters,
// and almost always requires adding parens (increasing noisiness).
// In the case of only two patterns, replacement adds net characters.
| Ref(_, Mutability::Not)
// FIXME(pin_ergonomics): handle pinned patterns
| Ref(_, _, Mutability::Not)
// Dealt with elsewhere.
| Or(_) | Paren(_) | Deref(_) | Guard(..) => false,
// Transform `box x | ... | box y` into `box (x | y)`.
@ -254,10 +255,10 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<Pat>, focus_idx: usize
|k| always_pat!(k, Box(p) => *p),
),
// Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
Ref(target, Mutability::Mut) => extend_with_matching(
Ref(target, _, Mutability::Mut) => extend_with_matching(
target, start, alternatives,
|k| matches!(k, Ref(_, Mutability::Mut)),
|k| always_pat!(k, Ref(p, _) => *p),
|k| matches!(k, Ref(_, _, Mutability::Mut)),
|k| always_pat!(k, Ref(p, _, _) => *p),
),
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
Ident(b1, i1, Some(target)) => extend_with_matching(

View file

@ -793,9 +793,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Deref({pat})");
self.pat(pat);
},
PatKind::Ref(pat, muta) => {
PatKind::Ref(pat, pinn, muta) => {
bind!(self, pat);
kind!("Ref({pat}, Mutability::{muta:?})");
kind!("Ref({pat}, Pinning::{pinn:?}, Mutability::{muta:?})");
self.pat(pat);
},
PatKind::Guard(pat, cond) => {

View file

@ -45,9 +45,8 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
&& eq_expr_opt(lt.as_deref(), rt.as_deref())
&& eq_range_end(&le.node, &re.node)
},
(Box(l), Box(r))
| (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
(Box(l), Box(r)) => eq_pat(l, r),
(Ref(l, l_pin, l_mut), Ref(r, r_pin, r_mut)) => l_pin == r_pin && l_mut == r_mut && eq_pat(l, r),
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, eq_pat),
(Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {

View file

@ -8,9 +8,9 @@ use rustc_data_structures::fx::FxHasher;
use rustc_hir::MatchSource::TryDesugar;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind,
Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, ByRef, Closure, ConstArg, ConstArgKind, Expr,
ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
LifetimeKind, Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
};
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
@ -538,7 +538,7 @@ impl HirEqInterExpr<'_, '_, '_> {
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& (li == ri)
},
(PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re),
(PatKind::Ref(le, lp, lm), PatKind::Ref(re, rp, rm)) => lp == rp && lm == rm && self.eq_pat(le, re),
(PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => {
over(ls, rs, |l, r| self.eq_pat(l, r))
&& over(le, re, |l, r| self.eq_pat(l, r))
@ -1131,6 +1131,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
PatKind::Missing => unreachable!(),
PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
std::mem::discriminant(by_ref).hash(&mut self.s);
if let ByRef::Yes(pi, mu) = by_ref {
std::mem::discriminant(pi).hash(&mut self.s);
std::mem::discriminant(mu).hash(&mut self.s);
}
std::mem::discriminant(mutability).hash(&mut self.s);
if let Some(pat) = pat {
self.hash_pat(pat);
@ -1152,8 +1156,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
std::mem::discriminant(i).hash(&mut self.s);
},
PatKind::Ref(pat, mu) => {
PatKind::Ref(pat, pi, mu) => {
self.hash_pat(pat);
std::mem::discriminant(pi).hash(&mut self.s);
std::mem::discriminant(mu).hash(&mut self.s);
},
PatKind::Guard(pat, guard) => {

View file

@ -1462,7 +1462,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
PatKind::Missing => unreachable!(),
PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
PatKind::Expr(PatExpr {
kind: PatExprKind::Path(qpath),
hir_id,
@ -1612,7 +1612,7 @@ pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) ->
}
pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
while let PatKind::Ref(subpat, _) = pat.kind {
while let PatKind::Ref(subpat, _, _) = pat.kind {
pat = subpat;
}
pat
@ -2157,7 +2157,7 @@ where
/// references removed.
pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
if let PatKind::Ref(pat, _) = pat.kind {
if let PatKind::Ref(pat, _, _) = pat.kind {
peel(pat, count + 1)
} else {
(pat, count)