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