extract supertrait collection to separate function
This commit is contained in:
parent
28443e63fb
commit
d1acbf576e
@ -1,16 +1,17 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use rustc_errors::{Applicability, SuggestionStyle};
|
use rustc_errors::{Applicability, SuggestionStyle};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
|
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, OpaqueTy, TraitBoundModifier,
|
||||||
TraitItemKind, TyKind,
|
TraitItem, TraitItemKind, TyKind, TypeBinding,
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
use rustc_hir_analysis::hir_ty_to_ty;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
|
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
@ -50,20 +51,17 @@ declare_clippy_lint! {
|
|||||||
}
|
}
|
||||||
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
|
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn emit_lint(
|
fn emit_lint(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
poly_trait: &rustc_hir::PolyTraitRef<'_>,
|
poly_trait: &rustc_hir::PolyTraitRef<'_>,
|
||||||
opaque_ty: &rustc_hir::OpaqueTy<'_>,
|
opaque_ty: &rustc_hir::OpaqueTy<'_>,
|
||||||
index: usize,
|
index: usize,
|
||||||
// The bindings that were implied
|
// The bindings that were implied, used for suggestion purposes since removing a bound with associated types
|
||||||
|
// means we might need to then move it to a different bound
|
||||||
implied_bindings: &[rustc_hir::TypeBinding<'_>],
|
implied_bindings: &[rustc_hir::TypeBinding<'_>],
|
||||||
// The original bindings that `implied_bindings` are implied from
|
bound: &ImplTraitBound<'_>,
|
||||||
implied_by_bindings: &[rustc_hir::TypeBinding<'_>],
|
|
||||||
implied_by_args: &[GenericArg<'_>],
|
|
||||||
implied_by_span: Span,
|
|
||||||
) {
|
) {
|
||||||
let implied_by = snippet(cx, implied_by_span, "..");
|
let implied_by = snippet(cx, bound.impl_trait_bound_span, "..");
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
@ -93,17 +91,17 @@ fn emit_lint(
|
|||||||
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
|
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
|
||||||
let omitted_assoc_tys: Vec<_> = implied_bindings
|
let omitted_assoc_tys: Vec<_> = implied_bindings
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident))
|
.filter(|binding| !bound.bindings.iter().any(|b| b.ident == binding.ident))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !omitted_assoc_tys.is_empty() {
|
if !omitted_assoc_tys.is_empty() {
|
||||||
// `<>` needs to be added if there aren't yet any generic arguments or bindings
|
// `<>` needs to be added if there aren't yet any generic arguments or bindings
|
||||||
let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty();
|
let needs_angle_brackets = bound.args.is_empty() && bound.bindings.is_empty();
|
||||||
let insert_span = match (implied_by_args, implied_by_bindings) {
|
let insert_span = match (bound.args, bound.bindings) {
|
||||||
([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(),
|
([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(),
|
||||||
([.., arg], []) => arg.span().shrink_to_hi(),
|
([.., arg], []) => arg.span().shrink_to_hi(),
|
||||||
([], [.., binding]) => binding.span.shrink_to_hi(),
|
([], [.., binding]) => binding.span.shrink_to_hi(),
|
||||||
([], []) => implied_by_span.shrink_to_hi(),
|
([], []) => bound.impl_trait_bound_span.shrink_to_hi(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut associated_tys_sugg = if needs_angle_brackets {
|
let mut associated_tys_sugg = if needs_angle_brackets {
|
||||||
@ -223,42 +221,64 @@ fn is_same_generics<'tcx>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ImplTraitBound<'tcx> {
|
||||||
|
/// The span of the bound in the `impl Trait` type
|
||||||
|
impl_trait_bound_span: Span,
|
||||||
|
/// The predicates defined in the trait referenced by this bound
|
||||||
|
predicates: &'tcx [(ty::Clause<'tcx>, Span)],
|
||||||
|
/// The `DefId` of the trait being referenced by this bound
|
||||||
|
trait_def_id: DefId,
|
||||||
|
/// The generic arguments on the `impl Trait` bound
|
||||||
|
args: &'tcx [GenericArg<'tcx>],
|
||||||
|
/// The associated types on this bound
|
||||||
|
bindings: &'tcx [TypeBinding<'tcx>],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds").
|
||||||
|
///
|
||||||
|
/// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
|
||||||
|
/// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
|
||||||
|
/// `Eq`.
|
||||||
|
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, opaque_ty: &OpaqueTy<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
|
||||||
|
opaque_ty
|
||||||
|
.bounds
|
||||||
|
.iter()
|
||||||
|
.filter_map(|bound| {
|
||||||
|
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
|
||||||
|
&& let [.., path] = poly_trait.trait_ref.path.segments
|
||||||
|
&& poly_trait.bound_generic_params.is_empty()
|
||||||
|
&& let Some(trait_def_id) = path.res.opt_def_id()
|
||||||
|
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
|
||||||
|
// If the trait has no supertrait, there is no need to collect anything from that bound
|
||||||
|
&& !predicates.is_empty()
|
||||||
|
{
|
||||||
|
Some(ImplTraitBound {
|
||||||
|
predicates,
|
||||||
|
args: path.args.map_or([].as_slice(), |p| p.args),
|
||||||
|
bindings: path.args.map_or([].as_slice(), |p| p.bindings),
|
||||||
|
trait_def_id,
|
||||||
|
impl_trait_bound_span: bound.span(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
||||||
if let FnRetTy::Return(ty) = decl.output
|
if let FnRetTy::Return(ty) = decl.output
|
||||||
&&let TyKind::OpaqueDef(item_id, ..) = ty.kind
|
&& let TyKind::OpaqueDef(item_id, ..) = ty.kind
|
||||||
&& let item = cx.tcx.hir().item(item_id)
|
&& let item = cx.tcx.hir().item(item_id)
|
||||||
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
|
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
|
||||||
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
|
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
|
||||||
// we can avoid doing a bunch of stuff unnecessarily.
|
// we can avoid doing a bunch of stuff unnecessarily.
|
||||||
&& opaque_ty.bounds.len() > 1
|
&& opaque_ty.bounds.len() > 1
|
||||||
{
|
{
|
||||||
// Get all the (implied) trait predicates in the bounds.
|
let supertraits = collect_supertrait_bounds(cx, opaque_ty);
|
||||||
// For `impl Deref + DerefMut` this will contain [`Deref`].
|
|
||||||
// The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
|
|
||||||
// N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
|
|
||||||
// Example:
|
|
||||||
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
|
|
||||||
// `DerefMut::Target` needs to match `Deref::Target`.
|
|
||||||
let implied_bounds: Vec<_> = opaque_ty
|
|
||||||
.bounds
|
|
||||||
.iter()
|
|
||||||
.filter_map(|bound| {
|
|
||||||
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
|
|
||||||
&& let [.., path] = poly_trait.trait_ref.path.segments
|
|
||||||
&& poly_trait.bound_generic_params.is_empty()
|
|
||||||
&& let Some(trait_def_id) = path.res.opt_def_id()
|
|
||||||
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
|
|
||||||
&& !predicates.is_empty()
|
|
||||||
// If the trait has no supertrait, there is nothing to add.
|
|
||||||
{
|
|
||||||
Some((bound.span(), path, predicates, trait_def_id))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
|
// Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
|
||||||
|
// supertraits of each of the bounds.
|
||||||
// This involves some extra logic when generic arguments are present, since
|
// This involves some extra logic when generic arguments are present, since
|
||||||
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
|
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
|
||||||
for (index, bound) in opaque_ty.bounds.iter().enumerate() {
|
for (index, bound) in opaque_ty.bounds.iter().enumerate() {
|
||||||
@ -267,42 +287,26 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
|||||||
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
|
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
|
||||||
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
|
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
|
||||||
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
|
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
|
||||||
&& let Some((implied_by_span, implied_by_args, implied_by_bindings)) =
|
&& let Some(bound) = supertraits.iter().find(|bound| {
|
||||||
implied_bounds
|
bound.predicates.iter().any(|(clause, _)| {
|
||||||
.iter()
|
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
|
||||||
.find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
|
&& tr.def_id() == def_id
|
||||||
let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
|
{
|
||||||
let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
|
is_same_generics(
|
||||||
|
cx.tcx,
|
||||||
preds.iter().find_map(|(clause, _)| {
|
tr.trait_ref.args,
|
||||||
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
|
bound.args,
|
||||||
&& tr.def_id() == def_id
|
implied_args,
|
||||||
&& is_same_generics(
|
bound.trait_def_id,
|
||||||
cx.tcx,
|
def_id,
|
||||||
tr.trait_ref.args,
|
)
|
||||||
implied_by_args,
|
} else {
|
||||||
implied_args,
|
false
|
||||||
implied_by_def_id,
|
}
|
||||||
def_id,
|
})
|
||||||
)
|
})
|
||||||
{
|
|
||||||
Some((span, implied_by_args, implied_by_bindings))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
emit_lint(
|
emit_lint(cx, poly_trait, opaque_ty, index, implied_bindings, bound);
|
||||||
cx,
|
|
||||||
poly_trait,
|
|
||||||
opaque_ty,
|
|
||||||
index,
|
|
||||||
implied_bindings,
|
|
||||||
implied_by_bindings,
|
|
||||||
implied_by_args,
|
|
||||||
implied_by_span,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user