Skip to content

Commit 20e5435

Browse files
authored
added ignoreEmptyLines to FileLength and MethodLength checks (#486)
* added ignoreEmptyLines to FileLength and MethodLength checks * *breaking* removed countEmpty from MethodLength (use ignoreEmptyLines) * using token count for threshold in CodeSimilarity
1 parent fca55ec commit 20e5435

15 files changed

+168
-118
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ after_script:
4141
- if [[ "$HAXE_VERSION" == "haxe4" ]]; then (cd src; ../cc-test-reporter upload-coverage); fi
4242

4343
after_success:
44-
- bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
44+
- if [[ "$HAXE_VERSION" == "haxe4" ]]; bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
## dev branch / next version (2.x.x)
44

5-
- New check `CodeSimilarity` to check for similar or identical code blocks ([#479](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/479) + [#480](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/480) + [#484](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/484))
5+
- **Breaking Change** changed `MethodLength.countEmpty` into `ignoreEmptyLines`
6+
- New check `CodeSimilarity` to check for similar or identical code blocks ([#479](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/479) + [#480](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/480) + [#484](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/484) + [#486](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/486))
67
- 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))
78
- New check `AvoidIdentifier` marks identifiers to avoid ([#483](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/483))
89
- New check `ArrowFunction` to check for curlies, nested functions and returns in arrow functions ([#484](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/484))
910
- 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))
1011
- Added coverage upload to codeclimate ([#478](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/478))
12+
- Added `ignoreEmptyLines` in FileLengthCheck to ignore empty lines (default = true) ([#486](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/486))
13+
- Changed default value for `max` in `FileLengthCheck` to 1000 ([#486](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/486))
14+
- Changed `MethodLength` check to use tokentree ([#486](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/486))
1115
- Fixed allow excluding construtor (`new`) via range exclusion ([#479](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/479))
1216
- Refactored build system to use lix ([#478](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/478))
1317
- Refactored / renamed AvoidInlineConditionals to AvoidTernaryOperator ([#479](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/479))

checkstyle.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,6 @@
134134
}
135135
},
136136
{
137-
"props": {
138-
"max": 2000
139-
},
140137
"type": "FileLength"
141138
},
142139
{

resources/checkstyle-schema.json

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,11 @@
910910
"ERROR",
911911
"IGNORE"
912912
],
913+
"propertyOrder": 2
914+
},
915+
"ignoreEmptyLines": {
916+
"description": "ignores or includes empty lines when counting total file length",
917+
"type": "boolean",
913918
"propertyOrder": 1
914919
}
915920
},
@@ -3513,12 +3518,12 @@
35133518
"additionalProperties": false,
35143519
"properties": {
35153520
"thresholdSimilar": {
3516-
"description": "threshold for similar code blocks",
3521+
"description": "maximum number of tokens allowed before detecting similar code blocks",
35173522
"type": "integer",
35183523
"propertyOrder": 2
35193524
},
35203525
"thresholdIdentical": {
3521-
"description": "threshold for identical code blocks",
3526+
"description": "maximum number of tokens allowed before detecting identical code blocks",
35223527
"type": "integer",
35233528
"propertyOrder": 1
35243529
},
@@ -3927,11 +3932,6 @@
39273932
"description": "Checks for long methods. If a method becomes very long it is hard to understand. Therefore long methods should usually be refactored into several individual methods that focus on a specific task.",
39283933
"additionalProperties": false,
39293934
"properties": {
3930-
"countEmpty": {
3931-
"description": "maximum includes empty lines / should ignore empty lines",
3932-
"type": "boolean",
3933-
"propertyOrder": 1
3934-
},
39353935
"max": {
39363936
"description": "maximum number of lines per method (default: 50)",
39373937
"type": "integer",
@@ -3947,6 +3947,11 @@
39473947
"IGNORE"
39483948
],
39493949
"propertyOrder": 2
3950+
},
3951+
"ignoreEmptyLines": {
3952+
"description": "ignores or includes empty lines when counting method length",
3953+
"type": "boolean",
3954+
"propertyOrder": 1
39503955
}
39513956
},
39523957
"type": "object"

resources/default-config.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@
5757
"type": "CodeSimilarity",
5858
"props": {
5959
"severityIdentical": "WARNING",
60-
"thresholdIdentical": 8,
61-
"thresholdSimilar": 12
60+
"thresholdIdentical": 60,
61+
"thresholdSimilar": 120
6262
}
6363
},
6464
{
@@ -192,7 +192,8 @@
192192
{
193193
"type": "FileLength",
194194
"props": {
195-
"max": 2000
195+
"max": 1000,
196+
"ignoreEmptyLines": true
196197
}
197198
},
198199
{
@@ -323,7 +324,7 @@
323324
"type": "MethodLength",
324325
"props": {
325326
"max": 50,
326-
"countEmpty": false
327+
"ignoreEmptyLines": true
327328
}
328329
},
329330
{

src/checkstyle/checks/coding/CodeSimilarityCheck.hx

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ class CodeSimilarityCheck extends Check {
2828
public var severityIdentical:SeverityLevel;
2929

3030
/**
31-
threshold for identical code blocks
31+
maximum number of tokens allowed before detecting identical code blocks
3232
**/
3333
public var thresholdIdentical:Int;
3434

3535
/**
36-
threshold for similar code blocks
36+
maximum number of tokens allowed before detecting similar code blocks
3737
**/
3838
public var thresholdSimilar:Int;
3939

4040
public function new() {
4141
super(TOKEN);
4242
severityIdentical = WARNING;
43-
thresholdIdentical = 8;
44-
thresholdSimilar = 12;
43+
thresholdIdentical = 60;
44+
thresholdSimilar = 120;
4545
categories = [STYLE, DUPLICATION];
4646
}
4747

@@ -82,10 +82,9 @@ class CodeSimilarityCheck extends Check {
8282

8383
var lineStart:LinePos = checker.getLinePos(pos.min);
8484
var lineEnd:LinePos = checker.getLinePos(pos.max);
85-
var lines:Int = lineEnd.line - lineStart.line;
86-
if (lines <= Math.min(thresholdIdentical, thresholdSimilar)) return false;
8785

8886
var hashes:CodeHashes = makeCodeHashes(token);
87+
if (hashes.tokenCount <= Math.min(thresholdIdentical, thresholdSimilar)) return false;
8988
var codeBlock:HashedCodeBlock = {
9089
fileName: token.pos.file,
9190
lineStart: lineStart,
@@ -94,15 +93,15 @@ class CodeSimilarityCheck extends Check {
9493
endColumn: offsetToColumn(lineEnd)
9594
}
9695

97-
if (lines > thresholdIdentical) {
96+
if (hashes.tokenCount > thresholdIdentical) {
9897
var existing:Null<HashedCodeBlock> = checkOrAddHash(hashes.identicalHash, codeBlock, IDENTICAL_HASHES);
9998
if (existing != null) {
100-
logRange("Found identical code block - " + formatFirstFound(existing), pos.min, pos.max, SIMILAR_BLOCK, ERROR);
99+
logRange("Found identical code block - " + formatFirstFound(existing), pos.min, pos.max, SIMILAR_BLOCK, severityIdentical);
101100
return true;
102101
}
103102
}
104103

105-
if (lines > thresholdSimilar) {
104+
if (hashes.tokenCount > thresholdSimilar) {
106105
var existing:Null<HashedCodeBlock> = checkOrAddHash(hashes.similarHash, codeBlock, SIMILAR_HASHES);
107106
if (existing == null) return false;
108107
logRange("Found similar code block - " + formatFirstFound(existing), pos.min, pos.max, SIMILAR_BLOCK);
@@ -126,19 +125,26 @@ class CodeSimilarityCheck extends Check {
126125
function makeCodeHashes(token:TokenTree):CodeHashes {
127126
var similar:StringBuf = new StringBuf();
128127
var identical:StringBuf = new StringBuf();
129-
makeCodeHashesRecursive(token, similar, identical);
128+
var tokenCount:Int = makeCodeHashesRecursive(token, similar, identical);
130129
return {
131130
identicalHash: identical.toString(),
132-
similarHash: similar.toString()
131+
similarHash: similar.toString(),
132+
tokenCount: tokenCount
133133
};
134134
}
135135

136-
function makeCodeHashesRecursive(token:TokenTree, similar:StringBuf, identical:StringBuf) {
136+
function makeCodeHashesRecursive(token:TokenTree, similar:StringBuf, identical:StringBuf):Int {
137137
similar.add(similarTokenText(token));
138-
identical.add(identicalTokenText(token));
138+
var count:Int = 0;
139+
var identicalText:Null<String> = identicalTokenText(token);
140+
if (identicalText != null) {
141+
count++;
142+
identical.add(identicalText);
143+
}
139144
if (token.children != null) {
140-
for (child in token.children) makeCodeHashesRecursive(child, similar, identical);
145+
for (child in token.children) count += makeCodeHashesRecursive(child, similar, identical);
141146
}
147+
return count;
142148
}
143149

144150
function similarTokenText(token:TokenTree):String {
@@ -184,7 +190,7 @@ class CodeSimilarityCheck extends Check {
184190
}
185191
}
186192

187-
function identicalTokenText(token:TokenTree):String {
193+
function identicalTokenText(token:TokenTree):Null<String> {
188194
switch (token.tok) {
189195
case Const(CFloat(f)):
190196
return '$f';
@@ -203,9 +209,9 @@ class CodeSimilarityCheck extends Check {
203209
case Binop(op):
204210
return '$op';
205211
case Comment(_):
206-
return "";
212+
return null;
207213
case CommentLine(_):
208-
return "";
214+
return null;
209215
case IntInterval(i):
210216
return '...$i';
211217
default:
@@ -241,4 +247,5 @@ typedef HashedCodeBlock = {
241247
typedef CodeHashes = {
242248
var identicalHash:String;
243249
var similarHash:String;
250+
var tokenCount:Int;
244251
}

src/checkstyle/checks/size/FileLengthCheck.hx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,51 @@
11
package checkstyle.checks.size;
22

3+
import checkstyle.checks.whitespace.ListOfEmptyLines;
4+
35
/**
46
Checks for long source files. If a source file becomes very long it is hard to understand.
57
Therefore long classes should usually be refactored into several individual classes that focus on a specific task.
68
**/
79
@name("FileLength")
810
@desc("Checks for long source files. If a source file becomes very long it is hard to understand. Therefore long classes should usually be refactored into several individual classes that focus on a specific task.")
911
class FileLengthCheck extends Check {
10-
static var DEFAULT_MAX_LENGTH:Int = 2000;
12+
static var DEFAULT_MAX_LENGTH:Int = 1000;
1113

1214
/**
1315
maximum number of lines permitted per file (default: 2000)
1416
**/
1517
public var max:Int;
1618

19+
/**
20+
ignores or includes empty lines when counting total file length
21+
**/
22+
public var ignoreEmptyLines:Bool;
23+
1724
public function new() {
1825
super(LINE);
1926
max = DEFAULT_MAX_LENGTH;
27+
ignoreEmptyLines = true;
2028
categories = [Category.COMPLEXITY, Category.CLARITY];
2129
points = 21;
2230
}
2331

2432
override function actualRun() {
2533
if (checker.ast == null) return;
34+
2635
for (td in checker.ast.decls) {
2736
switch (td.decl) {
2837
case EClass(d):
2938
for (field in d.data) if (isCheckSuppressed(field)) return;
3039
default:
3140
}
3241
}
33-
if (checker.lines.length > max) {
42+
43+
var count = checker.lines.length;
44+
if (ignoreEmptyLines) {
45+
var emptyLines:ListOfEmptyLines = ListOfEmptyLines.detectEmptyLines(checker);
46+
count -= emptyLines.lines.length;
47+
}
48+
if (count > max) {
3449
log('File length is ${checker.lines.length} lines (max allowed is ${max})', checker.lines.length, 0, checker.lines.length, 0);
3550
}
3651
}
@@ -40,7 +55,7 @@ class FileLengthCheck extends Check {
4055
fixed: [],
4156
properties: [{
4257
propertyName: "max",
43-
values: [for (i in 0...10) 400 + i * 200]
58+
values: [for (i in 0...10) 400 + i * 100]
4459
}]
4560
}];
4661
}

0 commit comments

Comments
 (0)