]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/types/type_complexity.rs
Rollup merge of #87528 - :stack_overflow_obsd, r=joshtriplett
[rust.git] / src / tools / clippy / clippy_lints / src / types / type_complexity.rs
1 use clippy_utils::diagnostics::span_lint;
2 use rustc_hir as hir;
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;
8
9 use super::TYPE_COMPLEXITY;
10
11 pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool {
12     let score = {
13         let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
14         visitor.visit_ty(ty);
15         visitor.score
16     };
17
18     if score > type_complexity_threshold {
19         span_lint(
20             cx,
21             TYPE_COMPLEXITY,
22             ty.span,
23             "very complex type used. Consider factoring parts into `type` definitions",
24         );
25         true
26     } else {
27         false
28     }
29 }
30
31 /// Walks a type and assigns a complexity score to it.
32 struct TypeComplexityVisitor {
33     /// total complexity score of the type
34     score: u64,
35     /// current nesting level
36     nest: u64,
37 }
38
39 impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
40     type Map = Map<'tcx>;
41
42     fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
43         self.score += 1;
44         walk_inf(self, inf);
45     }
46
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),
51
52             // the "normal" components of a type: named types, arrays/tuples
53             TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1),
54
55             // function types bring a lot of overhead
56             TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
57
58             TyKind::TraitObject(param_bounds, _, _) => {
59                 let has_lifetime_parameters = param_bounds.iter().any(|bound| {
60                     bound
61                         .bound_generic_params
62                         .iter()
63                         .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
64                 });
65                 if has_lifetime_parameters {
66                     // complex trait bounds like A<'a, 'b>
67                     (50 * self.nest, 1)
68                 } else {
69                     // simple trait bounds like A + B
70                     (20 * self.nest, 0)
71                 }
72             },
73
74             _ => (0, 0),
75         };
76         self.score += add_score;
77         self.nest += sub_nest;
78         walk_ty(self, ty);
79         self.nest -= sub_nest;
80     }
81     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
82         NestedVisitorMap::None
83     }
84 }