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
This commit is contained in:
bors 2022-10-28 06:26:49 +00:00
commit 33137dd612
4 changed files with 121 additions and 29 deletions

View File

@ -103,7 +103,7 @@ mod zst_offset;
use bind_instead_of_map::BindInsteadOfMap;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
@ -3394,36 +3394,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id());
// walk the return type and check for Self (this does not check associated types)
if let Some(self_adt) = self_ty.ty_adt_def() {
if contains_adt_constructor(ret_ty, self_adt) {
return;
}
} else if ret_ty.contains(self_ty) {
if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
return;
}
// if return type is impl trait, check the associated types
if let ty::Opaque(def_id, _) = *ret_ty.kind() {
// one of the associated types must be Self
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
let assoc_ty = match projection_predicate.term.unpack() {
ty::TermKind::Ty(ty) => ty,
ty::TermKind::Const(_c) => continue,
};
// walk the associated type and check for Self
if let Some(self_adt) = self_ty.ty_adt_def() {
if contains_adt_constructor(assoc_ty, self_adt) {
return;
}
} else if assoc_ty.contains(self_ty) {
return;
}
}
}
}
if name == "new" && ret_ty != self_ty {
span_lint(
cx,

View File

@ -59,6 +59,58 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
})
}
/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
/// constructor of the same type.
///
/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
/// will also return `true`.
pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
ty.walk().any(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => {
if inner_ty == needle {
return true;
}
if inner_ty.ty_adt_def() == needle.ty_adt_def() {
return true;
}
if let ty::Opaque(def_id, _) = *inner_ty.kind() {
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
match predicate.kind().skip_binder() {
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
// and check substituions to find `U`.
ty::PredicateKind::Trait(trait_predicate) => {
if trait_predicate
.trait_ref
.substs
.types()
.skip(1) // Skip the implicit `Self` generic parameter
.any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle))
{
return true;
}
},
// For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
// so we check the term for `U`.
ty::PredicateKind::Projection(projection_predicate) => {
if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
if contains_ty_adt_constructor_opaque(cx, ty, needle) {
return true;
}
};
},
_ => (),
}
}
}
false
},
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
})
}
/// Resolves `<T as Iterator>::Item` for `T`
/// Do not invoke without first verifying that the type implements `Iterator`
pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {

View File

@ -350,3 +350,53 @@ impl RetOtherSelf<T> {
RetOtherSelf(RetOtherSelfWrapper(t))
}
}
mod issue7344 {
struct RetImplTraitSelf<T>(T);
impl<T> RetImplTraitSelf<T> {
// should not trigger lint
fn new(t: T) -> impl Into<Self> {
Self(t)
}
}
struct RetImplTraitNoSelf<T>(T);
impl<T> RetImplTraitNoSelf<T> {
// should trigger lint
fn new(t: T) -> impl Into<i32> {
1
}
}
trait Trait2<T, U> {}
impl<T, U> Trait2<T, U> for () {}
struct RetImplTraitSelf2<T>(T);
impl<T> RetImplTraitSelf2<T> {
// should not trigger lint
fn new(t: T) -> impl Trait2<(), Self> {
unimplemented!()
}
}
struct RetImplTraitNoSelf2<T>(T);
impl<T> RetImplTraitNoSelf2<T> {
// should trigger lint
fn new(t: T) -> impl Trait2<(), i32> {
unimplemented!()
}
}
struct RetImplTraitSelfAdt<'a>(&'a str);
impl<'a> RetImplTraitSelfAdt<'a> {
// should not trigger lint
fn new<'b: 'a>(s: &'b str) -> impl Into<RetImplTraitSelfAdt<'b>> {
RetImplTraitSelfAdt(s)
}
}
}

View File

@ -76,5 +76,21 @@ LL | | unimplemented!();
LL | | }
| |_________^
error: aborting due to 10 previous errors
error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:368:9
|
LL | / fn new(t: T) -> impl Into<i32> {
LL | | 1
LL | | }
| |_________^
error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:389:9
|
LL | / fn new(t: T) -> impl Trait2<(), i32> {
LL | | unimplemented!()
LL | | }
| |_________^
error: aborting due to 12 previous errors