From 027311cfd446c9cecf791df16250ce836e26c9b2 Mon Sep 17 00:00:00 2001 From: AlexHaxe Date: Sat, 5 Nov 2016 15:09:24 +0100 Subject: [PATCH] added UnusedLocalVarCheck, fixes #231 (#307) --- checkstyle.json | 3 + resources/default-config.json | 4 + .../checks/coding/UnusedLocalVarCheck.hx | 101 ++++++++++++++++++ test/checks/coding/UnusedLocalVarCheckTest.hx | 86 +++++++++++++++ test/checks/size/LineLengthCheckTest.hx | 3 +- 5 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 src/checkstyle/checks/coding/UnusedLocalVarCheck.hx create mode 100644 test/checks/coding/UnusedLocalVarCheckTest.hx diff --git a/checkstyle.json b/checkstyle.json index 86f3376e..9a8fcb03 100644 --- a/checkstyle.json +++ b/checkstyle.json @@ -462,6 +462,9 @@ }, "type": "UnusedImport" }, + { + "type": "UnusedLocalVar" + }, { "type": "VariableInitialisation" }, diff --git a/resources/default-config.json b/resources/default-config.json index 9fc9e44f..fa16f869 100644 --- a/resources/default-config.json +++ b/resources/default-config.json @@ -512,6 +512,10 @@ }, "type": "UnusedImport" }, + { + "props": {}, + "type": "UnusedLocalVar" + }, { "props": {}, "type": "VariableInitialisation" diff --git a/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx b/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx new file mode 100644 index 00000000..35ab25af --- /dev/null +++ b/src/checkstyle/checks/coding/UnusedLocalVarCheck.hx @@ -0,0 +1,101 @@ +package checkstyle.checks.coding; + +import checkstyle.token.TokenTree; +import checkstyle.utils.StringUtils; + +@name("UnusedLocalVar") +@desc("Checks for unused local variables.") +class UnusedLocalVarCheck extends Check { + + public function new() { + super(TOKEN); + } + + override function actualRun() { + var root:TokenTree = checker.getTokenTree(); + var functions:Array = root.filter([Kwd(KwdFunction)], ALL); + + for (f in functions) { + if (isPosSuppressed(f.pos)) continue; + var skipFirstFunction:Bool = true; + var localVars:Array = f.filterCallback(function(tok:TokenTree, depth:Int):FilterResult { + return switch (tok.tok) { + case Kwd(KwdVar): FOUND_SKIP_SUBTREE; + case Kwd(KwdFunction): + if (skipFirstFunction) { + skipFirstFunction = false; + GO_DEEPER; + } + else SKIP_SUBTREE; + default: GO_DEEPER; + } + }); + checkLocalVars(f, localVars); + } + } + + function checkLocalVars(f:TokenTree, localVars:Array) { + for (localVar in localVars) { + for (child in localVar.childs) { + switch (child.tok) { + case Const(CIdent(name)): + checkLocalVar(f, child, name); + default: + } + } + } + } + + function checkLocalVar(f:TokenTree, v:TokenTree, name:String) { + var ignoreFunctionSignature:Bool = true; + var nameList:Array = f.filterCallback(function(tok:TokenTree, depth:Int):FilterResult { + if (ignoreFunctionSignature) { + switch (tok.tok) { + case Kwd(KwdPublic), Kwd(KwdPrivate): + return SKIP_SUBTREE; + case At: + return SKIP_SUBTREE; + case Comment(_), CommentLine(_): + return SKIP_SUBTREE; + case POpen: + ignoreFunctionSignature = false; + return SKIP_SUBTREE; + default: + return GO_DEEPER; + } + } + return switch (tok.tok) { + case Const(CIdent(n)): + if (n == name) FOUND_GO_DEEPER; + else GO_DEEPER; + case Const(CString(s)): + checkStringInterpolation(tok, name, s); + default: GO_DEEPER; + } + }); + if (nameList.length > 1) return; + + logPos('Unused local variable $name', v.pos); + } + + function checkStringInterpolation(tok:TokenTree, name:String, s:String):FilterResult { + if (!StringUtils.isStringInterpolation(s, checker.file.content, tok.pos)) { + return GO_DEEPER; + } + + // $name + var format:String = "\\$" + name + "([^_0-9a-zA-Z]|$)"; + var r:EReg = new EReg(format, ""); + if (r.match(s)) { + return FOUND_GO_DEEPER; + } + + // '${name.doSomething()} or ${doSomething(name)} + format = "\\$\\{(|.*[^_0-9a-zA-Z])" + name + "([^_0-9a-zA-Z].*)\\}"; + r = new EReg(format, ""); + if (r.match(s)) { + return FOUND_GO_DEEPER; + } + return GO_DEEPER; + } +} \ No newline at end of file diff --git a/test/checks/coding/UnusedLocalVarCheckTest.hx b/test/checks/coding/UnusedLocalVarCheckTest.hx new file mode 100644 index 00000000..acf43336 --- /dev/null +++ b/test/checks/coding/UnusedLocalVarCheckTest.hx @@ -0,0 +1,86 @@ +package checks.coding; + +import checkstyle.checks.coding.UnusedLocalVarCheck; + +class UnusedLocalVarCheckTest extends CheckTestCase { + + static inline var MSG_UNUSED_VAR_INDEX:String = "Unused local variable index"; + + public function testLocalVar() { + var check = new UnusedLocalVarCheck(); + assertNoMsg(check, USED_INDEX); + assertNoMsg(check, STRING_INTERPOLATION); + } + + public function testUnusedLocalVar() { + var check = new UnusedLocalVarCheck(); + assertMsg(check, UNUSED_INDEX, MSG_UNUSED_VAR_INDEX); + assertMsg(check, UNUSED_INDEX2, MSG_UNUSED_VAR_INDEX); + assertMsg(check, STRING_INTERPOLATION_UNUSED, MSG_UNUSED_VAR_INDEX); + } +} + +@:enum +abstract UnusedLocalVarCheckTests(String) to String { + var USED_INDEX = " + abstractAndClass Test { + function a() { + var index:Int; + index++; + } + @SuppressWarnings('checkstyle:UnusedLocalVar') + function b() { + var index:Int; + } + + function c() { + var index:Int; + call(function() { + index++; + }); + } + }"; + + var UNUSED_INDEX = " + abstractAndClass Test { + // index + @index + public function a(index:String) { + var index:Int; + } + }"; + + var UNUSED_INDEX2 = " + abstractAndClass Test { + public function a() { + call(function() { + var index:Int; + }); + } + }"; + + var STRING_INTERPOLATION = " + abstractAndClass Test { + function a() { + var index:Int; + var index2:Array; + var index3:String; + + trace ('$index'); + trace ('${index2.toString()}'); + trace ('${Std.parseInt(index3)}'); + } + }"; + + var STRING_INTERPOLATION_UNUSED = " + abstractAndClass Test { + function a() { + var index:Int; + + trace ('index'); + trace ('$index2'); + trace ('${index2.toString()}'); + trace ('${Std.parseInt(index3)}'); + } + }"; +} \ No newline at end of file diff --git a/test/checks/size/LineLengthCheckTest.hx b/test/checks/size/LineLengthCheckTest.hx index 711645bd..c0d4e34a 100644 --- a/test/checks/size/LineLengthCheckTest.hx +++ b/test/checks/size/LineLengthCheckTest.hx @@ -5,7 +5,6 @@ import checkstyle.checks.size.LineLengthCheck; class LineLengthCheckTest extends CheckTestCase { public function testDefaultLineLength() { - var check = new LineLengthCheck(); assertMsg(new LineLengthCheck(), TEST1, "Line is longer than 160 characters (found 178)"); } @@ -51,4 +50,4 @@ abstract LineLengthCheckTests(String) to String { var s = 'LONG LINE TEST LONG LINE TEST LONG LINE TEST LONG LINE TEST'; } }"; -} +} \ No newline at end of file