11//! calculate cognitive complexity and warn about overly complex functions
22
3- use rustc:: cfg:: CFG ;
43use rustc:: hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
54use rustc:: hir:: * ;
65use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintContext , LintPass } ;
7- use rustc:: ty;
86use rustc:: { declare_tool_lint, impl_lint_pass} ;
97use syntax:: ast:: Attribute ;
108use syntax:: source_map:: Span ;
119
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 } ;
1311
1412declare_clippy_lint ! {
1513 /// **What it does:** Checks for methods with high cognitive complexity.
@@ -46,30 +44,11 @@ impl CognitiveComplexity {
4644 return ;
4745 }
4846
49- let cfg = CFG :: new ( cx. tcx , body) ;
5047 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 } ;
6550 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;
7352 let ret_ty = cx. tables . node_type ( expr. hir_id ) ;
7453 let ret_adjust = if match_type ( cx, ret_ty, & paths:: RESULT ) {
7554 returns
@@ -78,36 +57,23 @@ impl CognitiveComplexity {
7857 ( returns / 2 )
7958 } ;
8059
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 (
8367 cx,
84- cc,
85- match_arms,
86- divergence,
87- short_circuits,
88- ret_adjust,
68+ COGNITIVE_COMPLEXITY ,
8969 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" ,
9176 ) ;
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- }
11177 }
11278 }
11379}
@@ -136,99 +102,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity {
136102 }
137103}
138104
139- struct CCHelper < ' a , ' tcx > {
140- match_arms : u64 ,
141- divergence : u64 ,
105+ struct CCHelper {
106+ cc : u64 ,
142107 returns : u64 ,
143- short_circuits : u64 , // && and ||
144- cx : & ' a LateContext < ' a , ' tcx > ,
145108}
146109
147- impl < ' a , ' tcx > Visitor < ' tcx > for CCHelper < ' a , ' tcx > {
110+ impl < ' tcx > Visitor < ' tcx > for CCHelper {
148111 fn visit_expr ( & mut self , e : & ' tcx Expr ) {
112+ walk_expr ( self , e) ;
149113 match e. node {
150114 ExprKind :: Match ( _, ref arms, _) => {
151- walk_expr ( self , e) ;
152115 let arms_n: u64 = arms. iter ( ) . map ( |arm| arm. pats . len ( ) as u64 ) . sum ( ) ;
153116 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 ;
176118 }
119+ self . cc += arms. iter ( ) . filter ( |arm| arm. guard . is_some ( ) ) . count ( ) as u64 ;
177120 } ,
178121 ExprKind :: Ret ( _) => self . returns += 1 ,
179- _ => walk_expr ( self , e ) ,
122+ _ => { } ,
180123 }
181124 }
182125 fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
183126 NestedVisitorMap :: None
184127 }
185128}
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