diff --git a/checkstyle/Checker.hx b/checkstyle/Checker.hx index 8a6a343d..9ac953df 100644 --- a/checkstyle/Checker.hx +++ b/checkstyle/Checker.hx @@ -112,6 +112,9 @@ class Checker { function makeAST() { var code = file.content; var parser = new HaxeParser(byte.ByteData.ofString(code), file.name); + parser.define("cross"); + parser.define("scriptable"); + parser.define("unsafe"); ast = parser.parse(); } diff --git a/checkstyle/ChecksInfo.hx b/checkstyle/ChecksInfo.hx index fc8c50f9..f55641f7 100644 --- a/checkstyle/ChecksInfo.hx +++ b/checkstyle/ChecksInfo.hx @@ -51,7 +51,10 @@ class ChecksInfo { @SuppressWarnings('checkstyle:Dynamic') public function build(name:String):Dynamic { - if (!name2info.exists(name)) throw 'Unknown check: $name'; + if (!name2info.exists(name)) { + Sys.stderr().writeString('Skipping unknown check: ${name}\n'); + return null; + } var cl = name2info[name].clazz; return Type.createInstance(cl, []); } diff --git a/checkstyle/Main.hx b/checkstyle/Main.hx index c40cea8d..26a7c540 100644 --- a/checkstyle/Main.hx +++ b/checkstyle/Main.hx @@ -38,8 +38,8 @@ class Main { } } catch (e:Dynamic) { - trace(e); - trace(CallStack.toString(CallStack.exceptionStack())); + Sys.stderr().writeString(e + "\n"); + Sys.stderr().writeString(CallStack.toString(CallStack.exceptionStack()) + "\n"); } if (oldCwd != null) Sys.setCwd(oldCwd); Sys.exit(exitCode); @@ -82,7 +82,7 @@ class Main { @doc("Show report") ["-report"] => function() REPORT = true, @doc("Return number of failed checks in exitcode") ["-exitcode"] => function() EXIT_CODE = true, @doc("Set source folder to process") ["-s", "--source"] => function(sourcePath:String) traverse(sourcePath, files), - _ => function(arg:String) throw "Unknown command: " + arg + _ => function(arg:String) failWith("Unknown command: " + arg) ]); if (args.length == 0) { @@ -101,28 +101,46 @@ class Main { else { var configText = File.getContent(configPath); var config = Json.parse(configText); + verifyAllowedFields(config, ["checks", "defaultSeverity"], "Config"); var defaultSeverity = config.defaultSeverity; var checks:Array = config.checks; - for (checkConf in checks) { - var check:Check = cast info.build(checkConf.type); - if (checkConf.props != null) { - var props = Reflect.fields(checkConf.props); - for (prop in props) { - var val = Reflect.field(checkConf.props, prop); - Reflect.setField(check, prop, val); - } - if (defaultSeverity != null && props.indexOf("severity") < 0) { - check.severity = defaultSeverity; - } - } - checker.addCheck(check); - } + for (checkConf in checks) createCheck(checkConf, defaultSeverity); } checker.addReporter(createReporter()); if (EXIT_CODE) checker.addReporter(new ExitCodeReporter()); checker.process(toProcess); } + @SuppressWarnings('checkstyle:Dynamic') + function createCheck(checkConf:Dynamic, defaultSeverity:String) { + var check:Check = cast info.build(checkConf.type); + if (check == null) return; + verifyAllowedFields(checkConf, ["type", "props"], check.getModuleName()); + if (checkConf.props == null) return; + + var props = Reflect.fields(checkConf.props); + for (prop in props) { + var val = Reflect.field(checkConf.props, prop); + if (!Reflect.hasField(check, prop)) { + failWith('Check ${check.getModuleName()} has no property named \'$prop\''); + } + Reflect.setField(check, prop, val); + } + if (defaultSeverity != null && props.indexOf("severity") < 0) { + check.severity = defaultSeverity; + } + checker.addCheck(check); + } + + @SuppressWarnings('checkstyle:Dynamic') + function verifyAllowedFields(object:Dynamic, allowedFields:Array, messagePrefix:String) { + for (field in Reflect.fields(object)) { + if (allowedFields.indexOf(field) < 0) { + failWith(messagePrefix + " has unknown field '" + field + "'"); + } + } + } + function addAllChecks() { for (check in info.checks()) checker.addCheck(info.build(check.name)); } @@ -136,7 +154,7 @@ class Main { case "xml": new XMLReporter(XML_PATH, STYLE); case "json": new JSONReporter(JSON_PATH); case "text": new Reporter(); - default: throw "Unknown reporter"; + default: failWith('Unknown reporter: $REPORT_TYPE'); null; } } @@ -159,6 +177,11 @@ class Main { else if (~/(.hx)$/i.match(node)) files.push(node); } + static function failWith(message:String) { + Sys.stderr().writeString(message + "\n"); + Sys.exit(1); + } + public static function setExitCode(newExitCode:Int) { exitCode = newExitCode; } diff --git a/checkstyle/checks/EmptyLinesCheck.hx b/checkstyle/checks/EmptyLinesCheck.hx index c5340ac5..a4c0e776 100644 --- a/checkstyle/checks/EmptyLinesCheck.hx +++ b/checkstyle/checks/EmptyLinesCheck.hx @@ -8,12 +8,14 @@ import checkstyle.LintMessage.SeverityLevel; class EmptyLinesCheck extends Check { public var max:Int; - public var allowEmptyLineAfterComment:Bool; + public var allowEmptyLineAfterSingleLineComment:Bool; + public var allowEmptyLineAfterMultiLineComment:Bool; public function new() { super(); max = 1; - allowEmptyLineAfterComment = true; + allowEmptyLineAfterSingleLineComment = true; + allowEmptyLineAfterMultiLineComment = true; } override function actualRun() { @@ -29,9 +31,8 @@ class EmptyLinesCheck extends Check { } end = i; - if (i > 0 && !allowEmptyLineAfterComment && ~/^(\/\/).*|^(\/\*).*|(\*\/)$/.match(StringTools.trim(checker.lines[i - 1]))) { - log('Empty line not allowed after comment(s)', start, 0, null, Reflect.field(SeverityLevel, severity)); - } + if (!allowEmptyLineAfterSingleLineComment) checkComment(i, start, ~/^(\/\/).*$/); + if (!allowEmptyLineAfterMultiLineComment) checkComment(i, start, ~/^^(\/\*).*|(\*\/)$/); } else { if (inGroup) { @@ -47,6 +48,12 @@ class EmptyLinesCheck extends Check { } } + function checkComment(i, start, regex) { + if (i > 0 && regex.match(StringTools.trim(checker.lines[i - 1]))) { + log('Empty line not allowed after comment(s)', start, 0, null, Reflect.field(SeverityLevel, severity)); + } + } + function logInfo(pos) { log('Too many consecutive empty lines (> ${max})', pos, 0, null, Reflect.field(SeverityLevel, severity)); } diff --git a/checkstyle/checks/IndentationCharacterCheck.hx b/checkstyle/checks/IndentationCharacterCheck.hx index 8c7aca5c..17dd15ca 100644 --- a/checkstyle/checks/IndentationCharacterCheck.hx +++ b/checkstyle/checks/IndentationCharacterCheck.hx @@ -25,7 +25,7 @@ class IndentationCharacterCheck extends Check { } for (i in 0 ... checker.lines.length) { var line = checker.lines[i]; - if (line.length > 0 && !re.match(line)) log('Wrong indentation character (${character})', i + 1, 0, null, Reflect.field(SeverityLevel, severity)); + if (line.length > 0 && !re.match(line)) log('Wrong indentation character (should be ${character})', i + 1, 0, null, Reflect.field(SeverityLevel, severity)); } } } \ No newline at end of file diff --git a/haxelib.json b/haxelib.json index 3f7a3b57..a3365f07 100644 --- a/haxelib.json +++ b/haxelib.json @@ -15,7 +15,7 @@ "contributors": [ "adireddy" ], - "releasenote": "bug fixes and minor updates", + "releasenote": "more improvements and bug fixes", "version": "1.2.4", "url": "https://github.com/adireddy/haxe-checkstyle", "dependencies": { diff --git a/package.json b/package.json index d2585f07..ea7b5246 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "checkstyle", - "version": "1.2.4", + "version": "1.2.5", "description": "Automated code analysis ideal for projects that want to enforce a coding standard.", "repository": { "type": "git", diff --git a/test/EmptyLinesCheckTest.hx b/test/EmptyLinesCheckTest.hx index 3bfff1ed..2110a704 100644 --- a/test/EmptyLinesCheckTest.hx +++ b/test/EmptyLinesCheckTest.hx @@ -22,22 +22,19 @@ class EmptyLinesCheckTest extends CheckTestCase { public function testEmptyLineAfterSingleLineComment() { var check = new EmptyLinesCheck(); - check.allowEmptyLineAfterComment = false; + check.allowEmptyLineAfterSingleLineComment = false; var msg = checkMessage(EmptyLinesTests.TEST4, check); assertEquals(msg, 'Empty line not allowed after comment(s)'); msg = checkMessage(EmptyLinesTests.TEST5, check); assertEquals(msg, 'Empty line not allowed after comment(s)'); - - msg = checkMessage(EmptyLinesTests.TEST6, check); - assertEquals(msg, 'Empty line not allowed after comment(s)'); } public function testEmptyLineAfterMultiLineComment() { var check = new EmptyLinesCheck(); - check.allowEmptyLineAfterComment = false; - + check.allowEmptyLineAfterMultiLineComment = false; + var msg = checkMessage(EmptyLinesTests.TEST6, check); assertEquals(msg, 'Empty line not allowed after comment(s)'); @@ -47,10 +44,16 @@ class EmptyLinesCheckTest extends CheckTestCase { public function testAllowEmptyLineAfterComment() { var check = new EmptyLinesCheck(); - + + var msg = checkMessage(EmptyLinesTests.TEST4, check); + assertEquals(msg, ''); + + var msg = checkMessage(EmptyLinesTests.TEST5, check); + assertEquals(msg, ''); + var msg = checkMessage(EmptyLinesTests.TEST6, check); assertEquals(msg, ''); - + msg = checkMessage(EmptyLinesTests.TEST7, check); assertEquals(msg, ''); } diff --git a/test/IndentationCharacterCheckTest.hx b/test/IndentationCharacterCheckTest.hx index 8486c983..60b54246 100644 --- a/test/IndentationCharacterCheckTest.hx +++ b/test/IndentationCharacterCheckTest.hx @@ -6,7 +6,7 @@ class IndentationCharacterCheckTest extends CheckTestCase { public function testWrongIndentation() { var msg = checkMessage(IndentationTests.TEST1, new IndentationCharacterCheck()); - assertEquals(msg, 'Wrong indentation character (tab)'); + assertEquals(msg, 'Wrong indentation character (should be tab)'); } public function testCorrectIndentation() { @@ -19,7 +19,7 @@ class IndentationCharacterCheckTest extends CheckTestCase { check.character = "space"; var msg = checkMessage(IndentationTests.TEST3, check); - assertEquals(msg, 'Wrong indentation character (space)'); + assertEquals(msg, 'Wrong indentation character (should be space)'); } public function testMultilineIfIndentation() {