From 3403fbe444fcda71e43bacc219d03ba6a544304c Mon Sep 17 00:00:00 2001 From: mmorozov Date: Thu, 22 Feb 2024 15:39:41 +0300 Subject: [PATCH] Linter autofix for comma operator Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I93U5B Tests: Were changed, according to new autofix Signed-off-by: mmorozov --- ets2panda/linter/lib/Autofixer.ts | 32 +++ ets2panda/linter/lib/TypeScriptLinter.ts | 13 + .../linter/lib/autofixes/AutofixTitles.ts | 1 + ets2panda/linter/test_rules/rule71.ts | 41 +++ .../linter/test_rules/rule71.ts.autofix.json | 257 ++++++++++++++++++ .../linter/test_rules/rule71.ts.autofix.skip | 14 - ets2panda/linter/test_rules/rule71.ts.json | 117 ++++++-- 7 files changed, 441 insertions(+), 34 deletions(-) create mode 100644 ets2panda/linter/test_rules/rule71.ts.autofix.json delete mode 100644 ets2panda/linter/test_rules/rule71.ts.autofix.skip diff --git a/ets2panda/linter/lib/Autofixer.ts b/ets2panda/linter/lib/Autofixer.ts index 7ceef597a8..5146e131e5 100644 --- a/ets2panda/linter/lib/Autofixer.ts +++ b/ets2panda/linter/lib/Autofixer.ts @@ -147,6 +147,38 @@ export function fixTypeAssertion(typeAssertion: ts.TypeAssertion): Autofix { return { start: typeAssertion.getStart(), end: typeAssertion.getEnd(), replacementText: text }; } +export function fixCommaOperator(tsNode: ts.Node): Autofix[] { + const tsExprNode = tsNode as ts.BinaryExpression; + const text = recursiveCommaOperator(tsExprNode); + return [{ start: tsExprNode.parent.getStart(), end: tsExprNode.parent.getEnd(), replacementText: text }]; +} + +function recursiveCommaOperator(tsExprNode: ts.BinaryExpression): string { + let text = ''; + if (tsExprNode.operatorToken.kind !== ts.SyntaxKind.CommaToken) { + const midExpr = ts.factory.createExpressionStatement(tsExprNode); + const midText = printer.printNode(ts.EmitHint.Unspecified, midExpr, tsExprNode.getSourceFile()); + return midText; + } + + if (tsExprNode.left.kind === ts.SyntaxKind.BinaryExpression) { + text += recursiveCommaOperator(tsExprNode.left as ts.BinaryExpression); + + const rightExpr = ts.factory.createExpressionStatement(tsExprNode.right); + const rightText = printer.printNode(ts.EmitHint.Unspecified, rightExpr, tsExprNode.getSourceFile()); + text += '\n' + rightText; + } else { + const leftExpr = ts.factory.createExpressionStatement(tsExprNode.left); + const rightExpr = ts.factory.createExpressionStatement(tsExprNode.right); + + const leftText = printer.printNode(ts.EmitHint.Unspecified, leftExpr, tsExprNode.getSourceFile()); + const rightText = printer.printNode(ts.EmitHint.Unspecified, rightExpr, tsExprNode.getSourceFile()); + text = leftText + '\n' + rightText; + } + + return text; +} + const printer: ts.Printer = ts.createPrinter({ omitTrailingSemicolon: false, removeComments: false, diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index 29b57204b2..3cbbb19050 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -1125,6 +1125,10 @@ export class TypeScriptLinter { while (tsParentNode && tsParentNode.kind === ts.SyntaxKind.BinaryExpression) { tsExprNode = tsParentNode; tsParentNode = tsExprNode.parent; + if ((tsExprNode as ts.BinaryExpression).operatorToken.kind === ts.SyntaxKind.CommaToken) { + // Need to return if one comma enclosed in expression with another comma to avoid multiple reports on one line + return; + } } if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ForStatement) { const tsForNode = tsParentNode as ts.ForStatement; @@ -1132,6 +1136,15 @@ export class TypeScriptLinter { return; } } + if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ExpressionStatement) { + let autofix: Autofix[] | undefined = undefined; + if (this.autofixesInfo.shouldAutofix(tsExprNode, FaultID.CommaOperator)) { + autofix = Autofixer.fixCommaOperator(tsExprNode); + } + this.incrementCounters(tsExprNode, FaultID.CommaOperator, true, autofix); + return; + } + this.incrementCounters(tsBinaryExpr as ts.Node, FaultID.CommaOperator); } diff --git a/ets2panda/linter/lib/autofixes/AutofixTitles.ts b/ets2panda/linter/lib/autofixes/AutofixTitles.ts index efb3ee2480..4c1500322b 100644 --- a/ets2panda/linter/lib/autofixes/AutofixTitles.ts +++ b/ets2panda/linter/lib/autofixes/AutofixTitles.ts @@ -21,6 +21,7 @@ export const cookBookRefToFixTitle: Map = new Map([ [29, 'Replace with dot notation'], [46, 'Convert to arrow function'], [53, 'Replace to \'as\' expression'], + [71, 'Split expression around \',\' operator'], [79, 'Remove type annotation'], [90, 'Annotate return type'], [120, 'Replace with explicit import'] diff --git a/ets2panda/linter/test_rules/rule71.ts b/ets2panda/linter/test_rules/rule71.ts index 41d7ee0192..634ff4c7a0 100644 --- a/ets2panda/linter/test_rules/rule71.ts +++ b/ets2panda/linter/test_rules/rule71.ts @@ -19,8 +19,11 @@ for (let i = 0, j = 0; i < 10; ++i, j += 2) { } let x = 0 + +// Error, no autofix x = (++x, x++) +// No error for (let i = 0, j = 0; i < 10; ++i, j += 2) { console.log(i) console.log(j) @@ -31,14 +34,52 @@ let x2 = 0 x2 = x2++ let c = () => 33; + +// Error, no autofix const a = (1, b = 2, c()); + +// Error, no autofix const r = (c(), b, 1) class Test { + // Error, no autofix static readonly sr = (1, c(), 2); + + // Error, no autofix field1 = (1, 2, c()); method() { + // Error, no autofix this.field1 = (c(), sr, 1); } } + +// Error, autofix +x++, x-- + +// Error, autofix +x++, x--, ++x, --x, x + +// Error, no autofix +if (x++, x === 1) { + // Error, autofix + x++, x--, ++x, --x, x +} + +// Error, autofix +x++ + x--, ++x, --x; + +// Error, autofix +++x, x-- + x++, --x + +// Error, autofix +++x, --x, x-- + x++; + +// Error, autofix +x === x, --x, x === x, x++, x === x + +// Error, autofix +x instanceof number, --x, x instanceof number, x++, x instanceof number; + +// Error, autofix +x in x, --x, x in x, x++, x in x diff --git a/ets2panda/linter/test_rules/rule71.ts.autofix.json b/ets2panda/linter/test_rules/rule71.ts.autofix.json new file mode 100644 index 0000000000..f61b9fca33 --- /dev/null +++ b/ets2panda/linter/test_rules/rule71.ts.autofix.json @@ -0,0 +1,257 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 24, + "column": 6, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 39, + "column": 12, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 42, + "column": 12, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 46, + "column": 27, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 49, + "column": 15, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 53, + "column": 24, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 58, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1219, + "end": 1227, + "replacementText": "// Error, autofix\nx++;\nx--;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 61, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1247, + "end": 1268, + "replacementText": "// Error, autofix\nx++;\nx--;\n++x;\n--x;\nx;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 64, + "column": 5, + "problem": "CommaOperator", + "autofixable": false, + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 66, + "column": 5, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1337, + "end": 1358, + "replacementText": "// Error, autofix\nx++;\nx--;\n++x;\n--x;\nx;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 70, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1380, + "end": 1400, + "replacementText": "// Error, autofix\nx++ + x--;\n++x;\n--x;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 73, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1420, + "end": 1439, + "replacementText": "// Error, autofix\n++x;\nx-- + x++;\n--x;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 76, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1459, + "end": 1479, + "replacementText": "// Error, autofix\n++x;\n--x;\nx-- + x++;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 79, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1499, + "end": 1534, + "replacementText": "// Error, autofix\nx === x;\n--x;\nx === x;\nx++;\nx === x;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 82, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1554, + "end": 1626, + "replacementText": "// Error, autofix\nx instanceof number;\n--x;\nx instanceof number;\nx++;\nx instanceof number;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 82, + "column": 3, + "problem": "InstanceofUnsupported", + "autofixable": false, + "suggest": "", + "rule": "\"instanceof\" operator is partially supported (arkts-instanceof-ref-types)" + }, + { + "line": 82, + "column": 29, + "problem": "InstanceofUnsupported", + "autofixable": false, + "suggest": "", + "rule": "\"instanceof\" operator is partially supported (arkts-instanceof-ref-types)" + }, + { + "line": 82, + "column": 55, + "problem": "InstanceofUnsupported", + "autofixable": false, + "suggest": "", + "rule": "\"instanceof\" operator is partially supported (arkts-instanceof-ref-types)" + }, + { + "line": 85, + "column": 1, + "problem": "CommaOperator", + "autofixable": true, + "autofix": [ + { + "start": 1646, + "end": 1678, + "replacementText": "// Error, autofix\nx in x;\n--x;\nx in x;\nx++;\nx in x;" + } + ], + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 85, + "column": 3, + "problem": "InOperator", + "autofixable": false, + "suggest": "", + "rule": "\"in\" operator is not supported (arkts-no-in)" + }, + { + "line": 85, + "column": 16, + "problem": "InOperator", + "autofixable": false, + "suggest": "", + "rule": "\"in\" operator is not supported (arkts-no-in)" + }, + { + "line": 85, + "column": 29, + "problem": "InOperator", + "autofixable": false, + "suggest": "", + "rule": "\"in\" operator is not supported (arkts-no-in)" + } + ] +} diff --git a/ets2panda/linter/test_rules/rule71.ts.autofix.skip b/ets2panda/linter/test_rules/rule71.ts.autofix.skip deleted file mode 100644 index 13982e9c91..0000000000 --- a/ets2panda/linter/test_rules/rule71.ts.autofix.skip +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2023-2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ diff --git a/ets2panda/linter/test_rules/rule71.ts.json b/ets2panda/linter/test_rules/rule71.ts.json index bd5e2d016b..50f06e6304 100644 --- a/ets2panda/linter/test_rules/rule71.ts.json +++ b/ets2panda/linter/test_rules/rule71.ts.json @@ -15,81 +15,158 @@ ], "nodes": [ { - "line": 22, + "line": 24, "column": 6, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 34, + "line": 39, "column": 12, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 34, + "line": 42, "column": 12, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 35, - "column": 12, + "line": 46, + "column": 27, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 35, - "column": 12, + "line": 49, + "column": 15, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 38, - "column": 27, + "line": 53, + "column": 24, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 38, - "column": 27, + "line": 58, + "column": 1, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 39, - "column": 15, + "line": 61, + "column": 1, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 39, - "column": 15, + "line": 64, + "column": 5, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 42, - "column": 24, + "line": 66, + "column": 5, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" }, { - "line": 42, - "column": 24, + "line": 70, + "column": 1, + "problem": "CommaOperator", + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 73, + "column": 1, + "problem": "CommaOperator", + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 76, + "column": 1, + "problem": "CommaOperator", + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 79, + "column": 1, + "problem": "CommaOperator", + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 82, + "column": 1, + "problem": "CommaOperator", + "suggest": "", + "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 82, + "column": 3, + "problem": "InstanceofUnsupported", + "suggest": "", + "rule": "\"instanceof\" operator is partially supported (arkts-instanceof-ref-types)" + }, + { + "line": 82, + "column": 29, + "problem": "InstanceofUnsupported", + "suggest": "", + "rule": "\"instanceof\" operator is partially supported (arkts-instanceof-ref-types)" + }, + { + "line": 82, + "column": 55, + "problem": "InstanceofUnsupported", + "suggest": "", + "rule": "\"instanceof\" operator is partially supported (arkts-instanceof-ref-types)" + }, + { + "line": 85, + "column": 1, "problem": "CommaOperator", "suggest": "", "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + }, + { + "line": 85, + "column": 3, + "problem": "InOperator", + "suggest": "", + "rule": "\"in\" operator is not supported (arkts-no-in)" + }, + { + "line": 85, + "column": 16, + "problem": "InOperator", + "suggest": "", + "rule": "\"in\" operator is not supported (arkts-no-in)" + }, + { + "line": 85, + "column": 29, + "problem": "InOperator", + "suggest": "", + "rule": "\"in\" operator is not supported (arkts-no-in)" } ] -} \ No newline at end of file +} -- Gitee