diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c2a2e8..2b48f6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,14 @@ ## dev branch / next version (1.x.x) -- fixed empty switch cases with comments +## version 1.2.11 (2024-02-10) + +- Added getBkOpenType utils function +- Fixed empty switch cases with comments ## version 1.2.10 (2023-05-21) -- fixed extends handling in interfaces +- Fixed extends handling in interfaces ## version 1.2.9 (2023-05-21) diff --git a/haxelib.json b/haxelib.json index 2bb4c77..085ae6d 100644 --- a/haxelib.json +++ b/haxelib.json @@ -7,8 +7,8 @@ "contributors": [ "AlexHaxe" ], - "releasenote": "fixed extends handling in interfaces - see CHANGELOG for details", - "version": "1.2.10", + "releasenote": "fixed empty switch cases with comments; added getBkOpenType utils function - see CHANGELOG for details", + "version": "1.2.11", "url": "https://github.com/HaxeCheckstyle/tokentree", "dependencies": {} } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2984555..e6fd377 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tokentree", - "version": "1.2.10", + "version": "1.2.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tokentree", - "version": "1.2.10", + "version": "1.2.11", "license": "MIT", "devDependencies": { "lix": "^15.12.0" diff --git a/package.json b/package.json index 2e91811..51ffac0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tokentree", - "version": "1.2.10", + "version": "1.2.11", "description": "TokenTree library used by haxe-checkstyle, haxe-formatter and haxe-languageserver", "repository": { "type": "git", diff --git a/src/tokentree/TokenTree.hx b/src/tokentree/TokenTree.hx index 17504ef..0f00d55 100644 --- a/src/tokentree/TokenTree.hx +++ b/src/tokentree/TokenTree.hx @@ -1,6 +1,7 @@ package tokentree; import tokentree.utils.TokenTreeCheckUtils.ArrowType; +import tokentree.utils.TokenTreeCheckUtils.BkOpenType; import tokentree.utils.TokenTreeCheckUtils.BrOpenType; import tokentree.utils.TokenTreeCheckUtils.ColonType; import tokentree.utils.TokenTreeCheckUtils.POpenType; @@ -174,6 +175,7 @@ enum FilterResult { typedef TokenTypeCache = { @:optional var brOpenType:Null; + @:optional var bkOpenType:Null; @:optional var pOpenType:Null; @:optional var colonType:Null; @:optional var arrowType:Null; diff --git a/src/tokentree/utils/TokenTreeCheckUtils.hx b/src/tokentree/utils/TokenTreeCheckUtils.hx index 3f81158..24cf6f3 100644 --- a/src/tokentree/utils/TokenTreeCheckUtils.hx +++ b/src/tokentree/utils/TokenTreeCheckUtils.hx @@ -494,6 +494,75 @@ class TokenTreeCheckUtils { return ObjectDecl; } + public static function getBkOpenType(token:TokenTree):BkOpenType { + if (token == null) { + return Unknown; + } + if (token.tokenTypeCache.bkOpenType != null) { + return token.tokenTypeCache.bkOpenType; + } + + var type:BkOpenType = determineBkOpenType(token); + token.tokenTypeCache.bkOpenType = type; + return type; + } + + static function determineBkOpenType(token:TokenTree):BkOpenType { + if (token == null) { + return Unknown; + } + var parent:TokenTree = token.parent; + if (parent == null || parent.tok == Root) { + return determinBkChildren(token); + } + return switch (parent.tok) { + case Kwd(KwdThis): ArrayAccess; + case BkOpen | BrOpen | POpen: determinBkChildren(token); + case DblDot: determinBkChildren(token); + case Binop(_): determinBkChildren(token); + case Kwd(_): determinBkChildren(token); + case Sharp(_): determinBkChildren(token); + default: ArrayAccess; + } + } + + static function determinBkChildren(token:TokenTree):BkOpenType { + if (!token.hasChildren()) { + return Unknown; + } + for (child in token.children) { + switch (child.tok) { + case Comment(_) | CommentLine(_): + case Kwd(KwdFor) | Kwd(KwdWhile): + return Comprehension; + default: + break; + } + } + var bkDepth:Int = -1; + var childArrows:Array = token.filterCallback(function(token:TokenTree, index:Int):FilterResult { + return switch (token.tok) { + case Binop(OpArrow): if (bkDepth == 0) { + FoundSkipSubtree; + } + else { + SkipSubtree; + } + case BkOpen: + bkDepth++; + GoDeeper; + case BkClose: + bkDepth--; + GoDeeper; + default: GoDeeper; + } + }); + if (childArrows.length > 0) { + return MapLiteral; + } + return ArrayLiteral; + } + public static function getPOpenType(token:TokenTree):POpenType { if (token == null) { return Expression; @@ -1059,6 +1128,14 @@ enum BrOpenType { Unknown; } +enum BkOpenType { + ArrayAccess; + ArrayLiteral; + Comprehension; + MapLiteral; + Unknown; +} + enum POpenType { At; Parameter; diff --git a/test/tokentree/utils/TokenTreeCheckUtilsTest.hx b/test/tokentree/utils/TokenTreeCheckUtilsTest.hx index e596656..41bc28f 100644 --- a/test/tokentree/utils/TokenTreeCheckUtilsTest.hx +++ b/test/tokentree/utils/TokenTreeCheckUtilsTest.hx @@ -4,6 +4,7 @@ import haxe.PosInfos; import tokentree.TokenTree.FilterResult; import tokentree.TokenTreeBuilderParsingTest.TokenTreeBuilderParsingTests; import tokentree.utils.TokenTreeCheckUtils.ArrowType; +import tokentree.utils.TokenTreeCheckUtils.BkOpenType; import tokentree.utils.TokenTreeCheckUtils.BrOpenType; import tokentree.utils.TokenTreeCheckUtils.ColonType; import tokentree.utils.TokenTreeCheckUtils.POpenType; @@ -628,6 +629,111 @@ class TokenTreeCheckUtilsTest implements ITest { Assert.isFalse(TokenTreeCheckUtils.isMetadata(tokens[index++])); } + public function testMixedBkOpenTypes() { + var root:TokenTree = assertCodeParses(TokenTreeCheckUtilsTests.MIXED_BKOPEN_TYPES); + Assert.isFalse(root.inserted); + + var allBk:Array = root.filterCallback(function(token:TokenTree, depth:Int):FilterResult { + return switch (token.tok) { + case BkOpen: FoundGoDeeper; + default: GoDeeper; + } + }); + Assert.equals(43, allBk.length); + var index:Int = 0; + + // a[1]; + Assert.equals(BkOpenType.ArrayAccess, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // a['xx']; + Assert.equals(BkOpenType.ArrayAccess, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // call()['xx']; + Assert.equals(BkOpenType.ArrayAccess, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // this['xx']; + Assert.equals(BkOpenType.ArrayAccess, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = []; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [1, 2, 3, 4]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = {a: [1, 2, 3, 4]}; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [{a:1}, {a:2}, {a:3}, {a:4}]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [{a: [1, 2, 3]}, {a: [1, 2, 3]}, {a: [1, 2, 3]}, {a: [1, 2, 3]}]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [[1], [2], [3], [4]]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // if (false) return [1, 2, 3, 4]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [for (i in 0...10) i]; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [while (i < 10) i++]; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [for (a in 1...11) for (b in 2...4) if (a % b == 0) a + "/" + b]; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [for (i in 0...5) i => 'number ${i}']; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [/* comment */for (i in 0...10) i]; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [while (i < 5) i => 'number ${i++}']; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [/* comment */while (i < 10) i++]; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [for (x in 0...5) for (y in 0...5) if (x != y) x => y]; + Assert.equals(BkOpenType.Comprehension, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = ['a' => 'aaa', 'b' => 'bbb', 'c' => 'ccc']; + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [{a: 1} => 'aaa', {a:2} => 'bbb', {a:3} => 'ccc']; + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + // var a = [['a'] => 'aaa', ['b'] => 'bbb', ['c'] => 'ccc']; + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [['a' => 'aaa'], ['b' => 'bbb'], ['c' => 'ccc']]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = [if (foo) bar else foo, if (foo) bar else foo, if (foo) bar else foo]; + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + + // var a = hxargs.Args.generate([ + // @doc('Dont format, only check if files are formatted correctly') + // ['--check'] => function() mode = Check, + // + // #if debug + // @doc('Dont format, only check if formatting is stable') + // ['--check-stability'] => function() mode = CheckStability, + // #end + // + // @doc('Dont format, only check if files are formatted correctly') + // ['--check'] => function() mode = Check, + // ]); + Assert.equals(BkOpenType.MapLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + Assert.equals(BkOpenType.ArrayLiteral, TokenTreeCheckUtils.getBkOpenType(allBk[index++])); + } + @Test public function testFindLastBinop() { function assertLastBinop(code:String, binop:String, offset:Int = 0, ?pos:PosInfos):Void { @@ -1090,4 +1196,66 @@ enum abstract TokenTreeCheckUtilsTests(String) to String { var foo:Int; } "; + + var MIXED_BKOPEN_TYPES = " + class Main { + static function main() { + a[1]; + a['xx']; + call()['xx']; + this['xx']; + + var a = []; + var a = [1, 2, 3, 4]; + var a = {a: [1, 2, 3, 4]}; + var a = [{a:1}, {a:2}, {a:3}, {a:4}]; + var a = [{a:[1, 2, 3]}, {a:[1, 2, 3]}, {a:[1, 2, 3]}, {a:[1, 2, 3]}]; + + var a = [[1], [2], [3], [4]]; + if (false) return [1, 2, 3, 4]; + + var a = [for (i in 0...10) i]; + var a = [/* comment */ for (i in 0...10) i]; + var a = [while (i < 10) i++]; + var a = [/* comment */ while (i < 10) i++]; + var a = [for (a in 1...11) for (b in 2...4) if (a % b == 0) a + ' / ' + b]; + + var a = [for (i in 0...5) i => 'number ${i}']; + var a = [while (i < 5) i => 'number ${i++}']; + var a = [for (x in 0...5) for (y in 0...5) if (x != y) x => y]; + + var a = ['a' => 'aaa', 'b' => 'bbb', 'c' => 'ccc']; + var a = [{a: 1} => 'aaa', {a:2} => 'bbb', {a:3} => 'ccc']; + var a = [['a'] => 'aaa', ['b'] => 'bbb', ['c'] => 'ccc']; + + var a = [['a' => 'aaa'], ['b' => 'bbb'], ['c' => 'ccc']]; + + var a = [if (foo) bar else foo, if (foo) bar else foo, if (foo) bar else foo]; + var a = function () { + [ + if (foo) bar else foo, + if (foo) bar else foo, + if (foo) bar else foo, + if (foo) bar else foo, + if (foo) bar else foo + ]; + } + + var a = hxargs.Args.generate([ + @doc('Dont format, only check if files are formatted correctly') + ['--check'] => function() mode = Check, + + #if debug + @doc('Dont format, only check if formatting is stable') + ['--check-stability'] => function() mode = CheckStability, + #end + + @doc('Dont format, only check if files are formatted correctly') + ['--check'] => function() mode = Check, + ]); + + + } + } + "; } \ No newline at end of file