• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as path from 'node:path';
17import * as ts from 'typescript';
18import { FaultID } from './Problems';
19import { TypeScriptLinterConfig } from './TypeScriptLinterConfig';
20import type { Autofix } from './autofixes/Autofixer';
21import { Autofixer } from './autofixes/Autofixer';
22import { PROMISE_METHODS, SYMBOL, SYMBOL_CONSTRUCTOR, TsUtils } from './utils/TsUtils';
23import { FUNCTION_HAS_NO_RETURN_ERROR_CODE } from './utils/consts/FunctionHasNoReturnErrorCode';
24import { LIMITED_STANDARD_UTILITY_TYPES } from './utils/consts/LimitedStandardUtilityTypes';
25import { LIKE_FUNCTION } from './utils/consts/LikeFunction';
26import { METHOD_DECLARATION } from './utils/consts/MethodDeclaration';
27import { METHOD_SIGNATURE } from './utils/consts/MethodSignature';
28import { OPTIONAL_METHOD } from './utils/consts/OptionalMethod';
29import {
30  STRINGLITERAL_NUMBER,
31  STRINGLITERAL_STRING,
32  STRINGLITERAL_INT,
33  STRINGLITERAL_BYTE,
34  STRINGLITERAL_SHORT,
35  STRINGLITERAL_CHAR,
36  STRINGLITERAL_LONG,
37  STRINGLITERAL_FROM,
38  STRINGLITERAL_ARRAY
39} from './utils/consts/StringLiteral';
40import {
41  NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS,
42  NON_INITIALIZABLE_PROPERTY_DECORATORS,
43  NON_INITIALIZABLE_PROPERTY_DECORATORS_TSC
44} from './utils/consts/NonInitializablePropertyDecorators';
45import { NON_RETURN_FUNCTION_DECORATORS } from './utils/consts/NonReturnFunctionDecorators';
46import { PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE } from './utils/consts/PropertyHasNoInitializerErrorCode';
47import {
48  CONCURRENT_DECORATOR,
49  ISCONCURRENT,
50  SENDABLE_DECORATOR,
51  SENDABLE_DECORATOR_NODES,
52  SENDABLE_FUNCTION_UNSUPPORTED_STAGES_IN_API12,
53  SENDBALE_FUNCTION_START_VERSION,
54  TASKPOOL
55} from './utils/consts/SendableAPI';
56import { DEFAULT_COMPATIBLE_SDK_VERSION, DEFAULT_COMPATIBLE_SDK_VERSION_STAGE } from './utils/consts/VersionInfo';
57import { forEachNodeInSubtree } from './utils/functions/ForEachNodeInSubtree';
58import { hasPredecessor } from './utils/functions/HasPredecessor';
59import { isStdLibrarySymbol, isStdLibraryType } from './utils/functions/IsStdLibrary';
60import { isStruct, isStructDeclaration } from './utils/functions/IsStruct';
61import {
62  LibraryTypeCallDiagnosticChecker,
63  ErrorType as DiagnosticCheckerErrorType
64} from './utils/functions/LibraryTypeCallDiagnosticChecker';
65import {
66  ALLOWED_STD_SYMBOL_API,
67  LIMITED_STD_API,
68  LIMITED_STD_GLOBAL_API,
69  LIMITED_STD_OBJECT_API,
70  LIMITED_STD_PROXYHANDLER_API,
71  LIMITED_STD_REFLECT_API,
72  MODULE_IMPORTS,
73  ARKTSUTILS_MODULES,
74  ARKTSUTILS_LOCKS_MEMBER
75} from './utils/consts/LimitedStdAPI';
76import { SupportedStdCallApiChecker } from './utils/functions/SupportedStdCallAPI';
77import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext';
78import { isAssignmentOperator } from './utils/functions/isAssignmentOperator';
79import { StdClassVarDecls } from './utils/consts/StdClassVariableDeclarations';
80import type { LinterOptions } from './LinterOptions';
81import { BUILTIN_GENERIC_CONSTRUCTORS } from './utils/consts/BuiltinGenericConstructor';
82import { DEFAULT_DECORATOR_WHITE_LIST } from './utils/consts/DefaultDecoratorWhitelist';
83import { INVALID_IDENTIFIER_KEYWORDS } from './utils/consts/InValidIndentifierKeywords';
84import { WORKER_MODULES, WORKER_TEXT } from './utils/consts/WorkerAPI';
85import { COLLECTIONS_TEXT, COLLECTIONS_MODULES } from './utils/consts/CollectionsAPI';
86import { ASON_TEXT, ASON_MODULES, ARKTS_UTILS_TEXT, JSON_TEXT, ASON_WHITE_SET } from './utils/consts/ArkTSUtilsAPI';
87import { interanlFunction } from './utils/consts/InternalFunction';
88import { ETS_PART, PATH_SEPARATOR } from './utils/consts/OhmUrl';
89import {
90  DOUBLE_DOLLAR_IDENTIFIER,
91  THIS_IDENTIFIER,
92  STATE_STYLES,
93  CustomDecoratorName,
94  observedDecoratorName,
95  skipImportDecoratorName,
96  ENTRY_DECORATOR_NAME,
97  PROVIDE_DECORATOR_NAME,
98  PROVIDE_ALLOW_OVERRIDE_PROPERTY_NAME,
99  ARKUI_PACKAGE_NAME,
100  MAKE_OBSERVED,
101  ARKUI_STATE_MANAGEMENT,
102  PropDecoratorName,
103  PropFunctionName,
104  StorageTypeName,
105  customLayoutFunctionName
106} from './utils/consts/ArkuiConstants';
107import { arkuiImportList } from './utils/consts/ArkuiImportList';
108import type { IdentifierAndArguments, ForbidenAPICheckResult } from './utils/consts/InteropAPI';
109import {
110  NONE,
111  OBJECT_LITERAL,
112  OBJECT_PROPERTIES,
113  REFLECT_LITERAL,
114  REFLECT_PROPERTIES
115} from './utils/consts/InteropAPI';
116import { EXTNAME_TS, EXTNAME_D_TS, EXTNAME_JS } from './utils/consts/ExtensionName';
117import { ARKTS_IGNORE_DIRS_OH_MODULES } from './utils/consts/ArktsIgnorePaths';
118import type { ApiInfo, ApiListItem } from './utils/consts/SdkWhitelist';
119import { ApiList, SdkProblem, SdkNameInfo } from './utils/consts/SdkWhitelist';
120import * as apiWhiteList from './data/SdkWhitelist.json';
121import * as builtinWhiteList from './data/BuiltinList.json';
122import {
123  BuiltinProblem,
124  SYMBOL_ITERATOR,
125  BUILTIN_DISABLE_CALLSIGNATURE,
126  GET_OWN_PROPERTY_NAMES_TEXT
127} from './utils/consts/BuiltinWhiteList';
128import {
129  USE_SHARED,
130  USE_CONCURRENT,
131  ESLIB_SHAREDMEMORY_FILENAME,
132  ESLIB_SHAREDARRAYBUFFER,
133  TASKPOOL_MODULES
134} from './utils/consts/ConcurrentAPI';
135import {
136  DEPRECATED_TASKPOOL_METHOD_SETCLONELIST,
137  DEPRECATED_TASKPOOL_METHOD_SETTRANSFERLIST,
138  STDLIB_TASK_CLASS_NAME,
139  STDLIB_TASKPOOL_OBJECT_NAME
140} from './utils/consts/TaskpoolAPI';
141import { BaseTypeScriptLinter } from './BaseTypeScriptLinter';
142import type { ArrayAccess, UncheckedIdentifier } from './utils/consts/RuntimeCheckAPI';
143import { NUMBER_LITERAL } from './utils/consts/RuntimeCheckAPI';
144import { globalApiAssociatedInfo } from './utils/consts/AssociatedInfo';
145import { ARRAY_API_LIST } from './utils/consts/ArraysAPI';
146import {
147  ABILITY_KIT,
148  ASYNC_LIFECYCLE_SDK_LIST,
149  ON_DESTROY,
150  ON_DISCONNECT,
151  PROMISE,
152  SERVICE_EXTENSION_ABILITY,
153  VOID
154} from './utils/consts/AsyncLifecycleSDK';
155import { ERROR_PROP_LIST } from './utils/consts/ErrorProp';
156import { D_ETS, D_TS } from './utils/consts/TsSuffix';
157import { arkTsBuiltInTypeName } from './utils/consts/ArkuiImportList';
158import { ERROR_TASKPOOL_PROP_LIST } from './utils/consts/ErrorProp';
159
160interface InterfaceSymbolTypeResult {
161  propNames: string[];
162  typeNames: string[];
163  allProps: Map<string, string>;
164}
165interface InterfaceSymbolTypePropertyNames {
166  propertyNames: string[];
167  typeNames: string[];
168}
169
170export class TypeScriptLinter extends BaseTypeScriptLinter {
171  supportedStdCallApiChecker: SupportedStdCallApiChecker;
172
173  autofixer: Autofixer | undefined;
174  private fileExportDeclCaches: Set<ts.Node> | undefined;
175
176  private useStatic?: boolean;
177
178  private readonly compatibleSdkVersion: number;
179  private readonly compatibleSdkVersionStage: string;
180  private static sharedModulesCache: Map<string, boolean>;
181  static nameSpaceFunctionCache: Map<string, Set<string>>;
182  private readonly constVariableInitCache: Map<ts.Symbol, number | null> = new Map();
183  static funcMap: Map<string, Map<string, Set<ApiInfo>>> = new Map<string, Map<string, Set<ApiInfo>>>();
184  private interfaceMap: Map<string, Set<ApiInfo>> = new Map<string, Set<ApiInfo>>();
185  static pathMap: Map<string, Set<ApiInfo>>;
186  static indexedTypeSet: Set<ApiListItem>;
187  static globalApiInfo: Map<string, Set<ApiListItem>>;
188  static symbotIterSet: Set<string>;
189  static missingAttributeSet: Set<string>;
190  static literalAsPropertyNameTypeSet: Set<ApiListItem>;
191  private localApiListItem: ApiListItem | undefined = undefined;
192  static constructorFuncsSet: Set<ApiListItem>;
193  static ConstructorIfaceSet: Set<ApiListItem>;
194
195  static initGlobals(): void {
196    TypeScriptLinter.sharedModulesCache = new Map<string, boolean>();
197    TypeScriptLinter.nameSpaceFunctionCache = new Map<string, Set<string>>();
198    TypeScriptLinter.pathMap = new Map<string, Set<ApiInfo>>();
199    TypeScriptLinter.globalApiInfo = new Map<string, Set<ApiListItem>>();
200    TypeScriptLinter.funcMap = new Map<string, Map<string, Set<ApiInfo>>>();
201    TypeScriptLinter.symbotIterSet = new Set<string>();
202    TypeScriptLinter.missingAttributeSet = new Set<string>();
203    TypeScriptLinter.initSdkWhitelist();
204    TypeScriptLinter.initSdkBuiltinInfo();
205    TypeScriptLinter.initBuiltinlist();
206  }
207
208  initSdkInfo(): void {
209    this.interfaceMap = new Map<string, Set<ApiInfo>>();
210  }
211
212  static initSdkBuiltinInfo(): void {
213    const list: ApiList = new ApiList(builtinWhiteList);
214    if (list?.api_list?.length > 0) {
215      for (const item of list.api_list) {
216        switch (item.api_info.problem) {
217          case BuiltinProblem.MissingAttributes:
218            TypeScriptLinter.missingAttributeSet.add(item.file_path);
219            break;
220          case BuiltinProblem.SymbolIterator:
221            TypeScriptLinter.symbotIterSet.add(item.file_path);
222            break;
223          case BuiltinProblem.LimitedThisArg:
224            TypeScriptLinter.initSdkBuiltinThisArgsWhitelist(item);
225            break;
226          default:
227        }
228      }
229    }
230  }
231
232  static initSdkBuiltinThisArgsWhitelist(item: ApiListItem): void {
233    if (item.file_path === '' || !item.api_info.api_name) {
234      return;
235    }
236
237    let funcApiInfos: Map<string, Set<ApiInfo>> | undefined = TypeScriptLinter.funcMap.get(item.api_info.api_name);
238    if (!funcApiInfos) {
239      funcApiInfos = new Map<string, Set<ApiInfo>>();
240      TypeScriptLinter.funcMap.set(item.api_info.api_name, funcApiInfos);
241    }
242    TypeScriptLinter.addOrUpdateData(funcApiInfos, item.file_path, item.api_info);
243  }
244
245  private initEtsHandlers(): void {
246
247    /*
248     * some syntax elements are ArkTs-specific and are only implemented inside patched
249     * compiler, so we initialize those handlers if corresponding properties do exist
250     */
251    const etsComponentExpression: ts.SyntaxKind | undefined = ts.SyntaxKind.EtsComponentExpression;
252    if (etsComponentExpression) {
253      this.handlersMap.set(etsComponentExpression, this.handleEtsComponentExpression);
254    }
255  }
256
257  private static addSdkIndexedTypeSetData(item: ApiListItem): void {
258    if (item.api_info.problem === SdkProblem.IndexedAccessType) {
259      TypeScriptLinter.indexedTypeSet.add(item);
260    }
261  }
262
263  private static addSdkliteralAsPropertyNameTypeSetData(item: ApiListItem): void {
264    if (item.api_info.problem === SdkProblem.LiteralAsPropertyName) {
265      TypeScriptLinter.literalAsPropertyNameTypeSet.add(item);
266    }
267  }
268
269  private static addSdkConstructorFuncsSetData(item: ApiListItem): void {
270    if (item.api_info.problem === SdkProblem.ConstructorFuncs) {
271      TypeScriptLinter.constructorFuncsSet.add(item);
272    }
273  }
274
275  private static addGlobalApiInfosCollocetionData(item: ApiListItem): void {
276    const problemType = item.api_info.problem;
277    const isGlobal = item.is_global;
278    if (isGlobal) {
279      if (!TypeScriptLinter.globalApiInfo.has(problemType)) {
280        TypeScriptLinter.globalApiInfo.set(problemType, new Set<ApiListItem>());
281      }
282      const setApiListItem = TypeScriptLinter.globalApiInfo.get(problemType);
283      setApiListItem?.add(item);
284    }
285  }
286
287  private static addSdkConstructorIfaceSetData(item: ApiListItem): void {
288    if (item.api_info.problem === SdkProblem.ConstructorIface) {
289      TypeScriptLinter.ConstructorIfaceSet.add(item);
290    }
291  }
292
293  private static initSdkWhitelist(): void {
294    TypeScriptLinter.indexedTypeSet = new Set<ApiListItem>();
295    TypeScriptLinter.literalAsPropertyNameTypeSet = new Set<ApiListItem>();
296    TypeScriptLinter.constructorFuncsSet = new Set<ApiListItem>();
297    const list: ApiList = new ApiList(apiWhiteList);
298    TypeScriptLinter.ConstructorIfaceSet = new Set<ApiListItem>();
299    if (list?.api_list?.length > 0) {
300      for (const item of list.api_list) {
301        if (item.file_path !== '') {
302          TypeScriptLinter.addOrUpdateData(TypeScriptLinter.pathMap, `'${item.file_path}'`, item.api_info);
303        }
304        item.import_path.forEach((path) => {
305          TypeScriptLinter.addOrUpdateData(TypeScriptLinter.pathMap, `'${path}'`, item.api_info);
306        });
307        TypeScriptLinter.addSdkIndexedTypeSetData(item);
308        TypeScriptLinter.addSdkliteralAsPropertyNameTypeSetData(item);
309        TypeScriptLinter.addSdkConstructorFuncsSetData(item);
310        TypeScriptLinter.addGlobalApiInfosCollocetionData(item);
311        TypeScriptLinter.addSdkConstructorIfaceSetData(item);
312      }
313    }
314  }
315
316  private static initBuiltinlist(): void {
317    const list: ApiList = new ApiList(builtinWhiteList);
318    if (list?.api_list?.length > 0) {
319      for (const item of list.api_list) {
320        TypeScriptLinter.addGlobalApiInfosCollocetionData(item);
321      }
322    }
323  }
324
325  private static addOrUpdateData(map: Map<string, Set<ApiInfo>>, path: string, data: ApiInfo): void {
326    let apiInfos = map.get(path);
327    if (!apiInfos) {
328      apiInfos = new Set<ApiInfo>();
329      map.set(path, apiInfos);
330    }
331    apiInfos.add(data);
332  }
333
334  constructor(
335    tsTypeChecker: ts.TypeChecker,
336    options: LinterOptions,
337    sourceFile: ts.SourceFile,
338    readonly tscStrictDiagnostics?: Map<string, ts.Diagnostic[]>
339  ) {
340    super(tsTypeChecker, options, sourceFile);
341    this.supportedStdCallApiChecker = new SupportedStdCallApiChecker(this.tsUtils, this.tsTypeChecker);
342    this.compatibleSdkVersion = options.compatibleSdkVersion || DEFAULT_COMPATIBLE_SDK_VERSION;
343    this.compatibleSdkVersionStage = options.compatibleSdkVersionStage || DEFAULT_COMPATIBLE_SDK_VERSION_STAGE;
344    this.initEtsHandlers();
345    this.initSdkInfo();
346  }
347
348  readonly handlersMap = new Map([
349    [ts.SyntaxKind.ObjectLiteralExpression, this.handleObjectLiteralExpression],
350    [ts.SyntaxKind.ArrayLiteralExpression, this.handleArrayLiteralExpression],
351    [ts.SyntaxKind.Parameter, this.handleParameter],
352    [ts.SyntaxKind.EnumDeclaration, this.handleEnumDeclaration],
353    [ts.SyntaxKind.InterfaceDeclaration, this.handleInterfaceDeclaration],
354    [ts.SyntaxKind.TryStatement, this.handleTryStatement],
355    [ts.SyntaxKind.ThrowStatement, this.handleThrowStatement],
356    [ts.SyntaxKind.ImportClause, this.handleImportClause],
357    [ts.SyntaxKind.ForStatement, this.handleForStatement],
358    [ts.SyntaxKind.ForInStatement, this.handleForInStatement],
359    [ts.SyntaxKind.ForOfStatement, this.handleForOfStatement],
360    [ts.SyntaxKind.ImportDeclaration, this.handleImportDeclaration],
361    [ts.SyntaxKind.PropertyAccessExpression, this.handlePropertyAccessExpression],
362    [ts.SyntaxKind.PropertyDeclaration, this.handlePropertyDeclaration],
363    [ts.SyntaxKind.PropertyAssignment, this.handlePropertyAssignment],
364    [ts.SyntaxKind.PropertySignature, this.handlePropertySignature],
365    [ts.SyntaxKind.FunctionExpression, this.handleFunctionExpression],
366    [ts.SyntaxKind.ArrowFunction, this.handleArrowFunction],
367    [ts.SyntaxKind.CatchClause, this.handleCatchClause],
368    [ts.SyntaxKind.FunctionDeclaration, this.handleFunctionDeclaration],
369    [ts.SyntaxKind.PrefixUnaryExpression, this.handlePrefixUnaryExpression],
370    [ts.SyntaxKind.BinaryExpression, this.handleBinaryExpression],
371    [ts.SyntaxKind.VariableDeclarationList, this.handleVariableDeclarationList],
372    [ts.SyntaxKind.VariableDeclaration, this.handleVariableDeclaration],
373    [ts.SyntaxKind.ClassDeclaration, this.handleClassDeclaration],
374    [ts.SyntaxKind.ModuleDeclaration, this.handleModuleDeclaration],
375    [ts.SyntaxKind.TypeAliasDeclaration, this.handleTypeAliasDeclaration],
376    [ts.SyntaxKind.ImportSpecifier, this.handleImportSpecifier],
377    [ts.SyntaxKind.NamespaceImport, this.handleNamespaceImport],
378    [ts.SyntaxKind.TypeAssertionExpression, this.handleTypeAssertionExpression],
379    [ts.SyntaxKind.MethodDeclaration, this.handleMethodDeclaration],
380    [ts.SyntaxKind.TupleType, this.handleTupleType],
381    [ts.SyntaxKind.MethodSignature, this.handleMethodSignature],
382    [ts.SyntaxKind.ClassStaticBlockDeclaration, this.handleClassStaticBlockDeclaration],
383    [ts.SyntaxKind.Identifier, this.handleIdentifier],
384    [ts.SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression],
385    [ts.SyntaxKind.EnumMember, this.handleEnumMember],
386    [ts.SyntaxKind.TypeReference, this.handleTypeReference],
387    [ts.SyntaxKind.ExportAssignment, this.handleExportAssignment],
388    [ts.SyntaxKind.CallExpression, this.handleCallExpression],
389    [ts.SyntaxKind.MetaProperty, this.handleMetaProperty],
390    [ts.SyntaxKind.NewExpression, this.handleNewExpression],
391    [ts.SyntaxKind.AsExpression, this.handleAsExpression],
392    [ts.SyntaxKind.SpreadElement, this.handleSpreadOp],
393    [ts.SyntaxKind.SpreadAssignment, this.handleSpreadOp],
394    [ts.SyntaxKind.GetAccessor, this.handleGetAccessor],
395    [ts.SyntaxKind.SetAccessor, this.handleSetAccessor],
396    [ts.SyntaxKind.StringLiteral, this.handleStringLiteral],
397    [ts.SyntaxKind.ConstructSignature, this.handleConstructSignature],
398    [ts.SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments],
399    [ts.SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName],
400    [ts.SyntaxKind.Constructor, this.handleConstructorDeclaration],
401    [ts.SyntaxKind.PrivateIdentifier, this.handlePrivateIdentifier],
402    [ts.SyntaxKind.IndexSignature, this.handleIndexSignature],
403    [ts.SyntaxKind.TypeLiteral, this.handleTypeLiteral],
404    [ts.SyntaxKind.ExportKeyword, this.handleExportKeyword],
405    [ts.SyntaxKind.ExportDeclaration, this.handleExportDeclaration],
406    [ts.SyntaxKind.ReturnStatement, this.handleReturnStatement],
407    [ts.SyntaxKind.Decorator, this.handleDecorator],
408    [ts.SyntaxKind.ImportType, this.handleImportType],
409    [ts.SyntaxKind.AsteriskAsteriskToken, this.handleExponentOperation],
410    [ts.SyntaxKind.VoidExpression, this.handleVoidExpression],
411    [ts.SyntaxKind.AsteriskAsteriskEqualsToken, this.handleExponentOperation],
412    [ts.SyntaxKind.RegularExpressionLiteral, this.handleRegularExpressionLiteral],
413    [ts.SyntaxKind.DebuggerStatement, this.handleDebuggerStatement],
414    [ts.SyntaxKind.SwitchStatement, this.handleSwitchStatement],
415    [ts.SyntaxKind.UnionType, this.handleUnionType],
416    [ts.SyntaxKind.ArrayType, this.handleArrayType],
417    [ts.SyntaxKind.LiteralType, this.handleLimitedLiteralType],
418    [ts.SyntaxKind.NonNullExpression, this.handleNonNullExpression],
419    [ts.SyntaxKind.HeritageClause, this.handleHeritageClause],
420    [ts.SyntaxKind.TaggedTemplateExpression, this.handleTaggedTemplatesExpression],
421    [ts.SyntaxKind.StructDeclaration, this.handleStructDeclaration],
422    [ts.SyntaxKind.TypeOfExpression, this.handleInterOpImportJsOnTypeOfNode],
423    [ts.SyntaxKind.AwaitExpression, this.handleAwaitExpression],
424    [ts.SyntaxKind.PostfixUnaryExpression, this.handlePostfixUnaryExpression],
425    [ts.SyntaxKind.BigIntLiteral, this.handleBigIntLiteral],
426    [ts.SyntaxKind.NumericLiteral, this.handleNumericLiteral]
427  ]);
428
429  lint(): void {
430    if (this.options.enableAutofix || this.options.migratorMode) {
431      this.autofixer = new Autofixer(this.tsTypeChecker, this.tsUtils, this.sourceFile, this.options.cancellationToken);
432    }
433
434    this.useStatic = this.tsUtils.isArkts12File(this.sourceFile);
435    this.fileExportDeclCaches = undefined;
436    this.extractImportedNames(this.sourceFile);
437    this.visitSourceFile(this.sourceFile);
438    this.handleCommentDirectives(this.sourceFile);
439    this.processInterfacesToImport(this.sourceFile);
440  }
441
442  private visitSourceFile(sf: ts.SourceFile): void {
443    const callback = (node: ts.Node): void => {
444      this.fileStats.visitedNodes++;
445      if (isStructDeclaration(node)) {
446        // early exit via exception if cancellation was requested
447        this.options.cancellationToken?.throwIfCancellationRequested();
448      }
449      const incrementedType = TypeScriptLinterConfig.incrementOnlyTokens.get(node.kind);
450      if (incrementedType !== undefined) {
451        this.incrementCounters(node, incrementedType);
452      } else {
453        const handler = this.handlersMap.get(node.kind);
454        if (handler !== undefined) {
455
456          /*
457           * possibly requested cancellation will be checked in a limited number of handlers
458           * checked nodes are selected as construct nodes, similar to how TSC does
459           */
460          handler.call(this, node);
461        }
462      }
463    };
464    const stopCondition = (node: ts.Node): boolean => {
465      if (!node) {
466        return true;
467      }
468      if (this.options.incrementalLintInfo?.shouldSkipCheck(node)) {
469        return true;
470      }
471      // Skip synthetic constructor in Struct declaration.
472      if (node.parent && isStructDeclaration(node.parent) && ts.isConstructorDeclaration(node)) {
473        return true;
474      }
475      if (TypeScriptLinterConfig.terminalTokens.has(node.kind)) {
476        return true;
477      }
478      return false;
479    };
480    forEachNodeInSubtree(sf, callback, stopCondition);
481  }
482
483  private countInterfaceExtendsDifferentPropertyTypes(
484    node: ts.Node,
485    prop2type: Map<string, string>,
486    propName: string,
487    type: ts.TypeNode | undefined
488  ): void {
489    if (type) {
490      const methodType = type.getText();
491      const propType = prop2type.get(propName);
492      if (!propType) {
493        prop2type.set(propName, methodType);
494      } else if (propType !== methodType) {
495        this.incrementCounters(node, FaultID.IntefaceExtendDifProps);
496      }
497    }
498  }
499
500  private countDeclarationsWithDuplicateName(tsNode: ts.Node, tsDeclNode: ts.Node, tsDeclKind?: ts.SyntaxKind): void {
501    const symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode);
502
503    /*
504     * If specific declaration kind is provided, check against it.
505     * Otherwise, use syntax kind of corresponding declaration node.
506     */
507    if (!!symbol && TsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) {
508      this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName);
509    }
510  }
511
512  private countClassMembersWithDuplicateName(tsClassDecl: ts.ClassDeclaration): void {
513    for (const currentMember of tsClassDecl.members) {
514      if (this.tsUtils.classMemberHasDuplicateName(currentMember, tsClassDecl, false)) {
515        this.incrementCounters(currentMember, FaultID.DeclWithDuplicateName);
516      }
517    }
518  }
519
520  private isPrototypePropertyAccess(
521    tsPropertyAccess: ts.PropertyAccessExpression,
522    propAccessSym: ts.Symbol | undefined,
523    baseExprSym: ts.Symbol | undefined,
524    baseExprType: ts.Type
525  ): boolean {
526    if (!(ts.isIdentifier(tsPropertyAccess.name) && tsPropertyAccess.name.text === 'prototype')) {
527      return false;
528    }
529
530    // #13600: Relax prototype check when expression comes from interop.
531    let curPropAccess: ts.Node = tsPropertyAccess;
532    while (curPropAccess && ts.isPropertyAccessExpression(curPropAccess)) {
533      const baseExprSym = this.tsUtils.trueSymbolAtLocation(curPropAccess.expression);
534      if (this.tsUtils.isLibrarySymbol(baseExprSym)) {
535        return false;
536      }
537      curPropAccess = curPropAccess.expression;
538    }
539
540    if (ts.isIdentifier(curPropAccess) && curPropAccess.text !== 'prototype') {
541      const type = this.tsTypeChecker.getTypeAtLocation(curPropAccess);
542      if (TsUtils.isAnyType(type)) {
543        return false;
544      }
545    }
546
547    // Check if property symbol is 'Prototype'
548    if (TsUtils.isPrototypeSymbol(propAccessSym)) {
549      return true;
550    }
551    // Check if symbol of LHS-expression is Class or Function.
552    if (TsUtils.isTypeSymbol(baseExprSym) || TsUtils.isFunctionSymbol(baseExprSym)) {
553      return true;
554    }
555
556    /*
557     * Check if type of LHS expression Function type or Any type.
558     * The latter check is to cover cases with multiple prototype
559     * chain (as the 'Prototype' property should be 'Any' type):
560     *      X.prototype.prototype.prototype = ...
561     */
562    const baseExprTypeNode = this.tsTypeChecker.typeToTypeNode(baseExprType, undefined, ts.NodeBuilderFlags.None);
563    return baseExprTypeNode && ts.isFunctionTypeNode(baseExprTypeNode) || TsUtils.isAnyType(baseExprType);
564  }
565
566  private interfaceInheritanceLint(node: ts.Node, heritageClauses: ts.NodeArray<ts.HeritageClause>): void {
567    for (const hClause of heritageClauses) {
568      if (hClause.token !== ts.SyntaxKind.ExtendsKeyword) {
569        continue;
570      }
571      const prop2type = new Map<string, string>();
572      for (const tsTypeExpr of hClause.types) {
573        const tsExprType = this.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression);
574        if (tsExprType.isClass()) {
575          this.incrementCounters(tsTypeExpr, FaultID.InterfaceExtendsClass);
576        } else if (tsExprType.isClassOrInterface()) {
577          this.lintForInterfaceExtendsDifferentPorpertyTypes(node, tsExprType, prop2type);
578        }
579      }
580    }
581  }
582
583  private lintForInterfaceExtendsDifferentPorpertyTypes(
584    node: ts.Node,
585    tsExprType: ts.Type,
586    prop2type: Map<string, string>
587  ): void {
588    const props = tsExprType.getProperties();
589    for (const p of props) {
590      if (!p.declarations) {
591        continue;
592      }
593      const decl: ts.Declaration = p.declarations[0];
594      const isPropertyDecl = ts.isPropertySignature(decl) || ts.isPropertyDeclaration(decl);
595      const isMethodDecl = ts.isMethodSignature(decl) || ts.isMethodDeclaration(decl);
596      if (isMethodDecl || isPropertyDecl) {
597        this.countInterfaceExtendsDifferentPropertyTypes(node, prop2type, p.name, decl.type);
598      }
599    }
600  }
601
602  private handleObjectLiteralExpression(node: ts.Node): void {
603    const objectLiteralExpr = node as ts.ObjectLiteralExpression;
604    // If object literal is a part of destructuring assignment, then don't process it further.
605    if (TsUtils.isDestructuringAssignmentLHS(objectLiteralExpr)) {
606      return;
607    }
608
609    const objectLiteralType = this.tsTypeChecker.getContextualType(objectLiteralExpr);
610    if (objectLiteralType && this.tsUtils.typeContainsSendableClassOrInterface(objectLiteralType)) {
611      this.incrementCounters(node, FaultID.SendableObjectInitialization);
612    } else if (
613      // issue 13082: Allow initializing struct instances with object literal.
614      !this.tsUtils.isStructObjectInitializer(objectLiteralExpr) &&
615      !this.tsUtils.isDynamicLiteralInitializer(objectLiteralExpr) &&
616      !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, objectLiteralExpr)
617    ) {
618      const autofix = this.autofixer?.fixUntypedObjectLiteral(objectLiteralExpr, objectLiteralType);
619      this.incrementCounters(node, FaultID.ObjectLiteralNoContextType, autofix);
620    }
621
622    if (this.options.arkts2) {
623      this.handleObjectLiteralProperties(objectLiteralType, objectLiteralExpr);
624    }
625  }
626
627  static ifValidObjectLiteralProperty(
628    prop: ts.ObjectLiteralElementLike,
629    objLitExpr: ts.ObjectLiteralExpression
630  ): boolean {
631    return (
632      ts.isPropertyAssignment(prop) ||
633      ts.isShorthandPropertyAssignment(prop) &&
634        (ts.isCallExpression(objLitExpr.parent) || ts.isNewExpression(objLitExpr.parent))
635    );
636  }
637
638  private handleObjectLiteralProperties(
639    objectLiteralType: ts.Type | undefined,
640    objectLiteralExpr: ts.ObjectLiteralExpression
641  ): void {
642    let objLiteralAutofix: Autofix[] | undefined;
643    const invalidProps = objectLiteralExpr.properties.filter((prop) => {
644      return !TypeScriptLinter.ifValidObjectLiteralProperty(prop, objectLiteralExpr);
645    });
646
647    if (
648      invalidProps.some((prop) => {
649        return ts.isMethodDeclaration(prop) || ts.isAccessor(prop);
650      })
651    ) {
652      objLiteralAutofix = this.autofixer?.fixTypedObjectLiteral(objectLiteralExpr, objectLiteralType);
653    }
654
655    for (const prop of invalidProps) {
656      const autofix = ts.isShorthandPropertyAssignment(prop) ?
657        this.autofixer?.fixShorthandPropertyAssignment(prop) :
658        objLiteralAutofix;
659      this.incrementCounters(prop, FaultID.ObjectLiteralProperty, autofix);
660    }
661  }
662
663  private handleArrayLiteralExpression(node: ts.Node): void {
664
665    /*
666     * If array literal is a part of destructuring assignment, then
667     * don't process it further.
668     */
669    if (TsUtils.isDestructuringAssignmentLHS(node as ts.ArrayLiteralExpression)) {
670      return;
671    }
672    const arrayLitNode = node as ts.ArrayLiteralExpression;
673    const arrayLitType = this.tsTypeChecker.getContextualType(arrayLitNode);
674    if (arrayLitType && this.tsUtils.typeContainsSendableClassOrInterface(arrayLitType)) {
675      this.incrementCounters(node, FaultID.SendableObjectInitialization);
676      return;
677    }
678
679    this.checkArrayElementsAndReportErrors(node, arrayLitNode, arrayLitType);
680
681    this.handleObjectLiteralAssignmentToClass(arrayLitNode);
682  }
683
684  private checkArrayElementsAndReportErrors(
685    node: ts.Node,
686    arrayLitNode: ts.ArrayLiteralExpression,
687    arrayLitType: undefined | ts.Type
688  ): void {
689    const parent = arrayLitNode.parent;
690    const arrayLitElements = arrayLitNode.elements;
691    const arrayElementIsEmpty = arrayLitElements.length === 0;
692    let emptyContextTypeForArrayLiteral = false;
693
694    /*
695     * check that array literal consists of inferrable types
696     * e.g. there is no element which is untyped object literals
697     */
698    const isPromiseEmptyArray = this.checkPromiseEmptyArray(parent, arrayElementIsEmpty);
699    const isEmptyArray = this.options.arkts2 && !arrayLitType && arrayElementIsEmpty;
700    if (isPromiseEmptyArray) {
701      this.incrementCounters(arrayLitNode, FaultID.NosparseArray);
702    } else if (isEmptyArray) {
703      this.incrementCounters(node, FaultID.NosparseArray);
704    }
705
706    for (const element of arrayLitElements) {
707      const elementContextType = this.tsTypeChecker.getContextualType(element);
708      if (ts.isObjectLiteralExpression(element)) {
709        if (
710          !this.tsUtils.isDynamicLiteralInitializer(arrayLitNode) &&
711          !this.tsUtils.isObjectLiteralAssignable(elementContextType, element)
712        ) {
713          emptyContextTypeForArrayLiteral = true;
714          break;
715        }
716      }
717      if (elementContextType) {
718        this.checkAssignmentMatching(element, elementContextType, element, true);
719      }
720      if (this.options.arkts2 && ts.isOmittedExpression(element)) {
721        this.incrementCounters(element, FaultID.NosparseArray);
722      }
723    }
724    if (emptyContextTypeForArrayLiteral) {
725      this.incrementCounters(node, FaultID.ArrayLiteralNoContextType);
726    }
727  }
728
729  private checkPromiseEmptyArray(parent: ts.Node, arrayElementIsEmpty: boolean): boolean {
730    if (this.options.arkts2 && ts.isCallExpression(parent) && arrayElementIsEmpty) {
731      const callExpr = parent;
732      const methodName = TypeScriptLinter.getPromiseMethodName(callExpr.expression);
733      if (methodName && PROMISE_METHODS.has(methodName)) {
734        return true;
735      }
736      return false;
737    }
738    return false;
739  }
740
741  private static getPromiseMethodName(node: ts.Expression): string | undefined {
742    if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'Promise') {
743      return node.name.text;
744    }
745    return undefined;
746  }
747
748  private handleStructDeclaration(node: ts.StructDeclaration): void {
749    if (!this.options.arkts2) {
750      return;
751    }
752    this.handleStructDeclarationForLayout(node);
753    this.handleInvalidIdentifier(node);
754  }
755
756  private handleParameter(node: ts.Node): void {
757    const tsParam = node as ts.ParameterDeclaration;
758    TsUtils.getDecoratorsIfInSendableClass(tsParam)?.forEach((decorator) => {
759      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
760    });
761    this.handleDeclarationDestructuring(tsParam);
762    this.handleDeclarationInferredType(tsParam);
763    this.handleInvalidIdentifier(tsParam);
764    this.handleSdkGlobalApi(tsParam);
765    const typeNode = tsParam.type;
766    if (this.options.arkts2 && typeNode && TsUtils.typeContainsVoid(typeNode)) {
767      const autofix = this.autofixer?.fixLimitedVoidType(tsParam);
768      this.incrementCounters(typeNode, FaultID.LimitedVoidType, autofix);
769    }
770    this.handlePropertyDescriptorInScenarios(tsParam);
771  }
772
773  private handleEnumDeclaration(node: ts.Node): void {
774    const enumNode = node as ts.EnumDeclaration;
775    this.countDeclarationsWithDuplicateName(enumNode.name, enumNode);
776    const enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name);
777    if (!enumSymbol) {
778      return;
779    }
780    const enumDecls = enumSymbol.getDeclarations();
781    if (!enumDecls) {
782      return;
783    }
784    if (this.options.arkts2) {
785      this.handleInvalidIdentifier(enumNode);
786    }
787
788    /*
789     * Since type checker merges all declarations with the same name
790     * into one symbol, we need to check that there's more than one
791     * enum declaration related to that specific symbol.
792     * See 'countDeclarationsWithDuplicateName' method for details.
793     */
794    let enumDeclCount = 0;
795    const enumDeclsInFile: ts.Declaration[] = [];
796    const nodeSrcFile = enumNode.getSourceFile();
797    for (const decl of enumDecls) {
798      if (decl.kind === ts.SyntaxKind.EnumDeclaration) {
799        if (nodeSrcFile === decl.getSourceFile()) {
800          enumDeclsInFile.push(decl);
801        }
802        enumDeclCount++;
803      }
804    }
805
806    if (enumDeclCount > 1) {
807      const autofix = this.autofixer?.fixEnumMerging(enumSymbol, enumDeclsInFile);
808      this.incrementCounters(node, FaultID.EnumMerging, autofix);
809    }
810  }
811
812  private handleInterfaceDeclaration(node: ts.Node): void {
813    // early exit via exception if cancellation was requested
814    this.options.cancellationToken?.throwIfCancellationRequested();
815
816    const interfaceNode = node as ts.InterfaceDeclaration;
817
818    if (this.options.arkts2) {
819      this.handleInvalidIdentifier(interfaceNode);
820    }
821
822    const iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name);
823    const iDecls = iSymbol ? iSymbol.getDeclarations() : null;
824    if (iDecls) {
825
826      /*
827       * Since type checker merges all declarations with the same name
828       * into one symbol, we need to check that there's more than one
829       * interface declaration related to that specific symbol.
830       * See 'countDeclarationsWithDuplicateName' method for details.
831       */
832      let iDeclCount = 0;
833      for (const decl of iDecls) {
834        if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) {
835          iDeclCount++;
836        }
837      }
838      if (iDeclCount > 1) {
839        this.incrementCounters(node, FaultID.InterfaceMerging);
840      }
841    }
842    if (interfaceNode.heritageClauses) {
843      this.interfaceInheritanceLint(node, interfaceNode.heritageClauses);
844    }
845    this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode);
846  }
847
848  private handleTryStatement(node: ts.TryStatement): void {
849    if (!this.options.arkts2) {
850      return;
851    }
852
853    for (const stmt of node.tryBlock.statements) {
854      if (!ts.isExpressionStatement(stmt)) {
855        continue;
856      }
857      const callExpr = stmt.expression;
858      if (!ts.isCallExpression(callExpr)) {
859        continue;
860      }
861      const ident = callExpr.expression;
862      if (!ts.isIdentifier(ident)) {
863        continue;
864      }
865
866      this.handleTsInterop(ident, () => {
867        this.tsFunctionInteropHandler(callExpr);
868      });
869
870      this.handleJsInterop(ident, () => {
871        this.jsFunctionInteropHandler(callExpr);
872      });
873    }
874  }
875
876  private tsFunctionInteropHandler(callExpr: ts.CallExpression): void {
877    this.checkInteropFunctionThrows(callExpr, FaultID.InteropTSFunctionInvoke);
878  }
879
880  private jsFunctionInteropHandler(callExpr: ts.CallExpression): void {
881    this.checkInteropFunctionThrows(callExpr, FaultID.InteropJSFunctionInvoke);
882  }
883
884  private checkInteropFunctionThrows(callExpr: ts.CallExpression, faultId: FaultID): void {
885    const signature = this.tsTypeChecker.getResolvedSignature(callExpr);
886    if (!signature) {
887      return;
888    }
889
890    if (!signature.declaration) {
891      return;
892    }
893
894    const functionSymbol = this.getFunctionSymbol(signature.declaration);
895    const functionDeclaration = functionSymbol?.valueDeclaration;
896    if (!functionDeclaration) {
897      return;
898    }
899
900    if (!TypeScriptLinter.isFunctionLike(functionDeclaration)) {
901      return;
902    }
903    if (this.containsThrowNonError(functionDeclaration)) {
904      this.incrementCounters(callExpr, faultId);
905    }
906  }
907
908  private containsThrowNonError(node: ts.FunctionDeclaration | ts.MethodDeclaration | ts.FunctionExpression): boolean {
909    if (!node.body) {
910      return false;
911    }
912
913    const statements = node.body.statements;
914    for (const stmt of statements) {
915      if (!ts.isThrowStatement(stmt)) {
916        continue;
917      }
918      return this.tsUtils.checkStatementForErrorClass(stmt);
919    }
920    return false;
921  }
922
923  private handleThrowStatement(node: ts.Node): void {
924    const throwStmt = node as ts.ThrowStatement;
925    const throwExprType = this.tsTypeChecker.getTypeAtLocation(throwStmt.expression);
926    if (
927      !throwExprType.isClassOrInterface() ||
928      !this.tsUtils.isOrDerivedFrom(throwExprType, this.tsUtils.isStdErrorType)
929    ) {
930      this.incrementCounters(node, FaultID.ThrowStatement);
931    }
932  }
933
934  private checkForLoopDestructuring(forInit: ts.ForInitializer): void {
935    if (ts.isVariableDeclarationList(forInit) && forInit.declarations.length === 1) {
936      const varDecl = forInit.declarations[0];
937      if (
938        this.options.useRtLogic &&
939        (ts.isArrayBindingPattern(varDecl.name) || ts.isObjectBindingPattern(varDecl.name))
940      ) {
941        this.incrementCounters(varDecl, FaultID.DestructuringDeclaration);
942      }
943    }
944    if (ts.isArrayLiteralExpression(forInit) || ts.isObjectLiteralExpression(forInit)) {
945      this.incrementCounters(forInit, FaultID.DestructuringAssignment);
946    }
947  }
948
949  /*
950   * this should report the point of access to the array
951   * and also should report the identifier type
952   */
953  private checkElementAccessOfArray(statement: ts.Node): ArrayAccess | false {
954    if (ts.isElementAccessExpression(statement)) {
955      return this.isElementAccessOfArray(statement);
956    }
957
958    for (const children of statement.getChildren()) {
959      return this.checkElementAccessOfArray(children);
960    }
961    return false;
962  }
963
964  private isElementAccessOfArray(expr: ts.ElementAccessExpression): false | ArrayAccess {
965    if (!ts.isIdentifier(expr.expression)) {
966      return false;
967    }
968    const type = this.tsTypeChecker.getTypeAtLocation(expr.expression);
969    if (!this.tsUtils.isArray(type)) {
970      return false;
971    }
972    const accessArgument = expr.argumentExpression;
973    if (ts.isNumericLiteral(accessArgument)) {
974      return {
975        pos: expr.getEnd(),
976        accessingIdentifier: NUMBER_LITERAL,
977        arrayIdent: expr.expression
978      };
979    }
980
981    if (ts.isIdentifier(accessArgument)) {
982      return {
983        pos: expr.getEnd(),
984        accessingIdentifier: accessArgument,
985        arrayIdent: expr.expression
986      };
987    }
988    return false;
989  }
990
991  private handleForStatement(node: ts.Node): void {
992    const tsForStmt = node as ts.ForStatement;
993    const tsForInit = tsForStmt.initializer;
994    if (tsForInit) {
995      this.checkForLoopDestructuring(tsForInit);
996    }
997  }
998
999  private checkConditionForArrayAccess(condition: ts.Expression, arraySymbol: ts.Symbol): UncheckedIdentifier {
1000    if (!ts.isBinaryExpression(condition)) {
1001      return undefined;
1002    }
1003    const { left, right } = condition;
1004
1005    if (ts.isBinaryExpression(left)) {
1006      return this.checkConditionForArrayAccess(left, arraySymbol);
1007    }
1008    if (ts.isBinaryExpression(right)) {
1009      return this.checkConditionForArrayAccess(right, arraySymbol);
1010    }
1011
1012    if (this.isArrayLengthAccess(left, arraySymbol)) {
1013      if (ts.isNumericLiteral(right)) {
1014        return NUMBER_LITERAL;
1015      }
1016      if (!ts.isIdentifier(right)) {
1017        return undefined;
1018      }
1019      return right;
1020    }
1021
1022    if (this.isArrayLengthAccess(right, arraySymbol)) {
1023      if (ts.isNumericLiteral(left)) {
1024        return NUMBER_LITERAL;
1025      }
1026      if (!ts.isIdentifier(left)) {
1027        return undefined;
1028      }
1029      return left;
1030    }
1031
1032    return undefined;
1033  }
1034
1035  private isArrayLengthAccess(expr: ts.Expression, arraySymbol: ts.Symbol): boolean {
1036    if (!ts.isPropertyAccessExpression(expr)) {
1037      return false;
1038    }
1039    if (this.tsUtils.trueSymbolAtLocation(expr.expression) !== arraySymbol) {
1040      return false;
1041    }
1042    if (expr.name.text !== 'length') {
1043      return false;
1044    }
1045
1046    return true;
1047  }
1048
1049  private checkBodyHasArrayAccess(loopBody: ts.Block): ArrayAccess | undefined {
1050    let arrayAccessResult: undefined | ArrayAccess;
1051    // check if this element access expression is of an array.
1052    for (const child of loopBody.statements) {
1053      const result = this.checkElementAccessOfArray(child);
1054      if (!result) {
1055        continue;
1056      }
1057      arrayAccessResult = result;
1058    }
1059    return arrayAccessResult;
1060  }
1061
1062  private handleForInStatement(node: ts.Node): void {
1063    const tsForInStmt = node as ts.ForInStatement;
1064    const tsForInInit = tsForInStmt.initializer;
1065    this.checkForLoopDestructuring(tsForInInit);
1066    this.incrementCounters(node, FaultID.ForInStatement);
1067  }
1068
1069  private handleForOfStatement(node: ts.Node): void {
1070    const tsForOfStmt = node as ts.ForOfStatement;
1071    const tsForOfInit = tsForOfStmt.initializer;
1072    this.checkForLoopDestructuring(tsForOfInit);
1073    this.handleForOfJsArray(tsForOfStmt);
1074  }
1075
1076  private updateDataSdkJsonInfo(importDeclNode: ts.ImportDeclaration, importClause: ts.ImportClause): void {
1077    const sdkInfo = TypeScriptLinter.pathMap.get(importDeclNode.moduleSpecifier.getText());
1078    if (!sdkInfo) {
1079      return;
1080    }
1081    if (importClause.name) {
1082      const importClauseName = importClause.name.text;
1083      sdkInfo.forEach((info) => {
1084        TypeScriptLinter.addOrUpdateData(this.interfaceMap, importClauseName, info);
1085      });
1086    }
1087    if (importClause.namedBindings) {
1088      const namedImports = importClause.namedBindings as ts.NamedImports;
1089      if (!namedImports.elements) {
1090        return;
1091      }
1092      namedImports.elements.forEach((element) => {
1093        const elementName = element.name.getText();
1094        sdkInfo.forEach((info) => {
1095          TypeScriptLinter.addOrUpdateData(this.interfaceMap, elementName, info);
1096        });
1097      });
1098    }
1099  }
1100
1101  private handleImportDeclaration(node: ts.Node): void {
1102    // early exit via exception if cancellation was requested
1103    this.options.cancellationToken?.throwIfCancellationRequested();
1104    const importDeclNode = node as ts.ImportDeclaration;
1105    this.handleImportModule(importDeclNode);
1106    if (this.options.arkts2) {
1107      const importClause = importDeclNode.importClause;
1108      if (!importClause || !importClause.name && !importClause.namedBindings) {
1109        this.incrementCounters(node, FaultID.NoSideEffectImport);
1110      } else {
1111        this.updateDataSdkJsonInfo(importDeclNode, importClause);
1112      }
1113    }
1114    if (importDeclNode.parent.statements) {
1115      for (const stmt of importDeclNode.parent.statements) {
1116        if (stmt === importDeclNode) {
1117          break;
1118        }
1119        if (!ts.isImportDeclaration(stmt)) {
1120          this.incrementCounters(node, FaultID.ImportAfterStatement);
1121          break;
1122        }
1123      }
1124    }
1125
1126    const expr = importDeclNode.moduleSpecifier;
1127    if (expr.kind === ts.SyntaxKind.StringLiteral) {
1128      if (importDeclNode.assertClause) {
1129        this.incrementCounters(importDeclNode.assertClause, FaultID.ImportAssertion);
1130      }
1131      const stringLiteral = expr as ts.StringLiteral;
1132      this.handleSdkSendable(stringLiteral);
1133    }
1134
1135    // handle no side effect import in sendable module
1136    this.handleSharedModuleNoSideEffectImport(importDeclNode);
1137    this.handleInvalidIdentifier(importDeclNode);
1138    this.checkStdLibConcurrencyImport(importDeclNode);
1139    this.handleInterOpImportJs(importDeclNode);
1140    this.checkForDeprecatedModules(node);
1141  }
1142
1143  private checkForDeprecatedModules(node: ts.Node): void {
1144    if (!ts.isImportDeclaration(node)) {
1145      return;
1146    }
1147
1148    const deprecatedModules = ['@ohos.file.sendablePhotoAccessHelper'];
1149
1150    const importDecl = node;
1151    const moduleSpecifier = importDecl.moduleSpecifier;
1152
1153    if (ts.isStringLiteral(moduleSpecifier)) {
1154      const moduleName = moduleSpecifier.text;
1155      if (deprecatedModules.includes(moduleName)) {
1156        this.incrementCounters(moduleSpecifier, FaultID.SdkTypeQuery);
1157      }
1158    }
1159  }
1160
1161  private handleSdkSendable(tsStringLiteral: ts.StringLiteral): void {
1162    if (!this.options.arkts2) {
1163      return;
1164    }
1165
1166    const moduleSpecifierValue = tsStringLiteral.getText();
1167    const sdkInfos = TypeScriptLinter.pathMap.get(moduleSpecifierValue);
1168
1169    if (!sdkInfos || sdkInfos.size === 0) {
1170      return;
1171    }
1172    if (moduleSpecifierValue.includes('sendable')) {
1173      this.incrementCounters(tsStringLiteral, FaultID.SendablePropTypeFromSdk);
1174    }
1175  }
1176
1177  private handleImportModule(importDeclNode: ts.ImportDeclaration): void {
1178    if (!this.options.arkts2) {
1179      return;
1180    }
1181
1182    const modulePath = importDeclNode.moduleSpecifier.getText().slice(1, -1);
1183    if (modulePath.startsWith('./') || modulePath.startsWith('../')) {
1184
1185      /*
1186       * Reason for this method to check the oh module imports,
1187       * We do not use relative paths when importing from OhModules,
1188       * So we do not check the relative paths
1189       */
1190      return;
1191    }
1192    if (!importDeclNode.importClause) {
1193      return;
1194    }
1195
1196    const pathParts = modulePath.split(PATH_SEPARATOR);
1197    const etsIdx = pathParts.indexOf(ETS_PART);
1198
1199    if (this.options.wholeProjectPath) {
1200      if (TsUtils.checkFileExists(etsIdx !== 0, importDeclNode, modulePath, this.options.wholeProjectPath)) {
1201        return;
1202      }
1203    }
1204
1205    if (TsUtils.isValidOhModulePath(modulePath) || !TsUtils.isOhModule(modulePath)) {
1206      // Valid or paths that we do not check because they are not ohModules
1207      return;
1208    }
1209
1210    if (etsIdx === 0) {
1211      const autofix = this.autofixer?.addDefaultModuleToPath(pathParts, importDeclNode);
1212      this.incrementCounters(importDeclNode, FaultID.OhmUrlFullPath, autofix);
1213      return;
1214    }
1215
1216    const autofix = this.autofixer?.fixImportPath(pathParts, etsIdx, importDeclNode);
1217    this.incrementCounters(importDeclNode, FaultID.OhmUrlFullPath, autofix);
1218  }
1219
1220  private handleSharedModuleNoSideEffectImport(node: ts.ImportDeclaration): void {
1221    // check 'use shared'
1222    if (TypeScriptLinter.inSharedModule(node) && !node.importClause) {
1223      this.incrementCounters(node, FaultID.SharedNoSideEffectImport);
1224    }
1225  }
1226
1227  private static inSharedModule(node: ts.Node): boolean {
1228    const sourceFile: ts.SourceFile = node.getSourceFile();
1229    const modulePath = path.normalize(sourceFile.fileName);
1230    if (TypeScriptLinter.sharedModulesCache.has(modulePath)) {
1231      return TypeScriptLinter.sharedModulesCache.get(modulePath)!;
1232    }
1233    const isSharedModule: boolean = TsUtils.isSharedModule(sourceFile);
1234    TypeScriptLinter.sharedModulesCache.set(modulePath, isSharedModule);
1235    return isSharedModule;
1236  }
1237
1238  private handlePropertyAccessExpression(node: ts.Node): void {
1239    this.handleMakeObserved(node as ts.PropertyAccessExpression);
1240    this.handleStateStyles(node as ts.PropertyAccessExpression);
1241    this.handleDoubleDollar(node);
1242    this.handleQuotedHyphenPropsDeprecated(node as ts.PropertyAccessExpression);
1243    this.handleSdkTypeQuery(node as ts.PropertyAccessExpression);
1244    this.checkUnionTypes(node as ts.PropertyAccessExpression);
1245    this.handleLimitedVoidTypeFromSdkOnPropertyAccessExpression(node as ts.PropertyAccessExpression);
1246    this.checkDepricatedIsConcurrent(node as ts.PropertyAccessExpression);
1247    this.propertyAccessExpressionForBuiltin(node as ts.PropertyAccessExpression);
1248    this.checkConstrutorAccess(node as ts.PropertyAccessExpression);
1249    this.handleTaskPoolDeprecatedUsages(node as ts.PropertyAccessExpression);
1250    this.handleNoTuplesArraysForPropertyAccessExpression(node as ts.PropertyAccessExpression);
1251    if (ts.isCallExpression(node.parent) && node === node.parent.expression) {
1252      return;
1253    }
1254    const propertyAccessNode = node as ts.PropertyAccessExpression;
1255    const exprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode);
1256    const baseExprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode.expression);
1257    const baseExprType = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression);
1258    this.handleTsInterop(propertyAccessNode, () => {
1259      const type = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression);
1260      this.checkUsageOfTsTypes(type, propertyAccessNode.expression);
1261    });
1262    this.propertyAccessExpressionForInterop(propertyAccessNode);
1263    if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) {
1264      this.incrementCounters(propertyAccessNode.name, FaultID.Prototype);
1265    }
1266    if (
1267      !this.options.arkts2 &&
1268      !!exprSym &&
1269      this.tsUtils.isStdSymbolAPI(exprSym) &&
1270      !ALLOWED_STD_SYMBOL_API.includes(exprSym.getName())
1271    ) {
1272      this.incrementCounters(propertyAccessNode, FaultID.SymbolType);
1273    }
1274    if (this.options.advancedClassChecks && this.tsUtils.isClassObjectExpression(propertyAccessNode.expression)) {
1275      this.incrementCounters(propertyAccessNode.expression, FaultID.ClassAsObject);
1276    }
1277    if (!!baseExprSym && TsUtils.symbolHasEsObjectType(baseExprSym)) {
1278      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
1279      this.incrementCounters(propertyAccessNode, faultId);
1280    }
1281    if (TsUtils.isSendableFunction(baseExprType) || this.tsUtils.hasSendableTypeAlias(baseExprType)) {
1282      this.incrementCounters(propertyAccessNode, FaultID.SendableFunctionProperty);
1283    }
1284    this.checkFunctionProperty(propertyAccessNode, baseExprSym, baseExprType);
1285    this.handleSdkForConstructorFuncs(propertyAccessNode);
1286    this.fixJsImportPropertyAccessExpression(node);
1287  }
1288
1289  propertyAccessExpressionForBuiltin(decl: ts.PropertyAccessExpression): void {
1290    if (this.options.arkts2) {
1291      this.handleSymbolIterator(decl);
1292      this.handleGetOwnPropertyNames(decl);
1293      this.handlePropertyDescriptorInScenarios(decl);
1294    }
1295  }
1296
1297  private isJsRelated(node: ts.Expression): boolean {
1298    if (this.tsUtils.isJsImport(node)) {
1299      return true;
1300    }
1301
1302    if (ts.isNewExpression(node)) {
1303      return this.tsUtils.isJsImport(node.expression);
1304    }
1305
1306    if (ts.isIdentifier(node)) {
1307      const symbol = this.tsUtils.trueSymbolAtLocation(node);
1308      if (!symbol) {
1309        return false;
1310      }
1311
1312      const declarations = symbol.getDeclarations();
1313      if (!declarations || declarations.length === 0) {
1314        return false;
1315      }
1316
1317      for (const declaration of declarations) {
1318        if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
1319          return this.isJsRelated(declaration.initializer);
1320        }
1321      }
1322    }
1323
1324    return false;
1325  }
1326
1327  propertyAccessExpressionForInterop(propertyAccessNode: ts.PropertyAccessExpression): void {
1328    if (!this.useStatic || !this.options.arkts2) {
1329      return;
1330    }
1331
1332    const getFirstObjectNode = (propertyAccessNode: ts.PropertyAccessExpression): ts.Expression => {
1333      let current: ts.Expression = propertyAccessNode.expression;
1334      while (ts.isPropertyAccessExpression(current)) {
1335        current = current.expression;
1336      }
1337
1338      return current;
1339    };
1340
1341    const firstObjNode = getFirstObjectNode(propertyAccessNode);
1342    const isJsObject = this.isJsRelated(firstObjNode);
1343    if (!isJsObject) {
1344      return;
1345    }
1346
1347    if (ts.isBinaryExpression(propertyAccessNode.parent)) {
1348      const isAssignment = propertyAccessNode.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
1349      const autofix = isAssignment ?
1350        this.autofixer?.fixInteropBinaryExpression(propertyAccessNode.parent) :
1351        this.autofixer?.fixInteropPropertyAccessExpression(propertyAccessNode);
1352
1353      this.incrementCounters(
1354        isAssignment ? propertyAccessNode.parent : propertyAccessNode,
1355        FaultID.InteropObjectProperty,
1356        autofix
1357      );
1358    } else {
1359      const autofix = this.autofixer?.fixInteropPropertyAccessExpression(propertyAccessNode);
1360      this.incrementCounters(propertyAccessNode, FaultID.InteropObjectProperty, autofix);
1361    }
1362  }
1363
1364  private checkDepricatedIsConcurrent(node: ts.PropertyAccessExpression): void {
1365    if (!this.options.arkts2) {
1366      return;
1367    }
1368    if (!ts.isCallExpression(node.parent)) {
1369      return;
1370    }
1371    const methodName = node.name.getText();
1372
1373    if (methodName !== ISCONCURRENT) {
1374      return;
1375    }
1376    const symbol = this.tsUtils.trueSymbolAtLocation(node.expression);
1377    if (!symbol) {
1378      return;
1379    }
1380    if (symbol.name === TASKPOOL) {
1381      const decl = TsUtils.getDeclaration(symbol);
1382
1383      if (!decl) {
1384        return;
1385      }
1386
1387      const sourceFile = decl.getSourceFile();
1388      const fileName = path.basename(sourceFile.fileName);
1389
1390      if (
1391        TASKPOOL_MODULES.some((moduleName) => {
1392          return fileName.startsWith(moduleName) && (fileName.endsWith(D_TS) || fileName.endsWith(D_ETS));
1393        })
1394      ) {
1395        this.incrementCounters(node.name, FaultID.IsConcurrentDeprecated);
1396      }
1397    }
1398  }
1399
1400  checkFunctionProperty(
1401    node: ts.PropertyAccessExpression,
1402    baseExprSym: ts.Symbol | undefined,
1403    baseExprType: ts.Type
1404  ): void {
1405    if (!this.options.arkts2) {
1406      return;
1407    }
1408
1409    if (
1410      baseExprSym && TsUtils.isFunctionSymbol(baseExprSym) ||
1411      this.tsUtils.isStdFunctionType(baseExprType) ||
1412      TsUtils.isFunctionalType(baseExprType) && TsUtils.isAnonymousType(baseExprType)
1413    ) {
1414      this.incrementCounters(node.expression, FaultID.PropertyDeclOnFunction);
1415    }
1416  }
1417
1418  private checkUsageOfTsTypes(baseType: ts.Type, node: ts.Node): void {
1419    const typeString = this.tsTypeChecker.typeToString(baseType);
1420    if (
1421      TsUtils.isAnyType(baseType) ||
1422      TsUtils.isUnknownType(baseType) ||
1423      this.tsUtils.isStdFunctionType(baseType) ||
1424      typeString === 'symbol'
1425    ) {
1426      this.incrementCounters(node, FaultID.InteropDirectAccessToTSTypes);
1427    }
1428  }
1429
1430  checkUnionTypes(propertyAccessNode: ts.PropertyAccessExpression): void {
1431    if (!this.options.arkts2) {
1432      return;
1433    }
1434    const baseExprType = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression);
1435    if (!baseExprType.isUnion() || this.tsTypeChecker.typeToString(baseExprType) === 'ArrayBufferLike') {
1436      return;
1437    }
1438    const allType = baseExprType.types;
1439    const commonPropertyType = allType.filter((type) => {
1440      return this.tsUtils.findProperty(type, propertyAccessNode.name.getText()) !== undefined;
1441    });
1442    const typeMap = new Map();
1443    if (commonPropertyType.length === allType.length) {
1444      allType.forEach((type) => {
1445        this.handleTypeMember(type, propertyAccessNode.name.getText(), typeMap);
1446      });
1447      if (typeMap.size > 1) {
1448        this.incrementCounters(propertyAccessNode, FaultID.AvoidUnionTypes);
1449      }
1450    }
1451  }
1452
1453  private handleTypeMember(
1454    type: ts.Type,
1455    memberName: string,
1456    typeMap: Map<string | ts.Type | undefined, string>
1457  ): void {
1458    const propertySymbol = this.tsUtils.findProperty(type, memberName);
1459    if (!propertySymbol?.declarations) {
1460      return;
1461    }
1462    const propertyType = this.tsTypeChecker.getTypeOfSymbolAtLocation(propertySymbol, propertySymbol.declarations[0]);
1463    const symbol = propertySymbol.valueDeclaration;
1464    if (!symbol) {
1465      return;
1466    }
1467    if (ts.isMethodDeclaration(symbol)) {
1468      const returnType = this.getMethodReturnType(propertySymbol);
1469      typeMap.set(returnType, memberName);
1470    } else {
1471      typeMap.set(propertyType, memberName);
1472    }
1473  }
1474
1475  private getMethodReturnType(symbol: ts.Symbol): string | undefined {
1476    const declaration = symbol.valueDeclaration ?? (symbol.declarations?.[0] as ts.Node | undefined);
1477    if (!declaration) {
1478      return undefined;
1479    }
1480    const methodType = this.tsTypeChecker.getTypeOfSymbolAtLocation(symbol, declaration);
1481    const signatures = methodType.getCallSignatures();
1482    if (signatures.length === 0) {
1483      return 'void';
1484    }
1485    const returnType = signatures[0].getReturnType();
1486    return this.tsTypeChecker.typeToString(returnType);
1487  }
1488
1489  private handleLiteralAsPropertyName(node: ts.PropertyDeclaration | ts.PropertySignature): void {
1490    const propName = node.name;
1491    if (!!propName && (ts.isNumericLiteral(propName) || this.options.arkts2 && ts.isStringLiteral(propName))) {
1492      const autofix = this.autofixer?.fixLiteralAsPropertyNamePropertyName(propName);
1493      this.incrementCounters(node.name, FaultID.LiteralAsPropertyName, autofix);
1494    }
1495  }
1496
1497  private handlePropertyDeclaration(node: ts.PropertyDeclaration): void {
1498    const propName = node.name;
1499    this.handleLiteralAsPropertyName(node);
1500    const decorators = ts.getDecorators(node);
1501    this.filterOutDecoratorsDiagnostics(
1502      decorators,
1503      this.options.useRtLogic ? NON_INITIALIZABLE_PROPERTY_DECORATORS : NON_INITIALIZABLE_PROPERTY_DECORATORS_TSC,
1504      { begin: propName.getStart(), end: propName.getStart() },
1505      PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE
1506    );
1507    const classDecorators = ts.getDecorators(node.parent);
1508    const propType = node.type?.getText();
1509    if (this.options.arkts2 && node.type && TsUtils.typeContainsVoid(node.type)) {
1510      const autofix = this.autofixer?.fixLimitedVoidType(node);
1511      this.incrementCounters(node.type, FaultID.LimitedVoidType, autofix);
1512    }
1513    this.filterOutDecoratorsDiagnostics(
1514      classDecorators,
1515      NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS,
1516      { begin: propName.getStart(), end: propName.getStart() },
1517      PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE,
1518      propType
1519    );
1520    if (node.type && node.initializer) {
1521      this.checkAssignmentMatching(node, this.tsTypeChecker.getTypeAtLocation(node.type), node.initializer, true);
1522      this.checkFunctionTypeCompatible(node.type, node.initializer);
1523    }
1524    this.handleDeclarationInferredType(node);
1525    this.handleDefiniteAssignmentAssertion(node);
1526    this.handleSendableClassProperty(node);
1527    this.checkAssignmentNumericSemanticslyPro(node);
1528    this.handleInvalidIdentifier(node);
1529    this.handleStructPropertyDecl(node);
1530    this.handlePropertyDeclarationForProp(node);
1531    this.handleSdkGlobalApi(node);
1532    this.handleObjectLiteralAssignmentToClass(node);
1533  }
1534
1535  private handleSendableClassProperty(node: ts.PropertyDeclaration): void {
1536    const classNode = node.parent;
1537    if (!ts.isClassDeclaration(classNode) || !TsUtils.hasSendableDecorator(classNode)) {
1538      return;
1539    }
1540    const typeNode = node.type;
1541    if (!typeNode) {
1542      const autofix = this.autofixer?.fixSendableExplicitFieldType(node);
1543      this.incrementCounters(node, FaultID.SendableExplicitFieldType, autofix);
1544      return;
1545    }
1546    TsUtils.getDecoratorsIfInSendableClass(node)?.forEach((decorator) => {
1547      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
1548    });
1549    if (!this.tsUtils.isSendableTypeNode(typeNode)) {
1550      this.incrementCounters(node, FaultID.SendablePropType);
1551    }
1552  }
1553
1554  private handlePropertyAssignment(node: ts.PropertyAssignment): void {
1555    this.handleDollarBind(node);
1556    this.handlePropertyAssignmentForProp(node);
1557
1558    this.handleQuotedHyphenPropsDeprecated(node);
1559    const propName = node.name;
1560    if (!propName || !(ts.isNumericLiteral(propName) || this.options.arkts2 && ts.isStringLiteral(propName))) {
1561      return;
1562    }
1563
1564    /*
1565     * We can use literals as property names only when creating Record or any interop instances.
1566     * We can also initialize with constant string literals.
1567     * Assignment with string enum values is handled in handleComputedPropertyName
1568     */
1569    let isRecordObjectInitializer = false;
1570    let isLibraryType = false;
1571    let isDynamic = false;
1572    const objectLiteralType = this.tsTypeChecker.getContextualType(node.parent);
1573    if (objectLiteralType) {
1574      isRecordObjectInitializer = this.tsUtils.checkTypeSet(objectLiteralType, this.tsUtils.isStdRecordType);
1575      isLibraryType = this.tsUtils.isLibraryType(objectLiteralType);
1576    }
1577
1578    isDynamic = isLibraryType || this.tsUtils.isDynamicLiteralInitializer(node.parent);
1579    if (!isRecordObjectInitializer && !isDynamic) {
1580      const autofix = this.autofixer?.fixLiteralAsPropertyNamePropertyAssignment(node);
1581      this.incrementCounters(node.name, FaultID.LiteralAsPropertyName, autofix);
1582    }
1583  }
1584
1585  private static getAllClassesFromSourceFile(sourceFile: ts.SourceFile): ts.ClassDeclaration[] {
1586    const allClasses: ts.ClassDeclaration[] = [];
1587    function visit(node: ts.Node): void {
1588      if (ts.isClassDeclaration(node)) {
1589        allClasses.push(node);
1590      }
1591      ts.forEachChild(node, visit);
1592    }
1593    visit(sourceFile);
1594    return allClasses;
1595  }
1596
1597  private static getAllInterfaceFromSourceFile(sourceFile: ts.SourceFile): ts.InterfaceDeclaration[] {
1598    const allInterfaces: ts.InterfaceDeclaration[] = [];
1599    function visit(node: ts.Node): void {
1600      if (ts.isInterfaceDeclaration(node)) {
1601        allInterfaces.push(node);
1602      }
1603      ts.forEachChild(node, visit);
1604    }
1605    visit(sourceFile);
1606    return allInterfaces;
1607  }
1608
1609  private handlePropertySignature(node: ts.PropertySignature): void {
1610    this.handleInterfaceProperty(node);
1611    this.handleLiteralAsPropertyName(node);
1612    this.handleSendableInterfaceProperty(node);
1613    this.handleInvalidIdentifier(node);
1614    const typeNode = node.type;
1615    if (this.options.arkts2 && typeNode && typeNode.kind === ts.SyntaxKind.VoidKeyword) {
1616      this.incrementCounters(typeNode, FaultID.LimitedVoidType);
1617    }
1618  }
1619
1620  private handleInterfaceProperty(node: ts.PropertySignature): void {
1621    if (this.options.arkts2 && ts.isInterfaceDeclaration(node.parent)) {
1622      if (node.type && ts.isFunctionTypeNode(node.type)) {
1623        const interfaceName = node.parent.name.getText();
1624        const propertyName = node.name.getText();
1625        const allClasses = TypeScriptLinter.getAllClassesFromSourceFile(this.sourceFile);
1626        const allInterfaces = TypeScriptLinter.getAllInterfaceFromSourceFile(this.sourceFile);
1627        this.visitClassMembers(allClasses, interfaceName, propertyName);
1628        this.visitInterfaceMembers(allInterfaces, interfaceName, propertyName);
1629      }
1630    }
1631  }
1632
1633  private visitInterfaceMembers(
1634    interfaces: ts.InterfaceDeclaration[],
1635    interfaceName: string,
1636    propertyName: string
1637  ): void {
1638    void this;
1639    interfaces.some((interfaceDecl) => {
1640      const implementsClause = this.getExtendsClause(interfaceDecl);
1641      if (
1642        implementsClause?.types.some((type) => {
1643          return type.expression.getText() === interfaceName;
1644        })
1645      ) {
1646        this.checkInterfaceForProperty(interfaceDecl, propertyName);
1647      }
1648    });
1649  }
1650
1651  private getExtendsClause(interfaceDecl: ts.InterfaceDeclaration): ts.HeritageClause | undefined {
1652    void this;
1653    return interfaceDecl.heritageClauses?.find((clause) => {
1654      return clause.token === ts.SyntaxKind.ExtendsKeyword;
1655    });
1656  }
1657
1658  private checkInterfaceForProperty(interfaceDecl: ts.InterfaceDeclaration, propertyName: string): void {
1659    for (const member of interfaceDecl.members) {
1660      if (ts.isMethodSignature(member) && member.name.getText() === propertyName) {
1661        this.incrementCounters(member, FaultID.MethodOverridingField);
1662      }
1663    }
1664  }
1665
1666  private getImplementsClause(classDecl: ts.ClassDeclaration): ts.HeritageClause | undefined {
1667    void this;
1668    return classDecl.heritageClauses?.find((clause) => {
1669      return clause.token === ts.SyntaxKind.ImplementsKeyword;
1670    });
1671  }
1672
1673  private checkClassForProperty(classDecl: ts.ClassDeclaration, propertyName: string): void {
1674    for (const member of classDecl.members) {
1675      if (ts.isMethodDeclaration(member) && member.name.getText() === propertyName) {
1676        this.incrementCounters(member, FaultID.MethodOverridingField);
1677      }
1678    }
1679  }
1680
1681  private visitClassMembers(classes: ts.ClassDeclaration[], interfaceName: string, propertyName: string): void {
1682    void this;
1683    classes.some((classDecl) => {
1684      const implementsClause = this.getImplementsClause(classDecl);
1685      if (
1686        implementsClause?.types.some((type) => {
1687          return type.expression.getText() === interfaceName;
1688        })
1689      ) {
1690        this.checkClassForProperty(classDecl, propertyName);
1691      }
1692    });
1693  }
1694
1695  private handleSendableInterfaceProperty(node: ts.PropertySignature): void {
1696    const typeNode = node.type;
1697    if (!typeNode) {
1698      return;
1699    }
1700    const interfaceNode = node.parent;
1701    const interfaceNodeType = this.tsTypeChecker.getTypeAtLocation(interfaceNode);
1702    if (!ts.isInterfaceDeclaration(interfaceNode) || !this.tsUtils.isSendableClassOrInterface(interfaceNodeType)) {
1703      return;
1704    }
1705    if (!this.tsUtils.isSendableTypeNode(typeNode)) {
1706      this.incrementCounters(node, FaultID.SendablePropType);
1707    }
1708  }
1709
1710  private filterOutDecoratorsDiagnostics(
1711    decorators: readonly ts.Decorator[] | undefined,
1712    expectedDecorators: readonly string[],
1713    range: { begin: number; end: number },
1714    code: number,
1715    propType?: string
1716  ): void {
1717    // Filter out non-initializable property decorators from strict diagnostics.
1718    if (this.tscStrictDiagnostics && this.sourceFile) {
1719      if (
1720        decorators?.some((decorator) => {
1721          const decoratorName = TsUtils.getDecoratorName(decorator);
1722          // special case for property of type CustomDialogController of the @CustomDialog-decorated class
1723          if (expectedDecorators.includes(NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0])) {
1724            return expectedDecorators.includes(decoratorName) && propType === 'CustomDialogController';
1725          }
1726          return expectedDecorators.includes(decoratorName);
1727        })
1728      ) {
1729        this.filterOutDiagnostics(range, code);
1730      }
1731    }
1732  }
1733
1734  private filterOutDiagnostics(range: { begin: number; end: number }, code: number): void {
1735    // Filter out strict diagnostics within the given range with the given code.
1736    if (!this.tscStrictDiagnostics || !this.sourceFile) {
1737      return;
1738    }
1739    const file = path.normalize(this.sourceFile.fileName);
1740    const tscDiagnostics = this.tscStrictDiagnostics.get(file);
1741    if (tscDiagnostics) {
1742      const filteredDiagnostics = tscDiagnostics.filter((val) => {
1743        if (val.code !== code) {
1744          return true;
1745        }
1746        if (val.start === undefined) {
1747          return true;
1748        }
1749        if (val.start < range.begin) {
1750          return true;
1751        }
1752        if (val.start > range.end) {
1753          return true;
1754        }
1755        return false;
1756      });
1757      this.tscStrictDiagnostics.set(file, filteredDiagnostics);
1758    }
1759  }
1760
1761  private static isClassLikeOrIface(node: ts.Node): boolean {
1762    return ts.isClassLike(node) || ts.isInterfaceDeclaration(node);
1763  }
1764
1765  private handleFunctionExpression(node: ts.Node): void {
1766    const funcExpr = node as ts.FunctionExpression;
1767    const isGenerator = funcExpr.asteriskToken !== undefined;
1768    const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr);
1769    const autofix = this.autofixer?.fixFunctionExpression(
1770      funcExpr,
1771      newRetTypeNode,
1772      ts.getModifiers(funcExpr),
1773      isGenerator,
1774      hasUnfixableReturnType
1775    );
1776    this.incrementCounters(funcExpr, FaultID.FunctionExpression, autofix);
1777    if (isGenerator) {
1778      this.incrementCounters(funcExpr, FaultID.GeneratorFunction);
1779    }
1780    if (!hasPredecessor(funcExpr, TypeScriptLinter.isClassLikeOrIface)) {
1781      this.reportThisKeywordsInScope(funcExpr.body);
1782    }
1783    if (hasUnfixableReturnType) {
1784      this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference);
1785    }
1786    this.handleLimitedVoidFunction(funcExpr);
1787  }
1788
1789  private handleArrowFunction(node: ts.Node): void {
1790    const arrowFunc = node as ts.ArrowFunction;
1791    if (!hasPredecessor(arrowFunc, TypeScriptLinter.isClassLikeOrIface)) {
1792      this.reportThisKeywordsInScope(arrowFunc.body);
1793    }
1794    const contextType = this.tsTypeChecker.getContextualType(arrowFunc);
1795    if (!(contextType && this.tsUtils.isLibraryType(contextType))) {
1796      if (!arrowFunc.type) {
1797        this.handleMissingReturnType(arrowFunc);
1798      }
1799    }
1800    this.checkDefaultParamBeforeRequired(arrowFunc);
1801    this.handleLimitedVoidFunction(arrowFunc);
1802  }
1803
1804  private handleFunctionDeclaration(node: ts.Node): void {
1805    // early exit via exception if cancellation was requested
1806    this.options.cancellationToken?.throwIfCancellationRequested();
1807
1808    const tsFunctionDeclaration = node as ts.FunctionDeclaration;
1809    if (!tsFunctionDeclaration.type) {
1810      this.handleMissingReturnType(tsFunctionDeclaration);
1811    }
1812    if (tsFunctionDeclaration.name) {
1813      this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration);
1814    }
1815    if (tsFunctionDeclaration.body) {
1816      this.reportThisKeywordsInScope(tsFunctionDeclaration.body);
1817    }
1818    const funcDeclParent = tsFunctionDeclaration.parent;
1819    if (!ts.isSourceFile(funcDeclParent) && !ts.isModuleBlock(funcDeclParent)) {
1820      const autofix = this.autofixer?.fixNestedFunction(tsFunctionDeclaration);
1821      this.incrementCounters(tsFunctionDeclaration, FaultID.LocalFunction, autofix);
1822    }
1823    if (tsFunctionDeclaration.asteriskToken) {
1824      this.incrementCounters(node, FaultID.GeneratorFunction);
1825    }
1826    if (TsUtils.hasSendableDecoratorFunctionOverload(tsFunctionDeclaration)) {
1827      if (!this.isSendableDecoratorValid(tsFunctionDeclaration)) {
1828        return;
1829      }
1830      TsUtils.getNonSendableDecorators(tsFunctionDeclaration)?.forEach((decorator) => {
1831        this.incrementCounters(decorator, FaultID.SendableFunctionDecorator);
1832      });
1833      if (!TsUtils.hasSendableDecorator(tsFunctionDeclaration)) {
1834        const autofix = this.autofixer?.addSendableDecorator(tsFunctionDeclaration);
1835        this.incrementCounters(tsFunctionDeclaration, FaultID.SendableFunctionOverloadDecorator, autofix);
1836      }
1837      this.scanCapturedVarsInSendableScope(
1838        tsFunctionDeclaration,
1839        tsFunctionDeclaration,
1840        FaultID.SendableFunctionImportedVariables
1841      );
1842    }
1843    this.handleTSOverload(tsFunctionDeclaration);
1844    this.checkAssignmentNumericSemanticsFuntion(tsFunctionDeclaration);
1845    this.handleInvalidIdentifier(tsFunctionDeclaration);
1846    this.checkDefaultParamBeforeRequired(tsFunctionDeclaration);
1847    this.handleLimitedVoidFunction(tsFunctionDeclaration);
1848  }
1849
1850  private handleMissingReturnType(
1851    funcLikeDecl: ts.FunctionLikeDeclaration | ts.MethodSignature
1852  ): [boolean, ts.TypeNode | undefined] {
1853    if (this.options.useRtLogic && funcLikeDecl.type) {
1854      return [false, funcLikeDecl.type];
1855    }
1856
1857    // Note: Return type can't be inferred for function without body.
1858    const isSignature = ts.isMethodSignature(funcLikeDecl);
1859    if (isSignature || !funcLikeDecl.body) {
1860      // Ambient flag is not exposed, so we apply dirty hack to make it visible
1861      const isDeclareDeclaration = TsUtils.isAmbientNode(funcLikeDecl);
1862      if ((isSignature || isDeclareDeclaration) && !funcLikeDecl.type) {
1863        this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference);
1864      }
1865      return [false, undefined];
1866    }
1867
1868    return this.tryAutofixMissingReturnType(funcLikeDecl);
1869  }
1870
1871  private tryAutofixMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration): [boolean, ts.TypeNode | undefined] {
1872    if (!funcLikeDecl.body) {
1873      return [false, undefined];
1874    }
1875
1876    let autofix: Autofix[] | undefined;
1877    let newRetTypeNode: ts.TypeNode | undefined;
1878    const isFuncExpr = ts.isFunctionExpression(funcLikeDecl);
1879
1880    /*
1881     * Currently, ArkTS can't infer return type of function, when expression
1882     * in the return statement is a call to a function or method whose return
1883     * value type is omitted. In that case, we attempt to prepare an autofix.
1884     */
1885    let hasLimitedRetTypeInference = this.hasLimitedTypeInferenceFromReturnExpr(funcLikeDecl.body);
1886    const tsSignature = this.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl);
1887    if (tsSignature) {
1888      const tsRetType = this.tsTypeChecker.getReturnTypeOfSignature(tsSignature);
1889      if (
1890        !tsRetType ||
1891        !this.options.arkts2 && TsUtils.isUnsupportedType(tsRetType) ||
1892        this.options.arkts2 && this.tsUtils.isUnsupportedTypeArkts2(tsRetType)
1893      ) {
1894        hasLimitedRetTypeInference = true;
1895      } else if (hasLimitedRetTypeInference) {
1896        newRetTypeNode = this.tsTypeChecker.typeToTypeNode(tsRetType, funcLikeDecl, ts.NodeBuilderFlags.None);
1897        if (this.autofixer !== undefined && newRetTypeNode && !isFuncExpr) {
1898          autofix = this.autofixer.fixMissingReturnType(funcLikeDecl, newRetTypeNode);
1899        }
1900      }
1901    }
1902
1903    /*
1904     * Don't report here if in function expression context.
1905     * See handleFunctionExpression for details.
1906     */
1907    if (hasLimitedRetTypeInference && !isFuncExpr) {
1908      this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference, autofix);
1909    }
1910
1911    return [hasLimitedRetTypeInference && !newRetTypeNode, newRetTypeNode];
1912  }
1913
1914  private hasLimitedTypeInferenceFromReturnExpr(funBody: ts.ConciseBody): boolean {
1915    let hasLimitedTypeInference = false;
1916    const callback = (node: ts.Node): void => {
1917      if (hasLimitedTypeInference) {
1918        return;
1919      }
1920      if (
1921        ts.isReturnStatement(node) &&
1922        node.expression &&
1923        this.tsUtils.isCallToFunctionWithOmittedReturnType(TsUtils.unwrapParenthesized(node.expression))
1924      ) {
1925        hasLimitedTypeInference = true;
1926      }
1927    };
1928    // Don't traverse other nested function-like declarations.
1929    const stopCondition = (node: ts.Node): boolean => {
1930      return (
1931        ts.isFunctionDeclaration(node) ||
1932        ts.isFunctionExpression(node) ||
1933        ts.isMethodDeclaration(node) ||
1934        ts.isAccessor(node) ||
1935        ts.isArrowFunction(node)
1936      );
1937    };
1938    if (ts.isBlock(funBody)) {
1939      forEachNodeInSubtree(funBody, callback, stopCondition);
1940    } else {
1941      const tsExpr = TsUtils.unwrapParenthesized(funBody);
1942      hasLimitedTypeInference = this.tsUtils.isCallToFunctionWithOmittedReturnType(tsExpr);
1943    }
1944    return hasLimitedTypeInference;
1945  }
1946
1947  private isValidTypeForUnaryArithmeticOperator(type: ts.Type): boolean {
1948    const typeFlags = type.getFlags();
1949    const numberLiteralFlags = ts.TypeFlags.BigIntLiteral | ts.TypeFlags.NumberLiteral;
1950    const numberLikeFlags = ts.TypeFlags.BigIntLike | ts.TypeFlags.NumberLike;
1951    const isNumberLike = !!(typeFlags & (numberLiteralFlags | numberLikeFlags));
1952
1953    const isAllowedNumericType = this.tsUtils.isStdBigIntType(type) || this.tsUtils.isStdNumberType(type);
1954
1955    return isNumberLike || isAllowedNumericType;
1956  }
1957
1958  private handleInteropOperand(tsUnaryArithm: ts.PrefixUnaryExpression): void {
1959    const processPropertyAccess = (expr: ts.PropertyAccessExpression | ts.ParenthesizedExpression): void => {
1960      const propertyAccess = ts.isParenthesizedExpression(expr) ? expr.expression : expr;
1961
1962      if (ts.isPropertyAccessExpression(propertyAccess)) {
1963        const exprSym = this.tsUtils.trueSymbolAtLocation(propertyAccess.expression);
1964        const declaration = exprSym?.declarations?.[0];
1965        this.checkAndProcessDeclaration(declaration, tsUnaryArithm);
1966      }
1967    };
1968
1969    if (ts.isPropertyAccessExpression(tsUnaryArithm.operand) || ts.isParenthesizedExpression(tsUnaryArithm.operand)) {
1970      processPropertyAccess(tsUnaryArithm.operand);
1971    }
1972  }
1973
1974  private checkAndProcessDeclaration(
1975    declaration: ts.Declaration | undefined,
1976    tsUnaryArithm: ts.PrefixUnaryExpression
1977  ): void {
1978    if (declaration?.getSourceFile().fileName.endsWith(EXTNAME_JS)) {
1979      if (
1980        [
1981          ts.SyntaxKind.PlusToken,
1982          ts.SyntaxKind.ExclamationToken,
1983          ts.SyntaxKind.TildeToken,
1984          ts.SyntaxKind.MinusToken
1985        ].includes(tsUnaryArithm.operator)
1986      ) {
1987        const autofix = this.autofixer?.fixInteropInterfaceConvertNum(tsUnaryArithm);
1988        this.incrementCounters(tsUnaryArithm, FaultID.InteropNoHaveNum, autofix);
1989      }
1990    }
1991  }
1992
1993  private handlePostfixUnaryExpression(node: ts.Node): void {
1994    const unaryExpr = node as ts.PostfixUnaryExpression;
1995    if (unaryExpr.operator === ts.SyntaxKind.PlusPlusToken || unaryExpr.operator === ts.SyntaxKind.MinusMinusToken) {
1996      this.checkAutoIncrementDecrement(unaryExpr);
1997    }
1998  }
1999
2000  private handlePrefixUnaryExpression(node: ts.Node): void {
2001    const tsUnaryArithm = node as ts.PrefixUnaryExpression;
2002    if (this.useStatic && this.options.arkts2) {
2003      const tsUnaryArithm = node as ts.PrefixUnaryExpression;
2004      this.handleInteropOperand(tsUnaryArithm);
2005    }
2006    const tsUnaryOp = tsUnaryArithm.operator;
2007    const tsUnaryOperand = tsUnaryArithm.operand;
2008    if (
2009      tsUnaryOp === ts.SyntaxKind.PlusToken ||
2010      tsUnaryOp === ts.SyntaxKind.MinusToken ||
2011      tsUnaryOp === ts.SyntaxKind.TildeToken
2012    ) {
2013      const tsOperatndType = this.tsTypeChecker.getTypeAtLocation(tsUnaryOperand);
2014      const isTilde = tsUnaryOp === ts.SyntaxKind.TildeToken;
2015      const isInvalidTilde =
2016        isTilde && ts.isNumericLiteral(tsUnaryOperand) && !this.tsUtils.isIntegerConstantValue(tsUnaryOperand);
2017      if (!this.isValidTypeForUnaryArithmeticOperator(tsOperatndType) || isInvalidTilde) {
2018        this.incrementCounters(node, FaultID.UnaryArithmNotNumber);
2019      }
2020    }
2021    if (
2022      tsUnaryArithm.operator === ts.SyntaxKind.PlusPlusToken ||
2023      tsUnaryArithm.operator === ts.SyntaxKind.MinusMinusToken
2024    ) {
2025      this.checkAutoIncrementDecrement(tsUnaryArithm);
2026    }
2027  }
2028
2029  private handleBinaryExpression(node: ts.Node): void {
2030    const tsBinaryExpr = node as ts.BinaryExpression;
2031    const tsLhsExpr = tsBinaryExpr.left;
2032    const tsRhsExpr = tsBinaryExpr.right;
2033    if (isAssignmentOperator(tsBinaryExpr.operatorToken)) {
2034      this.processBinaryAssignment(tsBinaryExpr, tsLhsExpr, tsRhsExpr);
2035    }
2036    const leftOperandType = this.tsTypeChecker.getTypeAtLocation(tsLhsExpr);
2037    const typeNode = this.tsUtils.getVariableDeclarationTypeNode(tsLhsExpr);
2038    switch (tsBinaryExpr.operatorToken.kind) {
2039      // FaultID.BitOpWithWrongType - removed as rule #61
2040      case ts.SyntaxKind.CommaToken:
2041        this.processBinaryComma(tsBinaryExpr);
2042        break;
2043      case ts.SyntaxKind.InstanceOfKeyword:
2044        this.processBinaryInstanceOf(node, tsLhsExpr, leftOperandType);
2045        this.handleInstanceOfExpression(tsBinaryExpr);
2046        break;
2047      case ts.SyntaxKind.InKeyword:
2048        this.incrementCounters(tsBinaryExpr.operatorToken, FaultID.InOperator);
2049        break;
2050      case ts.SyntaxKind.EqualsToken:
2051        this.handleTsInterop(tsLhsExpr, () => {
2052          this.checkUsageOfTsTypes(leftOperandType, tsBinaryExpr);
2053        });
2054        this.checkAssignmentMatching(tsBinaryExpr, leftOperandType, tsRhsExpr);
2055        this.checkFunctionTypeCompatible(typeNode, tsRhsExpr);
2056        this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr);
2057        this.handleSdkGlobalApi(tsBinaryExpr);
2058        break;
2059      case ts.SyntaxKind.AmpersandAmpersandEqualsToken:
2060      case ts.SyntaxKind.QuestionQuestionEqualsToken:
2061      case ts.SyntaxKind.BarBarEqualsToken:
2062        if (this.options.arkts2) {
2063          this.incrementCounters(tsBinaryExpr.operatorToken, FaultID.UnsupportOperator);
2064        }
2065        break;
2066      default:
2067    }
2068    this.checkInterOpImportJsDataCompare(tsBinaryExpr);
2069    this.checkInteropEqualityJudgment(tsBinaryExpr);
2070    this.handleNumericBigintCompare(tsBinaryExpr);
2071    this.handleArkTSPropertyAccess(tsBinaryExpr);
2072    this.handleObjectLiteralAssignmentToClass(tsBinaryExpr);
2073    this.handleAssignmentNotsLikeSmartType(tsBinaryExpr);
2074  }
2075
2076  private checkInterOpImportJsDataCompare(expr: ts.BinaryExpression): void {
2077    if (!this.useStatic || !this.options.arkts2 || !TypeScriptLinter.isComparisonOperator(expr.operatorToken.kind)) {
2078      return;
2079    }
2080
2081    const processExpression = (expr: ts.Expression): void => {
2082      const symbol = this.tsUtils.trueSymbolAtLocation(expr);
2083      if (this.isJsFileSymbol(symbol) || this.isJsFileExpression(expr)) {
2084        const autofix = this.autofixer?.fixInteropOperators(expr);
2085        this.incrementCounters(expr, FaultID.InterOpImportJsDataCompare, autofix);
2086      }
2087    };
2088
2089    processExpression(expr.left);
2090    processExpression(expr.right);
2091  }
2092
2093  private static isComparisonOperator(kind: ts.SyntaxKind): boolean {
2094    return [
2095      ts.SyntaxKind.GreaterThanToken,
2096      ts.SyntaxKind.LessThanToken,
2097      ts.SyntaxKind.GreaterThanEqualsToken,
2098      ts.SyntaxKind.LessThanEqualsToken
2099    ].includes(kind);
2100  }
2101
2102  private isJsFileSymbol(symbol: ts.Symbol | undefined): boolean {
2103    if (!symbol) {
2104      return false;
2105    }
2106
2107    const declaration = symbol.declarations?.[0];
2108    if (!declaration || !ts.isVariableDeclaration(declaration)) {
2109      return false;
2110    }
2111
2112    const initializer = declaration.initializer;
2113    return initializer ? this.isJsFileExpression(initializer) : false;
2114  }
2115
2116  private isJsFileExpression(expr: ts.Expression): boolean {
2117    if (ts.isPropertyAccessExpression(expr)) {
2118      const initializerSym = this.tsUtils.trueSymbolAtLocation(expr.expression);
2119      return initializerSym?.declarations?.[0]?.getSourceFile()?.fileName.endsWith(EXTNAME_JS) ?? false;
2120    }
2121    return expr.getSourceFile()?.fileName.endsWith(EXTNAME_JS) ?? false;
2122  }
2123
2124  private checkInteropEqualityJudgment(tsBinaryExpr: ts.BinaryExpression): void {
2125    if (this.useStatic && this.options.arkts2) {
2126      switch (tsBinaryExpr.operatorToken.kind) {
2127        case ts.SyntaxKind.EqualsEqualsToken:
2128        case ts.SyntaxKind.ExclamationEqualsToken:
2129        case ts.SyntaxKind.EqualsEqualsEqualsToken:
2130        case ts.SyntaxKind.ExclamationEqualsEqualsToken:
2131          if (this.tsUtils.isJsImport(tsBinaryExpr.left) || this.tsUtils.isJsImport(tsBinaryExpr.right)) {
2132            const autofix = this.autofixer?.fixInteropEqualityOperator(tsBinaryExpr, tsBinaryExpr.operatorToken.kind);
2133            this.incrementCounters(tsBinaryExpr, FaultID.InteropEqualityJudgment, autofix);
2134          }
2135          break;
2136        default:
2137      }
2138    }
2139  }
2140
2141  private handleTsInterop(nodeToCheck: ts.Node, handler: { (): void }): void {
2142    if (!this.options.arkts2 || !this.useStatic) {
2143      return;
2144    }
2145
2146    const declarationNode = this.tsUtils.getDeclarationNode(nodeToCheck);
2147    if (!declarationNode) {
2148      return;
2149    }
2150
2151    const fileName = declarationNode.getSourceFile().fileName;
2152    if (fileName.includes(ARKTS_IGNORE_DIRS_OH_MODULES)) {
2153      return;
2154    }
2155    if (!fileName.endsWith(EXTNAME_TS)) {
2156      return;
2157    }
2158
2159    if (fileName.endsWith(EXTNAME_D_TS)) {
2160      return;
2161    }
2162
2163    handler();
2164  }
2165
2166  private handleJsInterop(nodeToCheck: ts.Node, handler: { (): void }): void {
2167    if (!this.options.arkts2 || !this.useStatic) {
2168      return;
2169    }
2170
2171    const declarationNode = this.tsUtils.getDeclarationNode(nodeToCheck);
2172    if (!declarationNode) {
2173      return;
2174    }
2175
2176    const fileName = declarationNode.getSourceFile().fileName;
2177    if (fileName.includes(ARKTS_IGNORE_DIRS_OH_MODULES)) {
2178      return;
2179    }
2180    if (!fileName.endsWith(EXTNAME_JS)) {
2181      return;
2182    }
2183
2184    if (fileName.endsWith(EXTNAME_D_TS)) {
2185      return;
2186    }
2187
2188    handler();
2189  }
2190
2191  private processBinaryAssignment(
2192    binaryExpr: ts.BinaryExpression,
2193    tsLhsExpr: ts.Expression,
2194    tsRhsExpr: ts.Expression
2195  ): void {
2196    this.handleDestructuringAssignment(binaryExpr, tsLhsExpr, tsRhsExpr);
2197
2198    if (ts.isPropertyAccessExpression(tsLhsExpr)) {
2199      const tsLhsSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr);
2200      const tsLhsBaseSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr.expression);
2201      if (tsLhsSymbol && tsLhsSymbol.flags & ts.SymbolFlags.Method) {
2202        this.incrementCounters(tsLhsExpr, FaultID.MethodReassignment);
2203      }
2204      if (
2205        !this.options.arkts2 &&
2206        TsUtils.isMethodAssignment(tsLhsSymbol) &&
2207        tsLhsBaseSymbol &&
2208        (tsLhsBaseSymbol.flags & ts.SymbolFlags.Function) !== 0
2209      ) {
2210        this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction);
2211      }
2212    }
2213  }
2214
2215  private checkAssignmentNumericSemanticsly(node: ts.VariableDeclaration): void {
2216    if (!this.options.arkts2) {
2217      return;
2218    }
2219    const initializer = node.initializer;
2220    const name = node.name;
2221    if (node.type || !initializer || !ts.isIdentifier(name)) {
2222      return;
2223    }
2224
2225    // Early return if the variable is imported from JS
2226    if (this.tsUtils.isPossiblyImportedFromJS(name) || this.tsUtils.isPossiblyImportedFromJS(initializer)) {
2227      return;
2228    }
2229
2230    if (
2231      ts.isBinaryExpression(initializer) &&
2232      ts.isCallExpression(initializer.left) &&
2233      TsUtils.isAppStorageAccess(initializer.left)
2234    ) {
2235      return;
2236    }
2237
2238    const sym = this.tsTypeChecker.getSymbolAtLocation(name);
2239    if (!sym) {
2240      return;
2241    }
2242
2243    const type = this.tsTypeChecker.getTypeOfSymbolAtLocation(sym, name);
2244    const typeText = this.tsTypeChecker.typeToString(type);
2245    const isEnum = this.isNumericEnumType(type);
2246    if (TsUtils.isNumberLike(type, typeText, isEnum)) {
2247      const autofix = this.autofixer?.fixVariableDeclaration(node, isEnum);
2248      this.incrementCounters(node, FaultID.NumericSemantics, autofix);
2249    }
2250  }
2251
2252  private isEnumType(type: ts.Type): boolean {
2253    if (type.flags & ts.TypeFlags.Enum) {
2254      return true;
2255    }
2256
2257    if (type.symbol?.flags & ts.SymbolFlags.Enum) {
2258      return true;
2259    }
2260
2261    if (type.flags & ts.TypeFlags.EnumLiteral) {
2262      return true;
2263    }
2264
2265    if (type.isUnion()) {
2266      return type.types.some((t) => {
2267        return this.isEnumType(t);
2268      });
2269    }
2270    return false;
2271  }
2272
2273  private isNumericEnumType(type: ts.Type): boolean {
2274    if (!this.isEnumType(type)) {
2275      return false;
2276    }
2277    const declarations = type.symbol?.getDeclarations() || [];
2278    const enumMemberDecl = declarations.find(ts.isEnumMember);
2279    if (enumMemberDecl) {
2280      const value = this.tsTypeChecker.getConstantValue(enumMemberDecl);
2281      return typeof value === STRINGLITERAL_NUMBER;
2282    }
2283
2284    const enumDecl = declarations.find(ts.isEnumDeclaration);
2285    if (enumDecl) {
2286      return enumDecl.members.every((member) => {
2287        const memberType = this.tsTypeChecker.getTypeAtLocation(member.name);
2288        return (memberType.flags & ts.TypeFlags.NumberLike) !== 0;
2289      });
2290    }
2291    return false;
2292  }
2293
2294  private checkAssignmentNumericSemanticsFuntion(node: ts.FunctionDeclaration): void {
2295    if (!this.options.arkts2) {
2296      return;
2297    }
2298    for (const param of node.parameters) {
2299      if (param.type) {
2300        continue;
2301      }
2302      const sym = this.tsTypeChecker.getSymbolAtLocation(param.name);
2303      if (!sym) {
2304        continue;
2305      }
2306
2307      const type = this.tsTypeChecker.getTypeOfSymbolAtLocation(sym, param.name);
2308      const typeText = this.tsTypeChecker.typeToString(type);
2309      if (typeText === STRINGLITERAL_NUMBER) {
2310        const autofix = this.autofixer?.fixParameter(param);
2311        if (autofix) {
2312          this.incrementCounters(node, FaultID.NumericSemantics, autofix);
2313        }
2314      }
2315    }
2316    if (!node.type) {
2317      const signature = this.tsTypeChecker.getSignatureFromDeclaration(node);
2318      if (!signature) {
2319        return;
2320      }
2321      const retType = this.tsTypeChecker.getReturnTypeOfSignature(signature);
2322      if ((retType.getFlags() & ts.TypeFlags.Number) !== 0) {
2323        const returnTypeNode = this.tsTypeChecker.typeToTypeNode(retType, node, ts.NodeBuilderFlags.None);
2324        if (!returnTypeNode) {
2325          return;
2326        }
2327        const autofix = this.autofixer?.fixMissingReturnType(node, returnTypeNode);
2328        this.incrementCounters(node, FaultID.NumericSemantics, autofix);
2329      }
2330    }
2331  }
2332
2333  private checkAssignmentNumericSemanticslyPro(node: ts.PropertyDeclaration): void {
2334    if (!this.options.arkts2) {
2335      return;
2336    }
2337
2338    const initializer = node.initializer;
2339    const name = node.name;
2340    if (node.type || !initializer || !ts.isIdentifier(name)) {
2341      return;
2342    }
2343
2344    const isNumberArray = ts.isArrayLiteralExpression(initializer) && TypeScriptLinter.isNumberArray(initializer);
2345    const isNumber = !isNumberArray && TypeScriptLinter.isNumericInitializer(initializer);
2346
2347    const sym = this.tsTypeChecker.getSymbolAtLocation(name);
2348    if (!sym) {
2349      return;
2350    }
2351
2352    if (!isNumber && !isNumberArray) {
2353      return;
2354    }
2355    const type = this.tsTypeChecker.getTypeOfSymbolAtLocation(sym, name);
2356    const typeText = this.tsTypeChecker.typeToString(type);
2357    const typeFlags = type.flags;
2358    if (isNumber && (typeText === STRINGLITERAL_NUMBER || (typeFlags & ts.TypeFlags.NumberLiteral) !== 0)) {
2359      const autofix = this.autofixer?.fixPropertyDeclaration(node);
2360      this.incrementCounters(node, FaultID.NumericSemantics, autofix);
2361    }
2362    this.checkAssignmentNumericSemanticsArray(node, isNumberArray);
2363  }
2364
2365  checkAssignmentNumericSemanticsArray(node: ts.PropertyDeclaration, isNumberArray: boolean): void {
2366    if (isNumberArray) {
2367      const autofix = this.autofixer?.fixPropertyDeclarationNumericSemanticsArray(node);
2368      this.incrementCounters(node, FaultID.NumericSemantics, autofix);
2369    }
2370  }
2371
2372  private static isNumericInitializer(node: ts.Node): boolean {
2373    if (ts.isNumericLiteral(node)) {
2374      return true;
2375    }
2376    if (
2377      ts.isPrefixUnaryExpression(node) &&
2378      node.operator === ts.SyntaxKind.MinusToken &&
2379      ts.isNumericLiteral(node.operand)
2380    ) {
2381      return true;
2382    }
2383    return false;
2384  }
2385
2386  private static isNumberArray(arrayLiteral: ts.ArrayLiteralExpression): boolean {
2387    return arrayLiteral.elements.every((element) => {
2388      if (ts.isSpreadElement(element)) {
2389        return false;
2390      }
2391      return TypeScriptLinter.isNumericInitializer(element);
2392    });
2393  }
2394
2395  private handleDestructuringAssignment(node: ts.Node, tsLhsExpr: ts.Expression, tsRhsExpr: ts.Expression): void {
2396    if (ts.isObjectLiteralExpression(tsLhsExpr)) {
2397      const autofix = this.autofixer?.fixObjectLiteralExpressionDestructAssignment(node as ts.BinaryExpression);
2398      this.incrementCounters(node, FaultID.DestructuringAssignment, autofix);
2399    } else if (ts.isArrayLiteralExpression(tsLhsExpr)) {
2400      const rhsType = this.tsTypeChecker.getTypeAtLocation(tsRhsExpr);
2401      const isArrayOrTuple =
2402        this.tsUtils.isOrDerivedFrom(rhsType, this.tsUtils.isArray) ||
2403        this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple);
2404      const hasNestedObjectDestructuring = TsUtils.hasNestedObjectDestructuring(tsLhsExpr);
2405
2406      if (
2407        !this.options.useRelaxedRules ||
2408        !isArrayOrTuple ||
2409        hasNestedObjectDestructuring ||
2410        TsUtils.destructuringAssignmentHasSpreadOperator(tsLhsExpr)
2411      ) {
2412        const autofix = this.autofixer?.fixArrayBindingPatternAssignment(node as ts.BinaryExpression, isArrayOrTuple);
2413        this.incrementCounters(node, FaultID.DestructuringAssignment, autofix);
2414      }
2415    }
2416  }
2417
2418  private processBinaryComma(tsBinaryExpr: ts.BinaryExpression): void {
2419    // CommaOpertor is allowed in 'for' statement initalizer and incrementor
2420    let tsExprNode: ts.Node = tsBinaryExpr;
2421    let tsParentNode = tsExprNode.parent;
2422    while (tsParentNode && tsParentNode.kind === ts.SyntaxKind.BinaryExpression) {
2423      tsExprNode = tsParentNode;
2424      tsParentNode = tsExprNode.parent;
2425      if ((tsExprNode as ts.BinaryExpression).operatorToken.kind === ts.SyntaxKind.CommaToken) {
2426        // Need to return if one comma enclosed in expression with another comma to avoid multiple reports on one line
2427        return;
2428      }
2429    }
2430    if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ForStatement) {
2431      const tsForNode = tsParentNode as ts.ForStatement;
2432      if (tsExprNode === tsForNode.initializer || tsExprNode === tsForNode.incrementor) {
2433        return;
2434      }
2435    }
2436    if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ExpressionStatement) {
2437      const autofix = this.autofixer?.fixCommaOperator(tsExprNode);
2438      this.incrementCounters(tsExprNode, FaultID.CommaOperator, autofix);
2439      return;
2440    }
2441
2442    this.incrementCounters(tsBinaryExpr as ts.Node, FaultID.CommaOperator);
2443  }
2444
2445  private processBinaryInstanceOf(node: ts.Node, tsLhsExpr: ts.Expression, leftOperandType: ts.Type): void {
2446    const leftExpr = TsUtils.unwrapParenthesized(tsLhsExpr);
2447    const leftSymbol = this.tsUtils.trueSymbolAtLocation(leftExpr);
2448
2449    /*
2450     * In ETS, the left-hand side expression may be of any reference type, otherwise
2451     * a compile-time error occurs. In addition, the left operand in ETS cannot be a type.
2452     */
2453    if (tsLhsExpr.kind === ts.SyntaxKind.ThisKeyword) {
2454      return;
2455    }
2456
2457    if (TsUtils.isPrimitiveType(leftOperandType) || ts.isTypeNode(leftExpr) || TsUtils.isTypeSymbol(leftSymbol)) {
2458      this.incrementCounters(node, FaultID.InstanceofUnsupported);
2459    }
2460  }
2461
2462  private handleVariableDeclarationList(node: ts.Node): void {
2463    const varDeclFlags = ts.getCombinedNodeFlags(node);
2464    if (!(varDeclFlags & (ts.NodeFlags.Let | ts.NodeFlags.Const))) {
2465      const autofix = this.autofixer?.fixVarDeclaration(node as ts.VariableDeclarationList);
2466      this.incrementCounters(node, FaultID.VarDeclaration, autofix);
2467    }
2468  }
2469
2470  private handleVariableDeclaration(node: ts.Node): void {
2471    const tsVarDecl = node as ts.VariableDeclaration;
2472    this.handleVariableDeclarationForProp(tsVarDecl);
2473    if (
2474      !this.options.useRtLogic ||
2475      ts.isVariableDeclarationList(tsVarDecl.parent) && ts.isVariableStatement(tsVarDecl.parent.parent)
2476    ) {
2477      this.handleDeclarationDestructuring(tsVarDecl);
2478    }
2479
2480    // Check variable declaration for duplicate name.
2481    this.checkVarDeclForDuplicateNames(tsVarDecl.name);
2482
2483    if (tsVarDecl.type && tsVarDecl.initializer) {
2484      this.checkAssignmentMatching(
2485        tsVarDecl,
2486        this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type),
2487        tsVarDecl.initializer
2488      );
2489      this.checkFunctionTypeCompatible(tsVarDecl.type, tsVarDecl.initializer);
2490    }
2491    this.handleEsValueDeclaration(tsVarDecl);
2492    this.handleDeclarationInferredType(tsVarDecl);
2493    this.handleDefiniteAssignmentAssertion(tsVarDecl);
2494    this.handleLimitedVoidType(tsVarDecl);
2495    this.handleInvalidIdentifier(tsVarDecl);
2496    this.checkAssignmentNumericSemanticsly(tsVarDecl);
2497    this.checkTypeFromSdk(tsVarDecl.type);
2498    this.handleNoStructuralTyping(tsVarDecl);
2499    this.handleObjectLiteralforUnionTypeInterop(tsVarDecl);
2500    this.handleObjectLiteralAssignmentToClass(tsVarDecl);
2501    this.handleObjectLiteralAssignment(tsVarDecl);
2502    this.handlePropertyDescriptorInScenarios(tsVarDecl);
2503    this.handleSdkGlobalApi(tsVarDecl);
2504  }
2505
2506  private checkTypeFromSdk(type: ts.TypeNode | undefined): void {
2507    if (!this.options.arkts2 || !type) {
2508      return;
2509    }
2510
2511    const fullTypeName = type.getText();
2512    const nameArr = fullTypeName.split('.');
2513    const sdkInfos = this.interfaceMap.get(nameArr[0]);
2514    if (!sdkInfos || sdkInfos.size === 0) {
2515      return;
2516    }
2517
2518    for (const sdkInfo of sdkInfos) {
2519      if (sdkInfo.api_name && nameArr.includes(sdkInfo.api_name)) {
2520        this.incrementCounters(type, FaultID.LimitedVoidTypeFromSdk);
2521        return;
2522      }
2523    }
2524  }
2525
2526  private static extractUsedObjectType(tsVarDecl: ts.VariableDeclaration): InterfaceSymbolTypePropertyNames | null {
2527    const result = {
2528      propertyNames: [] as string[],
2529      typeNames: [] as string[]
2530    };
2531
2532    if (!this.isObjectLiteralWithProperties(tsVarDecl)) {
2533      return null;
2534    }
2535
2536    this.processObjectLiteralProperties(tsVarDecl.initializer as ts.ObjectLiteralExpression, result);
2537    return result.propertyNames.length > 0 ? result : null;
2538  }
2539
2540  private static isObjectLiteralWithProperties(tsVarDecl: ts.VariableDeclaration): boolean {
2541    return (
2542      tsVarDecl.initializer !== undefined &&
2543      ts.isObjectLiteralExpression(tsVarDecl.initializer) &&
2544      tsVarDecl.initializer.properties.length > 0
2545    );
2546  }
2547
2548  private static processObjectLiteralProperties(
2549    objectLiteral: ts.ObjectLiteralExpression,
2550    result: { propertyNames: string[]; typeNames: string[] }
2551  ): void {
2552    objectLiteral.properties.forEach((property) => {
2553      if (!ts.isPropertyAssignment(property)) {
2554        return;
2555      }
2556
2557      const propertyName = property.name.getText();
2558      result.propertyNames.push(propertyName);
2559
2560      if (ts.isNewExpression(property.initializer)) {
2561        const typeName = property.initializer.expression.getText();
2562        result.typeNames.push(typeName);
2563      }
2564    });
2565  }
2566
2567  private interfaceSymbolType(tsVarDecl: ts.VariableDeclaration): InterfaceSymbolTypeResult | null {
2568    if (!tsVarDecl.type) {
2569      return null;
2570    }
2571
2572    const typeSymbol = this.getTypeSymbol(tsVarDecl);
2573    if (!typeSymbol) {
2574      return null;
2575    }
2576
2577    const interfaceType = this.getInterfaceType(tsVarDecl);
2578    if (!interfaceType) {
2579      return null;
2580    }
2581
2582    return this.collectInterfaceProperties(interfaceType, tsVarDecl);
2583  }
2584
2585  private getTypeSymbol(tsVarDecl: ts.VariableDeclaration): ts.Symbol | null {
2586    const typeNode = ts.isTypeReferenceNode(tsVarDecl.type!) ? tsVarDecl.type.typeName : tsVarDecl.type;
2587    return this.tsTypeChecker.getSymbolAtLocation(typeNode!) ?? null;
2588  }
2589
2590  private getInterfaceType(tsVarDecl: ts.VariableDeclaration): ts.InterfaceType | null {
2591    const type = this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type!);
2592    return type && (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Interface ? (type as ts.InterfaceType) : null;
2593  }
2594
2595  private collectInterfaceProperties(
2596    interfaceType: ts.InterfaceType,
2597    tsVarDecl: ts.VariableDeclaration
2598  ): InterfaceSymbolTypeResult {
2599    const result = {
2600      propNames: [] as string[],
2601      typeNames: [] as string[],
2602      allProps: new Map<string, string>()
2603    };
2604
2605    this.collectPropertiesRecursive(interfaceType, result, tsVarDecl);
2606    return result;
2607  }
2608
2609  private collectPropertiesRecursive(
2610    type: ts.Type,
2611    result: {
2612      propNames: string[];
2613      typeNames: string[];
2614      allProps: Map<string, string>;
2615    },
2616    tsVarDecl: ts.VariableDeclaration
2617  ): void {
2618    type.getProperties().forEach((property) => {
2619      this.collectProperty(property, result, tsVarDecl);
2620    });
2621
2622    if ('getBaseTypes' in type) {
2623      type.getBaseTypes()?.forEach((baseType) => {
2624        this.collectPropertiesRecursive(baseType, result, tsVarDecl);
2625      });
2626    }
2627  }
2628
2629  private collectProperty(
2630    property: ts.Symbol,
2631    result: {
2632      propNames: string[];
2633      typeNames: string[];
2634      allProps: Map<string, string>;
2635    },
2636    tsVarDecl: ts.VariableDeclaration
2637  ): void {
2638    const propName = property.getName();
2639    const propType = this.tsTypeChecker.getTypeOfSymbolAtLocation(
2640      property,
2641      property.valueDeclaration || tsVarDecl.type!
2642    );
2643    const typeString = this.tsTypeChecker.typeToString(propType);
2644
2645    if (!result.allProps.has(propName)) {
2646      result.propNames.push(propName);
2647      result.typeNames.push(typeString);
2648      result.allProps.set(propName, typeString);
2649    }
2650  }
2651
2652  handleNoStructuralTyping(tsVarDecl: ts.VariableDeclaration): void {
2653    const { interfaceInfo, actualUsage } = this.getTypeComparisonData(tsVarDecl);
2654    if (!interfaceInfo || !actualUsage) {
2655      return;
2656    }
2657    if (!this.options.arkts2) {
2658      return;
2659    }
2660    const actualMap = TypeScriptLinter.createActualTypeMap(actualUsage);
2661    const hasMismatch = TypeScriptLinter.checkTypeMismatches(interfaceInfo, actualMap);
2662
2663    if (hasMismatch) {
2664      this.incrementCounters(tsVarDecl, FaultID.StructuralIdentity);
2665    }
2666  }
2667
2668  private getTypeComparisonData(tsVarDecl: ts.VariableDeclaration): {
2669    interfaceInfo: { propNames: string[]; typeNames: string[]; allProps: Map<string, string> } | null;
2670    actualUsage: {
2671      propertyNames: string[];
2672      typeNames: string[];
2673    } | null;
2674  } {
2675    return {
2676      interfaceInfo: this.interfaceSymbolType(tsVarDecl),
2677      actualUsage: TypeScriptLinter.extractUsedObjectType(tsVarDecl)
2678    };
2679  }
2680
2681  private static createActualTypeMap(actualUsage: {
2682    propertyNames: string[];
2683    typeNames: string[];
2684  }): Map<string, string> {
2685    const actualMap = new Map<string, string>();
2686    actualUsage.propertyNames.forEach((prop, index) => {
2687      if (actualUsage.typeNames[index]) {
2688        actualMap.set(prop, actualUsage.typeNames[index]);
2689      }
2690    });
2691    return actualMap;
2692  }
2693
2694  private static checkTypeMismatches(
2695    interfaceInfo: { allProps: Map<string, string> },
2696    actualMap: Map<string, string>
2697  ): boolean {
2698    let hasMismatch = false;
2699
2700    interfaceInfo.allProps.forEach((expectedType, prop) => {
2701      if (!actualMap.has(prop)) {
2702        return;
2703      }
2704
2705      const actualType = actualMap.get(prop)!;
2706      if (expectedType !== actualType) {
2707        hasMismatch = true;
2708      }
2709    });
2710
2711    return hasMismatch;
2712  }
2713
2714  private handleDeclarationDestructuring(decl: ts.VariableDeclaration | ts.ParameterDeclaration): void {
2715    const faultId = ts.isVariableDeclaration(decl) ? FaultID.DestructuringDeclaration : FaultID.DestructuringParameter;
2716    if (ts.isObjectBindingPattern(decl.name)) {
2717      const autofix = ts.isVariableDeclaration(decl) ?
2718        this.autofixer?.fixObjectBindingPatternDeclarations(decl) :
2719        undefined;
2720      this.incrementCounters(decl, faultId, autofix);
2721    } else if (ts.isArrayBindingPattern(decl.name)) {
2722      // Array destructuring is allowed only for Arrays/Tuples and without spread operator.
2723      const rhsType = this.tsTypeChecker.getTypeAtLocation(decl.initializer ?? decl.name);
2724      const isArrayOrTuple =
2725        rhsType &&
2726        (this.tsUtils.isOrDerivedFrom(rhsType, this.tsUtils.isArray) ||
2727          this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple));
2728      const hasNestedObjectDestructuring = TsUtils.hasNestedObjectDestructuring(decl.name);
2729
2730      if (
2731        !this.options.useRelaxedRules ||
2732        !isArrayOrTuple ||
2733        hasNestedObjectDestructuring ||
2734        TsUtils.destructuringDeclarationHasSpreadOperator(decl.name)
2735      ) {
2736        const autofix = ts.isVariableDeclaration(decl) ?
2737          this.autofixer?.fixArrayBindingPatternDeclarations(decl, isArrayOrTuple) :
2738          undefined;
2739        this.incrementCounters(decl, faultId, autofix);
2740      }
2741    }
2742  }
2743
2744  private checkVarDeclForDuplicateNames(tsBindingName: ts.BindingName): void {
2745    if (ts.isIdentifier(tsBindingName)) {
2746      // The syntax kind of the declaration is defined here by the parent of 'BindingName' node.
2747      this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind);
2748      return;
2749    }
2750    for (const tsBindingElem of tsBindingName.elements) {
2751      if (ts.isOmittedExpression(tsBindingElem)) {
2752        continue;
2753      }
2754
2755      this.checkVarDeclForDuplicateNames(tsBindingElem.name);
2756    }
2757  }
2758
2759  private handleEsValueDeclaration(node: ts.VariableDeclaration): void {
2760    const isDeclaredESValue = !!node.type && TsUtils.isEsValueType(node.type);
2761    const initalizerTypeNode = node.initializer && this.tsUtils.getVariableDeclarationTypeNode(node.initializer);
2762    const isInitializedWithESValue = !!initalizerTypeNode && TsUtils.isEsValueType(initalizerTypeNode);
2763    const isLocal = TsUtils.isInsideBlock(node);
2764    if ((isDeclaredESValue || isInitializedWithESValue) && !isLocal) {
2765      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
2766      this.incrementCounters(node, faultId);
2767      return;
2768    }
2769
2770    if (node.initializer) {
2771      this.handleEsObjectAssignment(node, node.type, node.initializer);
2772    }
2773  }
2774
2775  private handleEsObjectAssignment(node: ts.Node, nodeDeclType: ts.TypeNode | undefined, initializer: ts.Node): void {
2776    const isTypeAnnotated = !!nodeDeclType;
2777    const isDeclaredESValue = isTypeAnnotated && TsUtils.isEsValueType(nodeDeclType);
2778    const initalizerTypeNode = this.tsUtils.getVariableDeclarationTypeNode(initializer);
2779    const isInitializedWithESValue = !!initalizerTypeNode && TsUtils.isEsValueType(initalizerTypeNode);
2780    if (isTypeAnnotated && !isDeclaredESValue && isInitializedWithESValue) {
2781      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
2782      this.incrementCounters(node, faultId);
2783      return;
2784    }
2785
2786    if (isDeclaredESValue && !this.tsUtils.isValueAssignableToESValue(initializer)) {
2787      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
2788      this.incrementCounters(node, faultId);
2789    }
2790  }
2791
2792  private handleCatchClause(node: ts.Node): void {
2793    const tsCatch = node as ts.CatchClause;
2794
2795    /*
2796     * In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'.
2797     * It is not compatible with ETS 'catch' where the exception variable has to be of type
2798     * Error or derived from it.
2799     * So each 'catch' which has explicit type for the exception object goes to problems.
2800     */
2801    if (tsCatch.variableDeclaration?.type) {
2802      const autofix = this.autofixer?.dropTypeOnVarDecl(tsCatch.variableDeclaration);
2803      this.incrementCounters(node, FaultID.CatchWithUnsupportedType, autofix);
2804    }
2805
2806    if (this.options.arkts2 && tsCatch.variableDeclaration?.name) {
2807      const varDeclName = tsCatch.variableDeclaration?.name.getText();
2808      tsCatch.block.statements.forEach((statement) => {
2809        this.checkTsLikeCatchType(statement, varDeclName);
2810      });
2811    }
2812  }
2813
2814  private checkTsLikeCatchType(node: ts.Node, variableDeclarationName: string): void {
2815    if (!node) {
2816      return;
2817    }
2818    for (const child of node.getChildren()) {
2819      if (ts.isPropertyAccessExpression(child)) {
2820        if (child.expression.getText() === variableDeclarationName && !ERROR_PROP_LIST.has(child.name.getText())) {
2821          this.incrementCounters(child, FaultID.TsLikeCatchType);
2822        }
2823      }
2824      this.checkTsLikeCatchType(child, variableDeclarationName);
2825    }
2826  }
2827
2828  private handleClassExtends(tsClassDecl: ts.ClassDeclaration): void {
2829    if (!this.options.arkts2) {
2830      return;
2831    }
2832    const allClasses = TypeScriptLinter.getAllClassesFromSourceFile(this.sourceFile);
2833    const classMap = new Map<string, ts.ClassDeclaration>();
2834    allClasses.forEach((classDecl) => {
2835      if (classDecl.name && !classDecl.heritageClauses) {
2836        classMap.set(classDecl.name.getText(), classDecl);
2837      }
2838    });
2839    if (!tsClassDecl.heritageClauses) {
2840      return;
2841    }
2842    tsClassDecl.heritageClauses.forEach((clause) => {
2843      clause.types.forEach((type) => {
2844        const baseClassName = type.expression.getText();
2845        const baseClass = classMap.get(baseClassName);
2846        if (baseClass && ts.isClassDeclaration(baseClass)) {
2847          this.checkMembersConsistency(tsClassDecl, baseClass);
2848        }
2849      });
2850    });
2851  }
2852
2853  private checkMembersConsistency(derivedClass: ts.ClassDeclaration, baseClass: ts.ClassDeclaration): void {
2854    const baseMethods = new Set<string>();
2855    baseClass.members.forEach((member) => {
2856      if (ts.isMethodDeclaration(member)) {
2857        baseMethods.add(member.name.getText());
2858      }
2859    });
2860    derivedClass.members.forEach((member) => {
2861      const memberName = member.name?.getText();
2862      if (memberName && baseMethods.has(memberName)) {
2863        if (ts.isPropertyDeclaration(member)) {
2864          this.incrementCounters(member, FaultID.MethodOverridingField);
2865        }
2866      }
2867    });
2868  }
2869
2870  private handleClassDeclaration(node: ts.Node): void {
2871    // early exit via exception if cancellation was requested
2872    this.options.cancellationToken?.throwIfCancellationRequested();
2873
2874    const tsClassDecl = node as ts.ClassDeclaration;
2875    this.handleClassExtends(tsClassDecl);
2876    if (tsClassDecl.name) {
2877      this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl);
2878    }
2879    this.countClassMembersWithDuplicateName(tsClassDecl);
2880
2881    const isSendableClass = TsUtils.hasSendableDecorator(tsClassDecl);
2882    if (isSendableClass) {
2883      TsUtils.getNonSendableDecorators(tsClassDecl)?.forEach((decorator) => {
2884        this.incrementCounters(decorator, FaultID.SendableClassDecorator);
2885      });
2886      tsClassDecl.typeParameters?.forEach((typeParamDecl) => {
2887        this.checkSendableTypeParameter(typeParamDecl);
2888      });
2889    }
2890
2891    if (tsClassDecl.heritageClauses) {
2892      for (const hClause of tsClassDecl.heritageClauses) {
2893        if (!hClause) {
2894          continue;
2895        }
2896        this.checkClassDeclarationHeritageClause(hClause, isSendableClass);
2897      }
2898    }
2899
2900    // Check captured variables for sendable class
2901    if (isSendableClass) {
2902      tsClassDecl.members.forEach((classMember) => {
2903        this.scanCapturedVarsInSendableScope(classMember, tsClassDecl, FaultID.SendableCapturedVars);
2904      });
2905    }
2906
2907    this.processClassStaticBlocks(tsClassDecl);
2908    this.handleInvalidIdentifier(tsClassDecl);
2909    this.handleSdkMethod(tsClassDecl);
2910    this.handleNotsLikeSmartType(tsClassDecl);
2911  }
2912
2913  private static findFinalExpression(typeNode: ts.TypeNode): ts.Node {
2914    let currentNode = typeNode;
2915
2916    /*
2917     * CC-OFFNXT(no_explicit_any) std lib
2918     * Handle comment directive '@ts-nocheck'
2919     */
2920    while ((currentNode as any).expression) {
2921
2922      /*
2923       * CC-OFFNXT(no_explicit_any) std lib
2924       * Handle comment directive '@ts-nocheck'
2925       */
2926      currentNode = (currentNode as any).expression;
2927    }
2928    return currentNode;
2929  }
2930
2931  private processSdkMethodClauseTypes(
2932    tsClassDecl: ts.ClassDeclaration,
2933    heritageClause: ts.HeritageClause,
2934    methodName?: string
2935  ): boolean {
2936    return heritageClause.types.some((type) => {
2937      const parentName = ts.isPropertyAccessExpression(type.expression) ?
2938        type.expression.name.text :
2939        type.expression.getText();
2940      const fullTypeName = TypeScriptLinter.findFinalExpression(type).getText();
2941      const sdkInfos = this.interfaceMap.get(fullTypeName);
2942      if (!sdkInfos || sdkInfos.size === 0) {
2943        return false;
2944      }
2945
2946      return Array.from(sdkInfos).some((sdkInfo) => {
2947        if (sdkInfo.api_type !== METHOD_SIGNATURE && sdkInfo.api_type !== METHOD_DECLARATION) {
2948          return false;
2949        }
2950
2951        if (!methodName && sdkInfo.parent_api[0].api_name === parentName) {
2952          this.processSdkInfoWithMembers(sdkInfo, tsClassDecl.members, tsClassDecl);
2953          return false;
2954        }
2955
2956        const symbol = this.tsTypeChecker.getSymbolAtLocation(type.expression);
2957        return TypeScriptLinter.isHeritageClauseisThirdPartyBySymbol(symbol) && sdkInfo.api_name === methodName;
2958      });
2959    });
2960  }
2961
2962  private handleSdkMethod(tsClassDecl: ts.ClassDeclaration): void {
2963    if (
2964      !this.options.arkts2 ||
2965      !tsClassDecl.heritageClauses ||
2966      tsClassDecl.heritageClauses.length === 0 ||
2967      !tsClassDecl.members ||
2968      tsClassDecl.members.length === 0
2969    ) {
2970      return;
2971    }
2972
2973    for (const heritageClause of tsClassDecl.heritageClauses) {
2974      if (!heritageClause.types || heritageClause.types.length === 0) {
2975        continue;
2976      }
2977      this.processSdkMethodClauseTypes(tsClassDecl, heritageClause);
2978    }
2979  }
2980
2981  private processSdkInfoWithMembers(
2982    sdkInfo: ApiInfo,
2983    members: ts.NodeArray<ts.ClassElement>,
2984    tsClassDecl: ts.ClassDeclaration
2985  ): void {
2986    for (const member of members) {
2987      if (!ts.isMethodDeclaration(member)) {
2988        continue;
2989      }
2990
2991      const memberName = member.name?.getText();
2992      if (sdkInfo.api_name === memberName) {
2993        if (
2994          !TypeScriptLinter.areParametersEqual(sdkInfo.api_func_args ?? [], member.parameters) &&
2995          !TypeScriptLinter.areGenericsParametersEqual(sdkInfo.api_func_args ?? [], tsClassDecl)
2996        ) {
2997          return;
2998        }
2999        this.incrementCounters(
3000          member,
3001          sdkInfo.problem === OPTIONAL_METHOD ? FaultID.OptionalMethodFromSdk : FaultID.LimitedVoidTypeFromSdk
3002        );
3003      }
3004    }
3005  }
3006
3007  private static areParametersEqual(
3008    sdkFuncArgs: { name: string; type: string }[],
3009    memberParams: ts.NodeArray<ts.ParameterDeclaration>
3010  ): boolean {
3011    const apiParamCout = sdkFuncArgs.length;
3012    const memberParamCout = memberParams.length;
3013    if (apiParamCout > memberParamCout && sdkFuncArgs[memberParamCout]) {
3014      return false;
3015    }
3016
3017    for (let i = 0; i < apiParamCout; i++) {
3018      const typeName = memberParams[i]?.type?.getText();
3019      if (!typeName?.match(sdkFuncArgs[i].type)) {
3020        return false;
3021      }
3022    }
3023    return true;
3024  }
3025
3026  private processLimitedVoidTypeFromSdkOnClassDeclaration(
3027    tsClassDecl: ts.ClassDeclaration,
3028    methodName?: string
3029  ): boolean {
3030    if (
3031      !this.options.arkts2 ||
3032      !tsClassDecl.heritageClauses ||
3033      tsClassDecl.heritageClauses.length === 0 ||
3034      !tsClassDecl.members ||
3035      tsClassDecl.members.length === 0
3036    ) {
3037      return false;
3038    }
3039    let res: boolean = false;
3040    for (const heritageClause of tsClassDecl.heritageClauses) {
3041      if (heritageClause.types?.length) {
3042        res = this.processSdkMethodClauseTypes(tsClassDecl, heritageClause, methodName);
3043        break;
3044      }
3045    }
3046    return res;
3047  }
3048
3049  private static isHeritageClauseisThirdPartyBySymbol(symbol: ts.Symbol | undefined): boolean {
3050    if (!symbol) {
3051      return false;
3052    }
3053    const declarations = symbol.getDeclarations();
3054    if (declarations && declarations.length > 0) {
3055      const firstDeclaration = declarations[0];
3056      if (ts.isImportSpecifier(firstDeclaration)) {
3057        return true;
3058      }
3059    }
3060    return false;
3061  }
3062
3063  private handleLimitedVoidTypeFromSdkOnPropertyAccessExpression(node: ts.PropertyAccessExpression): void {
3064    if (!this.options.arkts2) {
3065      return;
3066    }
3067    const sym = this.getOriginalSymbol(node.name);
3068    if (!sym) {
3069      return;
3070    }
3071    const methodName = node.name.getText();
3072    const declaration = sym.declarations?.[0];
3073    if (declaration && ts.isClassDeclaration(declaration.parent)) {
3074      if (this.processLimitedVoidTypeFromSdkOnClassDeclaration(declaration.parent, methodName)) {
3075        this.incrementCounters(node, FaultID.LimitedVoidTypeFromSdk);
3076      }
3077    }
3078  }
3079
3080  private static areGenericsParametersEqual(
3081    sdkFuncArgs: { name: string; type: string }[],
3082    node: ts.ClassDeclaration
3083  ): boolean {
3084    if (!ts.isClassDeclaration(node)) {
3085      return false;
3086    }
3087    const apiParamCout = sdkFuncArgs.length;
3088    const typeParameters = node.typeParameters;
3089    if (!typeParameters) {
3090      return false;
3091    }
3092    typeParameters.forEach((typeParam) => {
3093      if (!typeParam.constraint) {
3094        return false;
3095      }
3096      for (let i = 0; i < apiParamCout; i++) {
3097        if (!typeParam.constraint.getText().match(sdkFuncArgs[i].type)) {
3098          return false;
3099        }
3100      }
3101      return true;
3102    });
3103    return true;
3104  }
3105
3106  private handleNotSupportCustomDecorators(decorator: ts.Decorator): void {
3107    if (!this.options.arkts2) {
3108      return;
3109    }
3110
3111    let decoratorName;
3112    if (ts.isCallExpression(decorator.expression)) {
3113      decoratorName = decorator.expression.expression.getText(this.sourceFile);
3114    } else {
3115      decoratorName = decorator.expression.getText(this.sourceFile);
3116    }
3117    if (!DEFAULT_DECORATOR_WHITE_LIST.includes(decoratorName)) {
3118      this.incrementCounters(decorator, FaultID.DecoratorsNotSupported);
3119    }
3120  }
3121
3122  private checkClassDeclarationHeritageClause(hClause: ts.HeritageClause, isSendableClass: boolean): void {
3123    for (const tsTypeExpr of hClause.types) {
3124
3125      /*
3126       * Always resolve type from 'tsTypeExpr' node, not from 'tsTypeExpr.expression' node,
3127       * as for the latter, type checker will return incorrect type result for classes in
3128       * 'extends' clause. Additionally, reduce reference, as mostly type checker returns
3129       * the TypeReference type objects for classes and interfaces.
3130       */
3131      const tsExprType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(tsTypeExpr));
3132      const isSendableBaseType = this.tsUtils.isSendableClassOrInterface(tsExprType);
3133      if (tsExprType.isClass() && hClause.token === ts.SyntaxKind.ImplementsKeyword) {
3134        this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass);
3135      }
3136      if (!isSendableClass) {
3137        // Non-Sendable class can not implements sendable interface / extends sendable class
3138        if (isSendableBaseType) {
3139          const autofix = this.autofixer?.addClassSendableDecorator(hClause, tsTypeExpr);
3140          this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance, autofix);
3141        }
3142        continue;
3143      }
3144
3145      /*
3146       * Sendable class can implements any interface / extends only sendable class
3147       * Sendable class can not extends sendable class variable(local / import)
3148       */
3149      if (hClause.token === ts.SyntaxKind.ExtendsKeyword) {
3150        if (!isSendableBaseType) {
3151          this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance);
3152          continue;
3153        }
3154        if (!this.tsUtils.isValidSendableClassExtends(tsTypeExpr)) {
3155          this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance);
3156        }
3157      }
3158    }
3159  }
3160
3161  private checkSendableTypeParameter(typeParamDecl: ts.TypeParameterDeclaration): void {
3162    const defaultTypeNode = typeParamDecl.default;
3163    if (defaultTypeNode) {
3164      if (!this.tsUtils.isSendableTypeNode(defaultTypeNode)) {
3165        this.incrementCounters(defaultTypeNode, FaultID.SendableGenericTypes);
3166      }
3167    }
3168  }
3169
3170  private processClassStaticBlocks(classDecl: ts.ClassDeclaration): void {
3171    let staticBlocksCntr = 0;
3172    const staticBlockNodes: ts.Node[] = [];
3173    for (const element of classDecl.members) {
3174      if (ts.isClassStaticBlockDeclaration(element)) {
3175        if (this.options.arkts2 && this.useStatic) {
3176          this.incrementCounters(element, FaultID.NoStaticOnClass);
3177        }
3178        staticBlockNodes[staticBlocksCntr] = element;
3179        staticBlocksCntr++;
3180      }
3181    }
3182    if (staticBlocksCntr > 1) {
3183      const autofix = this.autofixer?.fixMultipleStaticBlocks(staticBlockNodes);
3184      // autofixes for all additional static blocks are the same
3185      for (let i = 1; i < staticBlocksCntr; i++) {
3186        this.incrementCounters(staticBlockNodes[i], FaultID.MultipleStaticBlocks, autofix);
3187      }
3188    }
3189  }
3190
3191  private handleModuleDeclaration(node: ts.Node): void {
3192    // early exit via exception if cancellation was requested
3193    this.options.cancellationToken?.throwIfCancellationRequested();
3194
3195    const tsModuleDecl = node as ts.ModuleDeclaration;
3196
3197    this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl);
3198
3199    if (this.options.arkts2) {
3200      this.handleInvalidIdentifier(tsModuleDecl);
3201    }
3202
3203    const tsModuleBody = tsModuleDecl.body;
3204    const tsModifiers = ts.getModifiers(tsModuleDecl);
3205    if (tsModuleBody) {
3206      if (ts.isModuleBlock(tsModuleBody)) {
3207        this.handleModuleBlock(tsModuleBody);
3208      }
3209    }
3210
3211    if (
3212      this.options.arkts2 &&
3213      tsModuleBody &&
3214      ts.isModuleBlock(tsModuleBody) &&
3215      tsModuleDecl.flags & ts.NodeFlags.Namespace
3216    ) {
3217      this.handleNameSpaceModuleBlock(tsModuleBody, (tsModuleDecl.name as ts.Identifier).escapedText.toString());
3218    }
3219
3220    if (
3221      !(tsModuleDecl.flags & ts.NodeFlags.Namespace) &&
3222      TsUtils.hasModifier(tsModifiers, ts.SyntaxKind.DeclareKeyword)
3223    ) {
3224      this.incrementCounters(tsModuleDecl, FaultID.ShorthandAmbientModuleDecl);
3225    }
3226
3227    if (ts.isStringLiteral(tsModuleDecl.name) && tsModuleDecl.name.text.includes('*')) {
3228      this.incrementCounters(tsModuleDecl, FaultID.WildcardsInModuleName);
3229    }
3230  }
3231
3232  private handleNameSpaceModuleBlock(moduleBlock: ts.ModuleBlock, nameSpace: string): void {
3233    if (!TypeScriptLinter.nameSpaceFunctionCache.has(nameSpace)) {
3234      TypeScriptLinter.nameSpaceFunctionCache.set(nameSpace, new Set<string>());
3235    }
3236
3237    const nameSet = TypeScriptLinter.nameSpaceFunctionCache.get(nameSpace)!;
3238
3239    for (const statement of moduleBlock.statements) {
3240      const names = TypeScriptLinter.getDeclarationNames(statement);
3241      for (const name of names) {
3242        if (nameSet.has(name)) {
3243          this.incrementCounters(statement, FaultID.NoDuplicateFunctionName);
3244        } else {
3245          nameSet.add(name);
3246        }
3247      }
3248    }
3249  }
3250
3251  private static getDeclarationNames(statement: ts.Statement): Set<string> {
3252    const names = new Set<string>();
3253
3254    if (
3255      ts.isFunctionDeclaration(statement) && statement.name && statement.body ||
3256      ts.isClassDeclaration(statement) && statement.name ||
3257      ts.isInterfaceDeclaration(statement) && statement.name ||
3258      ts.isEnumDeclaration(statement) && statement.name
3259    ) {
3260      names.add(statement.name.text);
3261      return names;
3262    }
3263
3264    if (ts.isVariableStatement(statement)) {
3265      for (const decl of statement.declarationList.declarations) {
3266        if (ts.isIdentifier(decl.name)) {
3267          names.add(decl.name.text);
3268        }
3269      }
3270    }
3271
3272    return names;
3273  }
3274
3275  private handleModuleBlock(moduleBlock: ts.ModuleBlock): void {
3276    for (const tsModuleStmt of moduleBlock.statements) {
3277      switch (tsModuleStmt.kind) {
3278        case ts.SyntaxKind.VariableStatement:
3279        case ts.SyntaxKind.FunctionDeclaration:
3280        case ts.SyntaxKind.ClassDeclaration:
3281        case ts.SyntaxKind.InterfaceDeclaration:
3282        case ts.SyntaxKind.TypeAliasDeclaration:
3283        case ts.SyntaxKind.EnumDeclaration:
3284        case ts.SyntaxKind.ExportDeclaration:
3285          break;
3286
3287        /*
3288         * Nested namespace declarations are prohibited
3289         * but there is no cookbook recipe for it!
3290         */
3291        case ts.SyntaxKind.ModuleDeclaration:
3292          break;
3293        default:
3294          this.incrementCounters(tsModuleStmt, FaultID.NonDeclarationInNamespace);
3295          break;
3296      }
3297    }
3298  }
3299
3300  private handleTypeAliasDeclaration(node: ts.Node): void {
3301    const tsTypeAlias = node as ts.TypeAliasDeclaration;
3302    this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias);
3303    this.handleInvalidIdentifier(tsTypeAlias);
3304    if (TsUtils.hasSendableDecorator(tsTypeAlias)) {
3305      if (!this.isSendableDecoratorValid(tsTypeAlias)) {
3306        return;
3307      }
3308      TsUtils.getNonSendableDecorators(tsTypeAlias)?.forEach((decorator) => {
3309        this.incrementCounters(decorator, FaultID.SendableTypeAliasDecorator);
3310      });
3311      if (!ts.isFunctionTypeNode(tsTypeAlias.type)) {
3312        this.incrementCounters(tsTypeAlias.type, FaultID.SendableTypeAliasDeclaration);
3313      }
3314    }
3315    if (this.options.arkts2 && tsTypeAlias.type.kind === ts.SyntaxKind.VoidKeyword) {
3316      this.incrementCounters(tsTypeAlias.type, FaultID.LimitedVoidType);
3317    }
3318  }
3319
3320  private handleTupleType(node: ts.TupleTypeNode): void {
3321    if (!this.options.arkts2) {
3322      return;
3323    }
3324
3325    node.elements.forEach((elementType) => {
3326      if (elementType.kind === ts.SyntaxKind.VoidKeyword) {
3327        this.incrementCounters(elementType, FaultID.LimitedVoidType);
3328      }
3329    });
3330  }
3331
3332  private handleImportClause(node: ts.Node): void {
3333    const tsImportClause = node as ts.ImportClause;
3334    if (this.options.arkts2 && tsImportClause.isLazy) {
3335      const autofix = this.autofixer?.fixImportClause(tsImportClause);
3336      this.incrementCounters(node, FaultID.ImportLazyIdentifier, autofix);
3337    }
3338    if (tsImportClause.name) {
3339      this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause);
3340    }
3341  }
3342
3343  private handleImportSpecifier(node: ts.Node): void {
3344    const importSpec = node as ts.ImportSpecifier;
3345    this.countDeclarationsWithDuplicateName(importSpec.name, importSpec);
3346  }
3347
3348  private handleNamespaceImport(node: ts.Node): void {
3349    const tsNamespaceImport = node as ts.NamespaceImport;
3350    this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport);
3351  }
3352
3353  private handleTypeAssertionExpression(node: ts.Node): void {
3354    const tsTypeAssertion = node as ts.TypeAssertion;
3355    if (tsTypeAssertion.type.getText() === 'const') {
3356      this.incrementCounters(tsTypeAssertion, FaultID.ConstAssertion);
3357    } else {
3358      const autofix = this.autofixer?.fixTypeAssertion(tsTypeAssertion);
3359      this.incrementCounters(node, FaultID.TypeAssertion, autofix);
3360    }
3361  }
3362
3363  private handleMethodDeclaration(node: ts.Node): void {
3364    const tsMethodDecl = node as ts.MethodDeclaration;
3365    TsUtils.getDecoratorsIfInSendableClass(tsMethodDecl)?.forEach((decorator) => {
3366      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
3367    });
3368    let isStatic = false;
3369    if (tsMethodDecl.modifiers) {
3370      for (const mod of tsMethodDecl.modifiers) {
3371        if (mod.kind === ts.SyntaxKind.StaticKeyword) {
3372          isStatic = true;
3373          break;
3374        }
3375      }
3376    }
3377    if (tsMethodDecl.body && isStatic) {
3378      this.reportThisKeywordsInScope(tsMethodDecl.body);
3379    }
3380    if (!tsMethodDecl.type) {
3381      this.handleMissingReturnType(tsMethodDecl);
3382    }
3383    if (tsMethodDecl.asteriskToken) {
3384      this.incrementCounters(node, FaultID.GeneratorFunction);
3385    }
3386    this.filterOutDecoratorsDiagnostics(
3387      ts.getDecorators(tsMethodDecl),
3388      NON_RETURN_FUNCTION_DECORATORS,
3389      { begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end },
3390      FUNCTION_HAS_NO_RETURN_ERROR_CODE
3391    );
3392    if (this.options.arkts2 && tsMethodDecl.questionToken) {
3393      this.incrementCounters(tsMethodDecl.questionToken, FaultID.OptionalMethod);
3394    }
3395    this.handleInvalidIdentifier(tsMethodDecl);
3396    if (!this.tsUtils.isAbstractMethodInAbstractClass(node)) {
3397      this.handleTSOverload(tsMethodDecl);
3398    }
3399    this.checkDefaultParamBeforeRequired(tsMethodDecl);
3400    this.handleMethodInherit(tsMethodDecl);
3401    this.handleSdkGlobalApi(tsMethodDecl);
3402    this.handleLimitedVoidFunction(tsMethodDecl);
3403    this.checkVoidLifecycleReturn(tsMethodDecl);
3404  }
3405
3406  private handleLimitedVoidFunction(node: ts.FunctionLikeDeclaration): void {
3407    const typeNode = node.type;
3408    if (!typeNode || !ts.isUnionTypeNode(typeNode)) {
3409      return;
3410    }
3411    const containsVoid = typeNode.types.some((t) => {
3412      return t.kind === ts.SyntaxKind.VoidKeyword;
3413    });
3414    if (this.options.arkts2 && containsVoid) {
3415      const autofix = this.autofixer?.fixLimitedVoidTypeFunction(node);
3416      this.incrementCounters(typeNode, FaultID.LimitedVoidType, autofix);
3417    }
3418  }
3419
3420  private checkDefaultParamBeforeRequired(node: ts.FunctionLikeDeclarationBase): void {
3421    if (!this.options.arkts2) {
3422      return;
3423    }
3424
3425    const params = node.parameters;
3426    let seenRequired = false;
3427
3428    for (let i = params.length - 1; i >= 0; i--) {
3429      const param = params[i];
3430
3431      const isOptional = !!param.initializer || !!param.questionToken;
3432
3433      if (!isOptional) {
3434        seenRequired = true;
3435        continue;
3436      }
3437
3438      if (seenRequired && param.initializer) {
3439        this.incrementCounters(param.name, FaultID.DefaultArgsBehindRequiredArgs);
3440      }
3441    }
3442  }
3443
3444  private handleMethodInherit(node: ts.MethodDeclaration): void {
3445    if (!this.options.arkts2 || !node.name || !ts.isIdentifier(node.name)) {
3446      return;
3447    }
3448
3449    const classDecl = node.parent;
3450    if (!ts.isClassDeclaration(classDecl)) {
3451      return;
3452    }
3453
3454    const classType = this.tsTypeChecker.getTypeAtLocation(classDecl);
3455    const allBaseTypes = this.getAllBaseTypes(classType, classDecl);
3456    if (!allBaseTypes || allBaseTypes.length === 0) {
3457      return;
3458    }
3459
3460    const methodName = node.name.text;
3461
3462    for (const baseType of allBaseTypes) {
3463      const baseMethod = baseType.getProperty(methodName);
3464      if (!baseMethod) {
3465        continue;
3466      }
3467
3468      const baseMethodDecl = baseMethod.declarations?.find(
3469        (d) => {
3470          return (ts.isMethodDeclaration(d) || ts.isMethodSignature(d)) &&
3471          this.tsTypeChecker.getTypeAtLocation(d.parent) === baseType;
3472        }
3473      ) as ts.MethodDeclaration | ts.MethodSignature;
3474
3475      if (!baseMethodDecl) {
3476        continue;
3477      }
3478
3479      this.checkMethodParameters(node, baseMethodDecl);
3480
3481      this.checkMethodReturnType(node, baseMethodDecl);
3482
3483      break;
3484    }
3485  }
3486
3487  private getAllBaseTypes(type: ts.Type, classDecl: ts.ClassDeclaration): ts.Type[] | undefined {
3488    const baseClasses = type.getBaseTypes() || [];
3489    if (!classDecl.heritageClauses) {
3490      return baseClasses;
3491    }
3492    const interfaces: ts.Type[] = [];
3493    for (const clause of classDecl.heritageClauses) {
3494      if (clause.token !== ts.SyntaxKind.ImplementsKeyword) {
3495        continue;
3496      }
3497      for (const typeNode of clause.types) {
3498        const interfaceType = this.tsTypeChecker.getTypeAtLocation(typeNode);
3499        interfaces.push(interfaceType);
3500        const parentInterfaces = interfaceType.getBaseTypes();
3501        if (parentInterfaces) {
3502          interfaces.push(...parentInterfaces);
3503        }
3504      }
3505    }
3506    return [...baseClasses, ...interfaces];
3507  }
3508
3509  /**
3510   * Checks method parameter compatibility
3511   * Derived parameter types must be same or wider than base (contravariance principle)
3512   */
3513  private checkMethodParameters(
3514    derivedMethod: ts.MethodDeclaration,
3515    baseMethod: ts.MethodDeclaration | ts.MethodSignature
3516  ): void {
3517    const derivedParams = derivedMethod.parameters;
3518    const baseParams = baseMethod.parameters;
3519
3520    if (derivedParams.length !== baseParams.length) {
3521      this.incrementCounters(derivedMethod.name, FaultID.MethodInheritRule);
3522      return;
3523    }
3524
3525    const paramCount = Math.min(derivedParams.length, baseParams.length);
3526
3527    for (let i = 0; i < paramCount; i++) {
3528      const baseParamType = this.tsTypeChecker.getTypeAtLocation(baseParams[i]);
3529      const derivedParamType = this.tsTypeChecker.getTypeAtLocation(derivedParams[i]);
3530
3531      if (!this.isTypeSameOrWider(baseParamType, derivedParamType)) {
3532        this.incrementCounters(derivedParams[i], FaultID.MethodInheritRule);
3533      }
3534    }
3535  }
3536
3537  /**
3538   * Checks return type compatibility
3539   * Derived return type must be same or narrower than base (covariance principle)
3540   */
3541  private checkMethodReturnType(
3542    derivedMethod: ts.MethodDeclaration,
3543    baseMethod: ts.MethodDeclaration | ts.MethodSignature
3544  ): void {
3545    if (
3546      this.IsVoidTypeOnActualReturnType(baseMethod) &&
3547      derivedMethod.type &&
3548      !this.IsVoidTypeOnActualReturnType(derivedMethod)
3549    ) {
3550      this.incrementCounters(derivedMethod.type, FaultID.MethodInheritRule);
3551      return;
3552    }
3553
3554    if (!baseMethod.type || !derivedMethod.type) {
3555      return;
3556    }
3557
3558    const baseReturnType = this.tsTypeChecker.getTypeAtLocation(baseMethod.type);
3559    const derivedReturnType = this.tsTypeChecker.getTypeAtLocation(derivedMethod.type);
3560
3561    if (this.isDerivedTypeAssignable(derivedReturnType, baseReturnType)) {
3562      return;
3563    }
3564
3565    if (!this.isTypeAssignable(derivedReturnType, baseReturnType)) {
3566      this.incrementCounters(derivedMethod.type, FaultID.MethodInheritRule);
3567    }
3568  }
3569
3570  private IsVoidTypeOnActualReturnType(method: ts.MethodDeclaration | ts.MethodSignature): boolean | undefined {
3571    let type: ts.Type | undefined;
3572    if (method.type) {
3573      type = this.tsTypeChecker.getTypeAtLocation(method.type);
3574    } else {
3575      const signature = this.tsTypeChecker.getSignatureFromDeclaration(method);
3576      if (signature) {
3577        type = this.tsTypeChecker.getReturnTypeOfSignature(signature);
3578      }
3579    }
3580    return type && TsUtils.isVoidType(type);
3581  }
3582
3583  /**
3584   * Child type should include all types of parent type (be same or wider).
3585   * Returns true if every type in baseType is also included in derivedType.
3586   */
3587  private isTypeSameOrWider(baseType: ts.Type, derivedType: ts.Type): boolean {
3588    const baseTypeSet = new Set(this.flattenUnionTypes(baseType));
3589    const derivedTypeSet = new Set(this.flattenUnionTypes(derivedType));
3590
3591    // Check if every type in baseType is also present in derivedType
3592    for (const typeStr of baseTypeSet) {
3593      if (!derivedTypeSet.has(typeStr)) {
3594        return false;
3595      }
3596    }
3597
3598    return true;
3599  }
3600
3601  // Checks structural assignability between two types.
3602  private isTypeAssignable(fromType: ts.Type, toType: ts.Type): boolean {
3603    if (this.isDerivedTypeAssignable(fromType, toType)) {
3604      return true;
3605    }
3606    const fromTypes = this.flattenUnionTypes(fromType);
3607    const toTypes = new Set(this.flattenUnionTypes(toType));
3608
3609    // All types in `fromTypes` should exist in `toTypes` for assignability.
3610    return fromTypes.every((typeStr) => {
3611      return toTypes.has(typeStr);
3612    });
3613  }
3614
3615  private isDerivedTypeAssignable(derivedType: ts.Type, baseType: ts.Type): boolean {
3616    const baseSymbol = baseType.getSymbol();
3617    const derivedSymbol = derivedType.getSymbol();
3618
3619    if (!baseSymbol || !derivedSymbol) {
3620      return false;
3621    }
3622    const baseDeclarations = baseSymbol.getDeclarations();
3623    const derivedDeclarations = derivedSymbol.getDeclarations();
3624
3625    if (!baseDeclarations || !derivedDeclarations) {
3626      return false;
3627    }
3628    const baseTypeNode = baseDeclarations[0];
3629    const derivedTypeNode = derivedDeclarations[0];
3630
3631    if (ts.isClassDeclaration(baseTypeNode) && ts.isClassDeclaration(derivedTypeNode)) {
3632      const baseTypes = this.tsTypeChecker.getTypeAtLocation(derivedTypeNode).getBaseTypes();
3633      const baseTypesExtends = baseTypes?.some((t) => {
3634        return t === baseType;
3635      });
3636      if (baseTypesExtends) {
3637        return true;
3638      }
3639    }
3640
3641    return false;
3642  }
3643
3644  // Converts union types into an array of type strings for easy comparison.
3645  private flattenUnionTypes(type: ts.Type): string[] {
3646    if (type.isUnion()) {
3647      return type.types.map((t) => {
3648        return this.tsTypeChecker.typeToString(t);
3649      });
3650    }
3651    return [this.tsTypeChecker.typeToString(type)];
3652  }
3653
3654  private checkClassImplementsMethod(classDecl: ts.ClassDeclaration, methodName: string): boolean {
3655    for (const member of classDecl.members) {
3656      if (member.name?.getText() === methodName) {
3657        if (ts.isPropertyDeclaration(member)) {
3658          this.incrementCounters(member, FaultID.MethodOverridingField);
3659        }
3660      }
3661    }
3662    return false;
3663  }
3664
3665  private handleMethodSignature(node: ts.MethodSignature): void {
3666    const tsMethodSign = node;
3667    if (this.options.arkts2 && ts.isInterfaceDeclaration(node.parent)) {
3668      const methodName = node.name.getText();
3669      const interfaceName = node.parent.name.getText();
3670      const allClasses = TypeScriptLinter.getAllClassesFromSourceFile(this.sourceFile);
3671      const allInterfaces = TypeScriptLinter.getAllInterfaceFromSourceFile(this.sourceFile);
3672      allClasses.forEach((classDecl) => {
3673        if (this.classImplementsInterface(classDecl, interfaceName)) {
3674          this.checkClassImplementsMethod(classDecl, methodName);
3675        }
3676      });
3677      allInterfaces.forEach((interDecl) => {
3678        if (this.interfaceExtendsInterface(interDecl, interfaceName)) {
3679          this.checkInterfaceExtendsMethod(interDecl, methodName);
3680        }
3681      });
3682    }
3683    if (!tsMethodSign.type) {
3684      this.handleMissingReturnType(tsMethodSign);
3685    }
3686    if (this.options.arkts2 && tsMethodSign.questionToken) {
3687      this.incrementCounters(tsMethodSign.questionToken, FaultID.OptionalMethod);
3688    }
3689    this.handleInvalidIdentifier(tsMethodSign);
3690  }
3691
3692  private interfaceExtendsInterface(interDecl: ts.InterfaceDeclaration, interfaceName: string): boolean {
3693    void this;
3694    if (!interDecl.heritageClauses) {
3695      return false;
3696    }
3697    return interDecl.heritageClauses.some((clause) => {
3698      return clause.types.some((type) => {
3699        return (
3700          ts.isExpressionWithTypeArguments(type) &&
3701          ts.isIdentifier(type.expression) &&
3702          type.expression.text === interfaceName
3703        );
3704      });
3705    });
3706  }
3707
3708  private checkInterfaceExtendsMethod(interDecl: ts.InterfaceDeclaration, methodName: string): void {
3709    for (const member of interDecl.members) {
3710      if (member.name?.getText() === methodName) {
3711        if (ts.isPropertySignature(member)) {
3712          this.incrementCounters(member, FaultID.MethodOverridingField);
3713        }
3714      }
3715    }
3716  }
3717
3718  private classImplementsInterface(classDecl: ts.ClassDeclaration, interfaceName: string): boolean {
3719    void this;
3720    if (!classDecl.heritageClauses) {
3721      return false;
3722    }
3723    return classDecl.heritageClauses.some((clause) => {
3724      return clause.types.some((type) => {
3725        return (
3726          ts.isExpressionWithTypeArguments(type) &&
3727          ts.isIdentifier(type.expression) &&
3728          type.expression.text === interfaceName
3729        );
3730      });
3731    });
3732  }
3733
3734  private handleClassStaticBlockDeclaration(node: ts.Node): void {
3735    const classStaticBlockDecl = node as ts.ClassStaticBlockDeclaration;
3736    if (!ts.isClassDeclaration(classStaticBlockDecl.parent)) {
3737      return;
3738    }
3739    this.reportThisKeywordsInScope(classStaticBlockDecl.body);
3740  }
3741
3742  private handleIdentifier(node: ts.Node): void {
3743    if (!ts.isIdentifier(node)) {
3744      return;
3745    }
3746    this.handleInterfaceImport(node);
3747    this.checkAsonSymbol(node);
3748    const tsIdentifier = node;
3749    this.handleTsInterop(tsIdentifier, () => {
3750      const parent = tsIdentifier.parent;
3751      if (ts.isPropertyAccessExpression(parent) || ts.isImportSpecifier(parent)) {
3752        return;
3753      }
3754
3755      const type = this.tsTypeChecker.getTypeAtLocation(tsIdentifier);
3756      this.checkUsageOfTsTypes(type, tsIdentifier);
3757    });
3758
3759    const tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier);
3760    if (!tsIdentSym) {
3761      return;
3762    }
3763
3764    const isArkTs2 = this.options.arkts2;
3765    const isGlobalThis = tsIdentifier.text === 'globalThis';
3766
3767    if (
3768      isGlobalThis &&
3769      (tsIdentSym.flags & ts.SymbolFlags.Module) !== 0 &&
3770      (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0
3771    ) {
3772      this.handleGlobalThisCase(tsIdentifier, isArkTs2);
3773    } else {
3774      if (isArkTs2) {
3775        this.checkLimitedStdlibApi(tsIdentifier, tsIdentSym);
3776      }
3777      this.handleRestrictedValues(tsIdentifier, tsIdentSym);
3778    }
3779
3780    if (isArkTs2 && this.tsTypeChecker.isArgumentsSymbol(tsIdentSym)) {
3781      this.incrementCounters(node, FaultID.ArgumentsObject);
3782    }
3783
3784    if (isArkTs2) {
3785      this.checkWorkerSymbol(tsIdentSym, node);
3786      this.checkCollectionsSymbol(tsIdentSym, node);
3787      this.checkConcurrencySymbol(tsIdentSym, node);
3788    }
3789  }
3790
3791  private handlePropertyDescriptorInScenarios(node: ts.Node): void {
3792    if (ts.isVariableDeclaration(node)) {
3793      const name = node.name;
3794      this.handlePropertyDescriptor(name);
3795
3796      const type = node.type;
3797      if (!type || !ts.isTypeReferenceNode(type)) {
3798        return;
3799      }
3800      const typeName = type.typeName;
3801      this.handlePropertyDescriptor(typeName);
3802    }
3803
3804    if (ts.isParameter(node)) {
3805      const name = node.name;
3806      this.handlePropertyDescriptor(name);
3807
3808      const type = node.type;
3809      if (!type || !ts.isTypeReferenceNode(type)) {
3810        return;
3811      }
3812      const typeName = type.typeName;
3813      this.handlePropertyDescriptor(typeName);
3814    }
3815
3816    if (ts.isPropertyAccessExpression(node)) {
3817      const name = node.name;
3818      this.handlePropertyDescriptor(name);
3819
3820      const expression = node.expression;
3821      this.handlePropertyDescriptor(expression);
3822    }
3823  }
3824
3825  private handlePropertyDescriptor(node: ts.Node): void {
3826    if (!this.options.arkts2) {
3827      return;
3828    }
3829
3830    const symbol = this.tsUtils.trueSymbolAtLocation(node);
3831    if (!symbol || !ts.isIdentifier(node)) {
3832      return;
3833    }
3834    const tsIdentifier = node;
3835    const type = this.tsTypeChecker.getTypeOfSymbolAtLocation(symbol, tsIdentifier);
3836
3837    const typeSymbol = type.getSymbol();
3838    const typeName = typeSymbol ? typeSymbol.getName() : symbol.getName();
3839
3840    const noPropertyDescriptorSet = TypeScriptLinter.globalApiInfo.get(BuiltinProblem.BuiltinNoPropertyDescriptor);
3841    if (!noPropertyDescriptorSet) {
3842      return;
3843    }
3844
3845    const matchedApi = [...noPropertyDescriptorSet].some((apiInfoItem) => {
3846      if (apiInfoItem.api_info.parent_api?.length <= 0) {
3847        return false;
3848      }
3849      const apiInfoParentName = apiInfoItem.api_info.parent_api[0].api_name;
3850      const apiTypeName = apiInfoItem.api_info.method_return_type;
3851      const isSameApi = apiInfoParentName === typeName || apiTypeName === typeName;
3852      const decl = TsUtils.getDeclaration(typeSymbol ? typeSymbol : symbol);
3853      const sourceFileName = path.normalize(decl?.getSourceFile().fileName || '');
3854      const isSameFile = sourceFileName.endsWith(path.normalize(apiInfoItem.file_path));
3855      return isSameFile && isSameApi;
3856    });
3857
3858    if (matchedApi) {
3859      this.incrementCounters(tsIdentifier, FaultID.NoPropertyDescriptor);
3860    }
3861  }
3862
3863  private handleGlobalThisCase(node: ts.Identifier, isArkTs2: boolean | undefined): void {
3864    let faultId = FaultID.GlobalThis;
3865    let targetNode: ts.Node = node;
3866
3867    if (!isArkTs2) {
3868      this.incrementCounters(targetNode, faultId);
3869      return;
3870    }
3871    faultId = FaultID.GlobalThisError;
3872
3873    if (ts.isPropertyAccessExpression(node.parent)) {
3874      const parentExpression = node.parent.parent;
3875      if (
3876        parentExpression &&
3877        ts.isBinaryExpression(parentExpression) &&
3878        parentExpression.operatorToken.kind === ts.SyntaxKind.EqualsToken
3879      ) {
3880        targetNode = parentExpression;
3881      } else {
3882        targetNode = node.parent;
3883      }
3884    }
3885
3886    this.incrementCounters(targetNode, faultId);
3887  }
3888
3889  // hard-coded alternative to TypeScriptLinter.advancedClassChecks
3890  private isAllowedClassValueContext(tsIdentifier: ts.Identifier): boolean {
3891    let ctx: ts.Node = tsIdentifier;
3892    while (ts.isPropertyAccessExpression(ctx.parent) || ts.isQualifiedName(ctx.parent)) {
3893      ctx = ctx.parent;
3894    }
3895    if (ts.isPropertyAssignment(ctx.parent) && ts.isObjectLiteralExpression(ctx.parent.parent)) {
3896      ctx = ctx.parent.parent;
3897    }
3898    if (ts.isArrowFunction(ctx.parent) && ctx.parent.body === ctx) {
3899      ctx = ctx.parent;
3900    }
3901
3902    if (ts.isCallExpression(ctx.parent) || ts.isNewExpression(ctx.parent)) {
3903      const callee = ctx.parent.expression;
3904      const isAny = TsUtils.isAnyType(this.tsTypeChecker.getTypeAtLocation(callee));
3905      const isDynamic = isAny || this.tsUtils.hasLibraryType(callee);
3906      if (callee !== ctx && isDynamic) {
3907        return true;
3908      }
3909    }
3910    return false;
3911  }
3912
3913  private isStdlibClassVarDecl(ident: ts.Identifier, sym: ts.Symbol): boolean {
3914
3915    /*
3916     * Most standard JS classes are defined in TS stdlib as ambient global
3917     * variables with interface constructor type and require special check
3918     * when they are being referenced in code.
3919     */
3920
3921    if (
3922      !isStdLibrarySymbol(sym) ||
3923      !sym.valueDeclaration ||
3924      !ts.isVariableDeclaration(sym.valueDeclaration) ||
3925      !TsUtils.isAmbientNode(sym.valueDeclaration)
3926    ) {
3927      return false;
3928    }
3929
3930    /*
3931     * issue 24075: TS supports calling the constructor of built-in types
3932     * as function (without 'new' keyword): `const a = Number('10')`
3933     * Such cases need to be filtered out.
3934     */
3935    if (ts.isCallExpression(ident.parent) && ident.parent.expression === ident) {
3936      return false;
3937    }
3938
3939    const classVarDeclType = StdClassVarDecls.get(sym.name);
3940    if (!classVarDeclType) {
3941      return false;
3942    }
3943    const declType = this.tsTypeChecker.getTypeAtLocation(ident);
3944    return declType.symbol && declType.symbol.name === classVarDeclType;
3945  }
3946
3947  private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol): void {
3948    const illegalValues =
3949      ts.SymbolFlags.ConstEnum |
3950      ts.SymbolFlags.RegularEnum |
3951      ts.SymbolFlags.ValueModule |
3952      (this.options.advancedClassChecks ? 0 : ts.SymbolFlags.Class);
3953
3954    /*
3955     * If module name is duplicated by another declaration, this increases the possibility
3956     * of finding a lot of false positives. Thus, do not check further in that case.
3957     */
3958    if ((tsIdentSym.flags & ts.SymbolFlags.ValueModule) !== 0) {
3959      if (!!tsIdentSym && TsUtils.symbolHasDuplicateName(tsIdentSym, ts.SyntaxKind.ModuleDeclaration)) {
3960        return;
3961      }
3962    }
3963
3964    if (
3965      (tsIdentSym.flags & illegalValues) === 0 && !this.isStdlibClassVarDecl(tsIdentifier, tsIdentSym) ||
3966      isStruct(tsIdentSym) ||
3967      !identiferUseInValueContext(tsIdentifier, tsIdentSym)
3968    ) {
3969      return;
3970    }
3971
3972    if ((tsIdentSym.flags & ts.SymbolFlags.Class) !== 0) {
3973      if (!this.options.advancedClassChecks && this.isAllowedClassValueContext(tsIdentifier)) {
3974        return;
3975      }
3976    }
3977
3978    this.handleIllegalSymbolUsage(tsIdentifier, tsIdentSym);
3979  }
3980
3981  private handleIllegalSymbolUsage(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol): void {
3982    if (tsIdentSym.flags & ts.SymbolFlags.ValueModule) {
3983      this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject);
3984      return;
3985    }
3986
3987    const typeName = tsIdentifier.getText();
3988    const isWrapperObject = typeName === 'Number' || typeName === 'String' || typeName === 'Boolean';
3989    if (isWrapperObject) {
3990      return;
3991    }
3992
3993    // Special-case element-access cast for autofix: (X as object)["prop"]
3994    const asExpr = tsIdentifier.parent;
3995    let elemAccess: ts.ElementAccessExpression | undefined;
3996
3997    if (
3998      ts.isAsExpression(asExpr) &&
3999      ts.isParenthesizedExpression(asExpr.parent) &&
4000      ts.isElementAccessExpression(asExpr.parent.parent) &&
4001      ts.isStringLiteral(asExpr.parent.parent.argumentExpression)
4002    ) {
4003      // only care if it’s literally “as object” && static-class casts
4004      if (asExpr.type.getText() === 'object' && tsIdentSym.flags & ts.SymbolFlags.Class) {
4005        elemAccess = asExpr.parent.parent;
4006      }
4007    }
4008
4009    const autofix = elemAccess ? this.autofixer?.fixPropertyAccessByIndex(elemAccess) : undefined;
4010    const faultId = this.options.arkts2 ? FaultID.ClassAsObjectError : FaultID.ClassAsObject;
4011    this.incrementCounters(tsIdentifier, faultId, autofix);
4012  }
4013
4014  private isElementAcessAllowed(type: ts.Type, argType: ts.Type): boolean {
4015    if (type.isUnion()) {
4016      for (const t of type.types) {
4017        if (!this.isElementAcessAllowed(t, argType)) {
4018          return false;
4019        }
4020      }
4021      return true;
4022    }
4023
4024    const typeNode = this.tsTypeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None);
4025
4026    if (this.tsUtils.isArkTSCollectionsArrayLikeType(type)) {
4027      return this.tsUtils.isNumberLikeType(argType);
4028    }
4029
4030    return (
4031      this.tsUtils.isLibraryType(type) ||
4032      TsUtils.isAnyType(type) ||
4033      this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isIndexableArray) ||
4034      this.tsUtils.isOrDerivedFrom(type, TsUtils.isTuple) ||
4035      this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdRecordType) ||
4036      this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStringType) ||
4037      !this.options.arkts2 &&
4038        (this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdMapType) || TsUtils.isIntrinsicObjectType(type)) ||
4039      TsUtils.isEnumType(type) ||
4040      // we allow EsObject here beacuse it is reported later using FaultId.EsObjectType
4041      TsUtils.isEsValueType(typeNode)
4042    );
4043  }
4044
4045  private handleElementAccessExpression(node: ts.Node): void {
4046    const tsElementAccessExpr = node as ts.ElementAccessExpression;
4047    const tsElemAccessBaseExprType = this.tsUtils.getNonNullableType(
4048      this.tsUtils.getTypeOrTypeConstraintAtLocation(tsElementAccessExpr.expression)
4049    );
4050    const tsElemAccessArgType = this.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.argumentExpression);
4051
4052    if (this.tsUtils.hasEsObjectType(tsElementAccessExpr.expression)) {
4053      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
4054      this.incrementCounters(node, faultId);
4055    }
4056    if (this.tsUtils.isOrDerivedFrom(tsElemAccessBaseExprType, this.tsUtils.isIndexableArray)) {
4057      this.handleIndexNegative(node);
4058    }
4059    this.checkPropertyAccessByIndex(tsElementAccessExpr, tsElemAccessBaseExprType, tsElemAccessArgType);
4060    this.checkArrayUsageWithoutBound(tsElementAccessExpr);
4061    this.checkArrayIndexType(tsElemAccessBaseExprType, tsElemAccessArgType, tsElementAccessExpr);
4062    this.fixJsImportElementAccessExpression(tsElementAccessExpr);
4063    this.checkInterOpImportJsIndex(tsElementAccessExpr);
4064    this.checkEnumGetMemberValue(tsElementAccessExpr);
4065  }
4066
4067  private checkPropertyAccessByIndex(
4068    tsElementAccessExpr: ts.ElementAccessExpression,
4069    tsElemAccessBaseExprType: ts.Type,
4070    tsElemAccessArgType: ts.Type
4071  ): void {
4072    const tsElementAccessExprSymbol = this.tsUtils.trueSymbolAtLocation(tsElementAccessExpr.expression);
4073
4074    const isSet = TsUtils.isSetExpression(tsElementAccessExpr);
4075    const isSetIndexable =
4076      isSet &&
4077      this.tsUtils.isSetIndexableType(
4078        tsElemAccessBaseExprType,
4079        tsElemAccessArgType,
4080        this.tsTypeChecker.getTypeAtLocation((tsElementAccessExpr.parent as ts.BinaryExpression).right)
4081      );
4082
4083    const isGet = !isSet;
4084    const isGetIndexable = isGet && this.tsUtils.isGetIndexableType(tsElemAccessBaseExprType, tsElemAccessArgType);
4085
4086    if (
4087      // unnamed types do not have symbol, so need to check that explicitly
4088      this.tsUtils.isLibrarySymbol(tsElementAccessExprSymbol) ||
4089      ts.isArrayLiteralExpression(tsElementAccessExpr.expression) ||
4090      this.isElementAcessAllowed(tsElemAccessBaseExprType, tsElemAccessArgType) ||
4091      this.options.arkts2 && isGetIndexable ||
4092      this.options.arkts2 && isSetIndexable
4093    ) {
4094      return;
4095    }
4096
4097    if (this.isStaticClassAccess(tsElementAccessExpr)) {
4098      return;
4099    }
4100
4101    const autofix = this.autofixer?.fixPropertyAccessByIndex(tsElementAccessExpr);
4102    this.incrementCounters(tsElementAccessExpr, FaultID.PropertyAccessByIndex, autofix);
4103  }
4104
4105  /**
4106   * Returns true if this element-access is a static-class cast (e.g., (A as object)["foo"]).
4107   */
4108  private isStaticClassAccess(expr: ts.ElementAccessExpression): boolean {
4109    const inner = expr.expression;
4110    if (
4111      ts.isParenthesizedExpression(inner) &&
4112      ts.isAsExpression(inner.expression) &&
4113      ts.isIdentifier(inner.expression.expression)
4114    ) {
4115      const sym = this.tsTypeChecker.getSymbolAtLocation(inner.expression.expression);
4116      return !!(sym && sym.flags & ts.SymbolFlags.Class);
4117    }
4118    return false;
4119  }
4120
4121  private checkInterOpImportJsIndex(expr: ts.ElementAccessExpression): void {
4122    if (!this.useStatic || !this.options.arkts2) {
4123      return;
4124    }
4125
4126    const exprSym = this.tsUtils.trueSymbolAtLocation(expr.expression);
4127    if (!exprSym) {
4128      return;
4129    }
4130
4131    const exprDecl = TsUtils.getDeclaration(exprSym);
4132    if (!exprDecl || !ts.isVariableDeclaration(exprDecl)) {
4133      return;
4134    }
4135
4136    const initializer = exprDecl.initializer;
4137    if (!initializer || !ts.isPropertyAccessExpression(initializer)) {
4138      return;
4139    }
4140
4141    const initSym = this.tsUtils.trueSymbolAtLocation(initializer.expression);
4142    if (!initSym) {
4143      return;
4144    }
4145
4146    const initDecl = TsUtils.getDeclaration(initSym);
4147    if (!initDecl?.getSourceFile().fileName.endsWith(EXTNAME_JS)) {
4148      return;
4149    }
4150
4151    if (ts.isBinaryExpression(expr.parent) && expr.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
4152      const autofix = this.autofixer?.fixInteropArrayBinaryExpression(expr.parent);
4153      this.incrementCounters(expr.parent, FaultID.InterOpImportJsIndex, autofix);
4154    } else {
4155      const autofix = this.autofixer?.fixInteropArrayElementAccessExpression(expr);
4156      this.incrementCounters(expr, FaultID.InterOpImportJsIndex, autofix);
4157    }
4158  }
4159
4160  private checkArrayIndexType(exprType: ts.Type, argType: ts.Type, expr: ts.ElementAccessExpression): void {
4161    if (!this.options.arkts2 || !this.tsUtils.isOrDerivedFrom(exprType, this.tsUtils.isIndexableArray)) {
4162      return;
4163    }
4164
4165    const validStringLiteralTypes = [
4166      STRINGLITERAL_INT,
4167      STRINGLITERAL_BYTE,
4168      STRINGLITERAL_SHORT,
4169      STRINGLITERAL_LONG,
4170      STRINGLITERAL_CHAR
4171    ];
4172    const argTypeString = this.tsTypeChecker.typeToString(argType);
4173
4174    if (this.tsUtils.isNumberLikeType(argType)) {
4175      this.handleNumericArgument(expr.argumentExpression, argType);
4176    } else if (!validStringLiteralTypes.includes(argTypeString)) {
4177      this.incrementCounters(expr.argumentExpression, FaultID.ArrayIndexExprType);
4178    }
4179  }
4180
4181  private handleNumericArgument(argExpr: ts.Expression, argType: ts.Type): void {
4182    const isNumericLiteral = ts.isNumericLiteral(argExpr);
4183    const argText = argExpr.getText();
4184    const argValue = Number(argText);
4185
4186    if (isNumericLiteral) {
4187      const isInteger = Number.isInteger(argValue);
4188      const containsDot = argText.includes('.');
4189
4190      if (!isInteger || containsDot) {
4191        const autofix = this.autofixer?.fixArrayIndexExprType(argExpr);
4192        this.incrementCounters(argExpr, FaultID.ArrayIndexExprType, autofix);
4193      }
4194    } else if (this.tsTypeChecker.typeToString(argType) === 'number') {
4195      if (this.isArrayIndexValidNumber(argExpr)) {
4196        return;
4197      }
4198      const autofix = this.autofixer?.fixArrayIndexExprType(argExpr);
4199      this.incrementCounters(argExpr, FaultID.ArrayIndexExprType, autofix);
4200    } else {
4201      this.checkNumericArgumentDeclaration(argExpr);
4202    }
4203
4204    if (ts.isConditionalExpression(argExpr)) {
4205      this.incrementCounters(argExpr, FaultID.ArrayIndexExprType);
4206    }
4207  }
4208
4209  private checkNumericArgumentDeclaration(argExpr: ts.Expression): void {
4210    const symbol = this.tsTypeChecker.getSymbolAtLocation(argExpr);
4211
4212    if (!symbol) {
4213      return;
4214    }
4215
4216    const declarations = symbol.getDeclarations();
4217    if (!declarations || declarations.length === 0) {
4218      return;
4219    }
4220
4221    const firstDeclaration = declarations[0] as ts.VariableDeclaration;
4222    const initializer = firstDeclaration.initializer;
4223    const initializerText = initializer ? initializer.getText() : 'undefined';
4224    const isNumericInitializer = initializer && ts.isNumericLiteral(initializer);
4225    const initializerNumber = isNumericInitializer ? Number(initializerText) : NaN;
4226    const isUnsafeNumber = isNumericInitializer && !Number.isInteger(initializerNumber);
4227
4228    if (isUnsafeNumber) {
4229      const autofix = this.autofixer?.fixArrayIndexExprType(argExpr);
4230      this.incrementCounters(argExpr, FaultID.ArrayIndexExprType, autofix);
4231    }
4232
4233    if (initializerText === 'undefined') {
4234      this.handleUndefinedInitializer(argExpr, firstDeclaration);
4235    }
4236  }
4237
4238  private evaluateValueFromDeclaration(argExpr: ts.Expression): number | null | 'skip' {
4239    const declaration = this.tsUtils.getDeclarationNode(argExpr);
4240    if (!declaration) {
4241      return null;
4242    }
4243
4244    if (!ts.isVariableDeclaration(declaration)) {
4245      return null;
4246    }
4247
4248    if (declaration.type !== undefined) {
4249      return 'skip';
4250    }
4251
4252    const initializer = declaration.initializer;
4253    if (!initializer) {
4254      return null;
4255    }
4256
4257    if (!ts.isNumericLiteral(initializer)) {
4258      return null;
4259    }
4260
4261    const numericValue = Number(initializer.text);
4262    if (!Number.isInteger(numericValue)) {
4263      return null;
4264    }
4265
4266    return numericValue;
4267  }
4268
4269  private isArrayIndexValidNumber(argExpr: ts.Expression): boolean {
4270    let evaluatedValue: number | null = null;
4271    if (ts.isParenthesizedExpression(argExpr)) {
4272      return this.isArrayIndexValidNumber(argExpr.expression);
4273    }
4274
4275    if (ts.isBinaryExpression(argExpr)) {
4276      evaluatedValue = this.evaluateNumericValueFromBinaryExpression(argExpr);
4277    } else {
4278      const evalResult = this.evaluateValueFromDeclaration(argExpr);
4279      if (evalResult === 'skip') {
4280        return false;
4281      }
4282      evaluatedValue = evalResult;
4283    }
4284
4285    if (evaluatedValue === null) {
4286      return false;
4287    }
4288
4289    const valueString = String(evaluatedValue);
4290
4291    if (!Number.isInteger(evaluatedValue)) {
4292      return false;
4293    }
4294    if (valueString.includes('.')) {
4295      return false;
4296    }
4297
4298    return evaluatedValue >= 0;
4299  }
4300
4301  private handleUndefinedInitializer(argExpr: ts.Expression, declaration: ts.VariableDeclaration): void {
4302    if (ts.isParameter(declaration)) {
4303      const autofix = this.autofixer?.fixArrayIndexExprType(argExpr);
4304      this.incrementCounters(argExpr, FaultID.ArrayIndexExprType, autofix);
4305    } else {
4306      this.incrementCounters(argExpr, FaultID.ArrayIndexExprType);
4307    }
4308  }
4309
4310  private handleEnumMember(node: ts.Node): void {
4311    const tsEnumMember = node as ts.EnumMember;
4312    const tsEnumMemberType = this.tsTypeChecker.getTypeAtLocation(tsEnumMember);
4313    const constVal = this.tsTypeChecker.getConstantValue(tsEnumMember);
4314    const tsEnumMemberName = tsEnumMember.name;
4315    if (this.options.arkts2) {
4316      this.handleInvalidIdentifier(tsEnumMember);
4317      if (ts.isStringLiteral(tsEnumMemberName)) {
4318        this.handleStringLiteralEnumMember(tsEnumMember, tsEnumMemberName, node);
4319      }
4320    }
4321
4322    if (tsEnumMember.initializer && !this.tsUtils.isValidEnumMemberInit(tsEnumMember.initializer)) {
4323      this.incrementCounters(node, FaultID.EnumMemberNonConstInit);
4324    }
4325    // check for type - all members should be of same type
4326    const enumDecl = tsEnumMember.parent;
4327    const firstEnumMember = enumDecl.members[0];
4328    const firstEnumMemberType = this.tsTypeChecker.getTypeAtLocation(firstEnumMember);
4329    const firstElewmVal = this.tsTypeChecker.getConstantValue(firstEnumMember);
4330    this.handleEnumNotSupportFloat(tsEnumMember);
4331
4332    /*
4333     * each string enum member has its own type
4334     * so check that value type is string
4335     */
4336    if (
4337      constVal !== undefined &&
4338      typeof constVal === STRINGLITERAL_STRING &&
4339      firstElewmVal !== undefined &&
4340      typeof firstElewmVal === STRINGLITERAL_STRING
4341    ) {
4342      return;
4343    }
4344    if (
4345      constVal !== undefined &&
4346      typeof constVal === STRINGLITERAL_NUMBER &&
4347      firstElewmVal !== undefined &&
4348      typeof firstElewmVal === STRINGLITERAL_NUMBER
4349    ) {
4350      return;
4351    }
4352    if (firstEnumMemberType !== tsEnumMemberType) {
4353      this.incrementCounters(node, FaultID.EnumMemberNonConstInit);
4354    }
4355  }
4356
4357  private handleStringLiteralEnumMember(
4358    tsEnumMember: ts.EnumMember,
4359    tsEnumMemberName: ts.StringLiteral,
4360    node: ts.Node
4361  ): void {
4362    const autofix = this.autofixer?.fixLiteralAsPropertyNamePropertyName(tsEnumMemberName, tsEnumMember);
4363    this.incrementCounters(node, FaultID.LiteralAsPropertyName, autofix);
4364  }
4365
4366  private handleEnumNotSupportFloat(enumMember: ts.EnumMember): void {
4367    if (!this.options.arkts2) {
4368      return;
4369    }
4370    const initializer = enumMember.initializer;
4371    if (!initializer) {
4372      return;
4373    }
4374    let value;
4375    if (ts.isNumericLiteral(initializer)) {
4376      value = parseFloat(initializer.text);
4377    } else if (ts.isPrefixUnaryExpression(initializer)) {
4378      const operand = initializer.operand;
4379      value = ts.isNumericLiteral(operand) ? parseFloat(operand.text) : value;
4380    } else {
4381      return;
4382    }
4383
4384    if (!Number.isInteger(value)) {
4385      this.incrementCounters(enumMember, FaultID.EnumMemberNonConstInit);
4386    }
4387  }
4388
4389  private handleExportAssignment(node: ts.Node): void {
4390    const exportAssignment = node as ts.ExportAssignment;
4391    if (exportAssignment.isExportEquals) {
4392      this.incrementCounters(node, FaultID.ExportAssignment);
4393    }
4394
4395    if (!TypeScriptLinter.inSharedModule(node)) {
4396      return;
4397    }
4398
4399    if (!this.tsUtils.isShareableEntity(exportAssignment.expression)) {
4400      this.incrementCounters(exportAssignment.expression, FaultID.SharedModuleExports);
4401    }
4402  }
4403
4404  private processCalleeSym(calleeSym: ts.Symbol, tsCallExpr: ts.CallExpression): void {
4405    const name = calleeSym.getName();
4406    const parName = this.tsUtils.getParentSymbolName(calleeSym);
4407    if (!this.options.arkts2) {
4408      this.handleStdlibAPICall(tsCallExpr, calleeSym, name, parName);
4409      this.handleFunctionApplyBindPropCall(tsCallExpr, calleeSym);
4410    } else {
4411      this.handleBuiltinThisArgs(tsCallExpr, calleeSym, name, parName);
4412    }
4413    if (TsUtils.symbolHasEsObjectType(calleeSym)) {
4414      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
4415      this.incrementCounters(tsCallExpr, faultId);
4416    }
4417    // Need to process Symbol call separately in order to not report two times when using Symbol API
4418    if (this.options.arkts2 && this.tsUtils.isStdSymbol(calleeSym)) {
4419      this.incrementCounters(tsCallExpr, FaultID.SymbolType);
4420    }
4421
4422    if (this.options.arkts2 && calleeSym.getEscapedName() === 'pow' && isStdLibrarySymbol(calleeSym)) {
4423      this.incrementCounters(tsCallExpr, FaultID.MathPow);
4424    }
4425
4426    if (this.options.arkts2 && calleeSym.getEscapedName() === 'RegExp' && isStdLibrarySymbol(calleeSym)) {
4427      const autofix = this.autofixer?.fixRegularExpressionLiteral(tsCallExpr);
4428      this.incrementCounters(tsCallExpr, FaultID.RegularExpressionLiteral, autofix);
4429    }
4430  }
4431
4432  private handleSdkPropertyAccessByIndex(tsCallExpr: ts.CallExpression): void {
4433    const propertyAccessNode = tsCallExpr.expression as ts.PropertyAccessExpression;
4434    if (!ts.isPropertyAccessExpression(propertyAccessNode)) {
4435      return;
4436    }
4437
4438    const funcName = propertyAccessNode.name;
4439    const indexedTypeSdkInfos = Array.from(TypeScriptLinter.indexedTypeSet);
4440    const isCallInDeprecatedApi = indexedTypeSdkInfos.some((indexedTypeSdkInfo) => {
4441      const isApiNameMismatch = funcName?.getText() !== indexedTypeSdkInfo.api_info.api_name;
4442      if (isApiNameMismatch) {
4443        return false;
4444      }
4445
4446      const funcDecls = this.tsTypeChecker.getTypeAtLocation(funcName).symbol?.declarations;
4447      return funcDecls?.some((declaration) => {
4448        const interfaceDecl = declaration.parent as ts.InterfaceDeclaration;
4449        if (!(ts.isMethodSignature(declaration) && ts.isInterfaceDeclaration(interfaceDecl))) {
4450          return false;
4451        }
4452        const declFileFromJson = path.normalize(interfaceDecl.getSourceFile().fileName);
4453        const declFileFromSdk = path.normalize(indexedTypeSdkInfo.file_path);
4454        const isSameSdkFilePath = declFileFromJson.endsWith(declFileFromSdk);
4455        const interfaceNameData = indexedTypeSdkInfo.api_info.parent_api[0].api_name;
4456        const isSameInterfaceName = interfaceDecl.name.getText() === interfaceNameData;
4457        return isSameSdkFilePath && isSameInterfaceName;
4458      });
4459    });
4460    if (isCallInDeprecatedApi) {
4461      this.incrementCounters(tsCallExpr.expression, FaultID.PropertyAccessByIndexFromSdk);
4462    }
4463  }
4464
4465  private handleBuiltinCtorCallSignature(tsCallExpr: ts.CallExpression | ts.TypeReferenceNode): void {
4466    if (!this.options.arkts2) {
4467      return;
4468    }
4469    const node = ts.isCallExpression(tsCallExpr) ? tsCallExpr.expression : tsCallExpr.typeName;
4470    const constructorType = this.tsTypeChecker.getTypeAtLocation(node);
4471    const callSignatures = constructorType.getCallSignatures();
4472    if (callSignatures.length === 0 || BUILTIN_DISABLE_CALLSIGNATURE.includes(node.getText())) {
4473      return;
4474    }
4475    const isSameApi = callSignatures.some((callSignature) => {
4476      const callSignatureDecl = callSignature.getDeclaration();
4477      if (!ts.isCallSignatureDeclaration(callSignatureDecl)) {
4478        return false;
4479      }
4480      const parentDecl = callSignatureDecl.parent;
4481      const parentName = ts.isInterfaceDeclaration(parentDecl) ? parentDecl.name.getText() : '';
4482      const BultinNoCtorFuncApiInfoSet = TypeScriptLinter.globalApiInfo.get(BuiltinProblem.BuiltinNoCtorFunc);
4483      if (!BultinNoCtorFuncApiInfoSet) {
4484        return false;
4485      }
4486      const isSameApi = [...BultinNoCtorFuncApiInfoSet].some((apiInfoItem) => {
4487        if (apiInfoItem.api_info.parent_api?.length <= 0) {
4488          return false;
4489        }
4490        const apiInfoParentName = apiInfoItem.api_info.parent_api[0].api_name;
4491        return apiInfoParentName === parentName;
4492      });
4493      return isSameApi;
4494    });
4495    if (isSameApi) {
4496      this.incrementCounters(node, FaultID.BuiltinNoCtorFunc);
4497    }
4498  }
4499
4500  private handleCallExpression(node: ts.Node): void {
4501    const tsCallExpr = node as ts.CallExpression;
4502    this.handleStateStyles(tsCallExpr);
4503    this.handleBuiltinCtorCallSignature(tsCallExpr);
4504    this.handleSdkConstructorIfaceForCallExpression(tsCallExpr);
4505    if (this.options.arkts2 && tsCallExpr.typeArguments !== undefined) {
4506      this.handleSdkPropertyAccessByIndex(tsCallExpr);
4507    }
4508    const calleeSym = this.tsUtils.trueSymbolAtLocation(tsCallExpr.expression);
4509    const callSignature = this.tsTypeChecker.getResolvedSignature(tsCallExpr);
4510    this.handleImportCall(tsCallExpr);
4511    this.handleRequireCall(tsCallExpr);
4512    if (calleeSym !== undefined) {
4513      this.processCalleeSym(calleeSym, tsCallExpr);
4514    }
4515    if (callSignature !== undefined) {
4516      if (!this.tsUtils.isLibrarySymbol(calleeSym)) {
4517        this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature);
4518        this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature);
4519      } else if (this.options.arkts2) {
4520        this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature);
4521      }
4522      this.handleNotsLikeSmartTypeOnCallExpression(tsCallExpr, callSignature);
4523    }
4524    this.handleInteropForCallExpression(tsCallExpr);
4525    this.handleLibraryTypeCall(tsCallExpr);
4526    if (
4527      ts.isPropertyAccessExpression(tsCallExpr.expression) &&
4528      this.tsUtils.hasEsObjectType(tsCallExpr.expression.expression)
4529    ) {
4530      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
4531      this.incrementCounters(node, faultId);
4532    }
4533    if (
4534      !ts.isExpressionStatement(tsCallExpr.parent) &&
4535      !ts.isVoidExpression(tsCallExpr.parent) &&
4536      !ts.isArrowFunction(tsCallExpr.parent) &&
4537      !(ts.isConditionalExpression(tsCallExpr.parent) && ts.isExpressionStatement(tsCallExpr.parent.parent))
4538    ) {
4539      this.handleLimitedVoidWithCall(tsCallExpr);
4540    }
4541    this.handleAppStorageCallExpression(tsCallExpr);
4542    this.fixJsImportCallExpression(tsCallExpr);
4543    this.handleInteropForCallJSExpression(tsCallExpr, calleeSym, callSignature);
4544    this.handleNoTsLikeFunctionCall(tsCallExpr);
4545    this.handleObjectLiteralInFunctionArgs(tsCallExpr);
4546    this.handleSdkGlobalApi(tsCallExpr);
4547    this.handleObjectLiteralAssignmentToClass(tsCallExpr);
4548    this.checkRestrictedAPICall(tsCallExpr);
4549  }
4550
4551  handleNoTsLikeFunctionCall(callExpr: ts.CallExpression): void {
4552    if (!this.options.arkts2) {
4553      return;
4554    }
4555
4556    const expression = callExpr.expression;
4557    const type = this.tsTypeChecker.getTypeAtLocation(expression);
4558    const typeText = this.tsTypeChecker.typeToString(type);
4559    if (typeText === LIKE_FUNCTION) {
4560      const autofix = this.autofixer?.fixNoTsLikeFunctionCall(callExpr);
4561      this.incrementCounters(expression, FaultID.ExplicitFunctionType, autofix);
4562    }
4563  }
4564
4565  private handleAppStorageCallExpression(tsCallExpr: ts.CallExpression): void {
4566    if (!this.options.arkts2 || !tsCallExpr) {
4567      return;
4568    }
4569
4570    if (!TsUtils.isAppStorageAccess(tsCallExpr)) {
4571      return;
4572    }
4573
4574    let varDecl: ts.VariableDeclaration | undefined;
4575    let parent = tsCallExpr.parent;
4576
4577    while (parent) {
4578      if (ts.isVariableDeclaration(parent)) {
4579        varDecl = parent;
4580        break;
4581      }
4582      parent = parent.parent;
4583    }
4584
4585    if (!varDecl || varDecl.type) {
4586      return;
4587    }
4588    const callReturnType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr);
4589    const isNumberReturnType = callReturnType.flags & ts.TypeFlags.Number;
4590    const isNumberGeneric = ((): boolean => {
4591      if (tsCallExpr.typeArguments?.length !== 1) {
4592        return false;
4593      }
4594      const callText = tsCallExpr.getText();
4595      if (callText.startsWith('Array<') || callText.startsWith('Set<') || callText.startsWith('Map<')) {
4596        return false;
4597      }
4598      const typeArg = tsCallExpr.typeArguments[0];
4599      if (typeArg.kind === ts.SyntaxKind.NumberKeyword) {
4600        return true;
4601      }
4602
4603      if (ts.isTypeReferenceNode(typeArg)) {
4604        return ts.isIdentifier(typeArg.typeName) && typeArg.typeName.text === STRINGLITERAL_NUMBER;
4605      }
4606      return typeArg.getText().trim() === STRINGLITERAL_NUMBER;
4607    })();
4608
4609    if (isNumberGeneric && !isNumberReturnType) {
4610      const autofix = this.autofixer?.fixAppStorageCallExpression(tsCallExpr);
4611      this.incrementCounters(tsCallExpr, FaultID.NumericSemantics, autofix);
4612    }
4613  }
4614
4615  handleInteropForCallJSExpression(
4616    tsCallExpr: ts.CallExpression,
4617    sym: ts.Symbol | undefined,
4618    callSignature: ts.Signature | undefined
4619  ): void {
4620    if (!this.options.arkts2 || !this.useStatic) {
4621      return;
4622    }
4623    if (ts.isAwaitExpression(tsCallExpr.parent) || ts.isTypeOfExpression(tsCallExpr.parent)) {
4624      return;
4625    }
4626
4627    if (!callSignature || this.isDeclaredInArkTs2(callSignature)) {
4628      return;
4629    }
4630
4631    if (!sym?.declarations?.[0]?.getSourceFile().fileName.endsWith(EXTNAME_JS)) {
4632      return;
4633    }
4634
4635    const autofix = this.autofixer?.fixInteropInvokeExpression(tsCallExpr);
4636
4637    this.incrementCounters(
4638      tsCallExpr,
4639      ts.isPropertyAccessExpression(tsCallExpr.expression) ? FaultID.InteropCallObjectMethods : FaultID.CallJSFunction,
4640      autofix
4641    );
4642  }
4643
4644  private handleInteropForCallExpression(tsCallExpr: ts.CallExpression): void {
4645    if (!this.options.arkts2 || !this.useStatic) {
4646      return;
4647    }
4648
4649    const callSignature = this.tsTypeChecker.getResolvedSignature(tsCallExpr);
4650    if (!callSignature) {
4651      return;
4652    }
4653
4654    if (!this.isDeclaredInArkTs2(callSignature)) {
4655      return;
4656    }
4657
4658    this.checkForForbiddenAPIs(callSignature, tsCallExpr);
4659  }
4660
4661  private isExportedEntityDeclaredInJs(exportDecl: ts.ExportDeclaration): boolean {
4662    if (!this.options.arkts2 || !this.useStatic) {
4663      return false;
4664    }
4665
4666    // For named exports with braces { ... }
4667    if (exportDecl.exportClause && ts.isNamedExports(exportDecl.exportClause)) {
4668      for (const exportSpecifier of exportDecl.exportClause.elements) {
4669        const identifier = exportSpecifier.name;
4670        if (this.tsUtils.isImportedFromJS(identifier)) {
4671          return true;
4672        }
4673      }
4674    }
4675
4676    // For namespace exports (export * as namespace from ...)
4677    if (exportDecl.exportClause && ts.isNamespaceExport(exportDecl.exportClause)) {
4678      const namespaceIdentifier = exportDecl.exportClause.name;
4679      if (this.tsUtils.isImportedFromJS(namespaceIdentifier)) {
4680        return true;
4681      }
4682    }
4683
4684    return false;
4685  }
4686
4687  private isDeclaredInArkTs2(callSignature: ts.Signature): boolean | undefined {
4688    const declarationSourceFile = callSignature?.declaration?.getSourceFile();
4689    if (!declarationSourceFile) {
4690      return undefined;
4691    }
4692    if (!declarationSourceFile.statements) {
4693      return undefined;
4694    }
4695
4696    if (this.tsUtils.isArkts12File(declarationSourceFile)) {
4697      return true;
4698    }
4699    return false;
4700  }
4701
4702  private checkRestrictedAPICall(node: ts.CallExpression): void {
4703    if (TypeScriptLinter.isReflectAPICall(node)) {
4704      this.incrementCounters(node.parent, FaultID.InteropCallReflect);
4705    }
4706  }
4707
4708  static isReflectAPICall(callExpr: ts.CallExpression): boolean {
4709    if (ts.isPropertyAccessExpression(callExpr.expression)) {
4710      const expr = callExpr.expression.expression;
4711      if (ts.isIdentifier(expr) && expr.text === REFLECT_LITERAL) {
4712        return true;
4713      }
4714    }
4715
4716    if (ts.isElementAccessExpression(callExpr.expression)) {
4717      const expr = callExpr.expression.expression;
4718      if (ts.isIdentifier(expr) && expr.text === REFLECT_LITERAL) {
4719        return true;
4720      }
4721    }
4722    return false;
4723  }
4724
4725  private shouldCheckForForbiddenAPI(declaration: ts.SignatureDeclaration | ts.JSDocSignature): boolean {
4726    for (const parameter of declaration.parameters) {
4727      if (ts.isJSDocParameterTag(parameter)) {
4728        continue;
4729      }
4730      const parameterType = this.tsTypeChecker.getTypeAtLocation(parameter);
4731      const parameterTypeString = this.tsTypeChecker.typeToString(parameterType);
4732
4733      if (parameterTypeString === OBJECT_LITERAL) {
4734        return true;
4735      }
4736    }
4737
4738    return false;
4739  }
4740
4741  private checkForForbiddenAPIs(callSignature: ts.Signature, tsCallExpr: ts.CallExpression): void {
4742    if (!callSignature.declaration) {
4743      return;
4744    }
4745
4746    if (!this.shouldCheckForForbiddenAPI(callSignature.declaration)) {
4747      return;
4748    }
4749
4750    const functionSymbol = this.getFunctionSymbol(callSignature.declaration);
4751    const functionDeclaration = functionSymbol?.valueDeclaration;
4752    if (!functionDeclaration) {
4753      return;
4754    }
4755
4756    if (!TypeScriptLinter.isFunctionLike(functionDeclaration)) {
4757      return;
4758    }
4759
4760    switch (TypeScriptLinter.containsForbiddenAPI(functionDeclaration)) {
4761      case REFLECT_LITERAL:
4762        this.incrementCounters(tsCallExpr.parent, FaultID.InteropCallReflect);
4763        break;
4764      case OBJECT_LITERAL:
4765        this.incrementCounters(tsCallExpr.parent, FaultID.InteropCallObjectParam);
4766        break;
4767      default:
4768        break;
4769    }
4770  }
4771
4772  private handleEtsComponentExpression(node: ts.Node): void {
4773    // for all the checks we make EtsComponentExpression is compatible with the CallExpression
4774    const etsComponentExpression = node as ts.CallExpression;
4775    this.handleLibraryTypeCall(etsComponentExpression);
4776  }
4777
4778  private handleImportCall(tsCallExpr: ts.CallExpression): void {
4779    if (tsCallExpr.expression.kind !== ts.SyntaxKind.ImportKeyword) {
4780      return;
4781    } else if (this.options.arkts2) {
4782      this.incrementCounters(tsCallExpr, FaultID.DynamicImport);
4783    }
4784
4785    // relax rule#133 "arkts-no-runtime-import"
4786    const tsArgs = tsCallExpr.arguments;
4787    if (tsArgs.length <= 1 || !ts.isObjectLiteralExpression(tsArgs[1])) {
4788      return;
4789    }
4790
4791    for (const tsProp of tsArgs[1].properties) {
4792      if (
4793        (ts.isPropertyAssignment(tsProp) || ts.isShorthandPropertyAssignment(tsProp)) &&
4794        tsProp.name.getText() === 'assert'
4795      ) {
4796        this.incrementCounters(tsProp, FaultID.ImportAssertion);
4797        break;
4798      }
4799    }
4800  }
4801
4802  private handleRequireCall(tsCallExpr: ts.CallExpression): void {
4803    if (
4804      ts.isIdentifier(tsCallExpr.expression) &&
4805      tsCallExpr.expression.text === 'require' &&
4806      ts.isVariableDeclaration(tsCallExpr.parent)
4807    ) {
4808      const tsType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression);
4809      if (TsUtils.isInterfaceType(tsType) && tsType.symbol.name === 'NodeRequire') {
4810        this.incrementCounters(tsCallExpr.parent, FaultID.ImportAssignment);
4811      }
4812    }
4813  }
4814
4815  private handleGenericCallWithNoTypeArgs(
4816    callLikeExpr: ts.CallExpression | ts.NewExpression,
4817    callSignature: ts.Signature
4818  ): void {
4819
4820    /*
4821     * Note: The PR!716 has led to a significant performance degradation.
4822     * Since initial problem was fixed in a more general way, this change
4823     * became redundant. Therefore, it was reverted. See #13721 comments
4824     * for a detailed analysis.
4825     */
4826    if (this.options.arkts2 && TypeScriptLinter.isInvalidBuiltinGenericConstructorCall(callLikeExpr)) {
4827      const autofix = this.autofixer?.fixGenericCallNoTypeArgs(callLikeExpr as ts.NewExpression);
4828      this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs, autofix);
4829      return;
4830    }
4831    this.checkTypeArgumentsForGenericCallWithNoTypeArgs(callLikeExpr, callSignature);
4832  }
4833
4834  private static isInvalidBuiltinGenericConstructorCall(newExpression: ts.CallExpression | ts.NewExpression): boolean {
4835    if (!ts.isNewExpression(newExpression)) {
4836      return false;
4837    }
4838    const isBuiltin = BUILTIN_GENERIC_CONSTRUCTORS.has(newExpression.expression.getText().replace(/Constructor$/, ''));
4839    return isBuiltin && (!newExpression.typeArguments || newExpression.typeArguments.length === 0);
4840  }
4841
4842  private checkTypeArgumentsForGenericCallWithNoTypeArgs(
4843    callLikeExpr: ts.CallExpression | ts.NewExpression,
4844    callSignature: ts.Signature
4845  ): void {
4846    if (ts.isNewExpression(callLikeExpr) && this.isNonGenericClass(callLikeExpr)) {
4847      return;
4848    }
4849    const tsSyntaxKind = ts.isNewExpression(callLikeExpr) ?
4850      ts.SyntaxKind.Constructor :
4851      ts.SyntaxKind.FunctionDeclaration;
4852    const signFlags = ts.NodeBuilderFlags.WriteTypeArgumentsOfSignature | ts.NodeBuilderFlags.IgnoreErrors;
4853    const signDecl = this.tsTypeChecker.signatureToSignatureDeclaration(
4854      callSignature,
4855      tsSyntaxKind,
4856      undefined,
4857      signFlags
4858    );
4859    if (!signDecl?.typeArguments) {
4860      return;
4861    }
4862    const resolvedTypeArgs = signDecl.typeArguments;
4863    const providedTypeArgs = callLikeExpr.typeArguments;
4864    const startTypeArg = providedTypeArgs?.length ?? 0;
4865    let shouldReportError = startTypeArg !== resolvedTypeArgs.length;
4866    if (this.options.arkts2 && callLikeExpr.kind === ts.SyntaxKind.NewExpression) {
4867      shouldReportError = this.shouldReportGenericTypeArgsError(
4868        callLikeExpr,
4869        resolvedTypeArgs,
4870        providedTypeArgs,
4871        startTypeArg,
4872        shouldReportError
4873      );
4874      if (shouldReportError) {
4875        const autofix = this.autofixer?.fixGenericCallNoTypeArgs(callLikeExpr);
4876        this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs, autofix);
4877      }
4878    } else {
4879      this.checkForUnknownTypeInNonArkTS2(callLikeExpr, resolvedTypeArgs, startTypeArg);
4880    }
4881  }
4882
4883  private checkForUnknownTypeInNonArkTS2(
4884    callLikeExpr: ts.CallExpression | ts.NewExpression,
4885    resolvedTypeArgs: ts.NodeArray<ts.TypeNode>,
4886    startTypeArg: number
4887  ): void {
4888    for (let i = startTypeArg; i < resolvedTypeArgs.length; ++i) {
4889      const typeNode = resolvedTypeArgs[i];
4890
4891      /*
4892       * if compiler infers 'unknown' type there are 2 possible cases:
4893       *   1. Compiler unable to infer type from arguments and use 'unknown'
4894       *   2. Compiler infer 'unknown' from arguments
4895       * We report error in both cases. It is ok because we cannot use 'unknown'
4896       * in ArkTS and already have separate check for it.
4897       */
4898      if (typeNode.kind === ts.SyntaxKind.UnknownKeyword) {
4899        this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs);
4900        break;
4901      }
4902    }
4903  }
4904
4905  private shouldReportGenericTypeArgsError(
4906    callLikeExpr: ts.CallExpression | ts.NewExpression,
4907    resolvedTypeArgs: ts.NodeArray<ts.TypeNode>,
4908    providedTypeArgs: ts.NodeArray<ts.TypeNode> | undefined,
4909    startTypeArg: number,
4910    initialErrorState: boolean
4911  ): boolean {
4912    const typeParameters = this.getOriginalTypeParameters(callLikeExpr);
4913    if (!typeParameters || typeParameters.length === 0) {
4914      return initialErrorState;
4915    }
4916    const optionalParamsCount = typeParameters.filter((param, index) => {
4917      return param.default && (!providedTypeArgs || index >= providedTypeArgs.length);
4918    }).length;
4919    if (optionalParamsCount === 0) {
4920      return initialErrorState;
4921    }
4922    return startTypeArg + optionalParamsCount !== resolvedTypeArgs.length;
4923  }
4924
4925  private getOriginalTypeParameters(
4926    callLikeExpr: ts.CallExpression | ts.NewExpression
4927  ): ts.TypeParameterDeclaration[] | undefined {
4928    const typeChecker = this.tsTypeChecker;
4929    const expressionType = typeChecker.getTypeAtLocation(callLikeExpr.expression);
4930    const declarations = expressionType.symbol?.declarations;
4931    if (!declarations || declarations.length === 0) {
4932      return undefined;
4933    }
4934    for (const decl of declarations) {
4935      if (ts.isFunctionDeclaration(decl) && decl.typeParameters) {
4936        return [...decl.typeParameters];
4937      }
4938      if (ts.isMethodDeclaration(decl) && decl.typeParameters) {
4939        return [...decl.typeParameters];
4940      }
4941      if (ts.isClassDeclaration(decl) && decl.typeParameters) {
4942        return [...decl.typeParameters];
4943      }
4944      if (ts.isInterfaceDeclaration(decl) && decl.typeParameters) {
4945        return [...decl.typeParameters];
4946      }
4947    }
4948    return undefined;
4949  }
4950
4951  private isNonGenericClass(expression: ts.NewExpression): boolean {
4952    const declaration = this.tsUtils.getDeclarationNode(expression.expression);
4953    return !!declaration && ts.isClassDeclaration(declaration) && !declaration.typeParameters;
4954  }
4955
4956  static isArrayFromCall(callLikeExpr: ts.CallExpression | ts.NewExpression): boolean {
4957    return (
4958      ts.isCallExpression(callLikeExpr) &&
4959      ts.isPropertyAccessExpression(callLikeExpr.expression) &&
4960      callLikeExpr.expression.name.text === STRINGLITERAL_FROM &&
4961      ts.isIdentifier(callLikeExpr.expression.expression) &&
4962      callLikeExpr.expression.expression.text === STRINGLITERAL_ARRAY
4963    );
4964  }
4965
4966  private static readonly listFunctionApplyCallApis = [
4967    'Function.apply',
4968    'Function.call',
4969    'CallableFunction.apply',
4970    'CallableFunction.call'
4971  ];
4972
4973  private static readonly listFunctionBindApis = ['Function.bind', 'CallableFunction.bind'];
4974
4975  private handleFunctionApplyBindPropCall(tsCallExpr: ts.CallExpression, calleeSym: ts.Symbol): void {
4976    const exprName = this.tsTypeChecker.getFullyQualifiedName(calleeSym);
4977    if (TypeScriptLinter.listFunctionApplyCallApis.includes(exprName)) {
4978      this.incrementCounters(tsCallExpr, FaultID.FunctionApplyCall);
4979    }
4980    if (TypeScriptLinter.listFunctionBindApis.includes(exprName)) {
4981      const faultId = this.options.arkts2 ? FaultID.FunctionBindError : FaultID.FunctionBind;
4982      this.incrementCounters(tsCallExpr, faultId);
4983    }
4984  }
4985
4986  private handleStructIdentAndUndefinedInArgs(
4987    tsCallOrNewExpr: ts.CallExpression | ts.NewExpression,
4988    callSignature: ts.Signature
4989  ): void {
4990    if (!tsCallOrNewExpr.arguments) {
4991      return;
4992    }
4993    for (let argIndex = 0; argIndex < tsCallOrNewExpr.arguments.length; ++argIndex) {
4994      const tsArg = tsCallOrNewExpr.arguments[argIndex];
4995      const tsArgType = this.tsTypeChecker.getTypeAtLocation(tsArg);
4996      if (!tsArgType) {
4997        continue;
4998      }
4999      const paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length - 1;
5000      const tsParamSym = callSignature.parameters[paramIndex];
5001      if (!tsParamSym) {
5002        continue;
5003      }
5004      const tsParamDecl = tsParamSym.valueDeclaration;
5005      if (tsParamDecl && ts.isParameter(tsParamDecl)) {
5006        let tsParamType = this.tsTypeChecker.getTypeOfSymbolAtLocation(tsParamSym, tsParamDecl);
5007        if (tsParamDecl.dotDotDotToken && this.tsUtils.isGenericArrayType(tsParamType) && tsParamType.typeArguments) {
5008          tsParamType = tsParamType.typeArguments[0];
5009        }
5010        if (!tsParamType) {
5011          continue;
5012        }
5013        this.checkAssignmentMatching(tsArg, tsParamType, tsArg);
5014      }
5015    }
5016  }
5017
5018  private static readonly LimitedApis = new Map<string, { arr: Array<string> | null; fault: FaultID }>([
5019    ['global', { arr: LIMITED_STD_GLOBAL_API, fault: FaultID.LimitedStdLibApi }],
5020    ['Object', { arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi }],
5021    ['ObjectConstructor', { arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi }],
5022    ['Reflect', { arr: LIMITED_STD_REFLECT_API, fault: FaultID.LimitedStdLibApi }],
5023    ['ProxyHandler', { arr: LIMITED_STD_PROXYHANDLER_API, fault: FaultID.LimitedStdLibApi }],
5024    [SYMBOL, { arr: null, fault: FaultID.SymbolType }],
5025    [SYMBOL_CONSTRUCTOR, { arr: null, fault: FaultID.SymbolType }]
5026  ]);
5027
5028  private handleStdlibAPICall(
5029    callExpr: ts.CallExpression,
5030    calleeSym: ts.Symbol,
5031    name: string,
5032    parName: string | undefined
5033  ): void {
5034    if (parName === undefined) {
5035      if (LIMITED_STD_GLOBAL_API.includes(name)) {
5036        this.incrementCounters(callExpr, FaultID.LimitedStdLibApi);
5037        return;
5038      }
5039      const escapedName = calleeSym.escapedName;
5040      if (escapedName === 'Symbol' || escapedName === 'SymbolConstructor') {
5041        this.incrementCounters(callExpr, FaultID.SymbolType);
5042      }
5043      return;
5044    }
5045    const lookup = TypeScriptLinter.LimitedApis.get(parName);
5046    if (
5047      lookup !== undefined &&
5048      (lookup.arr === null || lookup.arr.includes(name)) &&
5049      (!this.options.useRelaxedRules || !this.supportedStdCallApiChecker.isSupportedStdCallAPI(callExpr, parName, name))
5050    ) {
5051      this.incrementCounters(callExpr, lookup.fault);
5052    }
5053  }
5054
5055  private handleBuiltinThisArgs(
5056    callExpr: ts.CallExpression,
5057    calleeSym: ts.Symbol,
5058    name: string,
5059    parName: string | undefined
5060  ): void {
5061    if (parName === undefined) {
5062      return;
5063    }
5064
5065    const builtinThisArgsInfos = TypeScriptLinter.funcMap.get(name);
5066    if (!builtinThisArgsInfos) {
5067      return;
5068    }
5069
5070    const sourceFile = calleeSym?.declarations?.[0]?.getSourceFile();
5071    if (!sourceFile) {
5072      return;
5073    }
5074
5075    const fileName = path.basename(sourceFile.fileName);
5076    const builtinInfos = builtinThisArgsInfos.get(fileName);
5077    if (!(builtinInfos && builtinInfos.size > 0)) {
5078      return;
5079    }
5080    for (const info of builtinInfos) {
5081      const needReport =
5082        info.parent_api.length > 0 &&
5083        info.parent_api[0].api_name === parName &&
5084        info?.api_func_args?.length === callExpr.arguments.length;
5085      if (needReport) {
5086        this.incrementCounters(callExpr, FaultID.BuiltinThisArgs);
5087        return;
5088      }
5089    }
5090  }
5091
5092  private checkLimitedStdlibApi(node: ts.Identifier, symbol: ts.Symbol): void {
5093    const parName = this.tsUtils.getParentSymbolName(symbol);
5094    const entries = LIMITED_STD_API.get(parName);
5095    if (!entries) {
5096      return;
5097    }
5098    for (const entry of entries) {
5099      if (
5100        entry.api.includes(symbol.name) &&
5101        !this.supportedStdCallApiChecker.isSupportedStdCallAPI(node, parName, symbol.name)
5102      ) {
5103        this.incrementCounters(node, entry.faultId);
5104        return;
5105      }
5106    }
5107  }
5108
5109  private handleLibraryTypeCall(expr: ts.CallExpression | ts.NewExpression): void {
5110    if (!expr.arguments || !this.tscStrictDiagnostics || !this.sourceFile) {
5111      return;
5112    }
5113
5114    const file = path.normalize(this.sourceFile.fileName);
5115    const tscDiagnostics: readonly ts.Diagnostic[] | undefined = this.tscStrictDiagnostics.get(file);
5116    if (!tscDiagnostics?.length) {
5117      return;
5118    }
5119
5120    const isOhModulesEts = TsUtils.isOhModulesEtsSymbol(this.tsUtils.trueSymbolAtLocation(expr.expression));
5121    const deleteDiagnostics: Set<ts.Diagnostic> = new Set();
5122    LibraryTypeCallDiagnosticChecker.instance.filterDiagnostics(
5123      tscDiagnostics,
5124      expr,
5125      this.tsUtils.isLibraryType(this.tsTypeChecker.getTypeAtLocation(expr.expression)),
5126      (diagnostic, errorType) => {
5127
5128        /*
5129         * When a diagnostic meets the filter criteria, If it happens in an ets file in the 'oh_modules' directory.
5130         * the diagnostic is downgraded to warning. For other files, downgraded to nothing.
5131         */
5132        if (isOhModulesEts && errorType !== DiagnosticCheckerErrorType.UNKNOW) {
5133          diagnostic.category = ts.DiagnosticCategory.Warning;
5134          return false;
5135        }
5136        deleteDiagnostics.add(diagnostic);
5137        return true;
5138      }
5139    );
5140
5141    if (!deleteDiagnostics.size) {
5142      return;
5143    }
5144
5145    this.tscStrictDiagnostics.set(
5146      file,
5147      tscDiagnostics.filter((item) => {
5148        return !deleteDiagnostics.has(item);
5149      })
5150    );
5151  }
5152
5153  private checkConstrutorAccess(propertyAccessExpr: ts.PropertyAccessExpression): void {
5154    if (!this.options.arkts2 || !this.useStatic) {
5155      return;
5156    }
5157
5158    if (propertyAccessExpr.name.text === 'constructor') {
5159      this.incrementCounters(propertyAccessExpr, FaultID.NoConstructorOnClass);
5160    }
5161  }
5162
5163  private checkForInterfaceInitialization(newExpression: ts.NewExpression): void {
5164    if (!this.options.arkts2 || !this.useStatic) {
5165      return;
5166    }
5167    const calleeExpr = newExpression.expression;
5168    if (!ts.isIdentifier(calleeExpr)) {
5169      return;
5170    }
5171
5172    const type = this.tsTypeChecker.getTypeAtLocation(calleeExpr);
5173    const typeDeclaration = TsUtils.getDeclaration(type.symbol);
5174    if (typeDeclaration && ts.isInterfaceDeclaration(typeDeclaration) && type.symbol) {
5175      const filePath = typeDeclaration.getSourceFile().fileName;
5176      this.checkIsConstructorIface(calleeExpr, type.symbol.name, path.basename(filePath));
5177    }
5178  }
5179
5180  private checkIsConstructorIface(node: ts.Node, symbol: string, filePath: string): void {
5181    const constructorIfaceSetInfos = Array.from(TypeScriptLinter.ConstructorIfaceSet);
5182    constructorIfaceSetInfos.some((constructorFuncsInfo) => {
5183      const api_name = constructorFuncsInfo.api_info.parent_api[0].api_name;
5184      if (
5185        symbol === api_name &&
5186        (constructorFuncsInfo.file_path.includes(filePath) || constructorFuncsInfo.import_path.includes(filePath))
5187      ) {
5188        this.incrementCounters(node, FaultID.ConstructorIfaceFromSdk);
5189        return true;
5190      }
5191      return false;
5192    });
5193  }
5194
5195  private handleNewExpression(node: ts.Node): void {
5196    const tsNewExpr = node as ts.NewExpression;
5197
5198    this.checkForInterfaceInitialization(tsNewExpr);
5199    this.handleSharedArrayBuffer(tsNewExpr);
5200    this.handleSdkGlobalApi(tsNewExpr);
5201    this.checkCreatingPrimitiveTypes(tsNewExpr);
5202
5203    if (this.options.advancedClassChecks || this.options.arkts2) {
5204      const calleeExpr = tsNewExpr.expression;
5205      const calleeType = this.tsTypeChecker.getTypeAtLocation(calleeExpr);
5206      if (
5207        !this.tsUtils.isClassTypeExpression(calleeExpr) &&
5208        !isStdLibraryType(calleeType) &&
5209        !this.tsUtils.isLibraryType(calleeType) &&
5210        !this.tsUtils.hasEsObjectType(calleeExpr)
5211      ) {
5212        // missing exact rule
5213        const faultId = this.options.arkts2 ? FaultID.DynamicCtorCall : FaultID.ClassAsObject;
5214        this.incrementCounters(calleeExpr, faultId);
5215      }
5216    }
5217    const sym = this.tsUtils.trueSymbolAtLocation(tsNewExpr.expression);
5218    const callSignature = this.tsTypeChecker.getResolvedSignature(tsNewExpr);
5219    if (callSignature !== undefined) {
5220      if (!this.tsUtils.isLibrarySymbol(sym)) {
5221        this.handleStructIdentAndUndefinedInArgs(tsNewExpr, callSignature);
5222        this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature);
5223      } else if (this.options.arkts2) {
5224        this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature);
5225      }
5226    }
5227    this.handleSendableGenericTypes(tsNewExpr);
5228    this.handleInstantiatedJsObject(tsNewExpr, sym);
5229    this.handlePromiseNeedVoidResolve(tsNewExpr);
5230  }
5231
5232  handlePromiseNeedVoidResolve(newExpr: ts.NewExpression): void {
5233    if (!this.options.arkts2) {
5234      return;
5235    }
5236
5237    if (!ts.isIdentifier(newExpr.expression) || newExpr.expression.text !== 'Promise') {
5238      return;
5239    }
5240
5241    const typeArg = newExpr.typeArguments?.[0];
5242    if (!typeArg) {
5243      return;
5244    }
5245
5246    const type = this.tsTypeChecker.getTypeAtLocation(typeArg);
5247    if (!(type.getFlags() & ts.TypeFlags.Void)) {
5248      return;
5249    }
5250
5251    const executor = newExpr.arguments?.[0];
5252    if (!executor || !ts.isFunctionLike(executor)) {
5253      return;
5254    }
5255
5256    const resolveParam = executor.parameters[0];
5257    if (resolveParam?.type) {
5258      if (ts.isFunctionTypeNode(resolveParam.type) &&
5259        resolveParam.type.parameters.length === 0) {
5260        this.incrementCounters(resolveParam.type,FaultID.PromiseVoidNeedResolveArg);
5261      }
5262    }
5263    if (executor.body) {
5264      ts.forEachChild(executor.body, node => {
5265        if (ts.isCallExpression(node) &&
5266          ts.isIdentifier(node.expression) &&
5267          node.expression.text === 'resolve' &&
5268          node.arguments.length === 0) {
5269          this.incrementCounters(node,FaultID.PromiseVoidNeedResolveArg);
5270        }
5271      });
5272    }
5273  }
5274
5275  private checkCreatingPrimitiveTypes(tsNewExpr: ts.NewExpression): void {
5276    if (!this.options.arkts2) {
5277      return;
5278    }
5279    const typeStr = this.tsTypeChecker.typeToString(this.tsTypeChecker.getTypeAtLocation(tsNewExpr));
5280    const primitiveTypes = ['Number', 'String', 'Boolean'];
5281    if (primitiveTypes.includes(typeStr)) {
5282      this.incrementCounters(tsNewExpr, FaultID.CreatingPrimitiveTypes);
5283    }
5284  }
5285
5286  handleInstantiatedJsObject(tsNewExpr: ts.NewExpression, sym: ts.Symbol | undefined): void {
5287    if (this.useStatic && this.options.arkts2) {
5288      if (sym?.declarations?.[0]?.getSourceFile().fileName.endsWith(EXTNAME_JS)) {
5289        const args = tsNewExpr.arguments;
5290        const autofix = this.autofixer?.fixInteropInstantiateExpression(tsNewExpr, args);
5291        this.incrementCounters(tsNewExpr, FaultID.InstantiatedJsOjbect, autofix);
5292      }
5293    }
5294  }
5295
5296  private handleSendableGenericTypes(node: ts.NewExpression): void {
5297    const type = this.tsTypeChecker.getTypeAtLocation(node);
5298    if (!this.tsUtils.isSendableClassOrInterface(type)) {
5299      return;
5300    }
5301
5302    const typeArgs = node.typeArguments;
5303    if (!typeArgs || typeArgs.length === 0) {
5304      return;
5305    }
5306
5307    for (const arg of typeArgs) {
5308      if (!this.tsUtils.isSendableTypeNode(arg)) {
5309        this.incrementCounters(arg, FaultID.SendableGenericTypes);
5310      }
5311    }
5312  }
5313
5314  private handleAsExpression(node: ts.Node): void {
5315    const tsAsExpr = node as ts.AsExpression;
5316    if (tsAsExpr.type.getText() === 'const') {
5317      this.incrementCounters(node, FaultID.ConstAssertion);
5318    }
5319    const targetType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType();
5320    const exprType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType();
5321    // check for rule#65:   'number as Number' and 'boolean as Boolean' are disabled
5322    if (
5323      this.tsUtils.isNumberLikeType(exprType) && this.tsUtils.isStdNumberType(targetType) ||
5324      TsUtils.isBooleanLikeType(exprType) && this.tsUtils.isStdBooleanType(targetType)
5325    ) {
5326      this.incrementCounters(node, FaultID.TypeAssertion);
5327    }
5328    if (
5329      !this.tsUtils.isSendableClassOrInterface(exprType) &&
5330      !this.tsUtils.isObject(exprType) &&
5331      !TsUtils.isAnyType(exprType) &&
5332      this.tsUtils.isSendableClassOrInterface(targetType)
5333    ) {
5334      this.incrementCounters(tsAsExpr, FaultID.SendableAsExpr);
5335    }
5336    if (this.tsUtils.isWrongSendableFunctionAssignment(targetType, exprType)) {
5337      this.incrementCounters(tsAsExpr, FaultID.SendableFunctionAsExpr);
5338    }
5339    if (
5340      this.options.arkts2 &&
5341      this.tsUtils.needToDeduceStructuralIdentity(targetType, exprType, tsAsExpr.expression, true)
5342    ) {
5343      if (this.isExemptedAsExpression(tsAsExpr)) {
5344        return;
5345      }
5346      if (!this.tsUtils.isObject(exprType)) {
5347        this.incrementCounters(node, FaultID.StructuralIdentity);
5348      }
5349    }
5350    this.handleAsExpressionImport(tsAsExpr);
5351    this.handleNoTuplesArrays(node, targetType, exprType);
5352    this.handleObjectLiteralAssignmentToClass(tsAsExpr);
5353    this.handleArrayTypeImmutable(tsAsExpr, exprType, targetType);
5354    this.handleNotsLikeSmartTypeOnAsExpression(tsAsExpr);
5355  }
5356
5357  private isExemptedAsExpression(node: ts.AsExpression): boolean {
5358    if (!ts.isElementAccessExpression(node.expression)) {
5359      return false;
5360    }
5361
5362    const sourceType = this.tsTypeChecker.getTypeAtLocation(node.expression);
5363    const targetType = this.tsTypeChecker.getTypeAtLocation(node.type);
5364    const isRecordIndexAccess = (): boolean => {
5365      const exprType = this.tsTypeChecker.getTypeAtLocation(node.expression);
5366      const hasNumberIndex = !!exprType.getNumberIndexType();
5367      const hasStringIndex = !!exprType.getStringIndexType();
5368      const hasBooleanIndex = !!exprType.getProperty('true') || !!exprType.getProperty('false');
5369
5370      return hasNumberIndex || hasStringIndex || hasBooleanIndex;
5371    };
5372
5373    if (isRecordIndexAccess()) {
5374      const targetSymbol = targetType.getSymbol();
5375      if (targetSymbol && targetSymbol.getName() === 'Array') {
5376        return true;
5377      }
5378    }
5379    const primitiveFlags = ts.TypeFlags.Number | ts.TypeFlags.String | ts.TypeFlags.Boolean;
5380    const objectFlag = ts.TypeFlags.Object;
5381    return (
5382      sourceType.isUnion() &&
5383      sourceType.types.some((t) => {
5384        return t.flags & primitiveFlags;
5385      }) &&
5386      sourceType.types.some((t) => {
5387        return t.flags & objectFlag;
5388      })
5389    );
5390  }
5391
5392  private handleAsExpressionImport(tsAsExpr: ts.AsExpression): void {
5393    if (!this.useStatic || !this.options.arkts2) {
5394      return;
5395    }
5396
5397    const type = tsAsExpr.type;
5398    const expression = tsAsExpr.expression;
5399    const restrictedPrimitiveTypes = [
5400      ts.SyntaxKind.NumberKeyword,
5401      ts.SyntaxKind.BooleanKeyword,
5402      ts.SyntaxKind.StringKeyword,
5403      ts.SyntaxKind.BigIntKeyword,
5404      ts.SyntaxKind.UndefinedKeyword
5405    ];
5406    this.handleAsExpressionImportNull(tsAsExpr);
5407    const isRestrictedPrimitive = restrictedPrimitiveTypes.includes(type.kind);
5408    const isRestrictedArrayType =
5409      type.kind === ts.SyntaxKind.ArrayType ||
5410      ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName) && type.typeName.text === 'Array';
5411
5412    if (!isRestrictedPrimitive && !isRestrictedArrayType) {
5413      return;
5414    }
5415
5416    let identifier: ts.Identifier | undefined;
5417    if (ts.isIdentifier(expression)) {
5418      identifier = expression;
5419    } else if (ts.isPropertyAccessExpression(expression)) {
5420      identifier = ts.isIdentifier(expression.expression) ? expression.expression : undefined;
5421    }
5422
5423    if (identifier) {
5424      const sym = this.tsUtils.trueSymbolAtLocation(identifier);
5425      const decl = TsUtils.getDeclaration(sym);
5426      if (decl?.getSourceFile().fileName.endsWith(EXTNAME_JS)) {
5427        const autofix = this.autofixer?.fixInteropAsExpression(tsAsExpr);
5428        this.incrementCounters(tsAsExpr, FaultID.InterOpConvertImport, autofix);
5429      }
5430    }
5431  }
5432
5433  private handleAsExpressionImportNull(tsAsExpr: ts.AsExpression): void {
5434    const type = tsAsExpr.type;
5435    const isNullAssertion =
5436      type.kind === ts.SyntaxKind.NullKeyword ||
5437      ts.isLiteralTypeNode(type) && type.literal.kind === ts.SyntaxKind.NullKeyword ||
5438      type.getText() === 'null';
5439    if (isNullAssertion) {
5440      this.incrementCounters(tsAsExpr, FaultID.InterOpConvertImport);
5441    }
5442  }
5443
5444  private handleSdkConstructorIface(typeRef: ts.TypeReferenceNode): void {
5445    if (!this.options.arkts2 && typeRef?.typeName === undefined && !ts.isQualifiedName(typeRef.typeName)) {
5446      return;
5447    }
5448    const qualifiedName = typeRef.typeName as ts.QualifiedName;
5449    // tsc version diff
5450    const topName = qualifiedName.left?.getText();
5451    const sdkInfos = this.interfaceMap.get(topName);
5452    if (!sdkInfos) {
5453      return;
5454    }
5455    for (const sdkInfo of sdkInfos) {
5456      if (sdkInfo.api_type !== 'ConstructSignature') {
5457        continue;
5458      }
5459      // sdk api from json has 3 overload. need consider these case.
5460      if (sdkInfo.parent_api[0].api_name === qualifiedName.right.getText()) {
5461        this.incrementCounters(typeRef, FaultID.ConstructorIfaceFromSdk);
5462        break;
5463      }
5464    }
5465  }
5466
5467  private handleSdkConstructorIfaceForCallExpression(callExpr: ts.CallExpression): void {
5468    if (!this.options.arkts2) {
5469      return;
5470    }
5471    let type: ts.Type | undefined;
5472    if (!callExpr.arguments || callExpr.arguments.length === 0) {
5473      if (ts.isPropertyAccessExpression(callExpr.expression)) {
5474        type = this.tsTypeChecker.getTypeAtLocation(callExpr.expression.expression);
5475      }
5476    }
5477    callExpr.arguments.some((args) => {
5478      if (ts.isIdentifier(args)) {
5479        type = this.tsTypeChecker.getTypeAtLocation(args);
5480      }
5481    });
5482    if (!type) {
5483      return;
5484    }
5485    const decl = TsUtils.getDeclaration(type?.symbol);
5486    if (!decl) {
5487      return;
5488    }
5489    const filePath = TypeScriptLinter.getFileName(decl);
5490    this.checkIsConstructorIface(callExpr, type.symbol.name, filePath);
5491  }
5492
5493  private static getFileName(decl: ts.Declaration): string {
5494    let filePath = '';
5495    if (
5496      ts.isImportSpecifier(decl) &&
5497      ts.isImportDeclaration(decl.parent.parent.parent) &&
5498      ts.isStringLiteral(decl.parent.parent.parent.moduleSpecifier)
5499    ) {
5500      filePath = decl.parent.parent.parent.moduleSpecifier.text;
5501    } else if (
5502      ts.isImportClause(decl) &&
5503      ts.isImportDeclaration(decl.parent) &&
5504      ts.isStringLiteral(decl.parent.moduleSpecifier)
5505    ) {
5506      filePath = decl.parent.moduleSpecifier.text;
5507    } else {
5508      filePath = decl.getSourceFile().fileName;
5509    }
5510    return path.basename(filePath);
5511  }
5512
5513  private handleSharedArrayBuffer(
5514    node: ts.TypeReferenceNode | ts.NewExpression | ts.ExpressionWithTypeArguments
5515  ): void {
5516    if (!this.options.arkts2) {
5517      return;
5518    }
5519
5520    const typeNameIdentifier = ts.isTypeReferenceNode(node) ? node.typeName : node.expression;
5521    if (!ts.isIdentifier(typeNameIdentifier) || typeNameIdentifier.getText() !== ESLIB_SHAREDARRAYBUFFER) {
5522      return;
5523    }
5524    const symbol = this.tsUtils.trueSymbolAtLocation(typeNameIdentifier);
5525    if (!symbol) {
5526      return;
5527    }
5528
5529    const isImported = this.sourceFile.statements.some((stmt) => {
5530      if (!ts.isImportDeclaration(stmt)) {
5531        return false;
5532      }
5533      const importClause = stmt.importClause;
5534      if (!importClause?.namedBindings || !ts.isNamedImports(importClause.namedBindings)) {
5535        return false;
5536      }
5537
5538      const elements = importClause.namedBindings.elements.some((element) => {
5539        return element.name.text === ESLIB_SHAREDARRAYBUFFER;
5540      });
5541      return elements;
5542    });
5543    if (isImported) {
5544      return;
5545    }
5546    const decls = symbol.getDeclarations();
5547    const isSharedMemoryEsLib = decls?.some((decl) => {
5548      const srcFileName = decl.getSourceFile().fileName;
5549      return srcFileName.endsWith(ESLIB_SHAREDMEMORY_FILENAME);
5550    });
5551
5552    if (!isSharedMemoryEsLib || this.hasLocalSharedArrayBufferClass()) {
5553      return;
5554    }
5555
5556    const autofix = this.autofixer?.replaceNode(typeNameIdentifier, 'ArrayBuffer');
5557    this.incrementCounters(typeNameIdentifier, FaultID.SharedArrayBufferDeprecated, autofix);
5558  }
5559
5560  private hasLocalSharedArrayBufferClass(): boolean {
5561    return this.sourceFile.statements.some((stmt) => {
5562      return ts.isClassDeclaration(stmt) && stmt.name?.text === ESLIB_SHAREDARRAYBUFFER;
5563    });
5564  }
5565
5566  private handleTypeReference(node: ts.Node): void {
5567    const typeRef = node as ts.TypeReferenceNode;
5568
5569    this.handleBuiltinCtorCallSignature(typeRef);
5570    this.handleSharedArrayBuffer(typeRef);
5571    this.handleSdkGlobalApi(typeRef);
5572
5573    this.handleSdkConstructorIface(typeRef);
5574
5575    const isESValue = TsUtils.isEsValueType(typeRef);
5576    const isPossiblyValidContext = TsUtils.isEsValuePossiblyAllowed(typeRef);
5577    if (isESValue && !isPossiblyValidContext) {
5578      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
5579      this.incrementCounters(node, faultId);
5580      return;
5581    }
5582
5583    const typeName = this.tsUtils.entityNameToString(typeRef.typeName);
5584    const isStdUtilityType = LIMITED_STANDARD_UTILITY_TYPES.includes(typeName);
5585    if (isStdUtilityType) {
5586      this.incrementCounters(node, FaultID.UtilityType);
5587      return;
5588    }
5589
5590    this.checkPartialType(node);
5591
5592    const typeNameType = this.tsTypeChecker.getTypeAtLocation(typeRef.typeName);
5593    if (this.options.arkts2 && (typeNameType.flags & ts.TypeFlags.Void) !== 0) {
5594      this.incrementCounters(typeRef, FaultID.LimitedVoidType);
5595    }
5596    if (this.tsUtils.isSendableClassOrInterface(typeNameType)) {
5597      this.checkSendableTypeArguments(typeRef);
5598    }
5599
5600    this.checkNoEnumProp(typeRef);
5601    if (ts.isQualifiedName(typeRef.typeName)) {
5602      this.handleSdkForConstructorFuncs(typeRef.typeName);
5603    }
5604  }
5605
5606  private checkNoEnumProp(typeRef: ts.TypeReferenceNode): void {
5607    if (!this.options.arkts2) {
5608      return;
5609    }
5610    if (ts.isQualifiedName(typeRef.typeName)) {
5611      const symbol = this.tsTypeChecker.getSymbolAtLocation(typeRef.typeName.right);
5612
5613      if (!symbol) {
5614        return;
5615      }
5616
5617      const declarations = symbol.getDeclarations();
5618      if (!declarations || declarations.length === 0) {
5619        return;
5620      }
5621
5622      if (ts.isEnumMember(declarations[0])) {
5623        this.incrementCounters(typeRef, FaultID.NoEnumPropAsType);
5624      }
5625    }
5626  }
5627
5628  private checkPartialType(node: ts.Node): void {
5629    const typeRef = node as ts.TypeReferenceNode;
5630    // Using Partial<T> type is allowed only when its argument type is either Class or Interface.
5631    const isStdPartial = this.tsUtils.entityNameToString(typeRef.typeName) === 'Partial';
5632    if (!isStdPartial) {
5633      return;
5634    }
5635
5636    const hasSingleTypeArgument = !!typeRef.typeArguments && typeRef.typeArguments.length === 1;
5637    let argType;
5638    if (!this.options.useRtLogic) {
5639      const firstTypeArg = !!typeRef.typeArguments && hasSingleTypeArgument && typeRef.typeArguments[0];
5640      argType = firstTypeArg && this.tsTypeChecker.getTypeFromTypeNode(firstTypeArg);
5641    } else {
5642      argType = hasSingleTypeArgument && this.tsTypeChecker.getTypeFromTypeNode(typeRef.typeArguments[0]);
5643    }
5644
5645    if (argType && !argType.isClassOrInterface()) {
5646      this.incrementCounters(node, FaultID.UtilityType);
5647    }
5648  }
5649
5650  private checkSendableTypeArguments(typeRef: ts.TypeReferenceNode): void {
5651    if (typeRef.typeArguments) {
5652      for (const typeArg of typeRef.typeArguments) {
5653        if (!this.tsUtils.isSendableTypeNode(typeArg)) {
5654          this.incrementCounters(typeArg, FaultID.SendableGenericTypes);
5655        }
5656      }
5657    }
5658  }
5659
5660  private handleMetaProperty(node: ts.Node): void {
5661    const tsMetaProperty = node as ts.MetaProperty;
5662    if (tsMetaProperty.name.text === 'target') {
5663      this.incrementCounters(node, FaultID.NewTarget);
5664    }
5665  }
5666
5667  private handleSpreadOp(node: ts.Node): void {
5668
5669    /*
5670     * spread assignment is disabled
5671     * spread element is allowed only for arrays as rest parameter
5672     */
5673    if (ts.isSpreadElement(node)) {
5674      const spreadExprType = this.tsUtils.getTypeOrTypeConstraintAtLocation(node.expression);
5675      if (
5676        spreadExprType &&
5677        (this.options.useRtLogic || ts.isCallLikeExpression(node.parent) || ts.isArrayLiteralExpression(node.parent)) &&
5678        (this.tsUtils.isOrDerivedFrom(spreadExprType, this.tsUtils.isArray) ||
5679          this.tsUtils.isOrDerivedFrom(spreadExprType, this.tsUtils.isCollectionArrayType))
5680      ) {
5681        return;
5682      }
5683    }
5684    this.incrementCounters(node, FaultID.SpreadOperator);
5685  }
5686
5687  private handleConstructSignature(node: ts.Node): void {
5688    switch (node.parent.kind) {
5689      case ts.SyntaxKind.TypeLiteral:
5690        this.incrementCounters(node, FaultID.ConstructorType);
5691        break;
5692      case ts.SyntaxKind.InterfaceDeclaration:
5693        this.incrementCounters(node, FaultID.ConstructorIface);
5694        break;
5695      default:
5696    }
5697  }
5698
5699  private handleExpressionWithTypeArguments(node: ts.Node): void {
5700    const tsTypeExpr = node as ts.ExpressionWithTypeArguments;
5701    const symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression);
5702
5703    if (!!symbol && TsUtils.isEsObjectSymbol(symbol)) {
5704      const faultId = this.options.arkts2 ? FaultID.EsValueTypeError : FaultID.EsValueType;
5705      this.incrementCounters(tsTypeExpr, faultId);
5706    }
5707    this.handleSdkGlobalApi(tsTypeExpr);
5708  }
5709
5710  private handleComputedPropertyName(node: ts.Node): void {
5711    const computedProperty = node as ts.ComputedPropertyName;
5712    if (this.isSendableCompPropName(computedProperty)) {
5713      // cancel the '[Symbol.iterface]' restriction of 'sendable class/interface' in the 'collections.d.ts' file
5714      if (this.tsUtils.isSymbolIteratorExpression(computedProperty.expression)) {
5715        const declNode = computedProperty.parent?.parent;
5716        if (declNode && TsUtils.isArkTSCollectionsClassOrInterfaceDeclaration(declNode)) {
5717          return;
5718        }
5719      }
5720      this.incrementCounters(node, FaultID.SendableComputedPropName);
5721    } else if (!this.tsUtils.isValidComputedPropertyName(computedProperty, false)) {
5722      this.incrementCounters(node, FaultID.ComputedPropertyName);
5723    }
5724  }
5725
5726  private isSendableCompPropName(compProp: ts.ComputedPropertyName): boolean {
5727    const declNode = compProp.parent?.parent;
5728    if (declNode && ts.isClassDeclaration(declNode) && TsUtils.hasSendableDecorator(declNode)) {
5729      return true;
5730    } else if (declNode && ts.isInterfaceDeclaration(declNode)) {
5731      const declNodeType = this.tsTypeChecker.getTypeAtLocation(declNode);
5732      if (this.tsUtils.isSendableClassOrInterface(declNodeType)) {
5733        return true;
5734      }
5735    }
5736    return false;
5737  }
5738
5739  private handleGetAccessor(node: ts.GetAccessorDeclaration): void {
5740    TsUtils.getDecoratorsIfInSendableClass(node)?.forEach((decorator) => {
5741      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
5742    });
5743  }
5744
5745  private handleSetAccessor(node: ts.SetAccessorDeclaration): void {
5746    TsUtils.getDecoratorsIfInSendableClass(node)?.forEach((decorator) => {
5747      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
5748    });
5749  }
5750
5751  /*
5752   * issue 13987:
5753   * When variable have no type annotation and no initial value, and 'noImplicitAny'
5754   * option is enabled, compiler attempts to infer type from variable references:
5755   * see https://github.com/microsoft/TypeScript/pull/11263.
5756   * In this case, we still want to report the error, since ArkTS doesn't allow
5757   * to omit both type annotation and initializer.
5758   */
5759  private proceedVarPropDeclaration(
5760    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
5761  ): boolean | undefined {
5762    if (
5763      (ts.isVariableDeclaration(decl) && ts.isVariableStatement(decl.parent.parent) ||
5764        ts.isPropertyDeclaration(decl)) &&
5765      !decl.initializer
5766    ) {
5767      if (
5768        ts.isPropertyDeclaration(decl) &&
5769        this.tsUtils.skipPropertyInferredTypeCheck(decl, this.sourceFile, this.options.isEtsFileCb)
5770      ) {
5771        return true;
5772      }
5773
5774      this.incrementCounters(decl, FaultID.AnyType);
5775      return true;
5776    }
5777    return undefined;
5778  }
5779
5780  private handleDeclarationInferredType(
5781    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
5782  ): void {
5783    // The type is explicitly specified, no need to check inferred type.
5784    if (decl.type) {
5785      return;
5786    }
5787
5788    /*
5789     * issue 13161:
5790     * In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since
5791     * ArkTS doesn't support these types, the type for such variable is simply omitted,
5792     * and we don't report it as an error. See TypeScriptLinter.handleCatchClause()
5793     * for reference.
5794     */
5795    if (ts.isCatchClause(decl.parent)) {
5796      return;
5797    }
5798    // Destructuring declarations are not supported, do not process them.
5799    if (ts.isArrayBindingPattern(decl.name) || ts.isObjectBindingPattern(decl.name)) {
5800      return;
5801    }
5802
5803    if (this.proceedVarPropDeclaration(decl)) {
5804      return;
5805    }
5806
5807    const type = this.tsTypeChecker.getTypeAtLocation(decl);
5808    if (type) {
5809      this.validateDeclInferredType(type, decl);
5810    }
5811  }
5812
5813  private handleDefiniteAssignmentAssertion(decl: ts.VariableDeclaration | ts.PropertyDeclaration): void {
5814    if (decl.exclamationToken === undefined) {
5815      return;
5816    }
5817
5818    if (decl.kind === ts.SyntaxKind.PropertyDeclaration) {
5819      const parentDecl = decl.parent;
5820      if (parentDecl.kind === ts.SyntaxKind.ClassDeclaration && TsUtils.hasSendableDecorator(parentDecl)) {
5821        this.incrementCounters(decl, FaultID.SendableDefiniteAssignment);
5822        return;
5823      }
5824    }
5825    const faultId = this.options.arkts2 ? FaultID.DefiniteAssignmentError : FaultID.DefiniteAssignment;
5826    this.incrementCounters(decl, faultId);
5827  }
5828
5829  private readonly validatedTypesSet = new Set<ts.Type>();
5830
5831  private checkAnyOrUnknownChildNode(node: ts.Node): boolean {
5832    if (node.kind === ts.SyntaxKind.AnyKeyword || node.kind === ts.SyntaxKind.UnknownKeyword) {
5833      return true;
5834    }
5835    for (const child of node.getChildren()) {
5836      if (this.checkAnyOrUnknownChildNode(child)) {
5837        return true;
5838      }
5839    }
5840    return false;
5841  }
5842
5843  private handleInferredObjectreference(
5844    type: ts.Type,
5845    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
5846  ): void {
5847    const typeArgs = this.tsTypeChecker.getTypeArguments(type as ts.TypeReference);
5848    if (typeArgs) {
5849      const haveAnyOrUnknownNodes = this.checkAnyOrUnknownChildNode(decl);
5850      if (!haveAnyOrUnknownNodes) {
5851        for (const typeArg of typeArgs) {
5852          this.validateDeclInferredType(typeArg, decl);
5853        }
5854      }
5855    }
5856  }
5857
5858  private validateDeclInferredType(
5859    type: ts.Type,
5860    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
5861  ): void {
5862    if (type.aliasSymbol !== undefined) {
5863      return;
5864    }
5865    if (TsUtils.isObjectType(type) && !!(type.objectFlags & ts.ObjectFlags.Reference)) {
5866      this.handleInferredObjectreference(type, decl);
5867      return;
5868    }
5869    if (this.validatedTypesSet.has(type)) {
5870      return;
5871    }
5872    if (type.isUnion()) {
5873      this.validatedTypesSet.add(type);
5874      for (const unionElem of type.types) {
5875        this.validateDeclInferredType(unionElem, decl);
5876      }
5877    }
5878    if (TsUtils.isAnyType(type)) {
5879      this.incrementCounters(decl, FaultID.AnyType);
5880    } else if (TsUtils.isUnknownType(type)) {
5881      this.incrementCounters(decl, FaultID.UnknownType);
5882    }
5883  }
5884
5885  private handleCommentDirectives(sourceFile: ts.SourceFile): void {
5886
5887    /*
5888     * We use a dirty hack to retrieve list of parsed comment directives by accessing
5889     * internal properties of SourceFile node.
5890     */
5891    /* CC-OFFNXT(no_explicit_any) std lib */
5892    // Handle comment directive '@ts-nocheck'
5893    const pragmas = (sourceFile as any).pragmas;
5894    if (pragmas && pragmas instanceof Map) {
5895      const noCheckPragma = pragmas.get('ts-nocheck');
5896      if (noCheckPragma) {
5897
5898        /*
5899         * The value is either a single entry or an array of entries.
5900         * Wrap up single entry with array to simplify processing.
5901         */
5902        /* CC-OFFNXT(no_explicit_any) std lib */
5903        const noCheckEntries: any[] = Array.isArray(noCheckPragma) ? noCheckPragma : [noCheckPragma];
5904        for (const entry of noCheckEntries) {
5905          this.processNoCheckEntry(entry);
5906        }
5907      }
5908    }
5909
5910    /* CC-OFFNXT(no_explicit_any) std lib */
5911    // Handle comment directives '@ts-ignore' and '@ts-expect-error'
5912    const commentDirectives = (sourceFile as any).commentDirectives;
5913    if (commentDirectives && Array.isArray(commentDirectives)) {
5914      for (const directive of commentDirectives) {
5915        if (directive.range?.pos === undefined || directive.range?.end === undefined) {
5916          continue;
5917        }
5918
5919        const range = directive.range as ts.TextRange;
5920        const kind: ts.SyntaxKind =
5921          sourceFile.text.slice(range.pos, range.pos + 2) === '/*' ?
5922            ts.SyntaxKind.MultiLineCommentTrivia :
5923            ts.SyntaxKind.SingleLineCommentTrivia;
5924        const commentRange: ts.CommentRange = {
5925          pos: range.pos,
5926          end: range.end,
5927          kind
5928        };
5929
5930        this.incrementCounters(commentRange, FaultID.ErrorSuppression);
5931      }
5932    }
5933  }
5934
5935  /* CC-OFFNXT(no_explicit_any) std lib */
5936  private processNoCheckEntry(entry: any): void {
5937    if (entry.range?.kind === undefined || entry.range?.pos === undefined || entry.range?.end === undefined) {
5938      return;
5939    }
5940
5941    this.incrementCounters(entry.range as ts.CommentRange, FaultID.ErrorSuppression);
5942  }
5943
5944  private reportThisKeywordsInScope(scope: ts.Block | ts.Expression): void {
5945    const callback = (node: ts.Node): void => {
5946      if (node.kind === ts.SyntaxKind.ThisKeyword) {
5947        this.incrementCounters(node, FaultID.FunctionContainsThis);
5948      }
5949    };
5950    const stopCondition = (node: ts.Node): boolean => {
5951      const isClassLike = ts.isClassDeclaration(node) || ts.isClassExpression(node);
5952      const isFunctionLike = ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node);
5953      const isModuleDecl = ts.isModuleDeclaration(node);
5954      return isClassLike || isFunctionLike || isModuleDecl;
5955    };
5956    forEachNodeInSubtree(scope, callback, stopCondition);
5957  }
5958
5959  private handleConstructorDeclaration(node: ts.Node): void {
5960    const ctorDecl = node as ts.ConstructorDeclaration;
5961    this.checkDefaultParamBeforeRequired(ctorDecl);
5962    this.handleTSOverload(ctorDecl);
5963    const paramProperties = ctorDecl.parameters.filter((x) => {
5964      return this.tsUtils.hasAccessModifier(x);
5965    });
5966    if (paramProperties.length === 0) {
5967      return;
5968    }
5969    let paramTypes: ts.TypeNode[] | undefined;
5970    if (ctorDecl.body) {
5971      paramTypes = this.collectCtorParamTypes(ctorDecl);
5972    }
5973    const autofix = this.autofixer?.fixCtorParameterProperties(ctorDecl, paramTypes);
5974    for (const param of paramProperties) {
5975      this.incrementCounters(param, FaultID.ParameterProperties, autofix);
5976    }
5977  }
5978
5979  private collectCtorParamTypes(ctorDecl: ts.ConstructorDeclaration): ts.TypeNode[] | undefined {
5980    const paramTypes: ts.TypeNode[] = [];
5981
5982    for (const param of ctorDecl.parameters) {
5983      let paramTypeNode = param.type;
5984      if (!paramTypeNode) {
5985        const paramType = this.tsTypeChecker.getTypeAtLocation(param);
5986        paramTypeNode = this.tsTypeChecker.typeToTypeNode(paramType, param, ts.NodeBuilderFlags.None);
5987      }
5988      if (!paramTypeNode || !this.tsUtils.isSupportedType(paramTypeNode)) {
5989        return undefined;
5990      }
5991      paramTypes.push(paramTypeNode);
5992    }
5993
5994    return paramTypes;
5995  }
5996
5997  private handlePrivateIdentifier(node: ts.Node): void {
5998    const ident = node as ts.PrivateIdentifier;
5999    const autofix = this.autofixer?.fixPrivateIdentifier(ident);
6000    this.incrementCounters(node, FaultID.PrivateIdentifier, autofix);
6001  }
6002
6003  private handleIndexSignature(node: ts.Node): void {
6004    if (!this.tsUtils.isAllowedIndexSignature(node as ts.IndexSignatureDeclaration)) {
6005      this.incrementCounters(node, FaultID.IndexMember);
6006    }
6007  }
6008
6009  private handleTypeLiteral(node: ts.Node): void {
6010    const typeLiteral = node as ts.TypeLiteralNode;
6011    const autofix = this.autofixer?.fixTypeliteral(typeLiteral);
6012    this.incrementCounters(node, FaultID.ObjectTypeLiteral, autofix);
6013  }
6014
6015  private scanCapturedVarsInSendableScope(startNode: ts.Node, scope: ts.Node, faultId: FaultID): void {
6016    const callback = (node: ts.Node): void => {
6017      // Namespace import will introduce closure in the es2abc compiler stage
6018      if (!ts.isIdentifier(node) || this.checkNamespaceImportVar(node)) {
6019        return;
6020      }
6021
6022      // The "b" of "A.b" should not be checked since it's load from object "A"
6023      const parent: ts.Node = node.parent;
6024      if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
6025        return;
6026      }
6027      // When overloading function, will misreport
6028      if (ts.isFunctionDeclaration(startNode) && startNode.name === node) {
6029        return;
6030      }
6031
6032      this.checkLocalDecl(node, scope, faultId);
6033    };
6034    // Type nodes should not checked because no closure will be introduced
6035    const stopCondition = (node: ts.Node): boolean => {
6036      // already existed 'arkts-sendable-class-decoratos' error
6037      if (ts.isDecorator(node) && node.parent === startNode) {
6038        return true;
6039      }
6040      return ts.isTypeReferenceNode(node);
6041    };
6042    forEachNodeInSubtree(startNode, callback, stopCondition);
6043  }
6044
6045  private checkLocalDecl(node: ts.Identifier, scope: ts.Node, faultId: FaultID): void {
6046    const trueSym = this.tsUtils.trueSymbolAtLocation(node);
6047    // Sendable decorator should be used in method of Sendable classes
6048    if (trueSym === undefined) {
6049      return;
6050    }
6051
6052    // Const enum member will be replaced by the exact value of it, no closure will be introduced
6053    if (TsUtils.isConstEnum(trueSym)) {
6054      return;
6055    }
6056
6057    const declarations = trueSym.getDeclarations();
6058    if (declarations?.length) {
6059      this.checkLocalDeclWithSendableClosure(node, scope, declarations[0], faultId);
6060    }
6061  }
6062
6063  private checkLocalDeclWithSendableClosure(
6064    node: ts.Identifier,
6065    scope: ts.Node,
6066    decl: ts.Declaration,
6067    faultId: FaultID
6068  ): void {
6069    const declPosition = decl.getStart();
6070    if (
6071      decl.getSourceFile().fileName !== node.getSourceFile().fileName ||
6072      declPosition !== undefined && declPosition >= scope.getStart() && declPosition < scope.getEnd()
6073    ) {
6074      return;
6075    }
6076
6077    if (this.isFileExportDecl(decl)) {
6078      return;
6079    }
6080
6081    if (this.isTopSendableClosure(decl)) {
6082      return;
6083    }
6084
6085    /**
6086     * The cases in condition will introduce closure if defined in the same file as the Sendable class. The following
6087     * cases are excluded because they are not allowed in ArkTS:
6088     * 1. ImportEqualDecalration
6089     * 2. BindingElement
6090     */
6091    if (
6092      ts.isVariableDeclaration(decl) ||
6093      ts.isFunctionDeclaration(decl) ||
6094      ts.isClassDeclaration(decl) ||
6095      ts.isInterfaceDeclaration(decl) ||
6096      ts.isEnumDeclaration(decl) ||
6097      ts.isModuleDeclaration(decl) ||
6098      ts.isParameter(decl)
6099    ) {
6100      this.incrementCounters(node, faultId);
6101    }
6102  }
6103
6104  private isTopSendableClosure(decl: ts.Declaration): boolean {
6105    if (!ts.isSourceFile(decl.parent)) {
6106      return false;
6107    }
6108    if (
6109      ts.isClassDeclaration(decl) &&
6110      this.tsUtils.isSendableClassOrInterface(this.tsTypeChecker.getTypeAtLocation(decl))
6111    ) {
6112      return true;
6113    }
6114    if (ts.isFunctionDeclaration(decl) && TsUtils.hasSendableDecoratorFunctionOverload(decl)) {
6115      return true;
6116    }
6117    return false;
6118  }
6119
6120  private checkNamespaceImportVar(node: ts.Node): boolean {
6121    // Namespace import cannot be determined by the true symbol
6122    const sym = this.tsTypeChecker.getSymbolAtLocation(node);
6123    const decls = sym?.getDeclarations();
6124    if (decls?.length) {
6125      if (ts.isNamespaceImport(decls[0])) {
6126        this.incrementCounters(node, FaultID.SendableCapturedVars);
6127        return true;
6128      }
6129    }
6130    return false;
6131  }
6132
6133  private isFileExportDecl(decl: ts.Declaration): boolean {
6134    const sourceFile = decl.getSourceFile();
6135    if (!this.fileExportDeclCaches) {
6136      this.fileExportDeclCaches = this.tsUtils.searchFileExportDecl(sourceFile);
6137    }
6138    return this.fileExportDeclCaches.has(decl);
6139  }
6140
6141  private handleExportKeyword(node: ts.Node): void {
6142    const parentNode = node.parent;
6143    if (!TypeScriptLinter.inSharedModule(node) || ts.isModuleBlock(parentNode.parent)) {
6144      return;
6145    }
6146
6147    switch (parentNode.kind) {
6148      case ts.SyntaxKind.EnumDeclaration:
6149      case ts.SyntaxKind.InterfaceDeclaration:
6150      case ts.SyntaxKind.FunctionDeclaration:
6151      case ts.SyntaxKind.ClassDeclaration:
6152        if (!this.tsUtils.isShareableType(this.tsTypeChecker.getTypeAtLocation(parentNode))) {
6153          this.incrementCounters((parentNode as ts.NamedDeclaration).name ?? parentNode, FaultID.SharedModuleExports);
6154        }
6155        return;
6156      case ts.SyntaxKind.VariableStatement:
6157        for (const variableDeclaration of (parentNode as ts.VariableStatement).declarationList.declarations) {
6158          if (!this.tsUtils.isShareableEntity(variableDeclaration.name)) {
6159            this.incrementCounters(variableDeclaration.name, FaultID.SharedModuleExports);
6160          }
6161        }
6162        return;
6163      case ts.SyntaxKind.TypeAliasDeclaration:
6164        if (!this.tsUtils.isShareableEntity(parentNode)) {
6165          this.incrementCounters(parentNode, FaultID.SharedModuleExportsWarning);
6166        }
6167        return;
6168      default:
6169        this.incrementCounters(parentNode, FaultID.SharedModuleExports);
6170    }
6171  }
6172
6173  private handleExportDeclaration(node: ts.Node): void {
6174    const exportDecl = node as ts.ExportDeclaration;
6175
6176    if (this.isExportedEntityDeclaredInJs(exportDecl)) {
6177      this.incrementCounters(node, FaultID.InteropJsObjectExport);
6178      return;
6179    }
6180
6181    if (!TypeScriptLinter.inSharedModule(node) || ts.isModuleBlock(node.parent)) {
6182      return;
6183    }
6184
6185    if (exportDecl.exportClause === undefined) {
6186      this.incrementCounters(exportDecl, FaultID.SharedModuleNoWildcardExport);
6187      return;
6188    }
6189
6190    if (ts.isNamespaceExport(exportDecl.exportClause)) {
6191      if (!this.tsUtils.isShareableType(this.tsTypeChecker.getTypeAtLocation(exportDecl.exportClause.name))) {
6192        this.incrementCounters(exportDecl.exportClause.name, FaultID.SharedModuleExports);
6193      }
6194      return;
6195    }
6196
6197    for (const exportSpecifier of exportDecl.exportClause.elements) {
6198      if (!this.tsUtils.isShareableEntity(exportSpecifier.name)) {
6199        this.incrementCounters(exportSpecifier.name, FaultID.SharedModuleExports);
6200      }
6201    }
6202  }
6203
6204  private handleReturnStatement(node: ts.Node): void {
6205    // The return value must match the return type of the 'function'
6206    const returnStat = node as ts.ReturnStatement;
6207    const expr = returnStat.expression;
6208    if (!expr) {
6209      return;
6210    }
6211    const lhsType = this.tsTypeChecker.getContextualType(expr);
6212    if (!lhsType) {
6213      return;
6214    }
6215    this.checkAssignmentMatching(node, lhsType, expr, true);
6216    this.handleObjectLiteralInReturn(returnStat);
6217    this.handleObjectLiteralAssignmentToClass(returnStat);
6218  }
6219
6220  /**
6221   * 'arkts-no-structural-typing' check was missing in some scenarios,
6222   * in order not to cause incompatibility,
6223   * only need to strictly match the type of filling the check again
6224   */
6225  private checkAssignmentMatching(
6226    field: ts.Node,
6227    lhsType: ts.Type,
6228    rhsExpr: ts.Expression,
6229    isNewStructuralCheck: boolean = false
6230  ): void {
6231    const rhsType = this.tsTypeChecker.getTypeAtLocation(rhsExpr);
6232    this.handleNoTuplesArrays(field, lhsType, rhsType);
6233    this.handleArrayTypeImmutable(field, lhsType, rhsType, rhsExpr);
6234    // check that 'sendable typeAlias' is assigned correctly
6235    if (this.tsUtils.isWrongSendableFunctionAssignment(lhsType, rhsType)) {
6236      this.incrementCounters(field, FaultID.SendableFunctionAssignment);
6237    }
6238    const isStrict = this.tsUtils.needStrictMatchType(lhsType, rhsType);
6239    // 'isNewStructuralCheck' means that this assignment scenario was previously omitted, so only strict matches are checked now
6240    if (isNewStructuralCheck && !isStrict) {
6241      return;
6242    }
6243    if (this.tsUtils.needToDeduceStructuralIdentity(lhsType, rhsType, rhsExpr, isStrict)) {
6244      if (ts.isNewExpression(rhsExpr) && ts.isIdentifier(rhsExpr.expression) && rhsExpr.expression.text === 'Promise') {
6245        const isReturnStatement = ts.isReturnStatement(rhsExpr.parent);
6246        const enclosingFunction = ts.findAncestor(rhsExpr, ts.isFunctionLike);
6247        const isAsyncFunction =
6248          enclosingFunction &&
6249          (enclosingFunction.modifiers?.some((m) => {
6250            return m.kind === ts.SyntaxKind.AsyncKeyword;
6251          }) ||
6252            false);
6253        if (isReturnStatement && isAsyncFunction) {
6254          return;
6255        }
6256      }
6257      this.incrementCounters(field, FaultID.StructuralIdentity);
6258    }
6259  }
6260
6261  private handleDecorator(node: ts.Node): void {
6262    this.handleExtendDecorator(node);
6263    this.handleEntryDecorator(node);
6264    this.handleProvideDecorator(node);
6265    this.handleLocalBuilderDecorator(node);
6266
6267    const decorator: ts.Decorator = node as ts.Decorator;
6268    this.checkSendableAndConcurrentDecorator(decorator);
6269    this.handleStylesDecorator(decorator);
6270    if (TsUtils.getDecoratorName(decorator) === SENDABLE_DECORATOR) {
6271      const parent: ts.Node = decorator.parent;
6272      if (!parent || !SENDABLE_DECORATOR_NODES.includes(parent.kind)) {
6273        const autofix = this.autofixer?.removeNode(decorator);
6274        this.incrementCounters(decorator, FaultID.SendableDecoratorLimited, autofix);
6275      }
6276    }
6277    this.handleNotSupportCustomDecorators(decorator);
6278  }
6279
6280  private handleProvideDecorator(node: ts.Node): void {
6281    if (!this.options.arkts2) {
6282      return;
6283    }
6284
6285    if (!ts.isDecorator(node)) {
6286      return;
6287    }
6288
6289    if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression)) {
6290      if (node.expression.expression.text !== PROVIDE_DECORATOR_NAME || node.expression.arguments.length !== 1) {
6291        return;
6292      }
6293      const arg = node.expression.arguments[0];
6294      if (!ts.isStringLiteral(arg) && !ts.isObjectLiteralExpression(arg)) {
6295        return;
6296      }
6297      if (ts.isObjectLiteralExpression(arg)) {
6298        const properties = arg.properties;
6299        if (properties.length !== 1) {
6300          return;
6301        }
6302        const property = properties[0] as ts.PropertyAssignment;
6303        if (!ts.isIdentifier(property.name) || !ts.isStringLiteral(property.initializer)) {
6304          return;
6305        }
6306        if (property.name.escapedText !== PROVIDE_ALLOW_OVERRIDE_PROPERTY_NAME) {
6307          return;
6308        }
6309      }
6310      const autofix = this.autofixer?.fixProvideDecorator(node);
6311      this.incrementCounters(node.parent, FaultID.ProvideAnnotation, autofix);
6312    }
6313  }
6314
6315  private isSendableDecoratorValid(decl: ts.FunctionDeclaration | ts.TypeAliasDeclaration): boolean {
6316    if (
6317      this.compatibleSdkVersion > SENDBALE_FUNCTION_START_VERSION ||
6318      this.compatibleSdkVersion === SENDBALE_FUNCTION_START_VERSION &&
6319        !SENDABLE_FUNCTION_UNSUPPORTED_STAGES_IN_API12.includes(this.compatibleSdkVersionStage)
6320    ) {
6321      return true;
6322    }
6323    const curDecorator = TsUtils.getSendableDecorator(decl);
6324    if (curDecorator) {
6325      this.incrementCounters(curDecorator, FaultID.SendableBetaCompatible);
6326    }
6327    return false;
6328  }
6329
6330  private handleImportType(node: ts.Node): void {
6331    if (!this.options.arkts2) {
6332      return;
6333    }
6334    this.incrementCounters(node, FaultID.ImportType);
6335    this.incrementCounters(node, FaultID.DynamicImport);
6336  }
6337
6338  private handleVoidExpression(node: ts.Node): void {
6339    if (!this.options.arkts2) {
6340      return;
6341    }
6342    const autofix = this.autofixer?.fixVoidOperator(node as ts.VoidExpression);
6343    this.incrementCounters(node, FaultID.VoidOperator, autofix);
6344  }
6345
6346  private handleRegularExpressionLiteral(node: ts.Node): void {
6347    if (!this.options.arkts2) {
6348      return;
6349    }
6350    const autofix = this.autofixer?.fixRegularExpressionLiteral(node as ts.RegularExpressionLiteral);
6351    this.incrementCounters(node, FaultID.RegularExpressionLiteral, autofix);
6352  }
6353
6354  private handleLimitedVoidType(node: ts.VariableDeclaration): void {
6355    if (!this.options.arkts2) {
6356      return;
6357    }
6358
6359    const typeNode = node.type;
6360    if (typeNode && TsUtils.typeContainsVoid(typeNode)) {
6361      const autofix = this.autofixer?.fixLimitedVoidType(node);
6362      this.incrementCounters(typeNode, FaultID.LimitedVoidType, autofix);
6363    }
6364  }
6365
6366  private handleLimitedVoidWithCall(node: ts.CallExpression): void {
6367    if (!this.options.arkts2) {
6368      return;
6369    }
6370
6371    if (ts.isPropertyAccessExpression(node.parent)) {
6372      return;
6373    }
6374
6375    const signature = this.tsTypeChecker.getResolvedSignature(node);
6376    if (!signature) {
6377      return;
6378    }
6379
6380    const returnType = this.tsTypeChecker.getReturnTypeOfSignature(signature);
6381    if (this.tsTypeChecker.typeToString(returnType) !== 'void') {
6382      return;
6383    }
6384
6385    if (ts.isReturnStatement(node.parent)) {
6386      const functionLike = TypeScriptLinter.findContainingFunction(node);
6387      if (functionLike && TypeScriptLinter.isRecursiveCall(node, functionLike)) {
6388        this.incrementCounters(node, FaultID.LimitedVoidType);
6389      }
6390      return;
6391    }
6392
6393    this.incrementCounters(node, FaultID.LimitedVoidType);
6394  }
6395
6396  private static findContainingFunction(node: ts.Node): ts.FunctionLikeDeclaration | undefined {
6397    while (node) {
6398      if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node)) {
6399        return node;
6400      }
6401      node = node.parent;
6402    }
6403    return undefined;
6404  }
6405
6406  // Helper function to check if a call is recursive
6407  private static isRecursiveCall(callExpr: ts.CallExpression, fn: ts.FunctionLikeDeclaration): boolean {
6408    return (
6409      ts.isIdentifier(callExpr.expression) &&
6410      ts.isFunctionDeclaration(fn) &&
6411      !!fn.name &&
6412      fn.name.text === callExpr.expression.text
6413    );
6414  }
6415
6416  private handleArrayType(arrayType: ts.Node): void {
6417    if (!this.options.arkts2) {
6418      return;
6419    }
6420
6421    if (!arrayType || !ts.isArrayTypeNode(arrayType)) {
6422      return;
6423    }
6424
6425    if (arrayType.elementType.kind === ts.SyntaxKind.VoidKeyword) {
6426      this.incrementCounters(arrayType.elementType, FaultID.LimitedVoidType);
6427    }
6428  }
6429
6430  private handleUnionType(unionType: ts.Node): void {
6431    if (!this.options.arkts2) {
6432      return;
6433    }
6434
6435    if (!unionType || !ts.isUnionTypeNode(unionType)) {
6436      return;
6437    }
6438
6439    const types = unionType.types;
6440    for (const type of types) {
6441      if (type.kind === ts.SyntaxKind.VoidKeyword) {
6442        this.incrementCounters(type, FaultID.LimitedVoidType);
6443      }
6444    }
6445  }
6446
6447  private handleDebuggerStatement(node: ts.Node): void {
6448    if (!this.options.arkts2) {
6449      return;
6450    }
6451
6452    this.incrementCounters(node, FaultID.DebuggerStatement);
6453  }
6454
6455  private handleTSOverload(decl: ts.FunctionDeclaration | ts.MethodDeclaration | ts.ConstructorDeclaration): void {
6456    if (!this.options.arkts2) {
6457      return;
6458    }
6459    if (decl.name) {
6460      const symbol = this.tsTypeChecker.getSymbolAtLocation(decl.name);
6461      if (!symbol) {
6462        return;
6463      }
6464      const declarations = symbol.getDeclarations();
6465      if (!declarations) {
6466        return;
6467      }
6468      const filterDecl = declarations.filter((name) => {
6469        return ts.isFunctionDeclaration(name) || ts.isMethodDeclaration(name);
6470      });
6471      const isInternalFunction = decl.name && ts.isIdentifier(decl.name) && interanlFunction.includes(decl.name.text);
6472      if (isInternalFunction && filterDecl.length > 2 || !isInternalFunction && filterDecl.length > 1) {
6473        this.incrementCounters(decl, FaultID.TsOverload);
6474      }
6475    } else if (ts.isConstructorDeclaration(decl) && decl.getText()) {
6476      this.handleTSOverloadUnderConstructorDeclaration(decl);
6477    }
6478  }
6479
6480  private handleTSOverloadUnderConstructorDeclaration(decl: ts.ConstructorDeclaration): void {
6481    const parent = decl.parent;
6482    const constructors = parent.members.filter(ts.isConstructorDeclaration);
6483    const isStruct = decl.getText() && ts.isStructDeclaration(parent);
6484    if ((isStruct ? --constructors.length : constructors.length) > 1) {
6485      this.incrementCounters(decl, FaultID.TsOverload);
6486    }
6487  }
6488
6489  private handleSwitchStatement(node: ts.Node): void {
6490    if (!this.options.arkts2) {
6491      return;
6492    }
6493    const switchStatement = node as ts.SwitchStatement;
6494
6495    this.validateSwitchExpression(switchStatement);
6496
6497    const duplicateCases = this.findDuplicateCases(switchStatement);
6498    if (duplicateCases.length > 0) {
6499      for (const duplicateCase of duplicateCases) {
6500        this.incrementCounters(duplicateCase.expression, FaultID.CaseExpression);
6501      }
6502    }
6503  }
6504
6505  private validateSwitchExpression(switchStatement: ts.SwitchStatement): void {
6506    const expr = switchStatement.expression;
6507    const nodeType = this.tsTypeChecker.getTypeAtLocation(expr);
6508    const { isLiteralInitialized, isFloatLiteral, hasExplicitTypeAnnotation } = this.getDeclarationInfo(expr);
6509
6510    const isUnionType = (nodeType.flags & ts.TypeFlags.Union) !== 0;
6511
6512    const isTypeAllowed = (t: ts.Type): boolean => {
6513      const typeText = this.tsTypeChecker.typeToString(t);
6514      return Boolean(
6515        t.flags & ts.TypeFlags.StringLike ||
6516          typeText === 'String' ||
6517          t.flags & ts.TypeFlags.NumberLike && (/^\d+$/).test(typeText) ||
6518          isLiteralInitialized && !hasExplicitTypeAnnotation && !isFloatLiteral ||
6519          t.flags & ts.TypeFlags.EnumLike
6520      );
6521    };
6522
6523    let isAllowed = !isUnionType && isTypeAllowed(nodeType);
6524
6525    if (isUnionType) {
6526      const unionType = nodeType as ts.UnionType;
6527      isAllowed = unionType.types.every(isTypeAllowed);
6528    }
6529
6530    if (!isAllowed) {
6531      this.incrementCounters(expr, FaultID.SwitchExpression);
6532    }
6533  }
6534
6535  private getDeclarationInfo(expression: ts.Expression): {
6536    isLiteralInitialized: boolean;
6537    isFloatLiteral: boolean;
6538    hasExplicitTypeAnnotation: boolean;
6539  } {
6540    const symbol = this.tsTypeChecker.getSymbolAtLocation(expression);
6541    const declaration = symbol?.valueDeclaration;
6542
6543    if (!declaration || !ts.isVariableDeclaration(declaration)) {
6544      return { isLiteralInitialized: false, isFloatLiteral: false, hasExplicitTypeAnnotation: false };
6545    }
6546
6547    const hasExplicitTypeAnnotation = !!declaration.type;
6548    const initializerInfo = TypeScriptLinter.getInitializerInfo(declaration.initializer);
6549
6550    return {
6551      isLiteralInitialized: initializerInfo.isLiteralInitialized,
6552      isFloatLiteral: initializerInfo.isFloatLiteral,
6553      hasExplicitTypeAnnotation
6554    };
6555  }
6556
6557  private static getInitializerInfo(initializer?: ts.Expression): {
6558    isLiteralInitialized: boolean;
6559    isFloatLiteral: boolean;
6560  } {
6561    if (!initializer) {
6562      return { isLiteralInitialized: false, isFloatLiteral: false };
6563    }
6564
6565    const isLiteralInitialized = ts.isNumericLiteral(initializer) || ts.isStringLiteral(initializer);
6566
6567    let isFloatLiteral = false;
6568    if (ts.isNumericLiteral(initializer)) {
6569      const literalText = initializer.getText();
6570      if (!(/^0[xX]/).test(literalText)) {
6571        isFloatLiteral = (/\.|e[-+]|\dE[-+]/i).test(literalText);
6572      }
6573    }
6574
6575    return { isLiteralInitialized, isFloatLiteral };
6576  }
6577
6578  private findDuplicateCases(switchStatement: ts.SwitchStatement): ts.CaseClause[] {
6579    const seenValues = new Map<string | number | boolean, ts.CaseClause>();
6580    const duplicates: ts.CaseClause[] = [];
6581
6582    for (const clause of switchStatement.caseBlock.clauses) {
6583      if (ts.isCaseClause(clause) && clause.expression) {
6584        const value = this.getConstantValue(clause.expression);
6585        const key = value !== undefined ? value : clause.expression.getText();
6586        if (seenValues.has(key)) {
6587          duplicates.push(clause);
6588        } else {
6589          seenValues.set(key, clause);
6590        }
6591      }
6592    }
6593    return duplicates;
6594  }
6595
6596  private getConstantValue(expression: ts.Expression): string | number | boolean | undefined {
6597    if (ts.isLiteralExpression(expression)) {
6598      return ts.isNumericLiteral(expression) ? Number(expression.text) : expression.text;
6599    }
6600
6601    switch (expression.kind) {
6602      case ts.SyntaxKind.TrueKeyword:
6603        return true;
6604      case ts.SyntaxKind.FalseKeyword:
6605        return false;
6606      default:
6607        if (ts.isElementAccessExpression(expression) || ts.isPropertyAccessExpression(expression)) {
6608          const constantValue = this.tsTypeChecker.getConstantValue(expression);
6609          if (constantValue !== undefined) {
6610            return constantValue;
6611          }
6612        }
6613        return undefined;
6614    }
6615  }
6616
6617  private handleLimitedLiteralType(literalTypeNode: ts.LiteralTypeNode): void {
6618    if (!this.options.arkts2 || !literalTypeNode) {
6619      return;
6620    }
6621    const literal = literalTypeNode.literal;
6622    if (
6623      !(
6624        literal.kind === ts.SyntaxKind.StringLiteral ||
6625        literal.kind === ts.SyntaxKind.NullKeyword ||
6626        literal.kind === ts.SyntaxKind.UndefinedKeyword
6627      )
6628    ) {
6629      this.incrementCounters(literalTypeNode, FaultID.LimitedLiteralType);
6630    }
6631  }
6632
6633  private findVariableInitializationValue(identifier: ts.Identifier): number | null {
6634    const symbol = this.tsTypeChecker.getSymbolAtLocation(identifier);
6635    if (!symbol) {
6636      return null;
6637    }
6638    if (this.constVariableInitCache.has(symbol)) {
6639      return this.constVariableInitCache.get(symbol)!;
6640    }
6641    const declarations = symbol.getDeclarations();
6642    if (declarations && declarations.length > 0) {
6643      const declaration = declarations[0];
6644
6645      const isConditionOnEnumMember = ts.isEnumMember(declaration) && declaration.initializer;
6646      const isConditionOnVariableDecl =
6647        ts.isVariableDeclaration(declaration) &&
6648        declaration.initializer &&
6649        (declaration.parent as ts.VariableDeclarationList).flags & ts.NodeFlags.Const;
6650      if (isConditionOnEnumMember || isConditionOnVariableDecl) {
6651        const res = this.evaluateNumericValue(declaration.initializer);
6652        this.constVariableInitCache.set(symbol, res);
6653        return res;
6654      }
6655    }
6656
6657    return null;
6658  }
6659
6660  private evaluateNumericValueFromPrefixUnaryExpression(node: ts.PrefixUnaryExpression): number | null {
6661    if (node.operator === ts.SyntaxKind.MinusToken) {
6662      if (ts.isNumericLiteral(node.operand) || ts.isIdentifier(node.operand) && node.operand.text === 'Infinity') {
6663        return node.operand.text === 'Infinity' ? Number.NEGATIVE_INFINITY : -Number(node.operand.text);
6664      }
6665      const operandValue = this.evaluateNumericValue(node.operand);
6666      if (operandValue !== null) {
6667        return -operandValue;
6668      }
6669    }
6670    return null;
6671  }
6672
6673  private evaluateNumericValueFromAsExpression(node: ts.AsExpression): number | null {
6674    const typeNode = node.type;
6675    if (
6676      typeNode.kind === ts.SyntaxKind.NumberKeyword ||
6677      ts.isTypeReferenceNode(typeNode) && typeNode.typeName.getText() === 'Number'
6678    ) {
6679      return this.evaluateNumericValue(node.expression);
6680    }
6681    return null;
6682  }
6683
6684  private evaluateNumericValue(node: ts.Expression): number | null {
6685    let result: number | null = null;
6686    if (ts.isNumericLiteral(node)) {
6687      result = Number(node.text);
6688    } else if (ts.isPrefixUnaryExpression(node)) {
6689      result = this.evaluateNumericValueFromPrefixUnaryExpression(node);
6690    } else if (ts.isBinaryExpression(node)) {
6691      result = this.evaluateNumericValueFromBinaryExpression(node);
6692    } else if (ts.isPropertyAccessExpression(node)) {
6693      result = this.evaluateNumericValueFromPropertyAccess(node);
6694    } else if (ts.isParenthesizedExpression(node)) {
6695      result = this.evaluateNumericValue(node.expression);
6696    } else if (ts.isAsExpression(node)) {
6697      result = this.evaluateNumericValueFromAsExpression(node);
6698    } else if (ts.isIdentifier(node)) {
6699      if (node.text === 'Infinity') {
6700        return Number.POSITIVE_INFINITY;
6701      } else if (node.text === 'NaN') {
6702        return Number.NaN;
6703      }
6704      const symbol = this.tsTypeChecker.getSymbolAtLocation(node);
6705      return symbol ? this.constVariableInitCache.get(symbol) || null : null;
6706    }
6707    return result;
6708  }
6709
6710  private evaluateNumericValueFromBinaryExpression(node: ts.BinaryExpression): number | null {
6711    const leftValue = this.evaluateNumericValue(node.left);
6712    const rightValue = this.evaluateNumericValue(node.right);
6713    if (leftValue !== null && rightValue !== null) {
6714      switch (node.operatorToken.kind) {
6715        case ts.SyntaxKind.PlusToken:
6716          return leftValue + rightValue;
6717        case ts.SyntaxKind.MinusToken:
6718          return leftValue - rightValue;
6719        case ts.SyntaxKind.AsteriskToken:
6720          return leftValue * rightValue;
6721        case ts.SyntaxKind.SlashToken:
6722          return leftValue / rightValue;
6723        case ts.SyntaxKind.PercentToken:
6724          return leftValue % rightValue;
6725        case ts.SyntaxKind.AsteriskAsteriskToken:
6726          return Math.pow(leftValue, rightValue);
6727        default:
6728          return null;
6729      }
6730    }
6731    return null;
6732  }
6733
6734  private evaluateNumericValueFromPropertyAccess(node: ts.PropertyAccessExpression): number | null {
6735    const numberProperties = ['MIN_SAFE_INTEGER', 'MAX_SAFE_INTEGER', 'NaN', 'NEGATIVE_INFINITY', 'POSITIVE_INFINITY'];
6736    if (
6737      ts.isIdentifier(node.expression) &&
6738      node.expression.text === 'Number' &&
6739      numberProperties.includes(node.name.text)
6740    ) {
6741      switch (node.name.text) {
6742        case 'MIN_SAFE_INTEGER':
6743          return Number.MIN_SAFE_INTEGER;
6744        case 'MAX_SAFE_INTEGER':
6745          return Number.MAX_SAFE_INTEGER;
6746        case 'NaN':
6747          return Number.NaN;
6748        case 'NEGATIVE_INFINITY':
6749          return Number.NEGATIVE_INFINITY;
6750        case 'POSITIVE_INFINITY':
6751          return Number.POSITIVE_INFINITY;
6752        default:
6753          return null;
6754      }
6755    }
6756    return this.evaluateNumericValue(node.name);
6757  }
6758
6759  private collectVariableNamesAndCache(node: ts.Node): void {
6760    if (ts.isIdentifier(node)) {
6761      const value = this.findVariableInitializationValue(node);
6762      const symbol = this.tsTypeChecker.getSymbolAtLocation(node);
6763      if (value && symbol) {
6764        this.constVariableInitCache.set(symbol, value);
6765      }
6766    }
6767    ts.forEachChild(node, this.collectVariableNamesAndCache.bind(this));
6768  }
6769
6770  private handleIndexNegative(node: ts.Node): void {
6771    if (!this.options.arkts2 || !ts.isElementAccessExpression(node)) {
6772      return;
6773    }
6774    const indexNode = node.argumentExpression;
6775    if (indexNode) {
6776      this.collectVariableNamesAndCache(indexNode);
6777      const indexValue = this.evaluateNumericValue(indexNode);
6778
6779      if (indexValue !== null && (indexValue < 0 || isNaN(indexValue))) {
6780        this.incrementCounters(node, FaultID.IndexNegative);
6781      }
6782    }
6783  }
6784
6785  private handleNoTuplesArrays(node: ts.Node, lhsType: ts.Type, rhsType: ts.Type): void {
6786    if (!this.options.arkts2) {
6787      return;
6788    }
6789    if (
6790      this.tsUtils.isOrDerivedFrom(lhsType, this.tsUtils.isArray) &&
6791        this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple) ||
6792      this.tsUtils.isOrDerivedFrom(rhsType, this.tsUtils.isArray) &&
6793        this.tsUtils.isOrDerivedFrom(lhsType, TsUtils.isTuple)
6794    ) {
6795      this.incrementCounters(node, FaultID.NoTuplesArrays);
6796    }
6797  }
6798
6799  private handleNoTuplesArraysForPropertyAccessExpression(node: ts.PropertyAccessExpression): void {
6800    if (!this.options.arkts2) {
6801      return;
6802    }
6803    const lhsType = this.tsTypeChecker.getTypeAtLocation(node.expression);
6804    if (this.tsUtils.isOrDerivedFrom(lhsType, TsUtils.isTuple)) {
6805      if (ARRAY_API_LIST.includes(node.name.text)) {
6806        this.incrementCounters(node, FaultID.NoTuplesArrays);
6807      }
6808    }
6809  }
6810
6811  private handleArrayTypeImmutable(node: ts.Node, lhsType: ts.Type, rhsType: ts.Type, rhsExpr?: ts.Expression): void {
6812    if (!this.options.arkts2) {
6813      return;
6814    }
6815    const isArray =
6816      this.tsUtils.isOrDerivedFrom(lhsType, this.tsUtils.isArray) &&
6817      this.tsUtils.isOrDerivedFrom(rhsType, this.tsUtils.isArray);
6818    const isTuple =
6819      this.tsUtils.isOrDerivedFrom(lhsType, TsUtils.isTuple) && this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple);
6820    if (!((isArray || isTuple) && lhsType !== rhsType)) {
6821      return;
6822    }
6823
6824    const rhsTypeStr = this.tsTypeChecker.typeToString(rhsType);
6825    let lhsTypeStr = this.tsTypeChecker.typeToString(lhsType);
6826    if (rhsExpr && (this.isNullOrEmptyArray(rhsExpr) || ts.isArrayLiteralExpression(rhsExpr))) {
6827      return;
6828    }
6829
6830    if (ts.isAsExpression(node) && ts.isArrayLiteralExpression(node.expression)) {
6831      node.expression.elements.forEach((elem) => {
6832        if (elem.kind === ts.SyntaxKind.FalseKeyword || elem.kind === ts.SyntaxKind.TrueKeyword) {
6833          lhsTypeStr = rhsTypeStr.replace(elem.getText(), 'boolean');
6834        }
6835      });
6836    }
6837    if (lhsTypeStr !== rhsTypeStr) {
6838      this.incrementCounters(node, FaultID.ArrayTypeImmutable);
6839    }
6840  }
6841
6842  private isNullOrEmptyArray(expr: ts.Expression): boolean {
6843    if (ts.isNewExpression(expr)) {
6844      const constructorSym = this.tsTypeChecker.getSymbolAtLocation(expr.expression);
6845      if (constructorSym?.name === 'Array') {
6846        if (!expr.arguments || expr.arguments.length === 0) {
6847          return true;
6848        }
6849        if (expr.arguments.length === 1) {
6850          const argType = this.tsTypeChecker.getTypeAtLocation(expr.arguments[0]);
6851          return !!(argType.flags & ts.TypeFlags.NumberLike);
6852        }
6853      }
6854    }
6855
6856    return false;
6857  }
6858
6859  private handleExponentOperation(node: ts.Node): void {
6860    if (!this.options.arkts2) {
6861      return;
6862    }
6863    const autofix = this.autofixer?.fixExponent(node.parent);
6864    this.incrementCounters(node, FaultID.ExponentOp, autofix);
6865  }
6866
6867  private handleNonNullExpression(node: ts.Node): void {
6868    if (!this.options.arkts2) {
6869      return;
6870    }
6871
6872    if (
6873      !ts.isNonNullExpression(node) ||
6874      !ts.isNonNullExpression(node.expression) ||
6875      ts.isNonNullExpression(node.parent) ||
6876      ts.isPropertyAccessExpression(node.parent) ||
6877      ts.isNonNullExpression(node.expression.expression)
6878    ) {
6879      return;
6880    }
6881
6882    const statement = ts.findAncestor(node, ts.isExpressionStatement);
6883    if (statement && this.isCustomComponent(statement)) {
6884      this.handleCustomBidirectionalBinding(node, node.expression);
6885    } else {
6886      const autofix = this.autofixer?.fixNativeBidirectionalBinding(node, this.interfacesNeedToImport);
6887      this.incrementCounters(node, FaultID.DoubleExclaBindingNotSupported, autofix);
6888    }
6889  }
6890
6891  private isCustomComponent(statement: ts.ExpressionStatement): boolean {
6892    const callExpr = statement.expression;
6893    if (!ts.isCallExpression(callExpr)) {
6894      return false;
6895    }
6896
6897    const identifier = callExpr.expression;
6898    if (!ts.isIdentifier(identifier)) {
6899      return false;
6900    }
6901
6902    const symbol = this.tsTypeChecker.getSymbolAtLocation(identifier);
6903    if (symbol) {
6904      const decl = this.tsUtils.getDeclarationNode(identifier);
6905      if (decl?.getSourceFile() === statement.getSourceFile()) {
6906        return true;
6907      }
6908    }
6909
6910    return this.interfacesAlreadyImported.has(callExpr.expression.getText());
6911  }
6912
6913  private handleCustomBidirectionalBinding(firstExpr: ts.NonNullExpression, secondExpr: ts.NonNullExpression): void {
6914    let currentParam: ts.Identifier | undefined;
6915    if (ts.isPropertyAccessExpression(secondExpr.expression)) {
6916      currentParam = secondExpr.expression.name as ts.Identifier;
6917    }
6918
6919    let customParam: ts.Identifier | undefined;
6920    if (ts.isPropertyAssignment(firstExpr.parent)) {
6921      customParam = firstExpr.parent.name as ts.Identifier;
6922    }
6923
6924    if (!currentParam || !customParam) {
6925      return;
6926    }
6927
6928    const originalExpr = firstExpr.parent.parent;
6929    if (!ts.isObjectLiteralExpression(originalExpr)) {
6930      return;
6931    }
6932
6933    const autofix = this.autofixer?.fixCustomBidirectionalBinding(originalExpr, currentParam, customParam);
6934    this.incrementCounters(firstExpr, FaultID.DoubleExclaBindingNotSupported, autofix);
6935  }
6936
6937  private handleDoubleDollar(node: ts.Node): void {
6938    if (!this.options.arkts2) {
6939      return;
6940    }
6941
6942    if (
6943      ts.isPropertyAccessExpression(node) &&
6944      ts.isIdentifier(node.expression) &&
6945      node.expression.escapedText === DOUBLE_DOLLAR_IDENTIFIER + THIS_IDENTIFIER
6946    ) {
6947      const autofix = this.autofixer?.fixDoubleDollar(node, this.interfacesNeedToImport);
6948      this.incrementCounters(node, FaultID.DoubleDollarBindingNotSupported, autofix);
6949    }
6950  }
6951
6952  private handleDollarBind(node: ts.Node): void {
6953    if (!this.options.arkts2) {
6954      return;
6955    }
6956
6957    if (!ts.isPropertyAssignment(node) || !ts.isIdentifier(node.initializer)) {
6958      return;
6959    }
6960
6961    const text = node.initializer.getText();
6962    if (!(/^\$.+$/).test(text)) {
6963      return;
6964    }
6965
6966    const autofix = this.autofixer?.fixDollarBind(node);
6967    this.incrementCounters(node, FaultID.DollarBindingNotSupported, autofix);
6968  }
6969
6970  private handleExtendDecorator(node: ts.Node): void {
6971    if (!this.options.arkts2) {
6972      return;
6973    }
6974
6975    if (!ts.isFunctionDeclaration(node.parent) || !ts.isDecorator(node)) {
6976      return;
6977    }
6978
6979    if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression)) {
6980      if (node.expression.expression.text === CustomDecoratorName.Extend) {
6981        const autofix = this.autofixer?.fixExtendDecorator(node, false, this.interfacesNeedToImport);
6982        this.incrementCounters(node.parent, FaultID.ExtendDecoratorNotSupported, autofix);
6983      } else if (node.expression.expression.text === CustomDecoratorName.AnimatableExtend) {
6984        const autofix = this.autofixer?.fixExtendDecorator(node, true, this.interfacesNeedToImport);
6985        this.incrementCounters(node.parent, FaultID.AnimatableExtendDecoratorTransform, autofix);
6986      }
6987    }
6988  }
6989
6990  private handleEntryDecorator(node: ts.Node): void {
6991    if (!this.options.arkts2) {
6992      return;
6993    }
6994
6995    if (!ts.isDecorator(node)) {
6996      return;
6997    }
6998
6999    if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression)) {
7000      if (node.expression.expression.escapedText !== ENTRY_DECORATOR_NAME || node.expression.arguments.length !== 1) {
7001        return;
7002      }
7003      const arg = node.expression.arguments[0];
7004      if (ts.isObjectLiteralExpression(arg)) {
7005        const properties = arg.properties;
7006        if (properties.length !== 1) {
7007          return;
7008        }
7009        if (!ts.isPropertyAssignment(properties[0])) {
7010          return;
7011        }
7012        const property = properties[0];
7013        if (ts.isStringLiteral(property.initializer)) {
7014          return;
7015        }
7016      }
7017      const autofix = this.autofixer?.fixEntryDecorator(node);
7018      this.incrementCounters(node, FaultID.EntryAnnotation, autofix);
7019    }
7020  }
7021
7022  private handleStructPropertyDecl(propDecl: ts.PropertyDeclaration): void {
7023    if (!this.options.arkts2) {
7024      return;
7025    }
7026    const isStatic = TsUtils.hasModifier(propDecl.modifiers, ts.SyntaxKind.StaticKeyword);
7027    const hasNoInitializer = !propDecl.initializer;
7028    const isOptional = !!propDecl.questionToken;
7029
7030    const defaultSkipTypeCheck = (typeNode: ts.TypeNode | undefined): boolean => {
7031      if (!typeNode) {
7032        return false;
7033      }
7034
7035      const typeText = typeNode.getText();
7036      if (ts.isLiteralTypeNode(typeNode) || ['boolean', 'number', 'null', 'undefined'].includes(typeText)) {
7037        return true;
7038      }
7039
7040      if (ts.isUnionTypeNode(typeNode)) {
7041        return typeNode.types.some((t) => {
7042          const tText = t.getText();
7043          return tText === 'undefined';
7044        });
7045      }
7046
7047      return false;
7048    };
7049
7050    const shouldSkipCheck = isOptional || defaultSkipTypeCheck(propDecl.type);
7051
7052    if (isStatic && hasNoInitializer && !shouldSkipCheck) {
7053      this.incrementCounters(propDecl, FaultID.ClassstaticInitialization);
7054    }
7055  }
7056
7057  private handleTaggedTemplatesExpression(node: ts.Node): void {
7058    if (!this.options.arkts2) {
7059      return;
7060    }
7061    this.incrementCounters(node, FaultID.TaggedTemplates);
7062  }
7063
7064  private checkFunctionTypeCompatible(lhsTypeNode: ts.TypeNode | undefined, rhsExpr: ts.Expression): void {
7065    if (this.options.arkts2 && lhsTypeNode && this.tsUtils.isIncompatibleFunctionals(lhsTypeNode, rhsExpr)) {
7066      this.incrementCounters(rhsExpr, FaultID.IncompationbleFunctionType);
7067    }
7068  }
7069
7070  private handleInvalidIdentifier(
7071    decl:
7072      | ts.TypeAliasDeclaration
7073      | ts.StructDeclaration
7074      | ts.VariableDeclaration
7075      | ts.FunctionDeclaration
7076      | ts.MethodSignature
7077      | ts.ClassDeclaration
7078      | ts.PropertyDeclaration
7079      | ts.MethodDeclaration
7080      | ts.ParameterDeclaration
7081      | ts.PropertySignature
7082      | ts.ImportDeclaration
7083      | ts.EnumDeclaration
7084      | ts.EnumMember
7085      | ts.ModuleDeclaration
7086      | ts.InterfaceDeclaration
7087  ): void {
7088    if (!this.options.arkts2) {
7089      return;
7090    }
7091
7092    const checkIdentifier = (identifier: ts.Identifier | undefined): void => {
7093      const text = identifier && ts.isIdentifier(identifier) ? identifier.text : '';
7094      if (identifier && text && INVALID_IDENTIFIER_KEYWORDS.includes(text)) {
7095        this.incrementCounters(identifier, FaultID.InvalidIdentifier);
7096      }
7097    };
7098
7099    if (ts.isImportDeclaration(decl)) {
7100      const importClause = decl.importClause;
7101      if (importClause?.namedBindings && ts.isNamedImports(importClause?.namedBindings)) {
7102        importClause.namedBindings.elements.forEach((importSpecifier) => {
7103          checkIdentifier(importSpecifier.name);
7104        });
7105      }
7106      checkIdentifier(importClause?.name);
7107    } else if (isStructDeclaration(decl)) {
7108      checkIdentifier((decl as ts.StructDeclaration).name);
7109    } else {
7110      checkIdentifier(decl.name as ts.Identifier);
7111    }
7112  }
7113
7114  private handleHeritageClause(node: ts.HeritageClause): void {
7115    this.checkEWTArgumentsForSdkDuplicateDeclName(node);
7116    if (!this.options.arkts2 || !this.useStatic) {
7117      return;
7118    }
7119    if (node.token === ts.SyntaxKind.ExtendsKeyword || node.token === ts.SyntaxKind.ImplementsKeyword) {
7120      node.types.forEach((type) => {
7121        const expr = type.expression;
7122        if (ts.isCallExpression(expr)) {
7123          this.incrementCounters(expr, FaultID.ExtendsExpression);
7124          return;
7125        }
7126        if (
7127          ts.isIdentifier(expr) &&
7128          this.isVariableReference(expr) &&
7129          this.tsUtils.isBuiltinClassHeritageClause(node)
7130        ) {
7131          this.incrementCounters(expr, FaultID.ExtendsExpression);
7132        } else if (ts.isIdentifier(expr)) {
7133          this.fixJsImportExtendsClass(node.parent, expr);
7134        }
7135      });
7136
7137      this.handleMissingSuperCallInExtendedClass(node);
7138    }
7139  }
7140
7141  private isVariableReference(identifier: ts.Identifier): boolean {
7142    const symbol = this.tsTypeChecker.getSymbolAtLocation(identifier);
7143    return !!symbol && (symbol.flags & ts.SymbolFlags.Variable) !== 0;
7144  }
7145
7146  private checkSendableAndConcurrentDecorator(decorator: ts.Decorator): void {
7147    if (!this.options.arkts2 || !this.useStatic) {
7148      return;
7149    }
7150    const decoratorName = TsUtils.getDecoratorName(decorator);
7151    const autofix = this.autofixer?.removeNode(decorator);
7152    if (decoratorName === SENDABLE_DECORATOR) {
7153      this.incrementCounters(decorator, FaultID.LimitedStdLibNoSendableDecorator, autofix);
7154    }
7155
7156    if (decoratorName === CONCURRENT_DECORATOR) {
7157      this.incrementCounters(decorator, FaultID.LimitedStdLibNoDoncurrentDecorator, autofix);
7158    }
7159  }
7160
7161  private checkAsonSymbol(node: ts.Identifier): void {
7162    if (!this.options.arkts2) {
7163      return;
7164    }
7165
7166    if (node.text !== ASON_TEXT) {
7167      return;
7168    }
7169
7170    const parent = node.parent;
7171    switch (parent.kind) {
7172      case ts.SyntaxKind.QualifiedName:
7173        if (!ts.isQualifiedName(parent)) {
7174          return;
7175        }
7176        if (parent.right.text !== node.text) {
7177          return;
7178        }
7179        if (ts.isQualifiedName(parent.parent) && ASON_WHITE_SET.has(parent.parent.right.text)) {
7180          this.checkAsonUsage(parent.left, true);
7181        } else {
7182          this.checkAsonUsage(parent.left, false);
7183        }
7184        break;
7185      case ts.SyntaxKind.PropertyAccessExpression:
7186        if (!ts.isPropertyAccessExpression(parent)) {
7187          return;
7188        }
7189        if (parent.name.text !== node.text) {
7190          return;
7191        }
7192        if (ts.isPropertyAccessExpression(parent.parent) && ASON_WHITE_SET.has(parent.parent.name.text)) {
7193          this.checkAsonUsage(parent.expression, true);
7194        } else {
7195          this.checkAsonUsage(parent.expression, false);
7196        }
7197        break;
7198      default:
7199    }
7200  }
7201
7202  private checkAsonUsage(nodeToCheck: ts.Node, needAutofix: boolean): void {
7203    if (!ts.isIdentifier(nodeToCheck)) {
7204      return;
7205    }
7206
7207    const declaration = this.tsUtils.getDeclarationNode(nodeToCheck);
7208    if (!declaration && nodeToCheck.text === ARKTS_UTILS_TEXT) {
7209      const autofix =
7210        needAutofix && this.autofixer ? this.autofixer.replaceNode(nodeToCheck.parent, JSON_TEXT) : undefined;
7211      this.incrementCounters(nodeToCheck, FaultID.LimitedStdLibNoASON, autofix);
7212      return;
7213    }
7214
7215    if (!declaration) {
7216      return;
7217    }
7218
7219    const sourceFile = declaration.getSourceFile();
7220    const fileName = path.basename(sourceFile.fileName);
7221
7222    if (
7223      ASON_MODULES.some((moduleName) => {
7224        return fileName.startsWith(moduleName);
7225      })
7226    ) {
7227      const autofix =
7228        needAutofix && this.autofixer ? this.autofixer.replaceNode(nodeToCheck.parent, JSON_TEXT) : undefined;
7229      this.incrementCounters(nodeToCheck, FaultID.LimitedStdLibNoASON, autofix);
7230    }
7231  }
7232
7233  private checkCollectionsSymbol(symbol: ts.Symbol, node: ts.Node): void {
7234    const cb = (): void => {
7235      const parent = node.parent;
7236      if (!parent) {
7237        return;
7238      }
7239      if (ts.isPropertyAccessExpression(parent)) {
7240        const autofix = this.autofixer?.replaceNode(parent, parent.name.text);
7241        this.incrementCounters(node, FaultID.NoNeedStdLibSendableContainer, autofix);
7242      }
7243
7244      if (ts.isQualifiedName(parent)) {
7245        const autofix = this.autofixer?.replaceNode(parent, parent.right.text);
7246        this.incrementCounters(node, FaultID.NoNeedStdLibSendableContainer, autofix);
7247      }
7248
7249      if (ts.isImportSpecifier(parent) && ts.isIdentifier(node)) {
7250        const autofix = this.autofixer?.removeImport(node, parent);
7251        this.incrementCounters(node, FaultID.NoNeedStdLibSendableContainer, autofix);
7252      }
7253    };
7254
7255    this.checkSymbolAndExecute(symbol, COLLECTIONS_TEXT, COLLECTIONS_MODULES, cb);
7256  }
7257
7258  private checkWorkerSymbol(symbol: ts.Symbol, node: ts.Node): void {
7259    const cb = (): void => {
7260      this.incrementCounters(node, FaultID.NoNeedStdlibWorker);
7261    };
7262
7263    this.checkSymbolAndExecute(symbol, WORKER_TEXT, WORKER_MODULES, cb);
7264  }
7265
7266  private checkConcurrencySymbol(symbol: ts.Symbol, node: ts.Node): void {
7267    const cb = (): void => {
7268      const parent = node.parent;
7269      if (!ts.isPropertyAccessExpression(parent)) {
7270        return;
7271      }
7272      if (parent.name.text === ARKTSUTILS_LOCKS_MEMBER) {
7273        this.incrementCounters(node, FaultID.LimitedStdLibNoImportConcurrency);
7274      }
7275    };
7276
7277    this.checkSymbolAndExecute(symbol, ARKTSUTILS_LOCKS_MEMBER, ARKTSUTILS_MODULES, cb);
7278  }
7279
7280  private checkSymbolAndExecute(symbol: ts.Symbol, symbolName: string, modules: string[], cb: () => void): void {
7281    void this;
7282    if (symbol.name === symbolName) {
7283      const decl = TsUtils.getDeclaration(symbol);
7284
7285      if (!decl) {
7286        return;
7287      }
7288
7289      const sourceFile = decl.getSourceFile();
7290      const fileName = path.basename(sourceFile.fileName);
7291
7292      if (
7293        modules.some((moduleName) => {
7294          return fileName.startsWith(moduleName);
7295        })
7296      ) {
7297        cb();
7298      }
7299    }
7300  }
7301
7302  interfacesNeedToAlarm: ts.Identifier[] = [];
7303  interfacesNeedToImport: Set<string> = new Set<string>();
7304  interfacesAlreadyImported: Set<string> = new Set<string>();
7305
7306  private handleInterfaceImport(identifier: ts.Identifier): void {
7307    if (!this.options.arkts2) {
7308      return;
7309    }
7310
7311    if (this.shouldSkipIdentifier(identifier)) {
7312      return;
7313    }
7314
7315    const name = identifier.getText();
7316    if (!this.interfacesNeedToImport.has(name)) {
7317      this.interfacesNeedToImport.add(name);
7318    }
7319
7320    this.interfacesNeedToAlarm.push(identifier);
7321  }
7322
7323  private shouldSkipIdentifier(identifier: ts.Identifier): boolean {
7324    const name = identifier.getText();
7325    if (!arkuiImportList.has(name)) {
7326      return true;
7327    }
7328
7329    if (skipImportDecoratorName.has(name)) {
7330      return true;
7331    }
7332
7333    const targetPropertyAccess = TypeScriptLinter.findTargetPropertyAccess(identifier.parent);
7334    if (targetPropertyAccess) {
7335      const expr = targetPropertyAccess.expression;
7336      if (this.isDeclarationInSameFile(expr)) {
7337        return true;
7338      }
7339    }
7340
7341    const parent = identifier.parent;
7342    const wrappedSkipComponents = new Set<string>([CustomDecoratorName.AnimatableExtend, CustomDecoratorName.Extend]);
7343    if (ts.isCallExpression(parent)) {
7344      const expr = parent.expression;
7345      if (wrappedSkipComponents.has(expr.getText()) && name !== CustomDecoratorName.AnimatableExtend) {
7346        return true;
7347      }
7348    }
7349
7350    if (this.isDeclarationInSameFile(identifier)) {
7351      return true;
7352    }
7353
7354    return this.interfacesAlreadyImported.has(name);
7355  }
7356
7357  private static findTargetPropertyAccess(node: ts.Node): ts.PropertyAccessExpression | undefined {
7358    while (ts.isPropertyAccessExpression(node)) {
7359      const expr = node.expression;
7360      if (!ts.isPropertyAccessExpression(expr)) {
7361        return node;
7362      }
7363      node = expr;
7364    }
7365    return undefined;
7366  }
7367
7368  private isDeclarationInSameFile(node: ts.Node): boolean {
7369    const symbol = this.tsTypeChecker.getSymbolAtLocation(node);
7370    const decl = TsUtils.getDeclaration(symbol);
7371    if (decl?.getSourceFile() === node.getSourceFile()) {
7372      return true;
7373    }
7374
7375    return false;
7376  }
7377
7378  private processInterfacesToImport(sourceFile: ts.SourceFile): void {
7379    if (!this.options.arkts2) {
7380      return;
7381    }
7382
7383    const autofix = this.autofixer?.fixInterfaceImport(
7384      this.interfacesNeedToImport,
7385      this.interfacesAlreadyImported,
7386      sourceFile
7387    );
7388
7389    this.interfacesNeedToAlarm.forEach((identifier) => {
7390      const name = identifier.getText();
7391      const errorMsg = `The ArkUI interface "${name}" should be imported before it is used (arkui-modular-interface)`;
7392      this.incrementCounters(identifier, FaultID.UIInterfaceImport, autofix, errorMsg);
7393    });
7394
7395    this.interfacesNeedToAlarm = [];
7396    this.interfacesNeedToImport.clear();
7397    this.interfacesAlreadyImported.clear();
7398  }
7399
7400  private extractImportedNames(sourceFile: ts.SourceFile): void {
7401    if (!this.options.arkts2) {
7402      return;
7403    }
7404    for (const statement of sourceFile.statements) {
7405      if (!ts.isImportDeclaration(statement)) {
7406        continue;
7407      }
7408
7409      const importClause = statement.importClause;
7410      if (!importClause) {
7411        continue;
7412      }
7413
7414      const namedBindings = importClause.namedBindings;
7415      if (!namedBindings || !ts.isNamedImports(namedBindings)) {
7416        continue;
7417      }
7418
7419      for (const specifier of namedBindings.elements) {
7420        const importedName = specifier.name.getText(sourceFile);
7421        this.interfacesAlreadyImported.add(importedName);
7422      }
7423    }
7424  }
7425
7426  private handleStylesDecorator(node: ts.Decorator): void {
7427    if (!this.options.arkts2) {
7428      return;
7429    }
7430
7431    if (!ts.isFunctionDeclaration(node.parent) && !ts.isMethodDeclaration(node.parent)) {
7432      return;
7433    }
7434
7435    if (!ts.isIdentifier(node.expression) || node.expression.text !== CustomDecoratorName.Styles) {
7436      return;
7437    }
7438
7439    const decl = node.parent;
7440    const declName = decl.name?.getText();
7441    if (ts.isFunctionDeclaration(decl)) {
7442      const functionCalls = TypeScriptLinter.findDeclarationCalls(this.sourceFile, declName as string);
7443      const autofix = this.autofixer?.fixStylesDecoratorGlobal(decl, functionCalls, this.interfacesNeedToImport);
7444      this.incrementCounters(decl, FaultID.StylesDecoratorNotSupported, autofix);
7445    }
7446
7447    if (ts.isMethodDeclaration(decl)) {
7448      const methodCalls = TypeScriptLinter.findDeclarationCalls(this.sourceFile, declName as string);
7449      const autofix = this.autofixer?.fixStylesDecoratorStruct(decl, methodCalls, this.interfacesNeedToImport);
7450      this.incrementCounters(decl, FaultID.StylesDecoratorNotSupported, autofix);
7451    }
7452  }
7453
7454  private handleStateStyles(node: ts.CallExpression | ts.PropertyAccessExpression): void {
7455    if (!this.options.arkts2) {
7456      return;
7457    }
7458
7459    let args: ts.Expression[] = [];
7460    let startNode: ts.Node | undefined;
7461    if (ts.isCallExpression(node)) {
7462      if (node.expression.getText() !== STATE_STYLES) {
7463        return;
7464      }
7465      startNode = node.expression;
7466      args = Array.from(node.arguments);
7467    }
7468
7469    if (ts.isPropertyAccessExpression(node)) {
7470      if (node.name.getText() !== STATE_STYLES) {
7471        return;
7472      }
7473      if (!ts.isCallExpression(node.parent)) {
7474        return;
7475      }
7476      startNode = node.name;
7477      args = Array.from(node.parent.arguments);
7478    }
7479
7480    if (args.length === 0 || !startNode) {
7481      return;
7482    }
7483
7484    const object = args[0];
7485    if (!object || !ts.isObjectLiteralExpression(object)) {
7486      return;
7487    }
7488
7489    const properties = object.properties;
7490    if (properties.length === 0) {
7491      return;
7492    }
7493
7494    if (!TypeScriptLinter.hasAnonBlock(properties)) {
7495      return;
7496    }
7497
7498    const autofix = this.autofixer?.fixStateStyles(object, startNode, this.interfacesNeedToImport);
7499    this.incrementCounters(object, FaultID.StateStylesBlockNeedArrowFunc, autofix);
7500  }
7501
7502  private static hasAnonBlock(properties: ts.NodeArray<ts.ObjectLiteralElementLike>): boolean {
7503    let anonBlockCount = 0;
7504
7505    properties.forEach((property) => {
7506      if (ts.isPropertyAssignment(property) && ts.isObjectLiteralExpression(property.initializer)) {
7507        anonBlockCount++;
7508      }
7509    });
7510
7511    return anonBlockCount !== 0;
7512  }
7513
7514  private handleStringLiteral(node: ts.StringLiteral): void {
7515    if (!this.options.arkts2) {
7516      return;
7517    }
7518
7519    this.checkForConcurrentExpressions(node);
7520  }
7521
7522  private checkForConcurrentExpressions(stringLiteral: ts.StringLiteral): void {
7523    if (!stringLiteral.parent) {
7524      return;
7525    }
7526
7527    if (!ts.isExpressionStatement(stringLiteral.parent)) {
7528      return;
7529    }
7530
7531    const text = stringLiteral.text;
7532    const autofix = this.autofixer?.removeNode(stringLiteral);
7533
7534    if (text === USE_CONCURRENT) {
7535      this.incrementCounters(stringLiteral, FaultID.UseConcurrentDeprecated, autofix);
7536    }
7537
7538    if (text === USE_SHARED) {
7539      this.incrementCounters(stringLiteral, FaultID.UseSharedDeprecated, autofix);
7540    }
7541  }
7542
7543  private static findDeclarationCalls(sourceFile: ts.SourceFile, declName: string): ts.Identifier[] {
7544    const functionCalls: ts.Identifier[] = [];
7545
7546    function traverse(node: ts.Node): void {
7547      const identifier = getIdentifierFromNode(node);
7548      if (identifier && identifier.getText() === declName) {
7549        functionCalls.push(identifier);
7550      }
7551
7552      ts.forEachChild(node, traverse);
7553    }
7554
7555    function getIdentifierFromNode(node: ts.Node): ts.Identifier | undefined {
7556      if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
7557        return node.expression;
7558      }
7559      if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name)) {
7560        if (node.expression.getText() === THIS_IDENTIFIER) {
7561          return undefined;
7562        }
7563        return node.name;
7564      }
7565      return undefined;
7566    }
7567
7568    traverse(sourceFile);
7569    return functionCalls;
7570  }
7571
7572  addObservedDecorator: Set<ts.ClassDeclaration> = new Set<ts.ClassDeclaration>();
7573
7574  private handleDataObservation(node: ts.PropertyDeclaration): void {
7575    if (!this.options.arkts2) {
7576      return;
7577    }
7578
7579    const decorators = ts.getDecorators(node);
7580    if (!decorators || decorators.length === 0) {
7581      return;
7582    }
7583    const decorator = decorators[0];
7584    let decoratorName = '';
7585    if (ts.isIdentifier(decorator.expression)) {
7586      decoratorName = decorator.expression.getText();
7587    } else if (ts.isCallExpression(decorator.expression)) {
7588      decoratorName = decorator.expression.expression.getText();
7589    }
7590    if (!observedDecoratorName.has(decoratorName)) {
7591      return;
7592    }
7593
7594    let firstClassDecls: ts.ClassDeclaration[] | undefined;
7595    const expr = node.initializer;
7596    if (expr && ts.isNewExpression(expr)) {
7597      firstClassDecls = this.addFromNewExpression(expr);
7598    }
7599
7600    let secondClassDecls: ts.ClassDeclaration[] | undefined;
7601    const type = node.type;
7602    if (type) {
7603      secondClassDecls = this.addFromTypeNode(type);
7604    }
7605
7606    const classDecls = (firstClassDecls || []).concat(secondClassDecls || []);
7607    if (classDecls.length === 0) {
7608      return;
7609    }
7610
7611    const filteredClassDecls = classDecls.filter((classDecl) => {
7612      if (this.addObservedDecorator.has(classDecl)) {
7613        return false;
7614      }
7615      this.addObservedDecorator.add(classDecl);
7616      return true;
7617    });
7618    if (filteredClassDecls.length !== 0) {
7619      this.interfacesNeedToImport.add(CustomDecoratorName.Observed);
7620    }
7621    const autofix = this.autofixer?.fixDataObservation(filteredClassDecls);
7622    this.incrementCounters(node, FaultID.DataObservation, autofix);
7623  }
7624
7625  private addFromNewExpression(expr: ts.NewExpression): ts.ClassDeclaration[] | undefined {
7626    const identifier = expr.expression;
7627    if (!ts.isIdentifier(identifier)) {
7628      return undefined;
7629    }
7630
7631    const decl: ts.ClassDeclaration | undefined = this.getClassDeclaration(identifier);
7632    if (!decl) {
7633      return undefined;
7634    }
7635
7636    const classDecls: ts.ClassDeclaration[] = this.getClassHierarchy(decl);
7637    const filteredClassDecls = classDecls.filter((classDecl) => {
7638      if (TypeScriptLinter.hasObservedDecorator(classDecl)) {
7639        return false;
7640      }
7641      return true;
7642    });
7643    return filteredClassDecls;
7644  }
7645
7646  private addFromTypeNode(type: ts.TypeNode): ts.ClassDeclaration[] | undefined {
7647    const targets: ts.Node[] = [];
7648    if (ts.isUnionTypeNode(type)) {
7649      const types = type.types;
7650      types.forEach((typeNode) => {
7651        if (ts.isTypeReferenceNode(typeNode)) {
7652          targets.push(typeNode.typeName);
7653        }
7654      });
7655    } else if (ts.isTypeReferenceNode(type)) {
7656      targets.push(type.typeName);
7657    }
7658
7659    const classDecls: ts.ClassDeclaration[] = [];
7660    targets.forEach((target) => {
7661      const decl: ts.ClassDeclaration | undefined = this.getClassDeclaration(target);
7662      if (!decl) {
7663        return;
7664      }
7665
7666      const decls: ts.ClassDeclaration[] = this.getClassHierarchy(decl);
7667      classDecls.push(...decls);
7668    });
7669    const filteredClassDecls = classDecls.filter((classDecl) => {
7670      if (TypeScriptLinter.hasObservedDecorator(classDecl)) {
7671        return false;
7672      }
7673      return true;
7674    });
7675    return filteredClassDecls;
7676  }
7677
7678  private static hasObservedDecorator(classDecl: ts.ClassDeclaration): boolean {
7679    return (
7680      ts.getDecorators(classDecl)?.some((decorator) => {
7681        return decorator.getText() === '@' + CustomDecoratorName.Observed;
7682      }) ?? false
7683    );
7684  }
7685
7686  private getClassDeclaration(node: ts.Node): ts.ClassDeclaration | undefined {
7687    const symbol = this.tsTypeChecker.getSymbolAtLocation(node);
7688    let decl: ts.Declaration | undefined;
7689    if (symbol) {
7690      decl = this.tsUtils.getDeclarationNode(node);
7691      if (decl?.getSourceFile() !== node.getSourceFile()) {
7692        return undefined;
7693      }
7694    }
7695
7696    if (!decl || !ts.isClassDeclaration(decl)) {
7697      return undefined;
7698    }
7699
7700    return decl;
7701  }
7702
7703  private getClassHierarchy(classDecl: ts.ClassDeclaration): ts.ClassDeclaration[] {
7704    const hierarchy: ts.ClassDeclaration[] = [];
7705    let currentClass: ts.ClassDeclaration | undefined = classDecl;
7706
7707    while (currentClass) {
7708      hierarchy.push(currentClass);
7709      const heritageClause = currentClass.heritageClauses?.find((clause) => {
7710        return clause.token === ts.SyntaxKind.ExtendsKeyword;
7711      });
7712      const identifier = heritageClause?.types[0]?.expression as ts.Identifier | undefined;
7713      if (!identifier) {
7714        break;
7715      }
7716      currentClass = this.getClassDeclaration(identifier);
7717    }
7718
7719    return hierarchy;
7720  }
7721
7722  private checkArkTSObjectInterop(tsCallExpr: ts.CallExpression): void {
7723    const callSignature = this.tsTypeChecker.getResolvedSignature(tsCallExpr);
7724    if (!callSignature?.declaration) {
7725      return;
7726    }
7727
7728    if (!this.isDeclaredInArkTs2(callSignature)) {
7729      return;
7730    }
7731
7732    if (!this.hasObjectParameter(callSignature, tsCallExpr)) {
7733      return;
7734    }
7735
7736    const functionSymbol = this.getFunctionSymbol(callSignature.declaration);
7737    const functionDeclaration = functionSymbol?.valueDeclaration;
7738    if (!functionDeclaration) {
7739      return;
7740    }
7741
7742    if (
7743      TypeScriptLinter.isFunctionLike(functionDeclaration) &&
7744      TypeScriptLinter.containsForbiddenAPI(functionDeclaration)
7745    ) {
7746      this.incrementCounters(tsCallExpr.parent, FaultID.InteropCallReflect);
7747    }
7748  }
7749
7750  private hasObjectParameter(callSignature: ts.Signature, tsCallExpr: ts.CallExpression): boolean {
7751    for (const [index, param] of callSignature.parameters.entries()) {
7752      const paramType = this.tsTypeChecker.getTypeOfSymbolAtLocation(param, tsCallExpr);
7753
7754      if (!this.tsUtils.isObject(paramType)) {
7755        continue;
7756      }
7757
7758      const argument = tsCallExpr.arguments[index];
7759      if (!argument) {
7760        continue;
7761      }
7762
7763      if (this.tsTypeChecker.getTypeAtLocation(argument).isClass()) {
7764        return true;
7765      }
7766    }
7767
7768    return false;
7769  }
7770
7771  private static containsForbiddenAPI(
7772    node: ts.FunctionDeclaration | ts.MethodDeclaration | ts.FunctionExpression
7773  ): ForbidenAPICheckResult {
7774    if (!node.body) {
7775      return NONE;
7776    }
7777    return TypeScriptLinter.isForbiddenUsed(node.body);
7778  }
7779
7780  private static isForbiddenUsed(currentNode: ts.Node): ForbidenAPICheckResult {
7781    if (!ts.isCallExpression(currentNode)) {
7782      let found: ForbidenAPICheckResult = NONE;
7783      ts.forEachChild(currentNode, (child) => {
7784        if (found === NONE) {
7785          found = TypeScriptLinter.isForbiddenUsed(child);
7786        }
7787      });
7788
7789      return found;
7790    }
7791
7792    const expr = currentNode.expression;
7793    if (!ts.isPropertyAccessExpression(expr)) {
7794      return NONE;
7795    }
7796
7797    const obj = expr.expression;
7798    const method = expr.name;
7799    if (!ts.isIdentifier(obj)) {
7800      return NONE;
7801    }
7802
7803    if (obj.text === REFLECT_LITERAL) {
7804      if (REFLECT_PROPERTIES.includes(method.text)) {
7805        return REFLECT_LITERAL;
7806      }
7807    }
7808
7809    if (obj.text === OBJECT_LITERAL) {
7810      if (OBJECT_PROPERTIES.includes(method.text)) {
7811        return OBJECT_LITERAL;
7812      }
7813    }
7814    return NONE;
7815  }
7816
7817  private getFunctionSymbol(declaration: ts.Declaration): ts.Symbol | undefined {
7818    if (TypeScriptLinter.isFunctionLike(declaration)) {
7819      return declaration.name ? this.tsTypeChecker.getSymbolAtLocation(declaration.name) : undefined;
7820    }
7821    return undefined;
7822  }
7823
7824  private static isFunctionLike(
7825    node: ts.Node
7826  ): node is ts.FunctionDeclaration | ts.MethodDeclaration | ts.FunctionExpression {
7827    return ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isFunctionExpression(node);
7828  }
7829
7830  private static isThirdPartyBySymbol(symbol: ts.Symbol | undefined, apiList: ApiListItem): boolean {
7831    if (!symbol) {
7832      return false;
7833    }
7834    const declaration = symbol.getDeclarations()?.[0];
7835    if (declaration && ts.isImportClause(declaration)) {
7836      const importDecl = declaration.parent;
7837      const importPath = TsUtils.removeOrReplaceQuotes(importDecl.moduleSpecifier.getText(), false);
7838      const import_path = TypeScriptLinter.getLocalApiListItemByKey(SdkNameInfo.ImportPath, apiList);
7839      if (import_path.includes(importPath)) {
7840        return true;
7841      }
7842    }
7843    return false;
7844  }
7845
7846  private static getLocalApiListItemByKey(key: string, apiList: ApiListItem): string | string[] {
7847    if (!apiList) {
7848      return '';
7849    }
7850    if (SdkNameInfo.ImportPath === key) {
7851      return apiList.import_path;
7852    }
7853    return '';
7854  }
7855
7856  private handleSdkForConstructorFuncs(node: ts.PropertyAccessExpression | ts.QualifiedName): void {
7857    if (!this.options.arkts2) {
7858      return;
7859    }
7860    const rightNode = ts.isPropertyAccessExpression(node) ? node.name : node.right;
7861    const leftNode = ts.isPropertyAccessExpression(node) ? node.expression : node.left;
7862    const constructorFuncsInfos = Array.from(TypeScriptLinter.constructorFuncsSet);
7863    constructorFuncsInfos.some((constructorFuncsInfo) => {
7864      const api_name = constructorFuncsInfo.api_info.api_name;
7865      if (api_name !== rightNode.getText()) {
7866        return;
7867      }
7868      const parentSym = this.tsTypeChecker.getSymbolAtLocation(leftNode);
7869      if (TypeScriptLinter.isThirdPartyBySymbol(parentSym, constructorFuncsInfo)) {
7870        this.incrementCounters(rightNode, FaultID.ConstructorTypesDeprecated);
7871      }
7872    });
7873  }
7874
7875  private handleQuotedHyphenPropsDeprecated(node: ts.PropertyAccessExpression | ts.PropertyAssignment): void {
7876    if (!this.options.arkts2 || !node) {
7877      return;
7878    }
7879    const literalAsPropertyNameInfos = Array.from(TypeScriptLinter.literalAsPropertyNameTypeSet);
7880    literalAsPropertyNameInfos.some((literalAsPropertyNameInfo) => {
7881      this.localApiListItem = literalAsPropertyNameInfo;
7882      const api_name = literalAsPropertyNameInfo.api_info.api_name;
7883      if (api_name !== (ts.isPropertyAccessExpression(node) ? node.name.text : node.name.getText())) {
7884        return false;
7885      }
7886      const parentSym = this.getFinalSymOnQuotedHyphenPropsDeprecated(
7887        ts.isPropertyAccessExpression(node) ? node.expression : node
7888      );
7889      if (parentSym && this.shouldWarn(parentSym)) {
7890        this.incrementCounters(node, FaultID.QuotedHyphenPropsDeprecated);
7891        return true;
7892      }
7893      return false;
7894    });
7895  }
7896
7897  private shouldWarn(symbol: ts.Symbol): boolean {
7898    const parentApiName = this.getLocalApiListItemByKey(SdkNameInfo.ParentApiName);
7899    return symbol && this.isHeritageClauseisThirdPartyBySymbol(symbol) || symbol.name === parentApiName;
7900  }
7901
7902  private getFinalSymOnQuotedHyphenPropsDeprecated(node: ts.Node): ts.Symbol | undefined {
7903    let currentNode = node;
7904    while (currentNode) {
7905      const symbol = this.checkNodeTypeOnQuotedHyphenPropsDeprecated(currentNode);
7906      if (symbol) {
7907        return symbol;
7908      }
7909      currentNode = currentNode.parent;
7910    }
7911    return undefined;
7912  }
7913
7914  private checkNodeTypeOnQuotedHyphenPropsDeprecated(node: ts.Node): ts.Symbol | undefined {
7915    if (ts.isVariableDeclaration(node)) {
7916      return this.getTypeOfVariable(node);
7917    }
7918
7919    if (ts.isPropertySignature(node)) {
7920      return this.tsTypeChecker.getSymbolAtLocation(node);
7921    }
7922
7923    const nodesWithResolvableType = [
7924      ts.isFunctionDeclaration(node) && node.type,
7925      ts.isMethodDeclaration(node) && node.type,
7926      ts.isTypeReferenceNode(node) && node,
7927      ts.isParameter(node) && node.type
7928    ].filter(Boolean);
7929
7930    for (const typeNode of nodesWithResolvableType) {
7931      return typeNode ? this.resolveTypeNodeSymbol(typeNode) : undefined;
7932    }
7933
7934    if (ts.isIdentifier(node)) {
7935      const symbol = this.tsTypeChecker.getSymbolAtLocation(node);
7936      const declaration = symbol?.getDeclarations()?.[0];
7937      if (declaration) {
7938        return this.getFinalSymOnQuotedHyphenPropsDeprecated(declaration);
7939      }
7940    }
7941
7942    return undefined;
7943  }
7944
7945  private getTypeOfVariable(variable: ts.VariableDeclaration): ts.Symbol | undefined {
7946    if (variable.type) {
7947      return ts.isArrayTypeNode(variable.type) ?
7948        this.resolveTypeNodeSymbol(variable.type.elementType) :
7949        this.resolveTypeNodeSymbol(variable.type);
7950    }
7951    return variable.initializer ? this.tsTypeChecker.getTypeAtLocation(variable.initializer).getSymbol() : undefined;
7952  }
7953
7954  private resolveTypeNodeSymbol(typeNode: ts.TypeNode): ts.Symbol | undefined {
7955    if (!ts.isTypeReferenceNode(typeNode)) {
7956      return undefined;
7957    }
7958    return this.resolveTypeNoSymbol(typeNode);
7959  }
7960
7961  private resolveTypeNoSymbol(typeNode: ts.TypeReferenceNode): ts.Symbol | undefined {
7962    if (!typeNode.typeName) {
7963      return undefined;
7964    }
7965
7966    if (ts.isQualifiedName(typeNode.typeName)) {
7967      return this.tsTypeChecker.getSymbolAtLocation(typeNode.typeName.right);
7968    }
7969
7970    const symbol = this.tsUtils.trueSymbolAtLocation(typeNode.typeName);
7971    if (symbol?.declarations && symbol.declarations.length > 0) {
7972      const globalDeclaration = symbol.declarations[0];
7973      if (ts.isTypeAliasDeclaration(globalDeclaration)) {
7974        return this.resolveTypeNodeSymbol(globalDeclaration.type);
7975      } else if (ts.isInterfaceDeclaration(globalDeclaration)) {
7976        return this.processQuotedHyphenPropsDeprecatedOnInterfaceDeclaration(globalDeclaration);
7977      }
7978    }
7979    return this.tsTypeChecker.getTypeAtLocation(typeNode).getSymbol();
7980  }
7981
7982  private isHeritageClauseisThirdPartyBySymbol(symbol: ts.Symbol): boolean {
7983    const declarations = symbol.getDeclarations();
7984    if (declarations && declarations.length > 0) {
7985      const firstDeclaration = declarations[0];
7986      if (ts.isImportSpecifier(firstDeclaration)) {
7987        const importDecl = firstDeclaration.parent.parent.parent;
7988        const importPath = importDecl.moduleSpecifier.getText();
7989        const import_path = this.getLocalApiListItemByKey(SdkNameInfo.ImportPath);
7990        if (import_path && JSON.stringify(import_path).includes(importPath)) {
7991          return true;
7992        }
7993      }
7994    }
7995    return false;
7996  }
7997
7998  private getLocalApiListItemByKey(key: string): string | string[] {
7999    if (!this.localApiListItem) {
8000      return '';
8001    }
8002    if (SdkNameInfo.ParentApiName === key) {
8003      return this.localApiListItem.api_info.parent_api[0].api_name;
8004    } else if (SdkNameInfo.ImportPath === key) {
8005      return this.localApiListItem.import_path;
8006    }
8007    return '';
8008  }
8009
8010  private processQuotedHyphenPropsDeprecatedOnInterfaceDeclaration(
8011    node: ts.InterfaceDeclaration
8012  ): ts.Symbol | undefined {
8013    const heritageSymbol = this.processHeritageClauses(node);
8014    if (heritageSymbol) {
8015      return heritageSymbol;
8016    }
8017    return this.processMembers(node);
8018  }
8019
8020  private processHeritageClauses(node: ts.InterfaceDeclaration): ts.Symbol | undefined {
8021    if (!node.heritageClauses) {
8022      return undefined;
8023    }
8024    for (const heritageClause of node.heritageClauses) {
8025      return this.processHeritageClause(heritageClause);
8026    }
8027
8028    return undefined;
8029  }
8030
8031  private processHeritageClause(heritageClause: ts.HeritageClause): ts.Symbol | undefined {
8032    for (const type of heritageClause.types) {
8033      if (!type.expression) {
8034        return undefined;
8035      }
8036      if (ts.isPropertyAccessExpression(type.expression)) {
8037        return this.processPropertyAccessExpression(type.expression);
8038      }
8039    }
8040    return undefined;
8041  }
8042
8043  private processPropertyAccessExpression(expression: ts.PropertyAccessExpression): ts.Symbol | undefined {
8044    const heritageSymbol = this.tsTypeChecker.getSymbolAtLocation(expression.expression);
8045    if (heritageSymbol && expression.name.text === this.getLocalApiListItemByKey(SdkNameInfo.ParentApiName)) {
8046      return heritageSymbol;
8047    }
8048    return undefined;
8049  }
8050
8051  private processMembers(node: ts.InterfaceDeclaration): ts.Symbol | undefined {
8052    if (!node.members) {
8053      return undefined;
8054    }
8055    for (const member of node.members) {
8056      if (ts.isPropertySignature(member) && member.type) {
8057        return this.resolveTypeNodeSymbol(member.type);
8058      }
8059    }
8060    return undefined;
8061  }
8062
8063  private processApiNodeSdkGlobalApi(apiName: string, errorNode: ts.Node): void {
8064    for (const [key, value] of globalApiAssociatedInfo) {
8065      this.doProcessApiNodeSdkGlobalApi(apiName, errorNode, key, value);
8066    }
8067  }
8068
8069  private doProcessApiNodeSdkGlobalApi(apiName: string, errorNode: ts.Node, key: string, faultId: number): void {
8070    const setApiListItem = TypeScriptLinter.globalApiInfo.get(key);
8071    if (!setApiListItem) {
8072      return;
8073    }
8074    const apiNamesArr = [...setApiListItem];
8075    const hasSameApiName = apiNamesArr.some((apilistItem) => {
8076      return apilistItem.api_info.api_name === errorNode.getText();
8077    });
8078    if (!hasSameApiName) {
8079      return;
8080    }
8081    if (ts.isTypeReferenceNode(errorNode)) {
8082      errorNode = errorNode.typeName;
8083    }
8084    const matchedApi = apiNamesArr.some((sdkInfo) => {
8085      const isSameName = sdkInfo.api_info.api_name === apiName;
8086      const isGlobal = sdkInfo.is_global;
8087      return isSameName && isGlobal;
8088    });
8089    const checkSymbol = this.isIdentifierFromSDK(errorNode);
8090    const type = this.tsTypeChecker.getTypeAtLocation(errorNode);
8091    const typeName = this.tsTypeChecker.typeToString(type);
8092
8093    if (checkSymbol) {
8094      if (arkTsBuiltInTypeName.has(typeName)) {
8095        return;
8096      }
8097      if (matchedApi) {
8098        this.incrementCounters(errorNode, faultId);
8099      }
8100    }
8101  }
8102
8103  private isIdentifierFromSDK(node: ts.Node): boolean {
8104    const symbol = this.tsTypeChecker.getSymbolAtLocation(node);
8105    if (!symbol) {
8106      return true;
8107    }
8108
8109    // Check if the symbol is from an SDK import
8110    const declarations = symbol.getDeclarations();
8111    if (!declarations || declarations.length === 0) {
8112      return true;
8113    }
8114
8115    let isLocal = false;
8116    for (const declaration of declarations) {
8117      if (ts.isVariableDeclaration(declaration) ||
8118          ts.isTypeAliasDeclaration(declaration) ||
8119          ts.isClassDeclaration(declaration) ||
8120          ts.isInterfaceDeclaration(declaration) ||
8121          ts.isFunctionDeclaration(declaration) ||
8122          ts.isEnumDeclaration(declaration)) {
8123        isLocal = true;
8124        break
8125      }
8126    }
8127
8128    if(isLocal) {
8129      return false;
8130    }
8131
8132    return true;
8133  }
8134
8135  private handleSdkGlobalApi(
8136    node:
8137      | ts.TypeReferenceNode
8138      | ts.NewExpression
8139      | ts.VariableDeclaration
8140      | ts.PropertyDeclaration
8141      | ts.ParameterDeclaration
8142      | ts.CallExpression
8143      | ts.BinaryExpression
8144      | ts.ExpressionWithTypeArguments
8145      | ts.Identifier
8146      | ts.MethodDeclaration
8147  ): void {
8148    if (!this.options.arkts2) {
8149      return;
8150    }
8151    switch (node.kind) {
8152      case ts.SyntaxKind.TypeReference:
8153        this.checkTypeReferenceForSdkGlobalApi(node);
8154        break;
8155      case ts.SyntaxKind.NewExpression:
8156        this.checkNewExpressionForSdkGlobalApi(node);
8157        break;
8158      case ts.SyntaxKind.Identifier:
8159        this.checkHeritageClauseForSdkGlobalApi(node);
8160        break;
8161      case ts.SyntaxKind.VariableDeclaration:
8162      case ts.SyntaxKind.PropertyDeclaration:
8163      case ts.SyntaxKind.Parameter:
8164        this.checkDeclarationForSdkGlobalApi(node);
8165        break;
8166      case ts.SyntaxKind.CallExpression:
8167        this.checkCallExpressionForSdkGlobalApi(node);
8168        break;
8169      case ts.SyntaxKind.BinaryExpression:
8170        this.checkBinaryExpressionForSdkGlobalApi(node);
8171        break;
8172      case ts.SyntaxKind.MethodDeclaration:
8173        this.checkMethodDeclarationForSdkGlobalApi(node);
8174        break;
8175      default:
8176    }
8177  }
8178
8179  private checkTypeReferenceForSdkGlobalApi(node: ts.TypeReferenceNode): void {
8180    const typeName = node.typeName;
8181    if (ts.isIdentifier(typeName)) {
8182      this.processApiNodeSdkGlobalApi(typeName.text, node);
8183    }
8184  }
8185
8186  private checkNewExpressionForSdkGlobalApi(node: ts.NewExpression): void {
8187    const expression = node.expression;
8188    if (ts.isIdentifier(expression)) {
8189      this.processApiNodeSdkGlobalApi(expression.text, expression);
8190    }
8191  }
8192
8193  private checkHeritageClauseForSdkGlobalApi(node: ts.Identifier): void {
8194    if (ts.isIdentifier(node)) {
8195      this.processApiNodeSdkGlobalApi(node.text, node);
8196    }
8197  }
8198
8199  private checkDeclarationForSdkGlobalApi(
8200    node: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
8201  ): void {
8202    const expression = node.initializer;
8203    if (expression && ts.isIdentifier(expression)) {
8204      this.processApiNodeSdkGlobalApi(expression.text, expression);
8205    }
8206  }
8207
8208  private checkCallExpressionForSdkGlobalApi(node: ts.CallExpression): void {
8209    if (ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression)) {
8210      const expression = node.expression.expression;
8211
8212      this.processApiNodeSdkGlobalApi(expression.text, expression);
8213    }
8214  }
8215
8216  private checkBinaryExpressionForSdkGlobalApi(node: ts.BinaryExpression): void {
8217    const expression = node.right;
8218    if (ts.isIdentifier(expression)) {
8219      this.processApiNodeSdkGlobalApi(expression.text, expression);
8220    }
8221  }
8222
8223  private checkMethodDeclarationForSdkGlobalApi(node: ts.MethodDeclaration): void {
8224    const expression = node.name;
8225    if (ts.isIdentifier(expression)) {
8226      this.processApiNodeSdkGlobalApi(expression.text, expression);
8227    }
8228  }
8229
8230  private checkEWTArgumentsForSdkDuplicateDeclName(node: ts.HeritageClause): void {
8231    if (!this.options.arkts2) {
8232      return;
8233    }
8234    if (node.token === ts.SyntaxKind.ExtendsKeyword || node.token === ts.SyntaxKind.ImplementsKeyword) {
8235      node.types.forEach((type) => {
8236        this.handleSharedArrayBuffer(type);
8237        const expr = type.expression;
8238        if (ts.isIdentifier(expr)) {
8239          this.processApiNodeSdkGlobalApi(expr.text, expr);
8240        }
8241      });
8242    }
8243  }
8244
8245  private getOriginalSymbol(node: ts.Node): ts.Symbol | undefined {
8246    if (ts.isIdentifier(node)) {
8247      const variableDeclaration = this.findVariableDeclaration(node);
8248      if (variableDeclaration?.initializer) {
8249        return this.getOriginalSymbol(variableDeclaration.initializer);
8250      }
8251    } else if (ts.isNewExpression(node)) {
8252      const constructor = node.expression;
8253      if (ts.isIdentifier(constructor)) {
8254        return this.tsUtils.trueSymbolAtLocation(constructor);
8255      }
8256    } else if (ts.isCallExpression(node)) {
8257      const callee = node.expression;
8258      if (ts.isIdentifier(callee)) {
8259        return this.tsUtils.trueSymbolAtLocation(callee);
8260      } else if (ts.isPropertyAccessExpression(callee)) {
8261        return this.getOriginalSymbol(callee.expression);
8262      }
8263    } else if (ts.isPropertyAccessExpression(node)) {
8264      return this.getOriginalSymbol(node.expression);
8265    }
8266    return this.tsUtils.trueSymbolAtLocation(node);
8267  }
8268
8269  private static isFromJsImport(symbol: ts.Symbol): boolean {
8270    const declaration = symbol.declarations?.[0];
8271    if (declaration) {
8272      const sourceFile = declaration.getSourceFile();
8273      return sourceFile.fileName.endsWith(EXTNAME_JS);
8274    }
8275    return false;
8276  }
8277
8278  private hasLocalAssignment(node: ts.Node): boolean {
8279    if (ts.isIdentifier(node)) {
8280      const variableDeclaration = this.findVariableDeclaration(node);
8281      return !!variableDeclaration?.initializer;
8282    }
8283    return false;
8284  }
8285
8286  private isLocalCall(node: ts.Node): boolean {
8287    if (ts.isCallExpression(node)) {
8288      const callee = node.expression;
8289      if (ts.isIdentifier(callee)) {
8290        return this.hasLocalAssignment(callee);
8291      } else if (ts.isPropertyAccessExpression(callee)) {
8292        const objectNode = callee.expression;
8293        return this.hasLocalAssignment(objectNode);
8294      }
8295    }
8296    return false;
8297  }
8298
8299  private handleInterOpImportJsOnTypeOfNode(typeofExpress: ts.TypeOfExpression): void {
8300    if (!this.options.arkts2 || !typeofExpress || !this.useStatic) {
8301      return;
8302    }
8303    const targetNode = typeofExpress.expression;
8304    if (this.hasLocalAssignment(targetNode) || this.isLocalCall(targetNode)) {
8305      return;
8306    }
8307    const targetSymbol = this.getOriginalSymbol(targetNode);
8308    if (targetSymbol && TypeScriptLinter.isFromJsImport(targetSymbol)) {
8309      const autofix = this.autofixer?.fixInterOpImportJsOnTypeOf(typeofExpress);
8310      this.incrementCounters(typeofExpress, FaultID.InterOpImportJsForTypeOf, autofix);
8311    }
8312  }
8313
8314  private handleSdkTypeQuery(decl: ts.PropertyAccessExpression): void {
8315    if (!this.options.arkts2 || !ts.isPropertyAccessExpression(decl)) {
8316      return;
8317    }
8318
8319    if (this.handleSelfPropertyAccess(decl)) {
8320      return;
8321    }
8322
8323    if (ts.isPropertyAccessExpression(decl)) {
8324      const deprecatedProperties = [
8325        'position',
8326        'subtype',
8327        'movingPhotoEffectMode',
8328        'dynamicRangeType',
8329        'thumbnailVisible'
8330      ];
8331
8332      const propertyName = ts.isIdentifier(decl.name) ? decl.name.text : '';
8333      if (deprecatedProperties.includes(propertyName)) {
8334        this.incrementCounters(decl.name, FaultID.SdkTypeQuery);
8335        return;
8336      }
8337    }
8338
8339    this.handleImportApiPropertyAccess(decl);
8340  }
8341
8342  private handleSelfPropertyAccess(decl: ts.PropertyAccessExpression): boolean {
8343    if (!ts.isPropertyAccessExpression(decl.expression)) {
8344      return false;
8345    }
8346
8347    const propertyName = ts.isIdentifier(decl.expression.name) && decl.expression.name.text || '';
8348    if (propertyName !== 'self') {
8349      return false;
8350    }
8351
8352    this.incrementCounters(decl.name, FaultID.SdkTypeQuery);
8353    return true;
8354  }
8355
8356  private handleImportApiPropertyAccess(decl: ts.PropertyAccessExpression): void {
8357    if (!ts.isPropertyAccessExpression(decl.expression)) {
8358      return;
8359    }
8360
8361    const importApiName = ts.isIdentifier(decl.expression.expression) && decl.expression.expression.text || '';
8362    const sdkInfos = importApiName && this.interfaceMap.get(importApiName);
8363    if (!sdkInfos) {
8364      return;
8365    }
8366
8367    const apiName = ts.isIdentifier(decl.name) && decl.name.text || '';
8368    const matchedApi = [...sdkInfos].find((sdkInfo) => {
8369      return sdkInfo.api_name === apiName;
8370    });
8371
8372    if (matchedApi) {
8373      this.incrementCounters(decl.name, FaultID.SdkTypeQuery);
8374    }
8375  }
8376
8377  /**
8378   * Returns true if the method’s declared return type or body returns Promise<void>.
8379   */
8380  private hasPromiseVoidReturn(method: ts.MethodDeclaration): boolean {
8381    return (
8382      this.hasAnnotatedPromiseVoidReturn(method) || this.isAsyncMethod(method) || this.hasBodyPromiseReturn(method)
8383    );
8384  }
8385
8386  /**
8387   * Checks if the method’s declared return type annotation includes Promise<void>.
8388   */
8389  private hasAnnotatedPromiseVoidReturn(method: ts.MethodDeclaration): boolean {
8390    void this;
8391    if (!method.type) {
8392      return false;
8393    }
8394    const t = method.type;
8395    // Union type check
8396    if (ts.isUnionTypeNode(t)) {
8397      return t.types.some((u) => {
8398        return this.isSinglePromiseVoid(u);
8399      });
8400    }
8401    // Single Promise<void> check
8402    return this.isSinglePromiseVoid(t);
8403  }
8404
8405  private isSinglePromiseVoid(n: ts.Node): boolean {
8406    void this;
8407    return ts.isTypeReferenceNode(n) && n.typeName.getText() === PROMISE && n.typeArguments?.[0]?.getText() === VOID;
8408  }
8409
8410  /**
8411   * Checks if the method is declared async (implying Promise return).
8412   */
8413  private isAsyncMethod(method: ts.MethodDeclaration): boolean {
8414    void this;
8415    return (
8416      method.modifiers?.some((m) => {
8417        return m.kind === ts.SyntaxKind.AsyncKeyword;
8418      }) ?? false
8419    );
8420  }
8421
8422  /**
8423   * Scans the method body iteratively for any Promise-returning statements.
8424   */
8425  private hasBodyPromiseReturn(method: ts.MethodDeclaration): boolean {
8426    if (!method.body) {
8427      return false;
8428    }
8429
8430    let found = false;
8431    const visit = (node: ts.Node): void => {
8432      if (ts.isReturnStatement(node) && node.expression) {
8433        const retType = this.tsTypeChecker.getTypeAtLocation(node.expression);
8434        if (retType.symbol?.getName() === PROMISE) {
8435          found = true;
8436          return;
8437        }
8438      }
8439      ts.forEachChild(node, visit);
8440    };
8441    ts.forEachChild(method.body, visit);
8442
8443    return found;
8444  }
8445
8446  /**
8447   * Returns true if this method name is onDestroy/onDisconnect and class extends one of the supported Ability subclasses.
8448   */
8449  private isLifecycleMethodOnAbilitySubclass(method: ts.MethodDeclaration): boolean {
8450    const name = method.name.getText();
8451    if (name !== ON_DESTROY && name !== ON_DISCONNECT) {
8452      return false;
8453    }
8454    const cls = method.parent;
8455    if (!ts.isClassDeclaration(cls) || !cls.heritageClauses) {
8456      return false;
8457    }
8458    return cls.heritageClauses.some((h) => {
8459      return (
8460        h.token === ts.SyntaxKind.ExtendsKeyword &&
8461        h.types.some((tn) => {
8462          return this.isSupportedAbilityBase(method.name.getText(), tn.expression);
8463        })
8464      );
8465    });
8466  }
8467
8468  /**
8469   * Checks that the base class name and its import source or declaration file are supported,
8470   * and matches the lifecycle method (onDestroy vs onDisconnect).
8471   */
8472  private isSupportedAbilityBase(methodName: string, baseExprNode: ts.Expression): boolean {
8473    const sym = this.tsTypeChecker.getSymbolAtLocation(baseExprNode);
8474    if (!sym) {
8475      return false;
8476    }
8477
8478    const baseName = sym.getName();
8479    if (!ASYNC_LIFECYCLE_SDK_LIST.has(baseName)) {
8480      return false;
8481    }
8482
8483    if (methodName === ON_DISCONNECT && baseName !== SERVICE_EXTENSION_ABILITY) {
8484      return false;
8485    }
8486    if (methodName === ON_DESTROY && baseName === SERVICE_EXTENSION_ABILITY) {
8487      return false;
8488    }
8489
8490    const decl = sym.getDeclarations()?.[0];
8491    if (!decl || !ts.isImportSpecifier(decl)) {
8492      return false;
8493    }
8494
8495    const importDecl = decl.parent.parent.parent;
8496    const moduleName = (importDecl.moduleSpecifier as ts.StringLiteral).text;
8497    const srcFile = decl.getSourceFile().fileName;
8498
8499    return moduleName === ABILITY_KIT || srcFile.endsWith(`${baseName}.${EXTNAME_D_TS}`);
8500  }
8501
8502  /**
8503   * Rule sdk-void-lifecycle-return:
8504   * Flags onDestroy/onDisconnect methods in Ability subclasses
8505   * whose return type includes Promise<void>.
8506   */
8507  private checkVoidLifecycleReturn(method: ts.MethodDeclaration): void {
8508    if (!this.options.arkts2) {
8509      return;
8510    }
8511
8512    if (!this.isLifecycleMethodOnAbilitySubclass(method)) {
8513      return;
8514    }
8515
8516    if (!this.hasPromiseVoidReturn(method)) {
8517      return;
8518    }
8519
8520    this.incrementCounters(method.name, FaultID.SdkAbilityAsynchronousLifecycle);
8521  }
8522
8523  private handleGetOwnPropertyNames(decl: ts.PropertyAccessExpression): void {
8524    if (this.checkPropertyAccessExpression(decl, GET_OWN_PROPERTY_NAMES_TEXT, TypeScriptLinter.missingAttributeSet)) {
8525      const autofix = this.autofixer?.fixMissingAttribute(decl);
8526      this.incrementCounters(decl, FaultID.BuiltinGetOwnPropertyNames, autofix);
8527    }
8528  }
8529
8530  private handleSymbolIterator(decl: ts.PropertyAccessExpression): void {
8531    if (this.checkPropertyAccessExpression(decl, SYMBOL_ITERATOR, TypeScriptLinter.symbotIterSet)) {
8532      this.incrementCounters(decl, FaultID.BuiltinSymbolIterator);
8533    }
8534  }
8535
8536  private checkPropertyAccessExpression(decl: ts.PropertyAccessExpression, name: string, set: Set<string>): boolean {
8537    if (set.size === 0 || decl.getText() !== name) {
8538      return false;
8539    }
8540
8541    const symbol = this.tsUtils.trueSymbolAtLocation(decl);
8542    const sourceFile = symbol?.declarations?.[0]?.getSourceFile();
8543    if (!sourceFile) {
8544      return false;
8545    }
8546
8547    const fileName = path.basename(sourceFile.fileName);
8548    return set.has(fileName);
8549  }
8550
8551  private fixJsImportCallExpression(callExpr: ts.CallExpression): void {
8552    if (
8553      !this.options.arkts2 ||
8554      !this.useStatic ||
8555      ts.isAwaitExpression(callExpr.parent) ||
8556      ts.isTypeOfExpression(callExpr.parent)
8557    ) {
8558      return;
8559    }
8560
8561    const identifier = this.tsUtils.findIdentifierInExpression(callExpr);
8562    if (!identifier) {
8563      return;
8564    }
8565
8566    if (!this.tsUtils.isImportedFromJS(identifier)) {
8567      return;
8568    }
8569
8570    callExpr.arguments.forEach((arg) => {
8571      const type = this.tsTypeChecker.getTypeAtLocation(arg);
8572      if (ts.isArrowFunction(arg)) {
8573        this.incrementCounters(arg, FaultID.InteropJsObjectCallStaticFunc);
8574      } else if (ts.isIdentifier(arg)) {
8575        const sym = this.tsTypeChecker.getSymbolAtLocation(arg);
8576        const decl = sym?.declarations?.[0];
8577        if (
8578          decl &&
8579          (ts.isFunctionDeclaration(decl) ||
8580            ts.isVariableDeclaration(decl) && decl.initializer && ts.isArrowFunction(decl.initializer))
8581        ) {
8582          this.incrementCounters(arg, FaultID.InteropJsObjectCallStaticFunc);
8583        }
8584        if (type?.isClassOrInterface()) {
8585          this.incrementCounters(arg, FaultID.InteropJsObjectExpandStaticInstance);
8586        }
8587      } else if (ts.isObjectLiteralExpression(arg) || type?.isClassOrInterface()) {
8588        this.incrementCounters(arg, FaultID.InteropJsObjectExpandStaticInstance);
8589      }
8590    });
8591  }
8592
8593  private fixJsImportExtendsClass(
8594    node: ts.ClassLikeDeclaration | ts.InterfaceDeclaration,
8595    identifier: ts.Identifier
8596  ): void {
8597    if (!this.options.arkts2) {
8598      return;
8599    }
8600
8601    if (!this.tsUtils.isImportedFromJS(identifier)) {
8602      return;
8603    }
8604
8605    const className = node.name?.text;
8606    if (!className) {
8607      return;
8608    }
8609    this.incrementCounters(node, FaultID.InteropJsObjectInheritance);
8610  }
8611
8612  private fixJsImportPropertyAccessExpression(node: ts.Node): void {
8613    if (!this.options.arkts2 || !this.useStatic) {
8614      return;
8615    }
8616
8617    const identifier = this.tsUtils.findIdentifierInExpression(node);
8618    if (!identifier) {
8619      return;
8620    }
8621
8622    // Try direct check first
8623    if (!this.tsUtils.isImportedFromJS(identifier)) {
8624      return;
8625    }
8626    const autofix = this.autofixer?.createReplacementForJsImportPropertyAccessExpression(
8627      node as ts.PropertyAccessExpression
8628    );
8629    if (!TsUtils.isInsideIfCondition(node)) {
8630      return;
8631    }
8632    this.incrementCounters(node, FaultID.InteropJsObjectConditionJudgment, autofix);
8633  }
8634
8635  private fixJsImportElementAccessExpression(elementAccessExpr: ts.ElementAccessExpression): void {
8636    if (!this.options.arkts2 || !this.useStatic) {
8637      return;
8638    }
8639    if (!TypeScriptLinter.isInForLoopBody(elementAccessExpr)) {
8640      return;
8641    }
8642    const variableDeclaration = ts.isIdentifier(elementAccessExpr.expression) ?
8643      this.tsUtils.findVariableDeclaration(elementAccessExpr.expression) :
8644      undefined;
8645    if (!variableDeclaration?.initializer) {
8646      return;
8647    }
8648
8649    const identifier = ts.isPropertyAccessExpression(variableDeclaration.initializer) ?
8650      (variableDeclaration.initializer.expression as ts.Identifier) :
8651      undefined;
8652    if (!identifier) {
8653      return;
8654    }
8655
8656    if (!this.tsUtils.isImportedFromJS(identifier)) {
8657      return;
8658    }
8659
8660    const autofix = this.autofixer?.fixJsImportElementAccessExpression(elementAccessExpr);
8661
8662    this.incrementCounters(elementAccessExpr, FaultID.InteropJsObjectTraverseJsInstance, autofix);
8663  }
8664
8665  private static isInForLoopBody(node: ts.Node): boolean {
8666    let current: ts.Node | undefined = node.parent;
8667    while (current) {
8668      if (ts.isForStatement(current) || ts.isForInStatement(current) || ts.isForOfStatement(current)) {
8669        return true;
8670      }
8671      current = current.parent;
8672    }
8673    return false;
8674  }
8675
8676  private handleTaskPoolDeprecatedUsages(propertyAccess: ts.PropertyAccessExpression): void {
8677    if (!this.options.arkts2 || !this.useStatic) {
8678      return;
8679    }
8680    const objectExpr = ts.isNewExpression(propertyAccess.expression) ?
8681      propertyAccess.expression.expression :
8682      propertyAccess.expression;
8683    // Step 1: Must be either setCloneList or setTransferList
8684    if (!TypeScriptLinter.isDeprecatedTaskPoolMethodCall(propertyAccess)) {
8685      return;
8686    }
8687    const variableDecl = TsUtils.getDeclaration(this.tsUtils.trueSymbolAtLocation(objectExpr));
8688    const isNoContinue =
8689      !variableDecl ||
8690      !ts.isVariableDeclaration(variableDecl) ||
8691      !variableDecl?.initializer ||
8692      !ts.isNewExpression(variableDecl.initializer);
8693    if (isNoContinue) {
8694      return;
8695    }
8696    const taskpoolExpr = variableDecl.initializer.expression;
8697    if (!this.isTaskPoolTaskCreation(taskpoolExpr)) {
8698      return;
8699    }
8700    const faultId =
8701      propertyAccess.name.text === DEPRECATED_TASKPOOL_METHOD_SETCLONELIST ?
8702        FaultID.SetCloneListDeprecated :
8703        FaultID.SetTransferListDeprecated;
8704    this.incrementCounters(propertyAccess.name, faultId);
8705  }
8706
8707  private static isDeprecatedTaskPoolMethodCall(propertyAccess: ts.PropertyAccessExpression): boolean {
8708    const methodName = propertyAccess.name.text;
8709    return (
8710      methodName === DEPRECATED_TASKPOOL_METHOD_SETCLONELIST ||
8711      methodName === DEPRECATED_TASKPOOL_METHOD_SETTRANSFERLIST
8712    );
8713  }
8714
8715  private isTaskPoolTaskCreation(taskpoolExpr: ts.Expression): boolean {
8716    if (
8717      ts.isIdentifier(taskpoolExpr) ||
8718      ts.isPropertyAccessExpression(taskpoolExpr) && taskpoolExpr.name.text === STDLIB_TASK_CLASS_NAME
8719    ) {
8720      const objectExpr = ts.isIdentifier(taskpoolExpr) ? taskpoolExpr : taskpoolExpr.expression;
8721      return this.isTaskPoolReferenceisTaskPoolImportForTaskPoolDeprecatedUsages(objectExpr);
8722    }
8723    return false;
8724  }
8725
8726  private isTaskPoolReferenceisTaskPoolImportForTaskPoolDeprecatedUsages(expr: ts.Expression): boolean {
8727    if (ts.isIdentifier(expr)) {
8728      const sym = this.tsTypeChecker.getSymbolAtLocation(expr);
8729      const importChild = TsUtils.getDeclaration(sym);
8730      if (!importChild) {
8731        return false;
8732      }
8733      if (ts.isImportSpecifier(importChild)) {
8734        return TypeScriptLinter.isTaskPoolImportForTaskPoolDeprecatedUsages(importChild);
8735      }
8736      if (ts.isImportClause(importChild) && importChild.name?.text === STDLIB_TASKPOOL_OBJECT_NAME) {
8737        return TypeScriptLinter.checkModuleSpecifierForTaskPoolDeprecatedUsages(importChild.parent);
8738      }
8739    }
8740    if (ts.isPropertyAccessExpression(expr)) {
8741      return this.isTaskPoolReferenceOnPropertyAccessExpression(expr);
8742    }
8743    return false;
8744  }
8745
8746  private static checkModuleSpecifierForTaskPoolDeprecatedUsages(importDecl: ts.ImportDeclaration): boolean {
8747    if (ts.isImportDeclaration(importDecl) && ts.isStringLiteral(importDecl.moduleSpecifier)) {
8748      const moduleSpecifier = importDecl.moduleSpecifier;
8749      return TASKPOOL_MODULES.includes(TsUtils.removeOrReplaceQuotes(moduleSpecifier.getText(), false));
8750    }
8751    return false;
8752  }
8753
8754  private isTaskPoolReferenceOnPropertyAccessExpression(expr: ts.PropertyAccessExpression): boolean {
8755    if (expr.name.text !== STDLIB_TASKPOOL_OBJECT_NAME || !ts.isIdentifier(expr.expression)) {
8756      return false;
8757    }
8758    const sym = this.tsTypeChecker.getSymbolAtLocation(expr.expression);
8759    const importChild = TsUtils.getDeclaration(sym);
8760    if (importChild && ts.isNamespaceImport(importChild)) {
8761      return TypeScriptLinter.checkModuleSpecifierForTaskPoolDeprecatedUsages(importChild.parent.parent);
8762    }
8763    return false;
8764  }
8765
8766  private static isTaskPoolImportForTaskPoolDeprecatedUsages(specifier: ts.ImportSpecifier): boolean {
8767    const specifierName = specifier.propertyName ? specifier.propertyName : specifier.name;
8768    if (STDLIB_TASKPOOL_OBJECT_NAME !== specifierName.text) {
8769      return false;
8770    }
8771    const importDeclaration = specifier.parent.parent.parent;
8772    return TypeScriptLinter.checkModuleSpecifierForTaskPoolDeprecatedUsages(importDeclaration);
8773  }
8774
8775  private handleForOfJsArray(node: ts.ForOfStatement): void {
8776    if (!this.options.arkts2 || !this.useStatic) {
8777      return;
8778    }
8779
8780    const expr = node.expression;
8781    if (!ts.isIdentifier(expr) || !this.tsUtils.isPossiblyImportedFromJS(expr)) {
8782      return;
8783    }
8784
8785    const exprType = this.tsTypeChecker.getTypeAtLocation(expr);
8786
8787    if (!this.tsUtils.isArray(exprType)) {
8788      return;
8789    }
8790
8791    this.incrementCounters(node, FaultID.InteropJsObjectTraverseJsInstance);
8792  }
8793
8794  private checkStdLibConcurrencyImport(importDeclaration: ts.ImportDeclaration): void {
8795    if (!this.options.arkts2) {
8796      return;
8797    }
8798
8799    const importClause = importDeclaration.importClause;
8800    if (!importClause) {
8801      return;
8802    }
8803
8804    const moduleName = (importDeclaration.moduleSpecifier as ts.StringLiteral).text;
8805    const expectedImports = MODULE_IMPORTS[moduleName];
8806    if (!expectedImports) {
8807      return;
8808    }
8809
8810    const defaultImport = importClause.name;
8811    const namedBindings = importClause.namedBindings;
8812
8813    const namedImports = namedBindings && ts.isNamedImports(namedBindings) ? namedBindings.elements : [];
8814
8815    const defaultIsForbidden = defaultImport && expectedImports.includes(defaultImport.getText());
8816    const forbiddenNamed = namedImports.filter((spec) => {
8817      const name = spec.propertyName ? spec.propertyName.getText() : spec.name.getText();
8818      return expectedImports.includes(name);
8819    });
8820
8821    if (
8822      TypeScriptLinter.shouldRemoveWholeImport(
8823        defaultIsForbidden,
8824        forbiddenNamed.length,
8825        namedImports.length,
8826        defaultImport
8827      )
8828    ) {
8829      const autofix = this.autofixer?.removeNode(importDeclaration);
8830      this.incrementCounters(importDeclaration, FaultID.LimitedStdLibNoImportConcurrency, autofix);
8831      return;
8832    }
8833
8834    if (defaultIsForbidden) {
8835      const autofix = this.autofixer?.removeDefaultImport(importDeclaration, defaultImport);
8836      this.incrementCounters(defaultImport, FaultID.LimitedStdLibNoImportConcurrency, autofix);
8837    }
8838
8839    for (const spec of forbiddenNamed) {
8840      const autofix = this.autofixer?.removeImportSpecifier(spec, importDeclaration);
8841      this.incrementCounters(spec, FaultID.LimitedStdLibNoImportConcurrency, autofix);
8842    }
8843  }
8844
8845  private static shouldRemoveWholeImport(
8846    defaultIsForbidden: boolean | undefined,
8847    forbiddenNamedCount: number,
8848    namedImportsCount: number,
8849    defaultImport: ts.Identifier | undefined
8850  ): boolean {
8851    return (
8852      defaultIsForbidden && forbiddenNamedCount === namedImportsCount ||
8853      defaultIsForbidden && namedImportsCount === 0 ||
8854      !defaultImport && forbiddenNamedCount === namedImportsCount && namedImportsCount > 0
8855    );
8856  }
8857
8858  /**
8859   * Checks for missing super() call in child classes that extend a parent class
8860   * with parameterized constructors. If parent class only has parameterized constructors
8861   * and the child class does not call super() in its constructor, report a fault.
8862   *
8863   * This ensures safe and correct subclassing behavior.
8864   *
8865   * @param node The HeritageClause node (extends clause) to analyze.
8866   */
8867  private handleMissingSuperCallInExtendedClass(node: ts.HeritageClause): void {
8868    if (!this.options.arkts2 || !this.useStatic) {
8869      return;
8870    }
8871
8872    // We are only interested in 'extends' clauses
8873    if (node.token !== ts.SyntaxKind.ExtendsKeyword) {
8874      return;
8875    }
8876
8877    // Get the parent class declaration (what the child class extends)
8878    const parentClass = this.getParentClassDeclaration(node);
8879    if (!parentClass) {
8880      return;
8881    }
8882
8883    // If parent class has a parameterless constructor (or no constructor at all), child is fine
8884    if (TypeScriptLinter.parentHasParameterlessConstructor(parentClass)) {
8885      return;
8886    }
8887
8888    // The child class node (the one extending)
8889    const childClass = node.parent;
8890    if (!ts.isClassDeclaration(childClass)) {
8891      return;
8892    }
8893
8894    // Look for child class constructor
8895    const childConstructor = childClass.members.find(ts.isConstructorDeclaration);
8896
8897    /*
8898     * If child has no constructor → error (super() cannot be called)
8899     * If child constructor exists but does not contain super() → error
8900     */
8901    if (!childConstructor?.body || !TypeScriptLinter.childHasSuperCall(childConstructor)) {
8902      this.incrementCounters(node, FaultID.MissingSuperCall);
8903    }
8904  }
8905
8906  /**
8907   * Retrieves the parent class declaration node from an extends heritage clause.
8908   */
8909  private getParentClassDeclaration(node: ts.HeritageClause): ts.ClassDeclaration | undefined {
8910    const parentExpr = node.types[0]?.expression;
8911    if (!parentExpr) {
8912      return undefined;
8913    }
8914    const parentSymbol = this.tsUtils.trueSymbolAtLocation(parentExpr);
8915    return parentSymbol?.declarations?.find(ts.isClassDeclaration);
8916  }
8917
8918  /**
8919   * Determines if a parent class has a parameterless constructor.
8920   * If it has no constructor at all, that counts as parameterless.
8921   */
8922  private static parentHasParameterlessConstructor(parentClass: ts.ClassDeclaration): boolean {
8923    const constructors = parentClass.members.filter(ts.isConstructorDeclaration);
8924    return (
8925      constructors.length === 0 ||
8926      constructors.some((ctor) => {
8927        return ctor.parameters.length === 0;
8928      })
8929    );
8930  }
8931
8932  private static childHasSuperCall(constructor: ts.ConstructorDeclaration): boolean {
8933    let superCalled = false;
8934
8935    if (!constructor.body) {
8936      return false;
8937    }
8938
8939    ts.forEachChild(constructor.body, (stmt) => {
8940      if (
8941        ts.isExpressionStatement(stmt) &&
8942        ts.isCallExpression(stmt.expression) &&
8943        stmt.expression.expression.kind === ts.SyntaxKind.SuperKeyword
8944      ) {
8945        superCalled = true;
8946      }
8947    });
8948    return superCalled;
8949  }
8950
8951  private handleInterOpImportJs(importDecl: ts.ImportDeclaration): void {
8952    if (!this.options.arkts2 || !importDecl || !this.useStatic) {
8953      return;
8954    }
8955    const importClause = importDecl.importClause;
8956    if (!importClause) {
8957      return;
8958    }
8959    const namedBindings = importClause.namedBindings;
8960    let symbol: ts.Symbol | undefined;
8961    let defaultSymbol: ts.Symbol | undefined;
8962    if (importClause.name) {
8963      defaultSymbol = this.tsUtils.trueSymbolAtLocation(importClause.name);
8964    }
8965    if (namedBindings) {
8966      if (ts.isNamedImports(namedBindings) && namedBindings.elements?.length > 0 && namedBindings.elements[0]?.name) {
8967        symbol = this.tsUtils.trueSymbolAtLocation(namedBindings.elements[0].name);
8968      } else if (ts.isNamespaceImport(namedBindings)) {
8969        symbol = this.tsUtils.trueSymbolAtLocation(namedBindings.name);
8970      }
8971    }
8972    const symbolToUse = defaultSymbol || symbol;
8973    if (symbolToUse) {
8974      this.tryAutoFixInterOpImportJs(importDecl, importClause, symbolToUse, defaultSymbol);
8975    }
8976  }
8977
8978  private tryAutoFixInterOpImportJs(
8979    importDecl: ts.ImportDeclaration,
8980    importClause: ts.ImportClause,
8981    symbolToUse: ts.Symbol,
8982    defaultSymbol?: ts.Symbol
8983  ): void {
8984    const declaration = symbolToUse.declarations?.[0];
8985    if (declaration) {
8986      const sourceFile = declaration.getSourceFile();
8987      if (sourceFile.fileName.endsWith(EXTNAME_JS)) {
8988        const autofix = this.autofixer?.fixInterOpImportJs(
8989          importDecl,
8990          importClause,
8991          TsUtils.removeOrReplaceQuotes(importDecl.moduleSpecifier.getText(this.sourceFile), false),
8992          defaultSymbol
8993        );
8994        this.incrementCounters(importDecl, FaultID.InterOpImportJs, autofix);
8995      }
8996    }
8997  }
8998
8999  private findVariableDeclaration(identifier: ts.Identifier): ts.VariableDeclaration | undefined {
9000    const sym = this.tsUtils.trueSymbolAtLocation(identifier);
9001    const decl = TsUtils.getDeclaration(sym);
9002    if (
9003      decl &&
9004      ts.isVariableDeclaration(decl) &&
9005      decl.getSourceFile().fileName === identifier.getSourceFile().fileName
9006    ) {
9007      return decl;
9008    }
9009    return undefined;
9010  }
9011
9012  private isFromJSModule(node: ts.Node): boolean {
9013    const symbol = this.tsUtils.trueSymbolAtLocation(node);
9014    if (symbol?.declarations?.[0]) {
9015      const sourceFile = symbol.declarations[0].getSourceFile();
9016      return sourceFile.fileName.endsWith(EXTNAME_JS);
9017    }
9018    return false;
9019  }
9020
9021  handleInstanceOfExpression(node: ts.BinaryExpression): void {
9022    if (!this.options.arkts2 || !this.useStatic) {
9023      return;
9024    }
9025
9026    const left = node.left;
9027    const right = node.right;
9028    const getNode = (expr: ts.Expression): ts.Node => {
9029      return ts.isPropertyAccessExpression(expr) || ts.isCallExpression(expr) ? expr.expression : expr;
9030    };
9031
9032    const leftExpr = getNode(left);
9033    const rightExpr = getNode(right);
9034
9035    if (!this.tsUtils.isJsImport(leftExpr) && !this.tsUtils.isJsImport(rightExpr)) {
9036      return;
9037    }
9038
9039    const autofix = this.autofixer?.fixInteropJsInstanceOfExpression(node);
9040    this.incrementCounters(node, FaultID.InteropJsInstanceof, autofix);
9041  }
9042
9043  private checkAutoIncrementDecrement(unaryExpr: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression): void {
9044    if (!this.useStatic || !this.options.arkts2) {
9045      return;
9046    }
9047
9048    if (!ts.isPropertyAccessExpression(unaryExpr.operand)) {
9049      return;
9050    }
9051
9052    const propertyAccess = unaryExpr.operand;
9053    if (!this.tsUtils.isJsImport(propertyAccess.expression)) {
9054      return;
9055    }
9056
9057    const autofix = this.autofixer?.fixUnaryIncrDecr(unaryExpr, propertyAccess);
9058
9059    this.incrementCounters(unaryExpr, FaultID.InteropIncrementDecrement, autofix);
9060  }
9061
9062  private handleObjectLiteralforUnionTypeInterop(node: ts.VariableDeclaration): void {
9063    if (!this.options.arkts2 || !this.useStatic) {
9064      return;
9065    }
9066
9067    if (!node.type || !ts.isUnionTypeNode(node.type)) {
9068      return;
9069    }
9070
9071    if (!node.initializer || node.initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
9072      return;
9073    }
9074
9075    const typeNodes = node.type.types;
9076
9077    const isDefected = typeNodes.some((tNode) => {
9078      if (!ts.isTypeReferenceNode(tNode)) {
9079        return false;
9080      }
9081      const type = this.tsTypeChecker.getTypeAtLocation(tNode);
9082      const symbol = type.getSymbol();
9083      if (!symbol) {
9084        return false;
9085      }
9086      for (const declaration of symbol.declarations ?? []) {
9087        if (!this.tsUtils.isArkts12File(declaration.getSourceFile())) {
9088          return true;
9089        }
9090      }
9091      return false;
9092    });
9093
9094    if (isDefected) {
9095      this.incrementCounters(node, FaultID.InteropObjectLiteralAmbiguity);
9096    }
9097  }
9098
9099  private handleObjectLiteralAssignmentToClass(
9100    node:
9101      | ts.VariableDeclaration
9102      | ts.CallExpression
9103      | ts.ReturnStatement
9104      | ts.ArrayLiteralExpression
9105      | ts.PropertyDeclaration
9106      | ts.AsExpression
9107      | ts.BinaryExpression
9108  ): void {
9109    if (!this.options.arkts2 || !this.useStatic) {
9110      return;
9111    }
9112
9113    switch (node.kind) {
9114      case ts.SyntaxKind.VariableDeclaration:
9115        this.checkVariableDeclarationForObjectLiteral(node);
9116        break;
9117      case ts.SyntaxKind.CallExpression:
9118        this.checkCallExpressionForObjectLiteral(node);
9119        break;
9120      case ts.SyntaxKind.ReturnStatement:
9121        this.checkReturnStatementForObjectLiteral(node);
9122        break;
9123      case ts.SyntaxKind.ArrayLiteralExpression:
9124        this.checkArrayLiteralExpressionForObjectLiteral(node);
9125        break;
9126      case ts.SyntaxKind.PropertyDeclaration:
9127        this.checkPropertyDeclarationForObjectLiteral(node);
9128        break;
9129      case ts.SyntaxKind.AsExpression:
9130        this.checkAsExpressionForObjectLiteral(node);
9131        break;
9132      case ts.SyntaxKind.BinaryExpression:
9133        this.checkBinaryExpressionForObjectLiteral(node);
9134        break;
9135      default:
9136    }
9137  }
9138
9139  private reportIfAssignedToNonArkts2Class(type: ts.Type, expr: ts.ObjectLiteralExpression): void {
9140    const symbol = type.getSymbol();
9141    if (!symbol) {
9142      return;
9143    }
9144
9145    const declarations = symbol.declarations ?? [];
9146    const isClass = declarations.some(ts.isClassDeclaration);
9147    if (!isClass) {
9148      return;
9149    }
9150
9151    const isFromArkTs2 = declarations.some((decl) => {
9152      return this.tsUtils.isArkts12File(decl.getSourceFile());
9153    });
9154
9155    if (isFromArkTs2) {
9156      return;
9157    }
9158
9159    const hasConstructor = declarations.some((decl) => {
9160      return ts.isClassDeclaration(decl) && decl.members.some(ts.isConstructorDeclaration);
9161    });
9162
9163    if (hasConstructor) {
9164      this.incrementCounters(expr, FaultID.InteropObjectLiteralClass);
9165    }
9166  }
9167
9168  private checkVariableDeclarationForObjectLiteral(node: ts.VariableDeclaration): void {
9169    if (!node.initializer || !node.type) {
9170      return;
9171    }
9172
9173    const type = this.tsTypeChecker.getTypeAtLocation(node.type);
9174
9175    const checkObjectLiteral = (expr: ts.Expression): void => {
9176      if (ts.isObjectLiteralExpression(expr)) {
9177        this.reportIfAssignedToNonArkts2Class(type, expr);
9178      }
9179    };
9180
9181    if (ts.isObjectLiteralExpression(node.initializer)) {
9182      checkObjectLiteral(node.initializer);
9183    } else if (ts.isConditionalExpression(node.initializer)) {
9184      checkObjectLiteral(node.initializer.whenTrue);
9185      checkObjectLiteral(node.initializer.whenFalse);
9186    }
9187  }
9188
9189  private checkCallExpressionForObjectLiteral(node: ts.CallExpression): void {
9190    for (const arg of node.arguments) {
9191      if (ts.isObjectLiteralExpression(arg)) {
9192        const signature = this.tsTypeChecker.getResolvedSignature(node);
9193        const params = signature?.getParameters() ?? [];
9194        const index = node.arguments.indexOf(arg);
9195        const paramSymbol = params[index];
9196        if (!paramSymbol) {
9197          continue;
9198        }
9199
9200        const paramDecl = paramSymbol.declarations?.[0];
9201        if (!paramDecl || !ts.isParameter(paramDecl) || !paramDecl.type) {
9202          continue;
9203        }
9204
9205        const type = this.tsTypeChecker.getTypeAtLocation(paramDecl.type);
9206        this.reportIfAssignedToNonArkts2Class(type, arg);
9207      }
9208    }
9209  }
9210
9211  private checkReturnStatementForObjectLiteral(node: ts.ReturnStatement): void {
9212    if (!node.expression || !ts.isObjectLiteralExpression(node.expression)) {
9213      return;
9214    }
9215    const func = ts.findAncestor(node, ts.isFunctionLike);
9216    if (!func?.type) {
9217      return;
9218    }
9219
9220    const returnType = this.tsTypeChecker.getTypeAtLocation(func.type);
9221    this.reportIfAssignedToNonArkts2Class(returnType, node.expression);
9222  }
9223
9224  private checkArrayLiteralExpressionForObjectLiteral(node: ts.ArrayLiteralExpression): void {
9225    for (const element of node.elements) {
9226      if (ts.isObjectLiteralExpression(element)) {
9227        const contextualType = this.tsTypeChecker.getContextualType(node);
9228        if (!contextualType) {
9229          continue;
9230        }
9231
9232        const typeArgs = (contextualType as ts.TypeReference).typeArguments;
9233        const elementType = typeArgs?.[0];
9234        if (!elementType) {
9235          continue;
9236        }
9237
9238        this.reportIfAssignedToNonArkts2Class(elementType, element);
9239      }
9240    }
9241  }
9242
9243  private checkPropertyDeclarationForObjectLiteral(node: ts.PropertyDeclaration): void {
9244    if (!node.initializer || !ts.isObjectLiteralExpression(node.initializer) || !node.type) {
9245      return;
9246    }
9247
9248    const type = this.tsTypeChecker.getTypeAtLocation(node.type);
9249    this.reportIfAssignedToNonArkts2Class(type, node.initializer);
9250  }
9251
9252  private checkAsExpressionForObjectLiteral(node: ts.AsExpression): void {
9253    if (!ts.isObjectLiteralExpression(node.expression)) {
9254      return;
9255    }
9256
9257    const type = this.tsTypeChecker.getTypeAtLocation(node.type);
9258    this.reportIfAssignedToNonArkts2Class(type, node.expression);
9259  }
9260
9261  private checkBinaryExpressionForObjectLiteral(node: ts.BinaryExpression): void {
9262    if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
9263      return;
9264    }
9265    if (!ts.isObjectLiteralExpression(node.right)) {
9266      return;
9267    }
9268
9269    const type = this.tsTypeChecker.getTypeAtLocation(node.left);
9270    this.reportIfAssignedToNonArkts2Class(type, node.right);
9271  }
9272
9273  private isObjectLiteralAssignedToArkts12Type(node: ts.Expression, expectedTypeNode?: ts.TypeNode): boolean {
9274    if (node.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
9275      return false;
9276    }
9277
9278    let type: ts.Type;
9279    if (expectedTypeNode) {
9280      type = this.tsTypeChecker.getTypeAtLocation(expectedTypeNode);
9281    } else {
9282      type = this.tsTypeChecker.getContextualType(node) ?? this.tsTypeChecker.getTypeAtLocation(node);
9283    }
9284
9285    if (!type) {
9286      return false;
9287    }
9288
9289    return this.isTypeFromArkts12(type);
9290  }
9291
9292  private isTypeFromArkts12(type: ts.Type): boolean {
9293    const symbol = type?.getSymbol();
9294    if (!symbol) {
9295      return false;
9296    }
9297
9298    const isFromArkts12 = (symbol.declarations ?? []).some((decl) => {
9299      return this.tsUtils.isArkts12File(decl.getSourceFile());
9300    });
9301
9302    if (isFromArkts12) {
9303      return true;
9304    }
9305    return false;
9306  }
9307
9308  private processNestedObjectLiterals(objLiteral: ts.Expression, parentType?: ts.Type): void {
9309    if (!ts.isObjectLiteralExpression(objLiteral)) {
9310      return;
9311    }
9312
9313    objLiteral.properties.forEach((prop) => {
9314      if (!ts.isPropertyAssignment(prop) || !ts.isObjectLiteralExpression(prop.initializer)) {
9315        return;
9316      }
9317
9318      if (this.isObjectLiteralAssignedToArkts12Type(prop.initializer)) {
9319        this.incrementCounters(prop.initializer, FaultID.InteropStaticObjectLiterals);
9320        return;
9321      }
9322
9323      this.checkPropertyTypeFromParent(prop, parentType);
9324      this.processNestedObjectLiterals(prop.initializer);
9325    });
9326  }
9327
9328  private checkPropertyTypeFromParent(prop: ts.PropertyAssignment, parentType?: ts.Type): void {
9329    if (!parentType) {
9330      return;
9331    }
9332    if (!ts.isObjectLiteralExpression(prop.initializer)) {
9333      return;
9334    }
9335
9336    const propName = prop.name.getText();
9337    const property = parentType.getProperty(propName);
9338
9339    if (!property?.valueDeclaration) {
9340      return;
9341    }
9342
9343    const propType = this.tsTypeChecker.getTypeOfSymbolAtLocation(property, property.valueDeclaration);
9344
9345    if (this.isTypeFromArkts12(propType)) {
9346      this.incrementCounters(prop.initializer, FaultID.InteropStaticObjectLiterals);
9347    }
9348  }
9349
9350  private handleObjectLiteralAssignment(node: ts.VariableDeclaration): void {
9351    if (this.tsUtils.isArkts12File(node.getSourceFile())) {
9352      return;
9353    }
9354
9355    if (!node.initializer) {
9356      return;
9357    }
9358
9359    if (
9360      ts.isObjectLiteralExpression(node.initializer) &&
9361      this.isObjectLiteralAssignedToArkts12Type(node.initializer, node.type)
9362    ) {
9363      this.incrementCounters(node.initializer, FaultID.InteropStaticObjectLiterals);
9364      return;
9365    }
9366
9367    const parentType = node.type ?
9368      this.tsTypeChecker.getTypeAtLocation(node.type) :
9369      this.tsTypeChecker.getTypeAtLocation(node.initializer);
9370
9371    this.processNestedObjectLiterals(node.initializer, parentType);
9372  }
9373
9374  private handleObjectLiteralInFunctionArgs(node: ts.CallExpression): void {
9375    if (this.tsUtils.isArkts12File(node.getSourceFile())) {
9376      return;
9377    }
9378    const signature = this.tsTypeChecker.getResolvedSignature(node);
9379    if (!signature) {
9380      return;
9381    }
9382
9383    const params = signature.getParameters();
9384
9385    node.arguments.forEach((arg, index) => {
9386      if (!ts.isObjectLiteralExpression(arg)) {
9387        return;
9388      }
9389
9390      if (index < params.length) {
9391        const param = params[index];
9392        if (!param.valueDeclaration) {
9393          return;
9394        }
9395
9396        const paramType = this.tsTypeChecker.getTypeOfSymbolAtLocation(param, param.valueDeclaration);
9397
9398        if (this.isTypeFromArkts12(paramType)) {
9399          this.incrementCounters(arg, FaultID.InteropStaticObjectLiterals);
9400        }
9401      } else if (this.isObjectLiteralAssignedToArkts12Type(arg)) {
9402        this.incrementCounters(arg, FaultID.InteropStaticObjectLiterals);
9403      }
9404    });
9405  }
9406
9407  private handleObjectLiteralInReturn(node: ts.ReturnStatement): void {
9408    if (this.tsUtils.isArkts12File(node.getSourceFile())) {
9409      return;
9410    }
9411
9412    if (!node.expression || !ts.isObjectLiteralExpression(node.expression)) {
9413      return;
9414    }
9415
9416    let current: ts.Node = node;
9417    let functionNode: ts.FunctionLikeDeclaration | undefined;
9418
9419    while (current && !functionNode) {
9420      current = current.parent;
9421      if (
9422        current &&
9423        (ts.isFunctionDeclaration(current) ||
9424          ts.isMethodDeclaration(current) ||
9425          ts.isFunctionExpression(current) ||
9426          ts.isArrowFunction(current))
9427      ) {
9428        functionNode = current;
9429      }
9430    }
9431
9432    if (functionNode?.type) {
9433      const returnType = this.tsTypeChecker.getTypeAtLocation(functionNode.type);
9434      if (this.isTypeFromArkts12(returnType)) {
9435        this.incrementCounters(node.expression, FaultID.InteropStaticObjectLiterals);
9436      }
9437    } else if (this.isObjectLiteralAssignedToArkts12Type(node.expression)) {
9438      this.incrementCounters(node.expression, FaultID.InteropStaticObjectLiterals);
9439    }
9440  }
9441
9442  private handleLocalBuilderDecorator(node: ts.Node): void {
9443    if (!this.options.arkts2) {
9444      return;
9445    }
9446    if (!ts.isDecorator(node) || !ts.isIdentifier(node.expression)) {
9447      return;
9448    }
9449    const decoratorName = node.expression.getText();
9450    if (decoratorName === CustomDecoratorName.LocalBuilder) {
9451      const autofix = this.autofixer?.fixBuilderDecorators(node);
9452      this.incrementCounters(node, FaultID.LocalBuilderDecoratorNotSupported, autofix);
9453    }
9454  }
9455
9456  private checkEnumGetMemberValue(node: ts.ElementAccessExpression): void {
9457    if (!this.options.arkts2) {
9458      return;
9459    }
9460
9461    const symbol = this.tsUtils.trueSymbolAtLocation(node.expression);
9462    if (!symbol?.declarations) {
9463      return;
9464    }
9465
9466    for (const decl of symbol.declarations) {
9467      if (ts.isEnumDeclaration(decl) && this.shouldIncrementCounters(node)) {
9468        this.incrementCounters(node, FaultID.UnsupportPropNameFromValue);
9469        return;
9470      }
9471    }
9472  }
9473
9474  private shouldIncrementCounters(node: ts.ElementAccessExpression): boolean {
9475    const indexExpr = node.argumentExpression;
9476    if (!indexExpr) {
9477      return false;
9478    }
9479    if (ts.isStringLiteral(indexExpr) || ts.isNumericLiteral(indexExpr)) {
9480      return true;
9481    }
9482    const type = this.tsTypeChecker.getTypeAtLocation(indexExpr);
9483    const typeString = this.tsTypeChecker.typeToString(type);
9484    return typeString === 'number' || typeString === 'string';
9485  }
9486
9487  private handleMakeObserved(node: ts.PropertyAccessExpression): void {
9488    if (!this.options.arkts2) {
9489      return;
9490    }
9491
9492    const name = node.name;
9493    if (name.getText() !== MAKE_OBSERVED) {
9494      return;
9495    }
9496
9497    const expr = node.expression;
9498    const symbol = this.tsTypeChecker.getSymbolAtLocation(expr);
9499    const importSpecifier = TsUtils.getDeclaration(symbol);
9500    if (!importSpecifier || !ts.isImportSpecifier(importSpecifier)) {
9501      return;
9502    }
9503
9504    const importDecl = ts.findAncestor(importSpecifier, ts.isImportDeclaration);
9505    if (!importDecl) {
9506      return;
9507    }
9508
9509    const moduleSpecifier = importDecl.moduleSpecifier;
9510    if (!ts.isStringLiteral(moduleSpecifier)) {
9511      return;
9512    }
9513    if (moduleSpecifier.text !== ARKUI_PACKAGE_NAME && moduleSpecifier.text !== ARKUI_STATE_MANAGEMENT) {
9514      return;
9515    }
9516
9517    this.incrementCounters(node, FaultID.MakeObservedIsNotSupported);
9518  }
9519
9520  private handlePropertyDeclarationForProp(node: ts.PropertyDeclaration): void {
9521    if (!this.options.arkts2) {
9522      return;
9523    }
9524
9525    const decorators = ts.getDecorators(node);
9526    if (!decorators || decorators.length === 0) {
9527      return;
9528    }
9529
9530    let decoratorName: string | undefined;
9531    if (ts.isIdentifier(decorators[0].expression)) {
9532      decoratorName = decorators[0].expression.getText();
9533    } else if (ts.isCallExpression(decorators[0].expression) && ts.isIdentifier(decorators[0].expression.expression)) {
9534      decoratorName = decorators[0].expression.expression.getText();
9535    }
9536
9537    if (!decoratorName) {
9538      return;
9539    }
9540
9541    const autofix = this.autofixer?.fixPropDecorator(decorators[0], decoratorName);
9542    switch (decoratorName) {
9543      case PropDecoratorName.Prop:
9544        this.incrementCounters(node, FaultID.PropDecoratorNotSupported, autofix);
9545        break;
9546      case PropDecoratorName.StorageProp:
9547        this.incrementCounters(node, FaultID.StoragePropDecoratorNotSupported, autofix);
9548        break;
9549      case PropDecoratorName.LocalStorageProp:
9550        this.incrementCounters(node, FaultID.LocalStoragePropDecoratorNotSupported, autofix);
9551        break;
9552      default:
9553    }
9554  }
9555
9556  private handleVariableDeclarationForProp(node: ts.VariableDeclaration): void {
9557    if (!this.options.arkts2) {
9558      return;
9559    }
9560
9561    const callExpr = node.initializer;
9562    if (!callExpr || !ts.isCallExpression(callExpr)) {
9563      return;
9564    }
9565
9566    const propertyAccessExpr = callExpr.expression;
9567    if (!ts.isPropertyAccessExpression(propertyAccessExpr)) {
9568      return;
9569    }
9570
9571    const storage = propertyAccessExpr.expression;
9572    if (
9573      !ts.isIdentifier(storage) ||
9574      !this.isTargetStorageType(storage, [StorageTypeName.LocalStorage, StorageTypeName.AppStorage])
9575    ) {
9576      return;
9577    }
9578
9579    const functionName = propertyAccessExpr.name.getText();
9580    switch (functionName) {
9581      case PropFunctionName.Prop:
9582        this.incrementCounters(node, FaultID.PropFunctionNotSupported);
9583        break;
9584      case PropFunctionName.SetAndProp:
9585        this.incrementCounters(node, FaultID.SetAndPropFunctionNotSupported);
9586        break;
9587      default:
9588    }
9589  }
9590
9591  private isTargetStorageType(storage: ts.Identifier, targetTypes: string[]): boolean {
9592    const decl = this.tsUtils.getDeclarationNode(storage);
9593    if (!decl || decl.getSourceFile() !== storage.getSourceFile()) {
9594      return targetTypes.includes(storage.getText());
9595    }
9596
9597    if (!ts.isVariableDeclaration(decl)) {
9598      return false;
9599    }
9600
9601    let storageType: ts.Node | undefined;
9602    if (decl.initializer) {
9603      if (ts.isNewExpression(decl.initializer)) {
9604        storageType = decl.initializer.expression;
9605      } else if (ts.isCallExpression(decl.initializer) && ts.isPropertyAccessExpression(decl.initializer.expression)) {
9606        storageType = decl.initializer.expression.expression;
9607      }
9608    }
9609
9610    if (!storageType || !ts.isIdentifier(storageType)) {
9611      return false;
9612    }
9613
9614    return targetTypes.includes(storageType.getText());
9615  }
9616
9617  private handlePropertyAssignmentForProp(node: ts.PropertyAssignment): void {
9618    if (!this.options.arkts2) {
9619      return;
9620    }
9621
9622    const callExpr = node.parent.parent;
9623    if (!ts.isCallExpression(callExpr)) {
9624      return;
9625    }
9626
9627    const structDecl = TsUtils.getDeclaration(this.tsTypeChecker.getSymbolAtLocation(callExpr.expression));
9628    if (!structDecl || !ts.isStructDeclaration(structDecl) || !structDecl.name) {
9629      return;
9630    }
9631
9632    const variable = node.name;
9633    if (!ts.isIdentifier(variable)) {
9634      return;
9635    }
9636
9637    const targetNode = TypeScriptLinter.findVariableChangeNodeInStruct(variable, structDecl);
9638    if (!targetNode) {
9639      return;
9640    }
9641
9642    const targetDecl = TsUtils.getDeclaration(this.tsTypeChecker.getSymbolAtLocation(targetNode));
9643    if (!targetDecl || !ts.isPropertyDeclaration(targetDecl)) {
9644      return;
9645    }
9646
9647    const decorators = ts.getDecorators(targetDecl);
9648    if (!decorators || decorators.length === 0) {
9649      return;
9650    }
9651
9652    const decorator = decorators[0];
9653    const decoratorName = TsUtils.getDecoratorName(decorator);
9654    if (decoratorName === PropDecoratorName.Prop) {
9655      this.incrementCounters(node, FaultID.PropNeedCallMethodForDeepCopy);
9656    }
9657  }
9658
9659  private static findVariableChangeNodeInStruct(
9660    variable: ts.Identifier,
9661    structDecl: ts.StructDeclaration
9662  ): ts.MemberName | undefined {
9663    let changeNode: ts.MemberName | undefined;
9664
9665    function traverse(node: ts.Node): void {
9666      if (changeNode) {
9667        return;
9668      }
9669
9670      if (ts.isPropertyAccessExpression(node)) {
9671        if (
9672          node.expression.kind === ts.SyntaxKind.ThisKeyword &&
9673          node.name.getText() === variable.getText() &&
9674          (ts.findAncestor(node, ts.isPostfixUnaryExpression) ||
9675            ts.findAncestor(node, ts.isPrefixUnaryExpression) ||
9676            ts.findAncestor(node, ts.isBinaryExpression))
9677        ) {
9678          changeNode = node.name;
9679        }
9680      }
9681
9682      ts.forEachChild(node, traverse);
9683    }
9684
9685    traverse(structDecl);
9686    return changeNode;
9687  }
9688
9689  private getIdentifierForAwaitExpr(awaitExpr: ts.AwaitExpression): IdentifierAndArguments {
9690    void this;
9691
9692    let ident: undefined | ts.Identifier;
9693    let args: ts.NodeArray<ts.Expression> | undefined;
9694
9695    const expr = awaitExpr.expression;
9696    if (ts.isCallExpression(expr)) {
9697      if (ts.isIdentifier(expr.expression)) {
9698        ident = expr.expression;
9699      }
9700
9701      if (ts.isPropertyAccessExpression(expr.expression)) {
9702        if (ts.isIdentifier(expr.expression.name)) {
9703          ident = expr.expression.name;
9704        }
9705      }
9706      args = expr.arguments;
9707    } else if (ts.isIdentifier(expr)) {
9708      ident = expr;
9709    }
9710
9711    return { ident, args };
9712  }
9713
9714  private handleAwaitExpression(awaitExpr: ts.AwaitExpression): void {
9715    if (!this.options.arkts2 || !this.useStatic) {
9716      return;
9717    }
9718    const { ident, args } = this.getIdentifierForAwaitExpr(awaitExpr);
9719    if (!ident) {
9720      return;
9721    }
9722
9723    if (!this.tsUtils.isJsImport(ident)) {
9724      return;
9725    }
9726
9727    const declaration = this.tsUtils.getDeclarationNode(ident);
9728    if (!declaration) {
9729      return;
9730    }
9731
9732    if (
9733      ts.isFunctionDeclaration(declaration) &&
9734      TsUtils.hasModifier(declaration.modifiers, ts.SyntaxKind.AsyncKeyword)
9735    ) {
9736      const autofix = this.autofixer?.fixAwaitJsCallExpression(ident, args);
9737      this.incrementCounters(awaitExpr, FaultID.NoAwaitJsPromise, autofix);
9738      return;
9739    }
9740
9741    if (ts.isMethodDeclaration(declaration) && TsUtils.hasModifier(declaration.modifiers, ts.SyntaxKind.AsyncKeyword)) {
9742      const autofix = this.autofixer?.fixAwaitJsMethodCallExpression(ident, args);
9743      this.incrementCounters(awaitExpr, FaultID.NoAwaitJsPromise, autofix);
9744      return;
9745    }
9746
9747    if (!ts.isVariableDeclaration(declaration)) {
9748      return;
9749    }
9750
9751    const type = this.tsTypeChecker.getTypeAtLocation(declaration);
9752    const typeString = this.tsTypeChecker.typeToString(type);
9753
9754    if (typeString.split('<')[0] !== 'Promise') {
9755      return;
9756    }
9757
9758    const autofix = this.autofixer?.fixAwaitJsPromise(ident);
9759    this.incrementCounters(awaitExpr, FaultID.NoAwaitJsPromise, autofix);
9760  }
9761
9762  private handleNotsLikeSmartTypeOnCallExpression(tsCallExpr: ts.CallExpression, callSignature: ts.Signature): void {
9763    if (!this.options.arkts2) {
9764      return;
9765    }
9766    const isContinue =
9767      ts.isCallExpression(tsCallExpr) &&
9768      ts.isIdentifier(tsCallExpr.expression) &&
9769      !ts.isReturnStatement(tsCallExpr.parent);
9770    if (!isContinue || !tsCallExpr.arguments) {
9771      return;
9772    }
9773    const declaration = callSignature.getDeclaration();
9774    if (!declaration || !ts.isFunctionDeclaration(declaration)) {
9775      return;
9776    }
9777    const parameterTypes = declaration.parameters?.map((param) => {
9778      const paramType = this.tsTypeChecker.getTypeAtLocation(param);
9779      return this.tsTypeChecker.typeToString(paramType);
9780    });
9781    tsCallExpr.arguments.forEach((arg, index) => {
9782      if (index >= parameterTypes.length) {
9783        return;
9784      }
9785      const expectedType = parameterTypes[index];
9786      const actualSym = this.tsTypeChecker.getSymbolAtLocation(arg);
9787      const decl = TsUtils.getDeclaration(actualSym);
9788      if (decl && ts.isParameter(decl) && decl.type) {
9789        const actualType = this.tsTypeChecker.getTypeFromTypeNode(decl.type);
9790        const actualTypeName = this.tsTypeChecker.typeToString(actualType);
9791        if (actualTypeName !== expectedType) {
9792          this.incrementCounters(arg, FaultID.NoTsLikeSmartType);
9793        }
9794      }
9795    });
9796  }
9797
9798  private handleNotsLikeSmartTypeOnAsExpression(tsAsExpr: ts.AsExpression): void {
9799    if (!this.options.arkts2) {
9800      return;
9801    }
9802    const asType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.type);
9803    const originType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression);
9804    const originTypeStr = this.tsTypeChecker.typeToString(originType);
9805    if (originTypeStr === 'never' && this.tsTypeChecker.typeToString(asType) !== originTypeStr) {
9806      this.incrementCounters(tsAsExpr, FaultID.NoTsLikeSmartType);
9807    }
9808  }
9809
9810  private handleAssignmentNotsLikeSmartType(tsBinaryExpr: ts.BinaryExpression): void {
9811    if (!this.options.arkts2) {
9812      return;
9813    }
9814
9815    if (this.isPriorityInThreadInfo(tsBinaryExpr)) {
9816      this.incrementCounters(tsBinaryExpr, FaultID.NoTsLikeSmartType);
9817    }
9818  }
9819
9820  private isPriorityInThreadInfo(node: ts.BinaryExpression): boolean {
9821    if (!ts.isBinaryExpression(node)) {
9822      return false;
9823    }
9824
9825    // Handle both regular assignment and 'as' type assertion
9826    let right: ts.Expression = ts.isAsExpression(node.right) ? node.right.expression : node.right;
9827    if (!ts.isPropertyAccessExpression(right)) {
9828        return false;
9829    }
9830
9831    const propertyName = right.name;
9832    if (!ts.isIdentifier(propertyName)) {
9833      return false;
9834    }
9835
9836    const object = right.expression;
9837    if (!ts.isIdentifier(object)) {
9838      return false;
9839    }
9840
9841    const symbol = this.tsTypeChecker.getSymbolAtLocation(object);
9842    if (!symbol) {
9843      return false;
9844    }
9845
9846    const type = this.tsTypeChecker.getTypeOfSymbolAtLocation(symbol, object);
9847    const typeString = this.tsTypeChecker.typeToString(type);
9848
9849    for (const [typeName, properties] of Object.entries(ERROR_TASKPOOL_PROP_LIST)) {
9850      if (typeString === typeName && properties.has(propertyName.text)) {
9851        return true;
9852      }
9853    }
9854
9855    return false;
9856  }
9857
9858  private handleNotsLikeSmartType(classDecl: ts.ClassDeclaration): void {
9859    if (!this.options.arkts2) {
9860      return;
9861    }
9862
9863    const className = classDecl.name?.getText();
9864    const { staticProps, instanceProps } = this.collectClassProperties(classDecl);
9865
9866    classDecl.members.forEach((member) => {
9867      if (!ts.isMethodDeclaration(member) || !member.body) {
9868        return;
9869      }
9870
9871      const methodReturnType = this.tsTypeChecker.getTypeAtLocation(member);
9872      this.checkMethodAndReturnStatements(member.body, className, methodReturnType, staticProps, instanceProps);
9873    });
9874  }
9875
9876  private checkMethodAndReturnStatements(
9877    body: ts.Block,
9878    className: string | undefined,
9879    methodReturnType: ts.Type,
9880    staticProps: Map<string, ts.Type>,
9881    instanceProps: Map<string, ts.Type>
9882  ): void {
9883    body.forEachChild((node) => {
9884      if (!ts.isReturnStatement(node) || !node.expression) {
9885        return;
9886      }
9887
9888      const isStaticPropertyAccess = (node: ts.Expression, className: string): boolean => {
9889        return (
9890          ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === className
9891        );
9892      };
9893      const isInstancePropertyAccess = (node: ts.Expression): boolean => {
9894        return ts.isPropertyAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword;
9895      };
9896
9897      if (className && isStaticPropertyAccess(node.expression, className)) {
9898        this.checkPropertyAccess(node, node.expression as ts.PropertyAccessExpression, staticProps, methodReturnType);
9899        return;
9900      }
9901
9902      if (isInstancePropertyAccess(node.expression)) {
9903        this.checkPropertyAccess(node, node.expression as ts.PropertyAccessExpression, instanceProps, methodReturnType);
9904      }
9905    });
9906  }
9907
9908  private checkPropertyAccess(
9909    returnNode: ts.ReturnStatement,
9910    propAccess: ts.PropertyAccessExpression,
9911    propsMap: Map<string, ts.Type>,
9912    methodReturnType: ts.Type
9913  ): void {
9914    const propName = propAccess.name.getText();
9915    const propType = propsMap.get(propName);
9916
9917    if (propType && this.isExactlySameType(propType, methodReturnType)) {
9918      return;
9919    }
9920
9921    this.incrementCounters(returnNode, FaultID.NoTsLikeSmartType);
9922  }
9923
9924  private collectClassProperties(classDecl: ts.ClassDeclaration): {
9925    staticProps: Map<string, ts.Type>;
9926    instanceProps: Map<string, ts.Type>;
9927  } {
9928    const result = {
9929      staticProps: new Map<string, ts.Type>(),
9930      instanceProps: new Map<string, ts.Type>()
9931    };
9932
9933    classDecl.members.forEach((member) => {
9934      if (!ts.isPropertyDeclaration(member)) {
9935        return;
9936      }
9937
9938      const propName = member.name.getText();
9939      const propType = this.tsTypeChecker.getTypeAtLocation(member);
9940      const isStatic = member.modifiers?.some((m) => {
9941        return m.kind === ts.SyntaxKind.StaticKeyword;
9942      });
9943
9944      if (isStatic) {
9945        result.staticProps.set(propName, propType);
9946      } else {
9947        result.instanceProps.set(propName, propType);
9948      }
9949    });
9950
9951    return result;
9952  }
9953
9954  private isExactlySameType(type1: ts.Type, type2: ts.Type): boolean {
9955    if (type2.getCallSignatures().length > 0) {
9956      const returnType = TsUtils.getFunctionReturnType(type2);
9957      return returnType ?
9958        this.tsTypeChecker.typeToString(type1) === this.tsTypeChecker.typeToString(returnType) :
9959        false;
9960    }
9961    return this.tsTypeChecker.typeToString(type1) === this.tsTypeChecker.typeToString(type2);
9962  }
9963
9964  private handleNumericBigintCompare(node: ts.BinaryExpression): void {
9965    if (!this.options.arkts2) {
9966      return;
9967    }
9968    const leftType = this.tsTypeChecker.getTypeAtLocation(node.left);
9969    const rightType = this.tsTypeChecker.getTypeAtLocation(node.right);
9970
9971    const isBigInt = (type: ts.Type): boolean => {
9972      return (type.flags & ts.TypeFlags.BigInt) !== 0 || (type.flags & ts.TypeFlags.BigIntLiteral) !== 0;
9973    };
9974    const isNumber = (type: ts.Type): boolean => {
9975      return (type.flags & ts.TypeFlags.Number) !== 0 || (type.flags & ts.TypeFlags.NumberLiteral) !== 0;
9976    };
9977
9978    const isBigIntAndNumberOperand =
9979      isNumber(leftType) && isBigInt(rightType) || isBigInt(leftType) && isNumber(rightType);
9980    if (isBigIntAndNumberOperand) {
9981      this.incrementCounters(node, FaultID.NumericBigintCompare);
9982    }
9983  }
9984
9985  private handleBigIntLiteral(node: ts.BigIntLiteral): void {
9986    if (!this.options.arkts2) {
9987      return;
9988    }
9989    const literalText = node.getText();
9990
9991    if ((/^0[box]/i).test(literalText)) {
9992      this.incrementCounters(node, FaultID.NondecimalBigint);
9993    }
9994  }
9995
9996  private handleStructDeclarationForLayout(node: ts.StructDeclaration): void {
9997    if (!this.options.arkts2) {
9998      return;
9999    }
10000
10001    if (!node.name) {
10002      return;
10003    }
10004
10005    let hasTargetFunc = false;
10006
10007    const members = node.members;
10008    for (const member of members) {
10009      if (!ts.isMethodDeclaration(member)) {
10010        continue;
10011      }
10012
10013      if (customLayoutFunctionName.has(member.name.getText())) {
10014        hasTargetFunc = true;
10015        break;
10016      }
10017    }
10018
10019    if (!hasTargetFunc) {
10020      return;
10021    }
10022
10023    const decorators = ts.getDecorators(node);
10024    if (decorators) {
10025      for (const decorator of decorators) {
10026        const decoratorName = TsUtils.getDecoratorName(decorator);
10027        if (decoratorName && decoratorName === CustomDecoratorName.CustomLayout) {
10028          return;
10029        }
10030      }
10031    }
10032
10033    const autofix = this.autofixer?.fixCustomLayout(node);
10034    const name = node.name.getText();
10035    const errorMsg =
10036      `The Custom component "${name}" with custom layout capability needs to add the "@CustomLayout" decorator ` +
10037      '(arkui-custom-layout-need-add-decorator)';
10038    this.incrementCounters(node.name, FaultID.CustomLayoutNeedAddDecorator, autofix, errorMsg);
10039  }
10040
10041  private handleArkTSPropertyAccess(expr: ts.BinaryExpression): void {
10042    if (!this.useStatic || !this.options.arkts2 || !TypeScriptLinter.isBinaryOperations(expr.operatorToken.kind)) {
10043      return;
10044    }
10045
10046    const processExpression = (expr: ts.Expression): void => {
10047      const symbol = this.tsUtils.trueSymbolAtLocation(expr);
10048      if (this.isJsFileSymbol(symbol) || this.isJsFileExpression(expr)) {
10049        const autofix = this.autofixer?.fixInteropOperators(expr);
10050        this.incrementCounters(expr, FaultID.BinaryOperations, autofix);
10051      }
10052    };
10053
10054    processExpression(expr.left);
10055    processExpression(expr.right);
10056  }
10057
10058  private static isBinaryOperations(kind: ts.SyntaxKind): boolean {
10059    const binaryOperators: ts.SyntaxKind[] = [
10060      ts.SyntaxKind.PlusToken,
10061      ts.SyntaxKind.MinusToken,
10062      ts.SyntaxKind.AsteriskToken,
10063      ts.SyntaxKind.SlashToken,
10064      ts.SyntaxKind.PercentToken,
10065      ts.SyntaxKind.AsteriskAsteriskToken
10066    ];
10067    return binaryOperators.includes(kind);
10068  }
10069
10070  private handleNumericLiteral(node: ts.Node): void {
10071    if (!this.options.arkts2 || !ts.isNumericLiteral(node)) {
10072      return;
10073    }
10074    const isInElementAccessExpression = (node: ts.NumericLiteral): boolean => {
10075      for (let parent = node.parent; parent; parent = parent.parent) {
10076        if (ts.isElementAccessExpression(parent)) {
10077          return true;
10078        }
10079      }
10080      return false;
10081    };
10082    const isStandardFloatFormat = (): boolean => {
10083      const text = node.getText();
10084      return (/\.\d*0+$/).test(text);
10085    };
10086    const isNoNeedFix =
10087      isInElementAccessExpression(node) ||
10088      'name' in node.parent && node.parent.name === node ||
10089      isStandardFloatFormat();
10090    if (isNoNeedFix) {
10091      return;
10092    }
10093    const value = parseFloat(node.text);
10094    const nodeText = node.getText();
10095    const hasScientificOrRadixNotation = (/[a-zA-Z]/).test(nodeText);
10096    const isIntegerWithoutZero = Number.isInteger(value) && !nodeText.endsWith('.0');
10097    if (isIntegerWithoutZero && !hasScientificOrRadixNotation) {
10098      const autofix = this.autofixer?.fixNumericLiteralIntToNumber(node);
10099      this.incrementCounters(node, FaultID.NumericSemantics, autofix);
10100    }
10101  }
10102
10103  private checkArrayUsageWithoutBound(accessExpr: ts.ElementAccessExpression): void {
10104    if (!this.options.arkts2 || !this.useStatic) {
10105      return;
10106    }
10107
10108    const arrayAccessInfo = this.getArrayAccessInfo(accessExpr);
10109    if (!arrayAccessInfo) {
10110      const accessArgument = accessExpr.argumentExpression;
10111      if (TypeScriptLinter.isFunctionCall(accessArgument)) {
10112        this.incrementCounters(accessExpr, FaultID.RuntimeArrayCheck);
10113      }
10114      return;
10115    }
10116
10117    const { arrayIdent } = arrayAccessInfo;
10118    const arraySym = this.tsUtils.trueSymbolAtLocation(arrayIdent);
10119    if (!arraySym) {
10120      return;
10121    }
10122
10123    const arrayDecl = TypeScriptLinter.findArrayDeclaration(arraySym);
10124    if (arrayDecl && TypeScriptLinter.isArrayCreatedWithOtherArrayLength(arrayDecl)) {
10125      return;
10126    }
10127
10128    const indexExpr = accessExpr.argumentExpression;
10129    const loopVarName = ts.isIdentifier(indexExpr) ? indexExpr.text : undefined;
10130
10131    const { isInSafeContext, isValidBoundCheck, isVarModifiedBeforeAccess } = this.analyzeSafeContext(
10132      accessExpr,
10133      loopVarName,
10134      arraySym
10135    );
10136
10137    if (isInSafeContext) {
10138      if (!isValidBoundCheck || isVarModifiedBeforeAccess) {
10139        this.incrementCounters(arrayIdent.parent, FaultID.RuntimeArrayCheck);
10140      }
10141    } else {
10142      this.incrementCounters(arrayIdent.parent, FaultID.RuntimeArrayCheck);
10143    }
10144  }
10145
10146  private analyzeSafeContext(
10147    accessExpr: ts.ElementAccessExpression,
10148    loopVarName: string | undefined,
10149    arraySym: ts.Symbol
10150  ): { isInSafeContext: boolean; isValidBoundCheck: boolean; isVarModifiedBeforeAccess: boolean } {
10151    const context = TypeScriptLinter.findSafeContext(accessExpr);
10152    if (!context) {
10153      return { isInSafeContext: false, isValidBoundCheck: false, isVarModifiedBeforeAccess: false };
10154    }
10155
10156    return this.analyzeContextSafety(context, accessExpr, loopVarName, arraySym);
10157  }
10158
10159  static findSafeContext(
10160    accessExpr: ts.ElementAccessExpression
10161  ): { node: ts.ForStatement | ts.WhileStatement | ts.IfStatement } | void {
10162    let currentNode: ts.Node | undefined = accessExpr;
10163
10164    while (currentNode) {
10165      if (ts.isForStatement(currentNode) || ts.isWhileStatement(currentNode) || ts.isIfStatement(currentNode)) {
10166        return { node: currentNode };
10167      }
10168      currentNode = currentNode.parent;
10169    }
10170
10171    return undefined;
10172  }
10173
10174  private analyzeContextSafety(
10175    context: { node: ts.ForStatement | ts.WhileStatement | ts.IfStatement },
10176    accessExpr: ts.ElementAccessExpression,
10177    loopVarName: string | undefined,
10178    arraySym: ts.Symbol
10179  ): { isInSafeContext: boolean; isValidBoundCheck: boolean; isVarModifiedBeforeAccess: boolean } {
10180    const { node } = context;
10181
10182    if (!loopVarName) {
10183      return {
10184        isInSafeContext: true,
10185        isValidBoundCheck: false,
10186        isVarModifiedBeforeAccess: false
10187      };
10188    }
10189
10190    const analysis = this.analyzeStatementType(node, accessExpr, loopVarName, arraySym);
10191
10192    return {
10193      isInSafeContext: true,
10194      isValidBoundCheck: analysis.isValidBoundCheck,
10195      isVarModifiedBeforeAccess: analysis.isVarModifiedBeforeAccess
10196    };
10197  }
10198
10199  private analyzeStatementType(
10200    node: ts.ForStatement | ts.WhileStatement | ts.IfStatement,
10201    accessExpr: ts.ElementAccessExpression,
10202    loopVarName: string,
10203    arraySym: ts.Symbol
10204  ): { isValidBoundCheck: boolean; isVarModifiedBeforeAccess: boolean } {
10205    switch (node.kind) {
10206      case ts.SyntaxKind.ForStatement:
10207        return this.analyzeForStatement(node, accessExpr, loopVarName, arraySym);
10208      case ts.SyntaxKind.WhileStatement:
10209        return this.analyzeWhileStatement(node, accessExpr, loopVarName, arraySym);
10210      case ts.SyntaxKind.IfStatement:
10211        return this.analyzeIfStatement(node, accessExpr, loopVarName, arraySym);
10212      default:
10213        return { isValidBoundCheck: false, isVarModifiedBeforeAccess: false };
10214    }
10215  }
10216
10217  private analyzeForStatement(
10218    forNode: ts.ForStatement,
10219    accessExpr: ts.ElementAccessExpression,
10220    loopVarName: string,
10221    arraySym: ts.Symbol
10222  ): { isValidBoundCheck: boolean; isVarModifiedBeforeAccess: boolean } {
10223    const isValidBoundCheck = forNode.condition ?
10224      this.checkBoundCondition(forNode.condition, loopVarName, arraySym) :
10225      false;
10226
10227    const isVarModifiedBeforeAccess = forNode.statement ?
10228      TypeScriptLinter.checkVarModifiedBeforeNode(forNode.statement, accessExpr, loopVarName) :
10229      false;
10230
10231    return { isValidBoundCheck, isVarModifiedBeforeAccess };
10232  }
10233
10234  private analyzeWhileStatement(
10235    whileNode: ts.WhileStatement,
10236    accessExpr: ts.ElementAccessExpression,
10237    loopVarName: string,
10238    arraySym: ts.Symbol
10239  ): { isValidBoundCheck: boolean; isVarModifiedBeforeAccess: boolean } {
10240    const isValidBoundCheck = whileNode.expression ?
10241      this.checkBoundCondition(whileNode.expression, loopVarName, arraySym) :
10242      false;
10243
10244    const isVarModifiedBeforeAccess = whileNode.statement ?
10245      TypeScriptLinter.checkVarModifiedBeforeNode(whileNode.statement, accessExpr, loopVarName) :
10246      false;
10247
10248    return { isValidBoundCheck, isVarModifiedBeforeAccess };
10249  }
10250
10251  private analyzeIfStatement(
10252    ifNode: ts.IfStatement,
10253    accessExpr: ts.ElementAccessExpression,
10254    loopVarName: string,
10255    arraySym: ts.Symbol
10256  ): { isValidBoundCheck: boolean; isVarModifiedBeforeAccess: boolean } {
10257    const isValidBoundCheck = ifNode.expression ?
10258      this.checkBoundCondition(ifNode.expression, loopVarName, arraySym) :
10259      false;
10260
10261    let isVarModifiedBeforeAccess = false;
10262    const statementBlock = ts.isBlock(ifNode.thenStatement) ? ifNode.thenStatement : undefined;
10263    if (statementBlock) {
10264      isVarModifiedBeforeAccess = TypeScriptLinter.checkVarModifiedBeforeNode(statementBlock, accessExpr, loopVarName);
10265    }
10266
10267    return { isValidBoundCheck, isVarModifiedBeforeAccess };
10268  }
10269
10270  private checkBoundCondition(condition: ts.Expression, varName: string, arraySym: ts.Symbol): boolean {
10271    if (ts.isBinaryExpression(condition)) {
10272      const { left, right, operatorToken } = condition;
10273
10274      if (this.checkVarLessThanArrayLength(left, right, operatorToken, varName, arraySym)) {
10275        return true;
10276      }
10277
10278      if (this.checkArrayLengthGreaterThanVar(left, right, operatorToken, varName, arraySym)) {
10279        return true;
10280      }
10281
10282      if (ts.isIdentifier(left) && left.text === varName && ts.isNumericLiteral(right)) {
10283        const value = parseFloat(right.text);
10284        if (
10285          operatorToken.kind === ts.SyntaxKind.GreaterThanEqualsToken && value <= 0 ||
10286          operatorToken.kind === ts.SyntaxKind.GreaterThanToken && value < 0
10287        ) {
10288          return true;
10289        }
10290      }
10291
10292      return this.checkBoundCondition(left, varName, arraySym) || this.checkBoundCondition(right, varName, arraySym);
10293    }
10294
10295    return false;
10296  }
10297
10298  private checkArrayLengthGreaterThanVar(
10299    left: ts.Expression,
10300    right: ts.Expression,
10301    operatorToken: ts.Token<ts.BinaryOperator>,
10302    varName: string,
10303    arraySym: ts.Symbol
10304  ): boolean {
10305    if (
10306      ts.isPropertyAccessExpression(left) &&
10307      left.name.text === 'length' &&
10308      ts.isIdentifier(right) &&
10309      right.text === varName
10310    ) {
10311      const leftArraySym = this.tsUtils.trueSymbolAtLocation(left.expression);
10312      if (leftArraySym === arraySym) {
10313        return (
10314          operatorToken.kind === ts.SyntaxKind.GreaterThanToken ||
10315          operatorToken.kind === ts.SyntaxKind.GreaterThanEqualsToken
10316        );
10317      }
10318    }
10319    return false;
10320  }
10321
10322  private checkVarLessThanArrayLength(
10323    left: ts.Expression,
10324    right: ts.Expression,
10325    operatorToken: ts.Token<ts.BinaryOperator>,
10326    varName: string,
10327    arraySym: ts.Symbol
10328  ): boolean {
10329    return (
10330      ts.isIdentifier(left) &&
10331      left.text === varName &&
10332      ts.isPropertyAccessExpression(right) &&
10333      right.name.text === 'length' &&
10334      (operatorToken.kind === ts.SyntaxKind.LessThanToken ||
10335        operatorToken.kind === ts.SyntaxKind.LessThanEqualsToken) &&
10336      this.tsUtils.trueSymbolAtLocation(right.expression) === arraySym
10337    );
10338  }
10339
10340  private static traverseNodesUntilTarget(
10341    node: ts.Node,
10342    targetNode: ts.Node,
10343    varName: string,
10344    scopeStack: { shadowed: boolean; localVars: Set<string> }[],
10345    state: { targetFound: boolean; modified: boolean }
10346  ): void {
10347    if (node === targetNode) {
10348      state.targetFound = true;
10349      return;
10350    }
10351
10352    if (state.targetFound) {
10353      return;
10354    }
10355
10356    const newScope = this.handleNewScope(node, scopeStack);
10357
10358    TypeScriptLinter.getVariablesFromScope(node, varName, scopeStack);
10359
10360    if (this.isVariableModified(node, varName, scopeStack)) {
10361      state.modified = true;
10362    }
10363
10364    ts.forEachChild(node, (child) => {
10365      this.traverseNodesUntilTarget(child, targetNode, varName, scopeStack, state);
10366    });
10367
10368    if (newScope) {
10369      scopeStack.pop();
10370    }
10371  }
10372
10373  private static handleNewScope(
10374    node: ts.Node,
10375    scopeStack: { shadowed: boolean; localVars: Set<string> }[]
10376  ): { shadowed: boolean; localVars: Set<string> } | null {
10377    if (ts.isBlock(node) || ts.isFunctionLike(node) || ts.isCatchClause(node)) {
10378      const parentScope = scopeStack[scopeStack.length - 1];
10379      const newScope = {
10380        shadowed: parentScope.shadowed,
10381        localVars: new Set<string>()
10382      };
10383      scopeStack.push(newScope);
10384      return newScope;
10385    }
10386    return null;
10387  }
10388
10389  static getVariablesFromScope(
10390    node: ts.Node,
10391    varName: string,
10392    scopeStack: { shadowed: boolean; localVars: Set<string> }[]
10393  ): void {
10394    if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === varName) {
10395      const parent = node.parent;
10396      if (
10397        ts.isVariableDeclarationList(parent) &&
10398        (parent.flags & ts.NodeFlags.Let || parent.flags & ts.NodeFlags.Const)
10399      ) {
10400        scopeStack[scopeStack.length - 1].localVars.add(varName);
10401      }
10402    }
10403
10404    if (ts.isParameter(node) && ts.isIdentifier(node.name) && node.name.text === varName) {
10405      scopeStack[scopeStack.length - 1].localVars.add(varName);
10406    }
10407  }
10408
10409  private static isVariableModified(
10410    node: ts.Node,
10411    varName: string,
10412    scopeStack: { shadowed: boolean; localVars: Set<string> }[]
10413  ): boolean {
10414    if (!ts.isBinaryExpression(node) || node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
10415      return false;
10416    }
10417
10418    if (!ts.isIdentifier(node.left) || node.left.text !== varName) {
10419      return false;
10420    }
10421
10422    for (let i = scopeStack.length - 1; i >= 0; i--) {
10423      if (scopeStack[i].localVars.has(varName)) {
10424        return false;
10425      }
10426    }
10427
10428    return true;
10429  }
10430
10431  static checkVarModifiedBeforeNode(container: ts.Node, targetNode: ts.Node, varName: string): boolean {
10432    const scopeStack: { shadowed: boolean; localVars: Set<string> }[] = [];
10433    scopeStack.push({ shadowed: false, localVars: new Set() });
10434
10435    const state = {
10436      targetFound: false,
10437      modified: false
10438    };
10439
10440    this.traverseNodesUntilTarget(container, targetNode, varName, scopeStack, state);
10441    return state.modified;
10442  }
10443
10444  static isFunctionCall(node: ts.Node): boolean {
10445    return ts.isCallExpression(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node);
10446  }
10447
10448  private getArrayAccessInfo(expr: ts.ElementAccessExpression): false | ArrayAccess {
10449    if (!ts.isIdentifier(expr.expression)) {
10450      return false;
10451    }
10452    const baseType = this.tsTypeChecker.getTypeAtLocation(expr.expression);
10453    if (!this.tsUtils.isArray(baseType)) {
10454      return false;
10455    }
10456    const accessArgument = expr.argumentExpression;
10457
10458    TypeScriptLinter.isFunctionCall(accessArgument);
10459
10460    const checkNumericType = (node: ts.Node): boolean => {
10461      const argType = this.tsTypeChecker.getTypeAtLocation(node);
10462      return (
10463        (argType.flags & ts.TypeFlags.NumberLike) !== 0 ||
10464        argType.isUnionOrIntersection() &&
10465          argType.types.some((t) => {
10466            return t.flags & ts.TypeFlags.NumberLike;
10467          })
10468      );
10469    };
10470
10471    const isEnumMember = (node: ts.Node): boolean => {
10472      if (ts.isPropertyAccessExpression(node)) {
10473        const symbol = this.tsUtils.trueSymbolAtLocation(node);
10474        return !!symbol && (symbol.flags & ts.SymbolFlags.EnumMember) !== 0;
10475      }
10476      return false;
10477    };
10478
10479    if (TypeScriptLinter.isFunctionCall(accessArgument)) {
10480      return false;
10481    }
10482
10483    if (checkNumericType(accessArgument) || isEnumMember(accessArgument)) {
10484      return {
10485        pos: expr.getEnd(),
10486        accessingIdentifier: accessArgument,
10487        arrayIdent: expr.expression
10488      };
10489    }
10490
10491    return false;
10492  }
10493
10494  static isArrayCreatedWithOtherArrayLength(decl: ts.VariableDeclaration): boolean {
10495    if (!decl.initializer || !ts.isNewExpression(decl.initializer)) {
10496      return false;
10497    }
10498
10499    const newExpr = decl.initializer;
10500    return (
10501      newExpr.arguments?.some((arg) => {
10502        return ts.isPropertyAccessExpression(arg) && arg.name.text === 'length';
10503      }) ?? false
10504    );
10505  }
10506
10507  static findArrayDeclaration(sym: ts.Symbol): ts.VariableDeclaration | undefined {
10508    const decls = sym.getDeclarations();
10509    if (!decls) {
10510      return undefined;
10511    }
10512
10513    for (const decl of decls) {
10514      if (ts.isVariableDeclaration(decl)) {
10515        return decl;
10516      }
10517    }
10518    return undefined;
10519  }
10520}
10521