diff --git a/linter/src/Autofixer.ts b/linter/src/Autofixer.ts index 58c574aa420817a3ffbc7d71f30ce2be54ad30ad..b5b3155f93a85821ea2a2aa9652f540ec4351f0c 100644 --- a/linter/src/Autofixer.ts +++ b/linter/src/Autofixer.ts @@ -135,4 +135,17 @@ export function fixFunctionExpression(funcExpr: ts.FunctionExpression, ); let text = printer.printNode(ts.EmitHint.Unspecified, arrowFunc, funcExpr.getSourceFile()); return { start: funcExpr.getStart(), end: funcExpr.getEnd(), replacementText: text }; +} + +export function dropTypeOnVarDecl(varDecl: ts.VariableDeclaration): Autofix { + let newVarDecl = ts.factory.createVariableDeclaration(varDecl.name, undefined, undefined, undefined); + let text = printer.printNode(ts.EmitHint.Unspecified, newVarDecl, varDecl.getSourceFile()); + return { start: varDecl.getStart(), end: varDecl.getEnd(), replacementText: text}; +} + +export function wrapExpressionInError(expr: ts.Expression, isString: boolean): Autofix { + let ctorArgs = isString ? [ expr ] : [ ts.factory.createStringLiteral('', true), expr]; + let newExpr = ts.factory.createNewExpression(ts.factory.createIdentifier('Error'), undefined, ctorArgs); + let text = printer.printNode(ts.EmitHint.Unspecified, newExpr, expr.getSourceFile()); + return { start: expr.getStart(), end: expr.getEnd(), replacementText: text }; } \ No newline at end of file diff --git a/linter/src/TypeScriptLinter.ts b/linter/src/TypeScriptLinter.ts index 8c002520db08ea884e3a713d082cc028389f996a..f4e68c9058ae4bd18157498a4a6434e0b0803696 100644 --- a/linter/src/TypeScriptLinter.ts +++ b/linter/src/TypeScriptLinter.ts @@ -604,8 +604,12 @@ export class TypeScriptLinter { private handleThrowStatement(node: ts.Node) { let throwStmt = node as ts.ThrowStatement; let throwExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(throwStmt.expression); - if (!throwExprType.isClassOrInterface() || !this.typeHierarchyHasTypeError(throwExprType)) - this.incrementCounters(node, FaultID.ThrowStatement); + if (!throwExprType.isClassOrInterface() || !this.typeHierarchyHasTypeError(throwExprType)) { + let autofix: Autofix[] | undefined = undefined; + if (Autofixer.shouldAutofix(throwStmt, FaultID.ThrowStatement)) + autofix = [ Autofixer.wrapExpressionInError(throwStmt.expression, Utils.isStringType(throwExprType)) ]; + this.incrementCounters(node, FaultID.ThrowStatement, true, autofix); + } } private handleForStatement(node: ts.Node) { @@ -942,11 +946,15 @@ export class TypeScriptLinter { private handleCatchClause(node: ts.Node) { let tsCatch = node as ts.CatchClause; // In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'. - // It is not compatible with STS 'catch' where the exception varilab has to be of type - // 'Exception' or derived from it. - // So each 'catch' which has explicite type for the exception object goes to problems in strict mode. - if (tsCatch.variableDeclaration && tsCatch.variableDeclaration.type) - this.incrementCounters(node, FaultID.CatchWithUnsupportedType); + // It is not compatible with STS 'catch' where the exception variable has to be of type + // Error or derived from it. + // So each 'catch' which has explicit type for the exception object goes to problems in strict mode. + if (tsCatch.variableDeclaration && tsCatch.variableDeclaration.type) { + let autofix: Autofix[] | undefined = undefined; + if (Autofixer.shouldAutofix(tsCatch, FaultID.CatchWithUnsupportedType)) + autofix = [ Autofixer.dropTypeOnVarDecl(tsCatch.variableDeclaration) ]; + this.incrementCounters(node, FaultID.CatchWithUnsupportedType, true, autofix); + } } private handleClassDeclaration(node: ts.Node) { diff --git a/linter/test/catch_clause.ts.autofix.json b/linter/test/catch_clause.ts.autofix.json new file mode 100755 index 0000000000000000000000000000000000000000..c3db7e9f3ffc54a00fead98d8b52c44295c003e8 --- /dev/null +++ b/linter/test/catch_clause.ts.autofix.json @@ -0,0 +1,95 @@ +{ + "copyright": [ + "Copyright (c) 2022-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": 17, + "column": 13, + "problem": "ThrowStatement", + "autofixable": true, + "autofix": [ + { + "start": 634, + "end": 657, + "replacementText": "new Error(\"Catch with 'any' type\")" + } + ] + }, + { + "line": 18, + "column": 3, + "problem": "CatchWithUnsupportedType", + "autofixable": true, + "autofix": [ + { + "start": 668, + "end": 674, + "replacementText": "e" + } + ] + }, + { + "line": 18, + "column": 13, + "problem": "AnyType", + "autofixable": false + }, + { + "line": 23, + "column": 13, + "problem": "ThrowStatement", + "autofixable": true, + "autofix": [ + { + "start": 723, + "end": 750, + "replacementText": "new Error(\"Catch with 'unknown' type\")" + } + ] + }, + { + "line": 24, + "column": 3, + "problem": "CatchWithUnsupportedType", + "autofixable": true, + "autofix": [ + { + "start": 761, + "end": 771, + "replacementText": "e" + } + ] + }, + { + "line": 24, + "column": 13, + "problem": "UnknownType", + "autofixable": false + }, + { + "line": 29, + "column": 13, + "problem": "ThrowStatement", + "autofixable": true, + "autofix": [ + { + "start": 820, + "end": 849, + "replacementText": "new Error('Catch without explicit type')" + } + ] + } + ] +} \ No newline at end of file diff --git a/linter/test/catch_clause.ts.autofix.skip b/linter/test/catch_clause.ts.autofix.skip deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/linter/test/conditional_types.ts b/linter/test/conditional_types.ts index 8d46cc39ace1282a10609e30906fae1806dca79b..af657f55078e6b8f20b432567030de20b916b8f9 100644 --- a/linter/test/conditional_types.ts +++ b/linter/test/conditional_types.ts @@ -32,7 +32,7 @@ type NameOrId = T extends number ? IdLabel : NameLabel; function createLabel(idOrName: T): NameOrId { - throw 'unimplemented'; + throw 1; } const a = createLabel('typescript'); // let a: NameLabel const b = createLabel(2.8); // let b: IdLabel diff --git a/linter/test/conditional_types.ts.autofix.json b/linter/test/conditional_types.ts.autofix.json new file mode 100755 index 0000000000000000000000000000000000000000..a27d015a5ff9a43f0eaa6c2503643a63cd1becac --- /dev/null +++ b/linter/test/conditional_types.ts.autofix.json @@ -0,0 +1,139 @@ +{ + "copyright": [ + "Copyright (c) 2022-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": 19, + "column": 1, + "problem": "InterfaceOrEnumMerging", + "autofixable": false + }, + { + "line": 22, + "column": 17, + "problem": "ConditionalType", + "autofixable": false + }, + { + "line": 23, + "column": 17, + "problem": "ConditionalType", + "autofixable": false + }, + { + "line": 31, + "column": 25, + "problem": "UnionType", + "autofixable": false + }, + { + "line": 31, + "column": 44, + "problem": "ConditionalType", + "autofixable": false + }, + { + "line": 34, + "column": 32, + "problem": "UnionType", + "autofixable": false + }, + { + "line": 35, + "column": 3, + "problem": "ThrowStatement", + "autofixable": true, + "autofix": [ + { + "start": 1144, + "end": 1145, + "replacementText": "new Error('', 1)" + } + ] + }, + { + "line": 37, + "column": 11, + "problem": "GenericCallNoTypeArgs", + "autofixable": false + }, + { + "line": 38, + "column": 11, + "problem": "GenericCallNoTypeArgs", + "autofixable": false + }, + { + "line": 39, + "column": 11, + "problem": "GenericCallNoTypeArgs", + "autofixable": false + }, + { + "line": 41, + "column": 21, + "problem": "ConditionalType", + "autofixable": false + }, + { + "line": 41, + "column": 31, + "problem": "ObjectTypeLiteral", + "autofixable": false + }, + { + "line": 41, + "column": 42, + "problem": "UnknownType", + "autofixable": false + }, + { + "line": 41, + "column": 54, + "problem": "IndexedAccessType", + "autofixable": false + }, + { + "line": 41, + "column": 56, + "problem": "StringLiteralType", + "autofixable": false + }, + { + "line": 45, + "column": 1, + "problem": "InterfaceOrEnumMerging", + "autofixable": false + }, + { + "line": 51, + "column": 19, + "problem": "ConditionalType", + "autofixable": false + }, + { + "line": 51, + "column": 29, + "problem": "AnyType", + "autofixable": false + }, + { + "line": 51, + "column": 37, + "problem": "IndexedAccessType", + "autofixable": false + } + ] +} diff --git a/linter/test/conditional_types.ts.autofix.skip b/linter/test/conditional_types.ts.autofix.skip deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/linter/test/types.ts b/linter/test/types.ts index 290453460031e9ccd88c7ed4a240f94eadf0cdfe..4230ef66d9dfc2355a81777a9dcd7ff584a88372 100644 --- a/linter/test/types.ts +++ b/linter/test/types.ts @@ -46,7 +46,7 @@ export function animations() { const regex = /go*d/; - throw 'labuda'; + throw new TypeError('labuda'); } const c = 'c'; diff --git a/linter/test/types.ts.strict.json b/linter/test/types.ts.strict.json index 4316cc603b949043e0f669f3e8bb94d1b61730bd..687c45fd76582412a8810ecda84d59c05cd66f82 100644 --- a/linter/test/types.ts.strict.json +++ b/linter/test/types.ts.strict.json @@ -129,11 +129,6 @@ "column": 17, "problem": "RegexLiteral" }, - { - "line": 49, - "column": 3, - "problem": "ThrowStatement" - }, { "line": 54, "column": 26,