1+ package checkstyle .checks ;
2+
3+ import haxeparser .Data .TypeDecl ;
4+ import haxeparser .Data .TypeDef ;
5+ import haxeparser .Data .Definition ;
6+ import haxeparser .Data .ClassFlag ;
7+ import checkstyle .LintMessage .SeverityLevel ;
8+ import haxe .macro .Expr ;
9+
10+ using Lambda ;
11+
12+ @name (" CyclomaticComplexity" )
13+ @desc (" McCabe simplified cyclomatic complexity check" )
14+ class CyclomaticComplexityCheck extends Check {
15+
16+ public var thresholds : Array <Threshold >;
17+
18+ override function _actualRun () {
19+ _checker .ast .decls .map (function (type : TypeDecl ): Null <Definition <ClassFlag , Array <Field >>> {
20+ return switch (type .decl ) {
21+ case TypeDef . EClass (definition ): definition ;
22+ default : null ;
23+ }
24+ }).filter (function (definition ): Bool {
25+ return definition != null ;
26+ }).iter (checkFields );
27+ }
28+
29+ function checkFields (definition : Definition <ClassFlag , Array <Field >>) {
30+ definition .data .map (function (field : Field ): Null <Target > {
31+ return switch (field .kind ) {
32+ case FieldType . FFun (f ): {name :field .name , expr :f .expr , pos :field .pos };
33+ default : null ;
34+ }
35+ }).filter (function (f : Null <Target >): Bool {
36+ return f != null ;
37+ }).iter (calculateComplexity );
38+ }
39+
40+ function calculateComplexity (method : Target ) {
41+ var complexity : Int = 1 + evaluateExpr (method .expr );
42+
43+ var risk : Null <Threshold > = thresholds .filter (function (t : Threshold ): Bool {
44+ return complexity >= t .complexity ;
45+ }).pop ();
46+
47+ if (risk != null ) {
48+ notify (method , complexity , risk );
49+ }
50+ }
51+
52+ // This would not pass the cyclomatic complexity test.
53+ function evaluateExpr (e : Expr ): Int {
54+ if (e == null ) {
55+ return 0 ;
56+ }
57+ return switch (e .expr ) {
58+ case ExprDef . EArray (e1 , e2 ) : evaluateExpr (e1 ) + evaluateExpr (e2 );
59+ case ExprDef . EBinop (op , e1 , e2 ) : evaluateExpr (e1 ) + evaluateExpr (e2 ) + switch (op ) {
60+ case Binop . OpBoolAnd : 1 ;
61+ case Binop . OpBoolOr : 1 ;
62+ default : 0 ;
63+ };
64+ case ExprDef . EParenthesis (e ) : evaluateExpr (e );
65+ case ExprDef . EObjectDecl (fields ) :
66+ fields .map (function (f ): Expr {
67+ return f .expr ;
68+ }).fold (function (e : Expr , total : Int ): Int {
69+ return total + evaluateExpr (e );
70+ }, 0 );
71+ case ExprDef . EArrayDecl (values ) :
72+ values .fold (function (e : Expr , total : Int ): Int {
73+ return total + evaluateExpr (e );
74+ }, 0 );
75+ case ExprDef . EBlock (exprs ) :
76+ exprs .fold (function (e : Expr , total : Int ): Int {
77+ return total + evaluateExpr (e );
78+ }, 0 );
79+ case ExprDef . EFor (it , e ) : 1 + evaluateExpr (it ) + evaluateExpr (e );
80+ case ExprDef . EIn (e1 , e2 ) : evaluateExpr (e1 ) + evaluateExpr (e2 );
81+ case ExprDef . EIf (econd , eif , eelse ) : 1 + evaluateExpr (econd ) + evaluateExpr (eif ) + evaluateExpr (eelse );
82+ case ExprDef . EWhile (econd , e , _ ) : 1 + evaluateExpr (econd ) + evaluateExpr (e );
83+ case ExprDef . ESwitch (e , cases , def ) :
84+ evaluateExpr (def ) + cases .map (function (c : Case ): Expr {
85+ return c .expr ;
86+ }).fold (function (e : Expr , total : Int ): Int {
87+ return total + 1 + evaluateExpr (e );
88+ }, 0 );
89+ case ExprDef . ETry (e , catches ) :
90+ catches .map (function (c : Catch ): Expr {
91+ return c .expr ;
92+ }).fold (function (e : Expr , total : Int ): Int {
93+ return total + 1 + evaluateExpr (e );
94+ }, 0 );
95+ case ExprDef . EReturn (e ) : (e != null ) ? evaluateExpr (e ) : 0 ;
96+ case ExprDef . EUntyped (e ) : evaluateExpr (e );
97+ case ExprDef . EThrow (e ) : evaluateExpr (e );
98+ case ExprDef . ECast (e , _ ) : evaluateExpr (e );
99+ case ExprDef . EDisplay (e , _ ) : evaluateExpr (e );
100+ case ExprDef . ETernary (econd , eif , eelse ) : 1 + evaluateExpr (econd ) + evaluateExpr (eif ) + evaluateExpr (eelse );
101+ case ExprDef . ECheckType (e , _ ) : evaluateExpr (e );
102+ default : 0 ;
103+ }
104+ }
105+
106+ function notify (method : Target , complexity : Int , risk : Threshold ) {
107+ logPos (' Function \" ${method .name }\" is too complex (score: $complexity ).' , method .pos , Reflect .field (SeverityLevel , risk .severity ));
108+ }
109+ }
110+
111+ typedef Target = {
112+ var name : String ;
113+ var expr : Expr ;
114+ var pos : Position ;
115+ }
116+ typedef Threshold = {
117+ var severity : String ;
118+ var complexity : Int ;
119+ }
0 commit comments