Skip to content

Commit 33137dd

Browse files
committed
Auto merge of #9733 - nbdd0121:master, r=dswij
Ensure new_ret_no_self is not fired if impl Trait<Self> is returned. Fix #7344: ensure new_ret_no_self is not fired if `impl Trait<Self>` is returned. changelog: [`new_ret_no_self`]: No longer lints when `impl Trait<Self>` is returned
2 parents 4326814 + 92a119b commit 33137dd

File tree

4 files changed

+121
-29
lines changed

4 files changed

+121
-29
lines changed

clippy_lints/src/methods/mod.rs

+2-28
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ mod zst_offset;
103103
use bind_instead_of_map::BindInsteadOfMap;
104104
use clippy_utils::consts::{constant, Constant};
105105
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
106-
use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
106+
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
107107
use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
108108
use if_chain::if_chain;
109109
use rustc_hir as hir;
@@ -3394,36 +3394,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
33943394
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
33953395
let ret_ty = return_ty(cx, impl_item.hir_id());
33963396

3397-
// walk the return type and check for Self (this does not check associated types)
3398-
if let Some(self_adt) = self_ty.ty_adt_def() {
3399-
if contains_adt_constructor(ret_ty, self_adt) {
3400-
return;
3401-
}
3402-
} else if ret_ty.contains(self_ty) {
3397+
if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
34033398
return;
34043399
}
34053400

3406-
// if return type is impl trait, check the associated types
3407-
if let ty::Opaque(def_id, _) = *ret_ty.kind() {
3408-
// one of the associated types must be Self
3409-
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
3410-
if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
3411-
let assoc_ty = match projection_predicate.term.unpack() {
3412-
ty::TermKind::Ty(ty) => ty,
3413-
ty::TermKind::Const(_c) => continue,
3414-
};
3415-
// walk the associated type and check for Self
3416-
if let Some(self_adt) = self_ty.ty_adt_def() {
3417-
if contains_adt_constructor(assoc_ty, self_adt) {
3418-
return;
3419-
}
3420-
} else if assoc_ty.contains(self_ty) {
3421-
return;
3422-
}
3423-
}
3424-
}
3425-
}
3426-
34273401
if name == "new" && ret_ty != self_ty {
34283402
span_lint(
34293403
cx,

clippy_utils/src/ty.rs

+52
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,58 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
5959
})
6060
}
6161

62+
/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
63+
/// constructor of the same type.
64+
///
65+
/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
66+
/// will also return `true`.
67+
pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
68+
ty.walk().any(|inner| match inner.unpack() {
69+
GenericArgKind::Type(inner_ty) => {
70+
if inner_ty == needle {
71+
return true;
72+
}
73+
74+
if inner_ty.ty_adt_def() == needle.ty_adt_def() {
75+
return true;
76+
}
77+
78+
if let ty::Opaque(def_id, _) = *inner_ty.kind() {
79+
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
80+
match predicate.kind().skip_binder() {
81+
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
82+
// and check substituions to find `U`.
83+
ty::PredicateKind::Trait(trait_predicate) => {
84+
if trait_predicate
85+
.trait_ref
86+
.substs
87+
.types()
88+
.skip(1) // Skip the implicit `Self` generic parameter
89+
.any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle))
90+
{
91+
return true;
92+
}
93+
},
94+
// For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
95+
// so we check the term for `U`.
96+
ty::PredicateKind::Projection(projection_predicate) => {
97+
if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
98+
if contains_ty_adt_constructor_opaque(cx, ty, needle) {
99+
return true;
100+
}
101+
};
102+
},
103+
_ => (),
104+
}
105+
}
106+
}
107+
108+
false
109+
},
110+
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
111+
})
112+
}
113+
62114
/// Resolves `<T as Iterator>::Item` for `T`
63115
/// Do not invoke without first verifying that the type implements `Iterator`
64116
pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {

tests/ui/new_ret_no_self.rs

+50
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,53 @@ impl RetOtherSelf<T> {
350350
RetOtherSelf(RetOtherSelfWrapper(t))
351351
}
352352
}
353+
354+
mod issue7344 {
355+
struct RetImplTraitSelf<T>(T);
356+
357+
impl<T> RetImplTraitSelf<T> {
358+
// should not trigger lint
359+
fn new(t: T) -> impl Into<Self> {
360+
Self(t)
361+
}
362+
}
363+
364+
struct RetImplTraitNoSelf<T>(T);
365+
366+
impl<T> RetImplTraitNoSelf<T> {
367+
// should trigger lint
368+
fn new(t: T) -> impl Into<i32> {
369+
1
370+
}
371+
}
372+
373+
trait Trait2<T, U> {}
374+
impl<T, U> Trait2<T, U> for () {}
375+
376+
struct RetImplTraitSelf2<T>(T);
377+
378+
impl<T> RetImplTraitSelf2<T> {
379+
// should not trigger lint
380+
fn new(t: T) -> impl Trait2<(), Self> {
381+
unimplemented!()
382+
}
383+
}
384+
385+
struct RetImplTraitNoSelf2<T>(T);
386+
387+
impl<T> RetImplTraitNoSelf2<T> {
388+
// should trigger lint
389+
fn new(t: T) -> impl Trait2<(), i32> {
390+
unimplemented!()
391+
}
392+
}
393+
394+
struct RetImplTraitSelfAdt<'a>(&'a str);
395+
396+
impl<'a> RetImplTraitSelfAdt<'a> {
397+
// should not trigger lint
398+
fn new<'b: 'a>(s: &'b str) -> impl Into<RetImplTraitSelfAdt<'b>> {
399+
RetImplTraitSelfAdt(s)
400+
}
401+
}
402+
}

tests/ui/new_ret_no_self.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,21 @@ LL | | unimplemented!();
7676
LL | | }
7777
| |_________^
7878

79-
error: aborting due to 10 previous errors
79+
error: methods called `new` usually return `Self`
80+
--> $DIR/new_ret_no_self.rs:368:9
81+
|
82+
LL | / fn new(t: T) -> impl Into<i32> {
83+
LL | | 1
84+
LL | | }
85+
| |_________^
86+
87+
error: methods called `new` usually return `Self`
88+
--> $DIR/new_ret_no_self.rs:389:9
89+
|
90+
LL | / fn new(t: T) -> impl Trait2<(), i32> {
91+
LL | | unimplemented!()
92+
LL | | }
93+
| |_________^
94+
95+
error: aborting due to 12 previous errors
8096

0 commit comments

Comments
 (0)