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