1
1
//! calculate cognitive complexity and warn about overly complex functions
2
2
3
- use rustc:: cfg:: CFG ;
4
3
use rustc:: hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
5
4
use rustc:: hir:: * ;
6
5
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintContext , LintPass } ;
7
- use rustc:: ty;
8
6
use rustc:: { declare_tool_lint, impl_lint_pass} ;
9
7
use syntax:: ast:: Attribute ;
10
8
use syntax:: source_map:: Span ;
11
9
12
- use crate :: utils:: { is_allowed , match_type, paths, span_help_and_lint, LimitStack } ;
10
+ use crate :: utils:: { match_type, paths, span_help_and_lint, LimitStack } ;
13
11
14
12
declare_clippy_lint ! {
15
13
/// **What it does:** Checks for methods with high cognitive complexity.
@@ -46,30 +44,11 @@ impl CognitiveComplexity {
46
44
return ;
47
45
}
48
46
49
- let cfg = CFG :: new ( cx. tcx , body) ;
50
47
let expr = & body. value ;
51
- let n = cfg. graph . len_nodes ( ) as u64 ;
52
- let e = cfg. graph . len_edges ( ) as u64 ;
53
- if e + 2 < n {
54
- // the function has unreachable code, other lints should catch this
55
- return ;
56
- }
57
- let cc = e + 2 - n;
58
- let mut helper = CCHelper {
59
- match_arms : 0 ,
60
- divergence : 0 ,
61
- short_circuits : 0 ,
62
- returns : 0 ,
63
- cx,
64
- } ;
48
+
49
+ let mut helper = CCHelper { cc : 1 , returns : 0 } ;
65
50
helper. visit_expr ( expr) ;
66
- let CCHelper {
67
- match_arms,
68
- divergence,
69
- short_circuits,
70
- returns,
71
- ..
72
- } = helper;
51
+ let CCHelper { cc, returns } = helper;
73
52
let ret_ty = cx. tables . node_type ( expr. hir_id ) ;
74
53
let ret_adjust = if match_type ( cx, ret_ty, & paths:: RESULT ) {
75
54
returns
@@ -78,36 +57,23 @@ impl CognitiveComplexity {
78
57
( returns / 2 )
79
58
} ;
80
59
81
- if cc + divergence < match_arms + short_circuits {
82
- report_cc_bug (
60
+ let mut rust_cc = cc;
61
+ // prevent degenerate cases where unreachable code contains `return` statements
62
+ if rust_cc >= ret_adjust {
63
+ rust_cc -= ret_adjust;
64
+ }
65
+ if rust_cc > self . limit . limit ( ) {
66
+ span_help_and_lint (
83
67
cx,
84
- cc,
85
- match_arms,
86
- divergence,
87
- short_circuits,
88
- ret_adjust,
68
+ COGNITIVE_COMPLEXITY ,
89
69
span,
90
- body. id ( ) . hir_id ,
70
+ & format ! (
71
+ "the function has a cognitive complexity of ({}/{})" ,
72
+ rust_cc,
73
+ self . limit. limit( )
74
+ ) ,
75
+ "you could split it up into multiple smaller functions" ,
91
76
) ;
92
- } else {
93
- let mut rust_cc = cc + divergence - match_arms - short_circuits;
94
- // prevent degenerate cases where unreachable code contains `return` statements
95
- if rust_cc >= ret_adjust {
96
- rust_cc -= ret_adjust;
97
- }
98
- if rust_cc > self . limit . limit ( ) {
99
- span_help_and_lint (
100
- cx,
101
- COGNITIVE_COMPLEXITY ,
102
- span,
103
- & format ! (
104
- "the function has a cognitive complexity of ({}/{})" ,
105
- rust_cc,
106
- self . limit. limit( )
107
- ) ,
108
- "you could split it up into multiple smaller functions" ,
109
- ) ;
110
- }
111
77
}
112
78
}
113
79
}
@@ -136,99 +102,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity {
136
102
}
137
103
}
138
104
139
- struct CCHelper < ' a , ' tcx > {
140
- match_arms : u64 ,
141
- divergence : u64 ,
105
+ struct CCHelper {
106
+ cc : u64 ,
142
107
returns : u64 ,
143
- short_circuits : u64 , // && and ||
144
- cx : & ' a LateContext < ' a , ' tcx > ,
145
108
}
146
109
147
- impl < ' a , ' tcx > Visitor < ' tcx > for CCHelper < ' a , ' tcx > {
110
+ impl < ' tcx > Visitor < ' tcx > for CCHelper {
148
111
fn visit_expr ( & mut self , e : & ' tcx Expr ) {
112
+ walk_expr ( self , e) ;
149
113
match e. node {
150
114
ExprKind :: Match ( _, ref arms, _) => {
151
- walk_expr ( self , e) ;
152
115
let arms_n: u64 = arms. iter ( ) . map ( |arm| arm. pats . len ( ) as u64 ) . sum ( ) ;
153
116
if arms_n > 1 {
154
- self . match_arms += arms_n - 2 ;
155
- }
156
- } ,
157
- ExprKind :: Call ( ref callee, _) => {
158
- walk_expr ( self , e) ;
159
- let ty = self . cx . tables . node_type ( callee. hir_id ) ;
160
- match ty. sty {
161
- ty:: FnDef ( ..) | ty:: FnPtr ( _) => {
162
- let sig = ty. fn_sig ( self . cx . tcx ) ;
163
- if sig. skip_binder ( ) . output ( ) . sty == ty:: Never {
164
- self . divergence += 1 ;
165
- }
166
- } ,
167
- _ => ( ) ,
168
- }
169
- } ,
170
- ExprKind :: Closure ( .., _) => ( ) ,
171
- ExprKind :: Binary ( op, _, _) => {
172
- walk_expr ( self , e) ;
173
- match op. node {
174
- BinOpKind :: And | BinOpKind :: Or => self . short_circuits += 1 ,
175
- _ => ( ) ,
117
+ self . cc += 1 ;
176
118
}
119
+ self . cc += arms. iter ( ) . filter ( |arm| arm. guard . is_some ( ) ) . count ( ) as u64 ;
177
120
} ,
178
121
ExprKind :: Ret ( _) => self . returns += 1 ,
179
- _ => walk_expr ( self , e ) ,
122
+ _ => { } ,
180
123
}
181
124
}
182
125
fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
183
126
NestedVisitorMap :: None
184
127
}
185
128
}
186
-
187
- #[ cfg( feature = "debugging" ) ]
188
- #[ allow( clippy:: too_many_arguments) ]
189
- fn report_cc_bug (
190
- _: & LateContext < ' _ , ' _ > ,
191
- cc : u64 ,
192
- narms : u64 ,
193
- div : u64 ,
194
- shorts : u64 ,
195
- returns : u64 ,
196
- span : Span ,
197
- _: HirId ,
198
- ) {
199
- span_bug ! (
200
- span,
201
- "Clippy encountered a bug calculating cognitive complexity: cc = {}, arms = {}, \
202
- div = {}, shorts = {}, returns = {}. Please file a bug report.",
203
- cc,
204
- narms,
205
- div,
206
- shorts,
207
- returns
208
- ) ;
209
- }
210
- #[ cfg( not( feature = "debugging" ) ) ]
211
- #[ allow( clippy:: too_many_arguments) ]
212
- fn report_cc_bug (
213
- cx : & LateContext < ' _ , ' _ > ,
214
- cc : u64 ,
215
- narms : u64 ,
216
- div : u64 ,
217
- shorts : u64 ,
218
- returns : u64 ,
219
- span : Span ,
220
- id : HirId ,
221
- ) {
222
- if !is_allowed ( cx, COGNITIVE_COMPLEXITY , id) {
223
- cx. sess ( ) . span_note_without_error (
224
- span,
225
- & format ! (
226
- "Clippy encountered a bug calculating cognitive complexity \
227
- (hide this message with `#[allow(cognitive_complexity)]`): \
228
- cc = {}, arms = {}, div = {}, shorts = {}, returns = {}. \
229
- Please file a bug report.",
230
- cc, narms, div, shorts, returns
231
- ) ,
232
- ) ;
233
- }
234
- }
0 commit comments