• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2025 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 * as path from 'node:path';
17import * as ts from 'typescript';
18import * as fs from 'fs';
19import type { IsEtsFileCallback } from '../IsEtsFileCallback';
20import { FaultID } from '../Problems';
21import { ARKTS_IGNORE_DIRS, ARKTS_IGNORE_DIRS_OH_MODULES, ARKTS_IGNORE_FILES } from './consts/ArktsIgnorePaths';
22import { ES_VALUE } from './consts/ESObject';
23import { EXTENDED_BASE_TYPES } from './consts/ExtendedBaseTypes';
24import { SENDABLE_DECORATOR } from './consts/SendableAPI';
25import { USE_SHARED } from './consts/SharedModuleAPI';
26import { STANDARD_LIBRARIES } from './consts/StandardLibraries';
27import {
28  ARKTS_COLLECTIONS_D_ETS,
29  ARKTS_LANG_D_ETS,
30  COLLECTIONS_NAMESPACE,
31  ISENDABLE_TYPE,
32  LANG_NAMESPACE
33} from './consts/SupportedDetsIndexableTypes';
34import { TYPED_ARRAYS } from './consts/TypedArrays';
35import { TYPED_COLLECTIONS } from './consts/TypedCollections';
36import { forEachNodeInSubtree } from './functions/ForEachNodeInSubtree';
37import { getScriptKind } from './functions/GetScriptKind';
38import { isStdLibrarySymbol, isStdLibraryType } from './functions/IsStdLibrary';
39import { isStructDeclaration, isStructDeclarationKind } from './functions/IsStruct';
40import type { NameGenerator } from './functions/NameGenerator';
41import { srcFilePathContainsDirectory } from './functions/PathHelper';
42import { isAssignmentOperator } from './functions/isAssignmentOperator';
43import { isIntrinsicObjectType } from './functions/isIntrinsicObjectType';
44import type { LinterOptions } from '../LinterOptions';
45import { ETS } from './consts/TsSuffix';
46import { STRINGLITERAL_NUMBER, STRINGLITERAL_NUMBER_ARRAY } from './consts/StringLiteral';
47import { ETS_MODULE, PATH_SEPARATOR, VALID_OHM_COMPONENTS_MODULE_PATH } from './consts/OhmUrl';
48import { EXTNAME_ETS, EXTNAME_JS, EXTNAME_D_ETS } from './consts/ExtensionName';
49import { STRING_ERROR_LITERAL } from './consts/Literals';
50
51export const PROMISE_METHODS = new Set(['all', 'race', 'any', 'resolve', 'allSettled']);
52export const SYMBOL = 'Symbol';
53export const SYMBOL_CONSTRUCTOR = 'SymbolConstructor';
54const ITERATOR = 'iterator';
55
56export type CheckType = (this: TsUtils, t: ts.Type) => boolean;
57export class TsUtils {
58  constructor(
59    private readonly tsTypeChecker: ts.TypeChecker,
60    private readonly options: LinterOptions
61  ) {}
62
63  entityNameToString(name: ts.EntityName): string {
64    if (ts.isIdentifier(name)) {
65      return name.escapedText.toString();
66    }
67    return this.entityNameToString(name.left) + this.entityNameToString(name.right);
68  }
69
70  isNumberLikeType(tsType: ts.Type): boolean {
71    if (this.options.useRtLogic && tsType.isUnion()) {
72      for (const tsCompType of tsType.types) {
73        if ((tsCompType.flags & ts.TypeFlags.NumberLike) === 0) {
74          return false;
75        }
76      }
77      return true;
78    }
79    return (tsType.getFlags() & ts.TypeFlags.NumberLike) !== 0;
80  }
81
82  static isBooleanLikeType(tsType: ts.Type): boolean {
83    return (tsType.getFlags() & ts.TypeFlags.BooleanLike) !== 0;
84  }
85
86  static isDestructuringAssignmentLHS(tsExpr: ts.ArrayLiteralExpression | ts.ObjectLiteralExpression): boolean {
87
88    /*
89     * Check whether given expression is the LHS part of the destructuring
90     * assignment (or is a nested element of destructuring pattern).
91     */
92    let tsParent = tsExpr.parent;
93    let tsCurrentExpr: ts.Node = tsExpr;
94    while (tsParent) {
95      if (
96        ts.isBinaryExpression(tsParent) &&
97        isAssignmentOperator(tsParent.operatorToken) &&
98        tsParent.left === tsCurrentExpr
99      ) {
100        return true;
101      }
102
103      if (
104        (ts.isForStatement(tsParent) || ts.isForInStatement(tsParent) || ts.isForOfStatement(tsParent)) &&
105        tsParent.initializer &&
106        tsParent.initializer === tsCurrentExpr
107      ) {
108        return true;
109      }
110
111      tsCurrentExpr = tsParent;
112      tsParent = tsParent.parent;
113    }
114
115    return false;
116  }
117
118  static isEnumType(tsType: ts.Type): boolean {
119    // when type equals `typeof <Enum>`, only symbol contains information about it's type.
120    const isEnumSymbol = tsType.symbol && this.isEnum(tsType.symbol);
121    // otherwise, we should analyze flags of the type itself
122    const isEnumType = !!(tsType.flags & ts.TypeFlags.Enum) || !!(tsType.flags & ts.TypeFlags.EnumLiteral);
123    return isEnumSymbol || isEnumType;
124  }
125
126  static isEnum(tsSymbol: ts.Symbol): boolean {
127    return !!(tsSymbol.flags & ts.SymbolFlags.Enum);
128  }
129
130  static hasModifier(
131    tsModifiers: readonly ts.Modifier[] | ts.NodeArray<ts.ModifierLike> | undefined,
132    tsModifierKind: number
133  ): boolean {
134    if (!tsModifiers) {
135      return false;
136    }
137
138    for (const tsModifier of tsModifiers) {
139      if (tsModifier.kind === tsModifierKind) {
140        return true;
141      }
142    }
143
144    return false;
145  }
146
147  static unwrapParenthesized(tsExpr: ts.Expression): ts.Expression {
148    let unwrappedExpr = tsExpr;
149    while (ts.isParenthesizedExpression(unwrappedExpr)) {
150      unwrappedExpr = unwrappedExpr.expression;
151    }
152
153    return unwrappedExpr;
154  }
155
156  followIfAliased(sym: ts.Symbol): ts.Symbol {
157    if ((sym.getFlags() & ts.SymbolFlags.Alias) !== 0) {
158      return this.tsTypeChecker.getAliasedSymbol(sym);
159    }
160    return sym;
161  }
162
163  private readonly trueSymbolAtLocationCache = new Map<ts.Node, ts.Symbol | null>();
164
165  trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined {
166    const cache = this.trueSymbolAtLocationCache;
167    const val = cache.get(node);
168    if (val !== undefined) {
169      return val !== null ? val : undefined;
170    }
171    let sym = this.tsTypeChecker.getSymbolAtLocation(node);
172    if (sym === undefined) {
173      cache.set(node, null);
174      return undefined;
175    }
176    sym = this.followIfAliased(sym);
177    cache.set(node, sym);
178    return sym;
179  }
180
181  private static isTypeDeclSyntaxKind(kind: ts.SyntaxKind): boolean {
182    return (
183      isStructDeclarationKind(kind) ||
184      kind === ts.SyntaxKind.EnumDeclaration ||
185      kind === ts.SyntaxKind.ClassDeclaration ||
186      kind === ts.SyntaxKind.InterfaceDeclaration ||
187      kind === ts.SyntaxKind.TypeAliasDeclaration
188    );
189  }
190
191  static symbolHasDuplicateName(symbol: ts.Symbol, tsDeclKind: ts.SyntaxKind): boolean {
192
193    /*
194     * Type Checker merges all declarations with the same name in one scope into one symbol.
195     * Thus, check whether the symbol of certain declaration has any declaration with
196     * different syntax kind.
197     */
198    const symbolDecls = symbol?.getDeclarations();
199    if (symbolDecls) {
200      for (const symDecl of symbolDecls) {
201        const declKind = symDecl.kind;
202        // we relax arkts-unique-names for namespace collision with class/interface/enum/type/struct
203        const isNamespaceTypeCollision =
204          TsUtils.isTypeDeclSyntaxKind(declKind) && tsDeclKind === ts.SyntaxKind.ModuleDeclaration ||
205          TsUtils.isTypeDeclSyntaxKind(tsDeclKind) && declKind === ts.SyntaxKind.ModuleDeclaration;
206
207        /*
208         * Don't count declarations with 'Identifier' syntax kind as those
209         * usually depict declaring an object's property through assignment.
210         */
211        if (declKind !== ts.SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) {
212          return true;
213        }
214      }
215    }
216
217    return false;
218  }
219
220  static isPrimitiveType(type: ts.Type): boolean {
221    const f = type.getFlags();
222    return (
223      (f & ts.TypeFlags.Boolean) !== 0 ||
224      (f & ts.TypeFlags.BooleanLiteral) !== 0 ||
225      (f & ts.TypeFlags.Number) !== 0 ||
226      (f & ts.TypeFlags.NumberLiteral) !== 0
227
228    /*
229     *  In ArkTS 'string' is not a primitive type. So for the common subset 'string'
230     *  should be considered as a reference type. That is why next line is commented out.
231     * (f & ts.TypeFlags.String) != 0 || (f & ts.TypeFlags.StringLiteral) != 0
232     */
233    );
234  }
235
236  checkStatementForErrorClass(stmt: ts.ThrowStatement): boolean {
237    const newExpr = stmt.expression;
238    if (!ts.isNewExpression(newExpr)) {
239      return true;
240    }
241    const ident = newExpr.expression;
242    if (!ts.isIdentifier(ident)) {
243      return true;
244    }
245
246    if (ident.text === STRING_ERROR_LITERAL) {
247      return false;
248    }
249
250    const declaration = this.getDeclarationNode(ident);
251    if (!declaration || ident.text.includes(STRING_ERROR_LITERAL)) {
252      return false;
253    }
254
255    if (!declaration || !ident.text.includes(STRING_ERROR_LITERAL)) {
256      return true;
257    }
258
259    if (!ts.isClassDeclaration(declaration)) {
260      return true;
261    }
262
263    if (!declaration.heritageClauses) {
264      return true;
265    }
266
267    return !this.includesErrorClass(declaration.heritageClauses);
268  }
269
270  includesErrorClass(hClauses: ts.NodeArray<ts.HeritageClause>): boolean {
271    void this;
272    let includesErrorClass = false;
273
274    for (const hClause of hClauses) {
275      for (const type of hClause.types) {
276        if (!ts.isIdentifier(type.expression)) {
277          continue;
278        }
279        if (type.expression.text === 'Error') {
280          includesErrorClass = true;
281        }
282      }
283    }
284
285    return includesErrorClass;
286  }
287
288  static isPrimitiveLiteralType(type: ts.Type): boolean {
289    return !!(
290      type.flags &
291      (ts.TypeFlags.BooleanLiteral |
292        ts.TypeFlags.NumberLiteral |
293        ts.TypeFlags.StringLiteral |
294        ts.TypeFlags.BigIntLiteral)
295    );
296  }
297
298  static isPurePrimitiveLiteralType(type: ts.Type): boolean {
299    return TsUtils.isPrimitiveLiteralType(type) && !(type.flags & ts.TypeFlags.EnumLiteral);
300  }
301
302  static isTypeSymbol(symbol: ts.Symbol | undefined): boolean {
303    return (
304      !!symbol &&
305      !!symbol.flags &&
306      ((symbol.flags & ts.SymbolFlags.Class) !== 0 || (symbol.flags & ts.SymbolFlags.Interface) !== 0)
307    );
308  }
309
310  // Check whether type is generic 'Array<T>' type defined in TypeScript standard library.
311  isGenericArrayType(tsType: ts.Type): tsType is ts.TypeReference {
312    return (
313      !(this.options.arkts2 && !isStdLibraryType(tsType)) &&
314      TsUtils.isTypeReference(tsType) &&
315      tsType.typeArguments?.length === 1 &&
316      tsType.target.typeParameters?.length === 1 &&
317      tsType.getSymbol()?.getName() === 'Array'
318    );
319  }
320
321  isReadonlyArrayType(tsType: ts.Type): boolean {
322    return (
323      !(this.options.arkts2 && !isStdLibraryType(tsType)) &&
324      TsUtils.isTypeReference(tsType) &&
325      tsType.typeArguments?.length === 1 &&
326      tsType.target.typeParameters?.length === 1 &&
327      tsType.getSymbol()?.getName() === 'ReadonlyArray'
328    );
329  }
330
331  static isConcatArrayType(tsType: ts.Type): boolean {
332    return (
333      isStdLibraryType(tsType) &&
334      TsUtils.isTypeReference(tsType) &&
335      tsType.typeArguments?.length === 1 &&
336      tsType.target.typeParameters?.length === 1 &&
337      tsType.getSymbol()?.getName() === 'ConcatArray'
338    );
339  }
340
341  static isArrayLikeType(tsType: ts.Type): boolean {
342    return (
343      isStdLibraryType(tsType) &&
344      TsUtils.isTypeReference(tsType) &&
345      tsType.typeArguments?.length === 1 &&
346      tsType.target.typeParameters?.length === 1 &&
347      tsType.getSymbol()?.getName() === 'ArrayLike'
348    );
349  }
350
351  isTypedArray(tsType: ts.Type, allowTypeArrays: string[]): boolean {
352    const symbol = tsType.symbol;
353    if (!symbol) {
354      return false;
355    }
356    const name = this.tsTypeChecker.getFullyQualifiedName(symbol);
357    if (this.isGlobalSymbol(symbol) && allowTypeArrays.includes(name)) {
358      return true;
359    }
360    const decl = TsUtils.getDeclaration(symbol);
361    return (
362      !!decl &&
363      TsUtils.isArkTSCollectionsClassOrInterfaceDeclaration(decl) &&
364      allowTypeArrays.includes(symbol.getName())
365    );
366  }
367
368  isArray(tsType: ts.Type): boolean {
369    return (
370      this.isGenericArrayType(tsType) || this.isReadonlyArrayType(tsType) || this.isTypedArray(tsType, TYPED_ARRAYS)
371    );
372  }
373
374  isCollectionArrayType(tsType: ts.Type): boolean {
375    return this.isTypedArray(tsType, TYPED_COLLECTIONS);
376  }
377
378  isIndexableArray(tsType: ts.Type): boolean {
379    return (
380      this.isGenericArrayType(tsType) ||
381      this.isReadonlyArrayType(tsType) ||
382      TsUtils.isConcatArrayType(tsType) ||
383      TsUtils.isArrayLikeType(tsType) ||
384      this.isTypedArray(tsType, TYPED_ARRAYS) ||
385      this.isTypedArray(tsType, TYPED_COLLECTIONS)
386    );
387  }
388
389  static isTuple(tsType: ts.Type): boolean {
390    return TsUtils.isTypeReference(tsType) && !!(tsType.objectFlags & ts.ObjectFlags.Tuple);
391  }
392
393  // does something similar to relatedByInheritanceOrIdentical function
394  isOrDerivedFrom(tsType: ts.Type, checkType: CheckType, checkedBaseTypes?: Set<ts.Type>): boolean {
395    // eslint-disable-next-line no-param-reassign
396    tsType = TsUtils.reduceReference(tsType);
397
398    if (checkType.call(this, tsType)) {
399      return true;
400    }
401
402    if (!tsType.symbol?.declarations) {
403      return false;
404    }
405
406    // Avoid type recursion in heritage by caching checked types.
407    (checkedBaseTypes = checkedBaseTypes || new Set<ts.Type>()).add(tsType);
408
409    for (const tsTypeDecl of tsType.symbol.declarations) {
410      const isClassOrInterfaceDecl = ts.isClassDeclaration(tsTypeDecl) || ts.isInterfaceDeclaration(tsTypeDecl);
411      const isDerived = isClassOrInterfaceDecl && !!tsTypeDecl.heritageClauses;
412      if (!isDerived) {
413        continue;
414      }
415      for (const heritageClause of tsTypeDecl.heritageClauses) {
416        if (this.processParentTypesCheck(heritageClause.types, checkType, checkedBaseTypes)) {
417          return true;
418        }
419      }
420    }
421
422    return false;
423  }
424
425  static isTypeReference(tsType: ts.Type): tsType is ts.TypeReference {
426    return (
427      (tsType.getFlags() & ts.TypeFlags.Object) !== 0 &&
428      ((tsType as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference) !== 0
429    );
430  }
431
432  static isPrototypeSymbol(symbol: ts.Symbol | undefined): boolean {
433    return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Prototype) !== 0;
434  }
435
436  static isFunctionSymbol(symbol: ts.Symbol | undefined): boolean {
437    return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Function) !== 0;
438  }
439
440  static isInterfaceType(tsType: ts.Type | undefined): boolean {
441    return (
442      !!tsType && !!tsType.symbol && !!tsType.symbol.flags && (tsType.symbol.flags & ts.SymbolFlags.Interface) !== 0
443    );
444  }
445
446  static isAnyType(tsType: ts.Type): tsType is ts.TypeReference {
447    return (tsType.getFlags() & ts.TypeFlags.Any) !== 0;
448  }
449
450  static isUnknownType(tsType: ts.Type): boolean {
451    return (tsType.getFlags() & ts.TypeFlags.Unknown) !== 0;
452  }
453
454  static isUnsupportedType(tsType: ts.Type): boolean {
455    return (
456      !!tsType.flags &&
457      ((tsType.flags & ts.TypeFlags.Any) !== 0 ||
458        (tsType.flags & ts.TypeFlags.Unknown) !== 0 ||
459        (tsType.flags & ts.TypeFlags.Intersection) !== 0)
460    );
461  }
462
463  isUnsupportedTypeArkts2(tsType: ts.Type): boolean {
464    const typenode = this.tsTypeChecker.typeToTypeNode(tsType, undefined, ts.NodeBuilderFlags.None);
465    return !!typenode && !this.isSupportedType(typenode);
466  }
467
468  static isNullableUnionType(type: ts.Type): boolean {
469    if (type.isUnion()) {
470      for (const t of type.types) {
471        if (!!(t.flags & ts.TypeFlags.Undefined) || !!(t.flags & ts.TypeFlags.Null)) {
472          return true;
473        }
474      }
475    }
476    return false;
477  }
478
479  static isMethodAssignment(tsSymbol: ts.Symbol | undefined): boolean {
480    return (
481      !!tsSymbol && (tsSymbol.flags & ts.SymbolFlags.Method) !== 0 && (tsSymbol.flags & ts.SymbolFlags.Assignment) !== 0
482    );
483  }
484
485  static getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined {
486    if (tsSymbol?.declarations && tsSymbol.declarations.length > 0) {
487      return tsSymbol.declarations[0];
488    }
489    return undefined;
490  }
491
492  private static isVarDeclaration(tsDecl: ts.Node): boolean {
493    return ts.isVariableDeclaration(tsDecl) && ts.isVariableDeclarationList(tsDecl.parent);
494  }
495
496  isValidEnumMemberInit(tsExpr: ts.Expression): boolean {
497    if (this.isNumberConstantValue(tsExpr.parent as ts.EnumMember)) {
498      return true;
499    }
500    if (this.isStringConstantValue(tsExpr.parent as ts.EnumMember)) {
501      return true;
502    }
503    return this.isCompileTimeExpression(tsExpr);
504  }
505
506  private isCompileTimeExpressionHandlePropertyAccess(tsExpr: ts.Expression): boolean {
507    if (!ts.isPropertyAccessExpression(tsExpr)) {
508      return false;
509    }
510
511    /*
512     * if enum member is in current enum declaration try to get value
513     * if it comes from another enum consider as constant
514     */
515    const propertyAccess = tsExpr;
516    if (this.isNumberConstantValue(propertyAccess)) {
517      return true;
518    }
519    const leftHandSymbol = this.trueSymbolAtLocation(propertyAccess.expression);
520    if (!leftHandSymbol) {
521      return false;
522    }
523    const decls = leftHandSymbol.getDeclarations();
524    if (!decls || decls.length !== 1) {
525      return false;
526    }
527    return ts.isEnumDeclaration(decls[0]);
528  }
529
530  isCompileTimeExpression(tsExpr: ts.Expression): boolean {
531    if (
532      ts.isParenthesizedExpression(tsExpr) ||
533      ts.isAsExpression(tsExpr) && tsExpr.type.kind === ts.SyntaxKind.NumberKeyword
534    ) {
535      return this.isCompileTimeExpression(tsExpr.expression);
536    }
537
538    switch (tsExpr.kind) {
539      case ts.SyntaxKind.PrefixUnaryExpression:
540        return this.isPrefixUnaryExprValidEnumMemberInit(tsExpr as ts.PrefixUnaryExpression);
541      case ts.SyntaxKind.ParenthesizedExpression:
542      case ts.SyntaxKind.BinaryExpression:
543        return this.isBinaryExprValidEnumMemberInit(tsExpr as ts.BinaryExpression);
544      case ts.SyntaxKind.ConditionalExpression:
545        return this.isConditionalExprValidEnumMemberInit(tsExpr as ts.ConditionalExpression);
546      case ts.SyntaxKind.Identifier:
547        return this.isIdentifierValidEnumMemberInit(tsExpr as ts.Identifier);
548      case ts.SyntaxKind.NumericLiteral:
549        return true;
550      case ts.SyntaxKind.StringLiteral:
551        return true;
552      case ts.SyntaxKind.PropertyAccessExpression:
553        return this.isCompileTimeExpressionHandlePropertyAccess(tsExpr);
554      default:
555        return false;
556    }
557  }
558
559  private isPrefixUnaryExprValidEnumMemberInit(tsExpr: ts.PrefixUnaryExpression): boolean {
560    return TsUtils.isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && this.isCompileTimeExpression(tsExpr.operand);
561  }
562
563  private isBinaryExprValidEnumMemberInit(tsExpr: ts.BinaryExpression): boolean {
564    return (
565      TsUtils.isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) &&
566      this.isCompileTimeExpression(tsExpr.left) &&
567      this.isCompileTimeExpression(tsExpr.right)
568    );
569  }
570
571  private isConditionalExprValidEnumMemberInit(tsExpr: ts.ConditionalExpression): boolean {
572    return this.isCompileTimeExpression(tsExpr.whenTrue) && this.isCompileTimeExpression(tsExpr.whenFalse);
573  }
574
575  private isIdentifierValidEnumMemberInit(tsExpr: ts.Identifier): boolean {
576    const tsSymbol = this.trueSymbolAtLocation(tsExpr);
577    const tsDecl = TsUtils.getDeclaration(tsSymbol);
578    return (
579      !!tsDecl &&
580      (TsUtils.isVarDeclaration(tsDecl) && TsUtils.isConst(tsDecl.parent) || tsDecl.kind === ts.SyntaxKind.EnumMember)
581    );
582  }
583
584  private static isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: ts.PrefixUnaryOperator): boolean {
585    return (
586      tsPrefixUnaryOp === ts.SyntaxKind.PlusToken ||
587      tsPrefixUnaryOp === ts.SyntaxKind.MinusToken ||
588      tsPrefixUnaryOp === ts.SyntaxKind.TildeToken
589    );
590  }
591
592  private static isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: ts.BinaryOperatorToken): boolean {
593    return (
594      tsBinaryOp.kind === ts.SyntaxKind.AsteriskToken ||
595      tsBinaryOp.kind === ts.SyntaxKind.SlashToken ||
596      tsBinaryOp.kind === ts.SyntaxKind.PercentToken ||
597      tsBinaryOp.kind === ts.SyntaxKind.MinusToken ||
598      tsBinaryOp.kind === ts.SyntaxKind.PlusToken ||
599      tsBinaryOp.kind === ts.SyntaxKind.LessThanLessThanToken ||
600      tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanToken ||
601      tsBinaryOp.kind === ts.SyntaxKind.BarBarToken ||
602      tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken ||
603      tsBinaryOp.kind === ts.SyntaxKind.AmpersandToken ||
604      tsBinaryOp.kind === ts.SyntaxKind.CaretToken ||
605      tsBinaryOp.kind === ts.SyntaxKind.BarToken ||
606      tsBinaryOp.kind === ts.SyntaxKind.AmpersandAmpersandToken
607    );
608  }
609
610  static isConst(tsNode: ts.Node): boolean {
611    return !!(ts.getCombinedNodeFlags(tsNode) & ts.NodeFlags.Const);
612  }
613
614  isNumberConstantValue(
615    tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.NumericLiteral
616  ): boolean {
617    const tsConstValue =
618      tsExpr.kind === ts.SyntaxKind.NumericLiteral ?
619        Number(tsExpr.getText()) :
620        this.tsTypeChecker.getConstantValue(tsExpr);
621
622    return tsConstValue !== undefined && typeof tsConstValue === 'number';
623  }
624
625  isIntegerConstantValue(
626    tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.NumericLiteral
627  ): boolean {
628    const tsConstValue =
629      tsExpr.kind === ts.SyntaxKind.NumericLiteral ?
630        Number(tsExpr.getText()) :
631        this.tsTypeChecker.getConstantValue(tsExpr);
632    return (
633      tsConstValue !== undefined &&
634      typeof tsConstValue === 'number' &&
635      tsConstValue.toFixed(0) === tsConstValue.toString()
636    );
637  }
638
639  isStringConstantValue(tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression): boolean {
640    const tsConstValue = this.tsTypeChecker.getConstantValue(tsExpr);
641    return tsConstValue !== undefined && typeof tsConstValue === 'string';
642  }
643
644  // Returns true if typeA is a subtype of typeB
645  relatedByInheritanceOrIdentical(typeA: ts.Type, typeB: ts.Type): boolean {
646    // eslint-disable-next-line no-param-reassign
647    typeA = TsUtils.reduceReference(typeA);
648    // eslint-disable-next-line no-param-reassign
649    typeB = TsUtils.reduceReference(typeB);
650
651    if (typeA === typeB || this.isObject(typeB)) {
652      return true;
653    }
654    if (!typeA.symbol?.declarations) {
655      return false;
656    }
657    const isBISendable = TsUtils.isISendableInterface(typeB);
658    for (const typeADecl of typeA.symbol.declarations) {
659      if (this.relatedByInheritanceOrIdenticalCheckParentTypes(typeA, typeB, typeADecl, isBISendable)) {
660        return true;
661      }
662    }
663    return false;
664  }
665
666  private relatedByInheritanceOrIdenticalCheckParentTypes(
667    typeA: ts.Type,
668    typeB: ts.Type,
669    typeADecl: ts.Declaration,
670    isBISendable = false
671  ): boolean {
672    if (isBISendable && ts.isClassDeclaration(typeADecl) && TsUtils.hasSendableDecorator(typeADecl)) {
673      return true;
674    }
675    if (!ts.isClassDeclaration(typeADecl) && !ts.isInterfaceDeclaration(typeADecl)) {
676      return false;
677    }
678    if (this.processExtendedParentTypes(typeA, typeB)) {
679      return true;
680    }
681    if (this.isStdIterableType(typeB) && this.hasSymbolIteratorMethod(typeA)) {
682      return true;
683    }
684    if (!typeADecl.heritageClauses) {
685      return false;
686    }
687    for (const heritageClause of typeADecl.heritageClauses) {
688      const processInterfaces = typeA.isClass() ? heritageClause.token !== ts.SyntaxKind.ExtendsKeyword : true;
689      if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) {
690        return true;
691      }
692    }
693    return false;
694  }
695
696  hasSymbolIteratorMethod(type: ts.Type): boolean {
697    const rhsTypeProps = this.tsTypeChecker.getPropertiesOfType(type);
698    return rhsTypeProps.some((prop) => {
699      const propDecl = TsUtils.getDeclaration(prop);
700      return (
701        propDecl &&
702        (ts.isMethodSignature(propDecl) || ts.isMethodDeclaration(propDecl)) &&
703        ts.isComputedPropertyName(propDecl.name) &&
704        this.isSymbolIteratorExpression(propDecl.name.expression)
705      );
706    });
707  }
708
709  isStdIterableType(type: ts.Type): boolean {
710    void this;
711    const sym = type.getSymbol();
712    return !!sym && sym.getName() === 'Iterable' && isStdLibrarySymbol(sym);
713  }
714
715  static reduceReference(t: ts.Type): ts.Type {
716    return TsUtils.isTypeReference(t) && t.target !== t ? t.target : t;
717  }
718
719  private needToDeduceStructuralIdentityHandleUnions(
720    lhsType: ts.Type,
721    rhsType: ts.Type,
722    rhsExpr: ts.Expression,
723    isStrict: boolean
724  ): boolean {
725    if (this.needToDeduceStructuralIdentityHandleUnionsIsStrict(lhsType, rhsType, rhsExpr, isStrict)) {
726      return true;
727    }
728    if (rhsType.isUnion()) {
729      // Each Class/Interface of the RHS union type must be compatible with LHS type.
730      for (const compType of rhsType.types) {
731        if (this.needToDeduceStructuralIdentity(lhsType, compType, rhsExpr, isStrict)) {
732          return true;
733        }
734      }
735      return false;
736    }
737    if (lhsType.isUnion() && TsUtils.isTypeReference(rhsType)) {
738      let needDeduce = false;
739      // RHS type needs to be compatible with at least one type of the LHS union.
740      for (const compType of lhsType.types) {
741        if (!TsUtils.isTypeReference(compType) && !TsUtils.isISendableInterface(compType)) {
742          continue;
743        }
744        if (this.needToDeduceStructuralIdentity(compType, rhsType, rhsExpr, isStrict)) {
745          needDeduce = true;
746        } else {
747          return false;
748        }
749      }
750      return needDeduce;
751    }
752    // should be unreachable
753    return false;
754  }
755
756  needToDeduceStructuralIdentityHandleUnionsIsStrict(
757    lhsType: ts.Type,
758    rhsType: ts.Type,
759    rhsExpr: ts.Expression,
760    isStrict: boolean
761  ): boolean {
762    if (rhsType.isUnion() && lhsType.isUnion()) {
763      return rhsType.types.some((compRhsType) => {
764        return lhsType.types.every((compLhsType) => {
765          return this.needToDeduceStructuralIdentity(compLhsType, compRhsType, rhsExpr, isStrict);
766        });
767      });
768    }
769    return false;
770  }
771
772  // return true if two class types are not related by inheritance and structural identity check is needed
773  needToDeduceStructuralIdentity(
774    lhsType: ts.Type,
775    rhsType: ts.Type,
776    rhsExpr: ts.Expression,
777    isStrict: boolean = false
778  ): boolean {
779    // eslint-disable-next-line no-param-reassign
780    lhsType = this.getNonNullableType(lhsType);
781    // eslint-disable-next-line no-param-reassign
782    rhsType = this.getNonNullableType(rhsType);
783    if (this.isLibraryType(lhsType)) {
784      return false;
785    }
786    if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) {
787      return false;
788    }
789    // #14569: Check for Function type.
790    if (this.areCompatibleFunctionals(lhsType, rhsType)) {
791      return false;
792    }
793    if (rhsType.isUnion() || lhsType.isUnion()) {
794      return this.needToDeduceStructuralIdentityHandleUnions(lhsType, rhsType, rhsExpr, isStrict);
795    }
796    if (this.needToDeduceStructuralIdentityAdvancedClassChecks(lhsType, rhsType)) {
797      // missing exact rule
798      return true;
799    }
800    // if isStrict, things like generics need to be checked
801    if (isStrict) {
802      if (TsUtils.isTypeReference(rhsType) && !!(rhsType.objectFlags & ts.ObjectFlags.ArrayLiteral)) {
803        // The 'arkts-sendable-obj-init' rule already exists. Wait for the new 'strict type' to be modified.
804        return false;
805      }
806      if (this.needToDeduceStructuralIdentityIsStrict(lhsType, rhsType, rhsExpr, isStrict)) {
807        return true;
808      }
809      // eslint-disable-next-line no-param-reassign
810      lhsType = TsUtils.reduceReference(lhsType);
811      // eslint-disable-next-line no-param-reassign
812      rhsType = TsUtils.reduceReference(rhsType);
813    }
814    return (
815      lhsType.isClassOrInterface() &&
816      rhsType.isClassOrInterface() &&
817      !this.relatedByInheritanceOrIdentical(rhsType, lhsType)
818    );
819  }
820
821  needToDeduceStructuralIdentityIsStrict(
822    lhsType: ts.Type,
823    rhsType: ts.Type,
824    rhsExpr: ts.Expression,
825    isStrict: boolean = false
826  ): boolean {
827    if (
828      TsUtils.reduceReference(lhsType) !== TsUtils.reduceReference(rhsType) ||
829      !TsUtils.isTypeReference(lhsType) ||
830      !TsUtils.isTypeReference(rhsType)
831    ) {
832      return false;
833    }
834    const lhsArgs = lhsType.typeArguments;
835    const rhsArgs = rhsType.typeArguments;
836    if (lhsArgs && lhsArgs.length > 0) {
837      if (rhsArgs && rhsArgs.length > 0) {
838        if (rhsArgs[0] === lhsArgs[0]) {
839          return false;
840        }
841        return this.needToDeduceStructuralIdentity(lhsArgs[0], rhsArgs[0], rhsExpr, isStrict);
842      }
843      return this.needToDeduceStructuralIdentity(lhsArgs[0], rhsType, rhsExpr, isStrict);
844    }
845    return false;
846  }
847
848  private needToDeduceStructuralIdentityAdvancedClassChecks(lhsType: ts.Type, rhsType: ts.Type): boolean {
849    return (
850      !!this.options.advancedClassChecks &&
851      TsUtils.isClassValueType(rhsType) &&
852      lhsType !== rhsType &&
853      !TsUtils.isObjectType(lhsType)
854    );
855  }
856
857  // Does the 'arkts-no-structural-typing' rule need to be strictly enforced to complete previously missed scenarios
858  needStrictMatchType(lhsType: ts.Type, rhsType: ts.Type): boolean {
859    if (this.options.arkts2) {
860      return true;
861    }
862    if (this.isStrictSendableMatch(lhsType, rhsType)) {
863      return true;
864    }
865    // add other requirements with strict type requirements here
866    return false;
867  }
868
869  // For compatibility, left must all ClassOrInterface is sendable, right must has non-sendable ClassorInterface
870  private isStrictSendableMatch(lhsType: ts.Type, rhsType: ts.Type): boolean {
871    let isStrictLhs = false;
872    if (lhsType.isUnion()) {
873      for (let compType of lhsType.types) {
874        compType = TsUtils.reduceReference(compType);
875        if (!compType.isClassOrInterface()) {
876          continue;
877        }
878        if (!this.isSendableClassOrInterface(compType)) {
879          return false;
880        }
881        isStrictLhs = true;
882      }
883    } else {
884      isStrictLhs = this.isSendableClassOrInterface(lhsType);
885    }
886    return isStrictLhs && this.typeContainsNonSendableClassOrInterface(rhsType);
887  }
888
889  private processExtendedParentTypes(typeA: ts.Type, typeB: ts.Type): boolean {
890
891    /*
892     * Most standard types in TS Stdlib do not use explicit inheritance and rely on
893     * structural compatibility. In contrast, the type definitions in ArkTS stdlib
894     * use inheritance with explicit base types. We check the inheritance hierarchy
895     * for such types according to how they are defined in ArkTS Stdlib.
896     */
897
898    if (!this.options.arkts2) {
899      return false;
900    }
901    if (!isStdLibrarySymbol(typeA.symbol) && !isStdLibrarySymbol(typeB.symbol)) {
902      return false;
903    }
904    return this.checkExtendedParentTypes(typeA.symbol.name, typeB.symbol.name);
905  }
906
907  private checkExtendedParentTypes(typeA: string, typeB: string): boolean {
908    if (typeA === typeB) {
909      return true;
910    }
911    const extBaseTypes = EXTENDED_BASE_TYPES.get(typeA);
912    if (!extBaseTypes) {
913      return false;
914    }
915    for (const extBaseType of extBaseTypes) {
916      if (this.checkExtendedParentTypes(extBaseType, typeB)) {
917        return true;
918      }
919    }
920    return false;
921  }
922
923  private processParentTypes(
924    parentTypes: ts.NodeArray<ts.Expression>,
925    typeB: ts.Type,
926    processInterfaces: boolean
927  ): boolean {
928    for (const baseTypeExpr of parentTypes) {
929      const baseType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(baseTypeExpr));
930      if (
931        baseType &&
932        baseType.isClass() !== processInterfaces &&
933        this.relatedByInheritanceOrIdentical(baseType, typeB)
934      ) {
935        return true;
936      }
937    }
938    return false;
939  }
940
941  private processParentTypesCheck(
942    parentTypes: ts.NodeArray<ts.Expression>,
943    checkType: CheckType,
944    checkedBaseTypes: Set<ts.Type>
945  ): boolean {
946    for (const baseTypeExpr of parentTypes) {
947      const baseType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(baseTypeExpr));
948      if (baseType && !checkedBaseTypes.has(baseType) && this.isOrDerivedFrom(baseType, checkType, checkedBaseTypes)) {
949        return true;
950      }
951    }
952    return false;
953  }
954
955  isObject(tsType: ts.Type): boolean {
956    if (!tsType) {
957      return false;
958    }
959    if (tsType.symbol && tsType.isClassOrInterface() && tsType.symbol.name === 'Object') {
960      return true;
961    }
962    const node = this.tsTypeChecker.typeToTypeNode(tsType, undefined, undefined);
963    return node !== undefined && node.kind === ts.SyntaxKind.ObjectKeyword;
964  }
965
966  isCallToFunctionWithOmittedReturnType(tsExpr: ts.Expression): boolean {
967    if (ts.isCallExpression(tsExpr)) {
968      const tsCallSignature = this.tsTypeChecker.getResolvedSignature(tsExpr);
969      if (tsCallSignature) {
970        const tsSignDecl = tsCallSignature.getDeclaration();
971        // `tsSignDecl` is undefined when `getResolvedSignature` returns `unknownSignature`
972        if (!tsSignDecl?.type) {
973          return true;
974        }
975      }
976    }
977
978    return false;
979  }
980
981  private static hasReadonlyFields(type: ts.Type): boolean {
982    // No members -> no readonly fields
983    if (type.symbol.members === undefined) {
984      return false;
985    }
986
987    let result: boolean = false;
988
989    type.symbol.members.forEach((value) => {
990      if (
991        value.declarations !== undefined &&
992        value.declarations.length > 0 &&
993        ts.isPropertyDeclaration(value.declarations[0])
994      ) {
995        const propmMods = ts.getModifiers(value.declarations[0]);
996        if (TsUtils.hasModifier(propmMods, ts.SyntaxKind.ReadonlyKeyword)) {
997          result = true;
998        }
999      }
1000    });
1001
1002    return result;
1003  }
1004
1005  private hasDefaultCtor(type: ts.Type): boolean {
1006    const checkBaseTypes = (type: ts.Type): boolean => {
1007      if (!this.options.arkts2) {
1008        return true;
1009      }
1010      const baseTypes = type.getBaseTypes()?.filter((baseType) => {
1011        return baseType.isClass();
1012      });
1013      if (!baseTypes || baseTypes.length === 0) {
1014        return true;
1015      }
1016      return baseTypes.some((baseType: ts.Type) => {
1017        return this.hasDefaultCtor(baseType);
1018      });
1019    };
1020
1021    // No members -> no explicit constructors -> there is default ctor
1022    if (type.symbol.members === undefined) {
1023      return checkBaseTypes(type);
1024    }
1025
1026    // has any constructor
1027    let hasCtor: boolean = false;
1028    // has default constructor
1029    let hasDefaultCtor: boolean = false;
1030
1031    type.symbol.members.forEach((value) => {
1032      if ((value.flags & ts.SymbolFlags.Constructor) === 0) {
1033        return;
1034      }
1035      hasCtor = true;
1036
1037      if (value.declarations === undefined || value.declarations.length <= 0) {
1038        return;
1039      }
1040
1041      const declCtor = value.declarations[0] as ts.ConstructorDeclaration;
1042      if (declCtor.parameters.length === 0) {
1043        hasDefaultCtor = true;
1044      }
1045    });
1046
1047    // Has no any explicit constructor -> has implicit default constructor.
1048    if (!hasCtor) {
1049      return checkBaseTypes(type);
1050    }
1051
1052    return hasDefaultCtor;
1053  }
1054
1055  private static isAbstractClass(type: ts.Type): boolean {
1056    if (type.isClass() && type.symbol.declarations && type.symbol.declarations.length > 0) {
1057      const declClass = type.symbol.declarations[0] as ts.ClassDeclaration;
1058      const classMods = ts.getModifiers(declClass);
1059      if (TsUtils.hasModifier(classMods, ts.SyntaxKind.AbstractKeyword)) {
1060        return true;
1061      }
1062    }
1063
1064    return false;
1065  }
1066
1067  isAbstractMethodInAbstractClass(node: ts.Node): boolean {
1068    const type = this.tsTypeChecker.getTypeAtLocation(node);
1069    const funcDeclParentType = this.tsTypeChecker.getTypeAtLocation(node.parent);
1070    if (
1071      TsUtils.isAbstractClass(funcDeclParentType) &&
1072      type.symbol?.declarations &&
1073      type.symbol.declarations.length > 0
1074    ) {
1075      const declClass = type.symbol.declarations[0] as ts.MethodDeclaration;
1076      const classMods = ts.getModifiers(declClass);
1077      if (TsUtils.hasModifier(classMods, ts.SyntaxKind.AbstractKeyword)) {
1078        return true;
1079      }
1080    }
1081    return false;
1082  }
1083
1084  validateObjectLiteralType(type: ts.Type | undefined): boolean {
1085    if (!type) {
1086      return false;
1087    }
1088    // eslint-disable-next-line no-param-reassign
1089    type = TsUtils.reduceReference(type);
1090    return (
1091      type.isClassOrInterface() &&
1092      this.hasDefaultCtor(type) &&
1093      !TsUtils.hasReadonlyFields(type) &&
1094      !TsUtils.isAbstractClass(type)
1095    );
1096  }
1097
1098  hasMethods(type: ts.Type): boolean {
1099    const properties = this.tsTypeChecker.getPropertiesOfType(type);
1100    if (properties?.length) {
1101      for (const prop of properties) {
1102        if (prop.getFlags() & ts.SymbolFlags.Method) {
1103          return true;
1104        }
1105      }
1106    }
1107    return false;
1108  }
1109
1110  findProperty(type: ts.Type, name: string): ts.Symbol | undefined {
1111    const properties = this.tsTypeChecker.getPropertiesOfType(type);
1112    if (properties?.length) {
1113      for (const prop of properties) {
1114        if (prop.name === name) {
1115          return prop;
1116        }
1117      }
1118    }
1119
1120    return undefined;
1121  }
1122
1123  checkTypeSet(typeSet: ts.Type, predicate: CheckType): boolean {
1124    if (!typeSet.isUnionOrIntersection()) {
1125      return predicate.call(this, typeSet);
1126    }
1127    for (const elemType of typeSet.types) {
1128      if (this.checkTypeSet(elemType, predicate)) {
1129        return true;
1130      }
1131    }
1132    return false;
1133  }
1134
1135  getNonNullableType(t: ts.Type): ts.Type {
1136    const isNullableUnionType = this.options.useRtLogic ? TsUtils.isNullableUnionType(t) : t.isUnion();
1137    if (isNullableUnionType) {
1138      return t.getNonNullableType();
1139    }
1140    return t;
1141  }
1142
1143  private isObjectLiteralAssignableToUnion(lhsType: ts.UnionType, rhsExpr: ts.ObjectLiteralExpression): boolean {
1144    for (const compType of lhsType.types) {
1145      if (this.isObjectLiteralAssignable(compType, rhsExpr)) {
1146        return true;
1147      }
1148    }
1149    return false;
1150  }
1151
1152  isObjectLiteralAssignable(lhsType: ts.Type | undefined, rhsExpr: ts.ObjectLiteralExpression): boolean {
1153    if (lhsType === undefined) {
1154      return false;
1155    }
1156    // Always check with the non-nullable variant of lhs type.
1157    // eslint-disable-next-line no-param-reassign
1158    lhsType = this.getNonNullableType(lhsType);
1159    if (lhsType.isUnion() && this.isObjectLiteralAssignableToUnion(lhsType, rhsExpr)) {
1160      return true;
1161    }
1162
1163    /*
1164     * Allow initializing with anything when the type
1165     * originates from the library.
1166     */
1167    if (TsUtils.isAnyType(lhsType) || this.isLibraryType(lhsType)) {
1168      return true;
1169    }
1170
1171    /*
1172     * issue 13412:
1173     * Allow initializing with a dynamic object when the LHS type
1174     * is primitive or defined in standard library.
1175     */
1176    if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) {
1177      return true;
1178    }
1179    // For Partial<T>, Required<T>, Readonly<T> types, validate their argument type.
1180    if (this.isStdPartialType(lhsType) || this.isStdRequiredType(lhsType) || this.isStdReadonlyType(lhsType)) {
1181      if (lhsType.aliasTypeArguments && lhsType.aliasTypeArguments.length === 1) {
1182        // eslint-disable-next-line no-param-reassign
1183        lhsType = lhsType.aliasTypeArguments[0];
1184      } else {
1185        return false;
1186      }
1187    }
1188
1189    /*
1190     * Allow initializing Record objects with object initializer.
1191     * Record supports any type for a its value, but the key value
1192     * must be either a string or number literal.
1193     */
1194    if (this.isStdRecordType(lhsType)) {
1195      return this.validateRecordObjectKeys(rhsExpr);
1196    }
1197    return (
1198      this.validateObjectLiteralType(lhsType) && !this.hasMethods(lhsType) && this.validateFields(lhsType, rhsExpr)
1199    );
1200  }
1201
1202  private isDynamicObjectAssignedToStdType(lhsType: ts.Type, rhsExpr: ts.Expression): boolean {
1203    if (isStdLibraryType(lhsType) || TsUtils.isPrimitiveType(lhsType)) {
1204      // eslint-disable-next-line no-nested-ternary
1205      const rhsSym = ts.isCallExpression(rhsExpr) ?
1206        this.getSymbolOfCallExpression(rhsExpr) :
1207        this.options.useRtLogic ?
1208          this.trueSymbolAtLocation(rhsExpr) :
1209          this.tsTypeChecker.getSymbolAtLocation(rhsExpr);
1210      if (rhsSym && this.isLibrarySymbol(rhsSym)) {
1211        return true;
1212      }
1213    }
1214    return false;
1215  }
1216
1217  validateFields(objectType: ts.Type, objectLiteral: ts.ObjectLiteralExpression): boolean {
1218    for (const prop of objectLiteral.properties) {
1219      if (ts.isPropertyAssignment(prop)) {
1220        if (!this.validateField(objectType, prop)) {
1221          return false;
1222        }
1223      }
1224    }
1225
1226    return true;
1227  }
1228
1229  getPropertySymbol(type: ts.Type, prop: ts.PropertyAssignment): ts.Symbol | undefined {
1230    const propNameSymbol = this.tsTypeChecker.getSymbolAtLocation(prop.name);
1231    // eslint-disable-next-line no-nested-ternary
1232    const propName = propNameSymbol ?
1233      ts.symbolName(propNameSymbol) :
1234      ts.isMemberName(prop.name) ?
1235        ts.idText(prop.name) :
1236        prop.name.getText();
1237    const propSym = this.findProperty(type, propName);
1238    return propSym;
1239  }
1240
1241  private validateField(type: ts.Type, prop: ts.PropertyAssignment): boolean {
1242    // Issue 15497: Use unescaped property name to find correpsponding property.
1243    const propSym = this.getPropertySymbol(type, prop);
1244    if (!propSym?.declarations?.length) {
1245      return false;
1246    }
1247
1248    const propType = this.tsTypeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]);
1249    const initExpr = TsUtils.unwrapParenthesized(prop.initializer);
1250    const rhsType = this.tsTypeChecker.getTypeAtLocation(initExpr);
1251    if (ts.isObjectLiteralExpression(initExpr)) {
1252      if (!this.isObjectLiteralAssignable(propType, initExpr)) {
1253        return false;
1254      }
1255    } else {
1256      // Only check for structural sub-typing.
1257      if (
1258        this.needToDeduceStructuralIdentity(propType, rhsType, initExpr, this.needStrictMatchType(propType, rhsType))
1259      ) {
1260        return false;
1261      }
1262      if (this.isWrongSendableFunctionAssignment(propType, rhsType)) {
1263        return false;
1264      }
1265    }
1266
1267    return true;
1268  }
1269
1270  validateRecordObjectKeys(objectLiteral: ts.ObjectLiteralExpression): boolean {
1271    for (const prop of objectLiteral.properties) {
1272      if (!prop.name || !this.isValidRecordObjectLiteralKey(prop.name)) {
1273        return false;
1274      }
1275    }
1276    return true;
1277  }
1278
1279  isValidRecordObjectLiteralKey(propName: ts.PropertyName): boolean {
1280    if (ts.isComputedPropertyName(propName)) {
1281      return this.isValidComputedPropertyName(propName, true);
1282    }
1283    return ts.isStringLiteral(propName) || ts.isNumericLiteral(propName);
1284  }
1285
1286  private static isSupportedTypeNodeKind(kind: ts.SyntaxKind): boolean {
1287    return (
1288      kind !== ts.SyntaxKind.AnyKeyword &&
1289      kind !== ts.SyntaxKind.UnknownKeyword &&
1290      kind !== ts.SyntaxKind.SymbolKeyword &&
1291      kind !== ts.SyntaxKind.IndexedAccessType &&
1292      kind !== ts.SyntaxKind.ConditionalType &&
1293      kind !== ts.SyntaxKind.MappedType &&
1294      kind !== ts.SyntaxKind.InferType
1295    );
1296  }
1297
1298  private isSupportedTypeHandleUnionTypeNode(typeNode: ts.UnionTypeNode): boolean {
1299    for (const unionTypeElem of typeNode.types) {
1300      if (!this.isSupportedType(unionTypeElem)) {
1301        return false;
1302      }
1303    }
1304    return true;
1305  }
1306
1307  private isSupportedTypeHandleTupleTypeNode(typeNode: ts.TupleTypeNode): boolean {
1308    for (const elem of typeNode.elements) {
1309      if (ts.isTypeNode(elem) && !this.isSupportedType(elem)) {
1310        return false;
1311      }
1312      if (ts.isNamedTupleMember(elem) && !this.isSupportedType(elem.type)) {
1313        return false;
1314      }
1315    }
1316    return true;
1317  }
1318
1319  isSupportedType(typeNode: ts.TypeNode): boolean {
1320    if (ts.isParenthesizedTypeNode(typeNode)) {
1321      return this.isSupportedType(typeNode.type);
1322    }
1323
1324    if (ts.isArrayTypeNode(typeNode)) {
1325      return this.isSupportedType(typeNode.elementType);
1326    }
1327
1328    if (ts.isTypeReferenceNode(typeNode) && typeNode.typeArguments) {
1329      for (const typeArg of typeNode.typeArguments) {
1330        if (!this.isSupportedType(typeArg)) {
1331          return false;
1332        }
1333      }
1334      return true;
1335    }
1336
1337    if (ts.isUnionTypeNode(typeNode)) {
1338      return this.isSupportedTypeHandleUnionTypeNode(typeNode);
1339    }
1340
1341    if (ts.isTupleTypeNode(typeNode)) {
1342      return this.isSupportedTypeHandleTupleTypeNode(typeNode);
1343    }
1344
1345    return (
1346      !ts.isTypeLiteralNode(typeNode) &&
1347      (this.options.advancedClassChecks || !ts.isTypeQueryNode(typeNode)) &&
1348      !ts.isIntersectionTypeNode(typeNode) &&
1349      TsUtils.isSupportedTypeNodeKind(typeNode.kind)
1350    );
1351  }
1352
1353  isStructObjectInitializer(objectLiteral: ts.ObjectLiteralExpression): boolean {
1354    if (ts.isCallLikeExpression(objectLiteral.parent)) {
1355      const signature = this.tsTypeChecker.getResolvedSignature(objectLiteral.parent);
1356      const signDecl = signature?.declaration;
1357      return !!signDecl && ts.isConstructorDeclaration(signDecl) && isStructDeclaration(signDecl.parent);
1358    }
1359    return false;
1360  }
1361
1362  parentSymbolCache = new Map<ts.Symbol, string | undefined>();
1363
1364  getParentSymbolName(symbol: ts.Symbol): string | undefined {
1365    const cached = this.parentSymbolCache.get(symbol);
1366    if (cached) {
1367      return cached;
1368    }
1369
1370    const name = this.tsTypeChecker.getFullyQualifiedName(symbol);
1371    const dotPosition = name.lastIndexOf('.');
1372    const result = dotPosition === -1 ? undefined : name.substring(0, dotPosition);
1373    this.parentSymbolCache.set(symbol, result);
1374    return result;
1375  }
1376
1377  isGlobalSymbol(symbol: ts.Symbol): boolean {
1378    const parentName = this.getParentSymbolName(symbol);
1379    return !parentName || parentName === 'global';
1380  }
1381
1382  isStdSymbol(symbol: ts.Symbol): boolean {
1383    const name = this.tsTypeChecker.getFullyQualifiedName(symbol);
1384    return name === SYMBOL || name === SYMBOL_CONSTRUCTOR;
1385  }
1386
1387  isStdSymbolAPI(symbol: ts.Symbol): boolean {
1388    const parentName = this.getParentSymbolName(symbol);
1389    if (!this.options.useRtLogic) {
1390      const name = parentName ? parentName : symbol.escapedName;
1391      return name === SYMBOL || name === SYMBOL_CONSTRUCTOR;
1392    }
1393    return !!parentName && (parentName === SYMBOL || parentName === SYMBOL_CONSTRUCTOR);
1394  }
1395
1396  isSymbolIterator(symbol: ts.Symbol): boolean {
1397    if (!this.options.useRtLogic) {
1398      const name = symbol.name;
1399      const parName = this.getParentSymbolName(symbol);
1400      return (parName === SYMBOL || parName === SYMBOL_CONSTRUCTOR) && name === ITERATOR;
1401    }
1402    return this.isStdSymbolAPI(symbol) && symbol.name === ITERATOR;
1403  }
1404
1405  isSymbolIteratorExpression(expr: ts.Expression): boolean {
1406    const symbol = this.trueSymbolAtLocation(expr);
1407    return !!symbol && this.isSymbolIterator(symbol);
1408  }
1409
1410  static isDefaultImport(importSpec: ts.ImportSpecifier): boolean {
1411    return importSpec?.propertyName?.text === 'default';
1412  }
1413
1414  static getStartPos(nodeOrComment: ts.Node | ts.CommentRange): number {
1415    return nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia ||
1416      nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia ?
1417      (nodeOrComment as ts.CommentRange).pos :
1418      (nodeOrComment as ts.Node).getStart();
1419  }
1420
1421  static getEndPos(nodeOrComment: ts.Node | ts.CommentRange): number {
1422    return nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia ||
1423      nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia ?
1424      (nodeOrComment as ts.CommentRange).end :
1425      (nodeOrComment as ts.Node).getEnd();
1426  }
1427
1428  static getHighlightRange(nodeOrComment: ts.Node | ts.CommentRange, faultId: number): [number, number] {
1429    return (
1430      this.highlightRangeHandlers.get(faultId)?.call(this, nodeOrComment) ?? [
1431        this.getStartPos(nodeOrComment),
1432        this.getEndPos(nodeOrComment)
1433      ]
1434    );
1435  }
1436
1437  static highlightRangeHandlers = new Map([
1438    [FaultID.VarDeclaration, TsUtils.getVarDeclarationHighlightRange],
1439    [FaultID.CatchWithUnsupportedType, TsUtils.getCatchWithUnsupportedTypeHighlightRange],
1440    [FaultID.ForInStatement, TsUtils.getForInStatementHighlightRange],
1441    [FaultID.WithStatement, TsUtils.getWithStatementHighlightRange],
1442    [FaultID.DeleteOperator, TsUtils.getDeleteOperatorHighlightRange],
1443    [FaultID.TypeQuery, TsUtils.getTypeQueryHighlightRange],
1444    [FaultID.InstanceofUnsupported, TsUtils.getInstanceofUnsupportedHighlightRange],
1445    [FaultID.ConstAssertion, TsUtils.getConstAssertionHighlightRange],
1446    [FaultID.LimitedReturnTypeInference, TsUtils.getLimitedReturnTypeInferenceHighlightRange],
1447    [FaultID.LocalFunction, TsUtils.getLocalFunctionHighlightRange],
1448    [FaultID.FunctionBind, TsUtils.getFunctionApplyCallHighlightRange],
1449    [FaultID.FunctionBindError, TsUtils.getFunctionApplyCallHighlightRange],
1450    [FaultID.FunctionApplyCall, TsUtils.getFunctionApplyCallHighlightRange],
1451    [FaultID.DeclWithDuplicateName, TsUtils.getDeclWithDuplicateNameHighlightRange],
1452    [FaultID.ObjectLiteralNoContextType, TsUtils.getObjectLiteralNoContextTypeHighlightRange],
1453    [FaultID.ClassExpression, TsUtils.getClassExpressionHighlightRange],
1454    [FaultID.MultipleStaticBlocks, TsUtils.getMultipleStaticBlocksHighlightRange],
1455    [FaultID.ParameterProperties, TsUtils.getParameterPropertiesHighlightRange],
1456    [FaultID.SendableDefiniteAssignment, TsUtils.getSendableDefiniteAssignmentHighlightRange],
1457    [FaultID.ObjectTypeLiteral, TsUtils.getObjectTypeLiteralHighlightRange],
1458    [FaultID.StructuralIdentity, TsUtils.getStructuralIdentityHighlightRange],
1459    [FaultID.VoidOperator, TsUtils.getVoidOperatorHighlightRange],
1460    [FaultID.ImportLazyIdentifier, TsUtils.getImportLazyHighlightRange],
1461    [FaultID.IncompationbleFunctionType, TsUtils.getIncompationbleFunctionTypeHighlightRange]
1462  ]);
1463
1464  static getKeywordHighlightRange(nodeOrComment: ts.Node | ts.CommentRange, keyword: string): [number, number] {
1465    const start = this.getStartPos(nodeOrComment);
1466    return [start, start + keyword.length];
1467  }
1468
1469  static getVarDeclarationHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1470    return this.getKeywordHighlightRange(nodeOrComment, 'var');
1471  }
1472
1473  static getCatchWithUnsupportedTypeHighlightRange(
1474    nodeOrComment: ts.Node | ts.CommentRange
1475  ): [number, number] | undefined {
1476    const catchClauseNode = (nodeOrComment as ts.CatchClause).variableDeclaration;
1477    if (catchClauseNode !== undefined) {
1478      return [catchClauseNode.getStart(), catchClauseNode.getEnd()];
1479    }
1480
1481    return undefined;
1482  }
1483
1484  static getForInStatementHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1485    return [
1486      this.getEndPos((nodeOrComment as ts.ForInStatement).initializer) + 1,
1487      this.getStartPos((nodeOrComment as ts.ForInStatement).expression) - 1
1488    ];
1489  }
1490
1491  static getWithStatementHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1492    return [this.getStartPos(nodeOrComment), (nodeOrComment as ts.WithStatement).statement.getStart() - 1];
1493  }
1494
1495  static getDeleteOperatorHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1496    return this.getKeywordHighlightRange(nodeOrComment, 'delete');
1497  }
1498
1499  static getTypeQueryHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1500    return this.getKeywordHighlightRange(nodeOrComment, 'typeof');
1501  }
1502
1503  static getInstanceofUnsupportedHighlightRange(
1504    nodeOrComment: ts.Node | ts.CommentRange
1505  ): [number, number] | undefined {
1506    return this.getKeywordHighlightRange((nodeOrComment as ts.BinaryExpression).operatorToken, 'instanceof');
1507  }
1508
1509  static getConstAssertionHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1510    if (nodeOrComment.kind === ts.SyntaxKind.AsExpression) {
1511      return [
1512        (nodeOrComment as ts.AsExpression).expression.getEnd() + 1,
1513        (nodeOrComment as ts.AsExpression).type.getStart() - 1
1514      ];
1515    }
1516    return [
1517      (nodeOrComment as ts.TypeAssertion).expression.getEnd() + 1,
1518      (nodeOrComment as ts.TypeAssertion).type.getEnd() + 1
1519    ];
1520  }
1521
1522  static getLimitedReturnTypeInferenceHighlightRange(
1523    nodeOrComment: ts.Node | ts.CommentRange
1524  ): [number, number] | undefined {
1525    let node: ts.Node | undefined;
1526    if (nodeOrComment.kind === ts.SyntaxKind.FunctionExpression) {
1527      // we got error about return type so it should be present
1528      node = (nodeOrComment as ts.FunctionExpression).type;
1529    } else if (nodeOrComment.kind === ts.SyntaxKind.FunctionDeclaration) {
1530      node = (nodeOrComment as ts.FunctionDeclaration).name;
1531    } else if (nodeOrComment.kind === ts.SyntaxKind.MethodDeclaration) {
1532      node = (nodeOrComment as ts.MethodDeclaration).name;
1533    }
1534
1535    if (node !== undefined) {
1536      return [node.getStart(), node.getEnd()];
1537    }
1538
1539    return undefined;
1540  }
1541
1542  static getLocalFunctionHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1543    return this.getKeywordHighlightRange(nodeOrComment, 'function');
1544  }
1545
1546  static getFunctionApplyCallHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1547    const pointPos = (nodeOrComment as ts.Node).getText().lastIndexOf('.');
1548    return [this.getStartPos(nodeOrComment) + pointPos + 1, this.getEndPos(nodeOrComment)];
1549  }
1550
1551  static getDeclWithDuplicateNameHighlightRange(
1552    nodeOrComment: ts.Node | ts.CommentRange
1553  ): [number, number] | undefined {
1554    // in case of private identifier no range update is needed
1555    const nameNode: ts.Node | undefined = (nodeOrComment as ts.NamedDeclaration).name;
1556    if (nameNode !== undefined) {
1557      return [nameNode.getStart(), nameNode.getEnd()];
1558    }
1559
1560    return undefined;
1561  }
1562
1563  static getObjectLiteralNoContextTypeHighlightRange(
1564    nodeOrComment: ts.Node | ts.CommentRange
1565  ): [number, number] | undefined {
1566    return this.getKeywordHighlightRange(nodeOrComment, '{');
1567  }
1568
1569  static getClassExpressionHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1570    return this.getKeywordHighlightRange(nodeOrComment, 'class');
1571  }
1572
1573  static getMultipleStaticBlocksHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1574    return this.getKeywordHighlightRange(nodeOrComment, 'static');
1575  }
1576
1577  static getParameterPropertiesHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1578    const param = nodeOrComment as ts.ParameterDeclaration;
1579    const modifier = TsUtils.getAccessModifier(ts.getModifiers(param));
1580    if (modifier !== undefined) {
1581      return [modifier.getStart(), modifier.getEnd()];
1582    }
1583    return undefined;
1584  }
1585
1586  static getObjectTypeLiteralHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1587    return this.getKeywordHighlightRange(nodeOrComment, '{');
1588  }
1589
1590  // highlight ranges for Sendable rules
1591
1592  static getSendableDefiniteAssignmentHighlightRange(
1593    nodeOrComment: ts.Node | ts.CommentRange
1594  ): [number, number] | undefined {
1595    const name = (nodeOrComment as ts.PropertyDeclaration).name;
1596    const exclamationToken = (nodeOrComment as ts.PropertyDeclaration).exclamationToken;
1597    return [name.getStart(), exclamationToken ? exclamationToken.getEnd() : name.getEnd()];
1598  }
1599
1600  static getStructuralIdentityHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1601    let node: ts.Node | undefined;
1602    if (nodeOrComment.kind === ts.SyntaxKind.ReturnStatement) {
1603      node = (nodeOrComment as ts.ReturnStatement).expression;
1604    } else if (nodeOrComment.kind === ts.SyntaxKind.PropertyDeclaration) {
1605      node = (nodeOrComment as ts.PropertyDeclaration).name;
1606    }
1607
1608    if (node !== undefined) {
1609      return [node.getStart(), node.getEnd()];
1610    }
1611
1612    return undefined;
1613  }
1614
1615  static getIncompationbleFunctionTypeHighlightRange(
1616    nodeOrComment: ts.Node | ts.CommentRange
1617  ): [number, number] | undefined {
1618    const node = nodeOrComment as ts.Node;
1619    if (ts.isArrowFunction(node)) {
1620      const parameters = node.parameters;
1621      if (parameters.length > 0) {
1622        const firstParamStart = parameters[0].getStart();
1623        const lastParamEnd = parameters[parameters.length - 1].getEnd();
1624        return [firstParamStart, lastParamEnd];
1625      }
1626    }
1627    return [node.getStart(), node.getEnd()];
1628  }
1629
1630  static getVoidOperatorHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1631    return this.getKeywordHighlightRange(nodeOrComment, 'void');
1632  }
1633
1634  static getImportLazyHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
1635    return this.getKeywordHighlightRange(nodeOrComment, 'lazy');
1636  }
1637
1638  isStdRecordType(type: ts.Type): boolean {
1639
1640    /*
1641     * In TypeScript, 'Record<K, T>' is defined as type alias to a mapped type.
1642     * Thus, it should have 'aliasSymbol' and 'target' properties. The 'target'
1643     * in this case will resolve to origin 'Record' symbol.
1644     */
1645    if (type.aliasSymbol) {
1646      const target = (type as ts.TypeReference).target;
1647      if (target) {
1648        const sym = target.aliasSymbol;
1649        return !!sym && sym.getName() === 'Record' && this.isGlobalSymbol(sym);
1650      }
1651    }
1652
1653    return false;
1654  }
1655
1656  isStdErrorType(type: ts.Type): boolean {
1657    const symbol = type.symbol;
1658    if (!symbol) {
1659      return false;
1660    }
1661    const name = this.tsTypeChecker.getFullyQualifiedName(symbol);
1662    return name === 'Error' && this.isGlobalSymbol(symbol);
1663  }
1664
1665  isStdPartialType(type: ts.Type): boolean {
1666    const sym = type.aliasSymbol;
1667    return !!sym && sym.getName() === 'Partial' && this.isGlobalSymbol(sym);
1668  }
1669
1670  isStdRequiredType(type: ts.Type): boolean {
1671    const sym = type.aliasSymbol;
1672    return !!sym && sym.getName() === 'Required' && this.isGlobalSymbol(sym);
1673  }
1674
1675  isStdReadonlyType(type: ts.Type): boolean {
1676    const sym = type.aliasSymbol;
1677    return !!sym && sym.getName() === 'Readonly' && this.isGlobalSymbol(sym);
1678  }
1679
1680  isLibraryType(type: ts.Type): boolean {
1681    const nonNullableType = type.getNonNullableType();
1682    if (nonNullableType.isUnion()) {
1683      for (const componentType of nonNullableType.types) {
1684        if (!this.isLibraryType(componentType)) {
1685          return false;
1686        }
1687      }
1688      return true;
1689    }
1690    return this.isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol());
1691  }
1692
1693  hasLibraryType(node: ts.Node): boolean {
1694    return this.isLibraryType(this.tsTypeChecker.getTypeAtLocation(node));
1695  }
1696
1697  isLibrarySymbol(sym: ts.Symbol | undefined): boolean {
1698    if (sym?.declarations && sym.declarations.length > 0) {
1699      const srcFile = sym.declarations[0].getSourceFile();
1700      if (!srcFile) {
1701        return false;
1702      }
1703      const fileName = srcFile.fileName;
1704
1705      /*
1706       * Symbols from both *.ts and *.d.ts files should obey interop rules.
1707       * We disable such behavior for *.ts files in the test mode due to lack of 'ets'
1708       * extension support.
1709       */
1710      const ext = path.extname(fileName).toLowerCase();
1711      const isThirdPartyCode =
1712        ARKTS_IGNORE_DIRS.some((ignore) => {
1713          return srcFilePathContainsDirectory(srcFile, ignore);
1714        }) ||
1715        ARKTS_IGNORE_FILES.some((ignore) => {
1716          return path.basename(fileName) === ignore;
1717        });
1718      const isEts = ext === '.ets';
1719      const isTs = ext === '.ts' && !srcFile.isDeclarationFile;
1720      const isStatic = (isEts || isTs && this.options.checkTsAsSource) && !isThirdPartyCode;
1721      const isStdLib = STANDARD_LIBRARIES.includes(path.basename(fileName).toLowerCase());
1722
1723      /*
1724       * We still need to confirm support for certain API from the
1725       * TypeScript standard library in ArkTS. Thus, for now do not
1726       * count standard library modules as dynamic.
1727       */
1728      return !isStatic && !isStdLib;
1729    }
1730    return false;
1731  }
1732
1733  static isOhModulesEtsSymbol(sym: ts.Symbol | undefined): boolean {
1734    const sourceFile = sym?.declarations?.[0]?.getSourceFile();
1735    return (
1736      !!sourceFile &&
1737      path.extname(sourceFile.fileName).toLowerCase() === ETS &&
1738      srcFilePathContainsDirectory(sourceFile, ARKTS_IGNORE_DIRS_OH_MODULES)
1739    );
1740  }
1741
1742  isDynamicType(type: ts.Type | undefined): boolean | undefined {
1743    if (type === undefined) {
1744      return false;
1745    }
1746
1747    /*
1748     * Return 'true' if it is an object of library type initialization, otherwise
1749     * return 'false' if it is not an object of standard library type one.
1750     * In the case of standard library type we need to determine context.
1751     */
1752
1753    /*
1754     * Check the non-nullable version of type to eliminate 'undefined' type
1755     * from the union type elements.
1756     */
1757    // eslint-disable-next-line no-param-reassign
1758    type = type.getNonNullableType();
1759
1760    if (type.isUnion()) {
1761      for (const compType of type.types) {
1762        const isDynamic = this.isDynamicType(compType);
1763        if (isDynamic || isDynamic === undefined) {
1764          return isDynamic;
1765        }
1766      }
1767      return false;
1768    }
1769
1770    if (this.isLibraryType(type)) {
1771      return true;
1772    }
1773
1774    if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !TsUtils.isAnyType(type)) {
1775      return false;
1776    }
1777
1778    return undefined;
1779  }
1780
1781  static isObjectType(type: ts.Type): type is ts.ObjectType {
1782    return !!(type.flags & ts.TypeFlags.Object);
1783  }
1784
1785  private static isAnonymous(type: ts.Type): boolean {
1786    if (TsUtils.isObjectType(type)) {
1787      return !!(type.objectFlags & ts.ObjectFlags.Anonymous);
1788    }
1789    return false;
1790  }
1791
1792  private isDynamicLiteralInitializerHandleCallExpression(callExpr: ts.CallExpression): boolean {
1793    const type = this.tsTypeChecker.getTypeAtLocation(callExpr.expression);
1794
1795    if (TsUtils.isAnyType(type)) {
1796      return true;
1797    }
1798
1799    let sym: ts.Symbol | undefined = type.symbol;
1800    if (this.isLibrarySymbol(sym)) {
1801      return true;
1802    }
1803
1804    /*
1805     * #13483:
1806     * x.foo({ ... }), where 'x' is exported from some library:
1807     */
1808    if (ts.isPropertyAccessExpression(callExpr.expression)) {
1809      sym = this.trueSymbolAtLocation(callExpr.expression.expression);
1810      if (sym && this.isLibrarySymbol(sym)) {
1811        return true;
1812      }
1813    }
1814
1815    return false;
1816  }
1817
1818  isDynamicLiteralInitializer(expr: ts.Expression): boolean {
1819    if (!ts.isObjectLiteralExpression(expr) && !ts.isArrayLiteralExpression(expr)) {
1820      return false;
1821    }
1822
1823    /*
1824     * Handle nested literals:
1825     * { f: { ... } }
1826     */
1827    let curNode: ts.Node = expr;
1828    while (ts.isObjectLiteralExpression(curNode) || ts.isArrayLiteralExpression(curNode)) {
1829      const exprType = this.tsTypeChecker.getContextualType(curNode);
1830      if (exprType !== undefined && !TsUtils.isAnonymous(exprType)) {
1831        const res = this.isDynamicType(exprType);
1832        if (res !== undefined) {
1833          return res;
1834        }
1835      }
1836
1837      curNode = curNode.parent;
1838      if (ts.isPropertyAssignment(curNode)) {
1839        curNode = curNode.parent;
1840      }
1841    }
1842
1843    /*
1844     * Handle calls with literals:
1845     * foo({ ... })
1846     */
1847    if (ts.isCallExpression(curNode) && this.isDynamicLiteralInitializerHandleCallExpression(curNode)) {
1848      return true;
1849    }
1850
1851    /*
1852     * Handle property assignments with literals:
1853     * obj.f = { ... }
1854     */
1855    if (ts.isBinaryExpression(curNode)) {
1856      const binExpr = curNode;
1857      if (ts.isPropertyAccessExpression(binExpr.left)) {
1858        const propAccessExpr = binExpr.left;
1859        const type = this.tsTypeChecker.getTypeAtLocation(propAccessExpr.expression);
1860        return this.isLibrarySymbol(type.symbol);
1861      }
1862    }
1863
1864    return false;
1865  }
1866
1867  static isEsValueType(typeNode: ts.TypeNode | undefined): boolean {
1868    return (
1869      !!typeNode &&
1870      ts.isTypeReferenceNode(typeNode) &&
1871      ts.isIdentifier(typeNode.typeName) &&
1872      typeNode.typeName.text === ES_VALUE
1873    );
1874  }
1875
1876  static isInsideBlock(node: ts.Node): boolean {
1877    let par = node.parent;
1878    while (par) {
1879      if (ts.isBlock(par)) {
1880        return true;
1881      }
1882      par = par.parent;
1883    }
1884    return false;
1885  }
1886
1887  static isEsValuePossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean {
1888    return ts.isVariableDeclaration(typeRef.parent);
1889  }
1890
1891  isValueAssignableToESValue(node: ts.Node): boolean {
1892    if (ts.isArrayLiteralExpression(node) || ts.isObjectLiteralExpression(node)) {
1893      return false;
1894    }
1895    const valueType = this.tsTypeChecker.getTypeAtLocation(node);
1896    return TsUtils.isUnsupportedType(valueType) || TsUtils.isAnonymousType(valueType);
1897  }
1898
1899  getVariableDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined {
1900    const sym = this.trueSymbolAtLocation(node);
1901    if (sym === undefined) {
1902      return undefined;
1903    }
1904    return TsUtils.getVariableSymbolDeclarationTypeNode(sym);
1905  }
1906
1907  static getVariableSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined {
1908    const decl = TsUtils.getDeclaration(sym);
1909    if (!!decl && ts.isVariableDeclaration(decl)) {
1910      return decl.type;
1911    }
1912    return undefined;
1913  }
1914
1915  getDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined {
1916    const sym = this.trueSymbolAtLocation(node);
1917    if (sym === undefined) {
1918      return undefined;
1919    }
1920    return TsUtils.getSymbolDeclarationTypeNode(sym);
1921  }
1922
1923  static getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined {
1924    const decl = TsUtils.getDeclaration(sym);
1925    if (!!decl && (ts.isVariableDeclaration(decl) || ts.isPropertyDeclaration(decl))) {
1926      return decl.type;
1927    }
1928    return undefined;
1929  }
1930
1931  hasEsObjectType(node: ts.Node): boolean {
1932    const typeNode = this.getVariableDeclarationTypeNode(node);
1933    return typeNode !== undefined && TsUtils.isEsValueType(typeNode);
1934  }
1935
1936  static symbolHasEsObjectType(sym: ts.Symbol): boolean {
1937    const typeNode = TsUtils.getVariableSymbolDeclarationTypeNode(sym);
1938    return typeNode !== undefined && TsUtils.isEsValueType(typeNode);
1939  }
1940
1941  static isEsObjectSymbol(sym: ts.Symbol): boolean {
1942    const decl = TsUtils.getDeclaration(sym);
1943    return (
1944      !!decl &&
1945      ts.isTypeAliasDeclaration(decl) &&
1946      decl.name.escapedText === ES_VALUE &&
1947      decl.type.kind === ts.SyntaxKind.AnyKeyword
1948    );
1949  }
1950
1951  static isAnonymousType(type: ts.Type): boolean {
1952    if (type.isUnionOrIntersection()) {
1953      for (const compType of type.types) {
1954        if (TsUtils.isAnonymousType(compType)) {
1955          return true;
1956        }
1957      }
1958      return false;
1959    }
1960
1961    return (
1962      (type.flags & ts.TypeFlags.Object) !== 0 && ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) !== 0
1963    );
1964  }
1965
1966  getSymbolOfCallExpression(callExpr: ts.CallExpression): ts.Symbol | undefined {
1967    const signature = this.tsTypeChecker.getResolvedSignature(callExpr);
1968    const signDecl = signature?.getDeclaration();
1969    if (signDecl?.name) {
1970      return this.trueSymbolAtLocation(signDecl.name);
1971    }
1972    return undefined;
1973  }
1974
1975  static isClassValueType(type: ts.Type): boolean {
1976    if (
1977      (type.flags & ts.TypeFlags.Object) === 0 ||
1978      ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) === 0
1979    ) {
1980      return false;
1981    }
1982    return type.symbol && (type.symbol.flags & ts.SymbolFlags.Class) !== 0;
1983  }
1984
1985  isClassObjectExpression(expr: ts.Expression): boolean {
1986    if (!TsUtils.isClassValueType(this.tsTypeChecker.getTypeAtLocation(expr))) {
1987      return false;
1988    }
1989    const symbol = this.trueSymbolAtLocation(expr);
1990    return !symbol || (symbol.flags & ts.SymbolFlags.Class) === 0;
1991  }
1992
1993  isClassTypeExpression(expr: ts.Expression): boolean {
1994    const sym = this.trueSymbolAtLocation(expr);
1995    return sym !== undefined && (sym.flags & ts.SymbolFlags.Class) !== 0;
1996  }
1997
1998  isFunctionCalledRecursively(funcExpr: ts.FunctionExpression): boolean {
1999    if (!funcExpr.name) {
2000      return false;
2001    }
2002
2003    const sym = this.tsTypeChecker.getSymbolAtLocation(funcExpr.name);
2004    if (!sym) {
2005      return false;
2006    }
2007
2008    let found = false;
2009    const callback = (node: ts.Node): void => {
2010      if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
2011        const callSym = this.tsTypeChecker.getSymbolAtLocation(node.expression);
2012        if (callSym && callSym === sym) {
2013          found = true;
2014        }
2015      }
2016    };
2017
2018    const stopCondition = (node: ts.Node): boolean => {
2019      void node;
2020      return found;
2021    };
2022
2023    forEachNodeInSubtree(funcExpr, callback, stopCondition);
2024    return found;
2025  }
2026
2027  getTypeOrTypeConstraintAtLocation(expr: ts.Expression): ts.Type {
2028    const type = this.tsTypeChecker.getTypeAtLocation(expr);
2029    if (type.isTypeParameter()) {
2030      const constraint = type.getConstraint();
2031      if (constraint) {
2032        return constraint;
2033      }
2034    }
2035    return type;
2036  }
2037
2038  private areCompatibleFunctionals(lhsType: ts.Type, rhsType: ts.Type): boolean {
2039    return (
2040      (this.isStdFunctionType(lhsType) || TsUtils.isFunctionalType(lhsType)) &&
2041      (this.isStdFunctionType(rhsType) || TsUtils.isFunctionalType(rhsType))
2042    );
2043  }
2044
2045  isIncompatibleFunctionals(lhsTypeNode: ts.TypeNode, rhsExpr: ts.Expression): boolean {
2046    if (ts.isUnionTypeNode(lhsTypeNode)) {
2047      for (let i = 0; i < lhsTypeNode.types.length; i++) {
2048        if (!this.isIncompatibleFunctional(lhsTypeNode.types[i], rhsExpr)) {
2049          return false;
2050        }
2051      }
2052      return true;
2053    }
2054    return this.isIncompatibleFunctional(lhsTypeNode, rhsExpr);
2055  }
2056
2057  private isIncompatibleFunctional(lhsTypeNode: ts.TypeNode, rhsExpr: ts.Expression): boolean {
2058    const lhsType = this.tsTypeChecker.getTypeAtLocation(lhsTypeNode);
2059    const rhsType = this.tsTypeChecker.getTypeAtLocation(rhsExpr);
2060    const lhsParams = this.getLhsFunctionParameters(lhsTypeNode);
2061    const rhsParams = this.getRhsFunctionParameters(rhsExpr);
2062
2063    if (lhsParams !== rhsParams) {
2064      return false;
2065    }
2066
2067    if (TsUtils.isFunctionalType(lhsType)) {
2068      const lhsFunctionReturnType = this.getFunctionType(lhsTypeNode);
2069      const rhsReturnType = this.getReturnTypeFromExpression(rhsType);
2070      if (lhsFunctionReturnType && rhsReturnType) {
2071        return TsUtils.isVoidType(lhsFunctionReturnType) && !TsUtils.isVoidType(rhsReturnType);
2072      }
2073    }
2074    return false;
2075  }
2076
2077  static isVoidType(tsType: ts.Type): boolean {
2078    return (tsType.getFlags() & ts.TypeFlags.Void) !== 0;
2079  }
2080
2081  private getRhsFunctionParameters(expr: ts.Expression): number {
2082    const type = this.tsTypeChecker.getTypeAtLocation(expr);
2083    const signatures = this.tsTypeChecker.getSignaturesOfType(type, ts.SignatureKind.Call);
2084    if (signatures.length > 0) {
2085      const signature = signatures[0];
2086      return signature.parameters.length;
2087    }
2088    return 0;
2089  }
2090
2091  private getLhsFunctionParameters(typeNode: ts.TypeNode): number {
2092    let current: ts.TypeNode = typeNode;
2093    while (ts.isTypeReferenceNode(current)) {
2094      const symbol = this.tsTypeChecker.getSymbolAtLocation(current.typeName);
2095      if (!symbol) {
2096        break;
2097      }
2098
2099      const declaration = symbol.declarations?.[0];
2100      if (!declaration || !ts.isTypeAliasDeclaration(declaration)) {
2101        break;
2102      }
2103
2104      current = declaration.type;
2105    }
2106    if (ts.isFunctionTypeNode(current)) {
2107      return current.parameters.length;
2108    }
2109    return 0;
2110  }
2111
2112  private getFunctionType(typeNode: ts.TypeNode): ts.Type | undefined {
2113    let current: ts.TypeNode = typeNode;
2114    while (ts.isTypeReferenceNode(current)) {
2115      const symbol = this.tsTypeChecker.getSymbolAtLocation(current.typeName);
2116      if (!symbol) {
2117        break;
2118      }
2119
2120      const declaration = symbol.declarations?.[0];
2121      if (!declaration || !ts.isTypeAliasDeclaration(declaration)) {
2122        break;
2123      }
2124
2125      current = declaration.type;
2126    }
2127    if (ts.isFunctionTypeNode(current)) {
2128      return this.tsTypeChecker.getTypeAtLocation(current.type);
2129    }
2130    return undefined;
2131  }
2132
2133  private getReturnTypeFromExpression(type: ts.Type): ts.Type | undefined {
2134    const signatures = this.tsTypeChecker.getSignaturesOfType(type, ts.SignatureKind.Call);
2135    if (signatures.length > 0) {
2136      const returnType = this.tsTypeChecker.getReturnTypeOfSignature(signatures[0]);
2137      return returnType;
2138    }
2139    return undefined;
2140  }
2141
2142  static isFunctionalType(type: ts.Type): boolean {
2143    const callSigns = type.getCallSignatures();
2144    return callSigns && callSigns.length > 0;
2145  }
2146
2147  static getFunctionReturnType(type: ts.Type): ts.Type | null {
2148    const signatures = type.getCallSignatures();
2149    if (signatures.length === 0) {
2150      return null;
2151    }
2152    return signatures[0].getReturnType();
2153  }
2154
2155  isStdFunctionType(type: ts.Type): boolean {
2156    const sym = type.getSymbol();
2157    return !!sym && sym.getName() === 'Function' && this.isGlobalSymbol(sym);
2158  }
2159
2160  isStdBigIntType(type: ts.Type): boolean {
2161    const sym = type.symbol;
2162    return !!sym && sym.getName() === 'BigInt' && this.isGlobalSymbol(sym);
2163  }
2164
2165  isStdNumberType(type: ts.Type): boolean {
2166    const sym = type.symbol;
2167    return !!sym && sym.getName() === 'Number' && this.isGlobalSymbol(sym);
2168  }
2169
2170  isStdBooleanType(type: ts.Type): boolean {
2171    const sym = type.symbol;
2172    return !!sym && sym.getName() === 'Boolean' && this.isGlobalSymbol(sym);
2173  }
2174
2175  isEnumStringLiteral(expr: ts.Expression): boolean {
2176    const symbol = this.trueSymbolAtLocation(expr);
2177    const isEnumMember = !!symbol && !!(symbol.flags & ts.SymbolFlags.EnumMember);
2178    const type = this.tsTypeChecker.getTypeAtLocation(expr);
2179    const isStringEnumLiteral = TsUtils.isEnumType(type) && !!(type.flags & ts.TypeFlags.StringLiteral);
2180    return isEnumMember && isStringEnumLiteral;
2181  }
2182
2183  isValidComputedPropertyName(computedProperty: ts.ComputedPropertyName, isRecordObjectInitializer = false): boolean {
2184    const expr = computedProperty.expression;
2185    if (!isRecordObjectInitializer) {
2186      if (this.isSymbolIteratorExpression(expr)) {
2187        return true;
2188      }
2189    }
2190    // In ArkTS 1.0, the computed property names are allowed if expression is string literal or string Enum member.
2191    return (
2192      !this.options.arkts2 && (ts.isStringLiteralLike(expr) || this.isEnumStringLiteral(computedProperty.expression))
2193    );
2194  }
2195
2196  skipPropertyInferredTypeCheck(
2197    decl: ts.PropertyDeclaration,
2198    sourceFile: ts.SourceFile | undefined,
2199    isEtsFileCb: IsEtsFileCallback | undefined
2200  ): boolean {
2201    if (!sourceFile) {
2202      return false;
2203    }
2204
2205    const isEts = this.options.useRtLogic ?
2206      !!isEtsFileCb && isEtsFileCb(sourceFile) :
2207      getScriptKind(sourceFile) === ts.ScriptKind.ETS;
2208    return (
2209      isEts &&
2210      sourceFile.isDeclarationFile &&
2211      !!decl.modifiers?.some((m) => {
2212        return m.kind === ts.SyntaxKind.PrivateKeyword;
2213      })
2214    );
2215  }
2216
2217  hasAccessModifier(decl: ts.HasModifiers): boolean {
2218    const modifiers = ts.getModifiers(decl);
2219    return (
2220      !!modifiers &&
2221      (!this.options.useRtLogic && TsUtils.hasModifier(modifiers, ts.SyntaxKind.ReadonlyKeyword) ||
2222        TsUtils.hasModifier(modifiers, ts.SyntaxKind.PublicKeyword) ||
2223        TsUtils.hasModifier(modifiers, ts.SyntaxKind.ProtectedKeyword) ||
2224        TsUtils.hasModifier(modifiers, ts.SyntaxKind.PrivateKeyword) ||
2225        !!this.options.arkts2 &&
2226          (TsUtils.hasModifier(modifiers, ts.SyntaxKind.ReadonlyKeyword) ||
2227            TsUtils.hasModifier(modifiers, ts.SyntaxKind.OverrideKeyword)))
2228    );
2229  }
2230
2231  static getModifier(
2232    modifiers: readonly ts.Modifier[] | undefined,
2233    modifierKind: ts.SyntaxKind
2234  ): ts.Modifier | undefined {
2235    if (!modifiers) {
2236      return undefined;
2237    }
2238    return modifiers.find((x) => {
2239      return x.kind === modifierKind;
2240    });
2241  }
2242
2243  static getAccessModifier(modifiers: readonly ts.Modifier[] | undefined): ts.Modifier | undefined {
2244    return (
2245      TsUtils.getModifier(modifiers, ts.SyntaxKind.PublicKeyword) ??
2246      TsUtils.getModifier(modifiers, ts.SyntaxKind.ProtectedKeyword) ??
2247      TsUtils.getModifier(modifiers, ts.SyntaxKind.PrivateKeyword)
2248    );
2249  }
2250
2251  static getBaseClassType(type: ts.Type): ts.InterfaceType | undefined {
2252    const baseTypes = type.getBaseTypes();
2253    if (baseTypes) {
2254      for (const baseType of baseTypes) {
2255        if (baseType.isClass()) {
2256          return baseType;
2257        }
2258      }
2259    }
2260
2261    return undefined;
2262  }
2263
2264  static destructuringAssignmentHasSpreadOperator(node: ts.AssignmentPattern): boolean {
2265    if (ts.isArrayLiteralExpression(node)) {
2266      return node.elements.some((x) => {
2267        if (ts.isSpreadElement(x)) {
2268          return true;
2269        }
2270        if (ts.isObjectLiteralExpression(x) || ts.isArrayLiteralExpression(x)) {
2271          return TsUtils.destructuringAssignmentHasSpreadOperator(x);
2272        }
2273        return false;
2274      });
2275    }
2276
2277    return node.properties.some((x) => {
2278      if (ts.isSpreadAssignment(x)) {
2279        return true;
2280      }
2281      if (
2282        ts.isPropertyAssignment(x) &&
2283        (ts.isObjectLiteralExpression(x.initializer) || ts.isArrayLiteralExpression(x.initializer))
2284      ) {
2285        return TsUtils.destructuringAssignmentHasSpreadOperator(x.initializer);
2286      }
2287      return false;
2288    });
2289  }
2290
2291  static destructuringDeclarationHasSpreadOperator(node: ts.BindingPattern): boolean {
2292    return node.elements.some((x) => {
2293      if (ts.isBindingElement(x)) {
2294        if (x.dotDotDotToken) {
2295          return true;
2296        }
2297        if (ts.isArrayBindingPattern(x.name) || ts.isObjectBindingPattern(x.name)) {
2298          return TsUtils.destructuringDeclarationHasSpreadOperator(x.name);
2299        }
2300      }
2301      return false;
2302    });
2303  }
2304
2305  // Check if the destructuring assignment has default values
2306  static destructuringAssignmentHasDefaultValue(node: ts.AssignmentPattern): boolean {
2307    if (ts.isArrayLiteralExpression(node)) {
2308      return node.elements.some((x) => {
2309        if (ts.isBinaryExpression(x) && x.right) {
2310          return true;
2311        }
2312        if (ts.isObjectLiteralExpression(x) || ts.isArrayLiteralExpression(x)) {
2313          return TsUtils.destructuringAssignmentHasDefaultValue(x);
2314        }
2315        return false;
2316      });
2317    }
2318
2319    return node.properties.some((x) => {
2320      if (ts.isShorthandPropertyAssignment(x) && x.equalsToken) {
2321        return true;
2322      }
2323      if (
2324        ts.isPropertyAssignment(x) &&
2325        (ts.isObjectLiteralExpression(x.initializer) || ts.isArrayLiteralExpression(x.initializer))
2326      ) {
2327        return TsUtils.destructuringAssignmentHasDefaultValue(x.initializer);
2328      }
2329      return false;
2330    });
2331  }
2332
2333  // Check if the destructuring declaration has default values
2334  static destructuringDeclarationHasDefaultValue(node: ts.BindingPattern): boolean {
2335    return node.elements.some((x) => {
2336      if (ts.isBindingElement(x)) {
2337        if (x.initializer) {
2338          return true;
2339        }
2340        if (ts.isArrayBindingPattern(x.name) || ts.isObjectBindingPattern(x.name)) {
2341          return TsUtils.destructuringDeclarationHasDefaultValue(x.name);
2342        }
2343      }
2344      return false;
2345    });
2346  }
2347
2348  // Check that the dimensions of the destruct array are the same
2349  static isSameDimension(arr: ts.Node): boolean | undefined {
2350    if (!ts.isArrayLiteralExpression(arr)) {
2351      return true;
2352    }
2353    let hasOneDimensional: boolean = false;
2354    let hasMultipleDimensions: boolean = false;
2355    const arrElements = arr.elements;
2356    let maxlen: number = 0;
2357    let minlen: number = 1000;
2358    for (let i = 0; i < arrElements.length; i++) {
2359      const e = arrElements[i] as ts.Node;
2360      if (!ts.isArrayLiteralExpression(e)) {
2361        hasOneDimensional = true;
2362      } else {
2363        hasMultipleDimensions = true;
2364        if (hasOneDimensional && hasMultipleDimensions) {
2365          return false;
2366        }
2367        if (!this.isSameDimension(e)) {
2368          return false;
2369        }
2370        maxlen = Math.max(e.elements.length, maxlen);
2371        minlen = Math.min(e.elements.length, minlen);
2372      }
2373    }
2374    if (hasOneDimensional && hasMultipleDimensions) {
2375      return false;
2376    } else if (hasOneDimensional && !hasMultipleDimensions) {
2377      return true;
2378    } else if (!hasOneDimensional && hasMultipleDimensions && maxlen === minlen) {
2379      return true;
2380    }
2381    return false;
2382  }
2383
2384  // if ArrayLiteralExpression has empty element, will be marked as not fixable
2385  static checkArrayLiteralHasEmptyElement(node: ts.ArrayLiteralExpression): boolean {
2386    return node.elements.some((x) => {
2387      if (ts.isOmittedExpression(x)) {
2388        return true;
2389      }
2390      if (ts.isArrayLiteralExpression(x)) {
2391        if (x.elements.length === 0) {
2392          return true;
2393        }
2394        return this.checkArrayLiteralHasEmptyElement(x);
2395      }
2396      return false;
2397    });
2398  }
2399
2400  // if ArrayBindingPattern has empty element, will be marked as not fixable
2401  static checkArrayBindingHasEmptyElement(node: ts.ArrayBindingPattern): boolean {
2402    return node.elements.some((x) => {
2403      if (ts.isOmittedExpression(x)) {
2404        return true;
2405      }
2406      if (ts.isBindingElement(x) && ts.isArrayBindingPattern(x.name)) {
2407        if (x.name.elements.length === 0) {
2408          return true;
2409        }
2410        return this.checkArrayBindingHasEmptyElement(x.name);
2411      }
2412      return false;
2413    });
2414  }
2415
2416  static hasNestedObjectDestructuring(node: ts.ArrayBindingOrAssignmentPattern): boolean {
2417    if (ts.isArrayLiteralExpression(node)) {
2418      return node.elements.some((x) => {
2419        const elem = ts.isSpreadElement(x) ? x.expression : x;
2420        if (ts.isArrayLiteralExpression(elem)) {
2421          return TsUtils.hasNestedObjectDestructuring(elem);
2422        }
2423        return ts.isObjectLiteralExpression(elem);
2424      });
2425    }
2426
2427    return node.elements.some((x) => {
2428      if (ts.isBindingElement(x)) {
2429        if (ts.isArrayBindingPattern(x.name)) {
2430          return TsUtils.hasNestedObjectDestructuring(x.name);
2431        }
2432        return ts.isObjectBindingPattern(x.name);
2433      }
2434      return false;
2435    });
2436  }
2437
2438  static getDecoratorName(decorator: ts.Decorator): string {
2439    let decoratorName = '';
2440    if (ts.isIdentifier(decorator.expression)) {
2441      decoratorName = decorator.expression.text;
2442    } else if (ts.isCallExpression(decorator.expression) && ts.isIdentifier(decorator.expression.expression)) {
2443      decoratorName = decorator.expression.expression.text;
2444    }
2445    return decoratorName;
2446  }
2447
2448  static unwrapParenthesizedTypeNode(typeNode: ts.TypeNode): ts.TypeNode {
2449    let unwrappedTypeNode = typeNode;
2450    while (ts.isParenthesizedTypeNode(unwrappedTypeNode)) {
2451      unwrappedTypeNode = unwrappedTypeNode.type;
2452    }
2453
2454    return unwrappedTypeNode;
2455  }
2456
2457  isSendableTypeNode(typeNode: ts.TypeNode, isShared: boolean = false): boolean {
2458
2459    /*
2460     * In order to correctly identify the usage of the enum member or
2461     * const enum in type annotation, we need to handle union type and
2462     * type alias cases by processing the type node and checking the
2463     * symbol in case of type reference node.
2464     */
2465
2466    // eslint-disable-next-line no-param-reassign
2467    typeNode = TsUtils.unwrapParenthesizedTypeNode(typeNode);
2468
2469    // Only a sendable union type is supported
2470    if (ts.isUnionTypeNode(typeNode)) {
2471      return typeNode.types.every((elemType) => {
2472        return this.isSendableTypeNode(elemType, isShared);
2473      });
2474    }
2475
2476    const sym = ts.isTypeReferenceNode(typeNode) ? this.trueSymbolAtLocation(typeNode.typeName) : undefined;
2477
2478    if (sym && sym.getFlags() & ts.SymbolFlags.TypeAlias) {
2479      const typeDecl = TsUtils.getDeclaration(sym);
2480      if (typeDecl && ts.isTypeAliasDeclaration(typeDecl)) {
2481        const typeArgs = (typeNode as ts.TypeReferenceNode).typeArguments;
2482        if (
2483          typeArgs &&
2484          !typeArgs.every((typeArg) => {
2485            return this.isSendableTypeNode(typeArg);
2486          })
2487        ) {
2488          return false;
2489        }
2490        return this.isSendableTypeNode(typeDecl.type, isShared);
2491      }
2492    }
2493
2494    // Const enum type is supported
2495    if (TsUtils.isConstEnum(sym)) {
2496      return true;
2497    }
2498    const type: ts.Type = this.tsTypeChecker.getTypeFromTypeNode(typeNode);
2499
2500    // In shared module, literal forms of primitive data types can be exported
2501    if (isShared && TsUtils.isPurePrimitiveLiteralType(type)) {
2502      return true;
2503    }
2504
2505    return this.isSendableType(type);
2506  }
2507
2508  isSendableType(type: ts.Type): boolean {
2509    if (
2510      (type.flags &
2511        (ts.TypeFlags.Boolean |
2512          ts.TypeFlags.Number |
2513          ts.TypeFlags.String |
2514          ts.TypeFlags.BigInt |
2515          ts.TypeFlags.Null |
2516          ts.TypeFlags.Undefined |
2517          ts.TypeFlags.TypeParameter)) !==
2518      0
2519    ) {
2520      return true;
2521    }
2522    if (this.isSendableTypeAlias(type)) {
2523      return true;
2524    }
2525    if (TsUtils.isSendableFunction(type)) {
2526      return true;
2527    }
2528
2529    return this.isSendableClassOrInterface(type);
2530  }
2531
2532  isShareableType(tsType: ts.Type): boolean {
2533    const sym = tsType.getSymbol();
2534    if (TsUtils.isConstEnum(sym)) {
2535      return true;
2536    }
2537
2538    if (tsType.isUnion()) {
2539      return tsType.types.every((elemType) => {
2540        return this.isShareableType(elemType);
2541      });
2542    }
2543
2544    if (TsUtils.isPurePrimitiveLiteralType(tsType)) {
2545      return true;
2546    }
2547
2548    return this.isSendableType(tsType);
2549  }
2550
2551  isSendableClassOrInterface(type: ts.Type): boolean {
2552    const sym = type.getSymbol();
2553    if (!sym) {
2554      return false;
2555    }
2556
2557    const targetType = TsUtils.reduceReference(type);
2558
2559    // class with @Sendable decorator
2560    if (targetType.isClass()) {
2561      if (sym.declarations?.length) {
2562        const decl = sym.declarations[0];
2563        if (ts.isClassDeclaration(decl)) {
2564          return TsUtils.hasSendableDecorator(decl);
2565        }
2566      }
2567    }
2568    // ISendable interface, or a class/interface that implements/extends ISendable interface
2569    return this.isOrDerivedFrom(type, TsUtils.isISendableInterface);
2570  }
2571
2572  typeContainsSendableClassOrInterface(type: ts.Type): boolean {
2573    // Only check type contains sendable class / interface
2574    if ((type.flags & ts.TypeFlags.Union) !== 0) {
2575      return !!(type as ts.UnionType)?.types?.some((type) => {
2576        return this.typeContainsSendableClassOrInterface(type);
2577      });
2578    }
2579
2580    return this.isSendableClassOrInterface(type);
2581  }
2582
2583  typeContainsNonSendableClassOrInterface(type: ts.Type): boolean {
2584    if (type.isUnion()) {
2585      return type.types.some((compType) => {
2586        return this.typeContainsNonSendableClassOrInterface(compType);
2587      });
2588    }
2589    // eslint-disable-next-line no-param-reassign
2590    type = TsUtils.reduceReference(type);
2591    return type.isClassOrInterface() && !this.isSendableClassOrInterface(type);
2592  }
2593
2594  static isConstEnum(sym: ts.Symbol | undefined): boolean {
2595    return !!sym && sym.flags === ts.SymbolFlags.ConstEnum;
2596  }
2597
2598  isSendableUnionType(type: ts.UnionType): boolean {
2599    const types = type?.types;
2600    if (!types) {
2601      return false;
2602    }
2603
2604    return types.every((type) => {
2605      return this.isSendableType(type);
2606    });
2607  }
2608
2609  static hasSendableDecorator(decl: ts.ClassDeclaration | ts.FunctionDeclaration | ts.TypeAliasDeclaration): boolean {
2610    return !!TsUtils.getSendableDecorator(decl);
2611  }
2612
2613  static getNonSendableDecorators(
2614    decl: ts.ClassDeclaration | ts.FunctionDeclaration | ts.TypeAliasDeclaration
2615  ): ts.Decorator[] | undefined {
2616    const decorators = ts.getAllDecorators(decl);
2617    return decorators?.filter((x) => {
2618      return TsUtils.getDecoratorName(x) !== SENDABLE_DECORATOR;
2619    });
2620  }
2621
2622  static getSendableDecorator(
2623    decl: ts.ClassDeclaration | ts.FunctionDeclaration | ts.TypeAliasDeclaration
2624  ): ts.Decorator | undefined {
2625    const decorators = ts.getAllDecorators(decl);
2626    return decorators?.find((x) => {
2627      return TsUtils.getDecoratorName(x) === SENDABLE_DECORATOR;
2628    });
2629  }
2630
2631  static getDecoratorsIfInSendableClass(declaration: ts.HasDecorators): readonly ts.Decorator[] | undefined {
2632    const classNode = TsUtils.getClassNodeFromDeclaration(declaration);
2633    if (classNode === undefined || !TsUtils.hasSendableDecorator(classNode)) {
2634      return undefined;
2635    }
2636    return ts.getDecorators(declaration);
2637  }
2638
2639  private static getClassNodeFromDeclaration(declaration: ts.HasDecorators): ts.ClassDeclaration | undefined {
2640    if (declaration.kind === ts.SyntaxKind.Parameter) {
2641      return ts.isClassDeclaration(declaration.parent.parent) ? declaration.parent.parent : undefined;
2642    }
2643    return ts.isClassDeclaration(declaration.parent) ? declaration.parent : undefined;
2644  }
2645
2646  static isISendableInterface(type: ts.Type): boolean {
2647    const symbol = type.aliasSymbol ?? type.getSymbol();
2648    if (symbol?.declarations === undefined || symbol.declarations.length < 1) {
2649      return false;
2650    }
2651
2652    return TsUtils.isArkTSISendableDeclaration(symbol.declarations[0]);
2653  }
2654
2655  private static isArkTSISendableDeclaration(decl: ts.Declaration): boolean {
2656    if (!ts.isInterfaceDeclaration(decl) || !decl.name || decl.name.text !== ISENDABLE_TYPE) {
2657      return false;
2658    }
2659
2660    if (!ts.isModuleBlock(decl.parent) || decl.parent.parent.name.text !== LANG_NAMESPACE) {
2661      return false;
2662    }
2663
2664    if (path.basename(decl.getSourceFile().fileName).toLowerCase() !== ARKTS_LANG_D_ETS) {
2665      return false;
2666    }
2667
2668    return true;
2669  }
2670
2671  isAllowedIndexSignature(node: ts.IndexSignatureDeclaration): boolean {
2672
2673    /*
2674     * For now, relax index signature only for specific array-like types
2675     * with the following signature: 'collections.Array<T>.[_: number]: T'.
2676     */
2677
2678    if (node.parameters.length !== 1) {
2679      return false;
2680    }
2681
2682    const paramType = this.tsTypeChecker.getTypeAtLocation(node.parameters[0]);
2683    if ((paramType.flags & ts.TypeFlags.Number) === 0) {
2684      return false;
2685    }
2686
2687    return this.isArkTSCollectionsArrayLikeDeclaration(node.parent);
2688  }
2689
2690  isArkTSCollectionsArrayLikeType(type: ts.Type): boolean {
2691    const symbol = type.aliasSymbol ?? type.getSymbol();
2692    if (symbol?.declarations === undefined || symbol.declarations.length < 1) {
2693      return false;
2694    }
2695
2696    return this.isArkTSCollectionsArrayLikeDeclaration(symbol.declarations[0]);
2697  }
2698
2699  private isArkTSCollectionsArrayLikeDeclaration(decl: ts.Declaration): boolean {
2700    if (!TsUtils.isArkTSCollectionsClassOrInterfaceDeclaration(decl)) {
2701      return false;
2702    }
2703    if (!this.tsTypeChecker.getTypeAtLocation(decl).getNumberIndexType()) {
2704      return false;
2705    }
2706    return true;
2707  }
2708
2709  static isArkTSCollectionsClassOrInterfaceDeclaration(decl: ts.Node): boolean {
2710    if (!ts.isClassDeclaration(decl) && !ts.isInterfaceDeclaration(decl) || !decl.name) {
2711      return false;
2712    }
2713    if (!ts.isModuleBlock(decl.parent) || decl.parent.parent.name.text !== COLLECTIONS_NAMESPACE) {
2714      return false;
2715    }
2716    if (path.basename(decl.getSourceFile().fileName).toLowerCase() !== ARKTS_COLLECTIONS_D_ETS) {
2717      return false;
2718    }
2719    return true;
2720  }
2721
2722  private proceedConstructorDeclaration(
2723    isFromPrivateIdentifierOrSdk: boolean,
2724    targetMember: ts.ClassElement,
2725    classMember: ts.ClassElement,
2726    isFromPrivateIdentifier: boolean
2727  ): boolean | undefined {
2728    if (
2729      isFromPrivateIdentifierOrSdk &&
2730      ts.isConstructorDeclaration(classMember) &&
2731      classMember.parameters.some((x) => {
2732        return (
2733          ts.isIdentifier(x.name) &&
2734          this.hasAccessModifier(x) &&
2735          this.isPrivateIdentifierDuplicateOfIdentifier(
2736            targetMember.name as ts.Identifier,
2737            x.name,
2738            isFromPrivateIdentifier
2739          )
2740        );
2741      })
2742    ) {
2743      return true;
2744    }
2745    return undefined;
2746  }
2747
2748  private proceedClassType(
2749    targetMember: ts.ClassElement,
2750    classType: ts.Type,
2751    isFromPrivateIdentifier: boolean
2752  ): boolean | undefined {
2753    if (classType) {
2754      const baseType = TsUtils.getBaseClassType(classType);
2755      if (baseType) {
2756        const baseDecl = baseType.getSymbol()?.valueDeclaration as ts.ClassLikeDeclaration;
2757        if (baseDecl) {
2758          return this.classMemberHasDuplicateName(targetMember, baseDecl, isFromPrivateIdentifier);
2759        }
2760      }
2761    }
2762    return undefined;
2763  }
2764
2765  classMemberHasDuplicateName(
2766    targetMember: ts.ClassElement,
2767    tsClassLikeDecl: ts.ClassLikeDeclaration,
2768    isFromPrivateIdentifier: boolean,
2769    classType?: ts.Type
2770  ): boolean {
2771
2772    /*
2773     * If two class members have the same name where one is a private identifer,
2774     * then such members are considered to have duplicate names.
2775     */
2776    if (!TsUtils.isIdentifierOrPrivateIdentifier(targetMember.name)) {
2777      return false;
2778    }
2779
2780    const isFromPrivateIdentifierOrSdk = this.isFromPrivateIdentifierOrSdk(isFromPrivateIdentifier);
2781    for (const classMember of tsClassLikeDecl.members) {
2782      if (targetMember === classMember) {
2783        continue;
2784      }
2785
2786      // Check constructor parameter properties.
2787      const constructorDeclarationProceedResult = this.proceedConstructorDeclaration(
2788        isFromPrivateIdentifierOrSdk,
2789        targetMember,
2790        classMember,
2791        isFromPrivateIdentifier
2792      );
2793      if (constructorDeclarationProceedResult) {
2794        return constructorDeclarationProceedResult;
2795      }
2796      if (!TsUtils.isIdentifierOrPrivateIdentifier(classMember.name)) {
2797        continue;
2798      }
2799      if (this.isPrivateIdentifierDuplicateOfIdentifier(targetMember.name, classMember.name, isFromPrivateIdentifier)) {
2800        return true;
2801      }
2802    }
2803
2804    if (isFromPrivateIdentifierOrSdk) {
2805      // eslint-disable-next-line no-param-reassign
2806      classType = classType ?? this.tsTypeChecker.getTypeAtLocation(tsClassLikeDecl);
2807      const proceedClassTypeResult = this.proceedClassType(targetMember, classType, isFromPrivateIdentifier);
2808      if (proceedClassTypeResult) {
2809        return proceedClassTypeResult;
2810      }
2811    }
2812
2813    return false;
2814  }
2815
2816  private isFromPrivateIdentifierOrSdk(isFromPrivateIdentifier: boolean): boolean {
2817    return this.options.useRtLogic || isFromPrivateIdentifier;
2818  }
2819
2820  private static isIdentifierOrPrivateIdentifier(node?: ts.PropertyName): node is ts.Identifier | ts.PrivateIdentifier {
2821    if (!node) {
2822      return false;
2823    }
2824    return ts.isIdentifier(node) || ts.isPrivateIdentifier(node);
2825  }
2826
2827  private isPrivateIdentifierDuplicateOfIdentifier(
2828    ident1: ts.Identifier | ts.PrivateIdentifier,
2829    ident2: ts.Identifier | ts.PrivateIdentifier,
2830    isFromPrivateIdentifier: boolean
2831  ): boolean {
2832    if (ts.isIdentifier(ident1) && ts.isPrivateIdentifier(ident2)) {
2833      return ident1.text === ident2.text.substring(1);
2834    }
2835    if (ts.isIdentifier(ident2) && ts.isPrivateIdentifier(ident1)) {
2836      return ident2.text === ident1.text.substring(1);
2837    }
2838    if (
2839      this.isFromPrivateIdentifierOrSdk(isFromPrivateIdentifier) &&
2840      ts.isPrivateIdentifier(ident1) &&
2841      ts.isPrivateIdentifier(ident2)
2842    ) {
2843      return ident1.text.substring(1) === ident2.text.substring(1);
2844    }
2845    return false;
2846  }
2847
2848  findIdentifierNameForSymbol(symbol: ts.Symbol, enumMember?: ts.EnumMember): string | undefined {
2849    let name = TsUtils.getIdentifierNameFromString(symbol.name);
2850    if (name === undefined || name === symbol.name) {
2851      return name;
2852    }
2853
2854    const enumExpress = enumMember?.parent;
2855    const parentType = this.getTypeByProperty(symbol);
2856    if (parentType === undefined) {
2857      return undefined;
2858    }
2859    if (enumExpress) {
2860      while (this.findEnumMember(enumExpress, name)) {
2861        name = '_' + name;
2862      }
2863    }
2864    while (this.findProperty(parentType, name) !== undefined) {
2865      name = '_' + name;
2866    }
2867
2868    return name;
2869  }
2870
2871  private static getIdentifierNameFromString(str: string): string | undefined {
2872    if (str === '') {
2873      return '__empty';
2874    }
2875    let result: string = '';
2876
2877    let offset = 0;
2878    while (offset < str.length) {
2879      const codePoint = str.codePointAt(offset);
2880      if (!codePoint) {
2881        return undefined;
2882      }
2883
2884      const charSize = TsUtils.charSize(codePoint);
2885
2886      if (offset === 0 && !ts.isIdentifierStart(codePoint, undefined)) {
2887        result = '__';
2888      }
2889
2890      if (!ts.isIdentifierPart(codePoint, undefined)) {
2891        if (codePoint === 0x20) {
2892          result += '_';
2893        } else {
2894          result += 'x' + codePoint.toString(16);
2895        }
2896      } else {
2897        for (let i = 0; i < charSize; i++) {
2898          result += str.charAt(offset + i);
2899        }
2900      }
2901
2902      offset += charSize;
2903    }
2904
2905    return result;
2906  }
2907
2908  private static charSize(codePoint: number): number {
2909    return codePoint >= 0x10000 ? 2 : 1;
2910  }
2911
2912  private getTypeByProperty(symbol: ts.Symbol): ts.Type | undefined {
2913    if (symbol.declarations === undefined) {
2914      return undefined;
2915    }
2916
2917    for (const propDecl of symbol.declarations) {
2918      if (
2919        !ts.isPropertyDeclaration(propDecl) &&
2920        !ts.isPropertyAssignment(propDecl) &&
2921        !ts.isPropertySignature(propDecl) &&
2922        !ts.isEnumMember(propDecl)
2923      ) {
2924        return undefined;
2925      }
2926
2927      const type = this.tsTypeChecker.getTypeAtLocation(propDecl.parent);
2928      if (type !== undefined) {
2929        return type;
2930      }
2931    }
2932
2933    return undefined;
2934  }
2935
2936  static isPropertyOfInternalClassOrInterface(symbol: ts.Symbol): boolean {
2937    if (symbol.declarations === undefined) {
2938      return false;
2939    }
2940
2941    for (const propDecl of symbol.declarations) {
2942      if (!ts.isPropertyDeclaration(propDecl) && !ts.isPropertySignature(propDecl) && !ts.isEnumMember(propDecl)) {
2943        return false;
2944      }
2945
2946      if (
2947        !ts.isClassDeclaration(propDecl.parent) &&
2948        !ts.isInterfaceDeclaration(propDecl.parent) &&
2949        !ts.isEnumDeclaration(propDecl.parent)
2950      ) {
2951        return false;
2952      }
2953
2954      if (TsUtils.hasModifier(ts.getModifiers(propDecl.parent), ts.SyntaxKind.ExportKeyword)) {
2955        return false;
2956      }
2957    }
2958
2959    return true;
2960  }
2961
2962  static isIntrinsicObjectType(type: ts.Type): boolean {
2963    return !!(type.flags & ts.TypeFlags.NonPrimitive);
2964  }
2965
2966  isStringType(tsType: ts.Type): boolean {
2967    if ((tsType.getFlags() & ts.TypeFlags.String) !== 0) {
2968      return true;
2969    }
2970
2971    if (!TsUtils.isTypeReference(tsType)) {
2972      return false;
2973    }
2974
2975    const symbol = tsType.symbol;
2976    if (symbol === undefined) {
2977      return false;
2978    }
2979    const name = this.tsTypeChecker.getFullyQualifiedName(symbol);
2980    return name === 'String' && this.isGlobalSymbol(symbol);
2981  }
2982
2983  isStdMapType(type: ts.Type): boolean {
2984    const sym = type.symbol;
2985    return !!sym && sym.getName() === 'Map' && this.isGlobalSymbol(sym);
2986  }
2987
2988  hasGenericTypeParameter(type: ts.Type): boolean {
2989    if (type.isUnionOrIntersection()) {
2990      return type.types.some((x) => {
2991        return this.hasGenericTypeParameter(x);
2992      });
2993    }
2994    if (TsUtils.isTypeReference(type)) {
2995      const typeArgs = this.tsTypeChecker.getTypeArguments(type);
2996      return typeArgs.some((x) => {
2997        return this.hasGenericTypeParameter(x);
2998      });
2999    }
3000    return type.isTypeParameter();
3001  }
3002
3003  static getEnclosingTopLevelStatement(node: ts.Node): ts.Node | undefined {
3004    return ts.findAncestor(node, (ancestor) => {
3005      return ts.isSourceFile(ancestor.parent);
3006    });
3007  }
3008
3009  static isDeclarationStatement(node: ts.Node): node is ts.DeclarationStatement {
3010    const kind = node.kind;
3011    return (
3012      kind === ts.SyntaxKind.FunctionDeclaration ||
3013      kind === ts.SyntaxKind.ModuleDeclaration ||
3014      kind === ts.SyntaxKind.ClassDeclaration ||
3015      kind === ts.SyntaxKind.StructDeclaration ||
3016      kind === ts.SyntaxKind.TypeAliasDeclaration ||
3017      kind === ts.SyntaxKind.InterfaceDeclaration ||
3018      kind === ts.SyntaxKind.EnumDeclaration ||
3019      kind === ts.SyntaxKind.MissingDeclaration ||
3020      kind === ts.SyntaxKind.ImportEqualsDeclaration ||
3021      kind === ts.SyntaxKind.ImportDeclaration ||
3022      kind === ts.SyntaxKind.NamespaceExportDeclaration
3023    );
3024  }
3025
3026  static declarationNameExists(srcFile: ts.SourceFile, name: string): boolean {
3027    return srcFile.statements.some((stmt) => {
3028      return (
3029        TsUtils.checkDeclarationInBlockStatement(stmt, name) ||
3030        TsUtils.checkImportDeclaration(stmt, name) ||
3031        TsUtils.checkDeclarationInLoopStatement(stmt, name) ||
3032        TsUtils.checkDeclarationInVariableStatement(stmt, name) ||
3033        TsUtils.checkGeneralDeclaration(stmt, name)
3034      );
3035    });
3036  }
3037
3038  static checkDeclarationInBlockStatement(stmt: ts.Statement, name: string): boolean {
3039    if (ts.isBlock(stmt)) {
3040      return stmt.statements.some((innerStmt) => {
3041        return (
3042          TsUtils.checkDeclarationInVariableStatement(innerStmt, name) ||
3043          TsUtils.checkGeneralDeclaration(innerStmt, name)
3044        );
3045      });
3046    }
3047    return false;
3048  }
3049
3050  static checkImportDeclaration(stmt: ts.Statement, name: string): boolean {
3051    if (ts.isImportDeclaration(stmt)) {
3052      if (!stmt.importClause) {
3053        return false;
3054      }
3055      if (stmt.importClause.namedBindings) {
3056        if (ts.isNamespaceImport(stmt.importClause.namedBindings)) {
3057          return stmt.importClause.namedBindings.name.text === name;
3058        }
3059        return stmt.importClause.namedBindings.elements.some((x) => {
3060          return x.name.text === name;
3061        });
3062      }
3063      return stmt.importClause.name?.text === name;
3064    }
3065    return false;
3066  }
3067
3068  static checkDeclarationInLoopStatement(stmt: ts.Statement, name: string): boolean {
3069    if (
3070      ts.isForStatement(stmt) ||
3071      ts.isWhileStatement(stmt) ||
3072      ts.isDoStatement(stmt) ||
3073      ts.isForOfStatement(stmt) ||
3074      ts.isForInStatement(stmt)
3075    ) {
3076      if (ts.isBlock(stmt.statement)) {
3077        return stmt.statement.statements.some((innerStmt) => {
3078          return (
3079            TsUtils.checkDeclarationInVariableStatement(innerStmt, name) ||
3080            TsUtils.checkGeneralDeclaration(innerStmt, name)
3081          );
3082        });
3083      }
3084    }
3085    return false;
3086  }
3087
3088  static checkDeclarationInVariableStatement(stmt: ts.Statement, name: string): boolean {
3089    if (ts.isVariableStatement(stmt)) {
3090      return stmt.declarationList.declarations.some((decl) => {
3091        return decl.name.getText() === name;
3092      });
3093    }
3094    return false;
3095  }
3096
3097  static checkGeneralDeclaration(stmt: ts.Statement, name: string): boolean {
3098    if (ts.isFunctionDeclaration(stmt) && stmt.body !== undefined) {
3099      return (
3100        stmt.body.statements.some((innerStmt) => {
3101          return TsUtils.checkDeclarationInVariableStatement(innerStmt, name);
3102        }) ||
3103        stmt.name !== undefined && ts.isIdentifier(stmt.name) && stmt.name.text === name
3104      );
3105    }
3106    return (
3107      TsUtils.isDeclarationStatement(stmt) &&
3108      stmt.name !== undefined &&
3109      ts.isIdentifier(stmt.name) &&
3110      stmt.name.text === name
3111    );
3112  }
3113
3114  static generateUniqueName(nameGenerator: NameGenerator, srcFile: ts.SourceFile): string | undefined {
3115    let newName: string | undefined;
3116
3117    do {
3118      newName = nameGenerator.getName();
3119      if (newName !== undefined && TsUtils.declarationNameExists(srcFile, newName)) {
3120        continue;
3121      }
3122      break;
3123    } while (newName !== undefined);
3124
3125    return newName;
3126  }
3127
3128  static isSharedModule(sourceFile: ts.SourceFile): boolean {
3129    const statements = sourceFile.statements;
3130    for (const statement of statements) {
3131      if (ts.isImportDeclaration(statement)) {
3132        continue;
3133      }
3134
3135      return (
3136        ts.isExpressionStatement(statement) &&
3137        ts.isStringLiteral(statement.expression) &&
3138        statement.expression.text === USE_SHARED
3139      );
3140    }
3141    return false;
3142  }
3143
3144  getDeclarationNode(node: ts.Node): ts.Declaration | undefined {
3145    const sym = this.trueSymbolAtLocation(node);
3146    return TsUtils.getDeclaration(sym);
3147  }
3148
3149  static isFunctionLikeDeclaration(node: ts.Declaration): boolean {
3150    return (
3151      ts.isFunctionDeclaration(node) ||
3152      ts.isMethodDeclaration(node) ||
3153      ts.isGetAccessorDeclaration(node) ||
3154      ts.isSetAccessorDeclaration(node) ||
3155      ts.isConstructorDeclaration(node) ||
3156      ts.isFunctionExpression(node) ||
3157      ts.isArrowFunction(node)
3158    );
3159  }
3160
3161  isShareableEntity(node: ts.Node): boolean {
3162    const decl = this.getDeclarationNode(node);
3163    // CC-OFFNXT(no_explicit_any) std lib
3164    const typeNode = (decl as any)?.type;
3165    return typeNode && !TsUtils.isFunctionLikeDeclaration(decl!) ?
3166      this.isSendableTypeNode(typeNode, true) :
3167      this.isShareableType(this.tsTypeChecker.getTypeAtLocation(decl ? decl : node));
3168  }
3169
3170  isSendableClassOrInterfaceEntity(node: ts.Node): boolean {
3171    const decl = this.getDeclarationNode(node);
3172    if (!decl) {
3173      return false;
3174    }
3175    if (ts.isClassDeclaration(decl)) {
3176      return TsUtils.hasSendableDecorator(decl);
3177    }
3178    if (ts.isInterfaceDeclaration(decl)) {
3179      return this.isOrDerivedFrom(this.tsTypeChecker.getTypeAtLocation(decl), TsUtils.isISendableInterface);
3180    }
3181    return false;
3182  }
3183
3184  static isInImportWhiteList(resolvedModule: ts.ResolvedModuleFull): boolean {
3185    if (
3186      !resolvedModule.resolvedFileName ||
3187      path.basename(resolvedModule.resolvedFileName).toLowerCase() !== ARKTS_LANG_D_ETS &&
3188        path.basename(resolvedModule.resolvedFileName).toLowerCase() !== ARKTS_COLLECTIONS_D_ETS
3189    ) {
3190      return false;
3191    }
3192    return true;
3193  }
3194
3195  // If it is an overloaded function, all declarations for that function are found
3196  static hasSendableDecoratorFunctionOverload(decl: ts.FunctionDeclaration): boolean {
3197    const decorators = TsUtils.getFunctionOverloadDecorators(decl);
3198    return !!decorators?.some((x) => {
3199      return TsUtils.getDecoratorName(x) === SENDABLE_DECORATOR;
3200    });
3201  }
3202
3203  static getFunctionOverloadDecorators(funcDecl: ts.FunctionDeclaration): readonly ts.Decorator[] | undefined {
3204    const decls = funcDecl.symbol.getDeclarations();
3205    if (!decls?.length) {
3206      return undefined;
3207    }
3208    let result: ts.Decorator[] = [];
3209    decls.forEach((decl) => {
3210      if (!ts.isFunctionDeclaration(decl)) {
3211        return;
3212      }
3213      const decorators = ts.getAllDecorators(decl);
3214      if (decorators?.length) {
3215        result = result.concat(decorators);
3216      }
3217    });
3218    return result.length ? result : undefined;
3219  }
3220
3221  static isSendableFunction(type: ts.Type): boolean {
3222    const callSigns = type.getCallSignatures();
3223    if (!callSigns?.length) {
3224      return false;
3225    }
3226    const decl = callSigns[0].declaration;
3227    if (!decl || !ts.isFunctionDeclaration(decl)) {
3228      return false;
3229    }
3230    return TsUtils.hasSendableDecoratorFunctionOverload(decl);
3231  }
3232
3233  isSendableTypeAlias(type: ts.Type): boolean {
3234    const decl = this.getTypsAliasOriginalDecl(type);
3235    return !!decl && TsUtils.hasSendableDecorator(decl);
3236  }
3237
3238  hasSendableTypeAlias(type: ts.Type): boolean {
3239    if (type.isUnion()) {
3240      return type.types.some((compType) => {
3241        return this.hasSendableTypeAlias(compType);
3242      });
3243    }
3244    return this.isSendableTypeAlias(type);
3245  }
3246
3247  isNonSendableFunctionTypeAlias(type: ts.Type): boolean {
3248    const decl = this.getTypsAliasOriginalDecl(type);
3249    return !!decl && ts.isFunctionTypeNode(decl.type) && !TsUtils.hasSendableDecorator(decl);
3250  }
3251
3252  // If the alias refers to another alias, the search continues
3253  private getTypsAliasOriginalDecl(type: ts.Type): ts.TypeAliasDeclaration | undefined {
3254    if (!type.aliasSymbol) {
3255      return undefined;
3256    }
3257    const decl = TsUtils.getDeclaration(type.aliasSymbol);
3258    if (!decl || !ts.isTypeAliasDeclaration(decl)) {
3259      return undefined;
3260    }
3261    if (ts.isTypeReferenceNode(decl.type)) {
3262      const targetType = this.tsTypeChecker.getTypeAtLocation(decl.type.typeName);
3263      if (targetType.aliasSymbol && targetType.aliasSymbol.getFlags() & ts.SymbolFlags.TypeAlias) {
3264        return this.getTypsAliasOriginalDecl(targetType);
3265      }
3266    }
3267    return decl;
3268  }
3269
3270  // not allow 'lhsType' contains 'sendable typeAlias' && 'rhsType' contains 'non-sendable function/non-sendable function typeAlias'
3271  isWrongSendableFunctionAssignment(lhsType: ts.Type, rhsType: ts.Type): boolean {
3272    // eslint-disable-next-line no-param-reassign
3273    lhsType = this.getNonNullableType(lhsType);
3274    // eslint-disable-next-line no-param-reassign
3275    rhsType = this.getNonNullableType(rhsType);
3276    if (!this.hasSendableTypeAlias(lhsType)) {
3277      return false;
3278    }
3279
3280    if (rhsType.isUnion()) {
3281      return rhsType.types.some((compType) => {
3282        return this.isInvalidSendableFunctionAssignmentType(compType);
3283      });
3284    }
3285    return this.isInvalidSendableFunctionAssignmentType(rhsType);
3286  }
3287
3288  private isInvalidSendableFunctionAssignmentType(type: ts.Type): boolean {
3289    if (type.aliasSymbol) {
3290      return this.isNonSendableFunctionTypeAlias(type);
3291    }
3292    if (TsUtils.isFunctionalType(type)) {
3293      return !TsUtils.isSendableFunction(type);
3294    }
3295    return false;
3296  }
3297
3298  static isSetExpression(accessExpr: ts.ElementAccessExpression): boolean {
3299    if (!ts.isBinaryExpression(accessExpr.parent)) {
3300      return false;
3301    }
3302    const binaryExpr = accessExpr.parent;
3303    return binaryExpr.operatorToken.kind === ts.SyntaxKind.EqualsToken && binaryExpr.left === accessExpr;
3304  }
3305
3306  haveSameBaseType(type1: ts.Type, type2: ts.Type): boolean {
3307    return this.tsTypeChecker.getBaseTypeOfLiteralType(type1) === this.tsTypeChecker.getBaseTypeOfLiteralType(type2);
3308  }
3309
3310  isGetIndexableType(type: ts.Type, indexType: ts.Type): boolean {
3311    const getDecls = type.getProperty('$_get')?.getDeclarations();
3312    if (getDecls?.length !== 1 || getDecls[0].kind !== ts.SyntaxKind.MethodDeclaration) {
3313      return false;
3314    }
3315    const getMethodDecl = getDecls[0] as ts.MethodDeclaration;
3316    const getParams = getMethodDecl.parameters;
3317    if (getMethodDecl.type === undefined || getParams.length !== 1 || getParams[0].type === undefined) {
3318      return false;
3319    }
3320
3321    return this.haveSameBaseType(this.tsTypeChecker.getTypeFromTypeNode(getParams[0].type), indexType);
3322  }
3323
3324  isSetIndexableType(type: ts.Type, indexType: ts.Type, valueType: ts.Type): boolean {
3325    const setProp = type.getProperty('$_set');
3326    const setDecls = setProp?.getDeclarations();
3327    if (setDecls?.length !== 1 || setDecls[0].kind !== ts.SyntaxKind.MethodDeclaration) {
3328      return false;
3329    }
3330    const setMethodDecl = setDecls[0] as ts.MethodDeclaration;
3331    const setParams = setMethodDecl.parameters;
3332    if (
3333      setMethodDecl.type !== undefined ||
3334      setParams.length !== 2 ||
3335      setParams[0].type === undefined ||
3336      setParams[1].type === undefined
3337    ) {
3338      return false;
3339    }
3340
3341    return (
3342      this.haveSameBaseType(this.tsTypeChecker.getTypeFromTypeNode(setParams[0].type), indexType) &&
3343      this.haveSameBaseType(this.tsTypeChecker.getTypeFromTypeNode(setParams[1].type), valueType)
3344    );
3345  }
3346
3347  // Search for and save the exported declaration in the specified file, re-exporting another module will not be included.
3348  searchFileExportDecl(sourceFile: ts.SourceFile, targetDecls?: ts.SyntaxKind[]): Set<ts.Node> {
3349    const exportDeclSet = new Set<ts.Node>();
3350    const appendDecl = (decl: ts.Node | undefined): void => {
3351      if (!decl || targetDecls && !targetDecls.includes(decl.kind)) {
3352        return;
3353      }
3354      exportDeclSet.add(decl);
3355    };
3356
3357    sourceFile.statements.forEach((statement: ts.Statement) => {
3358      if (ts.isExportAssignment(statement)) {
3359        // handle the case:"export default declName;"
3360        if (statement.isExportEquals) {
3361          return;
3362        }
3363        appendDecl(this.getDeclarationNode(statement.expression));
3364      } else if (ts.isExportDeclaration(statement)) {
3365        // handle the case:"export { declName1, declName2 };"
3366        if (!statement.exportClause || !ts.isNamedExports(statement.exportClause)) {
3367          return;
3368        }
3369        statement.exportClause.elements.forEach((specifier) => {
3370          appendDecl(this.getDeclarationNode(specifier.propertyName ?? specifier.name));
3371        });
3372      } else if (ts.canHaveModifiers(statement)) {
3373        // handle the case:"export const/class/function... decalName;"
3374        if (!TsUtils.hasModifier(ts.getModifiers(statement), ts.SyntaxKind.ExportKeyword)) {
3375          return;
3376        }
3377        if (!ts.isVariableStatement(statement)) {
3378          appendDecl(statement);
3379          return;
3380        }
3381        for (const exportDecl of statement.declarationList.declarations) {
3382          appendDecl(exportDecl);
3383        }
3384      }
3385    });
3386    return exportDeclSet;
3387  }
3388
3389  static isAmbientNode(node: ts.Node): boolean {
3390
3391    /* CC-OFFNXT(no_explicit_any) std lib */
3392    // Ambient flag is not exposed, so we apply dirty hack to make it visible
3393    return !!(node.flags & (ts.NodeFlags as any).Ambient);
3394  }
3395
3396  isValidSendableClassExtends(tsTypeExpr: ts.ExpressionWithTypeArguments): boolean {
3397    const expr = tsTypeExpr.expression;
3398    const sym = this.tsTypeChecker.getSymbolAtLocation(expr);
3399    if (sym && (sym.flags & ts.SymbolFlags.Class) === 0) {
3400      // handle non-class situation(local / import)
3401      if ((sym.flags & ts.SymbolFlags.Alias) !== 0) {
3402
3403        /*
3404         * Sendable class can not extends imported sendable class variable
3405         * Sendable class can extends imported sendable class
3406         */
3407        const realSym = this.tsTypeChecker.getAliasedSymbol(sym);
3408        if (realSym && (realSym.flags & ts.SymbolFlags.Class) === 0) {
3409          return false;
3410        }
3411        return true;
3412      }
3413      return false;
3414    }
3415    return true;
3416  }
3417
3418  isBuiltinClassHeritageClause(node: ts.HeritageClause): boolean {
3419    const typeNode = node.types[0].expression;
3420    if (!typeNode) {
3421      return false;
3422    }
3423
3424    const symbol = this.tsTypeChecker.getSymbolAtLocation(typeNode);
3425    return !!symbol && !isStdLibrarySymbol(symbol);
3426  }
3427
3428  isIntegerVariable(sym: ts.Symbol | undefined): boolean {
3429    if (!sym) {
3430      return false;
3431    }
3432    const decl = TsUtils.getDeclaration(sym);
3433    if (!decl || !ts.isVariableDeclaration(decl) && !ts.isPropertyDeclaration(decl) || decl.type) {
3434      return false;
3435    }
3436    const init = decl.initializer;
3437    return !!init && ts.isNumericLiteral(init) && this.isIntegerConstantValue(init);
3438  }
3439
3440  isIntegerValue(expr: ts.Expression): boolean {
3441    if (ts.isParenthesizedExpression(expr)) {
3442      return this.isIntegerValue(expr.expression);
3443    } else if (ts.isBinaryExpression(expr) && TsUtils.isArithmeticOperator(expr.operatorToken)) {
3444      return this.isIntegerValue(expr.left) && this.isIntegerValue(expr.right);
3445    } else if (
3446      (ts.isPrefixUnaryExpression(expr) || ts.isPostfixUnaryExpression(expr)) &&
3447      TsUtils.isUnaryArithmeticOperator(expr.operator)
3448    ) {
3449      return this.isIntegerValue(expr.operand);
3450    }
3451
3452    return (
3453      ts.isNumericLiteral(expr) && this.isIntegerConstantValue(expr) ||
3454      this.isIntegerVariable(this.tsTypeChecker.getSymbolAtLocation(expr))
3455    );
3456  }
3457
3458  static isAppStorageAccess(tsCallExpr: ts.CallExpression): boolean {
3459    const propertyAccessExpr = tsCallExpr.expression;
3460    if (!ts.isPropertyAccessExpression(propertyAccessExpr)) {
3461      return false;
3462    }
3463    const accessedExpr = propertyAccessExpr.expression;
3464    if (!ts.isIdentifier(accessedExpr)) {
3465      return false;
3466    }
3467
3468    if (accessedExpr.text !== 'AppStorage') {
3469      return false;
3470    }
3471
3472    return true;
3473  }
3474
3475  static isArithmeticOperator(op: ts.BinaryOperatorToken): boolean {
3476    switch (op.kind) {
3477      case ts.SyntaxKind.PlusToken:
3478      case ts.SyntaxKind.MinusToken:
3479      case ts.SyntaxKind.AsteriskToken:
3480      case ts.SyntaxKind.SlashToken:
3481        return true;
3482      default:
3483        return false;
3484    }
3485  }
3486
3487  static isUnaryArithmeticOperator(op: ts.SyntaxKind): boolean {
3488    switch (op) {
3489      case ts.SyntaxKind.PlusToken:
3490      case ts.SyntaxKind.MinusToken:
3491      case ts.SyntaxKind.PlusPlusToken:
3492      case ts.SyntaxKind.MinusMinusToken:
3493      case ts.SyntaxKind.TildeToken:
3494        return true;
3495      default:
3496        return false;
3497    }
3498  }
3499
3500  static isNumberLike(type: ts.Type, typeText: string, isEnum: boolean): boolean {
3501    const typeFlags = type.flags;
3502
3503    const isNumberLike =
3504      typeText === STRINGLITERAL_NUMBER ||
3505      typeText === STRINGLITERAL_NUMBER_ARRAY ||
3506      (typeFlags & ts.TypeFlags.NumberLiteral) !== 0 ||
3507      isEnum;
3508    return isNumberLike;
3509  }
3510
3511  static getModuleName(node: ts.Node): string | undefined {
3512    const currentFilePath = node.getSourceFile().fileName;
3513    if (!currentFilePath.includes('src')) {
3514      return undefined;
3515    }
3516
3517    /*
3518     * Assuming we are working with a path like "entry/src/main/ets/pages/test1.ets"
3519     * we are splitting at "/src" and get the first item in the list
3520     * in order to capture the "entry" group
3521     * we could have worked with an absolute path like
3522     * "home/user/projects/oh_project/entry/src/main/ets/pages/test1.sts"
3523     * in that case, after the fist split we would have
3524     * "home/user/projects/entry"
3525     * so we split once again with "/" to separate out the directories
3526     *
3527     * and get the last item which is entry or our module
3528     */
3529    const getPossibleModule = currentFilePath.split('/src')[0];
3530    if (getPossibleModule.length === 0) {
3531      return undefined;
3532    }
3533    const sanitizedDirectories = getPossibleModule.split('/');
3534    return sanitizedDirectories[sanitizedDirectories.length - 1];
3535  }
3536
3537  static checkFileExists(
3538    importIncludesModule: boolean,
3539    currentNode: ts.Node,
3540    importFilePath: string,
3541    projectPath: string,
3542    extension: string = EXTNAME_ETS
3543  ): boolean {
3544    const currentModule = TsUtils.getModuleName(currentNode);
3545
3546    /*
3547     * some imports are like this
3548     * ets/pages/test1
3549     * in this case, they are in the same module as the file we are trying to import to
3550     * so we add the current module back in
3551     */
3552    if (!importIncludesModule) {
3553      if (!currentModule) {
3554        return false;
3555      }
3556
3557      projectPath.concat(PATH_SEPARATOR + currentModule);
3558    }
3559
3560    const importedFile = path.resolve(projectPath, importFilePath + extension);
3561
3562    return fs.existsSync(importedFile);
3563  }
3564
3565  isImportedFromJS(identifier: ts.Identifier): boolean {
3566    const sym = this.trueSymbolAtLocation(identifier);
3567    const declaration = sym?.declarations?.[0];
3568    return !!declaration?.getSourceFile().fileName.endsWith(EXTNAME_JS);
3569  }
3570
3571  isPossiblyImportedFromJS(node: ts.Node): boolean {
3572    const identifier = this.findIdentifierInExpression(node);
3573    if (!identifier) {
3574      return false;
3575    }
3576
3577    // Direct import check
3578    if (this.isImportedFromJS(identifier)) {
3579      return true;
3580    }
3581
3582    // Indirect import check
3583    const originalIdentifier = this.findOriginalIdentifier(identifier);
3584    return !!originalIdentifier && this.isImportedFromJS(originalIdentifier);
3585  }
3586
3587  /**
3588   * Extracts the Identifier from the given node. returns undefined if no Identifier is found.
3589   *
3590   * Direct Identifier (foo) → Returns it.
3591   * Binary Expressions (foo + bar) → Recursively checks left and right.
3592   * Property Access (obj.foo) → Extracts obj.
3593   * Function Calls (foo()) → Extracts foo.
3594   * Array Access (arr[foo]) → Extracts foo.
3595   * Variable Declaration (let foo = ...) → Extracts foo.
3596   * Assignments (foo = ...) → Extracts foo.
3597   */
3598  findIdentifierInExpression(node: ts.Node): ts.Identifier | undefined {
3599    if (ts.isIdentifier(node)) {
3600      return node;
3601    } else if (ts.isBinaryExpression(node)) {
3602      return this.findIdentifierInExpression(node.left) || this.findIdentifierInExpression(node.right);
3603    } else if (ts.isPropertyAccessExpression(node)) {
3604      return this.findIdentifierInExpression(node.expression);
3605    } else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
3606      return this.findIdentifierInExpression(node.expression);
3607    } else if (ts.isElementAccessExpression(node)) {
3608      return this.findIdentifierInExpression(node.expression);
3609    } else if (ts.isAsExpression(node)) {
3610      return this.findIdentifierInExpression(node.expression);
3611    } else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
3612      return node.name;
3613    }
3614    return undefined;
3615  }
3616
3617  // Helper function: Find the variable declaration of an identifier -> let arr = foo.arr -> returns foo
3618  findVariableDeclaration(identifier: ts.Identifier): ts.VariableDeclaration | undefined {
3619    const sym = this.tsTypeChecker.getSymbolAtLocation(identifier);
3620    const decl = TsUtils.getDeclaration(sym);
3621    if (decl && ts.isVariableDeclaration(decl)) {
3622      return decl;
3623    }
3624    return undefined;
3625  }
3626
3627  findOriginalIdentifier(identifier: ts.Identifier): ts.Identifier | undefined {
3628    const variableDeclaration = this.findVariableDeclaration(identifier);
3629    if (!variableDeclaration?.initializer) {
3630      return undefined;
3631    }
3632
3633    const originalIdentifier = ts.isPropertyAccessExpression(variableDeclaration.initializer) ?
3634      (variableDeclaration.initializer.expression as ts.Identifier) :
3635      undefined;
3636
3637    if (!originalIdentifier) {
3638      return undefined;
3639    }
3640
3641    return originalIdentifier;
3642  }
3643
3644  findTypeOfNodeForConversion(node: ts.Node): string {
3645    // Get type of the property
3646    const type = this.tsTypeChecker.getTypeAtLocation(node);
3647    // Default
3648    let conversionMethod = '';
3649
3650    if (this.tsTypeChecker.typeToString(type) === 'boolean') {
3651      conversionMethod = '.toBoolean()';
3652    } else if (this.tsTypeChecker.typeToString(type) === 'number') {
3653      conversionMethod = '.toNumber()';
3654    } else if (this.tsTypeChecker.typeToString(type) === 'string') {
3655      conversionMethod = '.toString()';
3656    }
3657
3658    return conversionMethod;
3659  }
3660
3661  static isInsideIfCondition(node: ts.Node): boolean {
3662    let current: ts.Node | undefined = node;
3663    while (current) {
3664      if (ts.isIfStatement(current)) {
3665        if (
3666          ts.isBinaryExpression(node.parent) &&
3667          node.parent.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken
3668        ) {
3669          return false;
3670        }
3671
3672        return true;
3673      }
3674      current = current.parent;
3675    }
3676    return false;
3677  }
3678
3679  static isOhModule(path: string): boolean {
3680    return path.includes(ETS_MODULE);
3681  }
3682
3683  static isValidOhModulePath(path: string): boolean {
3684    return path.includes(VALID_OHM_COMPONENTS_MODULE_PATH);
3685  }
3686
3687  findEnumMember(enumDecl: ts.EnumDeclaration | undefined, name: string): ts.Symbol | undefined {
3688    if (!enumDecl) {
3689      return undefined;
3690    }
3691
3692    const syms: ts.Symbol[] = [];
3693    for (const enumMember of enumDecl.members) {
3694      const sym = this.trueSymbolAtLocation(enumMember.name);
3695      if (sym) {
3696        syms.push(sym);
3697      }
3698    }
3699
3700    for (const sym of syms) {
3701      if (sym.name === name) {
3702        return sym;
3703      }
3704    }
3705
3706    return undefined;
3707  }
3708
3709  isArkts12File(sourceFile: ts.SourceFile): boolean {
3710    if (!sourceFile?.fileName) {
3711      return false;
3712    }
3713    if (sourceFile.fileName.endsWith(EXTNAME_D_ETS)) {
3714      return true;
3715    }
3716    return !!this.options.inputFiles?.includes(path.normalize(sourceFile.fileName));
3717  }
3718
3719  static removeOrReplaceQuotes(str: string, isReplace: boolean): string {
3720    if (isReplace) {
3721      const charArray = new Array(str.length);
3722      for (let i = 0; i < str.length; i++) {
3723        charArray[i] = str[i] === '"' ? '\'' : str[i];
3724      }
3725      return charArray.join('');
3726    }
3727    if (str.startsWith('\'') && str.endsWith('\'') || str.startsWith('"') && str.endsWith('"')) {
3728      return str.slice(1, -1);
3729    }
3730    return str;
3731  }
3732
3733  isJsImport(node: ts.Node): boolean {
3734    const symbol = this.trueSymbolAtLocation(node);
3735    if (symbol) {
3736      const declaration = symbol.declarations?.[0];
3737      if (declaration) {
3738        const sourceFile = declaration.getSourceFile();
3739        return sourceFile.fileName.endsWith(EXTNAME_JS);
3740      }
3741    }
3742    return false;
3743  }
3744
3745  static typeIsCapturedFromEnclosingLocalScope(type: ts.Type, enclosingStmt: ts.Node): boolean {
3746    let symNode: ts.Node | undefined = TsUtils.getDeclaration(type.getSymbol());
3747
3748    while (symNode) {
3749      if (symNode === enclosingStmt) {
3750        return true;
3751      }
3752      symNode = symNode.parent;
3753    }
3754
3755    return false;
3756  }
3757
3758  nodeCapturesValueFromEnclosingLocalScope(targetNode: ts.Node, enclosingStmt: ts.Node): boolean {
3759    let found = false;
3760
3761    const callback = (node: ts.Node): void => {
3762      if (!ts.isIdentifier(node)) {
3763        return;
3764      }
3765      const sym = this.tsTypeChecker.getSymbolAtLocation(node);
3766      let symNode: ts.Node | undefined = TsUtils.getDeclaration(sym);
3767      while (symNode) {
3768        if (symNode === targetNode) {
3769          // Symbol originated from the target node. Skip and continue to search
3770          return;
3771        }
3772        if (symNode === enclosingStmt) {
3773          found = true;
3774          return;
3775        }
3776        symNode = symNode.parent;
3777      }
3778    };
3779
3780    const stopCondition = (node: ts.Node): boolean => {
3781      void node;
3782      return found;
3783    };
3784
3785    forEachNodeInSubtree(targetNode, callback, stopCondition);
3786    return found;
3787  }
3788
3789  static typeContainsVoid(typeNode: ts.TypeNode): boolean {
3790    if (ts.isUnionTypeNode(typeNode)) {
3791      return typeNode.types.some((t) => {
3792        return t.kind === ts.SyntaxKind.VoidKeyword;
3793      });
3794    }
3795    return typeNode.kind === ts.SyntaxKind.VoidKeyword;
3796  }
3797}
3798