Do not recurse forever in significant_drop_tightening (#14641)

Limit the recursion depth, as each level of nesting adds another deeper
projection.

There might be a more complex way of handling the problem, but infinite
recursions are bad, and don't allow Clippy to terminate properly.

changelog: [`significant_drop_tightening`]: do not recurse forever when
checking for attribute on type or its constituent

Fixes rust-lang/rust-clippy#13544

@rustbot label +L-nursery
This commit is contained in:
Timo 2025-04-17 11:09:01 +00:00 committed by GitHub
commit 30e9cd5a26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 71 additions and 7 deletions

View file

@ -144,7 +144,10 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
Self { cx, type_cache }
}
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>, depth: usize) -> bool {
if !self.cx.tcx.recursion_limit().value_within_limit(depth) {
return false;
}
let ty = self
.cx
.tcx
@ -156,12 +159,12 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
e.insert(false);
},
}
let value = self.has_sig_drop_attr_uncached(ty);
let value = self.has_sig_drop_attr_uncached(ty, depth + 1);
self.type_cache.insert(ty, value);
value
}
fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool {
fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool {
if let Some(adt) = ty.ty_adt_def() {
let mut iter = get_attr(
self.cx.sess(),
@ -176,13 +179,13 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
rustc_middle::ty::Adt(a, b) => {
for f in a.all_fields() {
let ty = f.ty(self.cx.tcx, b);
if self.has_sig_drop_attr(ty) {
if self.has_sig_drop_attr(ty, depth) {
return true;
}
}
for generic_arg in *b {
if let GenericArgKind::Type(ty) = generic_arg.unpack()
&& self.has_sig_drop_attr(ty)
&& self.has_sig_drop_attr(ty, depth)
{
return true;
}
@ -192,7 +195,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
rustc_middle::ty::Array(ty, _)
| rustc_middle::ty::RawPtr(ty, _)
| rustc_middle::ty::Ref(_, ty, _)
| rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty),
| rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty, depth),
_ => false,
}
}
@ -268,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> {
apa.has_expensive_expr_after_last_attr = false;
};
let mut ac = AttrChecker::new(self.cx, self.type_cache);
if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) {
if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr), 0) {
if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind
&& let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
&& !self.ap.apas.contains_key(&hir_id)

View file

@ -0,0 +1,45 @@
//@ check-pass
#![warn(clippy::significant_drop_tightening)]
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
trait Scopable: Sized {
type SubType: Scopable;
}
struct Subtree<T: Scopable>(ManuallyDrop<Box<Tree<T::SubType>>>);
impl<T: Scopable> Drop for Subtree<T> {
fn drop(&mut self) {
// SAFETY: The field cannot be used after we drop
unsafe { ManuallyDrop::drop(&mut self.0) }
}
}
impl<T: Scopable> Deref for Subtree<T> {
type Target = Tree<T::SubType>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Scopable> DerefMut for Subtree<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
enum Tree<T: Scopable> {
Group(Vec<Tree<T>>),
Subtree(Subtree<T>),
Leaf(T),
}
impl<T: Scopable> Tree<T> {
fn foo(self) -> Self {
self
}
}
fn main() {}

View file

@ -0,0 +1,16 @@
//@ check-pass
#![warn(clippy::significant_drop_tightening)]
#![allow(unused, clippy::no_effect)]
use std::marker::PhantomData;
trait Trait {
type Assoc: Trait;
}
struct S<T: Trait>(*const S<T::Assoc>, PhantomData<T>);
fn f<T: Trait>(x: &mut S<T>) {
&mut x.0;
}
fn main() {}