diff --git a/linter/src/Autofixer.ts b/linter/src/Autofixer.ts index 23c0389b79aa127f07ac89e03c47b11406079ddb..2273883f78c3028110034a605162efdd6d880b88 100644 --- a/linter/src/Autofixer.ts +++ b/linter/src/Autofixer.ts @@ -16,6 +16,7 @@ import * as ts from 'typescript'; import { AutofixInfo } from './Common'; import { FaultID } from './Problems'; +import * as Utils from './Utils'; export const AUTOFIX_ALL: AutofixInfo = { problemID: '', start: -1, end: -1 @@ -37,7 +38,7 @@ export interface Autofix { end: number; } -const printer: ts.Printer = ts.createPrinter(); +const printer: ts.Printer = ts.createPrinter({ omitTrailingSemicolon: false, removeComments: false }); function numericLiteral2IdentifierName(numeric: ts.NumericLiteral) { return '__' + numeric.getText(); @@ -137,7 +138,7 @@ export function fixFunctionExpression(funcExpr: ts.FunctionExpression, return { start: funcExpr.getStart(), end: funcExpr.getEnd(), replacementText: text }; } -export function fixReturnType(funcLikeDecl: ts.FunctionLikeDeclaration, typeNode: ts.TypeNode): Autofix { +export function fixMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration, typeNode: ts.TypeNode): Autofix { let text = ': ' + printer.printNode(ts.EmitHint.Unspecified, typeNode, funcLikeDecl.getSourceFile()); let pos = getReturnTypePosition(funcLikeDecl); return { start: pos, end: pos, replacementText: text }; @@ -207,4 +208,96 @@ export function fixDefaultImport(importClause: ts.ImportClause, let newImportClause = ts.factory.createImportClause(importClause.isTypeOnly, defaultSpec.name, nameBindings); let text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, importClause.getSourceFile()); return { start: importClause.getStart(), end: importClause.getEnd(), replacementText: text }; +} + +export function fixCtorParameterProperties(ctorDecl: ts.ConstructorDeclaration, paramTypes: ts.TypeNode[]): Autofix[] | undefined { + let fieldInitStmts: ts.Statement[] = []; + let newFieldPos = ctorDecl.getStart(); + let autofixes: Autofix[] = [{ start: newFieldPos, end: newFieldPos, replacementText: '' }]; + + for (let i = 0; i < ctorDecl.parameters.length; i++) { + const param = ctorDecl.parameters[i]; + + // Parameter property can not be a destructuring parameter. + if (!ts.isIdentifier(param.name)) continue; + + if (Utils.hasAccessModifier(param)) { + let propIdent = ts.factory.createIdentifier(param.name.text); + + let newFieldNode = ts.factory.createPropertyDeclaration( + ts.getModifiers(param), propIdent, undefined, paramTypes[i], undefined + ); + let newFieldText = printer.printNode(ts.EmitHint.Unspecified, newFieldNode, ctorDecl.getSourceFile()) + '\n'; + autofixes[0].replacementText += newFieldText; + + let newParamDecl = ts.factory.createParameterDeclaration( + undefined, undefined, param.name, param.questionToken, param.type, param.initializer + ); + let newParamText = printer.printNode(ts.EmitHint.Unspecified, newParamDecl, ctorDecl.getSourceFile()); + autofixes.push({ start: param.getStart(), end: param.getEnd(), replacementText: newParamText }); + + fieldInitStmts.push(ts.factory.createExpressionStatement(ts.factory.createAssignment( + ts.factory.createPropertyAccessExpression( + ts.factory.createThis(), + propIdent, + ), + propIdent + ))); + } + } + + // Note: Bodyless ctors can't have parameter properties. + if (ctorDecl.body) { + let newBody = ts.factory.createBlock(fieldInitStmts.concat(ctorDecl.body.statements), true); + let newBodyText = printer.printNode(ts.EmitHint.Unspecified, newBody, ctorDecl.getSourceFile()); + autofixes.push({ start: ctorDecl.body.getStart(), end: ctorDecl.body.getEnd(), replacementText: newBodyText }); + } + + return autofixes; +} + +export function fixPrivateIdentifier(ident: ts.PrivateIdentifier): Autofix { + if ( + ts.isPropertyDeclaration(ident.parent) || ts.isMethodDeclaration(ident.parent) || + ts.isGetAccessorDeclaration(ident.parent) || ts.isSetAccessorDeclaration(ident.parent) + ) { + const mods = ts.getModifiers(ident.parent); + let newMods: ts.Modifier[] = [ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword)]; + if (mods) newMods = newMods.concat(mods); // 'private' modifier should always be first. + + let newNode: ts.Node; + if (ts.isPropertyDeclaration(ident.parent)) { + let propDecl = ident.parent; + newNode = ts.factory.createPropertyDeclaration( + newMods, ts.factory.createIdentifier(ident.text.slice(1, ident.text.length)), + propDecl.questionToken ?? propDecl.exclamationToken, propDecl.type, propDecl.initializer + ); + } + else if (ts.isMethodDeclaration(ident.parent)) { + let methodDecl = ident.parent; + newNode = ts.factory.createMethodDeclaration( + newMods, methodDecl.asteriskToken, ts.factory.createIdentifier(ident.text.slice(1, ident.text.length)), + methodDecl.questionToken, methodDecl.typeParameters, methodDecl.parameters, methodDecl.type, methodDecl.body + ); + } + else if (ts.isGetAccessorDeclaration(ident.parent)) { + let getterDecl = ident.parent; + newNode = ts.factory.createGetAccessorDeclaration( + newMods, ts.factory.createIdentifier(ident.text.slice(1, ident.text.length)), + getterDecl.parameters, getterDecl.type, getterDecl.body + ); + } + else { + let setterDecl = ident.parent; + newNode = ts.factory.createSetAccessorDeclaration( + newMods, ts.factory.createIdentifier(ident.text.slice(1, ident.text.length)), + setterDecl.parameters, setterDecl.body + ); + } + + let text = printer.printNode(ts.EmitHint.Unspecified, newNode, ident.getSourceFile()); + return { start: ident.parent.getStart(), end: ident.parent.getEnd(), replacementText: text }; + } + + return { start: ident.getStart(), end: ident.getEnd(), replacementText: ident.text.slice(1, ident.text.length) }; } \ No newline at end of file diff --git a/linter/src/TypeScriptLinter.ts b/linter/src/TypeScriptLinter.ts index f255eedaba04c54445d2e9d30b1140ad85152432..c0b344432e2915b5b219317853f9cd6b5d661ad9 100644 --- a/linter/src/TypeScriptLinter.ts +++ b/linter/src/TypeScriptLinter.ts @@ -168,6 +168,8 @@ export class TypeScriptLinter { [ts.SyntaxKind.BigIntLiteral, this.handleBigIntLiteral], [ts.SyntaxKind.SpreadElement, this.handleSpreadOp], [ts.SyntaxKind.SpreadAssignment, this.handleSpreadOp], [ts.SyntaxKind.NonNullExpression, this.handleNonNullExpression], + [ts.SyntaxKind.Constructor, this.handleConstructorDeclaration], + [ts.SyntaxKind.PrivateIdentifier, this.handlePrivateIdentifier], ]); public incrementCounters(node: ts.Node, faultId: number, autofixable: boolean = false, autofix?: Autofix[]) { @@ -300,40 +302,48 @@ export class TypeScriptLinter { private countClassMembersWithDuplicateName(tsClassDecl: ts.ClassDeclaration): void { for (const tsCurrentMember of tsClassDecl.members) { - if ( - !tsCurrentMember.name || - !(ts.isIdentifier(tsCurrentMember.name) || ts.isPrivateIdentifier(tsCurrentMember.name)) - ) - continue; + if (this.classMemberHasDuplicateName(tsCurrentMember, tsClassDecl)) + this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); + } + } - for (const tsClassMember of tsClassDecl.members) { - if (tsCurrentMember === tsClassMember) continue; + private classMemberHasDuplicateName(targetMember: ts.ClassElement, tsClassLikeDecl: ts.ClassLikeDeclaration, + classType?: ts.Type): boolean { + // If two class members have the same name where one is a private identifer, + // then such members are considered to have duplicate names. - if ( - !tsClassMember.name || - !(ts.isIdentifier(tsClassMember.name) || ts.isPrivateIdentifier(tsClassMember.name)) - ) - continue; + if (!targetMember.name || !(ts.isIdentifier(targetMember.name) || ts.isPrivateIdentifier(targetMember.name))) + return false; - if ( - ts.isIdentifier(tsCurrentMember.name) && - ts.isPrivateIdentifier(tsClassMember.name) && - tsCurrentMember.name.text === tsClassMember.name.text.substring(1) - ) { - this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); - break; - } + for (const classMember of tsClassLikeDecl.members) { + if (targetMember === classMember) continue; - if ( - ts.isPrivateIdentifier(tsCurrentMember.name) && - ts.isIdentifier(tsClassMember.name) && - tsCurrentMember.name.text.substring(1) === tsClassMember.name.text - ) { - this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); - break; + if (!classMember.name || !(ts.isIdentifier(classMember.name) || ts.isPrivateIdentifier(classMember.name))) + continue; + + if ( + (ts.isIdentifier(targetMember.name) && ts.isPrivateIdentifier(classMember.name) && + targetMember.name.text === classMember.name.text.substring(1)) || + (ts.isPrivateIdentifier(targetMember.name) && ts.isIdentifier(classMember.name) && + targetMember.name.text.substring(1) === classMember.name.text) || + (ts.isPrivateIdentifier(targetMember.name) && ts.isPrivateIdentifier(classMember.name) && + targetMember.name.text.substring(1) === classMember.name.text.substring(1)) + ) + return true; + } + + classType ??= TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsClassLikeDecl); + if (classType) { + let baseType = Utils.getBaseClass(classType); + if (baseType) { + let baseDecl = baseType.getSymbol()?.valueDeclaration as ts.ClassLikeDeclaration; + if (baseDecl) { + return this.classMemberHasDuplicateName(targetMember, baseDecl); } } } + + return false; } private functionContainsThis(tsNode: ts.Node): boolean { @@ -533,15 +543,6 @@ export class TypeScriptLinter { let tsParam = node as ts.ParameterDeclaration; if (ts.isArrayBindingPattern(tsParam.name) || ts.isObjectBindingPattern(tsParam.name)) this.incrementCounters(node, FaultID.DestructuringParameter); - - let tsParamMods = ts.getModifiers(tsParam); - if ( - tsParamMods && - (Utils.hasModifier(tsParamMods, ts.SyntaxKind.PublicKeyword) || - Utils.hasModifier(tsParamMods, ts.SyntaxKind.ProtectedKeyword) || - Utils.hasModifier(tsParamMods, ts.SyntaxKind.PrivateKeyword)) - ) - this.incrementCounters(node, FaultID.ParameterProperties); } private handleEnumDeclaration(node: ts.Node) { @@ -786,6 +787,8 @@ export class TypeScriptLinter { } private handleMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration): [boolean, ts.TypeNode | undefined] { + if (funcLikeDecl.type) return [false, funcLikeDecl.type]; + // Note: Return type can't be inferred for function without body. if (!funcLikeDecl.body) return [false, undefined]; @@ -807,10 +810,10 @@ export class TypeScriptLinter { hasLimitedRetTypeInference = true; } else if (hasLimitedRetTypeInference) { newRetTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(tsRetType, funcLikeDecl, ts.NodeBuilderFlags.None); - if (newRetTypeNode && !isFuncExpr && Autofixer.shouldAutofix(funcLikeDecl, FaultID.LimitedReturnTypeInference)) { - autofixable = true; - autofix = [Autofixer.fixReturnType(funcLikeDecl, newRetTypeNode)]; - } + autofixable = !!newRetTypeNode; + + if (!isFuncExpr && newRetTypeNode && Autofixer.shouldAutofix(funcLikeDecl, FaultID.LimitedReturnTypeInference)) + autofix = [Autofixer.fixMissingReturnType(funcLikeDecl, newRetTypeNode)]; } } @@ -1538,6 +1541,55 @@ export class TypeScriptLinter { } } + private handleConstructorDeclaration(node: ts.Node) { + let ctorDecl = node as ts.ConstructorDeclaration; + + if (ctorDecl.parameters.some(x => Utils.hasAccessModifier(x))) { + let autofixable = true; + let autofix: Autofix[] | undefined; + let paramTypes: ts.TypeNode[] = []; + + for (const param of ctorDecl.parameters) { + let paramTypeNode = param.type; + if (!paramTypeNode) { + let paramType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(param); + paramTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(paramType, param, ts.NodeBuilderFlags.None); + if (!paramTypeNode || !Utils.isSupportedType(paramTypeNode)) { + autofixable = false; + break; + } + } + paramTypes.push(paramTypeNode); + } + + if (autofixable && Autofixer.shouldAutofix(node, FaultID.ParameterProperties)) { + autofix = Autofixer.fixCtorParameterProperties(ctorDecl, paramTypes) + } + + this.incrementCounters(node, FaultID.ParameterProperties, autofixable, autofix); + } + } + + private handlePrivateIdentifier(node: ts.Node) { + let ident = node as ts.PrivateIdentifier; + let autofix: Autofix[] | undefined = undefined; + let autofixable = false; + + let classMember = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(ident); + if (classMember && (classMember.getFlags() & ts.SymbolFlags.ClassMember) != 0 && classMember.valueDeclaration) { + let memberDecl = classMember.valueDeclaration as ts.ClassElement; + let parentDecl = memberDecl.parent; + if (ts.isClassLike(parentDecl) && !this.classMemberHasDuplicateName(memberDecl, parentDecl)) { + autofixable = true; + + if (Autofixer.shouldAutofix(node, FaultID.PrivateIdentifier)) + autofix = [Autofixer.fixPrivateIdentifier(ident)]; + } + } + + this.incrementCounters(node, FaultID.PrivateIdentifier, autofixable, autofix); + } + public lint(): void { this.visitTSNode(this.sourceFile); } diff --git a/linter/src/TypeScriptLinterConfig.ts b/linter/src/TypeScriptLinterConfig.ts index 0185c0adf40bc2010af529e3bdf563e7329f4f18..8ed2c270d981669dce8c45d7facfa9aa824ec199 100644 --- a/linter/src/TypeScriptLinterConfig.ts +++ b/linter/src/TypeScriptLinterConfig.ts @@ -169,7 +169,6 @@ export class LinterConfig { [ts.SyntaxKind.IntersectionType, FaultID.IntersectionType], [ts.SyntaxKind.TypeLiteral, FaultID.ObjectTypeLiteral], [ts.SyntaxKind.ConstructorType, FaultID.ConstructorType], [ts.SyntaxKind.ConstructSignature, FaultID.ConstructorType], - [ts.SyntaxKind.PrivateIdentifier, FaultID.PrivateIdentifier], [ts.SyntaxKind.ConditionalType, FaultID.ConditionalType], [ts.SyntaxKind.MappedType, FaultID.MappedType], [ts.SyntaxKind.JsxElement, FaultID.JsxElement], [ts.SyntaxKind.JsxSelfClosingElement, FaultID.JsxElement], [ts.SyntaxKind.ImportEqualsDeclaration, FaultID.ImportAssignment], diff --git a/linter/src/Utils.ts b/linter/src/Utils.ts index 03524c9216fd74443fa7e56d99b52990e4afc382..3fd52630392b9037de23b90542b9461f7285a226 100644 --- a/linter/src/Utils.ts +++ b/linter/src/Utils.ts @@ -235,7 +235,7 @@ export function isAnyType(tsType: ts.Type): tsType is ts.TypeReference { export function isUnsupportedType(tsType: ts.Type): boolean { return ( !!tsType.flags && ((tsType.flags & ts.TypeFlags.Any) !== 0 || (tsType.flags & ts.TypeFlags.Unknown) !== 0 || - (tsType.flags & ts.TypeFlags.Intersection) !== 0) || isUnsupportedUnionType(tsType) + (tsType.flags & ts.TypeFlags.Intersection) !== 0) ); } @@ -711,4 +711,36 @@ export function isStdArrayBufferAPI(symbol: ts.Symbol): boolean { export function isDefaultImport(importSpec: ts.ImportSpecifier): boolean { return importSpec?.propertyName?.text === 'default'; +} + +export function hasAccessModifier(decl: ts.HasModifiers): boolean { + let modifiers = ts.getModifiers(decl); + return ( + !!modifiers && + (hasModifier(modifiers, ts.SyntaxKind.PublicKeyword) || + hasModifier(modifiers, ts.SyntaxKind.ProtectedKeyword) || + hasModifier(modifiers, ts.SyntaxKind.PrivateKeyword)) + ); +} + +export function getModifier(modifiers: readonly ts.Modifier[] | undefined, modifierKind: ts.SyntaxKind): ts.Modifier | undefined { + if (!modifiers) return undefined; + return modifiers.find(x => x.kind === modifierKind); +} + +export function getAccessModifier(modifiers: readonly ts.Modifier[] | undefined): ts.Modifier | undefined { + return getModifier(modifiers, ts.SyntaxKind.PublicKeyword) ?? + getModifier(modifiers, ts.SyntaxKind.ProtectedKeyword) ?? + getModifier(modifiers, ts.SyntaxKind.PrivateKeyword); +} + +export function getBaseClass(type: ts.Type): ts.InterfaceType | undefined { + const baseTypes = type.getBaseTypes(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (baseType.isClass()) return baseType; + } + } + + return undefined; } \ No newline at end of file diff --git a/linter/test/limited_stdlib_api.ts.autofix.json b/linter/test/limited_stdlib_api.ts.autofix.json deleted file mode 100644 index 93aeac27a537e56098a24b0ad6b98b1b906b20ef..0000000000000000000000000000000000000000 --- a/linter/test/limited_stdlib_api.ts.autofix.json +++ /dev/null @@ -1,444 +0,0 @@ -{ - "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": 17, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 18, - "column": 11, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 19, - "column": 11, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 20, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 21, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 22, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 23, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 24, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 25, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 26, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 27, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 28, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 29, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 31, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 32, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 32, - "column": 1, - "problem": "GlobalThis", - "autofixable": false - }, - { - "line": 43, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 44, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 45, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 45, - "column": 31, - "problem": "ObjectLiteralNoContextType", - "autofixable": false - }, - { - "line": 46, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 47, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 48, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 49, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 50, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 51, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 52, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 53, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 54, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 56, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 57, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 58, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 59, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 60, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 61, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 62, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 63, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 64, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 65, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 66, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 67, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 70, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 71, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 72, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 72, - "column": 32, - "problem": "ObjectLiteralNoContextType", - "autofixable": false - }, - { - "line": 73, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 74, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 75, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 76, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 77, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 78, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 79, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 80, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 81, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 82, - "column": 1, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 85, - "column": 32, - "problem": "ObjectLiteralNoContextType", - "autofixable": false - }, - { - "line": 86, - "column": 20, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 87, - "column": 24, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 88, - "column": 29, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 89, - "column": 29, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 90, - "column": 18, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 90, - "column": 41, - "problem": "ObjectLiteralNoContextType", - "autofixable": false - }, - { - "line": 91, - "column": 39, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 92, - "column": 29, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 93, - "column": 18, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 94, - "column": 27, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 95, - "column": 22, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 96, - "column": 32, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 97, - "column": 18, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 98, - "column": 29, - "problem": "LimitedStdLibApi", - "autofixable": false - }, - { - "line": 102, - "column": 20, - "problem": "ObjectLiteralNoContextType", - "autofixable": false - } - ] -} \ No newline at end of file diff --git a/linter/test/parameter_properties.ts.autofix.skip b/linter/test/limited_stdlib_api.ts.autofix.skip similarity index 100% rename from linter/test/parameter_properties.ts.autofix.skip rename to linter/test/limited_stdlib_api.ts.autofix.skip diff --git a/linter/test/parameter_properties.ts b/linter/test/parameter_properties.ts index 44a263f3d7237b8f8f59c3f7054a278f4c699988..b57f98ca7706deba4bc5df095a7951d1ff0cd524 100644 --- a/linter/test/parameter_properties.ts +++ b/linter/test/parameter_properties.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -class Params { +class A { constructor( public readonly x: number, protected y: number, @@ -25,11 +25,16 @@ class Params { } } -class DerivedParams extends Params { - bar(): void { - console.log(this.x + this.y); +const a = new A(1, 2, 3); +console.log(a.x); + +class B { + public f: number = 10; + + constructor(q: number, public w = 'default', e: boolean, private readonly r: number[] = [1, 2, 3]) { + console.log(q, this.w, e, this.r, this.f); } } -const a = new Params(1, 2, 3); -console.log(a.x); +const b = new B(1, '2', true, []); +console.log(b.w); \ No newline at end of file diff --git a/linter/test/parameter_properties.ts.autofix.json b/linter/test/parameter_properties.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..4b7363a1814f03674450522bde6d7d50c9b6c444 --- /dev/null +++ b/linter/test/parameter_properties.ts.autofix.json @@ -0,0 +1,79 @@ +{ + "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": 17, + "column": 3, + "problem": "ParameterProperties", + "autofixable": true, + "autofix": [ + { + "start": 622, + "end": 622, + "replacementText": "public readonly x: number;\nprotected y: number;\nprivate z: number;\n" + }, + { + "start": 639, + "end": 664, + "replacementText": "x: number" + }, + { + "start": 670, + "end": 689, + "replacementText": "y: number" + }, + { + "start": 695, + "end": 712, + "replacementText": "z: number" + }, + { + "start": 717, + "end": 719, + "replacementText": "{\r\n this.x = x;\r\n this.y = y;\r\n this.z = z;\r\n}" + } + ] + }, + { + "line": 34, + "column": 3, + "problem": "ParameterProperties", + "autofixable": true, + "autofix": [ + { + "start": 870, + "end": 870, + "replacementText": "public w: string;\nprivate readonly r: number[];\n" + }, + { + "start": 893, + "end": 913, + "replacementText": "w = 'default'" + }, + { + "start": 927, + "end": 967, + "replacementText": "r: number[] = [1, 2, 3]" + }, + { + "start": 969, + "end": 1021, + "replacementText": "{\r\n this.w = w;\r\n this.r = r;\r\n console.log(q, this.w, e, this.r, this.f);\r\n}" + } + ] + } + ] +} \ No newline at end of file diff --git a/linter/test/parameter_properties.ts.strict.json b/linter/test/parameter_properties.ts.strict.json index 6dd05ac8d07bc7a817b7d49e4bc19b193b3719f5..c3d61ade9fab908d43db55b4ec40a1d08070f677 100644 --- a/linter/test/parameter_properties.ts.strict.json +++ b/linter/test/parameter_properties.ts.strict.json @@ -15,18 +15,13 @@ ], "nodes": [ { - "line": 18, - "column": 5, + "line": 17, + "column": 3, "problem": "ParameterProperties" }, { - "line": 19, - "column": 5, - "problem": "ParameterProperties" - }, - { - "line": 20, - "column": 5, + "line": 34, + "column": 3, "problem": "ParameterProperties" } ] diff --git a/linter/test/private_identifiers.ts b/linter/test/private_identifiers.ts new file mode 100644 index 0000000000000000000000000000000000000000..6951524b380a82b3dbc477d6a68f70361272ce70 --- /dev/null +++ b/linter/test/private_identifiers.ts @@ -0,0 +1,68 @@ +/* + * 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. + */ + +class C { + #p: number; + + p2: number; + #p2: string; // not fixable + + #q?: string; + #e!: string; + static #s = 0; + readonly #r = 20; + static readonly #sr = 0; + static readonly #srq?: string; + + #m(x: number): void {} + + m2(x: number): void {} + #m2(x: number): void {} // not fixable + + m3: boolean; + #m3(x: number): void {} // not fixable + + get #g1(): number { return 10; } + set #s1(x: number) { } + + static get #g2(): number { return 10; } + static set #s2(x: number) { } + + test() { + console.log(this.#p + this.#p2 + this.#q + this.#e + C.#s + this.#r + C.#sr + C.#srq); // '#p2' is not fixable + this.#m(10); + this.#m2(20); // not fixable + this.#m3(30); // not fixable + let x = this.#g1; + this.#s1 = x; + let y = C.#g2; + C.#s2 = y; + } +} + +class D extends C { + #a: string; + #p: number; // not fixable + + #m(): string { return 'foo'; } // not fixable + + #bar(): string { return 'baz'; } + + test() { + console.log(this.#p + this.#a); // '#p' is not fixable + let x = this.#m(); // not fixable + let y = this.#bar(); + } +} \ No newline at end of file diff --git a/linter/test/private_identifiers.ts.autofix.json b/linter/test/private_identifiers.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..5f458c837cc7195ada7449b5a0865ff06f20cb67 --- /dev/null +++ b/linter/test/private_identifiers.ts.autofix.json @@ -0,0 +1,490 @@ +{ + "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": 19, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 20, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 31, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 32, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 34, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 35, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 17, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 622, + "end": 633, + "replacementText": "private p: number;" + } + ] + }, + { + "line": 20, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 22, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 682, + "end": 694, + "replacementText": "private q?: string;" + } + ] + }, + { + "line": 23, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 697, + "end": 709, + "replacementText": "private e!: string;" + } + ] + }, + { + "line": 24, + "column": 10, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 712, + "end": 726, + "replacementText": "private static s = 0;" + } + ] + }, + { + "line": 25, + "column": 12, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 729, + "end": 746, + "replacementText": "private readonly r = 20;" + } + ] + }, + { + "line": 26, + "column": 19, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 749, + "end": 773, + "replacementText": "private static readonly sr = 0;" + } + ] + }, + { + "line": 27, + "column": 19, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 776, + "end": 806, + "replacementText": "private static readonly srq?: string;" + } + ] + }, + { + "line": 29, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 810, + "end": 832, + "replacementText": "private m(x: number): void { }" + } + ] + }, + { + "line": 32, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 35, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 37, + "column": 7, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 960, + "end": 992, + "replacementText": "private get g1(): number { return 10; }" + } + ] + }, + { + "line": 38, + "column": 7, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 995, + "end": 1017, + "replacementText": "private set s1(x: number) { }" + } + ] + }, + { + "line": 40, + "column": 14, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1021, + "end": 1060, + "replacementText": "private static get g2(): number { return 10; }" + } + ] + }, + { + "line": 41, + "column": 14, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1063, + "end": 1092, + "replacementText": "private static set s2(x: number) { }" + } + ] + }, + { + "line": 44, + "column": 22, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1126, + "end": 1128, + "replacementText": "p" + } + ] + }, + { + "line": 44, + "column": 32, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 44, + "column": 43, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1147, + "end": 1149, + "replacementText": "q" + } + ] + }, + { + "line": 44, + "column": 53, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1157, + "end": 1159, + "replacementText": "e" + } + ] + }, + { + "line": 44, + "column": 60, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1164, + "end": 1166, + "replacementText": "s" + } + ] + }, + { + "line": 44, + "column": 70, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1174, + "end": 1176, + "replacementText": "r" + } + ] + }, + { + "line": 44, + "column": 77, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1181, + "end": 1184, + "replacementText": "sr" + } + ] + }, + { + "line": 44, + "column": 85, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1189, + "end": 1193, + "replacementText": "srq" + } + ] + }, + { + "line": 45, + "column": 10, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1229, + "end": 1231, + "replacementText": "m" + } + ] + }, + { + "line": 46, + "column": 10, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 47, + "column": 10, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 48, + "column": 18, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1320, + "end": 1323, + "replacementText": "g1" + } + ] + }, + { + "line": 49, + "column": 10, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1334, + "end": 1337, + "replacementText": "s1" + } + ] + }, + { + "line": 50, + "column": 15, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1357, + "end": 1360, + "replacementText": "g2" + } + ] + }, + { + "line": 51, + "column": 7, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1368, + "end": 1371, + "replacementText": "s2" + } + ] + }, + { + "line": 57, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 59, + "column": 3, + "problem": "DeclWithDuplicateName", + "autofixable": false + }, + { + "line": 56, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1406, + "end": 1417, + "replacementText": "private a: string;" + } + ] + }, + { + "line": 57, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 59, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 61, + "column": 3, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1499, + "end": 1531, + "replacementText": "private bar(): string { return 'baz'; }" + } + ] + }, + { + "line": 64, + "column": 22, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 64, + "column": 32, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1577, + "end": 1579, + "replacementText": "a" + } + ] + }, + { + "line": 65, + "column": 18, + "problem": "PrivateIdentifier", + "autofixable": false + }, + { + "line": 66, + "column": 18, + "problem": "PrivateIdentifier", + "autofixable": true, + "autofix": [ + { + "start": 1660, + "end": 1664, + "replacementText": "bar" + } + ] + } + ] +} \ No newline at end of file diff --git a/linter/test/private_identifiers.ts.relax.json b/linter/test/private_identifiers.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..e7d2d6779bbeb56cae88903ecb7da87087831260 --- /dev/null +++ b/linter/test/private_identifiers.ts.relax.json @@ -0,0 +1,17 @@ +{ + "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": [] +} \ No newline at end of file diff --git a/linter/test/private_identifiers.ts.strict.json b/linter/test/private_identifiers.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..2ea545210cfefb2d6e4cd2f3100915dab81d908c --- /dev/null +++ b/linter/test/private_identifiers.ts.strict.json @@ -0,0 +1,248 @@ +{ + "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": 19, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 20, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 31, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 32, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 34, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 35, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 17, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 20, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 22, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 23, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 24, + "column": 10, + "problem": "PrivateIdentifier" + }, + { + "line": 25, + "column": 12, + "problem": "PrivateIdentifier" + }, + { + "line": 26, + "column": 19, + "problem": "PrivateIdentifier" + }, + { + "line": 27, + "column": 19, + "problem": "PrivateIdentifier" + }, + { + "line": 29, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 32, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 35, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 37, + "column": 7, + "problem": "PrivateIdentifier" + }, + { + "line": 38, + "column": 7, + "problem": "PrivateIdentifier" + }, + { + "line": 40, + "column": 14, + "problem": "PrivateIdentifier" + }, + { + "line": 41, + "column": 14, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 22, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 32, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 43, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 53, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 60, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 70, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 77, + "problem": "PrivateIdentifier" + }, + { + "line": 44, + "column": 85, + "problem": "PrivateIdentifier" + }, + { + "line": 45, + "column": 10, + "problem": "PrivateIdentifier" + }, + { + "line": 46, + "column": 10, + "problem": "PrivateIdentifier" + }, + { + "line": 47, + "column": 10, + "problem": "PrivateIdentifier" + }, + { + "line": 48, + "column": 18, + "problem": "PrivateIdentifier" + }, + { + "line": 49, + "column": 10, + "problem": "PrivateIdentifier" + }, + { + "line": 50, + "column": 15, + "problem": "PrivateIdentifier" + }, + { + "line": 51, + "column": 7, + "problem": "PrivateIdentifier" + }, + { + "line": 57, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 59, + "column": 3, + "problem": "DeclWithDuplicateName" + }, + { + "line": 56, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 57, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 59, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 61, + "column": 3, + "problem": "PrivateIdentifier" + }, + { + "line": 64, + "column": 22, + "problem": "PrivateIdentifier" + }, + { + "line": 64, + "column": 32, + "problem": "PrivateIdentifier" + }, + { + "line": 65, + "column": 18, + "problem": "PrivateIdentifier" + }, + { + "line": 66, + "column": 18, + "problem": "PrivateIdentifier" + } + ] +} \ No newline at end of file