Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NullParameterCheck, closes #49 #85

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions checkstyle/Main.hx
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,8 @@ class Main {
var check:Check = cast info.build(checkConf.type);
if (check == null) return;
verifyAllowedFields(checkConf, ["type", "props"], check.getModuleName());
if (checkConf.props == null) checkConf.props = [];

var props = Reflect.fields(checkConf.props);
var props = (checkConf.props == null) ? [] : Reflect.fields(checkConf.props);
for (prop in props) {
var val = Reflect.field(checkConf.props, prop);
if (!Reflect.hasField(check, prop)) {
Expand Down
52 changes: 52 additions & 0 deletions checkstyle/checks/NullParameterCheck.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package checkstyle.checks;

import checkstyle.LintMessage.SeverityLevel;
import haxe.macro.Expr.FunctionArg;
import haxe.macro.Expr.Position;
using haxe.macro.ExprTools;

@name("NullParameter")
@desc("Checks for parameters with a null default value")
class NullParameterCheck extends Check {

public static inline var EITHER:String = "either";
public static inline var QUESTION_MARK:String = "questionmark";
public static inline var NULL:String = "null";

public var nullDefaultValueStyle:String;

public function new() {
super();
nullDefaultValueStyle = QUESTION_MARK;
}

override function actualRun() {
forEachField(function(field, _) {
switch (field.kind) {
case FFun(f):
for (arg in f.args) checkArgument(arg, field.pos);
case _:
}
});
}

function checkArgument(arg:FunctionArg, pos:Position) {
var hasNullDefault = arg.value.toString() == "null";
var prefix = 'Parameter \'${arg.name}\' ';
if (arg.opt && hasNullDefault) {
logPos(prefix + "is marked as optional with '?' and has a default value of 'null', which is redundant",
arg.value.pos, Reflect.field(SeverityLevel, severity));
}
else {
var line = checker.getLinePos(pos.min).line + 1;
if (hasNullDefault && nullDefaultValueStyle == QUESTION_MARK) {
log(prefix + "should be marked as optional with '?' instead of using a null default value",
line, 0, null, Reflect.field(SeverityLevel, severity));
}
else if (arg.opt && nullDefaultValueStyle == NULL) {
log(prefix + "should have a null default value instead of being marked as optional with '?'",
line, 0, null, Reflect.field(SeverityLevel, severity));
}
}
}
}
7 changes: 7 additions & 0 deletions resources/sample-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@
"props": {
"severity": "ERROR"
}
},
{
"type": "NullParameter",
"props": {
"severity": "ERROR",
"nullDefaultValueStyle": "questionmark"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a convention for the naming of the option values? Sometimes they're UPPERCASE, (ENUM) sometimes lowercase (nl).

}
}
]
}
2 changes: 2 additions & 0 deletions resources/static-analysis.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
</file>
<file name="checkstyle&#x2F;checks&#x2F;NestedTryDepthCheck.hx">
</file>
<file name="checkstyle&#x2F;checks&#x2F;NullParameterCheck.hx">
</file>
<file name="checkstyle&#x2F;checks&#x2F;ParameterNameCheck.hx">
</file>
<file name="checkstyle&#x2F;checks&#x2F;ParameterNumberCheck.hx">
Expand Down
2 changes: 1 addition & 1 deletion test/AccessOrderCheckTest.hx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ;
package;

import checkstyle.checks.AccessOrderCheck;

Expand Down
2 changes: 1 addition & 1 deletion test/DynamicCheckTest.hx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ;
package;

import checkstyle.checks.DynamicCheck;

Expand Down
53 changes: 53 additions & 0 deletions test/NullParameterCheckTest.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package;

import checkstyle.checks.NullParameterCheck;

class NullParameterCheckTest extends CheckTestCase {

public function testDetectRedundancy() {
assertMsg(new NullParameterCheck(), NullParameterCheckTests.TEST1,
"Parameter 'a' is marked as optional with '?' and has a default value of 'null', which is redundant");
}

public function testSuppress() {
assertMsg(new NullParameterCheck(), NullParameterCheckTests.TEST2, '');
}

public function testAllowEitherStyle() {
var check = new NullParameterCheck();
check.nullDefaultValueStyle = NullParameterCheck.EITHER;
assertMsg(check, NullParameterCheckTests.TEST3, '');
}

public function testPreferQuestionMark() {
var check = new NullParameterCheck();
check.nullDefaultValueStyle = NullParameterCheck.QUESTION_MARK;
assertMsg(check, NullParameterCheckTests.TEST3,
"Parameter 'b' should be marked as optional with '?' instead of using a null default value");
}

public function testPreferNull() {
var check = new NullParameterCheck();
check.nullDefaultValueStyle = NullParameterCheck.NULL;
assertMsg(check, NullParameterCheckTests.TEST3,
"Parameter 'a' should have a null default value instead of being marked as optional with '?'");
}
}

class NullParameterCheckTests {
public static inline var TEST1:String = "
abstractAndClass Test {
function test(?a:Int = null) {}
}";

public static inline var TEST2:String = "
abstractAndClass Test {
@SuppressWarnings('checkstyle:NullParameter')
function test(?a:Int = null, b:Null<Int> = null, ?c:Int) {}
}";

public static inline var TEST3:String = "
abstractAndClass Test {
function test(?a:Int, b:Null<Int> = null) {}
}";
}
3 changes: 2 additions & 1 deletion test/TestMain.hx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ;
package;

class TestMain {

Expand Down Expand Up @@ -33,6 +33,7 @@ class TestMain {
runner.add(new NestedForDepthCheckTest());
runner.add(new NestedIfDepthCheckTest());
runner.add(new NestedTryDepthCheckTest());
runner.add(new NullParameterCheckTest());
runner.add(new ParameterNameCheckTest());
runner.add(new ParameterNumberCheckTest());
runner.add(new PublicPrivateCheckTest());
Expand Down