From 32624ccc9870870a9bec24627d444ced550faf60 Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Sun, 19 Jul 2026 19:58:51 +0800 Subject: [PATCH 01/11] Fix: `needless_borrow` do not contradict with `dangerous_implicit_autorefs` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Logic: If the innermost expression of the borrow is a deref of a raw pointer, and the borrow after multiple Index/Field operations, is used to call a method with `#[rustc_no_implicit_autorefs]`, skip the lint. Remove the narrow `&*raw_ptr` pattern check added in PR #14810. The old guard only matched the literal `&*raw_ptr`. Add Test `issue_17414` to `tests/ui/needless_borrow.rs` covering: - `(&(*tuple_slice).field).method()` — the reported case - `(&*raw_slice).len()` and `(&*raw_slice)[idx]` — direct deref - nested tuple field access through a raw pointer - array indexing via `Index::index` on a raw pointer - trait method dispatch (`dyn T`) through a raw pointer Problems Left: - Index Op of slice/array: `(*slice)[0]` can compile, but index method of Index trait has `#[rustc_no_implicit_autorefs]`. (See codes in tests/ui/needless_borrow.rs) - Is there any other operation between deref raw pointer and method with #[rustc_no_implicit_autorefs]? changelog: [`needless_borrow`]: do not contradict `dangerous_implicit_autorefs`. --- clippy_lints/src/dereference.rs | 78 +++++++++++++++++++++------ tests/ui/needless_borrow.fixed | 69 ++++++++++++++++++++++++ tests/ui/needless_borrow.rs | 69 ++++++++++++++++++++++++ tests/ui/needless_borrow.stderr | 94 +++++++++++++++++++++++---------- 4 files changed, 264 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index cd1ad57ee828..afc0400ef5de 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -5,18 +5,14 @@ use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{ adjust_derefs_manually_drop, get_adt_inherent_method, implements_trait, is_manually_drop, peel_and_count_ty_refs, }; -use clippy_utils::{ - DefinedTy, ExprUseNode, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, -}; +use clippy_utils::{expr_use_sites, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, DefinedTy, ExprUseNode}; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; +use rustc_hir::attrs::{AttributeKind, HasAttrs as _}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt as _, walk_ty}; -use rustc_hir::{ - self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Item, MatchSource, Mutability, - Node, OwnerId, Pat, PatKind, Path, QPath, TyKind, UnOp, -}; +use rustc_hir::intravisit::{walk_ty, InferKind, Visitor, VisitorExt as _}; +use rustc_hir::{self as hir, AmbigArg, Attribute, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Item, MatchSource, Mutability, Node, OwnerId, Pat, PatKind, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeVisitableExt as _, TypeckResults, Unnormalized}; @@ -275,6 +271,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } + if is_ref_from_no_implicit_raw_pointer(cx, typeck, expr, sub_expr) { + return; + } + match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); @@ -732,6 +732,59 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } } +fn is_ref_from_no_implicit_raw_pointer<'tcx>(cx: &LateContext<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + expr: &'tcx Expr<'tcx>, + sub_expr: &Expr<'tcx>) + -> bool { + let mut temp_e = sub_expr; + + typeck + .expr_ty(loop { + match temp_e.kind { + ExprKind::Index(e, _, _) | + ExprKind::Field(e, _) | + ExprKind::AddrOf(_, _, e) => temp_e = e, + ExprKind::Unary(UnOp::Deref, e) => break e, + _ => break temp_e, + } + }) + .is_raw_ptr() + && + find_no_auto_method(cx, typeck, expr) + .is_some_and(|fn_id| fn_id + .get_attrs(&cx.tcx) // can not use private micro find_attr!() + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::RustcNoImplicitAutorefs)))) +} + +fn find_no_auto_method<'tcx>(cx: &LateContext<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut expr: &'tcx Expr<'tcx>) + -> Option { + while let Some(use_site) = + expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr) + .next() + && let node = use_site.node + { + match node { + Node::Expr(use_expr) => { + match use_expr.kind { + ExprKind::Field(_, _) | ExprKind::Index(_, _, _) => { + expr = use_expr; + } + ExprKind::MethodCall(_, _, _, _) => { + return typeck.type_dependent_def_id(use_expr.hir_id) + } + _ => return None, + } + } + _ => return None, + } + }; + None +} + fn is_deref_or_derefmut_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let hir::ItemKind::Impl(impl_) = item.kind && let Some(of_trait) = impl_.of_trait @@ -1153,15 +1206,6 @@ impl<'tcx> Dereferencing<'tcx> { ); }, State::DerefedBorrow(state) => { - // Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context, - // as this may make rustc trigger its `dangerous_implicit_autorefs` lint. - if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind - && let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind - && cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr() - { - return; - } - let mut app = Applicability::MachineApplicable; let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index fd17fbb7c093..e93a6710fba1 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -5,6 +5,8 @@ clippy::unnecessary_mut_passed )] +use std::ops::Index; + fn main() { let a = 5; let ref_a = &a; @@ -294,3 +296,70 @@ fn issue_14743(slice: &[T]) { #[expect(dangerous_implicit_autorefs)] let _ = unsafe { (*slice).len() }; } + +fn issue_17414(slice: &(i32, (i32, [usize])), + tuple_slice: *const (i32, [usize]), + nested_tuple: *const (i32, (i32, [usize])) +) { + let _ = slice.1.1.len(); + //~^ needless_borrow + let _ = slice.1.1.len(); + //~^ needless_borrow + let _ = slice.1.1.len(); + //~^ needless_borrow + + unsafe { + // Rule: `rustc_lint:autoref.rs` require explicit ref to arg from a [inner] derefed raw pointer + // when calling a #[rustc_no_implicit_autorefs] method. + + // 1. Issue 17414 case: deref tuple, get slice field, call [].len() method. + let _ = (&(*tuple_slice).1).len(); // necessary borrow, no lint + + // 2. simple slice + let a = [0, 1, 2]; + let b = &raw const a[0..2]; // same with raw const or raw mut + let _ = (&*b).len(); // necessary + let _ = (*b)[0]; // ok + let _ = (&*b)[0]; // TODO needless, should lint + + // 3. nested tuple + let _ = (&*nested_tuple).1.1.len(); // necessary + let _ = (&(*nested_tuple).1).1.len(); // necessary + let _ = (&(*nested_tuple).1.1).len(); // necessary + + let _ = (*nested_tuple).1.1.first(); // ok + let _ = (*nested_tuple).1.1.first(); // needless + //~^ needless_borrow + let _ = (*nested_tuple).1.1.first(); // needless + //~^ needless_borrow + let _ = (*nested_tuple).1.1.first(); // needless + //~^ needless_borrow + + // 4. array + let a = [1, 2, 3]; + let b: *const [i32; 3] = &raw const a; + // let _ = (*b).index(0); // fail + let _ = (&*b).index(0); // necessary + + // 5. trait + trait T: Index {} + struct S {} + impl Index for S { + type Output = i32; + // no attr, defined in super.index + fn index(&self, _: usize) -> &i32 { + &42 + } + } + impl T for S {} + + let s = S {}; + let t: &dyn T = &s; + let ptr: *const dyn T = t; + + // let _ = (*ptr)[0]; // fail + let _ = (&*ptr)[0]; // necessary + // let _ = (*ptr).index(0); // fail + let _ = (&*ptr).index(0); // necessary + } +} \ No newline at end of file diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index a96252bcb3be..580b4dd2864b 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -5,6 +5,8 @@ clippy::unnecessary_mut_passed )] +use std::ops::Index; + fn main() { let a = 5; let ref_a = &a; @@ -294,3 +296,70 @@ fn issue_14743(slice: &[T]) { #[expect(dangerous_implicit_autorefs)] let _ = unsafe { (*slice).len() }; } + +fn issue_17414(slice: &(i32, (i32, [usize])), + tuple_slice: *const (i32, [usize]), + nested_tuple: *const (i32, (i32, [usize])) +) { + let _ = (&slice).1.1.len(); + //~^ needless_borrow + let _ = (&slice.1).1.len(); + //~^ needless_borrow + let _ = (&slice.1.1).len(); + //~^ needless_borrow + + unsafe { + // Rule: `rustc_lint:autoref.rs` require explicit ref to arg from a [inner] derefed raw pointer + // when calling a #[rustc_no_implicit_autorefs] method. + + // 1. Issue 17414 case: deref tuple, get slice field, call [].len() method. + let _ = (&(*tuple_slice).1).len(); // necessary borrow, no lint + + // 2. simple slice + let a = [0, 1, 2]; + let b = &raw const a[0..2]; // same with raw const or raw mut + let _ = (&*b).len(); // necessary + let _ = (*b)[0]; // ok + let _ = (&*b)[0]; // TODO needless, should lint + + // 3. nested tuple + let _ = (&*nested_tuple).1.1.len(); // necessary + let _ = (&(*nested_tuple).1).1.len(); // necessary + let _ = (&(*nested_tuple).1.1).len(); // necessary + + let _ = (*nested_tuple).1.1.first(); // ok + let _ = (&*nested_tuple).1.1.first(); // needless + //~^ needless_borrow + let _ = (&(*nested_tuple).1).1.first(); // needless + //~^ needless_borrow + let _ = (&(*nested_tuple).1.1).first(); // needless + //~^ needless_borrow + + // 4. array + let a = [1, 2, 3]; + let b: *const [i32; 3] = &raw const a; + // let _ = (*b).index(0); // fail + let _ = (&*b).index(0); // necessary + + // 5. trait + trait T: Index {} + struct S {} + impl Index for S { + type Output = i32; + // no attr, defined in super.index + fn index(&self, _: usize) -> &i32 { + &42 + } + } + impl T for S {} + + let s = S {}; + let t: &dyn T = &s; + let ptr: *const dyn T = t; + + // let _ = (*ptr)[0]; // fail + let _ = (&*ptr)[0]; // necessary + // let _ = (*ptr).index(0); // fail + let _ = (&*ptr).index(0); // necessary + } +} \ No newline at end of file diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index e7a488b9ed92..790b762b0e4d 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:12:15 + --> tests/ui/needless_borrow.rs:14:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -8,166 +8,202 @@ LL | let _ = x(&&a); // warn = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:18:13 + --> tests/ui/needless_borrow.rs:20:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:32:13 + --> tests/ui/needless_borrow.rs:34:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:35:15 + --> tests/ui/needless_borrow.rs:37:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:42:27 + --> tests/ui/needless_borrow.rs:44:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:50:15 + --> tests/ui/needless_borrow.rs:52:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:52:15 + --> tests/ui/needless_borrow.rs:54:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:54:15 + --> tests/ui/needless_borrow.rs:56:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:56:15 + --> tests/ui/needless_borrow.rs:58:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:60:11 + --> tests/ui/needless_borrow.rs:62:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:68:13 + --> tests/ui/needless_borrow.rs:70:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:70:13 + --> tests/ui/needless_borrow.rs:72:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:72:23 + --> tests/ui/needless_borrow.rs:74:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:74:23 + --> tests/ui/needless_borrow.rs:76:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:84:14 + --> tests/ui/needless_borrow.rs:86:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:91:14 + --> tests/ui/needless_borrow.rs:93:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:104:13 + --> tests/ui/needless_borrow.rs:106:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:115:5 + --> tests/ui/needless_borrow.rs:117:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:125:5 + --> tests/ui/needless_borrow.rs:127:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:152:23 + --> tests/ui/needless_borrow.rs:154:23 | LL | let x: (&str,) = (&"",); | ^^^ help: change this to: `""` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:195:13 + --> tests/ui/needless_borrow.rs:197:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:205:13 + --> tests/ui/needless_borrow.rs:207:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:243:22 + --> tests/ui/needless_borrow.rs:245:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:251:22 + --> tests/ui/needless_borrow.rs:253:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:256:22 + --> tests/ui/needless_borrow.rs:258:22 | LL | let _ = &mut (&mut x.u).x; | ^^^^^^^^^^ help: change this to: `x.u` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:258:22 + --> tests/ui/needless_borrow.rs:260:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:280:23 + --> tests/ui/needless_borrow.rs:282:23 | LL | option.unwrap_or((&x.0,)); | ^^^^ help: change this to: `x.0` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:287:13 + --> tests/ui/needless_borrow.rs:289:13 | LL | let _ = (&slice).len(); | ^^^^^^^^ help: change this to: `slice` -error: aborting due to 28 previous errors +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_borrow.rs:304:13 + | +LL | let _ = (&slice).1.1.len(); + | ^^^^^^^^ help: change this to: `slice` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:306:13 + | +LL | let _ = (&slice.1).1.len(); + | ^^^^^^^^^^ help: change this to: `slice.1` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:308:13 + | +LL | let _ = (&slice.1.1).len(); + | ^^^^^^^^^^^^ help: change this to: `slice.1.1` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:331:17 + | +LL | let _ = (&*nested_tuple).1.1.first(); // needless + | ^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple)` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:333:17 + | +LL | let _ = (&(*nested_tuple).1).1.first(); // needless + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple).1` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:335:17 + | +LL | let _ = (&(*nested_tuple).1.1).first(); // needless + | ^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple).1.1` + +error: aborting due to 34 previous errors From b9b401a5e03871ed904a2e39ebb470b493bba7da Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Sun, 19 Jul 2026 21:14:10 +0800 Subject: [PATCH 02/11] Fix: `needless_borrow` do not contradict with `dangerous_implicit_autorefs` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Logic: If the innermost expression of the borrow is a deref of a raw pointer, and the borrow after multiple Index/Field operations, is used to call a method with `#[rustc_no_implicit_autorefs]`, skip the lint. Remove the narrow `&*raw_ptr` pattern check added in PR #14810. The old guard only matched the literal `&*raw_ptr`. Add Test `issue_17414` to `tests/ui/needless_borrow.rs` covering: - `(&(*tuple_slice).field).method()` — the reported case - `(&*raw_slice).len()` and `(&*raw_slice)[idx]` — direct deref - nested tuple field access through a raw pointer - array indexing via `Index::index` on a raw pointer - trait method dispatch (`dyn T`) through a raw pointer Problems Left: - Index Op of slice/array: `(*slice)[0]` can compile, but index method of Index trait has `#[rustc_no_implicit_autorefs]`. (See codes in tests/ui/needless_borrow.rs) - Is there any other operation between deref raw pointer and method with #[rustc_no_implicit_autorefs]? changelog: [`needless_borrow`]: do not contradict `dangerous_implicit_autorefs`. --- clippy_lints/src/dereference.rs | 68 ++++++++++++++++----------------- tests/ui/needless_borrow.rs | 12 ++++-- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index afc0400ef5de..46994285d2d7 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -5,14 +5,20 @@ use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{ adjust_derefs_manually_drop, get_adt_inherent_method, implements_trait, is_manually_drop, peel_and_count_ty_refs, }; -use clippy_utils::{expr_use_sites, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, DefinedTy, ExprUseNode}; +use clippy_utils::{ + DefinedTy, ExprUseNode, expr_use_sites, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, + is_lint_allowed, sym, +}; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::attrs::{AttributeKind, HasAttrs as _}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_ty, InferKind, Visitor, VisitorExt as _}; -use rustc_hir::{self as hir, AmbigArg, Attribute, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Item, MatchSource, Mutability, Node, OwnerId, Pat, PatKind, Path, QPath, TyKind, UnOp}; +use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt as _, walk_ty}; +use rustc_hir::{ + self as hir, AmbigArg, Attribute, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Item, MatchSource, + Mutability, Node, OwnerId, Pat, PatKind, Path, QPath, TyKind, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeVisitableExt as _, TypeckResults, Unnormalized}; @@ -732,56 +738,50 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } } -fn is_ref_from_no_implicit_raw_pointer<'tcx>(cx: &LateContext<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, - expr: &'tcx Expr<'tcx>, - sub_expr: &Expr<'tcx>) - -> bool { +fn is_ref_from_no_implicit_raw_pointer<'tcx>( + cx: &LateContext<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + expr: &'tcx Expr<'tcx>, + sub_expr: &Expr<'tcx>, +) -> bool { let mut temp_e = sub_expr; typeck .expr_ty(loop { match temp_e.kind { - ExprKind::Index(e, _, _) | - ExprKind::Field(e, _) | - ExprKind::AddrOf(_, _, e) => temp_e = e, + ExprKind::Index(e, _, _) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => temp_e = e, ExprKind::Unary(UnOp::Deref, e) => break e, _ => break temp_e, } }) .is_raw_ptr() - && - find_no_auto_method(cx, typeck, expr) - .is_some_and(|fn_id| fn_id + && find_no_auto_method(cx, typeck, expr).is_some_and(|fn_id| { + fn_id .get_attrs(&cx.tcx) // can not use private micro find_attr!() .iter() - .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::RustcNoImplicitAutorefs)))) + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::RustcNoImplicitAutorefs))) + }) } -fn find_no_auto_method<'tcx>(cx: &LateContext<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, - mut expr: &'tcx Expr<'tcx>) - -> Option { - while let Some(use_site) = - expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr) - .next() +fn find_no_auto_method<'tcx>( + cx: &LateContext<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut expr: &'tcx Expr<'tcx>, +) -> Option { + while let Some(use_site) = expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr).next() && let node = use_site.node { match node { - Node::Expr(use_expr) => { - match use_expr.kind { - ExprKind::Field(_, _) | ExprKind::Index(_, _, _) => { - expr = use_expr; - } - ExprKind::MethodCall(_, _, _, _) => { - return typeck.type_dependent_def_id(use_expr.hir_id) - } - _ => return None, - } - } + Node::Expr(use_expr) => match use_expr.kind { + ExprKind::Field(_, _) | ExprKind::Index(_, _, _) => { + expr = use_expr; + }, + ExprKind::MethodCall(_, _, _, _) => return typeck.type_dependent_def_id(use_expr.hir_id), + _ => return None, + }, _ => return None, } - }; + } None } diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 580b4dd2864b..b04245bf1268 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -297,9 +297,10 @@ fn issue_14743(slice: &[T]) { let _ = unsafe { (*slice).len() }; } -fn issue_17414(slice: &(i32, (i32, [usize])), - tuple_slice: *const (i32, [usize]), - nested_tuple: *const (i32, (i32, [usize])) +fn issue_17414( + slice: &(i32, (i32, [usize])), + tuple_slice: *const (i32, [usize]), + nested_tuple: *const (i32, (i32, [usize])), ) { let _ = (&slice).1.1.len(); //~^ needless_borrow @@ -329,10 +330,13 @@ fn issue_17414(slice: &(i32, (i32, [usize])), let _ = (*nested_tuple).1.1.first(); // ok let _ = (&*nested_tuple).1.1.first(); // needless + // //~^ needless_borrow let _ = (&(*nested_tuple).1).1.first(); // needless + // //~^ needless_borrow let _ = (&(*nested_tuple).1.1).first(); // needless + // //~^ needless_borrow // 4. array @@ -362,4 +366,4 @@ fn issue_17414(slice: &(i32, (i32, [usize])), // let _ = (*ptr).index(0); // fail let _ = (&*ptr).index(0); // necessary } -} \ No newline at end of file +} From ad3781a36d1672abee60e897c501bdd8a17ed940 Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Sun, 19 Jul 2026 21:23:14 +0800 Subject: [PATCH 03/11] Fix: `needless_borrow` do not contradict with `dangerous_implicit_autorefs` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Logic: If the innermost expression of the borrow is a deref of a raw pointer, and the borrow after multiple Index/Field operations, is used to call a method with `#[rustc_no_implicit_autorefs]`, skip the lint. Remove the narrow `&*raw_ptr` pattern check added in PR14810. The old guard only matched the literal `&*raw_ptr`. Add Test `issue_17414` to `tests/ui/needless_borrow.rs` covering: - `(&(*tuple_slice).field).method()` — the reported case - `(&*raw_slice).len()` and `(&*raw_slice)[idx]` — direct deref - nested tuple field access through a raw pointer - array indexing via `Index::index` on a raw pointer - trait method dispatch (`dyn T`) through a raw pointer Problems Left: - Index Op of slice/array: `(*slice)[0]` can compile, but index method of Index trait has `#[rustc_no_implicit_autorefs]`. (See codes in tests/ui/needless_borrow.rs) - Is there any other operation between deref raw pointer and method with #[rustc_no_implicit_autorefs]? changelog: [`needless_borrow`]: do not contradict `dangerous_implicit_autorefs`. --- tests/ui/needless_borrow.fixed | 18 +++++++++++------- tests/ui/needless_borrow.rs | 12 ++++++------ tests/ui/needless_borrow.stderr | 18 +++++++++--------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index e93a6710fba1..8c6c268912db 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -297,9 +297,10 @@ fn issue_14743(slice: &[T]) { let _ = unsafe { (*slice).len() }; } -fn issue_17414(slice: &(i32, (i32, [usize])), - tuple_slice: *const (i32, [usize]), - nested_tuple: *const (i32, (i32, [usize])) +fn issue_17414( + slice: &(i32, (i32, [usize])), + tuple_slice: *const (i32, [usize]), + nested_tuple: *const (i32, (i32, [usize])), ) { let _ = slice.1.1.len(); //~^ needless_borrow @@ -328,11 +329,14 @@ fn issue_17414(slice: &(i32, (i32, [usize])), let _ = (&(*nested_tuple).1.1).len(); // necessary let _ = (*nested_tuple).1.1.first(); // ok - let _ = (*nested_tuple).1.1.first(); // needless + + let _ = (*nested_tuple).1.1.first(); //~^ needless_borrow - let _ = (*nested_tuple).1.1.first(); // needless + + let _ = (*nested_tuple).1.1.first(); //~^ needless_borrow - let _ = (*nested_tuple).1.1.first(); // needless + + let _ = (*nested_tuple).1.1.first(); //~^ needless_borrow // 4. array @@ -362,4 +366,4 @@ fn issue_17414(slice: &(i32, (i32, [usize])), // let _ = (*ptr).index(0); // fail let _ = (&*ptr).index(0); // necessary } -} \ No newline at end of file +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index b04245bf1268..0a6246e70eec 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -329,14 +329,14 @@ fn issue_17414( let _ = (&(*nested_tuple).1.1).len(); // necessary let _ = (*nested_tuple).1.1.first(); // ok - let _ = (&*nested_tuple).1.1.first(); // needless - // + + let _ = (&*nested_tuple).1.1.first(); //~^ needless_borrow - let _ = (&(*nested_tuple).1).1.first(); // needless - // + + let _ = (&(*nested_tuple).1).1.first(); //~^ needless_borrow - let _ = (&(*nested_tuple).1.1).first(); // needless - // + + let _ = (&(*nested_tuple).1.1).first(); //~^ needless_borrow // 4. array diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 790b762b0e4d..6807512b4e69 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -170,39 +170,39 @@ LL | let _ = (&slice).len(); | ^^^^^^^^ help: change this to: `slice` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:304:13 + --> tests/ui/needless_borrow.rs:305:13 | LL | let _ = (&slice).1.1.len(); | ^^^^^^^^ help: change this to: `slice` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:306:13 + --> tests/ui/needless_borrow.rs:307:13 | LL | let _ = (&slice.1).1.len(); | ^^^^^^^^^^ help: change this to: `slice.1` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:308:13 + --> tests/ui/needless_borrow.rs:309:13 | LL | let _ = (&slice.1.1).len(); | ^^^^^^^^^^^^ help: change this to: `slice.1.1` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:331:17 + --> tests/ui/needless_borrow.rs:333:17 | -LL | let _ = (&*nested_tuple).1.1.first(); // needless +LL | let _ = (&*nested_tuple).1.1.first(); | ^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:333:17 + --> tests/ui/needless_borrow.rs:336:17 | -LL | let _ = (&(*nested_tuple).1).1.first(); // needless +LL | let _ = (&(*nested_tuple).1).1.first(); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple).1` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:335:17 + --> tests/ui/needless_borrow.rs:339:17 | -LL | let _ = (&(*nested_tuple).1.1).first(); // needless +LL | let _ = (&(*nested_tuple).1.1).first(); | ^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple).1.1` error: aborting due to 34 previous errors From 20c5fcc3780bf0aaac41dd36a3f282076cb41dce Mon Sep 17 00:00:00 2001 From: skiefucker <67737202+skiefucker@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:07:23 +0800 Subject: [PATCH 04/11] Apply suggestions from code review needless variable Co-authored-by: Poliorcetics --- clippy_lints/src/dereference.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 46994285d2d7..831c8a695a9e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -769,9 +769,8 @@ fn find_no_auto_method<'tcx>( mut expr: &'tcx Expr<'tcx>, ) -> Option { while let Some(use_site) = expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr).next() - && let node = use_site.node { - match node { + match use_site.node { Node::Expr(use_expr) => match use_expr.kind { ExprKind::Field(_, _) | ExprKind::Index(_, _, _) => { expr = use_expr; From 5ab6541b87c4b6f7c55df6402ddc08b65fb1cb19 Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Tue, 21 Jul 2026 10:20:40 +0800 Subject: [PATCH 05/11] fmt: run code format One of Fix: `needless_borrow` do not contradict with `dangerous_implicit_autorefs` --- clippy_lints/src/dereference.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 831c8a695a9e..2f0b25fe0ca9 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -768,8 +768,7 @@ fn find_no_auto_method<'tcx>( typeck: &'tcx TypeckResults<'tcx>, mut expr: &'tcx Expr<'tcx>, ) -> Option { - while let Some(use_site) = expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr).next() - { + while let Some(use_site) = expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr).next() { match use_site.node { Node::Expr(use_expr) => match use_expr.kind { ExprKind::Field(_, _) | ExprKind::Index(_, _, _) => { From 888897fc3eddaf7e526c31c5560a6111ac9281f8 Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Wed, 22 Jul 2026 13:04:21 +0800 Subject: [PATCH 06/11] needless_borrow: check whether borrow of index operation is necessary. 1. add needless_borrow lint to a normal index operation. 2. skip this check for raw pointer because explicit borrow is necessary. --- clippy_lints/src/dereference.rs | 102 ++++++++++++++++++++++---------- clippy_utils/src/lib.rs | 4 +- tests/ui/needless_borrow.fixed | 15 +++-- tests/ui/needless_borrow.rs | 15 +++-- tests/ui/needless_borrow.stderr | 20 +++++-- 5 files changed, 109 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 2f0b25fe0ca9..0d5c899860ec 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -20,7 +20,7 @@ use rustc_hir::{ Mutability, Node, OwnerId, Pat, PatKind, Path, QPath, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind}; use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeVisitableExt as _, TypeckResults, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol, SyntaxContext}; @@ -277,7 +277,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if is_ref_from_no_implicit_raw_pointer(cx, typeck, expr, sub_expr) { + if need_explicit_ref(cx, typeck, expr) { return; } @@ -374,7 +374,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // deref through `ManuallyDrop<_>` will not compile. !adjust_derefs_manually_drop(use_site.adjustments, expr_ty) }, - ExprUseNode::Callee | ExprUseNode::FieldAccess(_) if !use_site.moved_before_use => true, + ExprUseNode::Callee | ExprUseNode::FieldAccess(_) | ExprUseNode::Index + if !use_site.moved_before_use => + { + true + }, ExprUseNode::MethodArg(hir_id, _, 0) if !use_site.moved_before_use => { // Check for calls to trait methods where auto-borrow will not resolve. // Three cases need to be handled: @@ -738,49 +742,83 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } } -fn is_ref_from_no_implicit_raw_pointer<'tcx>( - cx: &LateContext<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, - expr: &'tcx Expr<'tcx>, - sub_expr: &Expr<'tcx>, -) -> bool { - let mut temp_e = sub_expr; - - typeck - .expr_ty(loop { - match temp_e.kind { - ExprKind::Index(e, _, _) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => temp_e = e, - ExprKind::Unary(UnOp::Deref, e) => break e, - _ => break temp_e, - } - }) - .is_raw_ptr() - && find_no_auto_method(cx, typeck, expr).is_some_and(|fn_id| { - fn_id - .get_attrs(&cx.tcx) // can not use private micro find_attr!() - .iter() - .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::RustcNoImplicitAutorefs))) - }) +fn need_explicit_ref<'tcx>(cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + match expr.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, mut sub_expr) => { + typeck + .expr_ty(loop { + match sub_expr.kind { + ExprKind::Index(e, _, _) | ExprKind::Field(e, _) => sub_expr = e, + ExprKind::Unary(UnOp::Deref, e) => break e, + _ => break sub_expr, + } + }) + .is_raw_ptr() + && check_invoke_chain_danger(cx, typeck, expr) + }, + _ => false, + } } -fn find_no_auto_method<'tcx>( +fn check_invoke_chain_danger<'tcx>( cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, mut expr: &'tcx Expr<'tcx>, -) -> Option { +) -> bool { + let adjs_table = typeck.adjustments(); + while let Some(use_site) = expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr).next() { + let auto_ref = adjs_table + .get(expr.hir_id) + .map(|adjs| peel_derefs_adjustments(adjs)) + .and_then(|adjs| if adjs.len() == 1 { adjs.first() } else { None }) + .map(|adj| { + matches!( + adj.kind, + Adjust::Deref(DerefAdjustKind::Overloaded(_)) | Adjust::Borrow(AutoBorrow::Ref(_)) + ) + }) + .unwrap_or(false); + match use_site.node { Node::Expr(use_expr) => match use_expr.kind { ExprKind::Field(_, _) | ExprKind::Index(_, _, _) => { + if auto_ref { + return true; + } expr = use_expr; }, - ExprKind::MethodCall(_, _, _, _) => return typeck.type_dependent_def_id(use_expr.hir_id), - _ => return None, + ExprKind::MethodCall(_, _, _, _) => { + return auto_ref + && typeck.type_dependent_def_id(use_expr.hir_id).is_some_and(|fn_id| { + fn_id + .get_attrs(&cx.tcx) // can not use private micro find_attr!() + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::RustcNoImplicitAutorefs))) + }); + }, + _ => return false, }, - _ => return None, + _ => return false, } } - None + + false +} + +// same code from rustc_lint:auto_ref.rs +fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustment<'a>] { + while let [ + Adjustment { + kind: Adjust::Deref(_), .. + }, + end @ .., + ] = adjs + && !end.is_empty() + { + adjs = end; + } + adjs } fn is_deref_or_derefmut_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3eef8e6bca36..0671bbd0b14d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2547,6 +2547,7 @@ impl<'tcx> ExprUseSite<'tcx> { ), ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name), ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl), + ExprKind::Index(_, _, _) => ExprUseNode::Index, _ => ExprUseNode::Other, }, _ => ExprUseNode::Other, @@ -2574,6 +2575,7 @@ pub enum ExprUseNode<'tcx> { FieldAccess(Ident), /// Borrow expression. AddrOf(ast::BorrowKind, Mutability), + Index, Other, } impl<'tcx> ExprUseNode<'tcx> { @@ -2651,7 +2653,7 @@ impl<'tcx> ExprUseNode<'tcx> { ty: sig.input(i), }) }, - Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None, + Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) | _ => None, } } } diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 8c6c268912db..9648c02ba80c 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -320,22 +320,23 @@ fn issue_17414( let a = [0, 1, 2]; let b = &raw const a[0..2]; // same with raw const or raw mut let _ = (&*b).len(); // necessary - let _ = (*b)[0]; // ok - let _ = (&*b)[0]; // TODO needless, should lint + let _ = (&*b).index(0); // necessary + let _: i32 = (*b)[0]; // success + + let _: i32 = (*b)[0]; + //~^ needless_borrow // 3. nested tuple let _ = (&*nested_tuple).1.1.len(); // necessary let _ = (&(*nested_tuple).1).1.len(); // necessary let _ = (&(*nested_tuple).1.1).len(); // necessary - let _ = (*nested_tuple).1.1.first(); // ok + let _ = (*nested_tuple).1.1.first(); // success let _ = (*nested_tuple).1.1.first(); //~^ needless_borrow - let _ = (*nested_tuple).1.1.first(); //~^ needless_borrow - let _ = (*nested_tuple).1.1.first(); //~^ needless_borrow @@ -345,6 +346,10 @@ fn issue_17414( // let _ = (*b).index(0); // fail let _ = (&*b).index(0); // necessary + let _ = (*b)[0]; // ok + let _ = (*b)[0]; + //~^ needless_borrow + // 5. trait trait T: Index {} struct S {} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 0a6246e70eec..b083a2e90e7a 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -320,22 +320,23 @@ fn issue_17414( let a = [0, 1, 2]; let b = &raw const a[0..2]; // same with raw const or raw mut let _ = (&*b).len(); // necessary - let _ = (*b)[0]; // ok - let _ = (&*b)[0]; // TODO needless, should lint + let _ = (&*b).index(0); // necessary + let _: i32 = (*b)[0]; // success + + let _: i32 = (&*b)[0]; + //~^ needless_borrow // 3. nested tuple let _ = (&*nested_tuple).1.1.len(); // necessary let _ = (&(*nested_tuple).1).1.len(); // necessary let _ = (&(*nested_tuple).1.1).len(); // necessary - let _ = (*nested_tuple).1.1.first(); // ok + let _ = (*nested_tuple).1.1.first(); // success let _ = (&*nested_tuple).1.1.first(); //~^ needless_borrow - let _ = (&(*nested_tuple).1).1.first(); //~^ needless_borrow - let _ = (&(*nested_tuple).1.1).first(); //~^ needless_borrow @@ -345,6 +346,10 @@ fn issue_17414( // let _ = (*b).index(0); // fail let _ = (&*b).index(0); // necessary + let _ = (*b)[0]; // ok + let _ = (&*b)[0]; + //~^ needless_borrow + // 5. trait trait T: Index {} struct S {} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 6807512b4e69..eee7579e42b3 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -188,22 +188,34 @@ LL | let _ = (&slice.1.1).len(); | ^^^^^^^^^^^^ help: change this to: `slice.1.1` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:333:17 + --> tests/ui/needless_borrow.rs:326:22 + | +LL | let _: i32 = (&*b)[0]; + | ^^^^^ help: change this to: `(*b)` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:336:17 | LL | let _ = (&*nested_tuple).1.1.first(); | ^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:336:17 + --> tests/ui/needless_borrow.rs:338:17 | LL | let _ = (&(*nested_tuple).1).1.first(); | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple).1` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:339:17 + --> tests/ui/needless_borrow.rs:340:17 | LL | let _ = (&(*nested_tuple).1.1).first(); | ^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `(*nested_tuple).1.1` -error: aborting due to 34 previous errors +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow.rs:350:17 + | +LL | let _ = (&*b)[0]; + | ^^^^^ help: change this to: `(*b)` + +error: aborting due to 36 previous errors From f107b9c4b1ef2356b011bfe2fcb814d60c552e0b Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Wed, 22 Jul 2026 13:04:21 +0800 Subject: [PATCH 07/11] needless_borrow: check whether borrow of index operation is necessary. 1. add needless_borrow lint to a normal index operation. 2. skip this check for raw pointer because explicit borrow is necessary. --- clippy_lints/src/dereference.rs | 2 ++ clippy_utils/src/lib.rs | 5 +++-- tests/ui/redundant_slicing.fixed | 1 + tests/ui/redundant_slicing.rs | 1 + tests/ui/redundant_slicing.stderr | 6 +++--- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 0d5c899860ec..1a73e820f25e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -283,6 +283,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { + // dbg!(expr); let expr_ty = typeck.expr_ty(expr); let use_site = get_expr_use_site(cx.tcx, typeck, SyntaxContext::root(), expr); let adjusted_ty = use_site.adjustments.last().map_or(expr_ty, |a| a.target); @@ -451,6 +452,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { true } }, + ExprUseNode::Index(base, _) if expr.hir_id == base.hir_id => true, _ => false, }; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0671bbd0b14d..2c26d4edfef5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2547,7 +2547,7 @@ impl<'tcx> ExprUseSite<'tcx> { ), ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name), ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl), - ExprKind::Index(_, _, _) => ExprUseNode::Index, + ExprKind::Index(base, idx, _) => ExprUseNode::Index(base, idx), _ => ExprUseNode::Other, }, _ => ExprUseNode::Other, @@ -2575,7 +2575,8 @@ pub enum ExprUseNode<'tcx> { FieldAccess(Ident), /// Borrow expression. AddrOf(ast::BorrowKind, Mutability), - Index, + /// Index operation: base, index num + Index(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>), Other, } impl<'tcx> ExprUseNode<'tcx> { diff --git a/tests/ui/redundant_slicing.fixed b/tests/ui/redundant_slicing.fixed index 2d194fa6f7b1..413e25e8aad2 100644 --- a/tests/ui/redundant_slicing.fixed +++ b/tests/ui/redundant_slicing.fixed @@ -1,5 +1,6 @@ #![warn(clippy::redundant_slicing)] #![expect(clippy::deref_by_slicing)] +#![allow(clippy::needless_borrow)] use std::io::Read; diff --git a/tests/ui/redundant_slicing.rs b/tests/ui/redundant_slicing.rs index 74fc86993a86..30e737d4e8fe 100644 --- a/tests/ui/redundant_slicing.rs +++ b/tests/ui/redundant_slicing.rs @@ -1,5 +1,6 @@ #![warn(clippy::redundant_slicing)] #![expect(clippy::deref_by_slicing)] +#![allow(clippy::needless_borrow)] use std::io::Read; diff --git a/tests/ui/redundant_slicing.stderr b/tests/ui/redundant_slicing.stderr index 964111bccf88..db1b8d4210fa 100644 --- a/tests/ui/redundant_slicing.stderr +++ b/tests/ui/redundant_slicing.stderr @@ -1,5 +1,5 @@ error: redundant slicing of the whole range - --> tests/ui/redundant_slicing.rs:8:13 + --> tests/ui/redundant_slicing.rs:9:13 | LL | let _ = &slice[..]; // Redundant slice | ^^^^^^^^^^ help: use the original value instead: `slice` @@ -8,13 +8,13 @@ LL | let _ = &slice[..]; // Redundant slice = help: to override `-D warnings` add `#[allow(clippy::redundant_slicing)]` error: redundant slicing of the whole range - --> tests/ui/redundant_slicing.rs:14:13 + --> tests/ui/redundant_slicing.rs:15:13 | LL | let _ = &(&*v)[..]; // Outer borrow is redundant | ^^^^^^^^^^ help: use the original value instead: `(&*v)` error: redundant slicing of the whole range - --> tests/ui/redundant_slicing.rs:33:13 + --> tests/ui/redundant_slicing.rs:34:13 | LL | let _ = &m!(slice)[..]; | ^^^^^^^^^^^^^^ help: use the original value instead: `slice` From 41832828d31ec13598ffd0638466b7c88a54771a Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Wed, 22 Jul 2026 16:06:53 +0800 Subject: [PATCH 08/11] needless_borrow: check whether borrow of index operation is necessary. 1. add needless_borrow lint to a normal index operation. 2. skip this check for raw pointer because explicit borrow is necessary. --- clippy_lints/src/dereference.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 1a73e820f25e..1b050a026650 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -375,11 +375,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // deref through `ManuallyDrop<_>` will not compile. !adjust_derefs_manually_drop(use_site.adjustments, expr_ty) }, - ExprUseNode::Callee | ExprUseNode::FieldAccess(_) | ExprUseNode::Index - if !use_site.moved_before_use => - { - true - }, + ExprUseNode::Callee | ExprUseNode::FieldAccess(_) if !use_site.moved_before_use => true, ExprUseNode::MethodArg(hir_id, _, 0) if !use_site.moved_before_use => { // Check for calls to trait methods where auto-borrow will not resolve. // Three cases need to be handled: From db3ec1e1012e116e4acf0a8e9193862ad6408eda Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Wed, 22 Jul 2026 16:19:50 +0800 Subject: [PATCH 09/11] needless_borrow: check whether borrow of index operation is necessary. 1. add needless_borrow lint to a normal index operation. 2. skip this check for raw pointer because explicit borrow is necessary. --- clippy_lints/src/dereference.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 1b050a026650..211298dadca2 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -770,13 +770,12 @@ fn check_invoke_chain_danger<'tcx>( .get(expr.hir_id) .map(|adjs| peel_derefs_adjustments(adjs)) .and_then(|adjs| if adjs.len() == 1 { adjs.first() } else { None }) - .map(|adj| { + .is_some_and(|adj| { matches!( adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)) | Adjust::Borrow(AutoBorrow::Ref(_)) ) - }) - .unwrap_or(false); + }); match use_site.node { Node::Expr(use_expr) => match use_expr.kind { From 1ca481cc63cd1a025ce10ed81372ae91102c885c Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Wed, 22 Jul 2026 16:30:49 +0800 Subject: [PATCH 10/11] needless_borrow: check whether borrow of index operation is necessary. 1. add needless_borrow lint to a normal index operation. 2. skip this check for raw pointer because explicit borrow is necessary. --- clippy_utils/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2c26d4edfef5..411b340f852b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2654,7 +2654,12 @@ impl<'tcx> ExprUseNode<'tcx> { ty: sig.input(i), }) }, - Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) | _ => None, + Self::LetStmt(_) + | Self::FieldAccess(..) + | Self::Callee + | Self::Other + | Self::AddrOf(..) + | Self::Index(_, _) => None, } } } From a74342f75966c55a0d595aa928456f1f658e831c Mon Sep 17 00:00:00 2001 From: SkieFucker <1215233654@qq.com> Date: Wed, 22 Jul 2026 19:44:54 +0800 Subject: [PATCH 11/11] add doc & get adjustments simply --- clippy_lints/src/dereference.rs | 35 +++++++++++++++++++++------------ clippy_utils/src/lib.rs | 2 +- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 211298dadca2..4335e637936c 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -283,7 +283,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { - // dbg!(expr); let expr_ty = typeck.expr_ty(expr); let use_site = get_expr_use_site(cx.tcx, typeck, SyntaxContext::root(), expr); let adjusted_ty = use_site.adjustments.last().map_or(expr_ty, |a| a.target); @@ -740,6 +739,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } } +// Issue: https://github.com/rust-lang/rust-clippy/issues/17414 +// Related: https://doc.rust-lang.org/beta/nightly-rustc/rustc_lint/autorefs/static.DANGEROUS_IMPLICIT_AUTOREFS.html +// A raw pointer need explicit ref to prevent from dangerous_implicit_autorefs. +// Recognize whether the ref of expr must be explicit. Similar Logic to `rustc_lint::autorefs` . +// 1. expr is a AddrOf Ref. +// 2. Loop sub_expr of expr if sub_expr is index/field, and is a deref of a raw ptr at the end. +// 3. Not all raw ptr need explicit ref. Only if it is in a danger invoke chain. Will check in +// method check_invoke_chain_danger. fn need_explicit_ref<'tcx>(cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { match expr.kind { ExprKind::AddrOf(BorrowKind::Ref, _, mut sub_expr) => { @@ -758,24 +765,26 @@ fn need_explicit_ref<'tcx>(cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<' } } +// Check expr whether in a danger invoke chain. +// 1. Loop use site of expr. +// 2. Get adjustments. +// 3. If adjs list pattern [N * Deref Adjust, 1 * AutoBorrow Adjust], means auto ref exist. +// 3. Get use node. +// 4. If node is field or index and auto ref exist, return true, else continue loop. +// 5. If method call, check method has `RustcNoImplicitAutorefs` attr. fn check_invoke_chain_danger<'tcx>( cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, mut expr: &'tcx Expr<'tcx>, ) -> bool { - let adjs_table = typeck.adjustments(); - while let Some(use_site) = expr_use_sites(cx.tcx, typeck, SyntaxContext::root(), expr).next() { - let auto_ref = adjs_table - .get(expr.hir_id) - .map(|adjs| peel_derefs_adjustments(adjs)) - .and_then(|adjs| if adjs.len() == 1 { adjs.first() } else { None }) - .is_some_and(|adj| { - matches!( - adj.kind, - Adjust::Deref(DerefAdjustKind::Overloaded(_)) | Adjust::Borrow(AutoBorrow::Ref(_)) - ) - }); + let adjs = peel_derefs_adjustments(use_site.adjustments); + let auto_ref = if adjs.len() == 1 { adjs.first() } else { None }.is_some_and(|adj| { + matches!( + adj.kind, + Adjust::Deref(DerefAdjustKind::Overloaded(_)) | Adjust::Borrow(AutoBorrow::Ref(_)) + ) + }); match use_site.node { Node::Expr(use_expr) => match use_expr.kind { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 411b340f852b..e3f1b0a3cac8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2659,7 +2659,7 @@ impl<'tcx> ExprUseNode<'tcx> { | Self::Callee | Self::Other | Self::AddrOf(..) - | Self::Index(_, _) => None, + | Self::Index(..) => None, } } }