• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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