Skip to content

Commit 6b444f3

Browse files
Also check code generated by macros
1 parent 91fd01c commit 6b444f3

File tree

3 files changed

+104
-110
lines changed

3 files changed

+104
-110
lines changed

clippy_lints/src/unconditional_recursion.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::{get_parent_as_impl, get_trait_def_id, path_res};
2+
use clippy_utils::{get_trait_def_id, path_res};
33
use rustc_ast::BinOpKind;
44
use rustc_hir::def::Res;
55
use rustc_hir::def_id::{DefId, LocalDefId};
66
use rustc_hir::intravisit::FnKind;
7-
use rustc_hir::{Body, Expr, ExprKind, FnDecl};
7+
use rustc_hir::{Body, Expr, ExprKind, FnDecl, Item, ItemKind, Node};
88
use rustc_lint::{LateContext, LateLintPass};
99
use rustc_middle::ty::{self, Ty};
1010
use rustc_session::declare_lint_pass;
@@ -66,22 +66,32 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
6666
method_span: Span,
6767
def_id: LocalDefId,
6868
) {
69-
// We don't check code generated from (proc) macro.
70-
if method_span.from_expansion() {
71-
return;
72-
}
69+
// If the function is a method...
7370
if let FnKind::Method(name, _) = kind
71+
// That has two arguments.
7472
&& let [self_arg, other_arg] = cx
7573
.tcx
7674
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder())
7775
.inputs()
7876
&& let Some(self_arg) = get_ty_def_id(*self_arg)
7977
&& let Some(other_arg) = get_ty_def_id(*other_arg)
78+
// The two arguments are of the same type.
8079
&& self_arg == other_arg
8180
&& let hir_id = cx.tcx.local_def_id_to_hir_id(def_id)
82-
&& let Some(impl_) = get_parent_as_impl(cx.tcx, hir_id)
81+
&& let Some((
82+
_,
83+
Node::Item(Item {
84+
kind: ItemKind::Impl(impl_),
85+
owner_id,
86+
..
87+
}),
88+
)) = cx.tcx.hir().parent_iter(hir_id).next()
89+
// We exclude `impl` blocks generated from rustc's proc macros.
90+
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
91+
// It is a implementation of a trait.
8392
&& let Some(trait_) = impl_.of_trait
8493
&& let Some(trait_def_id) = trait_.trait_def_id()
94+
// The trait is `PartialEq`.
8595
&& Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"])
8696
{
8797
let to_check_op = if name.name == sym::eq {

tests/ui/unconditional_recursion.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@no-rustfix
22

33
#![warn(clippy::unconditional_recursion)]
4+
#![allow(clippy::partialeq_ne_impl)]
45

56
enum Foo {
67
A,
@@ -123,6 +124,40 @@ impl PartialEq for S3 {
123124
}
124125
}
125126

127+
// There should be no warning here!
128+
#[derive(PartialEq)]
129+
enum E {
130+
A,
131+
B,
132+
}
133+
134+
#[derive(PartialEq)]
135+
struct Bar<T: PartialEq>(T);
136+
137+
struct S4;
138+
139+
impl PartialEq for S4 {
140+
fn eq(&self, other: &Self) -> bool {
141+
// No warning here.
142+
Bar(self) == Bar(other)
143+
}
144+
}
145+
146+
macro_rules! impl_partial_eq {
147+
($ty:ident) => {
148+
impl PartialEq for $ty {
149+
fn eq(&self, other: &Self) -> bool {
150+
self == other
151+
}
152+
}
153+
};
154+
}
155+
156+
struct S5;
157+
158+
impl_partial_eq!(S5);
159+
//~^ ERROR: function cannot return without recursing
160+
126161
fn main() {
127162
// test code goes here
128163
}

0 commit comments

Comments
 (0)