From fdeb52a5af68caee3ff9de37db7841f73f8211a8 Mon Sep 17 00:00:00 2001 From: AlexHaxe Date: Wed, 9 May 2018 15:00:59 +0200 Subject: [PATCH] fix parser errors and refactored error handling (#413) * fix parser errors and refactored error handling --- CHANGES.md | 11 +++- src/checkstyle/Checker.hx | 21 ++----- src/checkstyle/CheckerThread.hx | 4 +- src/checkstyle/Main.hx | 7 +-- src/checkstyle/checks/Check.hx | 12 +--- .../checks/block/EmptyBlockCheck.hx | 7 ++- .../checks/block/RightCurlyCheck.hx | 1 + .../checks/coding/ReturnCountCheck.hx | 1 + .../checks/whitespace/IndentationCheck.hx | 26 +++++++-- src/checkstyle/import.hx | 1 + src/checkstyle/reporter/ReporterManager.hx | 22 +++----- src/checkstyle/token/TokenTreeAccessHelper.hx | 56 +++++++++++++++++++ src/checkstyle/token/walk/WalkAt.hx | 12 ++++ src/checkstyle/token/walk/WalkBlock.hx | 28 +++++----- src/checkstyle/token/walk/WalkClass.hx | 29 +++++++++- src/checkstyle/token/walk/WalkFieldDef.hx | 4 -- src/checkstyle/token/walk/WalkFile.hx | 13 ++++- src/checkstyle/token/walk/WalkNew.hx | 3 + src/checkstyle/token/walk/WalkStatement.hx | 15 ++--- src/checkstyle/token/walk/WalkTypeNameDef.hx | 22 +++++--- src/checkstyle/utils/ErrorUtils.hx | 21 +++++++ test/checks/block/BlockTest.hx | 24 ++++++++ test/token/TokenTreeBuilderParsingTest.hx | 39 ++++++++++++- 23 files changed, 283 insertions(+), 96 deletions(-) create mode 100644 src/checkstyle/token/TokenTreeAccessHelper.hx create mode 100644 src/checkstyle/utils/ErrorUtils.hx diff --git a/CHANGES.md b/CHANGES.md index 70d778c7..057bcf1b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ ## dev branch / next version (2.x.x) +- New command line option `-show-parser-errors` to include parser errors into checkstyle results (default: off) [#413](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/413) +- Fixed handling of `Arrow` in type names in TokenTree [#413](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/413) +- Fixed handling of `Dot` after `KwdNew` in TokenTree [#413](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/413) +- Improved handling of file content and `class` parsing in TokenTree [#413](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/413) +- Refactored handling of internal errors (parsing and checks) [#413](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/413) ## version 2.3.0 (2018-05-07) @@ -42,9 +47,9 @@ - Added support for Binop(OpIn) [#352](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/352) ([#359](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/359)) - Added 1 parser and n checker threads ([#374](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/374)) - * use `-checkerthreads n` to change number of checker threads (range:1-15 default: 5) - * use `-nothreads` to turn off threads and use old behaviour - * use `numberOfCheckerThreads` in config file to set number of checker threads (see `resources/default-conmfig.json`) + - use `-checkerthreads n` to change number of checker threads (range:1-15 default: 5) + - use `-nothreads` to turn off threads and use old behaviour + - use `numberOfCheckerThreads` in config file to set number of checker threads (see `resources/default-conmfig.json`) - Fixed allow same regex logic for "all" excludes, fixes [#361](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/361) ([#362](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/362)) - Fixed altering position info in `RightCurlyCheck` ([#367](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/367)) - Fixed multiple metadatas infront of statement ([#369](https://github.com/HaxeCheckstyle/haxe-checkstyle/issues/369)) diff --git a/src/checkstyle/Checker.hx b/src/checkstyle/Checker.hx index 61d55547..3c172080 100644 --- a/src/checkstyle/Checker.hx +++ b/src/checkstyle/Checker.hx @@ -1,6 +1,5 @@ package checkstyle; -import haxe.CallStack; import haxeparser.HaxeParser; import haxeparser.HaxeLexer; import sys.io.File; @@ -113,13 +112,7 @@ class Checker { } } catch (e:Any) { - #if debug - Sys.println(e); - Sys.println("Stacktrace: " + CallStack.toString(CallStack.exceptionStack())); - #end - #if unittest - throw e; - #end + ErrorUtils.handleException(e, file, "makeTokens"); } } @@ -147,13 +140,7 @@ class Checker { return parser.parse(); } catch (e:Any) { - #if debug - Sys.println(e); - Sys.println("Stacktrace: " + CallStack.toString(CallStack.exceptionStack())); - #end - #if unittest - throw e; - #end + ErrorUtils.handleException(e, file, "makeAST [" + defines.join(",") + "]"); } return null; } @@ -201,7 +188,7 @@ class Checker { getTokenTree(); } catch (e:Any) { - ReporterManager.INSTANCE.addParseError(file, e); + ErrorUtils.handleException(e, file, "createContext"); return false; } return true; @@ -233,7 +220,7 @@ class Checker { return check.run(this); } catch (e:Any) { - ReporterManager.INSTANCE.addCheckError(file, e, check.getModuleName()); + ErrorUtils.handleException(e, file, check.getModuleName()); return []; } } diff --git a/src/checkstyle/CheckerThread.hx b/src/checkstyle/CheckerThread.hx index bab8146e..fed22c7a 100644 --- a/src/checkstyle/CheckerThread.hx +++ b/src/checkstyle/CheckerThread.hx @@ -99,9 +99,9 @@ class CheckerThread { return check.run(checker); } catch (e:Any) { - ReporterManager.INSTANCE.addCheckError(checker.file, e, check.getModuleName()); - return []; + ErrorUtils.handleException(e, checker.file, check.getModuleName()); } + return []; } function checkForExclude(moduleName:String, checker:Checker):Bool { diff --git a/src/checkstyle/Main.hx b/src/checkstyle/Main.hx index 1488bfd5..3a186495 100644 --- a/src/checkstyle/Main.hx +++ b/src/checkstyle/Main.hx @@ -82,11 +82,13 @@ class Main { @doc("List all available checks and exit") ["--list-checks"] => function() listChecks(), @doc("List all available reporters and exit") ["--list-reporters"] => function() listReporters(), @doc("Generate a default config and exit") ["--default-config"] => function(path) generateDefaultConfig(path), - @doc("To omit styling in output summary") ["-nostyle"] => function() NO_STYLE = true, + @doc("Omit styling in output summary") ["-nostyle"] => function() NO_STYLE = true, @doc("Show checks missing from active config") ["-show-missing-checks"] => function () SHOW_MISSING_CHECKS = true, @doc("Sets the number of checker threads") ["-checkerthreads"] => function (num:Int) overrideCheckerThreads = num, @doc("Do not use checker threads") ["-nothreads"] => function () disableThreads = true, @doc("Try to detect your coding style (experimental)") ["-detect"] => function (path) detectCodingStyle(path), + @doc("Adds error messages for files that checkstyle fails to parse") ["-show-parser-errors"] => function () + ReporterManager.SHOW_PARSE_ERRORS = true, @doc("Show report [DEPRECATED]") ["-report"] => function() Sys.println("\n-report is no longer needed."), _ => function(arg:String) failWith("Unknown command: " + arg) ]); @@ -117,15 +119,12 @@ class Main { if (configPath == null && FileSystem.exists(DEFAULT_CONFIG) && !FileSystem.isDirectory(DEFAULT_CONFIG)) { configPath = DEFAULT_CONFIG; } - if (excludePath == null && FileSystem.exists(DEFAULT_EXCLUDE_CONFIG) && !FileSystem.isDirectory(DEFAULT_EXCLUDE_CONFIG)) { excludePath = DEFAULT_EXCLUDE_CONFIG; } loadConfig(configPath); - if (excludePath != null) loadExcludeConfig(excludePath); - start(); } diff --git a/src/checkstyle/checks/Check.hx b/src/checkstyle/checks/Check.hx index 87a9eb4a..81354044 100644 --- a/src/checkstyle/checks/Check.hx +++ b/src/checkstyle/checks/Check.hx @@ -1,9 +1,5 @@ package checkstyle.checks; -#if debug -import haxe.CallStack; -#end - class Check { public var severity:SeverityLevel; @@ -41,13 +37,7 @@ class Check { actualRun(); } catch (e:String) { - #if debug - Sys.println(e); - Sys.println("Stacktrace: " + CallStack.toString(CallStack.exceptionStack())); - #end - #if unittest - throw e; - #end + ErrorUtils.handleException(e, checker.file, getModuleName()); } } return messages; diff --git a/src/checkstyle/checks/block/EmptyBlockCheck.hx b/src/checkstyle/checks/block/EmptyBlockCheck.hx index 2428edf5..74c689bb 100644 --- a/src/checkstyle/checks/block/EmptyBlockCheck.hx +++ b/src/checkstyle/checks/block/EmptyBlockCheck.hx @@ -1,5 +1,7 @@ package checkstyle.checks.block; +import checkstyle.token.TokenTreeAccessHelper; + @name("EmptyBlock") @desc("Checks for empty blocks. The policy to verify is specified using the property `option`.") class EmptyBlockCheck extends Check { @@ -104,8 +106,9 @@ class EmptyBlockCheck extends Check { } function checkForEmpty(brOpen:TokenTree) { - if (brOpen.children.length > 1) return; - var brClose:TokenTree = brOpen.children[0]; + if ((brOpen.children == null) || (brOpen.children.length > 1)) return; + var brClose:TokenTree = TokenTreeAccessHelper.access(brOpen).firstChild().is(BrClose).token; + if (brClose == null) return; if (brOpen.pos.max != brClose.pos.min) logPos('Empty block should be written as "{}"', brOpen.pos); } } diff --git a/src/checkstyle/checks/block/RightCurlyCheck.hx b/src/checkstyle/checks/block/RightCurlyCheck.hx index 59f7ae4a..aac4c2b2 100644 --- a/src/checkstyle/checks/block/RightCurlyCheck.hx +++ b/src/checkstyle/checks/block/RightCurlyCheck.hx @@ -38,6 +38,7 @@ class RightCurlyCheck extends Check { for (brClose in allBrClose) { if (isPosSuppressed(brClose.pos)) continue; var brOpen:TokenTree = brClose.parent; + if ((brOpen == null) || (brOpen.pos == null)) continue; if (filterParentToken(brOpen.parent)) continue; check(brClose, isSingleLine(brOpen.pos.min, brClose.pos.max)); } diff --git a/src/checkstyle/checks/coding/ReturnCountCheck.hx b/src/checkstyle/checks/coding/ReturnCountCheck.hx index 162b88a6..c2aa6b1a 100644 --- a/src/checkstyle/checks/coding/ReturnCountCheck.hx +++ b/src/checkstyle/checks/coding/ReturnCountCheck.hx @@ -20,6 +20,7 @@ class ReturnCountCheck extends Check { var root:TokenTree = checker.getTokenTree(); var functions = root.filter([Kwd(KwdFunction)], ALL); for (fn in functions) { + if (fn.children == null) continue; switch (fn.getFirstChild().tok) { case Const(CIdent(name)): if (ignoreFormatRE.match(name)) continue; diff --git a/src/checkstyle/checks/whitespace/IndentationCheck.hx b/src/checkstyle/checks/whitespace/IndentationCheck.hx index 14325565..88abd37c 100644 --- a/src/checkstyle/checks/whitespace/IndentationCheck.hx +++ b/src/checkstyle/checks/whitespace/IndentationCheck.hx @@ -117,12 +117,7 @@ class IndentationCheck extends Check { for (token in tokenList) { switch (token.tok) { case BkOpen: - var child:TokenTree = token.getFirstChild(); - if (child.is(BrOpen)) { - // only indent once, if directly next to each other `[{` - if (token.pos.min + 1 == child.pos.min) continue; - } - increaseBlockIndent(token, lineIndentation); + calcLineIndentationBkOpen(token, lineIndentation); case BrOpen: increaseBlockIndent(token, lineIndentation); case Kwd(KwdIf), Kwd(KwdElse): @@ -131,6 +126,7 @@ class IndentationCheck extends Check { calcLineIndentationLoops(token, lineIndentation); case Kwd(KwdCase): var child:TokenTree = token.getLastChild(); + if (child == null) continue; increaseRangeIndent(child.getPos(), lineIndentation); case Kwd(KwdDefault): var child:TokenTree = token.getLastChild(); @@ -145,10 +141,21 @@ class IndentationCheck extends Check { return lineIndentation; } + function calcLineIndentationBkOpen(token:TokenTree, lineIndentation:Array) { + var child:TokenTree = token.getFirstChild(); + if (child == null) return; + if (child.is(BrOpen)) { + // only indent once, if directly next to each other `[{` + if (token.pos.min + 1 == child.pos.min) return; + } + increaseBlockIndent(token, lineIndentation); + } + function calcLineIndentationIf(token:TokenTree, lineIndentation:Array) { switch (token.tok) { case Kwd(KwdIf): var child:TokenTree = token.getLastChild(); + if (child == null) return; if (child.is(Kwd(KwdElse))) { child = token.children[token.children.length - 2]; } @@ -156,6 +163,7 @@ class IndentationCheck extends Check { increaseIndentIfNextLine(token, child, lineIndentation); case Kwd(KwdElse): var child:TokenTree = token.getFirstChild(); + if (child == null) return; if (child.is(BrOpen)) return; increaseIndentIfNextLine(token, child, lineIndentation); default: @@ -199,14 +207,17 @@ class IndentationCheck extends Check { switch (token.tok) { case Kwd(KwdFor): var child:TokenTree = token.getLastChild(); + if (child == null) return; if (child.is(BrOpen)) return; increaseIndentIfNextLine(token, child, lineIndentation); case Kwd(KwdDo): var child:TokenTree = token.getFirstChild(); + if (child == null) return; if (child.is(BrOpen)) return; increaseIndentIfNextLine(token, child, lineIndentation); case Kwd(KwdWhile): var child:TokenTree = token.getLastChild(); + if (child == null) return; if (child.is(BrOpen)) return; increaseIndentIfNextLine(token, child, lineIndentation); default: @@ -239,6 +250,7 @@ class IndentationCheck extends Check { for (token in tokenList) { var pos = token.getPos(); var child:TokenTree = token.getFirstChild(); + if (child == null) continue; if (token.is(Dot)) pos = token.parent.getPos(); if (child.is(BkOpen)) continue; ignoreRange(pos, wrapped); @@ -280,6 +292,7 @@ class IndentationCheck extends Check { } function increaseIndentBetween(blockStart:TokenTree, blockEnd:TokenTree, lineIndentation:Array) { + if (blockEnd == null) return; var start:Int = checker.getLinePos(blockStart.pos.min).line + 1; var end:Int = checker.getLinePos(blockEnd.pos.min).line; increaseIndent(lineIndentation, start, end); @@ -292,6 +305,7 @@ class IndentationCheck extends Check { } function increaseIndentIfNextLine(parent:TokenTree, child:TokenTree, lineIndentation:Array) { + if (child == null) return; var parentLine:Int = checker.getLinePos(parent.pos.min).line; var childLine:Int = checker.getLinePos(child.pos.min).line; if (parentLine == childLine) return; diff --git a/src/checkstyle/import.hx b/src/checkstyle/import.hx index 0d65b203..7c11ad74 100644 --- a/src/checkstyle/import.hx +++ b/src/checkstyle/import.hx @@ -10,6 +10,7 @@ import checkstyle.SeverityLevel; import checkstyle.token.TokenTree; import checkstyle.detect.DetectableInstances; +import checkstyle.utils.ErrorUtils; using checkstyle.utils.ArrayUtils; using checkstyle.utils.FieldUtils; using checkstyle.utils.ExprUtils; diff --git a/src/checkstyle/reporter/ReporterManager.hx b/src/checkstyle/reporter/ReporterManager.hx index b2489741..b418033a 100644 --- a/src/checkstyle/reporter/ReporterManager.hx +++ b/src/checkstyle/reporter/ReporterManager.hx @@ -6,19 +6,21 @@ import neko.vm.Mutex; import cpp.vm.Mutex; #end -import haxe.CallStack; - import checkstyle.CheckMessage; import checkstyle.checks.Category; class ReporterManager { public static var INSTANCE:ReporterManager = new ReporterManager(); + public static var SHOW_PARSE_ERRORS:Bool = false; var reporters:Array; var lock:Mutex; function new () { + #if (debug || unittest) + SHOW_PARSE_ERRORS = true; + #end clear(); lock = new Mutex(); } @@ -51,16 +53,8 @@ class ReporterManager { lock.release(); } - public function addParseError(f:CheckFile, e:Any) { - lock.acquire(); - for (reporter in reporters) { - reporter.addMessage(getErrorMessage(e, f.name, "Parsing")); - reporter.fileFinish(f); - } - lock.release(); - } - - public function addCheckError(f:CheckFile, e:Any, name:String) { + public function addError(f:CheckFile, e:Any, name:String) { + if (!SHOW_PARSE_ERRORS) return; lock.acquire(); for (reporter in reporters) reporter.addMessage(getErrorMessage(e, f.name, "Check " + name)); lock.release(); @@ -110,8 +104,8 @@ class ReporterManager { moduleName:"Checker", categories:[Category.STYLE], points:1, - desc: "", - message:step + " failed: " + e + "\nStacktrace: " + CallStack.toString(CallStack.exceptionStack()) + desc:"", + message:step + " failed: " + e + "\nPlease file a github issue at https://github.com/HaxeCheckstyle/haxe-checkstyle/issues" }; } } diff --git a/src/checkstyle/token/TokenTreeAccessHelper.hx b/src/checkstyle/token/TokenTreeAccessHelper.hx new file mode 100644 index 00000000..cff5f7f7 --- /dev/null +++ b/src/checkstyle/token/TokenTreeAccessHelper.hx @@ -0,0 +1,56 @@ +package checkstyle.token; + +class TokenTreeAccessHelper { + + public var token:TokenTree; + + function new(tok:TokenTree) { + token = tok; + } + + public static function access(tok:TokenTree):TokenTreeAccessHelper { + return new TokenTreeAccessHelper(tok); + } + + public function firstChild():TokenTreeAccessHelper { + if (token == null) return this; + return new TokenTreeAccessHelper(token.getFirstChild()); + } + + public function lastChild():TokenTreeAccessHelper { + if (token == null) return this; + return new TokenTreeAccessHelper(token.getLastChild()); + } + + public function firstOf(tokenDef:TokenDef):TokenTreeAccessHelper { + if (token == null) return this; + if (token.children == null) return new TokenTreeAccessHelper(null); + for (tok in token.children) { + if (tok.is(tokenDef)) return new TokenTreeAccessHelper(tok); + } + return new TokenTreeAccessHelper(null); + } + + public function lastOf(tokenDef:TokenDef):TokenTreeAccessHelper { + if (token == null) return this; + if (token.children == null) return new TokenTreeAccessHelper(null); + var found:TokenTree = null; + for (tok in token.children) { + if (tok.is(tokenDef)) found = tok; + } + return new TokenTreeAccessHelper(found); + } + + public function child(index:Int):TokenTreeAccessHelper { + if (token == null) return this; + if (token.children == null) return new TokenTreeAccessHelper(null); + if (token.children.length <= index) return new TokenTreeAccessHelper(null); + return new TokenTreeAccessHelper(token.children[index]); + } + + public function is(tokenDef:TokenDef):TokenTreeAccessHelper { + if (token == null) return this; + if (token.is(tokenDef)) return this; + return new TokenTreeAccessHelper(null); + } +} \ No newline at end of file diff --git a/src/checkstyle/token/walk/WalkAt.hx b/src/checkstyle/token/walk/WalkAt.hx index 8976c4e6..4140aca3 100644 --- a/src/checkstyle/token/walk/WalkAt.hx +++ b/src/checkstyle/token/walk/WalkAt.hx @@ -42,4 +42,16 @@ class WalkAt { if (stream.is(POpen)) WalkPOpen.walkPOpen(stream, name); return atTok; } + + public static function walkAts(stream:TokenStream):Array { + var tempStore:Array = []; + var progress:TokenStreamProgress = new TokenStreamProgress(stream); + while (progress.streamHasChanged()) { + switch (stream.token()) { + case At: tempStore.push(WalkAt.walkAt(stream)); + default: + } + } + return tempStore; + } } \ No newline at end of file diff --git a/src/checkstyle/token/walk/WalkBlock.hx b/src/checkstyle/token/walk/WalkBlock.hx index 9bb1ee3f..044b5547 100644 --- a/src/checkstyle/token/walk/WalkBlock.hx +++ b/src/checkstyle/token/walk/WalkBlock.hx @@ -16,23 +16,25 @@ class WalkBlock { var openTok:TokenTree = stream.consumeTokenDef(BrOpen); parent.addChild(openTok); for (tok in tempStore) openTok.addChild(tok); - - var progress:TokenStreamProgress = new TokenStreamProgress(stream); - while (progress.streamHasChanged()) { - switch (stream.token()) { - case BrClose: break; - case Comma, BkClose, PClose: - var child:TokenTree = stream.consumeToken(); - openTok.addChild(child); - default: WalkStatement.walkStatement(stream, openTok); - - } - } - openTok.addChild(stream.consumeTokenDef(BrClose)); + walkBlockContinue(stream, openTok); } else { stream.rewindTo(rewindPos); WalkStatement.walkStatement(stream, parent); } } + + public static function walkBlockContinue(stream:TokenStream, parent:TokenTree) { + var progress:TokenStreamProgress = new TokenStreamProgress(stream); + while (progress.streamHasChanged()) { + switch (stream.token()) { + case BrClose: break; + case Comma, BkClose, PClose: + var child:TokenTree = stream.consumeToken(); + parent.addChild(child); + default: WalkStatement.walkStatement(stream, parent); + } + } + parent.addChild(stream.consumeTokenDef(BrClose)); + } } \ No newline at end of file diff --git a/src/checkstyle/token/walk/WalkClass.hx b/src/checkstyle/token/walk/WalkClass.hx index ce72efb0..b4aef4ec 100644 --- a/src/checkstyle/token/walk/WalkClass.hx +++ b/src/checkstyle/token/walk/WalkClass.hx @@ -1,5 +1,7 @@ package checkstyle.token.walk; +import checkstyle.token.TokenTreeAccessHelper; + class WalkClass { public static function walkClass(stream:TokenStream, parent:TokenTree, prefixes:Array) { @@ -38,15 +40,38 @@ class WalkClass { tempStore = []; case Sharp(_): WalkSharp.walkSharp(stream, parent, WalkClass.walkClassBody); + walkClassContinueAfterSharp(stream, parent); case At: tempStore.push(WalkAt.walkAt(stream)); case BrClose: break; case Semicolon: parent.addChild(stream.consumeToken()); - default: + case Kwd(KwdPublic), Kwd(KwdPrivate), Kwd(KwdStatic), Kwd(KwdInline), Kwd(KwdMacro), Kwd(KwdOverride), Kwd(KwdDynamic): tempStore.push(stream.consumeToken()); + case Comment(_), CommentLine(_): + tempStore.push(stream.consumeToken()); + default: + throw "invalid token tree structure"; } } - for (tok in tempStore) parent.addChild(tok); + for (tok in tempStore) { + switch (tok.tok) { + case Comment(_), CommentLine(_): parent.addChild(tok); + default: throw "invalid token tree structure"; + } + } + } + + static function walkClassContinueAfterSharp(stream:TokenStream, parent:TokenTree) { + var brOpen:TokenTreeAccessHelper = TokenTreeAccessHelper + .access(parent) + .lastChild().is(Sharp("if")) + .lastOf(Kwd(KwdFunction)) + .firstChild() + .lastChild() + .is(BrOpen); + if (brOpen.token == null) return; + if (brOpen.lastChild().is(BrClose).token != null) return; + WalkBlock.walkBlockContinue(stream, parent); } } \ No newline at end of file diff --git a/src/checkstyle/token/walk/WalkFieldDef.hx b/src/checkstyle/token/walk/WalkFieldDef.hx index a072c2fb..82c8e009 100644 --- a/src/checkstyle/token/walk/WalkFieldDef.hx +++ b/src/checkstyle/token/walk/WalkFieldDef.hx @@ -35,10 +35,6 @@ class WalkFieldDef { WalkStatement.walkStatement(stream, name); } switch (stream.token()) { - case Arrow: - var arrowTok:TokenTree = stream.consumeTokenDef(Arrow); - name.addChild(arrowTok); - walkFieldDef (stream, arrowTok); case Comma: name.addChild(stream.consumeTokenDef(Comma)); case Semicolon: diff --git a/src/checkstyle/token/walk/WalkFile.hx b/src/checkstyle/token/walk/WalkFile.hx index 1858f3c4..5731d79c 100644 --- a/src/checkstyle/token/walk/WalkFile.hx +++ b/src/checkstyle/token/walk/WalkFile.hx @@ -24,10 +24,19 @@ class WalkFile { case Kwd(KwdClass), Kwd(KwdInterface), Kwd(KwdEnum), Kwd(KwdTypedef), Kwd(KwdAbstract): WalkType.walkType(stream, parent, tempStore); tempStore = []; - default: + case PClose, BrClose, BkClose, Semicolon, Comma: + parent.addChild(stream.consumeToken()); + case Kwd(KwdExtern), Kwd(KwdPrivate), Kwd(KwdPublic): tempStore.push(stream.consumeToken()); + default: + WalkBlock.walkBlock(stream, parent); + } + } + for (stored in tempStore) { + switch (stored.tok) { + case Kwd(KwdExtern), Kwd(KwdPrivate), Kwd(KwdPublic), At: throw "invalid token tree structure"; + default: parent.addChild(stored); } } - for (stored in tempStore) parent.addChild(stored); } } \ No newline at end of file diff --git a/src/checkstyle/token/walk/WalkNew.hx b/src/checkstyle/token/walk/WalkNew.hx index 66728213..5bcc1083 100644 --- a/src/checkstyle/token/walk/WalkNew.hx +++ b/src/checkstyle/token/walk/WalkNew.hx @@ -16,5 +16,8 @@ class WalkNew { } } WalkPOpen.walkPOpen(stream, name); + if (stream.is(Dot)) { + WalkStatement.walkStatement(stream, name); + } } } \ No newline at end of file diff --git a/src/checkstyle/token/walk/WalkStatement.hx b/src/checkstyle/token/walk/WalkStatement.hx index f03919aa..1d223c7a 100644 --- a/src/checkstyle/token/walk/WalkStatement.hx +++ b/src/checkstyle/token/walk/WalkStatement.hx @@ -8,7 +8,6 @@ class WalkStatement { var wantMore:Bool = true; while (stream.is(At)) tempStore.push(WalkAt.walkAt(stream)); - switch (stream.token()) { case Binop(OpSub): WalkBinopSub.walkBinopSub(stream, parent); @@ -85,6 +84,14 @@ class WalkStatement { } var dblDotTok:TokenTree = stream.consumeToken(); parent.addChild(dblDotTok); + if (stream.is(Kwd(KwdNew))) { + WalkNew.walkNew(stream, dblDotTok); + return; + } + if (stream.is(Kwd(KwdFunction))) { + WalkFunction.walkFunction(stream, dblDotTok, WalkAt.walkAts(stream)); + return; + } WalkTypeNameDef.walkTypeNameDef(stream, dblDotTok); if (stream.is(Binop(OpAssign))) { walkStatement(stream, parent); @@ -125,12 +132,6 @@ class WalkStatement { WalkFor.walkFor(stream, parent); case Kwd(KwdFunction): WalkFunction.walkFunction(stream, parent, []); - case Kwd(KwdPackage), Kwd(KwdImport), Kwd(KwdUsing): - WalkPackageImport.walkPackageImport(stream, parent); - case Kwd(KwdExtends): - WalkExtends.walkExtends(stream, parent); - case Kwd(KwdImplements): - WalkImplements.walkImplements(stream, parent); case Kwd(KwdClass): WalkClass.walkClass(stream, parent, []); case Kwd(KwdMacro), Kwd(KwdReturn): diff --git a/src/checkstyle/token/walk/WalkTypeNameDef.hx b/src/checkstyle/token/walk/WalkTypeNameDef.hx index b4ff9106..aa7d2393 100644 --- a/src/checkstyle/token/walk/WalkTypeNameDef.hx +++ b/src/checkstyle/token/walk/WalkTypeNameDef.hx @@ -3,14 +3,7 @@ package checkstyle.token.walk; class WalkTypeNameDef { public static function walkTypeNameDef(stream:TokenStream, parent:TokenTree):TokenTree { WalkComment.walkComment(stream, parent); - if (stream.is(BrOpen)) { - WalkTypedefBody.walkTypedefBody(stream, parent); - return parent.getFirstChild(); - } - if (stream.is(BkOpen)) { - WalkArrayAccess.walkArrayAccess(stream, parent); - return parent.getFirstChild(); - } + var tempStore:Array = WalkAt.walkAts(stream); if (stream.is(Question)) { var questTok:TokenTree = stream.consumeTokenDef(Question); parent.addChild(questTok); @@ -19,6 +12,12 @@ class WalkTypeNameDef { var name:TokenTree; var bAdd:Bool = true; switch (stream.token()) { + case BrOpen: + WalkTypedefBody.walkTypedefBody(stream, parent); + return parent.getFirstChild(); + case BkOpen: + WalkArrayAccess.walkArrayAccess(stream, parent); + return parent.getFirstChild(); case Kwd(KwdMacro), Kwd(KwdExtern), Kwd(KwdNew): name = stream.consumeToken(); case Const(_): @@ -43,6 +42,7 @@ class WalkTypeNameDef { default: name = stream.consumeToken(); } + for (tok in tempStore) name.addChild(tok); if (bAdd) parent.addChild(name); walkTypeNameDefContinue(stream, name); return name; @@ -56,6 +56,12 @@ class WalkTypeNameDef { WalkTypeNameDef.walkTypeNameDef(stream, dot); return; } + if (stream.is(Arrow)) { + var arrow:TokenTree = stream.consumeTokenDef(Arrow); + parent.addChild(arrow); + WalkTypeNameDef.walkTypeNameDef(stream, arrow); + return; + } if (stream.is(Binop(OpLt))) WalkLtGt.walkLtGt(stream, parent); if (stream.is(BkOpen)) WalkArrayAccess.walkArrayAccess(stream, parent); WalkComment.walkComment(stream, parent); diff --git a/src/checkstyle/utils/ErrorUtils.hx b/src/checkstyle/utils/ErrorUtils.hx new file mode 100644 index 00000000..2978c0e9 --- /dev/null +++ b/src/checkstyle/utils/ErrorUtils.hx @@ -0,0 +1,21 @@ +package checkstyle.utils; + +#if debug +import haxe.CallStack; +#end + +import checkstyle.reporter.ReporterManager; + +class ErrorUtils { + public static function handleException(e:Any, file:CheckFile, name:String) { + #if debug + Sys.println(e); + Sys.println("File: " + file.name); + Sys.println("Stacktrace: " + CallStack.toString(CallStack.exceptionStack())); + #end + #if unittest + throw e; + #end + ReporterManager.INSTANCE.addError(file, e, name); + } +} diff --git a/test/checks/block/BlockTest.hx b/test/checks/block/BlockTest.hx index 365c2b54..2543dd98 100644 --- a/test/checks/block/BlockTest.hx +++ b/test/checks/block/BlockTest.hx @@ -12,6 +12,7 @@ class BlockTest extends CheckTestCase { var check:EmptyBlockCheck = new EmptyBlockCheck(); assertNoMsg(check, ISSUE_42); assertNoMsg(check, ISSUE_42_MACRO); + assertNoMsg(check, CONDITIONAL_TEST); var checkLeft:LeftCurlyCheck = new LeftCurlyCheck(); checkLeft.option = NL; @@ -21,6 +22,7 @@ class BlockTest extends CheckTestCase { var checkRight:RightCurlyCheck = new RightCurlyCheck(); assertNoMsg(checkRight, ISSUE_42); assertNoMsg(checkRight, ISSUE_42_MACRO); + assertNoMsg(checkRight, CONDITIONAL_TEST); } @Test @@ -74,4 +76,26 @@ abstract BlockTests(String) to String { } } }"; + + var CONDITIONAL_TEST = " + abstractAndClass Test { + #if false + function build() { + #else + function build2() { + #end + } + + function test() { + #if debug + try { + #end + + #if debug + catch(e) { + // nothing + } + #end + } + }"; } \ No newline at end of file diff --git a/test/token/TokenTreeBuilderParsingTest.hx b/test/token/TokenTreeBuilderParsingTest.hx index cc4d7a07..ed0d4dff 100644 --- a/test/token/TokenTreeBuilderParsingTest.hx +++ b/test/token/TokenTreeBuilderParsingTest.hx @@ -37,6 +37,8 @@ class TokenTreeBuilderParsingTest { assertCodeParses(BLOCK_OBJECT_DECL_WITH_TERNARY); assertCodeParses(TYPEDEF_COMMENTS); assertCodeParses(TYPEDEF_COMMENTS_2); + assertCodeParses(FUNCTION_RETURN_TYPE); + assertCodeParses(FUNCTION_SHARP); } public function assertCodeParses(code:String, ?pos:PosInfos) { @@ -45,10 +47,22 @@ class TokenTreeBuilderParsingTest { builder = TestTokenTreeBuilder.parseCode(code); } catch (e:Any) { - Assert.isTrue(false, pos); + Assert.fail("code should not throw execption", pos); } Assert.isTrue(builder.isStreamEmpty(), pos); } + + public function assertCodeThrows(code:String, ?pos:PosInfos) { + var builder:TestTokenTreeBuilder = null; + try { + builder = TestTokenTreeBuilder.parseCode(code); + } + catch (e:Any) { + Assert.isTrue(true, pos); + return; + } + Assert.fail("code should throw an exception", pos); + } } @:enum @@ -453,6 +467,8 @@ abstract TokenTreeBuilderParsingTests(String) to String { checkInfos[names[i]] = { name: names[i], clazz: cl, + test: new Value(1), + test2: function() { return 1 }, isAlias: i > 0, description: (i == 0) ? desc : desc + ' [DEPRECATED, use ' + names[0] + ' instead]' }; @@ -478,4 +494,25 @@ abstract TokenTreeBuilderParsingTests(String) to String { // €łµ var index:Int; }"; + + var FUNCTION_RETURN_TYPE = " + class Test { + function test(x:String->Int->Void):String->Int->Void { + return new TestCallback().with(x); + } + }"; + + var FUNCTION_SHARP = " + class Test { + #if test + function test() { + #else + function _test() { + #end + doSomething(); + if (test2()) return false; + } + function test2() { + } + }"; } \ No newline at end of file