]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/types/type_complexity.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.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 }