1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import type {ClassElement, Expression, Identifier, Node, ObjectBindingPattern, SourceFile, StructDeclaration} from 'typescript'; 17import { 18 SyntaxKind, 19 factory, 20 getModifiers, 21 isBinaryExpression, 22 isBindingElement, 23 isCallExpression, 24 isClassDeclaration, 25 isClassExpression, 26 isComputedPropertyName, 27 isConstructorDeclaration, 28 isElementAccessExpression, 29 isEnumMember, 30 isGetAccessor, 31 isIdentifier, 32 isMetaProperty, 33 isMethodDeclaration, 34 isMethodSignature, 35 isParameter, 36 isPrivateIdentifier, 37 isPropertyAccessExpression, 38 isPropertyAssignment, 39 isPropertyDeclaration, 40 isPropertySignature, 41 isQualifiedName, 42 isSetAccessor, 43 isVariableDeclaration 44} from 'typescript'; 45import { isParameterPropertyModifier } from './OhsUtil'; 46 47export class NodeUtils { 48 public static isPropertyDeclarationNode(node: Node): boolean { 49 let parent: Node | undefined = node.parent; 50 if (!parent) { 51 return false; 52 } 53 54 /** eg: { 'name'' : 'akira' }, pass */ 55 if (isPropertyAssignment(parent)) { 56 return parent.name === node; 57 } 58 59 if (isComputedPropertyName(parent) && parent.expression === node) { 60 return true; 61 } 62 63 /** object binding pattern */ 64 if (isBindingElement(parent) && parent.propertyName === node) { 65 return true; 66 } 67 68 /** eg: interface/type inf { 'name' : string}, pass */ 69 if (isPropertySignature(parent) && parent.name === node) { 70 return true; 71 } 72 73 /** eg: interface/type T1 { func(arg: string): number;} */ 74 if (isMethodSignature(parent) && parent.name === node) { 75 return true; 76 } 77 78 /** eg: enum { xxx = 1}; */ 79 if (isEnumMember(parent) && parent.name === node) { 80 return true; 81 } 82 83 /** class { private name= 1}; */ 84 if (isPropertyDeclaration(parent) && parent.name === node) { 85 return true; 86 } 87 88 /** class {'getName': function() {}} let _ = { getName() [}} */ 89 if (isMethodDeclaration(parent) && parent.name === node) { 90 return true; 91 } 92 93 if (isSetAccessor(parent) && parent.name === node) { 94 return true; 95 } 96 97 return isGetAccessor(parent) && parent.name === node; 98 } 99 100 public static isPropertyOrElementAccessNode(node: Node): boolean { 101 return this.isPropertyAccessNode(node) || this.isElementAccessNode(node) || false; 102 } 103 104 public static isPropertyAccessNode(node: Node): boolean { 105 let parent: Node | undefined = node.parent; 106 if (!parent) { 107 return false; 108 } 109 110 /** eg: a.b = 1 */ 111 if (isPropertyAccessExpression(parent) && parent.name === node) { 112 return true; 113 } 114 if (isPrivateIdentifier(node) && NodeUtils.isInClassDeclaration(parent)) { 115 return NodeUtils.isInExpression(parent); 116 } 117 return isQualifiedName(parent) && parent.right === node; 118 } 119 120 private static isInClassDeclaration(node: Node | undefined): boolean { 121 if (!node) { 122 return false; 123 } 124 125 if (isClassDeclaration(node) || isClassExpression(node)) { 126 return true; 127 } 128 129 return NodeUtils.isInClassDeclaration(node.parent); 130 } 131 132 private static isInExpression(node: Node | undefined): boolean { 133 return !!node && NodeUtils.isInOperator(node); 134 } 135 136 private static isInOperator(node: Node): boolean { 137 return isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.InKeyword; 138 } 139 140 public static isElementAccessNode(node: Node): boolean { 141 let parent: Node | undefined = node.parent; 142 if (!parent) { 143 return false; 144 } 145 146 return isElementAccessExpression(parent) && parent.argumentExpression === node; 147 } 148 149 public static isClassPropertyInConstructorParams(node: Node): boolean { 150 if (!isIdentifier(node)) { 151 return false; 152 } 153 154 if (!node.parent || !isParameter(node.parent)) { 155 return false; 156 } 157 158 const modifiers = getModifiers(node.parent); 159 if (!modifiers || modifiers.length === 0 || !modifiers.find(modifier => isParameterPropertyModifier(modifier))) { 160 return false; 161 } 162 163 return node.parent.parent && isConstructorDeclaration(node.parent.parent); 164 } 165 166 public static isClassPropertyInConstructorBody(node: Node, constructorParams: Set<string>): boolean { 167 if (!isIdentifier(node)) { 168 return false; 169 } 170 171 const id: string = node.escapedText.toString(); 172 let curNode: Node = node.parent; 173 while (curNode) { 174 if (isConstructorDeclaration(curNode) && constructorParams.has(id)) { 175 return true; 176 } 177 178 curNode = curNode.parent; 179 } 180 181 return false; 182 } 183 184 public static isPropertyNode(node: Node): boolean { 185 if (this.isPropertyOrElementAccessNode(node)) { 186 return true; 187 } 188 189 return this.isPropertyDeclarationNode(node); 190 } 191 192 public static isObjectBindingPatternAssignment(node: ObjectBindingPattern): boolean { 193 if (!node || !node.parent || !isVariableDeclaration(node.parent)) { 194 return false; 195 } 196 197 const initializer: Expression = node.parent.initializer; 198 return initializer && isCallExpression(initializer); 199 } 200 201 public static isDeclarationFile(node: SourceFile): boolean { 202 return node.isDeclarationFile; 203 } 204 205 public static getSourceFileOfNode(node: Node): SourceFile { 206 while (node && node.kind !== SyntaxKind.SourceFile) { 207 node = node.parent; 208 } 209 return <SourceFile>node; 210 } 211 212 public static isInETSFile(node: Node | undefined): boolean { 213 return !!node && NodeUtils.getSourceFileOfNode(node).fileName.endsWith('.ets'); 214 } 215 216 public static isNewTargetNode(node: Identifier): boolean { 217 if (isMetaProperty(node.parent) && node.parent.keywordToken === SyntaxKind.NewKeyword && node.escapedText === 'target') { 218 return true; 219 } 220 return false; 221 } 222} 223