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