From a4bde32e7eb5355ec07e5ec20f5de660c25a17d1 Mon Sep 17 00:00:00 2001 From: AlexHaxe Date: Thu, 24 Oct 2019 21:44:23 +0200 Subject: [PATCH] Nested control flow (#485) * new check NestedControlFlow --- CHANGELOG.md | 1 + resources/checkstyle-excludes-schema.json | 72 +++++----- resources/checkstyle-schema.json | 112 +++++++++++----- resources/default-config.json | 6 + .../checks/coding/NestedControlFlowCheck.hx | 89 +++++++++++++ .../checks/coding/UnusedLocalVarCheck.hx | 2 +- .../coding/NestedControlFlowCheckTest.hx | 123 ++++++++++++++++++ test/checkstyle/config/ConfigParserTest.hx | 9 +- .../detect/DetectCodingStyleTest.hx | 10 ++ 9 files changed, 351 insertions(+), 73 deletions(-) create mode 100644 src/checkstyle/checks/coding/NestedControlFlowCheck.hx create mode 100644 test/checkstyle/checks/coding/NestedControlFlowCheckTest.hx diff --git a/CHANGELOG.md b/CHANGELOG.md index 50ac6eca..411ba565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - New check `EnforceVarTypeHint` to enforce type hints for all variables and finals, fixes [#464](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/464) ([#481](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/481) + [#482](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/482)) - New check `AvoidIdentifier` marks identifiers to avoid ([#483](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/483)) - New check `ArrowFunction` to check for curlies, nested functions and returns in arrow functions ([#484](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/484)) +- New check `NestedControlFlow` to check for nested control flow expressions (e.g. `if`, `for`, `while`, `do/while`, `switch` and `try`) ([#485](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/485)) - Added coverage upload to codeclimate ([#478](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/478)) - Fixed allow excluding construtor (`new`) via range exclusion ([#479](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/479)) - Refactored build system to use lix ([#478](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/478)) diff --git a/resources/checkstyle-excludes-schema.json b/resources/checkstyle-excludes-schema.json index a335e54d..1fbf7c1a 100644 --- a/resources/checkstyle-excludes-schema.json +++ b/resources/checkstyle-excludes-schema.json @@ -18,7 +18,7 @@ }, "UnusedImport": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 77 + "propertyOrder": 78 }, "Dynamic": { "$ref": "#/definitions/ExcludeFilterList", @@ -38,7 +38,7 @@ }, "SeparatorWhitespace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 63 + "propertyOrder": 64 }, "all": { "$ref": "#/definitions/ExcludeFilterList" @@ -49,7 +49,7 @@ }, "NestedForDepth": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 48 + "propertyOrder": 49 }, "Anonymous": { "$ref": "#/definitions/ExcludeFilterList", @@ -61,7 +61,7 @@ }, "NestedTryDepth": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 50 + "propertyOrder": 51 }, "BlockBreakingConditional": { "$ref": "#/definitions/ExcludeFilterList", @@ -81,7 +81,7 @@ }, "ReturnCount": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 61 + "propertyOrder": 62 }, "DefaultComesLast": { "$ref": "#/definitions/ExcludeFilterList", @@ -93,7 +93,7 @@ }, "WhitespaceAfter": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 80 + "propertyOrder": 81 }, "ConstantName": { "$ref": "#/definitions/ExcludeFilterList", @@ -109,19 +109,19 @@ }, "WhitespaceAround": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 81 + "propertyOrder": 82 }, "NestedIfDepth": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 49 + "propertyOrder": 50 }, "ParameterName": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 54 + "propertyOrder": 55 }, "NullableParameter": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 51 + "propertyOrder": 52 }, "InlineFinal": { "$ref": "#/definitions/ExcludeFilterList", @@ -133,7 +133,7 @@ }, "SeparatorWrap": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 64 + "propertyOrder": 65 }, "AvoidIdentifier": { "$ref": "#/definitions/ExcludeFilterList", @@ -141,7 +141,7 @@ }, "RedundantAllowMeta": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 58 + "propertyOrder": 59 }, "HiddenField": { "$ref": "#/definitions/ExcludeFilterList", @@ -149,11 +149,11 @@ }, "UnnecessaryConstructor": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 76 + "propertyOrder": 77 }, "SimplifyBooleanExpression": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 65 + "propertyOrder": 66 }, "path": { "description": "filters excludes relative to\n\t- RELATIVE_TO_PROJECT = use project root\n\t- RELATIVE_TO_SOURCE = use path(s) specified via \"-s \" command line switches", @@ -169,11 +169,11 @@ }, "UnusedLocalVar": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 78 + "propertyOrder": 79 }, "TabForAligning": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 70 + "propertyOrder": 71 }, "MethodName": { "$ref": "#/definitions/ExcludeFilterList", @@ -181,7 +181,7 @@ }, "RightCurly": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 62 + "propertyOrder": 63 }, "AvoidTernaryOperator": { "$ref": "#/definitions/ExcludeFilterList", @@ -189,7 +189,7 @@ }, "ParameterNumber": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 55 + "propertyOrder": 56 }, "ArrowFunction": { "$ref": "#/definitions/ExcludeFilterList", @@ -217,15 +217,15 @@ }, "RedundantModifier": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 59 + "propertyOrder": 60 }, "Type": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 73 + "propertyOrder": 74 }, "TypeName": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 75 + "propertyOrder": 76 }, "Indentation": { "$ref": "#/definitions/ExcludeFilterList", @@ -238,23 +238,27 @@ }, "Return": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 60 + "propertyOrder": 61 }, "SimplifyBooleanReturn": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 66 + "propertyOrder": 67 }, "TODOComment": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 69 + "propertyOrder": 70 + }, + "NestedControlFlow": { + "$ref": "#/definitions/ExcludeFilterList", + "propertyOrder": 48 }, "OperatorWhitespace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 52 + "propertyOrder": 53 }, "Spacing": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 67 + "propertyOrder": 68 }, "IndentationCharacter": { "$ref": "#/definitions/ExcludeFilterList", @@ -270,11 +274,11 @@ }, "TrailingWhitespace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 72 + "propertyOrder": 73 }, "VariableInitialisation": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 79 + "propertyOrder": 80 }, "LineLength": { "$ref": "#/definitions/ExcludeFilterList", @@ -294,11 +298,11 @@ }, "StringLiteral": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 68 + "propertyOrder": 69 }, "Trace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 71 + "propertyOrder": 72 }, "ArrayAccess": { "$ref": "#/definitions/ExcludeFilterList", @@ -306,7 +310,7 @@ }, "PublicAccessor": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 56 + "propertyOrder": 57 }, "DocCommentStyle": { "$ref": "#/definitions/ExcludeFilterList", @@ -318,7 +322,7 @@ }, "RedundantAccessMeta": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 57 + "propertyOrder": 58 }, "CodeSimilarity": { "$ref": "#/definitions/ExcludeFilterList", @@ -326,11 +330,11 @@ }, "OperatorWrap": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 53 + "propertyOrder": 54 }, "TypeDocComment": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 74 + "propertyOrder": 75 }, "LeftCurly": { "$ref": "#/definitions/ExcludeFilterList", diff --git a/resources/checkstyle-schema.json b/resources/checkstyle-schema.json index 901054df..cf86fac1 100644 --- a/resources/checkstyle-schema.json +++ b/resources/checkstyle-schema.json @@ -2778,6 +2778,43 @@ }, "type": "object" }, + "NestedControlFlowCheck": { + "description": "Checks for maximium nesting depth of control flow expressions (`if`, `for`, `while`, `do/while`, `switch` and `try`).", + "additionalProperties": false, + "properties": { + "type": { + "description": "Checks for maximium nesting depth of control flow expressions (`if`, `for`, `while`, `do/while`, `switch` and `try`).", + "type": "string", + "enum": [ + "NestedControlFlow" + ] + }, + "props": { + "description": "Checks for maximium nesting depth of control flow expressions (`if`, `for`, `while`, `do/while`, `switch` and `try`).", + "additionalProperties": false, + "properties": { + "max": { + "description": "maximum number of nested control flow expressions allowed", + "type": "integer", + "propertyOrder": 0 + }, + "severity": { + "description": "sets gravity of reported violations:\n\t- IGNORE = do not report violations, violations do not appear anywhere in output\n\t- INFO = all violations have info / lowest priority\n\t- WARNING = all violations have warning / medium priority\n\t- ERROR = all violations have error / highest priority", + "type": "string", + "enum": [ + "INFO", + "WARNING", + "ERROR", + "IGNORE" + ], + "propertyOrder": 1 + } + }, + "type": "object" + } + }, + "type": "object" + }, "EmptyLinesCheck": { "description": "Checks for consecutive empty lines (default is 1). Also have options to check empty line separators after package, single-line and multi-line comments and class/interface/abstract declarations.", "additionalProperties": false, @@ -3011,7 +3048,7 @@ }, "UnusedImport": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 77 + "propertyOrder": 78 }, "Dynamic": { "$ref": "#/definitions/ExcludeFilterList", @@ -3031,7 +3068,7 @@ }, "SeparatorWhitespace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 63 + "propertyOrder": 64 }, "all": { "$ref": "#/definitions/ExcludeFilterList" @@ -3042,7 +3079,7 @@ }, "NestedForDepth": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 48 + "propertyOrder": 49 }, "Anonymous": { "$ref": "#/definitions/ExcludeFilterList", @@ -3054,7 +3091,7 @@ }, "NestedTryDepth": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 50 + "propertyOrder": 51 }, "BlockBreakingConditional": { "$ref": "#/definitions/ExcludeFilterList", @@ -3074,7 +3111,7 @@ }, "ReturnCount": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 61 + "propertyOrder": 62 }, "DefaultComesLast": { "$ref": "#/definitions/ExcludeFilterList", @@ -3086,7 +3123,7 @@ }, "WhitespaceAfter": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 80 + "propertyOrder": 81 }, "ConstantName": { "$ref": "#/definitions/ExcludeFilterList", @@ -3102,19 +3139,19 @@ }, "WhitespaceAround": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 81 + "propertyOrder": 82 }, "NestedIfDepth": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 49 + "propertyOrder": 50 }, "ParameterName": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 54 + "propertyOrder": 55 }, "NullableParameter": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 51 + "propertyOrder": 52 }, "InlineFinal": { "$ref": "#/definitions/ExcludeFilterList", @@ -3126,7 +3163,7 @@ }, "SeparatorWrap": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 64 + "propertyOrder": 65 }, "AvoidIdentifier": { "$ref": "#/definitions/ExcludeFilterList", @@ -3134,7 +3171,7 @@ }, "RedundantAllowMeta": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 58 + "propertyOrder": 59 }, "HiddenField": { "$ref": "#/definitions/ExcludeFilterList", @@ -3142,11 +3179,11 @@ }, "UnnecessaryConstructor": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 76 + "propertyOrder": 77 }, "SimplifyBooleanExpression": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 65 + "propertyOrder": 66 }, "path": { "description": "filters excludes relative to\n\t- RELATIVE_TO_PROJECT = use project root\n\t- RELATIVE_TO_SOURCE = use path(s) specified via \"-s \" command line switches", @@ -3162,11 +3199,11 @@ }, "UnusedLocalVar": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 78 + "propertyOrder": 79 }, "TabForAligning": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 70 + "propertyOrder": 71 }, "MethodName": { "$ref": "#/definitions/ExcludeFilterList", @@ -3174,7 +3211,7 @@ }, "RightCurly": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 62 + "propertyOrder": 63 }, "AvoidTernaryOperator": { "$ref": "#/definitions/ExcludeFilterList", @@ -3182,7 +3219,7 @@ }, "ParameterNumber": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 55 + "propertyOrder": 56 }, "ArrowFunction": { "$ref": "#/definitions/ExcludeFilterList", @@ -3210,15 +3247,15 @@ }, "RedundantModifier": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 59 + "propertyOrder": 60 }, "Type": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 73 + "propertyOrder": 74 }, "TypeName": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 75 + "propertyOrder": 76 }, "Indentation": { "$ref": "#/definitions/ExcludeFilterList", @@ -3231,23 +3268,27 @@ }, "Return": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 60 + "propertyOrder": 61 }, "SimplifyBooleanReturn": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 66 + "propertyOrder": 67 }, "TODOComment": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 69 + "propertyOrder": 70 + }, + "NestedControlFlow": { + "$ref": "#/definitions/ExcludeFilterList", + "propertyOrder": 48 }, "OperatorWhitespace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 52 + "propertyOrder": 53 }, "Spacing": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 67 + "propertyOrder": 68 }, "IndentationCharacter": { "$ref": "#/definitions/ExcludeFilterList", @@ -3263,11 +3304,11 @@ }, "TrailingWhitespace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 72 + "propertyOrder": 73 }, "VariableInitialisation": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 79 + "propertyOrder": 80 }, "LineLength": { "$ref": "#/definitions/ExcludeFilterList", @@ -3287,11 +3328,11 @@ }, "StringLiteral": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 68 + "propertyOrder": 69 }, "Trace": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 71 + "propertyOrder": 72 }, "ArrayAccess": { "$ref": "#/definitions/ExcludeFilterList", @@ -3299,7 +3340,7 @@ }, "PublicAccessor": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 56 + "propertyOrder": 57 }, "DocCommentStyle": { "$ref": "#/definitions/ExcludeFilterList", @@ -3311,7 +3352,7 @@ }, "RedundantAccessMeta": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 57 + "propertyOrder": 58 }, "CodeSimilarity": { "$ref": "#/definitions/ExcludeFilterList", @@ -3319,11 +3360,11 @@ }, "OperatorWrap": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 53 + "propertyOrder": 54 }, "TypeDocComment": { "$ref": "#/definitions/ExcludeFilterList", - "propertyOrder": 74 + "propertyOrder": 75 }, "LeftCurly": { "$ref": "#/definitions/ExcludeFilterList", @@ -4313,6 +4354,9 @@ { "$ref": "#/definitions/NeedBracesCheck" }, + { + "$ref": "#/definitions/NestedControlFlowCheck" + }, { "$ref": "#/definitions/NestedForDepthCheck" }, diff --git a/resources/default-config.json b/resources/default-config.json index 435cba88..01cb4f54 100644 --- a/resources/default-config.json +++ b/resources/default-config.json @@ -373,6 +373,12 @@ "allowSingleLineStatement": true } }, + { + "type": "NestedControlFlow", + "props": { + "max": 3 + } + }, { "type": "NestedForDepth", "props": { diff --git a/src/checkstyle/checks/coding/NestedControlFlowCheck.hx b/src/checkstyle/checks/coding/NestedControlFlowCheck.hx new file mode 100644 index 00000000..f33e154f --- /dev/null +++ b/src/checkstyle/checks/coding/NestedControlFlowCheck.hx @@ -0,0 +1,89 @@ +package checkstyle.checks.coding; + +/** + Checks for maximium nesting depth of control flow expressions (`if`, `for`, `while`, `do/while`, `switch` and `try`). +**/ +@name("NestedControlFlow") +@desc("Checks for maximium nesting depth of control flow expressions (`if`, `for`, `while`, `do/while`, `switch` and `try`).") +class NestedControlFlowCheck extends Check { + /** + maximum number of nested control flow expressions allowed + **/ + public var max:Int; + + public function new() { + super(TOKEN); + max = 3; + categories = [Category.COMPLEXITY]; + points = 8; + } + + override function actualRun() { + var root:TokenTree = checker.getTokenTree(); + var controlFlowTokens:Array = root.filterCallback(function(token:TokenTree, index:Int):FilterResult { + return switch (token.tok) { + case Kwd(KwdFor): + FOUND_GO_DEEPER; + case Kwd(KwdIf): + FOUND_GO_DEEPER; + case Kwd(KwdSwitch): + FOUND_GO_DEEPER; + case Kwd(KwdDo): + FOUND_GO_DEEPER; + case Kwd(KwdWhile): + if ((token.parent != null) && (token.parent.is(Kwd(KwdDo)))) GO_DEEPER; else FOUND_GO_DEEPER; + case Kwd(KwdTry): + FOUND_GO_DEEPER; + default: + GO_DEEPER; + } + }); + for (token in controlFlowTokens) { + if (isPosSuppressed(token.pos)) continue; + checkExpressionDepth(token); + } + } + + function checkExpressionDepth(token:TokenTree) { + var depth:Int = calcDepth(token); + if (depth > max) warnNestedDepth(depth, token.getPos()); + } + + function calcDepth(token:TokenTree):Int { + var parent:TokenTree = token.parent; + var count:Int = 1; + while ((parent != null) && (parent.tok != null)) { + switch (parent.tok) { + case Kwd(KwdFor): + count++; + case Kwd(KwdIf): + count++; + case Kwd(KwdSwitch): + count++; + case Kwd(KwdDo): + count++; + case Kwd(KwdWhile): + if ((parent.parent == null) || (!parent.parent.is(Kwd(KwdDo)))) count++; + case Kwd(KwdTry): + count++; + default: + } + parent = parent.parent; + } + return count; + } + + function warnNestedDepth(depth:Int, pos:Position) { + logPos('Nested control flow depth is $depth (max allowed is ${max})', pos); + } + + override public function detectableInstances():DetectableInstances { + return [{ + fixed: [], + properties: [{ + propertyName: "max", + values: [for (i in 1...5) i] + }] + }]; + } +} \ No newline at end of file diff --git a/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx b/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx index c74bf20c..42e7e9b1 100644 --- a/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx +++ b/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx @@ -77,7 +77,7 @@ class UnusedLocalVarCheck extends Check { }); if (nameList.length > 1) return; - logPos('Unused local variable $name', v.parent.getPos()); + logPos('Unused local variable $name', v.pos); } function checkStringInterpolation(tok:TokenTree, name:String, s:String):FilterResult { diff --git a/test/checkstyle/checks/coding/NestedControlFlowCheckTest.hx b/test/checkstyle/checks/coding/NestedControlFlowCheckTest.hx new file mode 100644 index 00000000..db3e35d6 --- /dev/null +++ b/test/checkstyle/checks/coding/NestedControlFlowCheckTest.hx @@ -0,0 +1,123 @@ +package checkstyle.checks.coding; + +class NestedControlFlowCheckTest extends CheckTestCase { + @Test + public function testDefault() { + var check = new NestedControlFlowCheck(); + assertNoMsg(check, COMPLIANT_NESTING); + } + + @Test + public function testDefaultTooMany() { + var check = new NestedControlFlowCheck(); + assertMsg(check, TOO_MANY_FORS, "Nested control flow depth is 4 (max allowed is 3)"); + assertMsg(check, NESTED_IF_FOR, "Nested control flow depth is 4 (max allowed is 3)"); + assertMsg(check, NESTED_TRY_SWITCH_WHILE, "Nested control flow depth is 4 (max allowed is 3)"); + assertMsg(check, NESTED_TRY_SWITCH_DO_WHILE, "Nested control flow depth is 4 (max allowed is 3)"); + } + + @Test + public function testMaxParameter() { + var check = new NestedControlFlowCheck(); + check.max = 4; + + assertNoMsg(check, COMPLIANT_NESTING); + assertNoMsg(check, TOO_MANY_FORS); + assertNoMsg(check, NESTED_IF_FOR); + assertNoMsg(check, NESTED_TRY_SWITCH_WHILE); + assertNoMsg(check, NESTED_TRY_SWITCH_DO_WHILE); + + check.max = 1; + assertNoMsg(check, COMPLIANT_NESTING); + assertMsg(check, TOO_MANY_FORS, "Nested control flow depth is 4 (max allowed is 1)"); + } +} + +@:enum +abstract NestedControlFlowCheckTests(String) to String { + var COMPLIANT_NESTING = " + abstractAndClass Test { + public function test(param:Int):Void { + if (param == 0) return 0; // level 0 + if (param == 1) { // level 0 + return 1; + } + else { + return 2; + } + for (outerParam in params) trace(outerParam); + } + + @SuppressWarnings('checkstyle:NestedControlFlow') + public function test1(param:Int) { + for (outerParam in params) { // level 1 + for (middleParam in params) { // level 2 + for (innerParam in params) { // level 3 + if (outerParam == innerParam) { // level 4 + trace (param); + } + } + } + } + } + }"; + var TOO_MANY_FORS = " + abstractAndClass Test { + public function test1(param:Int) { + for (outerParam in params) { // level 1 + for (middleParam in params) { // level 2 + for (innerParam in params) { // level 3 + if (innerParam == null) { // level 4 + } + } + } + } + } + }"; + var NESTED_IF_FOR = " + abstractAndClass Test { + public function test1(param:Int) { + if (outerParam != null) { // level 1 + for (middleParam in params) { // level 2 + if (innerParam == null) { // level 3 + if (innerParam == null) { // level 4 + } + } + } + } + } + }"; + var NESTED_TRY_SWITCH_WHILE = " + abstractAndClass Test { + public function test1(param:Int) { + try { // level 1 + switch (param) { // level 2 + case 1: + while (true) { // level 3 + if (outerParam == innerParam) { // level 4 + trace (param); + } + } + } + } + catch (e:Any) {} + } + }"; + var NESTED_TRY_SWITCH_DO_WHILE = " + abstractAndClass Test { + public function test1(param:Int) { + try { // level 1 + switch (param) { // level 2 + case 1: + do { // level 3 + if (outerParam == innerParam) { // level 4 + trace (param); + } + } + while (true); + } + } + catch (e:Any) {} + } + }"; +} \ No newline at end of file diff --git a/test/checkstyle/config/ConfigParserTest.hx b/test/checkstyle/config/ConfigParserTest.hx index 67611334..2ce8ceea 100644 --- a/test/checkstyle/config/ConfigParserTest.hx +++ b/test/checkstyle/config/ConfigParserTest.hx @@ -4,6 +4,7 @@ import checkstyle.utils.ConfigUtils; class ConfigParserTest { static inline var LOCAL_PATH:String = "./"; + static inline var TEST_COUNT:Int = 79; @Test public function testCheckstyleConfig() { @@ -58,9 +59,9 @@ class ConfigParserTest { var configParser:ConfigParser = new ConfigParser(reportConfigParserFailure); #if haxe4 - Assert.areEqual(78, configParser.getCheckCount()); + Assert.areEqual(TEST_COUNT, configParser.getCheckCount()); #else - Assert.areEqual(77, configParser.getCheckCount()); + Assert.areEqual(TEST_COUNT - 1, configParser.getCheckCount()); #end } @@ -71,9 +72,9 @@ class ConfigParserTest { Assert.areEqual(0, configParser.getUsedCheckCount()); configParser.addAllChecks(); #if haxe4 - Assert.areEqual(78, configParser.getUsedCheckCount()); + Assert.areEqual(TEST_COUNT, configParser.getUsedCheckCount()); #else - Assert.areEqual(77, configParser.getUsedCheckCount()); + Assert.areEqual(TEST_COUNT - 1, configParser.getUsedCheckCount()); #end } diff --git a/test/checkstyle/detect/DetectCodingStyleTest.hx b/test/checkstyle/detect/DetectCodingStyleTest.hx index d89942bb..77d8c77f 100644 --- a/test/checkstyle/detect/DetectCodingStyleTest.hx +++ b/test/checkstyle/detect/DetectCodingStyleTest.hx @@ -12,6 +12,7 @@ import checkstyle.checks.coding.ArrowFunctionCheck; import checkstyle.checks.coding.CodeSimilarityCheck; import checkstyle.checks.coding.HiddenFieldCheck; import checkstyle.checks.coding.InnerAssignmentCheck; +import checkstyle.checks.coding.NestedControlFlowCheck; import checkstyle.checks.coding.NestedForDepthCheck; import checkstyle.checks.coding.NestedIfDepthCheck; import checkstyle.checks.coding.NestedTryDepthCheck; @@ -149,6 +150,15 @@ class DetectCodingStyleTest { Assert.areEqual(0, detectedChecks.length); } + @Test + public function testDetectNestedControlFlowC() { + var detectedChecks:Array = DetectCodingStyle.detectCodingStyle([new NestedControlFlowCheck()], [buildCheckFile(SAMPLE_CODING_STYLE)]); + Assert.areEqual(1, detectedChecks.length); + Assert.areEqual("NestedControlFlow", detectedChecks[0].type); + var props = cast detectedChecks[0].props; + Assert.areEqual(3, props.max); + } + @Test public function testDetectNestedForDepth() { var detectedChecks:Array = DetectCodingStyle.detectCodingStyle([new NestedForDepthCheck()], [buildCheckFile(SAMPLE_CODING_STYLE)]);