1 use clippy_utils::diagnostics::span_lint;
3 use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor};
4 use rustc_hir::{GenericParamKind, TyKind};
5 use rustc_lint::LateContext;
6 use rustc_target::spec::abi::Abi;
8 use super::TYPE_COMPLEXITY;
10 pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool {
12 let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
17 if score > type_complexity_threshold {
22 "very complex type used. Consider factoring parts into `type` definitions",
30 /// Walks a type and assigns a complexity score to it.
31 struct TypeComplexityVisitor {
32 /// total complexity score of the type
34 /// current nesting level
38 impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
39 fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
44 fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
45 let (add_score, sub_nest) = match ty.kind {
46 // _, &x and *x have only small overhead; don't mess with nesting level
47 TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0),
49 // the "normal" components of a type: named types, arrays/tuples
50 TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1),
52 // function types bring a lot of overhead
53 TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
55 TyKind::TraitObject(param_bounds, _, _) => {
56 let has_lifetime_parameters = param_bounds.iter().any(|bound| {
60 .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
62 if has_lifetime_parameters {
63 // complex trait bounds like A<'a, 'b>
66 // simple trait bounds like A + B
73 self.score += add_score;
74 self.nest += sub_nest;
76 self.nest -= sub_nest;