mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-24 13:43:17 +00:00
Better support projection types when finding the signature for an expression
This commit is contained in:
parent
b776fb8294
commit
c107c97e69
1 changed files with 109 additions and 65 deletions
|
@ -14,8 +14,8 @@ use rustc_lint::LateContext;
|
||||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Region, RegionKind, Ty,
|
self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, Region,
|
||||||
TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr,
|
RegionKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr,
|
||||||
};
|
};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||||
|
@ -530,7 +530,11 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
|
||||||
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
|
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
|
||||||
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
|
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
|
||||||
} else {
|
} else {
|
||||||
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
|
ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||||
match *ty.kind() {
|
match *ty.kind() {
|
||||||
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
|
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
|
||||||
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
|
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
|
||||||
|
@ -546,58 +550,98 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
|
||||||
let output = bounds
|
let output = bounds
|
||||||
.projection_bounds()
|
.projection_bounds()
|
||||||
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
|
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
|
||||||
.map(|p| p.map_bound(|p| p.term.ty().expect("return type was a const")));
|
.map(|p| p.map_bound(|p| p.term.ty().unwrap()));
|
||||||
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
|
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ty::Param(_) | ty::Projection(..) => {
|
ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
|
||||||
|
Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
|
||||||
|
_ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
|
||||||
|
},
|
||||||
|
ty::Param(_) => sig_from_bounds(cx, ty),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||||
let mut inputs = None;
|
let mut inputs = None;
|
||||||
let mut output = None;
|
let mut output = None;
|
||||||
let lang_items = cx.tcx.lang_items();
|
let lang_items = cx.tcx.lang_items();
|
||||||
|
|
||||||
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
|
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
|
||||||
let mut is_input = false;
|
match pred.kind().skip_binder() {
|
||||||
if let Some(ty) = pred
|
|
||||||
.kind()
|
|
||||||
.map_bound(|pred| match pred {
|
|
||||||
PredicateKind::Trait(p)
|
PredicateKind::Trait(p)
|
||||||
if (lang_items.fn_trait() == Some(p.def_id())
|
if (lang_items.fn_trait() == Some(p.def_id())
|
||||||
|| lang_items.fn_mut_trait() == Some(p.def_id())
|
|| lang_items.fn_mut_trait() == Some(p.def_id())
|
||||||
|| lang_items.fn_once_trait() == Some(p.def_id()))
|
|| lang_items.fn_once_trait() == Some(p.def_id()))
|
||||||
&& p.self_ty() == ty =>
|
&& p.self_ty() == ty =>
|
||||||
{
|
{
|
||||||
is_input = true;
|
if inputs.is_some() {
|
||||||
Some(p.trait_ref.substs.type_at(1))
|
// Multiple different fn trait impls. Is this even allowed?
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
|
||||||
},
|
},
|
||||||
PredicateKind::Projection(p)
|
PredicateKind::Projection(p)
|
||||||
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
|
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
|
||||||
&& p.projection_ty.self_ty() == ty =>
|
&& p.projection_ty.self_ty() == ty =>
|
||||||
{
|
{
|
||||||
is_input = false;
|
if output.is_some() {
|
||||||
p.term.ty()
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.transpose()
|
|
||||||
{
|
|
||||||
if is_input && inputs.is_none() {
|
|
||||||
inputs = Some(ty);
|
|
||||||
} else if !is_input && output.is_none() {
|
|
||||||
output = Some(ty);
|
|
||||||
} else {
|
|
||||||
// Multiple different fn trait impls. Is this even allowed?
|
// Multiple different fn trait impls. Is this even allowed?
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
output = Some(pred.kind().rebind(p.term.ty().unwrap()));
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs.map(|ty| ExprFnSig::Trait(ty, output))
|
inputs.map(|ty| ExprFnSig::Trait(ty, output))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||||
|
let mut inputs = None;
|
||||||
|
let mut output = None;
|
||||||
|
let lang_items = cx.tcx.lang_items();
|
||||||
|
|
||||||
|
for pred in cx
|
||||||
|
.tcx
|
||||||
|
.bound_explicit_item_bounds(ty.item_def_id)
|
||||||
|
.transpose_iter()
|
||||||
|
.map(|x| x.map_bound(|(p, _)| p))
|
||||||
|
{
|
||||||
|
match pred.0.kind().skip_binder() {
|
||||||
|
PredicateKind::Trait(p)
|
||||||
|
if (lang_items.fn_trait() == Some(p.def_id())
|
||||||
|
|| lang_items.fn_mut_trait() == Some(p.def_id())
|
||||||
|
|| lang_items.fn_once_trait() == Some(p.def_id())) =>
|
||||||
|
{
|
||||||
|
if inputs.is_some() {
|
||||||
|
// Multiple different fn trait impls. Is this even allowed?
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
inputs = Some(
|
||||||
|
pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
|
||||||
|
.subst(cx.tcx, ty.substs),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
_ => None,
|
PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
|
||||||
|
if output.is_some() {
|
||||||
|
// Multiple different fn trait impls. Is this even allowed?
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
output = Some(
|
||||||
|
pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
|
||||||
|
.subst(cx.tcx, ty.substs),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputs.map(|ty| ExprFnSig::Trait(ty, output))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
Loading…
Reference in a new issue