Skip to content

Commit c107c97

Browse files
committed
Better support projection types when finding the signature for an expression
1 parent b776fb8 commit c107c97

File tree

1 file changed

+107
-63
lines changed

1 file changed

+107
-63
lines changed

clippy_utils/src/ty.rs

Lines changed: 107 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_lint::LateContext;
1414
use rustc_middle::mir::interpret::{ConstValue, Scalar};
1515
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
1616
use rustc_middle::ty::{
17-
self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Region, RegionKind, Ty,
18-
TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr,
17+
self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, Region,
18+
RegionKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr,
1919
};
2020
use rustc_span::symbol::Ident;
2121
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
@@ -530,74 +530,118 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
530530
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
531531
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
532532
} else {
533-
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
534-
match *ty.kind() {
535-
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
536-
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
537-
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
538-
ty::Dynamic(bounds, _) => {
539-
let lang_items = cx.tcx.lang_items();
540-
match bounds.principal() {
541-
Some(bound)
542-
if Some(bound.def_id()) == lang_items.fn_trait()
543-
|| Some(bound.def_id()) == lang_items.fn_once_trait()
544-
|| Some(bound.def_id()) == lang_items.fn_mut_trait() =>
545-
{
546-
let output = bounds
547-
.projection_bounds()
548-
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
549-
.map(|p| p.map_bound(|p| p.term.ty().expect("return type was a const")));
550-
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
551-
},
552-
_ => None,
533+
ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
534+
}
535+
}
536+
537+
fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
538+
match *ty.kind() {
539+
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
540+
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
541+
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
542+
ty::Dynamic(bounds, _) => {
543+
let lang_items = cx.tcx.lang_items();
544+
match bounds.principal() {
545+
Some(bound)
546+
if Some(bound.def_id()) == lang_items.fn_trait()
547+
|| Some(bound.def_id()) == lang_items.fn_once_trait()
548+
|| Some(bound.def_id()) == lang_items.fn_mut_trait() =>
549+
{
550+
let output = bounds
551+
.projection_bounds()
552+
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
553+
.map(|p| p.map_bound(|p| p.term.ty().unwrap()));
554+
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
555+
},
556+
_ => None,
557+
}
558+
},
559+
ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
560+
Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
561+
_ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
562+
},
563+
ty::Param(_) => sig_from_bounds(cx, ty),
564+
_ => None,
565+
}
566+
}
567+
568+
fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
569+
let mut inputs = None;
570+
let mut output = None;
571+
let lang_items = cx.tcx.lang_items();
572+
573+
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
574+
match pred.kind().skip_binder() {
575+
PredicateKind::Trait(p)
576+
if (lang_items.fn_trait() == Some(p.def_id())
577+
|| lang_items.fn_mut_trait() == Some(p.def_id())
578+
|| lang_items.fn_once_trait() == Some(p.def_id()))
579+
&& p.self_ty() == ty =>
580+
{
581+
if inputs.is_some() {
582+
// Multiple different fn trait impls. Is this even allowed?
583+
return None;
553584
}
585+
inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
554586
},
555-
ty::Param(_) | ty::Projection(..) => {
556-
let mut inputs = None;
557-
let mut output = None;
558-
let lang_items = cx.tcx.lang_items();
559-
560-
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
561-
let mut is_input = false;
562-
if let Some(ty) = pred
563-
.kind()
564-
.map_bound(|pred| match pred {
565-
PredicateKind::Trait(p)
566-
if (lang_items.fn_trait() == Some(p.def_id())
567-
|| lang_items.fn_mut_trait() == Some(p.def_id())
568-
|| lang_items.fn_once_trait() == Some(p.def_id()))
569-
&& p.self_ty() == ty =>
570-
{
571-
is_input = true;
572-
Some(p.trait_ref.substs.type_at(1))
573-
},
574-
PredicateKind::Projection(p)
575-
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
576-
&& p.projection_ty.self_ty() == ty =>
577-
{
578-
is_input = false;
579-
p.term.ty()
580-
},
581-
_ => None,
582-
})
583-
.transpose()
584-
{
585-
if is_input && inputs.is_none() {
586-
inputs = Some(ty);
587-
} else if !is_input && output.is_none() {
588-
output = Some(ty);
589-
} else {
590-
// Multiple different fn trait impls. Is this even allowed?
591-
return None;
592-
}
593-
}
587+
PredicateKind::Projection(p)
588+
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
589+
&& p.projection_ty.self_ty() == ty =>
590+
{
591+
if output.is_some() {
592+
// Multiple different fn trait impls. Is this even allowed?
593+
return None;
594594
}
595+
output = Some(pred.kind().rebind(p.term.ty().unwrap()));
596+
},
597+
_ => (),
598+
}
599+
}
600+
601+
inputs.map(|ty| ExprFnSig::Trait(ty, output))
602+
}
603+
604+
fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
605+
let mut inputs = None;
606+
let mut output = None;
607+
let lang_items = cx.tcx.lang_items();
595608

596-
inputs.map(|ty| ExprFnSig::Trait(ty, output))
609+
for pred in cx
610+
.tcx
611+
.bound_explicit_item_bounds(ty.item_def_id)
612+
.transpose_iter()
613+
.map(|x| x.map_bound(|(p, _)| p))
614+
{
615+
match pred.0.kind().skip_binder() {
616+
PredicateKind::Trait(p)
617+
if (lang_items.fn_trait() == Some(p.def_id())
618+
|| lang_items.fn_mut_trait() == Some(p.def_id())
619+
|| lang_items.fn_once_trait() == Some(p.def_id())) =>
620+
{
621+
if inputs.is_some() {
622+
// Multiple different fn trait impls. Is this even allowed?
623+
return None;
624+
}
625+
inputs = Some(
626+
pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
627+
.subst(cx.tcx, ty.substs),
628+
);
597629
},
598-
_ => None,
630+
PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
631+
if output.is_some() {
632+
// Multiple different fn trait impls. Is this even allowed?
633+
return None;
634+
}
635+
output = Some(
636+
pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
637+
.subst(cx.tcx, ty.substs),
638+
);
639+
},
640+
_ => (),
599641
}
600642
}
643+
644+
inputs.map(|ty| ExprFnSig::Trait(ty, output))
601645
}
602646

603647
#[derive(Clone, Copy)]

0 commit comments

Comments
 (0)