13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ciimport * as ts from 'typescript'; 173af6ab5fSopenharmony_ciimport { TsUtils } from '../utils/TsUtils'; 183af6ab5fSopenharmony_ciimport { scopeContainsThis } from '../utils/functions/ContainsThis'; 193af6ab5fSopenharmony_ciimport { forEachNodeInSubtree } from '../utils/functions/ForEachNodeInSubtree'; 203af6ab5fSopenharmony_ciimport { NameGenerator } from '../utils/functions/NameGenerator'; 213af6ab5fSopenharmony_ciimport { isAssignmentOperator } from '../utils/functions/isAssignmentOperator'; 223af6ab5fSopenharmony_ciimport { SymbolCache } from './SymbolCache'; 233af6ab5fSopenharmony_ci 243af6ab5fSopenharmony_ciconst GENERATED_OBJECT_LITERAL_INTERFACE_NAME = 'GeneratedObjectLiteralInterface_'; 253af6ab5fSopenharmony_ciconst GENERATED_OBJECT_LITERAL_INTERFACE_TRESHOLD = 1000; 263af6ab5fSopenharmony_ci 273af6ab5fSopenharmony_ciconst GENERATED_TYPE_LITERAL_INTERFACE_NAME = 'GeneratedTypeLiteralInterface_'; 283af6ab5fSopenharmony_ciconst GENERATED_TYPE_LITERAL_INTERFACE_TRESHOLD = 1000; 293af6ab5fSopenharmony_ci 303af6ab5fSopenharmony_ciexport interface Autofix { 313af6ab5fSopenharmony_ci replacementText: string; 323af6ab5fSopenharmony_ci start: number; 333af6ab5fSopenharmony_ci end: number; 343af6ab5fSopenharmony_ci} 353af6ab5fSopenharmony_ci 363af6ab5fSopenharmony_ciexport class Autofixer { 373af6ab5fSopenharmony_ci constructor( 383af6ab5fSopenharmony_ci private readonly typeChecker: ts.TypeChecker, 393af6ab5fSopenharmony_ci private readonly utils: TsUtils, 403af6ab5fSopenharmony_ci readonly sourceFile: ts.SourceFile, 413af6ab5fSopenharmony_ci readonly cancellationToken?: ts.CancellationToken 423af6ab5fSopenharmony_ci ) { 433af6ab5fSopenharmony_ci this.symbolCache = new SymbolCache(this.typeChecker, this.utils, sourceFile, cancellationToken); 443af6ab5fSopenharmony_ci } 453af6ab5fSopenharmony_ci 463af6ab5fSopenharmony_ci fixLiteralAsPropertyNamePropertyAssignment(node: ts.PropertyAssignment): Autofix[] | undefined { 473af6ab5fSopenharmony_ci const contextualType = this.typeChecker.getContextualType(node.parent); 483af6ab5fSopenharmony_ci if (contextualType === undefined) { 493af6ab5fSopenharmony_ci return undefined; 503af6ab5fSopenharmony_ci } 513af6ab5fSopenharmony_ci 523af6ab5fSopenharmony_ci const symbol = this.utils.getPropertySymbol(contextualType, node); 533af6ab5fSopenharmony_ci if (symbol === undefined) { 543af6ab5fSopenharmony_ci return undefined; 553af6ab5fSopenharmony_ci } 563af6ab5fSopenharmony_ci 573af6ab5fSopenharmony_ci return this.renameSymbolAsIdentifier(symbol); 583af6ab5fSopenharmony_ci } 593af6ab5fSopenharmony_ci 603af6ab5fSopenharmony_ci fixLiteralAsPropertyNamePropertyName(node: ts.PropertyName): Autofix[] | undefined { 613af6ab5fSopenharmony_ci const symbol = this.typeChecker.getSymbolAtLocation(node); 623af6ab5fSopenharmony_ci if (symbol === undefined) { 633af6ab5fSopenharmony_ci return undefined; 643af6ab5fSopenharmony_ci } 653af6ab5fSopenharmony_ci 663af6ab5fSopenharmony_ci return this.renameSymbolAsIdentifier(symbol); 673af6ab5fSopenharmony_ci } 683af6ab5fSopenharmony_ci 693af6ab5fSopenharmony_ci fixPropertyAccessByIndex(node: ts.ElementAccessExpression): Autofix[] | undefined { 703af6ab5fSopenharmony_ci const symbol = this.typeChecker.getSymbolAtLocation(node.argumentExpression); 713af6ab5fSopenharmony_ci if (symbol === undefined) { 723af6ab5fSopenharmony_ci return undefined; 733af6ab5fSopenharmony_ci } 743af6ab5fSopenharmony_ci 753af6ab5fSopenharmony_ci return this.renameSymbolAsIdentifier(symbol); 763af6ab5fSopenharmony_ci } 773af6ab5fSopenharmony_ci 783af6ab5fSopenharmony_ci private renameSymbolAsIdentifier(symbol: ts.Symbol): Autofix[] | undefined { 793af6ab5fSopenharmony_ci if (this.renameSymbolAsIdentifierCache.has(symbol)) { 803af6ab5fSopenharmony_ci return this.renameSymbolAsIdentifierCache.get(symbol); 813af6ab5fSopenharmony_ci } 823af6ab5fSopenharmony_ci 833af6ab5fSopenharmony_ci if (!TsUtils.isPropertyOfInternalClassOrInterface(symbol)) { 843af6ab5fSopenharmony_ci this.renameSymbolAsIdentifierCache.set(symbol, undefined); 853af6ab5fSopenharmony_ci return undefined; 863af6ab5fSopenharmony_ci } 873af6ab5fSopenharmony_ci 883af6ab5fSopenharmony_ci const newName = this.utils.findIdentifierNameForSymbol(symbol); 893af6ab5fSopenharmony_ci if (newName === undefined) { 903af6ab5fSopenharmony_ci this.renameSymbolAsIdentifierCache.set(symbol, undefined); 913af6ab5fSopenharmony_ci return undefined; 923af6ab5fSopenharmony_ci } 933af6ab5fSopenharmony_ci 943af6ab5fSopenharmony_ci let result: Autofix[] | undefined = []; 953af6ab5fSopenharmony_ci this.symbolCache.getReferences(symbol).forEach((node) => { 963af6ab5fSopenharmony_ci if (result === undefined) { 973af6ab5fSopenharmony_ci return; 983af6ab5fSopenharmony_ci } 993af6ab5fSopenharmony_ci 1003af6ab5fSopenharmony_ci let autofix: Autofix[] | undefined; 1013af6ab5fSopenharmony_ci if (ts.isPropertyDeclaration(node) || ts.isPropertyAssignment(node) || ts.isPropertySignature(node)) { 1023af6ab5fSopenharmony_ci autofix = Autofixer.renamePropertyName(node.name, newName); 1033af6ab5fSopenharmony_ci } else if (ts.isElementAccessExpression(node)) { 1043af6ab5fSopenharmony_ci autofix = Autofixer.renameElementAccessExpression(node, newName); 1053af6ab5fSopenharmony_ci } 1063af6ab5fSopenharmony_ci 1073af6ab5fSopenharmony_ci if (autofix === undefined) { 1083af6ab5fSopenharmony_ci result = undefined; 1093af6ab5fSopenharmony_ci return; 1103af6ab5fSopenharmony_ci } 1113af6ab5fSopenharmony_ci 1123af6ab5fSopenharmony_ci result.push(...autofix); 1133af6ab5fSopenharmony_ci }); 1143af6ab5fSopenharmony_ci if (!result?.length) { 1153af6ab5fSopenharmony_ci result = undefined; 1163af6ab5fSopenharmony_ci } 1173af6ab5fSopenharmony_ci 1183af6ab5fSopenharmony_ci this.renameSymbolAsIdentifierCache.set(symbol, result); 1193af6ab5fSopenharmony_ci return result; 1203af6ab5fSopenharmony_ci } 1213af6ab5fSopenharmony_ci 1223af6ab5fSopenharmony_ci private readonly renameSymbolAsIdentifierCache = new Map<ts.Symbol, Autofix[] | undefined>(); 1233af6ab5fSopenharmony_ci 1243af6ab5fSopenharmony_ci private static renamePropertyName(node: ts.PropertyName, newName: string): Autofix[] | undefined { 1253af6ab5fSopenharmony_ci if (ts.isComputedPropertyName(node)) { 1263af6ab5fSopenharmony_ci return undefined; 1273af6ab5fSopenharmony_ci } 1283af6ab5fSopenharmony_ci 1293af6ab5fSopenharmony_ci if (ts.isMemberName(node)) { 1303af6ab5fSopenharmony_ci if (ts.idText(node) !== newName) { 1313af6ab5fSopenharmony_ci return undefined; 1323af6ab5fSopenharmony_ci } 1333af6ab5fSopenharmony_ci 1343af6ab5fSopenharmony_ci return []; 1353af6ab5fSopenharmony_ci } 1363af6ab5fSopenharmony_ci 1373af6ab5fSopenharmony_ci return [{ replacementText: newName, start: node.getStart(), end: node.getEnd() }]; 1383af6ab5fSopenharmony_ci } 1393af6ab5fSopenharmony_ci 1403af6ab5fSopenharmony_ci private static renameElementAccessExpression( 1413af6ab5fSopenharmony_ci node: ts.ElementAccessExpression, 1423af6ab5fSopenharmony_ci newName: string 1433af6ab5fSopenharmony_ci ): Autofix[] | undefined { 1443af6ab5fSopenharmony_ci const argExprKind = node.argumentExpression.kind; 1453af6ab5fSopenharmony_ci if (argExprKind !== ts.SyntaxKind.NumericLiteral && argExprKind !== ts.SyntaxKind.StringLiteral) { 1463af6ab5fSopenharmony_ci return undefined; 1473af6ab5fSopenharmony_ci } 1483af6ab5fSopenharmony_ci 1493af6ab5fSopenharmony_ci return [ 1503af6ab5fSopenharmony_ci { 1513af6ab5fSopenharmony_ci replacementText: node.expression.getText() + '.' + newName, 1523af6ab5fSopenharmony_ci start: node.getStart(), 1533af6ab5fSopenharmony_ci end: node.getEnd() 1543af6ab5fSopenharmony_ci } 1553af6ab5fSopenharmony_ci ]; 1563af6ab5fSopenharmony_ci } 1573af6ab5fSopenharmony_ci 1583af6ab5fSopenharmony_ci fixFunctionExpression( 1593af6ab5fSopenharmony_ci funcExpr: ts.FunctionExpression, 1603af6ab5fSopenharmony_ci // eslint-disable-next-line default-param-last 1613af6ab5fSopenharmony_ci retType: ts.TypeNode | undefined = funcExpr.type, 1623af6ab5fSopenharmony_ci modifiers: readonly ts.Modifier[] | undefined, 1633af6ab5fSopenharmony_ci isGenerator: boolean, 1643af6ab5fSopenharmony_ci hasUnfixableReturnType: boolean 1653af6ab5fSopenharmony_ci ): Autofix[] | undefined { 1663af6ab5fSopenharmony_ci const hasThisKeyword = scopeContainsThis(funcExpr.body); 1673af6ab5fSopenharmony_ci const isCalledRecursively = this.utils.isFunctionCalledRecursively(funcExpr); 1683af6ab5fSopenharmony_ci if (isGenerator || hasThisKeyword || isCalledRecursively || hasUnfixableReturnType) { 1693af6ab5fSopenharmony_ci return undefined; 1703af6ab5fSopenharmony_ci } 1713af6ab5fSopenharmony_ci 1723af6ab5fSopenharmony_ci let arrowFunc: ts.Expression = ts.factory.createArrowFunction( 1733af6ab5fSopenharmony_ci modifiers, 1743af6ab5fSopenharmony_ci funcExpr.typeParameters, 1753af6ab5fSopenharmony_ci funcExpr.parameters, 1763af6ab5fSopenharmony_ci retType, 1773af6ab5fSopenharmony_ci ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 1783af6ab5fSopenharmony_ci funcExpr.body 1793af6ab5fSopenharmony_ci ); 1803af6ab5fSopenharmony_ci if (Autofixer.needsParentheses(funcExpr)) { 1813af6ab5fSopenharmony_ci arrowFunc = ts.factory.createParenthesizedExpression(arrowFunc); 1823af6ab5fSopenharmony_ci } 1833af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, arrowFunc, funcExpr.getSourceFile()); 1843af6ab5fSopenharmony_ci return [{ start: funcExpr.getStart(), end: funcExpr.getEnd(), replacementText: text }]; 1853af6ab5fSopenharmony_ci } 1863af6ab5fSopenharmony_ci 1873af6ab5fSopenharmony_ci private static isNodeInWhileOrIf(node: ts.Node): boolean { 1883af6ab5fSopenharmony_ci return ( 1893af6ab5fSopenharmony_ci node.kind === ts.SyntaxKind.WhileStatement || 1903af6ab5fSopenharmony_ci node.kind === ts.SyntaxKind.DoStatement || 1913af6ab5fSopenharmony_ci node.kind === ts.SyntaxKind.IfStatement 1923af6ab5fSopenharmony_ci ); 1933af6ab5fSopenharmony_ci } 1943af6ab5fSopenharmony_ci 1953af6ab5fSopenharmony_ci private static isNodeInForLoop(node: ts.Node): boolean { 1963af6ab5fSopenharmony_ci return ( 1973af6ab5fSopenharmony_ci node.kind === ts.SyntaxKind.ForInStatement || 1983af6ab5fSopenharmony_ci node.kind === ts.SyntaxKind.ForOfStatement || 1993af6ab5fSopenharmony_ci node.kind === ts.SyntaxKind.ForStatement 2003af6ab5fSopenharmony_ci ); 2013af6ab5fSopenharmony_ci } 2023af6ab5fSopenharmony_ci 2033af6ab5fSopenharmony_ci private static parentInFor(node: ts.Node): ts.Node | undefined { 2043af6ab5fSopenharmony_ci let parentNode = node.parent; 2053af6ab5fSopenharmony_ci while (parentNode) { 2063af6ab5fSopenharmony_ci if (Autofixer.isNodeInForLoop(parentNode)) { 2073af6ab5fSopenharmony_ci return parentNode; 2083af6ab5fSopenharmony_ci } 2093af6ab5fSopenharmony_ci parentNode = parentNode.parent; 2103af6ab5fSopenharmony_ci } 2113af6ab5fSopenharmony_ci return undefined; 2123af6ab5fSopenharmony_ci } 2133af6ab5fSopenharmony_ci 2143af6ab5fSopenharmony_ci private static parentInCaseOrWhile(varDeclList: ts.VariableDeclarationList): boolean { 2153af6ab5fSopenharmony_ci let parentNode: ts.Node = varDeclList.parent; 2163af6ab5fSopenharmony_ci while (parentNode) { 2173af6ab5fSopenharmony_ci if (parentNode.kind === ts.SyntaxKind.CaseClause || Autofixer.isNodeInWhileOrIf(parentNode)) { 2183af6ab5fSopenharmony_ci return false; 2193af6ab5fSopenharmony_ci } 2203af6ab5fSopenharmony_ci parentNode = parentNode.parent; 2213af6ab5fSopenharmony_ci } 2223af6ab5fSopenharmony_ci return true; 2233af6ab5fSopenharmony_ci } 2243af6ab5fSopenharmony_ci 2253af6ab5fSopenharmony_ci private static isFunctionLikeDeclarationKind(node: ts.Node): boolean { 2263af6ab5fSopenharmony_ci switch (node.kind) { 2273af6ab5fSopenharmony_ci case ts.SyntaxKind.FunctionDeclaration: 2283af6ab5fSopenharmony_ci case ts.SyntaxKind.MethodDeclaration: 2293af6ab5fSopenharmony_ci case ts.SyntaxKind.Constructor: 2303af6ab5fSopenharmony_ci case ts.SyntaxKind.GetAccessor: 2313af6ab5fSopenharmony_ci case ts.SyntaxKind.SetAccessor: 2323af6ab5fSopenharmony_ci case ts.SyntaxKind.FunctionExpression: 2333af6ab5fSopenharmony_ci case ts.SyntaxKind.ArrowFunction: 2343af6ab5fSopenharmony_ci return true; 2353af6ab5fSopenharmony_ci default: 2363af6ab5fSopenharmony_ci return false; 2373af6ab5fSopenharmony_ci } 2383af6ab5fSopenharmony_ci } 2393af6ab5fSopenharmony_ci 2403af6ab5fSopenharmony_ci private static findVarScope(node: ts.Node): ts.Node { 2413af6ab5fSopenharmony_ci while (node !== undefined) { 2423af6ab5fSopenharmony_ci if (node.kind === ts.SyntaxKind.Block || node.kind === ts.SyntaxKind.SourceFile) { 2433af6ab5fSopenharmony_ci break; 2443af6ab5fSopenharmony_ci } 2453af6ab5fSopenharmony_ci // eslint-disable-next-line no-param-reassign 2463af6ab5fSopenharmony_ci node = node.parent; 2473af6ab5fSopenharmony_ci } 2483af6ab5fSopenharmony_ci return node; 2493af6ab5fSopenharmony_ci } 2503af6ab5fSopenharmony_ci 2513af6ab5fSopenharmony_ci private static varHasScope(node: ts.Node, scope: ts.Node): boolean { 2523af6ab5fSopenharmony_ci while (node !== undefined) { 2533af6ab5fSopenharmony_ci if (node === scope) { 2543af6ab5fSopenharmony_ci return true; 2553af6ab5fSopenharmony_ci } 2563af6ab5fSopenharmony_ci // eslint-disable-next-line no-param-reassign 2573af6ab5fSopenharmony_ci node = node.parent; 2583af6ab5fSopenharmony_ci } 2593af6ab5fSopenharmony_ci return false; 2603af6ab5fSopenharmony_ci } 2613af6ab5fSopenharmony_ci 2623af6ab5fSopenharmony_ci private static varInFunctionForScope(node: ts.Node, scope: ts.Node): boolean { 2633af6ab5fSopenharmony_ci while (node !== undefined) { 2643af6ab5fSopenharmony_ci if (Autofixer.isFunctionLikeDeclarationKind(node)) { 2653af6ab5fSopenharmony_ci break; 2663af6ab5fSopenharmony_ci } 2673af6ab5fSopenharmony_ci // eslint-disable-next-line no-param-reassign 2683af6ab5fSopenharmony_ci node = node.parent; 2693af6ab5fSopenharmony_ci } 2703af6ab5fSopenharmony_ci // node now Function like declaration 2713af6ab5fSopenharmony_ci 2723af6ab5fSopenharmony_ci // node need to check that function like declaration is in scope 2733af6ab5fSopenharmony_ci if (Autofixer.varHasScope(node, scope)) { 2743af6ab5fSopenharmony_ci // var use is in function scope, which is in for scope 2753af6ab5fSopenharmony_ci return true; 2763af6ab5fSopenharmony_ci } 2773af6ab5fSopenharmony_ci return false; 2783af6ab5fSopenharmony_ci } 2793af6ab5fSopenharmony_ci 2803af6ab5fSopenharmony_ci private static selfDeclared(decl: ts.Node, ident: ts.Node): boolean { 2813af6ab5fSopenharmony_ci // Do not check the same node 2823af6ab5fSopenharmony_ci if (ident === decl) { 2833af6ab5fSopenharmony_ci return false; 2843af6ab5fSopenharmony_ci } 2853af6ab5fSopenharmony_ci 2863af6ab5fSopenharmony_ci while (ident !== undefined) { 2873af6ab5fSopenharmony_ci if (ident.kind === ts.SyntaxKind.VariableDeclaration) { 2883af6ab5fSopenharmony_ci const declName = (ident as ts.VariableDeclaration).name; 2893af6ab5fSopenharmony_ci if (declName === decl) { 2903af6ab5fSopenharmony_ci return true; 2913af6ab5fSopenharmony_ci } 2923af6ab5fSopenharmony_ci } 2933af6ab5fSopenharmony_ci // eslint-disable-next-line no-param-reassign 2943af6ab5fSopenharmony_ci ident = ident.parent; 2953af6ab5fSopenharmony_ci } 2963af6ab5fSopenharmony_ci return false; 2973af6ab5fSopenharmony_ci } 2983af6ab5fSopenharmony_ci 2993af6ab5fSopenharmony_ci private static analizeTDZ(decl: ts.VariableDeclaration, identifiers: ts.Node[]): boolean { 3003af6ab5fSopenharmony_ci for (const ident of identifiers) { 3013af6ab5fSopenharmony_ci if (Autofixer.selfDeclared(decl.name, ident)) { 3023af6ab5fSopenharmony_ci return false; 3033af6ab5fSopenharmony_ci } 3043af6ab5fSopenharmony_ci if (ident.pos < decl.pos) { 3053af6ab5fSopenharmony_ci return false; 3063af6ab5fSopenharmony_ci } 3073af6ab5fSopenharmony_ci } 3083af6ab5fSopenharmony_ci return true; 3093af6ab5fSopenharmony_ci } 3103af6ab5fSopenharmony_ci 3113af6ab5fSopenharmony_ci private static analizeScope(decl: ts.VariableDeclaration, identifiers: ts.Node[]): boolean { 3123af6ab5fSopenharmony_ci const scope = Autofixer.findVarScope(decl); 3133af6ab5fSopenharmony_ci if (scope === undefined) { 3143af6ab5fSopenharmony_ci return false; 3153af6ab5fSopenharmony_ci } else if (scope.kind === ts.SyntaxKind.Block) { 3163af6ab5fSopenharmony_ci for (const ident of identifiers) { 3173af6ab5fSopenharmony_ci if (!Autofixer.varHasScope(ident, scope)) { 3183af6ab5fSopenharmony_ci return false; 3193af6ab5fSopenharmony_ci } 3203af6ab5fSopenharmony_ci } 3213af6ab5fSopenharmony_ci } else if (scope.kind === ts.SyntaxKind.SourceFile) { 3223af6ab5fSopenharmony_ci // Do nothing 3233af6ab5fSopenharmony_ci } else { 3243af6ab5fSopenharmony_ci // Unreachable, but check it 3253af6ab5fSopenharmony_ci return false; 3263af6ab5fSopenharmony_ci } 3273af6ab5fSopenharmony_ci return true; 3283af6ab5fSopenharmony_ci } 3293af6ab5fSopenharmony_ci 3303af6ab5fSopenharmony_ci private static analizeFor(decl: ts.VariableDeclaration, identifiers: ts.Node[]): boolean { 3313af6ab5fSopenharmony_ci const forNode = Autofixer.parentInFor(decl); 3323af6ab5fSopenharmony_ci if (forNode) { 3333af6ab5fSopenharmony_ci // analize that var is initialized 3343af6ab5fSopenharmony_ci if (forNode.kind === ts.SyntaxKind.ForInStatement || forNode.kind === ts.SyntaxKind.ForOfStatement) { 3353af6ab5fSopenharmony_ci const typedForNode = forNode as ts.ForInOrOfStatement; 3363af6ab5fSopenharmony_ci const forVarDeclarations = (typedForNode.initializer as ts.VariableDeclarationList).declarations; 3373af6ab5fSopenharmony_ci if (forVarDeclarations.length !== 1) { 3383af6ab5fSopenharmony_ci return false; 3393af6ab5fSopenharmony_ci } 3403af6ab5fSopenharmony_ci const forVarDecl = forVarDeclarations[0]; 3413af6ab5fSopenharmony_ci 3423af6ab5fSopenharmony_ci // our goal to skip declarations in for of/in initializer 3433af6ab5fSopenharmony_ci if (forVarDecl !== decl && decl.initializer === undefined) { 3443af6ab5fSopenharmony_ci return false; 3453af6ab5fSopenharmony_ci } 3463af6ab5fSopenharmony_ci } else if (decl.initializer === undefined) { 3473af6ab5fSopenharmony_ci return false; 3483af6ab5fSopenharmony_ci } 3493af6ab5fSopenharmony_ci 3503af6ab5fSopenharmony_ci // analize that var uses are only in function block 3513af6ab5fSopenharmony_ci for (const ident of identifiers) { 3523af6ab5fSopenharmony_ci if (ident !== decl && !Autofixer.varHasScope(ident, forNode)) { 3533af6ab5fSopenharmony_ci return false; 3543af6ab5fSopenharmony_ci } 3553af6ab5fSopenharmony_ci } 3563af6ab5fSopenharmony_ci 3573af6ab5fSopenharmony_ci // analize that var is not in function 3583af6ab5fSopenharmony_ci for (const ident of identifiers) { 3593af6ab5fSopenharmony_ci if (ident !== decl && Autofixer.varInFunctionForScope(ident, forNode)) { 3603af6ab5fSopenharmony_ci return false; 3613af6ab5fSopenharmony_ci } 3623af6ab5fSopenharmony_ci } 3633af6ab5fSopenharmony_ci } 3643af6ab5fSopenharmony_ci return true; 3653af6ab5fSopenharmony_ci } 3663af6ab5fSopenharmony_ci 3673af6ab5fSopenharmony_ci private checkVarDeclarations(varDeclList: ts.VariableDeclarationList): boolean { 3683af6ab5fSopenharmony_ci for (const decl of varDeclList.declarations) { 3693af6ab5fSopenharmony_ci const symbol = this.typeChecker.getSymbolAtLocation(decl.name); 3703af6ab5fSopenharmony_ci if (!symbol) { 3713af6ab5fSopenharmony_ci return false; 3723af6ab5fSopenharmony_ci } 3733af6ab5fSopenharmony_ci 3743af6ab5fSopenharmony_ci const identifiers = this.symbolCache.getReferences(symbol); 3753af6ab5fSopenharmony_ci 3763af6ab5fSopenharmony_ci const declLength = symbol.declarations?.length; 3773af6ab5fSopenharmony_ci if (!declLength || declLength >= 2) { 3783af6ab5fSopenharmony_ci return false; 3793af6ab5fSopenharmony_ci } 3803af6ab5fSopenharmony_ci 3813af6ab5fSopenharmony_ci // Check for var use in tdz oe self declaration 3823af6ab5fSopenharmony_ci if (!Autofixer.analizeTDZ(decl, identifiers)) { 3833af6ab5fSopenharmony_ci return false; 3843af6ab5fSopenharmony_ci } 3853af6ab5fSopenharmony_ci 3863af6ab5fSopenharmony_ci // Has use outside scope of declaration? 3873af6ab5fSopenharmony_ci if (!Autofixer.analizeScope(decl, identifiers)) { 3883af6ab5fSopenharmony_ci return false; 3893af6ab5fSopenharmony_ci } 3903af6ab5fSopenharmony_ci 3913af6ab5fSopenharmony_ci // For analisys 3923af6ab5fSopenharmony_ci if (!Autofixer.analizeFor(decl, identifiers)) { 3933af6ab5fSopenharmony_ci return false; 3943af6ab5fSopenharmony_ci } 3953af6ab5fSopenharmony_ci 3963af6ab5fSopenharmony_ci if (symbol.getName() === 'let') { 3973af6ab5fSopenharmony_ci return false; 3983af6ab5fSopenharmony_ci } 3993af6ab5fSopenharmony_ci } 4003af6ab5fSopenharmony_ci return true; 4013af6ab5fSopenharmony_ci } 4023af6ab5fSopenharmony_ci 4033af6ab5fSopenharmony_ci private canAutofixNoVar(varDeclList: ts.VariableDeclarationList): boolean { 4043af6ab5fSopenharmony_ci if (!Autofixer.parentInCaseOrWhile(varDeclList)) { 4053af6ab5fSopenharmony_ci return false; 4063af6ab5fSopenharmony_ci } 4073af6ab5fSopenharmony_ci 4083af6ab5fSopenharmony_ci if (!this.checkVarDeclarations(varDeclList)) { 4093af6ab5fSopenharmony_ci return false; 4103af6ab5fSopenharmony_ci } 4113af6ab5fSopenharmony_ci 4123af6ab5fSopenharmony_ci return true; 4133af6ab5fSopenharmony_ci } 4143af6ab5fSopenharmony_ci 4153af6ab5fSopenharmony_ci fixVarDeclaration(node: ts.VariableDeclarationList): Autofix[] | undefined { 4163af6ab5fSopenharmony_ci const newNode = ts.factory.createVariableDeclarationList(node.declarations, ts.NodeFlags.Let); 4173af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, newNode, node.getSourceFile()); 4183af6ab5fSopenharmony_ci return this.canAutofixNoVar(node) ? 4193af6ab5fSopenharmony_ci [{ start: node.getStart(), end: node.getEnd(), replacementText: text }] : 4203af6ab5fSopenharmony_ci undefined; 4213af6ab5fSopenharmony_ci } 4223af6ab5fSopenharmony_ci 4233af6ab5fSopenharmony_ci private getFixReturnTypeArrowFunction(funcLikeDecl: ts.FunctionLikeDeclaration, typeNode: ts.TypeNode): string { 4243af6ab5fSopenharmony_ci if (!funcLikeDecl.body) { 4253af6ab5fSopenharmony_ci return ''; 4263af6ab5fSopenharmony_ci } 4273af6ab5fSopenharmony_ci const node = ts.factory.createArrowFunction( 4283af6ab5fSopenharmony_ci undefined, 4293af6ab5fSopenharmony_ci funcLikeDecl.typeParameters, 4303af6ab5fSopenharmony_ci funcLikeDecl.parameters, 4313af6ab5fSopenharmony_ci typeNode, 4323af6ab5fSopenharmony_ci ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 4333af6ab5fSopenharmony_ci funcLikeDecl.body 4343af6ab5fSopenharmony_ci ); 4353af6ab5fSopenharmony_ci return this.printer.printNode(ts.EmitHint.Unspecified, node, funcLikeDecl.getSourceFile()); 4363af6ab5fSopenharmony_ci } 4373af6ab5fSopenharmony_ci 4383af6ab5fSopenharmony_ci fixMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration, typeNode: ts.TypeNode): Autofix[] { 4393af6ab5fSopenharmony_ci if (ts.isArrowFunction(funcLikeDecl)) { 4403af6ab5fSopenharmony_ci const text = this.getFixReturnTypeArrowFunction(funcLikeDecl, typeNode); 4413af6ab5fSopenharmony_ci const startPos = funcLikeDecl.getStart(); 4423af6ab5fSopenharmony_ci const endPos = funcLikeDecl.getEnd(); 4433af6ab5fSopenharmony_ci return [{ start: startPos, end: endPos, replacementText: text }]; 4443af6ab5fSopenharmony_ci } 4453af6ab5fSopenharmony_ci const text = ': ' + this.printer.printNode(ts.EmitHint.Unspecified, typeNode, funcLikeDecl.getSourceFile()); 4463af6ab5fSopenharmony_ci const pos = Autofixer.getReturnTypePosition(funcLikeDecl); 4473af6ab5fSopenharmony_ci return [{ start: pos, end: pos, replacementText: text }]; 4483af6ab5fSopenharmony_ci } 4493af6ab5fSopenharmony_ci 4503af6ab5fSopenharmony_ci dropTypeOnVarDecl(varDecl: ts.VariableDeclaration): Autofix[] { 4513af6ab5fSopenharmony_ci const newVarDecl = ts.factory.createVariableDeclaration(varDecl.name, undefined, undefined, undefined); 4523af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, newVarDecl, varDecl.getSourceFile()); 4533af6ab5fSopenharmony_ci return [{ start: varDecl.getStart(), end: varDecl.getEnd(), replacementText: text }]; 4543af6ab5fSopenharmony_ci } 4553af6ab5fSopenharmony_ci 4563af6ab5fSopenharmony_ci fixTypeAssertion(typeAssertion: ts.TypeAssertion): Autofix[] { 4573af6ab5fSopenharmony_ci const asExpr = ts.factory.createAsExpression(typeAssertion.expression, typeAssertion.type); 4583af6ab5fSopenharmony_ci const text = this.nonCommentPrinter.printNode(ts.EmitHint.Unspecified, asExpr, typeAssertion.getSourceFile()); 4593af6ab5fSopenharmony_ci return [{ start: typeAssertion.getStart(), end: typeAssertion.getEnd(), replacementText: text }]; 4603af6ab5fSopenharmony_ci } 4613af6ab5fSopenharmony_ci 4623af6ab5fSopenharmony_ci fixCommaOperator(tsNode: ts.Node): Autofix[] { 4633af6ab5fSopenharmony_ci const tsExprNode = tsNode as ts.BinaryExpression; 4643af6ab5fSopenharmony_ci const text = this.recursiveCommaOperator(tsExprNode); 4653af6ab5fSopenharmony_ci return [{ start: tsExprNode.parent.getFullStart(), end: tsExprNode.parent.getEnd(), replacementText: text }]; 4663af6ab5fSopenharmony_ci } 4673af6ab5fSopenharmony_ci 4683af6ab5fSopenharmony_ci private recursiveCommaOperator(tsExprNode: ts.BinaryExpression): string { 4693af6ab5fSopenharmony_ci let text = ''; 4703af6ab5fSopenharmony_ci if (tsExprNode.operatorToken.kind !== ts.SyntaxKind.CommaToken) { 4713af6ab5fSopenharmony_ci return tsExprNode.getFullText() + ';'; 4723af6ab5fSopenharmony_ci } 4733af6ab5fSopenharmony_ci 4743af6ab5fSopenharmony_ci if (tsExprNode.left.kind === ts.SyntaxKind.BinaryExpression) { 4753af6ab5fSopenharmony_ci text += this.recursiveCommaOperator(tsExprNode.left as ts.BinaryExpression); 4763af6ab5fSopenharmony_ci text += '\n' + tsExprNode.right.getFullText() + ';'; 4773af6ab5fSopenharmony_ci } else { 4783af6ab5fSopenharmony_ci const leftText = tsExprNode.left.getFullText(); 4793af6ab5fSopenharmony_ci const rightText = tsExprNode.right.getFullText(); 4803af6ab5fSopenharmony_ci text = leftText + ';\n' + rightText + ';'; 4813af6ab5fSopenharmony_ci } 4823af6ab5fSopenharmony_ci 4833af6ab5fSopenharmony_ci return text; 4843af6ab5fSopenharmony_ci } 4853af6ab5fSopenharmony_ci 4863af6ab5fSopenharmony_ci private getEnumMembers(node: ts.Node, enumDeclsInFile: ts.Declaration[], result: Autofix[] | undefined): void { 4873af6ab5fSopenharmony_ci if (result === undefined || !ts.isEnumDeclaration(node)) { 4883af6ab5fSopenharmony_ci return; 4893af6ab5fSopenharmony_ci } 4903af6ab5fSopenharmony_ci 4913af6ab5fSopenharmony_ci if (result.length) { 4923af6ab5fSopenharmony_ci result.push({ start: node.getStart(), end: node.getEnd(), replacementText: '' }); 4933af6ab5fSopenharmony_ci return; 4943af6ab5fSopenharmony_ci } 4953af6ab5fSopenharmony_ci 4963af6ab5fSopenharmony_ci const members: ts.EnumMember[] = []; 4973af6ab5fSopenharmony_ci for (const decl of enumDeclsInFile) { 4983af6ab5fSopenharmony_ci for (const member of (decl as ts.EnumDeclaration).members) { 4993af6ab5fSopenharmony_ci if ( 5003af6ab5fSopenharmony_ci member.initializer && 5013af6ab5fSopenharmony_ci member.initializer.kind !== ts.SyntaxKind.NumericLiteral && 5023af6ab5fSopenharmony_ci member.initializer.kind !== ts.SyntaxKind.StringLiteral 5033af6ab5fSopenharmony_ci ) { 5043af6ab5fSopenharmony_ci result = undefined; 5053af6ab5fSopenharmony_ci return; 5063af6ab5fSopenharmony_ci } 5073af6ab5fSopenharmony_ci } 5083af6ab5fSopenharmony_ci members.push(...(decl as ts.EnumDeclaration).members); 5093af6ab5fSopenharmony_ci } 5103af6ab5fSopenharmony_ci 5113af6ab5fSopenharmony_ci const fullEnum = ts.factory.createEnumDeclaration(node.modifiers, node.name, members); 5123af6ab5fSopenharmony_ci const fullText = this.printer.printNode(ts.EmitHint.Unspecified, fullEnum, node.getSourceFile()); 5133af6ab5fSopenharmony_ci result.push({ start: node.getStart(), end: node.getEnd(), replacementText: fullText }); 5143af6ab5fSopenharmony_ci } 5153af6ab5fSopenharmony_ci 5163af6ab5fSopenharmony_ci fixEnumMerging(enumSymbol: ts.Symbol, enumDeclsInFile: ts.Declaration[]): Autofix[] | undefined { 5173af6ab5fSopenharmony_ci if (this.enumMergingCache.has(enumSymbol)) { 5183af6ab5fSopenharmony_ci return this.enumMergingCache.get(enumSymbol); 5193af6ab5fSopenharmony_ci } 5203af6ab5fSopenharmony_ci 5213af6ab5fSopenharmony_ci if (enumDeclsInFile.length <= 1) { 5223af6ab5fSopenharmony_ci this.enumMergingCache.set(enumSymbol, undefined); 5233af6ab5fSopenharmony_ci return undefined; 5243af6ab5fSopenharmony_ci } 5253af6ab5fSopenharmony_ci 5263af6ab5fSopenharmony_ci let result: Autofix[] | undefined = []; 5273af6ab5fSopenharmony_ci this.symbolCache.getReferences(enumSymbol).forEach((node) => { 5283af6ab5fSopenharmony_ci this.getEnumMembers(node, enumDeclsInFile, result); 5293af6ab5fSopenharmony_ci }); 5303af6ab5fSopenharmony_ci if (!result?.length) { 5313af6ab5fSopenharmony_ci result = undefined; 5323af6ab5fSopenharmony_ci } 5333af6ab5fSopenharmony_ci 5343af6ab5fSopenharmony_ci this.enumMergingCache.set(enumSymbol, result); 5353af6ab5fSopenharmony_ci return result; 5363af6ab5fSopenharmony_ci } 5373af6ab5fSopenharmony_ci 5383af6ab5fSopenharmony_ci private readonly enumMergingCache = new Map<ts.Symbol, Autofix[] | undefined>(); 5393af6ab5fSopenharmony_ci 5403af6ab5fSopenharmony_ci private readonly printer: ts.Printer = ts.createPrinter({ 5413af6ab5fSopenharmony_ci omitTrailingSemicolon: false, 5423af6ab5fSopenharmony_ci removeComments: false, 5433af6ab5fSopenharmony_ci newLine: ts.NewLineKind.LineFeed 5443af6ab5fSopenharmony_ci }); 5453af6ab5fSopenharmony_ci 5463af6ab5fSopenharmony_ci private readonly nonCommentPrinter: ts.Printer = ts.createPrinter({ 5473af6ab5fSopenharmony_ci omitTrailingSemicolon: false, 5483af6ab5fSopenharmony_ci removeComments: true, 5493af6ab5fSopenharmony_ci newLine: ts.NewLineKind.LineFeed 5503af6ab5fSopenharmony_ci }); 5513af6ab5fSopenharmony_ci 5523af6ab5fSopenharmony_ci private static getReturnTypePosition(funcLikeDecl: ts.FunctionLikeDeclaration): number { 5533af6ab5fSopenharmony_ci if (funcLikeDecl.body) { 5543af6ab5fSopenharmony_ci 5553af6ab5fSopenharmony_ci /* 5563af6ab5fSopenharmony_ci * Find position of the first node or token that follows parameters. 5573af6ab5fSopenharmony_ci * After that, iterate over child nodes in reverse order, until found 5583af6ab5fSopenharmony_ci * first closing parenthesis. 5593af6ab5fSopenharmony_ci */ 5603af6ab5fSopenharmony_ci const postParametersPosition = ts.isArrowFunction(funcLikeDecl) ? 5613af6ab5fSopenharmony_ci funcLikeDecl.equalsGreaterThanToken.getStart() : 5623af6ab5fSopenharmony_ci funcLikeDecl.body.getStart(); 5633af6ab5fSopenharmony_ci 5643af6ab5fSopenharmony_ci const children = funcLikeDecl.getChildren(); 5653af6ab5fSopenharmony_ci for (let i = children.length - 1; i >= 0; i--) { 5663af6ab5fSopenharmony_ci const child = children[i]; 5673af6ab5fSopenharmony_ci if (child.kind === ts.SyntaxKind.CloseParenToken && child.getEnd() <= postParametersPosition) { 5683af6ab5fSopenharmony_ci return child.getEnd(); 5693af6ab5fSopenharmony_ci } 5703af6ab5fSopenharmony_ci } 5713af6ab5fSopenharmony_ci } 5723af6ab5fSopenharmony_ci 5733af6ab5fSopenharmony_ci // Shouldn't get here. 5743af6ab5fSopenharmony_ci return -1; 5753af6ab5fSopenharmony_ci } 5763af6ab5fSopenharmony_ci 5773af6ab5fSopenharmony_ci private static needsParentheses(node: ts.FunctionExpression): boolean { 5783af6ab5fSopenharmony_ci const parent = node.parent; 5793af6ab5fSopenharmony_ci return ( 5803af6ab5fSopenharmony_ci ts.isPrefixUnaryExpression(parent) || 5813af6ab5fSopenharmony_ci ts.isPostfixUnaryExpression(parent) || 5823af6ab5fSopenharmony_ci ts.isPropertyAccessExpression(parent) || 5833af6ab5fSopenharmony_ci ts.isElementAccessExpression(parent) || 5843af6ab5fSopenharmony_ci ts.isTypeOfExpression(parent) || 5853af6ab5fSopenharmony_ci ts.isVoidExpression(parent) || 5863af6ab5fSopenharmony_ci ts.isAwaitExpression(parent) || 5873af6ab5fSopenharmony_ci ts.isCallExpression(parent) && node === parent.expression || 5883af6ab5fSopenharmony_ci ts.isBinaryExpression(parent) && !isAssignmentOperator(parent.operatorToken) 5893af6ab5fSopenharmony_ci ); 5903af6ab5fSopenharmony_ci } 5913af6ab5fSopenharmony_ci 5923af6ab5fSopenharmony_ci fixCtorParameterProperties( 5933af6ab5fSopenharmony_ci ctorDecl: ts.ConstructorDeclaration, 5943af6ab5fSopenharmony_ci paramTypes: ts.TypeNode[] | undefined 5953af6ab5fSopenharmony_ci ): Autofix[] | undefined { 5963af6ab5fSopenharmony_ci if (paramTypes === undefined) { 5973af6ab5fSopenharmony_ci return undefined; 5983af6ab5fSopenharmony_ci } 5993af6ab5fSopenharmony_ci 6003af6ab5fSopenharmony_ci const fieldInitStmts: ts.Statement[] = []; 6013af6ab5fSopenharmony_ci const newFieldPos = ctorDecl.getStart(); 6023af6ab5fSopenharmony_ci const autofixes: Autofix[] = [{ start: newFieldPos, end: newFieldPos, replacementText: '' }]; 6033af6ab5fSopenharmony_ci 6043af6ab5fSopenharmony_ci for (let i = 0; i < ctorDecl.parameters.length; i++) { 6053af6ab5fSopenharmony_ci this.fixCtorParameterPropertiesProcessParam( 6063af6ab5fSopenharmony_ci ctorDecl.parameters[i], 6073af6ab5fSopenharmony_ci paramTypes[i], 6083af6ab5fSopenharmony_ci ctorDecl.getSourceFile(), 6093af6ab5fSopenharmony_ci fieldInitStmts, 6103af6ab5fSopenharmony_ci autofixes 6113af6ab5fSopenharmony_ci ); 6123af6ab5fSopenharmony_ci } 6133af6ab5fSopenharmony_ci 6143af6ab5fSopenharmony_ci // Note: Bodyless ctors can't have parameter properties. 6153af6ab5fSopenharmony_ci if (ctorDecl.body) { 6163af6ab5fSopenharmony_ci const newBody = ts.factory.createBlock(fieldInitStmts.concat(ctorDecl.body.statements), true); 6173af6ab5fSopenharmony_ci const newBodyText = this.printer.printNode(ts.EmitHint.Unspecified, newBody, ctorDecl.getSourceFile()); 6183af6ab5fSopenharmony_ci autofixes.push({ start: ctorDecl.body.getStart(), end: ctorDecl.body.getEnd(), replacementText: newBodyText }); 6193af6ab5fSopenharmony_ci } 6203af6ab5fSopenharmony_ci 6213af6ab5fSopenharmony_ci return autofixes; 6223af6ab5fSopenharmony_ci } 6233af6ab5fSopenharmony_ci 6243af6ab5fSopenharmony_ci private fixCtorParameterPropertiesProcessParam( 6253af6ab5fSopenharmony_ci param: ts.ParameterDeclaration, 6263af6ab5fSopenharmony_ci paramType: ts.TypeNode, 6273af6ab5fSopenharmony_ci sourceFile: ts.SourceFile, 6283af6ab5fSopenharmony_ci fieldInitStmts: ts.Statement[], 6293af6ab5fSopenharmony_ci autofixes: Autofix[] 6303af6ab5fSopenharmony_ci ): void { 6313af6ab5fSopenharmony_ci // Parameter property can not be a destructuring parameter. 6323af6ab5fSopenharmony_ci if (!ts.isIdentifier(param.name)) { 6333af6ab5fSopenharmony_ci return; 6343af6ab5fSopenharmony_ci } 6353af6ab5fSopenharmony_ci 6363af6ab5fSopenharmony_ci if (this.utils.hasAccessModifier(param)) { 6373af6ab5fSopenharmony_ci const propIdent = ts.factory.createIdentifier(param.name.text); 6383af6ab5fSopenharmony_ci 6393af6ab5fSopenharmony_ci const newFieldNode = ts.factory.createPropertyDeclaration( 6403af6ab5fSopenharmony_ci ts.getModifiers(param), 6413af6ab5fSopenharmony_ci propIdent, 6423af6ab5fSopenharmony_ci undefined, 6433af6ab5fSopenharmony_ci paramType, 6443af6ab5fSopenharmony_ci undefined 6453af6ab5fSopenharmony_ci ); 6463af6ab5fSopenharmony_ci const newFieldText = this.printer.printNode(ts.EmitHint.Unspecified, newFieldNode, sourceFile) + '\n'; 6473af6ab5fSopenharmony_ci autofixes[0].replacementText += newFieldText; 6483af6ab5fSopenharmony_ci 6493af6ab5fSopenharmony_ci const newParamDecl = ts.factory.createParameterDeclaration( 6503af6ab5fSopenharmony_ci undefined, 6513af6ab5fSopenharmony_ci undefined, 6523af6ab5fSopenharmony_ci param.name, 6533af6ab5fSopenharmony_ci param.questionToken, 6543af6ab5fSopenharmony_ci param.type, 6553af6ab5fSopenharmony_ci param.initializer 6563af6ab5fSopenharmony_ci ); 6573af6ab5fSopenharmony_ci const newParamText = this.printer.printNode(ts.EmitHint.Unspecified, newParamDecl, sourceFile); 6583af6ab5fSopenharmony_ci autofixes.push({ start: param.getStart(), end: param.getEnd(), replacementText: newParamText }); 6593af6ab5fSopenharmony_ci 6603af6ab5fSopenharmony_ci fieldInitStmts.push( 6613af6ab5fSopenharmony_ci ts.factory.createExpressionStatement( 6623af6ab5fSopenharmony_ci ts.factory.createAssignment( 6633af6ab5fSopenharmony_ci ts.factory.createPropertyAccessExpression(ts.factory.createThis(), propIdent), 6643af6ab5fSopenharmony_ci propIdent 6653af6ab5fSopenharmony_ci ) 6663af6ab5fSopenharmony_ci ) 6673af6ab5fSopenharmony_ci ); 6683af6ab5fSopenharmony_ci } 6693af6ab5fSopenharmony_ci } 6703af6ab5fSopenharmony_ci 6713af6ab5fSopenharmony_ci fixPrivateIdentifier(node: ts.PrivateIdentifier): Autofix[] | undefined { 6723af6ab5fSopenharmony_ci const classMember = this.typeChecker.getSymbolAtLocation(node); 6733af6ab5fSopenharmony_ci if (!classMember || (classMember.getFlags() & ts.SymbolFlags.ClassMember) === 0 || !classMember.valueDeclaration) { 6743af6ab5fSopenharmony_ci return undefined; 6753af6ab5fSopenharmony_ci } 6763af6ab5fSopenharmony_ci 6773af6ab5fSopenharmony_ci if (this.privateIdentifierCache.has(classMember)) { 6783af6ab5fSopenharmony_ci return this.privateIdentifierCache.get(classMember); 6793af6ab5fSopenharmony_ci } 6803af6ab5fSopenharmony_ci 6813af6ab5fSopenharmony_ci const memberDecl = classMember.valueDeclaration as ts.ClassElement; 6823af6ab5fSopenharmony_ci const parentDecl = memberDecl.parent; 6833af6ab5fSopenharmony_ci if (!ts.isClassLike(parentDecl) || this.utils.classMemberHasDuplicateName(memberDecl, parentDecl, true)) { 6843af6ab5fSopenharmony_ci this.privateIdentifierCache.set(classMember, undefined); 6853af6ab5fSopenharmony_ci return undefined; 6863af6ab5fSopenharmony_ci } 6873af6ab5fSopenharmony_ci 6883af6ab5fSopenharmony_ci let result: Autofix[] | undefined = []; 6893af6ab5fSopenharmony_ci this.symbolCache.getReferences(classMember).forEach((ident) => { 6903af6ab5fSopenharmony_ci if (ts.isPrivateIdentifier(ident)) { 6913af6ab5fSopenharmony_ci result!.push(this.fixSinglePrivateIdentifier(ident)); 6923af6ab5fSopenharmony_ci } 6933af6ab5fSopenharmony_ci }); 6943af6ab5fSopenharmony_ci if (!result.length) { 6953af6ab5fSopenharmony_ci result = undefined; 6963af6ab5fSopenharmony_ci } 6973af6ab5fSopenharmony_ci 6983af6ab5fSopenharmony_ci this.privateIdentifierCache.set(classMember, result); 6993af6ab5fSopenharmony_ci return result; 7003af6ab5fSopenharmony_ci } 7013af6ab5fSopenharmony_ci 7023af6ab5fSopenharmony_ci private isFunctionDeclarationFirst(tsFunctionDeclaration: ts.FunctionDeclaration): boolean { 7033af6ab5fSopenharmony_ci if (tsFunctionDeclaration.name === undefined) { 7043af6ab5fSopenharmony_ci return false; 7053af6ab5fSopenharmony_ci } 7063af6ab5fSopenharmony_ci 7073af6ab5fSopenharmony_ci const symbol = this.typeChecker.getSymbolAtLocation(tsFunctionDeclaration.name); 7083af6ab5fSopenharmony_ci if (symbol === undefined) { 7093af6ab5fSopenharmony_ci return false; 7103af6ab5fSopenharmony_ci } 7113af6ab5fSopenharmony_ci 7123af6ab5fSopenharmony_ci let minPos = tsFunctionDeclaration.pos; 7133af6ab5fSopenharmony_ci this.symbolCache.getReferences(symbol).forEach((ident) => { 7143af6ab5fSopenharmony_ci if (ident.pos < minPos) { 7153af6ab5fSopenharmony_ci minPos = ident.pos; 7163af6ab5fSopenharmony_ci } 7173af6ab5fSopenharmony_ci }); 7183af6ab5fSopenharmony_ci 7193af6ab5fSopenharmony_ci return minPos >= tsFunctionDeclaration.pos; 7203af6ab5fSopenharmony_ci } 7213af6ab5fSopenharmony_ci 7223af6ab5fSopenharmony_ci fixNestedFunction(tsFunctionDeclaration: ts.FunctionDeclaration): Autofix[] | undefined { 7233af6ab5fSopenharmony_ci const isGenerator = tsFunctionDeclaration.asteriskToken !== undefined; 7243af6ab5fSopenharmony_ci const hasThisKeyword = 7253af6ab5fSopenharmony_ci tsFunctionDeclaration.body === undefined ? false : scopeContainsThis(tsFunctionDeclaration.body); 7263af6ab5fSopenharmony_ci const canBeFixed = !isGenerator && !hasThisKeyword; 7273af6ab5fSopenharmony_ci if (!canBeFixed) { 7283af6ab5fSopenharmony_ci return undefined; 7293af6ab5fSopenharmony_ci } 7303af6ab5fSopenharmony_ci 7313af6ab5fSopenharmony_ci const name = tsFunctionDeclaration.name?.escapedText; 7323af6ab5fSopenharmony_ci const type = tsFunctionDeclaration.type; 7333af6ab5fSopenharmony_ci const body = tsFunctionDeclaration.body; 7343af6ab5fSopenharmony_ci if (!name || !type || !body) { 7353af6ab5fSopenharmony_ci return undefined; 7363af6ab5fSopenharmony_ci } 7373af6ab5fSopenharmony_ci 7383af6ab5fSopenharmony_ci // Check only illegal decorators, cause all decorators for function declaration are illegal 7393af6ab5fSopenharmony_ci if (ts.getIllegalDecorators(tsFunctionDeclaration)) { 7403af6ab5fSopenharmony_ci return undefined; 7413af6ab5fSopenharmony_ci } 7423af6ab5fSopenharmony_ci 7433af6ab5fSopenharmony_ci if (!this.isFunctionDeclarationFirst(tsFunctionDeclaration)) { 7443af6ab5fSopenharmony_ci return undefined; 7453af6ab5fSopenharmony_ci } 7463af6ab5fSopenharmony_ci 7473af6ab5fSopenharmony_ci const typeParameters = tsFunctionDeclaration.typeParameters; 7483af6ab5fSopenharmony_ci const parameters = tsFunctionDeclaration.parameters; 7493af6ab5fSopenharmony_ci const modifiers = ts.getModifiers(tsFunctionDeclaration); 7503af6ab5fSopenharmony_ci 7513af6ab5fSopenharmony_ci const token = ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken); 7523af6ab5fSopenharmony_ci const typeDecl = ts.factory.createFunctionTypeNode(typeParameters, parameters, type); 7533af6ab5fSopenharmony_ci const arrowFunc = ts.factory.createArrowFunction(modifiers, typeParameters, parameters, type, token, body); 7543af6ab5fSopenharmony_ci 7553af6ab5fSopenharmony_ci const declaration: ts.VariableDeclaration = ts.factory.createVariableDeclaration( 7563af6ab5fSopenharmony_ci name, 7573af6ab5fSopenharmony_ci undefined, 7583af6ab5fSopenharmony_ci typeDecl, 7593af6ab5fSopenharmony_ci arrowFunc 7603af6ab5fSopenharmony_ci ); 7613af6ab5fSopenharmony_ci const list: ts.VariableDeclarationList = ts.factory.createVariableDeclarationList([declaration], ts.NodeFlags.Let); 7623af6ab5fSopenharmony_ci 7633af6ab5fSopenharmony_ci const statement = ts.factory.createVariableStatement(modifiers, list); 7643af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, statement, tsFunctionDeclaration.getSourceFile()); 7653af6ab5fSopenharmony_ci return [{ start: tsFunctionDeclaration.getStart(), end: tsFunctionDeclaration.getEnd(), replacementText: text }]; 7663af6ab5fSopenharmony_ci } 7673af6ab5fSopenharmony_ci 7683af6ab5fSopenharmony_ci fixMultipleStaticBlocks(nodes: ts.Node[]): Autofix[] | undefined { 7693af6ab5fSopenharmony_ci const autofix: Autofix[] | undefined = []; 7703af6ab5fSopenharmony_ci let body = (nodes[0] as ts.ClassStaticBlockDeclaration).body; 7713af6ab5fSopenharmony_ci let bodyStatements: ts.Statement[] = []; 7723af6ab5fSopenharmony_ci bodyStatements = bodyStatements.concat(body.statements); 7733af6ab5fSopenharmony_ci for (let i = 1; i < nodes.length; i++) { 7743af6ab5fSopenharmony_ci bodyStatements = bodyStatements.concat((nodes[i] as ts.ClassStaticBlockDeclaration).body.statements); 7753af6ab5fSopenharmony_ci autofix[i] = { start: nodes[i].getStart(), end: nodes[i].getEnd(), replacementText: '' }; 7763af6ab5fSopenharmony_ci } 7773af6ab5fSopenharmony_ci body = ts.factory.createBlock(bodyStatements, true); 7783af6ab5fSopenharmony_ci // static blocks shouldn't have modifiers 7793af6ab5fSopenharmony_ci const statickBlock = ts.factory.createClassStaticBlockDeclaration(body); 7803af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, statickBlock, nodes[0].getSourceFile()); 7813af6ab5fSopenharmony_ci autofix[0] = { start: nodes[0].getStart(), end: nodes[0].getEnd(), replacementText: text }; 7823af6ab5fSopenharmony_ci return autofix; 7833af6ab5fSopenharmony_ci } 7843af6ab5fSopenharmony_ci 7853af6ab5fSopenharmony_ci private readonly privateIdentifierCache = new Map<ts.Symbol, Autofix[] | undefined>(); 7863af6ab5fSopenharmony_ci 7873af6ab5fSopenharmony_ci private fixSinglePrivateIdentifier(ident: ts.PrivateIdentifier): Autofix { 7883af6ab5fSopenharmony_ci if ( 7893af6ab5fSopenharmony_ci ts.isPropertyDeclaration(ident.parent) || 7903af6ab5fSopenharmony_ci ts.isMethodDeclaration(ident.parent) || 7913af6ab5fSopenharmony_ci ts.isGetAccessorDeclaration(ident.parent) || 7923af6ab5fSopenharmony_ci ts.isSetAccessorDeclaration(ident.parent) 7933af6ab5fSopenharmony_ci ) { 7943af6ab5fSopenharmony_ci // Note: 'private' modifier should always be first. 7953af6ab5fSopenharmony_ci const mods = ts.getModifiers(ident.parent); 7963af6ab5fSopenharmony_ci const newMods: ts.Modifier[] = [ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword)]; 7973af6ab5fSopenharmony_ci if (mods) { 7983af6ab5fSopenharmony_ci for (const mod of mods) { 7993af6ab5fSopenharmony_ci newMods.push(ts.factory.createModifier(mod.kind)); 8003af6ab5fSopenharmony_ci } 8013af6ab5fSopenharmony_ci } 8023af6ab5fSopenharmony_ci 8033af6ab5fSopenharmony_ci const newName = ident.text.slice(1, ident.text.length); 8043af6ab5fSopenharmony_ci const newDecl = Autofixer.replacePrivateIdentInDeclarationName(newMods, newName, ident.parent); 8053af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, newDecl, ident.getSourceFile()); 8063af6ab5fSopenharmony_ci return { start: ident.parent.getStart(), end: ident.parent.getEnd(), replacementText: text }; 8073af6ab5fSopenharmony_ci } 8083af6ab5fSopenharmony_ci 8093af6ab5fSopenharmony_ci return { 8103af6ab5fSopenharmony_ci start: ident.getStart(), 8113af6ab5fSopenharmony_ci end: ident.getEnd(), 8123af6ab5fSopenharmony_ci replacementText: ident.text.slice(1, ident.text.length) 8133af6ab5fSopenharmony_ci }; 8143af6ab5fSopenharmony_ci } 8153af6ab5fSopenharmony_ci 8163af6ab5fSopenharmony_ci private static replacePrivateIdentInDeclarationName( 8173af6ab5fSopenharmony_ci mods: ts.Modifier[], 8183af6ab5fSopenharmony_ci name: string, 8193af6ab5fSopenharmony_ci oldDecl: ts.PropertyDeclaration | ts.MethodDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration 8203af6ab5fSopenharmony_ci ): ts.Declaration { 8213af6ab5fSopenharmony_ci if (ts.isPropertyDeclaration(oldDecl)) { 8223af6ab5fSopenharmony_ci return ts.factory.createPropertyDeclaration( 8233af6ab5fSopenharmony_ci mods, 8243af6ab5fSopenharmony_ci ts.factory.createIdentifier(name), 8253af6ab5fSopenharmony_ci oldDecl.questionToken ?? oldDecl.exclamationToken, 8263af6ab5fSopenharmony_ci oldDecl.type, 8273af6ab5fSopenharmony_ci oldDecl.initializer 8283af6ab5fSopenharmony_ci ); 8293af6ab5fSopenharmony_ci } else if (ts.isMethodDeclaration(oldDecl)) { 8303af6ab5fSopenharmony_ci return ts.factory.createMethodDeclaration( 8313af6ab5fSopenharmony_ci mods, 8323af6ab5fSopenharmony_ci oldDecl.asteriskToken, 8333af6ab5fSopenharmony_ci ts.factory.createIdentifier(name), 8343af6ab5fSopenharmony_ci oldDecl.questionToken, 8353af6ab5fSopenharmony_ci oldDecl.typeParameters, 8363af6ab5fSopenharmony_ci oldDecl.parameters, 8373af6ab5fSopenharmony_ci oldDecl.type, 8383af6ab5fSopenharmony_ci oldDecl.body 8393af6ab5fSopenharmony_ci ); 8403af6ab5fSopenharmony_ci } else if (ts.isGetAccessorDeclaration(oldDecl)) { 8413af6ab5fSopenharmony_ci return ts.factory.createGetAccessorDeclaration( 8423af6ab5fSopenharmony_ci mods, 8433af6ab5fSopenharmony_ci ts.factory.createIdentifier(name), 8443af6ab5fSopenharmony_ci oldDecl.parameters, 8453af6ab5fSopenharmony_ci oldDecl.type, 8463af6ab5fSopenharmony_ci oldDecl.body 8473af6ab5fSopenharmony_ci ); 8483af6ab5fSopenharmony_ci } 8493af6ab5fSopenharmony_ci return ts.factory.createSetAccessorDeclaration( 8503af6ab5fSopenharmony_ci mods, 8513af6ab5fSopenharmony_ci ts.factory.createIdentifier(name), 8523af6ab5fSopenharmony_ci oldDecl.parameters, 8533af6ab5fSopenharmony_ci oldDecl.body 8543af6ab5fSopenharmony_ci ); 8553af6ab5fSopenharmony_ci } 8563af6ab5fSopenharmony_ci 8573af6ab5fSopenharmony_ci fixRecordObjectLiteral(objectLiteralExpr: ts.ObjectLiteralExpression): Autofix[] | undefined { 8583af6ab5fSopenharmony_ci const autofix: Autofix[] = []; 8593af6ab5fSopenharmony_ci 8603af6ab5fSopenharmony_ci for (const prop of objectLiteralExpr.properties) { 8613af6ab5fSopenharmony_ci if (!prop.name) { 8623af6ab5fSopenharmony_ci return undefined; 8633af6ab5fSopenharmony_ci } 8643af6ab5fSopenharmony_ci if (this.utils.isValidRecordObjectLiteralKey(prop.name)) { 8653af6ab5fSopenharmony_ci // Skip property with a valid property key. 8663af6ab5fSopenharmony_ci continue; 8673af6ab5fSopenharmony_ci } 8683af6ab5fSopenharmony_ci if (!ts.isIdentifier(prop.name)) { 8693af6ab5fSopenharmony_ci // Can only fix identifier name. 8703af6ab5fSopenharmony_ci return undefined; 8713af6ab5fSopenharmony_ci } 8723af6ab5fSopenharmony_ci 8733af6ab5fSopenharmony_ci const stringLiteralName = ts.factory.createStringLiteralFromNode(prop.name, true); 8743af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, stringLiteralName, prop.name.getSourceFile()); 8753af6ab5fSopenharmony_ci autofix.push({ start: prop.name.getStart(), end: prop.name.getEnd(), replacementText: text }); 8763af6ab5fSopenharmony_ci } 8773af6ab5fSopenharmony_ci 8783af6ab5fSopenharmony_ci return autofix; 8793af6ab5fSopenharmony_ci } 8803af6ab5fSopenharmony_ci 8813af6ab5fSopenharmony_ci fixUntypedObjectLiteral( 8823af6ab5fSopenharmony_ci objectLiteralExpr: ts.ObjectLiteralExpression, 8833af6ab5fSopenharmony_ci objectLiteralType: ts.Type | undefined 8843af6ab5fSopenharmony_ci ): Autofix[] | undefined { 8853af6ab5fSopenharmony_ci if (objectLiteralType) { 8863af6ab5fSopenharmony_ci 8873af6ab5fSopenharmony_ci /* 8883af6ab5fSopenharmony_ci * Special case for object literal of Record type: fix object's property names 8893af6ab5fSopenharmony_ci * by replacing identifiers with string literals. 8903af6ab5fSopenharmony_ci */ 8913af6ab5fSopenharmony_ci if (this.utils.isStdRecordType(this.utils.getNonNullableType(objectLiteralType))) { 8923af6ab5fSopenharmony_ci return this.fixRecordObjectLiteral(objectLiteralExpr); 8933af6ab5fSopenharmony_ci } 8943af6ab5fSopenharmony_ci 8953af6ab5fSopenharmony_ci // Can't fix when object literal has a contextual type. 8963af6ab5fSopenharmony_ci return undefined; 8973af6ab5fSopenharmony_ci } 8983af6ab5fSopenharmony_ci 8993af6ab5fSopenharmony_ci const enclosingStmt = TsUtils.getEnclosingTopLevelStatement(objectLiteralExpr); 9003af6ab5fSopenharmony_ci if (!enclosingStmt) { 9013af6ab5fSopenharmony_ci return undefined; 9023af6ab5fSopenharmony_ci } 9033af6ab5fSopenharmony_ci 9043af6ab5fSopenharmony_ci const newInterfaceProps = this.getInterfacePropertiesFromObjectLiteral(objectLiteralExpr, enclosingStmt); 9053af6ab5fSopenharmony_ci if (!newInterfaceProps) { 9063af6ab5fSopenharmony_ci return undefined; 9073af6ab5fSopenharmony_ci } 9083af6ab5fSopenharmony_ci 9093af6ab5fSopenharmony_ci const srcFile = objectLiteralExpr.getSourceFile(); 9103af6ab5fSopenharmony_ci const newInterfaceName = TsUtils.generateUniqueName(this.objectLiteralInterfaceNameGenerator, srcFile); 9113af6ab5fSopenharmony_ci if (!newInterfaceName) { 9123af6ab5fSopenharmony_ci return undefined; 9133af6ab5fSopenharmony_ci } 9143af6ab5fSopenharmony_ci 9153af6ab5fSopenharmony_ci return [ 9163af6ab5fSopenharmony_ci this.createNewInterface(srcFile, newInterfaceName, newInterfaceProps, enclosingStmt.getStart()), 9173af6ab5fSopenharmony_ci this.fixObjectLiteralExpression(srcFile, newInterfaceName, objectLiteralExpr) 9183af6ab5fSopenharmony_ci ]; 9193af6ab5fSopenharmony_ci } 9203af6ab5fSopenharmony_ci 9213af6ab5fSopenharmony_ci private getInterfacePropertiesFromObjectLiteral( 9223af6ab5fSopenharmony_ci objectLiteralExpr: ts.ObjectLiteralExpression, 9233af6ab5fSopenharmony_ci enclosingStmt: ts.Node 9243af6ab5fSopenharmony_ci ): ts.PropertySignature[] | undefined { 9253af6ab5fSopenharmony_ci const interfaceProps: ts.PropertySignature[] = []; 9263af6ab5fSopenharmony_ci for (const prop of objectLiteralExpr.properties) { 9273af6ab5fSopenharmony_ci const interfaceProp = this.getInterfacePropertyFromObjectLiteralElement(prop, enclosingStmt); 9283af6ab5fSopenharmony_ci if (!interfaceProp) { 9293af6ab5fSopenharmony_ci return undefined; 9303af6ab5fSopenharmony_ci } 9313af6ab5fSopenharmony_ci interfaceProps.push(interfaceProp); 9323af6ab5fSopenharmony_ci } 9333af6ab5fSopenharmony_ci return interfaceProps; 9343af6ab5fSopenharmony_ci } 9353af6ab5fSopenharmony_ci 9363af6ab5fSopenharmony_ci private getInterfacePropertyFromObjectLiteralElement( 9373af6ab5fSopenharmony_ci prop: ts.ObjectLiteralElementLike, 9383af6ab5fSopenharmony_ci enclosingStmt: ts.Node 9393af6ab5fSopenharmony_ci ): ts.PropertySignature | undefined { 9403af6ab5fSopenharmony_ci // Can't fix if property is not a key-value pair, or the property name is a computed value. 9413af6ab5fSopenharmony_ci if (!ts.isPropertyAssignment(prop) || ts.isComputedPropertyName(prop.name)) { 9423af6ab5fSopenharmony_ci return undefined; 9433af6ab5fSopenharmony_ci } 9443af6ab5fSopenharmony_ci 9453af6ab5fSopenharmony_ci const propType = this.typeChecker.getTypeAtLocation(prop); 9463af6ab5fSopenharmony_ci 9473af6ab5fSopenharmony_ci // Can't capture generic type parameters of enclosing declarations. 9483af6ab5fSopenharmony_ci if (this.utils.hasGenericTypeParameter(propType)) { 9493af6ab5fSopenharmony_ci return undefined; 9503af6ab5fSopenharmony_ci } 9513af6ab5fSopenharmony_ci 9523af6ab5fSopenharmony_ci if (Autofixer.propertyTypeIsCapturedFromEnclosingLocalScope(propType, enclosingStmt)) { 9533af6ab5fSopenharmony_ci return undefined; 9543af6ab5fSopenharmony_ci } 9553af6ab5fSopenharmony_ci 9563af6ab5fSopenharmony_ci const propTypeNode = this.typeChecker.typeToTypeNode(propType, undefined, ts.NodeBuilderFlags.None); 9573af6ab5fSopenharmony_ci if (!propTypeNode || !this.utils.isSupportedType(propTypeNode)) { 9583af6ab5fSopenharmony_ci return undefined; 9593af6ab5fSopenharmony_ci } 9603af6ab5fSopenharmony_ci 9613af6ab5fSopenharmony_ci const newProp: ts.PropertySignature = ts.factory.createPropertySignature( 9623af6ab5fSopenharmony_ci undefined, 9633af6ab5fSopenharmony_ci prop.name, 9643af6ab5fSopenharmony_ci undefined, 9653af6ab5fSopenharmony_ci propTypeNode 9663af6ab5fSopenharmony_ci ); 9673af6ab5fSopenharmony_ci return newProp; 9683af6ab5fSopenharmony_ci } 9693af6ab5fSopenharmony_ci 9703af6ab5fSopenharmony_ci private static propertyTypeIsCapturedFromEnclosingLocalScope(type: ts.Type, enclosingStmt: ts.Node): boolean { 9713af6ab5fSopenharmony_ci const sym = type.getSymbol(); 9723af6ab5fSopenharmony_ci let symNode: ts.Node | undefined = TsUtils.getDeclaration(sym); 9733af6ab5fSopenharmony_ci 9743af6ab5fSopenharmony_ci while (symNode) { 9753af6ab5fSopenharmony_ci if (symNode === enclosingStmt) { 9763af6ab5fSopenharmony_ci return true; 9773af6ab5fSopenharmony_ci } 9783af6ab5fSopenharmony_ci symNode = symNode.parent; 9793af6ab5fSopenharmony_ci } 9803af6ab5fSopenharmony_ci 9813af6ab5fSopenharmony_ci return false; 9823af6ab5fSopenharmony_ci } 9833af6ab5fSopenharmony_ci 9843af6ab5fSopenharmony_ci private createNewInterface( 9853af6ab5fSopenharmony_ci srcFile: ts.SourceFile, 9863af6ab5fSopenharmony_ci interfaceName: string, 9873af6ab5fSopenharmony_ci members: ts.TypeElement[], 9883af6ab5fSopenharmony_ci pos: number 9893af6ab5fSopenharmony_ci ): Autofix { 9903af6ab5fSopenharmony_ci const newInterfaceDecl = ts.factory.createInterfaceDeclaration( 9913af6ab5fSopenharmony_ci undefined, 9923af6ab5fSopenharmony_ci interfaceName, 9933af6ab5fSopenharmony_ci undefined, 9943af6ab5fSopenharmony_ci undefined, 9953af6ab5fSopenharmony_ci members 9963af6ab5fSopenharmony_ci ); 9973af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, newInterfaceDecl, srcFile) + '\n'; 9983af6ab5fSopenharmony_ci return { start: pos, end: pos, replacementText: text }; 9993af6ab5fSopenharmony_ci } 10003af6ab5fSopenharmony_ci 10013af6ab5fSopenharmony_ci private fixObjectLiteralExpression( 10023af6ab5fSopenharmony_ci srcFile: ts.SourceFile, 10033af6ab5fSopenharmony_ci newInterfaceName: string, 10043af6ab5fSopenharmony_ci objectLiteralExpr: ts.ObjectLiteralExpression 10053af6ab5fSopenharmony_ci ): Autofix { 10063af6ab5fSopenharmony_ci 10073af6ab5fSopenharmony_ci /* 10083af6ab5fSopenharmony_ci * If object literal is initializing a variable or property, 10093af6ab5fSopenharmony_ci * then simply add new 'contextual' type to the declaration. 10103af6ab5fSopenharmony_ci * Otherwise, cast object literal to newly created interface type. 10113af6ab5fSopenharmony_ci */ 10123af6ab5fSopenharmony_ci if ( 10133af6ab5fSopenharmony_ci (ts.isVariableDeclaration(objectLiteralExpr.parent) || 10143af6ab5fSopenharmony_ci ts.isPropertyDeclaration(objectLiteralExpr.parent) || 10153af6ab5fSopenharmony_ci ts.isParameter(objectLiteralExpr.parent)) && 10163af6ab5fSopenharmony_ci !objectLiteralExpr.parent.type 10173af6ab5fSopenharmony_ci ) { 10183af6ab5fSopenharmony_ci const text = ': ' + newInterfaceName; 10193af6ab5fSopenharmony_ci const pos = Autofixer.getDeclarationTypePositionForObjectLiteral(objectLiteralExpr.parent); 10203af6ab5fSopenharmony_ci return { start: pos, end: pos, replacementText: text }; 10213af6ab5fSopenharmony_ci } 10223af6ab5fSopenharmony_ci 10233af6ab5fSopenharmony_ci const newTypeRef = ts.factory.createTypeReferenceNode(newInterfaceName); 10243af6ab5fSopenharmony_ci let newExpr: ts.Expression = ts.factory.createAsExpression( 10253af6ab5fSopenharmony_ci ts.factory.createObjectLiteralExpression(objectLiteralExpr.properties), 10263af6ab5fSopenharmony_ci newTypeRef 10273af6ab5fSopenharmony_ci ); 10283af6ab5fSopenharmony_ci if (!ts.isParenthesizedExpression(objectLiteralExpr.parent)) { 10293af6ab5fSopenharmony_ci newExpr = ts.factory.createParenthesizedExpression(newExpr); 10303af6ab5fSopenharmony_ci } 10313af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, newExpr, srcFile); 10323af6ab5fSopenharmony_ci return { start: objectLiteralExpr.getStart(), end: objectLiteralExpr.getEnd(), replacementText: text }; 10333af6ab5fSopenharmony_ci } 10343af6ab5fSopenharmony_ci 10353af6ab5fSopenharmony_ci private static getDeclarationTypePositionForObjectLiteral( 10363af6ab5fSopenharmony_ci decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration 10373af6ab5fSopenharmony_ci ): number { 10383af6ab5fSopenharmony_ci if (ts.isPropertyDeclaration(decl)) { 10393af6ab5fSopenharmony_ci return (decl.questionToken || decl.exclamationToken || decl.name).getEnd(); 10403af6ab5fSopenharmony_ci } else if (ts.isParameter(decl)) { 10413af6ab5fSopenharmony_ci return (decl.questionToken || decl.name).getEnd(); 10423af6ab5fSopenharmony_ci } 10433af6ab5fSopenharmony_ci return (decl.exclamationToken || decl.name).getEnd(); 10443af6ab5fSopenharmony_ci } 10453af6ab5fSopenharmony_ci 10463af6ab5fSopenharmony_ci private readonly objectLiteralInterfaceNameGenerator = new NameGenerator( 10473af6ab5fSopenharmony_ci GENERATED_OBJECT_LITERAL_INTERFACE_NAME, 10483af6ab5fSopenharmony_ci GENERATED_OBJECT_LITERAL_INTERFACE_TRESHOLD 10493af6ab5fSopenharmony_ci ); 10503af6ab5fSopenharmony_ci 10513af6ab5fSopenharmony_ci /* 10523af6ab5fSopenharmony_ci * In case of type alias initialized with type literal, replace 10533af6ab5fSopenharmony_ci * entire type alias with identical interface declaration. 10543af6ab5fSopenharmony_ci */ 10553af6ab5fSopenharmony_ci private proceedTypeAliasDeclaration(typeLiteral: ts.TypeLiteralNode): Autofix[] | undefined { 10563af6ab5fSopenharmony_ci if (ts.isTypeAliasDeclaration(typeLiteral.parent)) { 10573af6ab5fSopenharmony_ci const typeAlias = typeLiteral.parent; 10583af6ab5fSopenharmony_ci const newInterfaceDecl = ts.factory.createInterfaceDeclaration( 10593af6ab5fSopenharmony_ci typeAlias.modifiers, 10603af6ab5fSopenharmony_ci typeAlias.name, 10613af6ab5fSopenharmony_ci typeAlias.typeParameters, 10623af6ab5fSopenharmony_ci undefined, 10633af6ab5fSopenharmony_ci typeLiteral.members 10643af6ab5fSopenharmony_ci ); 10653af6ab5fSopenharmony_ci const text = this.printer.printNode(ts.EmitHint.Unspecified, newInterfaceDecl, typeLiteral.getSourceFile()); 10663af6ab5fSopenharmony_ci return [{ start: typeAlias.getStart(), end: typeAlias.getEnd(), replacementText: text }]; 10673af6ab5fSopenharmony_ci } 10683af6ab5fSopenharmony_ci return undefined; 10693af6ab5fSopenharmony_ci } 10703af6ab5fSopenharmony_ci 10713af6ab5fSopenharmony_ci fixTypeliteral(typeLiteral: ts.TypeLiteralNode): Autofix[] | undefined { 10723af6ab5fSopenharmony_ci const typeAliasAutofix = this.proceedTypeAliasDeclaration(typeLiteral); 10733af6ab5fSopenharmony_ci if (typeAliasAutofix) { 10743af6ab5fSopenharmony_ci return typeAliasAutofix; 10753af6ab5fSopenharmony_ci } 10763af6ab5fSopenharmony_ci 10773af6ab5fSopenharmony_ci /* 10783af6ab5fSopenharmony_ci * Create new interface declaration with members of type literal 10793af6ab5fSopenharmony_ci * and put the interface name in place of the type literal. 10803af6ab5fSopenharmony_ci */ 10813af6ab5fSopenharmony_ci const srcFile = typeLiteral.getSourceFile(); 10823af6ab5fSopenharmony_ci const enclosingStmt = TsUtils.getEnclosingTopLevelStatement(typeLiteral); 10833af6ab5fSopenharmony_ci if (!enclosingStmt) { 10843af6ab5fSopenharmony_ci return undefined; 10853af6ab5fSopenharmony_ci } 10863af6ab5fSopenharmony_ci 10873af6ab5fSopenharmony_ci if (this.typeLiteralCapturesTypeFromEnclosingLocalScope(typeLiteral, enclosingStmt)) { 10883af6ab5fSopenharmony_ci return undefined; 10893af6ab5fSopenharmony_ci } 10903af6ab5fSopenharmony_ci 10913af6ab5fSopenharmony_ci const newInterfaceName = TsUtils.generateUniqueName(this.typeLiteralInterfaceNameGenerator, srcFile); 10923af6ab5fSopenharmony_ci if (!newInterfaceName) { 10933af6ab5fSopenharmony_ci return undefined; 10943af6ab5fSopenharmony_ci } 10953af6ab5fSopenharmony_ci const newInterfacePos = enclosingStmt.getStart(); 10963af6ab5fSopenharmony_ci const newInterfaceDecl = ts.factory.createInterfaceDeclaration( 10973af6ab5fSopenharmony_ci undefined, 10983af6ab5fSopenharmony_ci newInterfaceName, 10993af6ab5fSopenharmony_ci undefined, 11003af6ab5fSopenharmony_ci undefined, 11013af6ab5fSopenharmony_ci typeLiteral.members 11023af6ab5fSopenharmony_ci ); 11033af6ab5fSopenharmony_ci const interfaceText = this.printer.printNode(ts.EmitHint.Unspecified, newInterfaceDecl, srcFile) + '\n'; 11043af6ab5fSopenharmony_ci 11053af6ab5fSopenharmony_ci return [ 11063af6ab5fSopenharmony_ci { start: newInterfacePos, end: newInterfacePos, replacementText: interfaceText }, 11073af6ab5fSopenharmony_ci { start: typeLiteral.getStart(), end: typeLiteral.getEnd(), replacementText: newInterfaceName } 11083af6ab5fSopenharmony_ci ]; 11093af6ab5fSopenharmony_ci } 11103af6ab5fSopenharmony_ci 11113af6ab5fSopenharmony_ci typeLiteralCapturesTypeFromEnclosingLocalScope(typeLiteral: ts.TypeLiteralNode, enclosingStmt: ts.Node): boolean { 11123af6ab5fSopenharmony_ci let found = false; 11133af6ab5fSopenharmony_ci 11143af6ab5fSopenharmony_ci const callback = (node: ts.Node): void => { 11153af6ab5fSopenharmony_ci if (!ts.isIdentifier(node)) { 11163af6ab5fSopenharmony_ci return; 11173af6ab5fSopenharmony_ci } 11183af6ab5fSopenharmony_ci const sym = this.typeChecker.getSymbolAtLocation(node); 11193af6ab5fSopenharmony_ci let symNode: ts.Node | undefined = TsUtils.getDeclaration(sym); 11203af6ab5fSopenharmony_ci while (symNode) { 11213af6ab5fSopenharmony_ci if (symNode === typeLiteral) { 11223af6ab5fSopenharmony_ci return; 11233af6ab5fSopenharmony_ci } 11243af6ab5fSopenharmony_ci if (symNode === enclosingStmt) { 11253af6ab5fSopenharmony_ci found = true; 11263af6ab5fSopenharmony_ci return; 11273af6ab5fSopenharmony_ci } 11283af6ab5fSopenharmony_ci symNode = symNode.parent; 11293af6ab5fSopenharmony_ci } 11303af6ab5fSopenharmony_ci }; 11313af6ab5fSopenharmony_ci 11323af6ab5fSopenharmony_ci const stopCondition = (node: ts.Node): boolean => { 11333af6ab5fSopenharmony_ci void node; 11343af6ab5fSopenharmony_ci return found; 11353af6ab5fSopenharmony_ci }; 11363af6ab5fSopenharmony_ci 11373af6ab5fSopenharmony_ci forEachNodeInSubtree(typeLiteral, callback, stopCondition); 11383af6ab5fSopenharmony_ci return found; 11393af6ab5fSopenharmony_ci } 11403af6ab5fSopenharmony_ci 11413af6ab5fSopenharmony_ci // eslint-disable-next-line class-methods-use-this 11423af6ab5fSopenharmony_ci removeDecorator(decorator: ts.Decorator): Autofix[] { 11433af6ab5fSopenharmony_ci return [{ start: decorator.getStart(), end: decorator.getEnd(), replacementText: '' }]; 11443af6ab5fSopenharmony_ci } 11453af6ab5fSopenharmony_ci 11463af6ab5fSopenharmony_ci private readonly typeLiteralInterfaceNameGenerator = new NameGenerator( 11473af6ab5fSopenharmony_ci GENERATED_TYPE_LITERAL_INTERFACE_NAME, 11483af6ab5fSopenharmony_ci GENERATED_TYPE_LITERAL_INTERFACE_TRESHOLD 11493af6ab5fSopenharmony_ci ); 11503af6ab5fSopenharmony_ci 11513af6ab5fSopenharmony_ci private readonly symbolCache: SymbolCache; 11523af6ab5fSopenharmony_ci} 1153