Skip to content

Commit

Permalink
added UnusedLocalVarCheck, fixes #231 (#307)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexHaxe authored Nov 5, 2016
1 parent 4662a4a commit 027311c
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 2 deletions.
3 changes: 3 additions & 0 deletions checkstyle.json
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,9 @@
},
"type": "UnusedImport"
},
{
"type": "UnusedLocalVar"
},
{
"type": "VariableInitialisation"
},
Expand Down
4 changes: 4 additions & 0 deletions resources/default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,10 @@
},
"type": "UnusedImport"
},
{
"props": {},
"type": "UnusedLocalVar"
},
{
"props": {},
"type": "VariableInitialisation"
Expand Down
101 changes: 101 additions & 0 deletions src/checkstyle/checks/coding/UnusedLocalVarCheck.hx
Original file line number Diff line number Diff line change
@@ -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<TokenTree> = root.filter([Kwd(KwdFunction)], ALL);

for (f in functions) {
if (isPosSuppressed(f.pos)) continue;
var skipFirstFunction:Bool = true;
var localVars:Array<TokenTree> = 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<TokenTree>) {
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<TokenTree> = 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;
}
}
86 changes: 86 additions & 0 deletions test/checks/coding/UnusedLocalVarCheckTest.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package checks.coding;

import checkstyle.checks.coding.UnusedLocalVarCheck;

class UnusedLocalVarCheckTest extends CheckTestCase<UnusedLocalVarCheckTests> {

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<Int>;
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)}');
}
}";
}
3 changes: 1 addition & 2 deletions test/checks/size/LineLengthCheckTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import checkstyle.checks.size.LineLengthCheck;
class LineLengthCheckTest extends CheckTestCase<LineLengthCheckTests> {

public function testDefaultLineLength() {
var check = new LineLengthCheck();
assertMsg(new LineLengthCheck(), TEST1, "Line is longer than 160 characters (found 178)");
}

Expand Down Expand Up @@ -51,4 +50,4 @@ abstract LineLengthCheckTests(String) to String {
var s = 'LONG LINE TEST LONG LINE TEST LONG LINE TEST LONG LINE TEST';
}
}";
}
}

0 comments on commit 027311c

Please sign in to comment.