From 3a907da5468323bc10e2ed6ecff8f24432a302a8 Mon Sep 17 00:00:00 2001 From: AlexHaxe Date: Wed, 15 Feb 2023 21:34:51 +0100 Subject: [PATCH] fixed null pointer issues (#666) * fixed null pointer issues --- .haxerc | 2 +- CHANGELOG.md | 4 + haxe_libraries/tokentree.hxml | 6 +- haxelib.json | 4 +- package-lock.json | 4 +- package.json | 2 +- resources/hxformat-schema.json | 4 +- src/formatter/config/WrapConfig.hx | 4 +- src/formatter/marker/MarkEmptyLines.hx | 16 ++- src/formatter/marker/wrapping/MarkWrapping.hx | 73 +++++----- .../marker/wrapping/MarkWrappingBase.hx | 88 +++++++----- .../type_parameter_detection.hxtest | 131 ++++++++++++++++++ ...238_keep_wrapping_call_one_per_line.hxtest | 47 +++++++ .../wrapping/wrapping_call_with_new.hxtest | 40 ++++++ 14 files changed, 339 insertions(+), 86 deletions(-) create mode 100644 test/testcases/emptylines/type_parameter_detection.hxtest create mode 100644 test/testcases/wrapping/issue_238_keep_wrapping_call_one_per_line.hxtest create mode 100644 test/testcases/wrapping/wrapping_call_with_new.hxtest diff --git a/.haxerc b/.haxerc index 98d2f73..49f7327 100644 --- a/.haxerc +++ b/.haxerc @@ -1,4 +1,4 @@ { - "version": "5645ecc", + "version": "a985681", "resolveLibs": "scoped" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ff2a533..e01b966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## dev branch / next version (1.x.x) +## version 1.14.5 (2023-02-15) + +- Fixed null pointer issues ([#666](https://github.com/HaxeCheckstyle/haxe-formatter/issues/666)) + ## version 1.14.4 (2022-12-14) - Refactored PosInfosMacro to limit number of invocations of inner loop diff --git a/haxe_libraries/tokentree.hxml b/haxe_libraries/tokentree.hxml index 63a42eb..c0e9ddf 100644 --- a/haxe_libraries/tokentree.hxml +++ b/haxe_libraries/tokentree.hxml @@ -1,3 +1,3 @@ -# @install: lix --silent download "haxelib:/tokentree#1.2.5" into tokentree/1.2.5/haxelib --cp ${HAXE_LIBCACHE}/tokentree/1.2.5/haxelib/src --D tokentree=1.2.5 \ No newline at end of file +# @install: lix --silent download "haxelib:/tokentree#1.2.7" into tokentree/1.2.7/haxelib +-cp ${HAXE_LIBCACHE}/tokentree/1.2.7/haxelib/src +-D tokentree=1.2.7 \ No newline at end of file diff --git a/haxelib.json b/haxelib.json index 1ca760a..c9ffe87 100644 --- a/haxelib.json +++ b/haxelib.json @@ -8,8 +8,8 @@ "style" ], "description": "A code formatter for Haxe", - "version": "1.14.4", - "releasenote": "fixed some whitespace formatting issues - see CHANGELOG for details.", + "version": "1.14.5", + "releasenote": "fixed null pointer issue - see CHANGELOG for details.", "contributors": [ "AlexHaxe", "Gama11" diff --git a/package-lock.json b/package-lock.json index 9d2b5f0..1e5ae6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@haxecheckstyle/haxe-formatter", - "version": "1.14.4", + "version": "1.14.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@haxecheckstyle/haxe-formatter", - "version": "1.14.4", + "version": "1.14.5", "license": "MIT", "bin": { "haxe-formatter": "bin/formatter.js" diff --git a/package.json b/package.json index 8bc9f46..8ec8725 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@haxecheckstyle/haxe-formatter", - "version": "1.14.4", + "version": "1.14.5", "description": "A code formatter for Haxe", "repository": { "type": "git", diff --git a/resources/hxformat-schema.json b/resources/hxformat-schema.json index 0774b85..5dead17 100644 --- a/resources/hxformat-schema.json +++ b/resources/hxformat-schema.json @@ -576,11 +576,11 @@ "formatter.config.WrapConditionType": { "anyOf": [ { - "description": "condition matches if item count is larger than or equal n characters", + "description": "condition matches if item count is larger than or equal n items", "const": "itemCount >= n" }, { - "description": "condition matches if item count is less than or equal n characters", + "description": "condition matches if item count is less than or equal n items", "const": "itemCount <= n" }, { diff --git a/src/formatter/config/WrapConfig.hx b/src/formatter/config/WrapConfig.hx index de432da..38cec7b 100644 --- a/src/formatter/config/WrapConfig.hx +++ b/src/formatter/config/WrapConfig.hx @@ -516,12 +516,12 @@ typedef WrapCondition = { enum abstract WrapConditionType(String) { /** - condition matches if item count is larger than or equal n characters + condition matches if item count is larger than or equal n items **/ var ItemCountLargerThan = "itemCount >= n"; /** - condition matches if item count is less than or equal n characters + condition matches if item count is less than or equal n items **/ var ItemCountLessThan = "itemCount <= n"; diff --git a/src/formatter/marker/MarkEmptyLines.hx b/src/formatter/marker/MarkEmptyLines.hx index 1c22aed..378568c 100644 --- a/src/formatter/marker/MarkEmptyLines.hx +++ b/src/formatter/marker/MarkEmptyLines.hx @@ -1063,7 +1063,9 @@ class MarkEmptyLines extends MarkerBase { parsedCode.root.filterCallback(function(token:TokenTree, index:Int):FilterResult { switch (token.tok) { case Kwd(KwdIf): - removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); + if ((token.children != null) && (token.children.length > 0)) { + removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); + } var block:Null = token.access().firstOf(Kwd(KwdElse)).previousSibling().token; if (block != null) { removeEmptyLinesAroundBlock(block, Keep, config.emptyLines.afterBlocks); @@ -1075,13 +1077,17 @@ class MarkEmptyLines extends MarkerBase { removeEmptyLinesAroundBlock(block, config.emptyLines.beforeBlocks, Keep); case Kwd(KwdFunction): case Kwd(KwdFor): - removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); + if ((token.children != null) && (token.children.length > 0)) { + removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); + } case Kwd(KwdDo): removeEmptyLinesAroundBlock(token.getFirstChild(), config.emptyLines.beforeBlocks, Keep); var block:Null = token.access().lastChild().previousSibling().token; removeEmptyLinesAroundBlock(block, Keep, config.emptyLines.afterBlocks); case Kwd(KwdWhile): - if ((token.parent == null) || (!token.parent.tok.match(Kwd(KwdDo)))) { + if ((token.children != null) + && (token.children.length > 0) + && (token.parent == null || !token.parent.tok.match(Kwd(KwdDo)))) { removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); } case Kwd(KwdTry): @@ -1089,7 +1095,9 @@ class MarkEmptyLines extends MarkerBase { var block:Null = token.access().lastChild().previousSibling().token; removeEmptyLinesAroundBlock(block, Keep, config.emptyLines.afterBlocks); case Kwd(KwdCatch): - removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); + if ((token.children != null) && (token.children.length > 0)) { + removeEmptyLinesAroundBlock(token.children[1], config.emptyLines.beforeBlocks, Keep); + } default: } return GoDeeper; diff --git a/src/formatter/marker/wrapping/MarkWrapping.hx b/src/formatter/marker/wrapping/MarkWrapping.hx index f3e12d6..c183323 100644 --- a/src/formatter/marker/wrapping/MarkWrapping.hx +++ b/src/formatter/marker/wrapping/MarkWrapping.hx @@ -577,21 +577,23 @@ class MarkWrapping extends MarkWrappingBase { } } var first:Bool = true; - for (child in itemStart.children) { - switch (child.tok) { - case Binop(OpBoolAnd), Binop(OpBoolOr): - if (first) { - itemStart = firstItemStart; - first = false; - } - items.push(makeWrappableItem(itemStart, child)); - var next:Null = getNextToken(child); - if (next == null) { - return; - } - itemStart = next.token; - default: - continue; + if (itemStart.children != null) { + for (child in itemStart.children) { + switch (child.tok) { + case Binop(OpBoolAnd), Binop(OpBoolOr): + if (first) { + itemStart = firstItemStart; + first = false; + } + items.push(makeWrappableItem(itemStart, child)); + var next:Null = getNextToken(child); + if (next == null) { + return; + } + itemStart = next.token; + default: + continue; + } } } items.push(makeWrappableItem(itemStart, TokenTreeCheckUtils.getLastToken(itemStart))); @@ -630,13 +632,15 @@ class MarkWrapping extends MarkWrappingBase { return; } var itemStart:TokenTree = next.token; - for (child in itemContainer.children) { - switch (child.tok) { - case DblDot: - break; - default: - var lastToken:TokenTree = TokenTreeCheckUtils.getLastToken(child); - items.push(makeWrappableItem(child, lastToken)); + if (itemContainer.children != null) { + for (child in itemContainer.children) { + switch (child.tok) { + case DblDot: + break; + default: + var lastToken:TokenTree = TokenTreeCheckUtils.getLastToken(child); + items.push(makeWrappableItem(child, lastToken)); + } } } queueWrapping({ @@ -709,17 +713,19 @@ class MarkWrapping extends MarkWrappingBase { return; } var itemStart:TokenTree = next.token; - for (child in itemContainer.children) { - switch (child.tok) { - case Binop(OpAdd), Binop(OpSub): - items.push(makeWrappableItem(itemStart, child)); - var next:Null = getNextToken(child); - if (next == null) { + if (itemContainer.children != null) { + for (child in itemContainer.children) { + switch (child.tok) { + case Binop(OpAdd), Binop(OpSub): + items.push(makeWrappableItem(itemStart, child)); + var next:Null = getNextToken(child); + if (next == null) { + continue; + } + itemStart = next.token; + default: continue; - } - itemStart = next.token; - default: - continue; + } } } items.push(makeWrappableItem(itemStart, TokenTreeCheckUtils.getLastToken(itemStart))); @@ -850,6 +856,9 @@ class MarkWrapping extends MarkWrappingBase { }); for (v in allVars) { var items:Array = []; + if (v.children == null) { + continue; + } for (child in v.children) { var endToken:TokenTree = TokenTreeCheckUtils.getLastToken(child); items.push(makeWrappableItem(child, endToken)); diff --git a/src/formatter/marker/wrapping/MarkWrappingBase.hx b/src/formatter/marker/wrapping/MarkWrappingBase.hx index 90b42d2..093c66f 100644 --- a/src/formatter/marker/wrapping/MarkWrappingBase.hx +++ b/src/formatter/marker/wrapping/MarkWrappingBase.hx @@ -36,25 +36,27 @@ class MarkWrappingBase extends MarkerBase { } } noWrappingBetween(open, close); - for (child in open.children) { - switch (child.tok) { - case PClose, BrClose, BkClose: - break; - case Binop(OpGt): - continue; - case Semicolon, Comma: - continue; - default: - } - var lastChild:Null = TokenTreeCheckUtils.getLastToken(child); - if (lastChild == null) { - continue; - } else { - switch (lastChild.tok) { - case Comma, Semicolon: - noLineEndAfter(lastChild); + if (open.children != null) { + for (child in open.children) { + switch (child.tok) { + case PClose, BrClose, BkClose: + break; + case Binop(OpGt): + continue; + case Semicolon, Comma: + continue; default: } + var lastChild:Null = TokenTreeCheckUtils.getLastToken(child); + if (lastChild == null) { + continue; + } else { + switch (lastChild.tok) { + case Comma, Semicolon: + noLineEndAfter(lastChild); + default: + } + } } } noLineEndBefore(close); @@ -87,26 +89,29 @@ class MarkWrappingBase extends MarkerBase { public function keep(open:TokenTree, close:TokenTree, addIndent:Int) { noWrappingBetween(open, close); - for (child in open.children) { - var last:Bool = false; - switch (child.tok) { - case PClose, BrClose, BkClose: - last = true; - case Binop(OpGt): - continue; - case Semicolon, Comma: - continue; - default: - } - if (parsedCode.isOriginalNewlineBefore(child)) { - lineEndBefore(child); - additionalIndent(child, addIndent); - } else { - noLineEndBefore(child); - wrapBefore(child, false); - } - if (last) { - break; + + if (open.children != null) { + for (child in open.children) { + var last:Bool = false; + switch (child.tok) { + case PClose, BrClose, BkClose: + last = true; + case Binop(OpGt): + continue; + case Semicolon, Comma: + continue; + default: + } + if (parsedCode.isOriginalNewlineBefore(child)) { + lineEndBefore(child); + additionalIndent(child, addIndent); + } else { + noLineEndBefore(child); + wrapBefore(child, false); + } + if (last) { + break; + } } } if (!parsedCode.isOriginalNewlineBefore(open)) { @@ -182,6 +187,9 @@ class MarkWrappingBase extends MarkerBase { if (!keepFirst) { lineEndAfter(open); } + if (open.children == null) { + return; + } for (child in open.children) { switch (child.tok) { case PClose, BrClose, BkClose: @@ -452,6 +460,9 @@ class MarkWrappingBase extends MarkerBase { var indent:Int = indenter.calcIndent(lineStart); var lineLength:Int = calcLineLengthBefore(open) + indenter.calcAbsoluteIndent(indent + addIndent); var first:Bool = true; + if (open.children == null) { + return; + } for (child in open.children) { switch (child.tok) { case PClose, BrClose, BkClose: @@ -552,6 +563,9 @@ class MarkWrappingBase extends MarkerBase { function makeWrappableItems(token:TokenTree):Array { var items:Array = []; var lastIndex:Int = -1; + if (token.children == null) { + return items; + } for (child in token.children) { switch (child.tok) { case PClose, BkClose, BrClose: diff --git a/test/testcases/emptylines/type_parameter_detection.hxtest b/test/testcases/emptylines/type_parameter_detection.hxtest new file mode 100644 index 0000000..ee064c0 --- /dev/null +++ b/test/testcases/emptylines/type_parameter_detection.hxtest @@ -0,0 +1,131 @@ +{} + +--- + +package stdgo.math.big; + +import stdgo.Chan; +import stdgo.Error; +import stdgo.Go; +import stdgo.GoArray; +import stdgo.GoMap; +import stdgo.GoString; +import stdgo.Pointer; +import stdgo.Slice; +import stdgo.StdGoTypes; + +/** + // construct table of powers of bb*leafSize to use in subdivisions. +**/ +private function _divisors(_m:GoInt, _b:Word, _ndigits:GoInt, _bb:Word):Slice { + if ((_leafSize == (0 : GoInt)) || (_m <= _leafSize)) { + return (null : Slice); + }; + var _k:GoInt = (1 : GoInt); + { + var _words:GoInt = _leafSize; + Go.cfor((_words < (_m >> (("1" : GoUInt64) : GoUInt64))) + && (_k < _cacheBase10._table.length), _words = _words << (("1" : GoUInt64)), { + _k++; + }); + }; + var _table:Slice = (null : Slice); + if (_b == ((("10" : GoUInt) : Word))) { + _cacheBase10.lock(); + _table = (_cacheBase10._table.__slice__((0 : GoInt), _k) : Slice); + } else { + _table = new Slice((_k : GoInt).toBasic(), 0, ...[for (i in 0 ... (_k : GoInt).toBasic()) ({} : T_divisor)]); + }; + if (_table[(_k - (1 : GoInt) : GoInt)]._ndigits == ((0 : GoInt))) { + var _larger:T_nat = new T_nat(0, 0); + { + var _i:GoInt = (0 : GoInt); + Go.cfor(_i < _k, _i++, { + if (_table[(_i : GoInt)]._ndigits == ((0 : GoInt))) { + if (_i == ((0 : GoInt))) { + _table[(0 : GoInt)]._bbb = (new T_nat(0, 0) : T_nat)._expWW(_bb, (_leafSize : Word)); + _table[(0 : GoInt)]._ndigits = _ndigits * _leafSize; + } else { + _table[(_i : GoInt)]._bbb = (new T_nat(0, 0) : T_nat)._sqr(_table[(_i - (1 : GoInt) : GoInt)]._bbb); + _table[(_i : GoInt)]._ndigits = (2 : GoInt) * _table[(_i - (1 : GoInt) : GoInt)]._ndigits; + }; + _larger = (new T_nat(0, 0) : T_nat)._set(_table[(_i : GoInt)]._bbb); + while (_mulAddVWW(_larger, _larger, _b, (("0" : GoUInt) : Word)) == ((("0" : GoUInt) : Word))) { + _table[(_i : GoInt)]._bbb = _table[(_i : GoInt)]._bbb._set(_larger); + _table[(_i : GoInt)]._ndigits++; + }; + _table[(_i : GoInt)]._nbits = _table[(_i : GoInt)]._bbb._bitLen(); + }; + }); + }; + }; + if (_b == ((("10" : GoUInt) : Word))) { + _cacheBase10.unlock(); + }; + return _table; +} + +--- + +package stdgo.math.big; + +import stdgo.Chan; +import stdgo.Error; +import stdgo.Go; +import stdgo.GoArray; +import stdgo.GoMap; +import stdgo.GoString; +import stdgo.Pointer; +import stdgo.Slice; +import stdgo.StdGoTypes; + +/** + // construct table of powers of bb*leafSize to use in subdivisions. +**/ +private function _divisors(_m:GoInt, _b:Word, _ndigits:GoInt, _bb:Word):Slice { + if ((_leafSize == (0 : GoInt)) || (_m <= _leafSize)) { + return (null : Slice); + }; + var _k:GoInt = (1 : GoInt); + { + var _words:GoInt = _leafSize; + Go.cfor((_words < (_m >> (("1" : GoUInt64) : GoUInt64))) + && (_k < _cacheBase10._table.length), _words = _words << (("1" : GoUInt64)), { + _k++; + }); + }; + var _table:Slice = (null : Slice); + if (_b == ((("10" : GoUInt) : Word))) { + _cacheBase10.lock(); + _table = (_cacheBase10._table.__slice__((0 : GoInt), _k) : Slice); + } else { + _table = new Slice((_k : GoInt).toBasic(), 0, ...[for (i in 0...(_k : GoInt).toBasic()) ({} : T_divisor)]); + }; + if (_table[(_k - (1 : GoInt) : GoInt)]._ndigits == ((0 : GoInt))) { + var _larger:T_nat = new T_nat(0, 0); + { + var _i:GoInt = (0 : GoInt); + Go.cfor(_i < _k, _i++, { + if (_table[(_i : GoInt)]._ndigits == ((0 : GoInt))) { + if (_i == ((0 : GoInt))) { + _table[(0 : GoInt)]._bbb = (new T_nat(0, 0) : T_nat)._expWW(_bb, (_leafSize : Word)); + _table[(0 : GoInt)]._ndigits = _ndigits * _leafSize; + } else { + _table[(_i : GoInt)]._bbb = (new T_nat(0, 0) : T_nat)._sqr(_table[(_i - (1 : GoInt) : GoInt)]._bbb); + _table[(_i : GoInt)]._ndigits = (2 : GoInt) * _table[(_i - (1 : GoInt) : GoInt)]._ndigits; + }; + _larger = (new T_nat(0, 0) : T_nat)._set(_table[(_i : GoInt)]._bbb); + while (_mulAddVWW(_larger, _larger, _b, (("0" : GoUInt) : Word)) == ((("0" : GoUInt) : Word))) { + _table[(_i : GoInt)]._bbb = _table[(_i : GoInt)]._bbb._set(_larger); + _table[(_i : GoInt)]._ndigits++; + }; + _table[(_i : GoInt)]._nbits = _table[(_i : GoInt)]._bbb._bitLen(); + }; + }); + }; + }; + if (_b == ((("10" : GoUInt) : Word))) { + _cacheBase10.unlock(); + }; + return _table; +} diff --git a/test/testcases/wrapping/issue_238_keep_wrapping_call_one_per_line.hxtest b/test/testcases/wrapping/issue_238_keep_wrapping_call_one_per_line.hxtest new file mode 100644 index 0000000..1727960 --- /dev/null +++ b/test/testcases/wrapping/issue_238_keep_wrapping_call_one_per_line.hxtest @@ -0,0 +1,47 @@ +{ + "wrapping": { + "callParameter": { + "defaultWrap": "onePerLine", + "rules": [] + } + } +} + +--- + +class Main { + static function main():Void { + g.drawImage( + tileset.img, + ix * tileSize, iy * tileSize, + tileset.img, + ix * tileSize, iy * tileSize, + tileset.img, + ix * tileSize, iy * tileSize, + ix * tileSize, iy * tileSize, + tileSize, tileSize + ); + } +} + +--- + +class Main { + static function main():Void { + g.drawImage( + tileset.img, + ix * tileSize, + iy * tileSize, + tileset.img, + ix * tileSize, + iy * tileSize, + tileset.img, + ix * tileSize, + iy * tileSize, + ix * tileSize, + iy * tileSize, + tileSize, + tileSize + ); + } +} diff --git a/test/testcases/wrapping/wrapping_call_with_new.hxtest b/test/testcases/wrapping/wrapping_call_with_new.hxtest new file mode 100644 index 0000000..79611e5 --- /dev/null +++ b/test/testcases/wrapping/wrapping_call_with_new.hxtest @@ -0,0 +1,40 @@ +{ + "wrapping": { + "callParameter": { + "defaultWrap": "fillLine", + "rules": [ + { + "conditions": [ + { + "cond": "anyItemLength >= n", + "value": 60 + } + ], + "type": "onePerLine" + } + ] + } + } +} + +--- + +class Main { + static function main() { + builder.addComponents(new ButtonBuilder().setCustomId('snippet_left').setLabel('Prev').setStyle(Primary), new ButtonBuilder().setCustomId('snippet_right').setLabel('Next').setStyle(Primary)); + builder.addComponents(new ButtonBuilder().setCustomId('snippet_left'), new ButtonBuilder().setCustomId('snippet_right'), new ButtonBuilder().setCustomId('snippet_left'), new ButtonBuilder().setCustomId('snippet_right')); + } +} + +--- + +class Main { + static function main() { + builder.addComponents( + new ButtonBuilder().setCustomId('snippet_left').setLabel('Prev').setStyle(Primary), + new ButtonBuilder().setCustomId('snippet_right').setLabel('Next').setStyle(Primary) + ); + builder.addComponents(new ButtonBuilder().setCustomId('snippet_left'), new ButtonBuilder().setCustomId('snippet_right'), + new ButtonBuilder().setCustomId('snippet_left'), new ButtonBuilder().setCustomId('snippet_right')); + } +}