• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as path from 'node:path';
17import * as ts from 'typescript';
18import { cookBookTag } from './CookBookMsg';
19import { faultsAttrs } from './FaultAttrs';
20import { faultDesc } from './FaultDesc';
21import { Logger } from './Logger';
22import type { ProblemInfo } from './ProblemInfo';
23import { ProblemSeverity } from './ProblemSeverity';
24import { FaultID } from './Problems';
25import { LinterConfig } from './TypeScriptLinterConfig';
26import { cookBookRefToFixTitle } from './autofixes/AutofixTitles';
27import type { Autofix } from './autofixes/Autofixer';
28import { Autofixer } from './autofixes/Autofixer';
29import { SYMBOL, SYMBOL_CONSTRUCTOR, TsUtils } from './utils/TsUtils';
30import { FUNCTION_HAS_NO_RETURN_ERROR_CODE } from './utils/consts/FunctionHasNoReturnErrorCode';
31import { LIMITED_STANDARD_UTILITY_TYPES } from './utils/consts/LimitedStandardUtilityTypes';
32import {
33  NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS,
34  NON_INITIALIZABLE_PROPERTY_DECORATORS,
35  NON_INITIALIZABLE_PROPERTY_DECORATORS_TSC
36} from './utils/consts/NonInitializablePropertyDecorators';
37import { NON_RETURN_FUNCTION_DECORATORS } from './utils/consts/NonReturnFunctionDecorators';
38import { PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE } from './utils/consts/PropertyHasNoInitializerErrorCode';
39import {
40  SENDABLE_DECORATOR,
41  SENDABLE_DECORATOR_NODES,
42  SENDABLE_FUNCTION_UNSUPPORTED_STAGES_IN_API12,
43  SENDBALE_FUNCTION_START_VERSION
44} from './utils/consts/SendableAPI';
45import { DEFAULT_COMPATIBLE_SDK_VERSION, DEFAULT_COMPATIBLE_SDK_VERSION_STAGE } from './utils/consts/VersionInfo';
46import { forEachNodeInSubtree } from './utils/functions/ForEachNodeInSubtree';
47import { hasPredecessor } from './utils/functions/HasPredecessor';
48import { isStdLibrarySymbol, isStdLibraryType } from './utils/functions/IsStdLibrary';
49import { isStruct, isStructDeclaration } from './utils/functions/IsStruct';
50import {
51  LibraryTypeCallDiagnosticChecker,
52  ErrorType as DiagnosticCheckerErrorType
53} from './utils/functions/LibraryTypeCallDiagnosticChecker';
54import {
55  ALLOWED_STD_SYMBOL_API,
56  LIMITED_STD_API,
57  LIMITED_STD_GLOBAL_API,
58  LIMITED_STD_OBJECT_API,
59  LIMITED_STD_PROXYHANDLER_API,
60  LIMITED_STD_REFLECT_API
61} from './utils/consts/LimitedStdAPI';
62import { SupportedStdCallApiChecker } from './utils/functions/SupportedStdCallAPI';
63import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext';
64import { isAssignmentOperator } from './utils/functions/isAssignmentOperator';
65import { StdClassVarDecls } from './utils/consts/StdClassVariableDeclarations';
66import type { LinterOptions } from './LinterOptions';
67
68export class TypeScriptLinter {
69  totalVisitedNodes: number = 0;
70  nodeCounters: number[] = [];
71  lineCounters: number[] = [];
72
73  totalErrorLines: number = 0;
74  errorLineNumbersString: string = '';
75  totalWarningLines: number = 0;
76  warningLineNumbersString: string = '';
77
78  problemsInfos: ProblemInfo[] = [];
79
80  tsUtils: TsUtils;
81
82  currentErrorLine: number;
83  currentWarningLine: number;
84  supportedStdCallApiChecker: SupportedStdCallApiChecker;
85
86  autofixer: Autofixer | undefined;
87  private fileExportDeclCaches: Set<ts.Node> | undefined;
88
89  private sourceFile?: ts.SourceFile;
90  private readonly compatibleSdkVersion: number;
91  private readonly compatibleSdkVersionStage: string;
92  private static sharedModulesCache: Map<string, boolean>;
93
94  static initGlobals(): void {
95    TypeScriptLinter.sharedModulesCache = new Map<string, boolean>();
96  }
97
98  private initEtsHandlers(): void {
99
100    /*
101     * some syntax elements are ArkTs-specific and are only implemented inside patched
102     * compiler, so we initialize those handlers if corresponding properties do exist
103     */
104    const etsComponentExpression: ts.SyntaxKind | undefined = ts.SyntaxKind.EtsComponentExpression;
105    if (etsComponentExpression) {
106      this.handlersMap.set(etsComponentExpression, this.handleEtsComponentExpression);
107    }
108  }
109
110  private initCounters(): void {
111    for (let i = 0; i < FaultID.LAST_ID; i++) {
112      this.nodeCounters[i] = 0;
113      this.lineCounters[i] = 0;
114    }
115  }
116
117  constructor(
118    private readonly tsTypeChecker: ts.TypeChecker,
119    readonly options: LinterOptions,
120    private readonly tscStrictDiagnostics?: Map<string, ts.Diagnostic[]>
121  ) {
122    this.tsUtils = new TsUtils(this.tsTypeChecker, options);
123    this.currentErrorLine = 0;
124    this.currentWarningLine = 0;
125    this.supportedStdCallApiChecker = new SupportedStdCallApiChecker(this.tsUtils, this.tsTypeChecker);
126    this.compatibleSdkVersion = options.compatibleSdkVersion || DEFAULT_COMPATIBLE_SDK_VERSION;
127    this.compatibleSdkVersionStage = options.compatibleSdkVersionStage || DEFAULT_COMPATIBLE_SDK_VERSION_STAGE;
128    this.initEtsHandlers();
129    this.initCounters();
130  }
131
132  readonly handlersMap = new Map([
133    [ts.SyntaxKind.ObjectLiteralExpression, this.handleObjectLiteralExpression],
134    [ts.SyntaxKind.ArrayLiteralExpression, this.handleArrayLiteralExpression],
135    [ts.SyntaxKind.Parameter, this.handleParameter],
136    [ts.SyntaxKind.EnumDeclaration, this.handleEnumDeclaration],
137    [ts.SyntaxKind.InterfaceDeclaration, this.handleInterfaceDeclaration],
138    [ts.SyntaxKind.ThrowStatement, this.handleThrowStatement],
139    [ts.SyntaxKind.ImportClause, this.handleImportClause],
140    [ts.SyntaxKind.ForStatement, this.handleForStatement],
141    [ts.SyntaxKind.ForInStatement, this.handleForInStatement],
142    [ts.SyntaxKind.ForOfStatement, this.handleForOfStatement],
143    [ts.SyntaxKind.ImportDeclaration, this.handleImportDeclaration],
144    [ts.SyntaxKind.PropertyAccessExpression, this.handlePropertyAccessExpression],
145    [ts.SyntaxKind.PropertyDeclaration, this.handlePropertyDeclaration],
146    [ts.SyntaxKind.PropertyAssignment, this.handlePropertyAssignment],
147    [ts.SyntaxKind.PropertySignature, this.handlePropertySignature],
148    [ts.SyntaxKind.FunctionExpression, this.handleFunctionExpression],
149    [ts.SyntaxKind.ArrowFunction, this.handleArrowFunction],
150    [ts.SyntaxKind.CatchClause, this.handleCatchClause],
151    [ts.SyntaxKind.FunctionDeclaration, this.handleFunctionDeclaration],
152    [ts.SyntaxKind.PrefixUnaryExpression, this.handlePrefixUnaryExpression],
153    [ts.SyntaxKind.BinaryExpression, this.handleBinaryExpression],
154    [ts.SyntaxKind.VariableDeclarationList, this.handleVariableDeclarationList],
155    [ts.SyntaxKind.VariableDeclaration, this.handleVariableDeclaration],
156    [ts.SyntaxKind.ClassDeclaration, this.handleClassDeclaration],
157    [ts.SyntaxKind.ModuleDeclaration, this.handleModuleDeclaration],
158    [ts.SyntaxKind.TypeAliasDeclaration, this.handleTypeAliasDeclaration],
159    [ts.SyntaxKind.ImportSpecifier, this.handleImportSpecifier],
160    [ts.SyntaxKind.NamespaceImport, this.handleNamespaceImport],
161    [ts.SyntaxKind.TypeAssertionExpression, this.handleTypeAssertionExpression],
162    [ts.SyntaxKind.MethodDeclaration, this.handleMethodDeclaration],
163    [ts.SyntaxKind.MethodSignature, this.handleMethodSignature],
164    [ts.SyntaxKind.ClassStaticBlockDeclaration, this.handleClassStaticBlockDeclaration],
165    [ts.SyntaxKind.Identifier, this.handleIdentifier],
166    [ts.SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression],
167    [ts.SyntaxKind.EnumMember, this.handleEnumMember],
168    [ts.SyntaxKind.TypeReference, this.handleTypeReference],
169    [ts.SyntaxKind.ExportAssignment, this.handleExportAssignment],
170    [ts.SyntaxKind.CallExpression, this.handleCallExpression],
171    [ts.SyntaxKind.MetaProperty, this.handleMetaProperty],
172    [ts.SyntaxKind.NewExpression, this.handleNewExpression],
173    [ts.SyntaxKind.AsExpression, this.handleAsExpression],
174    [ts.SyntaxKind.SpreadElement, this.handleSpreadOp],
175    [ts.SyntaxKind.SpreadAssignment, this.handleSpreadOp],
176    [ts.SyntaxKind.GetAccessor, this.handleGetAccessor],
177    [ts.SyntaxKind.SetAccessor, this.handleSetAccessor],
178    [ts.SyntaxKind.ConstructSignature, this.handleConstructSignature],
179    [ts.SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments],
180    [ts.SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName],
181    [ts.SyntaxKind.Constructor, this.handleConstructorDeclaration],
182    [ts.SyntaxKind.PrivateIdentifier, this.handlePrivateIdentifier],
183    [ts.SyntaxKind.IndexSignature, this.handleIndexSignature],
184    [ts.SyntaxKind.TypeLiteral, this.handleTypeLiteral],
185    [ts.SyntaxKind.ExportKeyword, this.handleExportKeyword],
186    [ts.SyntaxKind.ExportDeclaration, this.handleExportDeclaration],
187    [ts.SyntaxKind.ReturnStatement, this.handleReturnStatement],
188    [ts.SyntaxKind.Decorator, this.handleDecorator],
189    [ts.SyntaxKind.ImportType, this.handleImportType]
190  ]);
191
192  private getLineAndCharacterOfNode(node: ts.Node | ts.CommentRange): ts.LineAndCharacter {
193    const startPos = TsUtils.getStartPos(node);
194    const { line, character } = this.sourceFile!.getLineAndCharacterOfPosition(startPos);
195    // TSC counts lines and columns from zero
196    return { line: line + 1, character: character + 1 };
197  }
198
199  incrementCounters(node: ts.Node | ts.CommentRange, faultId: number, autofix?: Autofix[]): void {
200    this.nodeCounters[faultId]++;
201    const { line, character } = this.getLineAndCharacterOfNode(node);
202    if (this.options.ideMode) {
203      this.incrementCountersIdeMode(node, faultId, autofix);
204    } else {
205      const faultDescr = faultDesc[faultId];
206      const faultType = LinterConfig.tsSyntaxKindNames[node.kind];
207      Logger.info(
208        `Warning: ${this.sourceFile!.fileName} (${line}, ${character}): ${faultDescr ? faultDescr : faultType}`
209      );
210    }
211    this.lineCounters[faultId]++;
212    switch (faultsAttrs[faultId].severity) {
213      case ProblemSeverity.ERROR: {
214        this.currentErrorLine = line;
215        ++this.totalErrorLines;
216        this.errorLineNumbersString += line + ', ';
217        break;
218      }
219      case ProblemSeverity.WARNING: {
220        if (line === this.currentWarningLine) {
221          break;
222        }
223        this.currentWarningLine = line;
224        ++this.totalWarningLines;
225        this.warningLineNumbersString += line + ', ';
226        break;
227      }
228      default:
229    }
230  }
231
232  private incrementCountersIdeMode(node: ts.Node | ts.CommentRange, faultId: number, autofix?: Autofix[]): void {
233    if (!this.options.ideMode) {
234      return;
235    }
236    const [startOffset, endOffset] = TsUtils.getHighlightRange(node, faultId);
237    const startPos = this.sourceFile!.getLineAndCharacterOfPosition(startOffset);
238    const endPos = this.sourceFile!.getLineAndCharacterOfPosition(endOffset);
239
240    const faultDescr = faultDesc[faultId];
241    const faultType = LinterConfig.tsSyntaxKindNames[node.kind];
242
243    const cookBookMsgNum = faultsAttrs[faultId] ? faultsAttrs[faultId].cookBookRef : 0;
244    const cookBookTg = cookBookTag[cookBookMsgNum];
245    const severity = faultsAttrs[faultId]?.severity ?? ProblemSeverity.ERROR;
246    const isMsgNumValid = cookBookMsgNum > 0;
247    const badNodeInfo: ProblemInfo = {
248      line: startPos.line + 1,
249      column: startPos.character + 1,
250      endLine: endPos.line + 1,
251      endColumn: endPos.character + 1,
252      start: startOffset,
253      end: endOffset,
254      type: faultType,
255      severity: severity,
256      problem: FaultID[faultId],
257      suggest: '',
258      // eslint-disable-next-line no-nested-ternary
259      rule: isMsgNumValid && cookBookTg !== '' ? cookBookTg : faultDescr ? faultDescr : faultType,
260      ruleTag: cookBookMsgNum,
261      autofix: autofix,
262      autofixTitle: isMsgNumValid && autofix !== undefined ? cookBookRefToFixTitle.get(cookBookMsgNum) : undefined
263    };
264    this.problemsInfos.push(badNodeInfo);
265    // problems with autofixes might be collected separately
266    if (this.options.reportAutofixCb && badNodeInfo.autofix) {
267      this.options.reportAutofixCb(badNodeInfo);
268    }
269  }
270
271  private visitSourceFile(sf: ts.SourceFile): void {
272    const callback = (node: ts.Node): void => {
273      this.totalVisitedNodes++;
274      if (isStructDeclaration(node)) {
275        // early exit via exception if cancellation was requested
276        this.options.cancellationToken?.throwIfCancellationRequested();
277      }
278      const incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind);
279      if (incrementedType !== undefined) {
280        this.incrementCounters(node, incrementedType);
281      } else {
282        const handler = this.handlersMap.get(node.kind);
283        if (handler !== undefined) {
284
285          /*
286           * possibly requested cancellation will be checked in a limited number of handlers
287           * checked nodes are selected as construct nodes, similar to how TSC does
288           */
289          handler.call(this, node);
290        }
291      }
292    };
293    const stopCondition = (node: ts.Node): boolean => {
294      if (!node) {
295        return true;
296      }
297      if (this.options.incrementalLintInfo?.shouldSkipCheck(node)) {
298        return true;
299      }
300      // Skip synthetic constructor in Struct declaration.
301      if (node.parent && isStructDeclaration(node.parent) && ts.isConstructorDeclaration(node)) {
302        return true;
303      }
304      if (LinterConfig.terminalTokens.has(node.kind)) {
305        return true;
306      }
307      return false;
308    };
309    forEachNodeInSubtree(sf, callback, stopCondition);
310  }
311
312  private countInterfaceExtendsDifferentPropertyTypes(
313    node: ts.Node,
314    prop2type: Map<string, string>,
315    propName: string,
316    type: ts.TypeNode | undefined
317  ): void {
318    if (type) {
319      const methodType = type.getText();
320      const propType = prop2type.get(propName);
321      if (!propType) {
322        prop2type.set(propName, methodType);
323      } else if (propType !== methodType) {
324        this.incrementCounters(node, FaultID.IntefaceExtendDifProps);
325      }
326    }
327  }
328
329  private countDeclarationsWithDuplicateName(tsNode: ts.Node, tsDeclNode: ts.Node, tsDeclKind?: ts.SyntaxKind): void {
330    const symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode);
331
332    /*
333     * If specific declaration kind is provided, check against it.
334     * Otherwise, use syntax kind of corresponding declaration node.
335     */
336    if (!!symbol && TsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) {
337      this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName);
338    }
339  }
340
341  private countClassMembersWithDuplicateName(tsClassDecl: ts.ClassDeclaration): void {
342    for (const currentMember of tsClassDecl.members) {
343      if (this.tsUtils.classMemberHasDuplicateName(currentMember, tsClassDecl, false)) {
344        this.incrementCounters(currentMember, FaultID.DeclWithDuplicateName);
345      }
346    }
347  }
348
349  private isPrototypePropertyAccess(
350    tsPropertyAccess: ts.PropertyAccessExpression,
351    propAccessSym: ts.Symbol | undefined,
352    baseExprSym: ts.Symbol | undefined,
353    baseExprType: ts.Type
354  ): boolean {
355    if (!(ts.isIdentifier(tsPropertyAccess.name) && tsPropertyAccess.name.text === 'prototype')) {
356      return false;
357    }
358
359    // #13600: Relax prototype check when expression comes from interop.
360    let curPropAccess: ts.Node = tsPropertyAccess;
361    while (curPropAccess && ts.isPropertyAccessExpression(curPropAccess)) {
362      const baseExprSym = this.tsUtils.trueSymbolAtLocation(curPropAccess.expression);
363      if (this.tsUtils.isLibrarySymbol(baseExprSym)) {
364        return false;
365      }
366      curPropAccess = curPropAccess.expression;
367    }
368
369    if (ts.isIdentifier(curPropAccess) && curPropAccess.text !== 'prototype') {
370      const type = this.tsTypeChecker.getTypeAtLocation(curPropAccess);
371      if (TsUtils.isAnyType(type)) {
372        return false;
373      }
374    }
375
376    // Check if property symbol is 'Prototype'
377    if (TsUtils.isPrototypeSymbol(propAccessSym)) {
378      return true;
379    }
380    // Check if symbol of LHS-expression is Class or Function.
381    if (TsUtils.isTypeSymbol(baseExprSym) || TsUtils.isFunctionSymbol(baseExprSym)) {
382      return true;
383    }
384
385    /*
386     * Check if type of LHS expression Function type or Any type.
387     * The latter check is to cover cases with multiple prototype
388     * chain (as the 'Prototype' property should be 'Any' type):
389     *      X.prototype.prototype.prototype = ...
390     */
391    const baseExprTypeNode = this.tsTypeChecker.typeToTypeNode(baseExprType, undefined, ts.NodeBuilderFlags.None);
392    return baseExprTypeNode && ts.isFunctionTypeNode(baseExprTypeNode) || TsUtils.isAnyType(baseExprType);
393  }
394
395  private interfaceInheritanceLint(node: ts.Node, heritageClauses: ts.NodeArray<ts.HeritageClause>): void {
396    for (const hClause of heritageClauses) {
397      if (hClause.token !== ts.SyntaxKind.ExtendsKeyword) {
398        continue;
399      }
400      const prop2type = new Map<string, string>();
401      for (const tsTypeExpr of hClause.types) {
402        const tsExprType = this.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression);
403        if (tsExprType.isClass()) {
404          this.incrementCounters(tsTypeExpr, FaultID.InterfaceExtendsClass);
405        } else if (tsExprType.isClassOrInterface()) {
406          this.lintForInterfaceExtendsDifferentPorpertyTypes(node, tsExprType, prop2type);
407        }
408      }
409    }
410  }
411
412  private lintForInterfaceExtendsDifferentPorpertyTypes(
413    node: ts.Node,
414    tsExprType: ts.Type,
415    prop2type: Map<string, string>
416  ): void {
417    const props = tsExprType.getProperties();
418    for (const p of props) {
419      if (!p.declarations) {
420        continue;
421      }
422      const decl: ts.Declaration = p.declarations[0];
423      const isPropertyDecl = ts.isPropertySignature(decl) || ts.isPropertyDeclaration(decl);
424      const isMethodDecl = ts.isMethodSignature(decl) || ts.isMethodDeclaration(decl);
425      if (isMethodDecl || isPropertyDecl) {
426        this.countInterfaceExtendsDifferentPropertyTypes(node, prop2type, p.name, decl.type);
427      }
428    }
429  }
430
431  private handleObjectLiteralExpression(node: ts.Node): void {
432    const objectLiteralExpr = node as ts.ObjectLiteralExpression;
433    // If object literal is a part of destructuring assignment, then don't process it further.
434    if (TsUtils.isDestructuringAssignmentLHS(objectLiteralExpr)) {
435      return;
436    }
437
438    const objectLiteralType = this.tsTypeChecker.getContextualType(objectLiteralExpr);
439    if (objectLiteralType && this.tsUtils.typeContainsSendableClassOrInterface(objectLiteralType)) {
440      this.incrementCounters(node, FaultID.SendableObjectInitialization);
441    } else if (
442      // issue 13082: Allow initializing struct instances with object literal.
443      !this.tsUtils.isStructObjectInitializer(objectLiteralExpr) &&
444      !this.tsUtils.isDynamicLiteralInitializer(objectLiteralExpr) &&
445      !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, objectLiteralExpr)
446    ) {
447      const autofix = this.autofixer?.fixUntypedObjectLiteral(objectLiteralExpr, objectLiteralType);
448      this.incrementCounters(node, FaultID.ObjectLiteralNoContextType, autofix);
449    }
450
451    if (this.options.arkts2) {
452      this.handleObjectLiteralProperties(objectLiteralType, objectLiteralExpr);
453    }
454  }
455
456  private handleObjectLiteralProperties(
457    objectLiteralType: ts.Type | undefined,
458    objectLiteralExpr: ts.ObjectLiteralExpression
459  ): void {
460    const isRecordObject = objectLiteralType && this.tsUtils.isStdRecordType(objectLiteralType);
461    for (const prop of objectLiteralExpr.properties) {
462      if (
463        isRecordObject && !(prop.name && this.tsUtils.isValidRecordObjectLiteralKey(prop.name)) ||
464        !isRecordObject && !(ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name))
465      ) {
466        const faultNode = ts.isPropertyAssignment(prop) ? prop.name : prop;
467        this.incrementCounters(faultNode, FaultID.ObjectLiteralProperty);
468      }
469    }
470  }
471
472  private handleArrayLiteralExpression(node: ts.Node): void {
473
474    /*
475     * If array literal is a part of destructuring assignment, then
476     * don't process it further.
477     */
478    if (TsUtils.isDestructuringAssignmentLHS(node as ts.ArrayLiteralExpression)) {
479      return;
480    }
481    const arrayLitNode = node as ts.ArrayLiteralExpression;
482    let emptyContextTypeForArrayLiteral = false;
483
484    const arrayLitType = this.tsTypeChecker.getContextualType(arrayLitNode);
485    if (arrayLitType && this.tsUtils.typeContainsSendableClassOrInterface(arrayLitType)) {
486      this.incrementCounters(node, FaultID.SendableObjectInitialization);
487      return;
488    }
489
490    /*
491     * check that array literal consists of inferrable types
492     * e.g. there is no element which is untyped object literals
493     */
494    const arrayLitElements = arrayLitNode.elements;
495    for (const element of arrayLitElements) {
496      const elementContextType = this.tsTypeChecker.getContextualType(element);
497      if (ts.isObjectLiteralExpression(element)) {
498        if (
499          !this.tsUtils.isDynamicLiteralInitializer(arrayLitNode) &&
500          !this.tsUtils.isObjectLiteralAssignable(elementContextType, element)
501        ) {
502          emptyContextTypeForArrayLiteral = true;
503          break;
504        }
505      }
506      if (elementContextType) {
507        this.checkAssignmentMatching(element, elementContextType, element, true);
508      }
509    }
510    if (emptyContextTypeForArrayLiteral) {
511      this.incrementCounters(node, FaultID.ArrayLiteralNoContextType);
512    }
513  }
514
515  private handleParameter(node: ts.Node): void {
516    const tsParam = node as ts.ParameterDeclaration;
517    TsUtils.getDecoratorsIfInSendableClass(tsParam)?.forEach((decorator) => {
518      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
519    });
520    this.handleDeclarationDestructuring(tsParam);
521    this.handleDeclarationInferredType(tsParam);
522  }
523
524  private handleEnumDeclaration(node: ts.Node): void {
525    const enumNode = node as ts.EnumDeclaration;
526    this.countDeclarationsWithDuplicateName(enumNode.name, enumNode);
527    const enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name);
528    if (!enumSymbol) {
529      return;
530    }
531    const enumDecls = enumSymbol.getDeclarations();
532    if (!enumDecls) {
533      return;
534    }
535
536    /*
537     * Since type checker merges all declarations with the same name
538     * into one symbol, we need to check that there's more than one
539     * enum declaration related to that specific symbol.
540     * See 'countDeclarationsWithDuplicateName' method for details.
541     */
542    let enumDeclCount = 0;
543    const enumDeclsInFile: ts.Declaration[] = [];
544    const nodeSrcFile = enumNode.getSourceFile();
545    for (const decl of enumDecls) {
546      if (decl.kind === ts.SyntaxKind.EnumDeclaration) {
547        if (nodeSrcFile === decl.getSourceFile()) {
548          enumDeclsInFile.push(decl);
549        }
550        enumDeclCount++;
551      }
552    }
553
554    if (enumDeclCount > 1) {
555      const autofix = this.autofixer?.fixEnumMerging(enumSymbol, enumDeclsInFile);
556      this.incrementCounters(node, FaultID.EnumMerging, autofix);
557    }
558  }
559
560  private handleInterfaceDeclaration(node: ts.Node): void {
561    // early exit via exception if cancellation was requested
562    this.options.cancellationToken?.throwIfCancellationRequested();
563
564    const interfaceNode = node as ts.InterfaceDeclaration;
565    const iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name);
566    const iDecls = iSymbol ? iSymbol.getDeclarations() : null;
567    if (iDecls) {
568
569      /*
570       * Since type checker merges all declarations with the same name
571       * into one symbol, we need to check that there's more than one
572       * interface declaration related to that specific symbol.
573       * See 'countDeclarationsWithDuplicateName' method for details.
574       */
575      let iDeclCount = 0;
576      for (const decl of iDecls) {
577        if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) {
578          iDeclCount++;
579        }
580      }
581      if (iDeclCount > 1) {
582        this.incrementCounters(node, FaultID.InterfaceMerging);
583      }
584    }
585    if (interfaceNode.heritageClauses) {
586      this.interfaceInheritanceLint(node, interfaceNode.heritageClauses);
587    }
588    this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode);
589  }
590
591  private handleThrowStatement(node: ts.Node): void {
592    const throwStmt = node as ts.ThrowStatement;
593    const throwExprType = this.tsTypeChecker.getTypeAtLocation(throwStmt.expression);
594    if (
595      !throwExprType.isClassOrInterface() ||
596      !this.tsUtils.isOrDerivedFrom(throwExprType, this.tsUtils.isStdErrorType)
597    ) {
598      this.incrementCounters(node, FaultID.ThrowStatement);
599    }
600  }
601
602  private checkForLoopDestructuring(forInit: ts.ForInitializer): void {
603    if (ts.isVariableDeclarationList(forInit) && forInit.declarations.length === 1) {
604      const varDecl = forInit.declarations[0];
605      if (
606        this.options.useRtLogic &&
607        (ts.isArrayBindingPattern(varDecl.name) || ts.isObjectBindingPattern(varDecl.name))
608      ) {
609        this.incrementCounters(varDecl, FaultID.DestructuringDeclaration);
610      }
611    }
612    if (ts.isArrayLiteralExpression(forInit) || ts.isObjectLiteralExpression(forInit)) {
613      this.incrementCounters(forInit, FaultID.DestructuringAssignment);
614    }
615  }
616
617  private handleForStatement(node: ts.Node): void {
618    const tsForStmt = node as ts.ForStatement;
619    const tsForInit = tsForStmt.initializer;
620    if (tsForInit) {
621      this.checkForLoopDestructuring(tsForInit);
622    }
623  }
624
625  private handleForInStatement(node: ts.Node): void {
626    const tsForInStmt = node as ts.ForInStatement;
627    const tsForInInit = tsForInStmt.initializer;
628    this.checkForLoopDestructuring(tsForInInit);
629    this.incrementCounters(node, FaultID.ForInStatement);
630  }
631
632  private handleForOfStatement(node: ts.Node): void {
633    const tsForOfStmt = node as ts.ForOfStatement;
634    const tsForOfInit = tsForOfStmt.initializer;
635    this.checkForLoopDestructuring(tsForOfInit);
636  }
637
638  private handleImportDeclaration(node: ts.Node): void {
639    // early exit via exception if cancellation was requested
640    this.options.cancellationToken?.throwIfCancellationRequested();
641
642    const importDeclNode = node as ts.ImportDeclaration;
643    for (const stmt of importDeclNode.parent.statements) {
644      if (stmt === importDeclNode) {
645        break;
646      }
647      if (!ts.isImportDeclaration(stmt)) {
648        this.incrementCounters(node, FaultID.ImportAfterStatement);
649        break;
650      }
651    }
652
653    const expr = importDeclNode.moduleSpecifier;
654    if (expr.kind === ts.SyntaxKind.StringLiteral) {
655      if (importDeclNode.assertClause) {
656        this.incrementCounters(importDeclNode.assertClause, FaultID.ImportAssertion);
657      }
658    }
659
660    // handle no side effect import in sendable module
661    this.handleSharedModuleNoSideEffectImport(importDeclNode);
662  }
663
664  private handleSharedModuleNoSideEffectImport(node: ts.ImportDeclaration): void {
665    // check 'use shared'
666    if (TypeScriptLinter.inSharedModule(node) && !node.importClause) {
667      this.incrementCounters(node, FaultID.SharedNoSideEffectImport);
668    }
669  }
670
671  private static inSharedModule(node: ts.Node): boolean {
672    const sourceFile: ts.SourceFile = node.getSourceFile();
673    const modulePath = path.normalize(sourceFile.fileName);
674    if (TypeScriptLinter.sharedModulesCache.has(modulePath)) {
675      return TypeScriptLinter.sharedModulesCache.get(modulePath)!;
676    }
677    const isSharedModule: boolean = TsUtils.isSharedModule(sourceFile);
678    TypeScriptLinter.sharedModulesCache.set(modulePath, isSharedModule);
679    return isSharedModule;
680  }
681
682  private handlePropertyAccessExpression(node: ts.Node): void {
683    if (ts.isCallExpression(node.parent) && node === node.parent.expression) {
684      return;
685    }
686
687    const propertyAccessNode = node as ts.PropertyAccessExpression;
688    const exprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode);
689    const baseExprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode.expression);
690    const baseExprType = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression);
691
692    if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) {
693      this.incrementCounters(propertyAccessNode.name, FaultID.Prototype);
694    }
695
696    if (
697      !this.options.arkts2 &&
698      !!exprSym &&
699      this.tsUtils.isStdSymbolAPI(exprSym) &&
700      !ALLOWED_STD_SYMBOL_API.includes(exprSym.getName())
701    ) {
702      this.incrementCounters(propertyAccessNode, FaultID.SymbolType);
703    }
704    if (this.options.advancedClassChecks && this.tsUtils.isClassObjectExpression(propertyAccessNode.expression)) {
705      // missing exact rule
706      this.incrementCounters(propertyAccessNode.expression, FaultID.ClassAsObject);
707    }
708
709    if (!!baseExprSym && TsUtils.symbolHasEsObjectType(baseExprSym)) {
710      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
711      this.incrementCounters(propertyAccessNode, faultId);
712    }
713    if (TsUtils.isSendableFunction(baseExprType) || this.tsUtils.hasSendableTypeAlias(baseExprType)) {
714      this.incrementCounters(propertyAccessNode, FaultID.SendableFunctionProperty);
715    }
716  }
717
718  private handlePropertyDeclaration(node: ts.PropertyDeclaration): void {
719    const propName = node.name;
720    if (!!propName && ts.isNumericLiteral(propName)) {
721      const autofix = this.autofixer?.fixLiteralAsPropertyNamePropertyName(propName);
722      this.incrementCounters(node.name, FaultID.LiteralAsPropertyName, autofix);
723    }
724
725    const decorators = ts.getDecorators(node);
726    this.filterOutDecoratorsDiagnostics(
727      decorators,
728      this.options.useRtLogic ? NON_INITIALIZABLE_PROPERTY_DECORATORS : NON_INITIALIZABLE_PROPERTY_DECORATORS_TSC,
729      { begin: propName.getStart(), end: propName.getStart() },
730      PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE
731    );
732    const classDecorators = ts.getDecorators(node.parent);
733    const propType = node.type?.getText();
734    this.filterOutDecoratorsDiagnostics(
735      classDecorators,
736      NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS,
737      { begin: propName.getStart(), end: propName.getStart() },
738      PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE,
739      propType
740    );
741    if (node.type && node.initializer) {
742      this.checkAssignmentMatching(node, this.tsTypeChecker.getTypeAtLocation(node.type), node.initializer, true);
743    }
744    this.handleDeclarationInferredType(node);
745    this.handleDefiniteAssignmentAssertion(node);
746    this.handleSendableClassProperty(node);
747  }
748
749  private handleSendableClassProperty(node: ts.PropertyDeclaration): void {
750    const classNode = node.parent;
751    if (!ts.isClassDeclaration(classNode) || !TsUtils.hasSendableDecorator(classNode)) {
752      return;
753    }
754    const typeNode = node.type;
755    if (!typeNode) {
756      this.incrementCounters(node, FaultID.SendableExplicitFieldType);
757      return;
758    }
759    TsUtils.getDecoratorsIfInSendableClass(node)?.forEach((decorator) => {
760      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
761    });
762    if (!this.tsUtils.isSendableTypeNode(typeNode)) {
763      this.incrementCounters(node, FaultID.SendablePropType);
764    }
765  }
766
767  private handlePropertyAssignment(node: ts.PropertyAssignment): void {
768    const propName = node.name;
769    if (!(!!propName && ts.isNumericLiteral(propName))) {
770      return;
771    }
772
773    /*
774     * We can use literals as property names only when creating Record or any interop instances.
775     * We can also initialize with constant string literals.
776     * Assignment with string enum values is handled in handleComputedPropertyName
777     */
778    let isRecordObjectInitializer = false;
779    let isLibraryType = false;
780    let isDynamic = false;
781    const objectLiteralType = this.tsTypeChecker.getContextualType(node.parent);
782    if (objectLiteralType) {
783      isRecordObjectInitializer = this.tsUtils.checkTypeSet(objectLiteralType, this.tsUtils.isStdRecordType);
784      isLibraryType = this.tsUtils.isLibraryType(objectLiteralType);
785    }
786
787    isDynamic = isLibraryType || this.tsUtils.isDynamicLiteralInitializer(node.parent);
788    if (!isRecordObjectInitializer && !isDynamic) {
789      const autofix = this.autofixer?.fixLiteralAsPropertyNamePropertyAssignment(node);
790      this.incrementCounters(node.name, FaultID.LiteralAsPropertyName, autofix);
791    }
792  }
793
794  private handlePropertySignature(node: ts.PropertySignature): void {
795    const propName = node.name;
796    if (!!propName && ts.isNumericLiteral(propName)) {
797      const autofix = this.autofixer?.fixLiteralAsPropertyNamePropertyName(propName);
798      this.incrementCounters(node.name, FaultID.LiteralAsPropertyName, autofix);
799    }
800    this.handleSendableInterfaceProperty(node);
801  }
802
803  private handleSendableInterfaceProperty(node: ts.PropertySignature): void {
804    const typeNode = node.type;
805    if (!typeNode) {
806      return;
807    }
808    const interfaceNode = node.parent;
809    const interfaceNodeType = this.tsTypeChecker.getTypeAtLocation(interfaceNode);
810    if (!ts.isInterfaceDeclaration(interfaceNode) || !this.tsUtils.isSendableClassOrInterface(interfaceNodeType)) {
811      return;
812    }
813    if (!this.tsUtils.isSendableTypeNode(typeNode)) {
814      this.incrementCounters(node, FaultID.SendablePropType);
815    }
816  }
817
818  private filterOutDecoratorsDiagnostics(
819    decorators: readonly ts.Decorator[] | undefined,
820    expectedDecorators: readonly string[],
821    range: { begin: number; end: number },
822    code: number,
823    propType?: string
824  ): void {
825    // Filter out non-initializable property decorators from strict diagnostics.
826    if (this.tscStrictDiagnostics && this.sourceFile) {
827      if (
828        decorators?.some((decorator) => {
829          const decoratorName = TsUtils.getDecoratorName(decorator);
830          // special case for property of type CustomDialogController of the @CustomDialog-decorated class
831          if (expectedDecorators.includes(NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0])) {
832            return expectedDecorators.includes(decoratorName) && propType === 'CustomDialogController';
833          }
834          return expectedDecorators.includes(decoratorName);
835        })
836      ) {
837        this.filterOutDiagnostics(range, code);
838      }
839    }
840  }
841
842  private filterOutDiagnostics(range: { begin: number; end: number }, code: number): void {
843    // Filter out strict diagnostics within the given range with the given code.
844    if (!this.tscStrictDiagnostics || !this.sourceFile) {
845      return;
846    }
847    const file = path.normalize(this.sourceFile.fileName);
848    const tscDiagnostics = this.tscStrictDiagnostics.get(file);
849    if (tscDiagnostics) {
850      const filteredDiagnostics = tscDiagnostics.filter((val) => {
851        if (val.code !== code) {
852          return true;
853        }
854        if (val.start === undefined) {
855          return true;
856        }
857        if (val.start < range.begin) {
858          return true;
859        }
860        if (val.start > range.end) {
861          return true;
862        }
863        return false;
864      });
865      this.tscStrictDiagnostics.set(file, filteredDiagnostics);
866    }
867  }
868
869  private static isClassLikeOrIface(node: ts.Node): boolean {
870    return ts.isClassLike(node) || ts.isInterfaceDeclaration(node);
871  }
872
873  private handleFunctionExpression(node: ts.Node): void {
874    const funcExpr = node as ts.FunctionExpression;
875    const isGenerator = funcExpr.asteriskToken !== undefined;
876    const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr);
877    const autofix = this.autofixer?.fixFunctionExpression(
878      funcExpr,
879      newRetTypeNode,
880      ts.getModifiers(funcExpr),
881      isGenerator,
882      hasUnfixableReturnType
883    );
884    this.incrementCounters(funcExpr, FaultID.FunctionExpression, autofix);
885    if (isGenerator) {
886      this.incrementCounters(funcExpr, FaultID.GeneratorFunction);
887    }
888    if (!hasPredecessor(funcExpr, TypeScriptLinter.isClassLikeOrIface)) {
889      this.reportThisKeywordsInScope(funcExpr.body);
890    }
891    if (hasUnfixableReturnType) {
892      this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference);
893    }
894  }
895
896  private handleArrowFunction(node: ts.Node): void {
897    const arrowFunc = node as ts.ArrowFunction;
898    if (!hasPredecessor(arrowFunc, TypeScriptLinter.isClassLikeOrIface)) {
899      this.reportThisKeywordsInScope(arrowFunc.body);
900    }
901    const contextType = this.tsTypeChecker.getContextualType(arrowFunc);
902    if (!(contextType && this.tsUtils.isLibraryType(contextType))) {
903      if (!arrowFunc.type) {
904        this.handleMissingReturnType(arrowFunc);
905      }
906    }
907  }
908
909  private handleFunctionDeclaration(node: ts.Node): void {
910    // early exit via exception if cancellation was requested
911    this.options.cancellationToken?.throwIfCancellationRequested();
912
913    const tsFunctionDeclaration = node as ts.FunctionDeclaration;
914    if (!tsFunctionDeclaration.type) {
915      this.handleMissingReturnType(tsFunctionDeclaration);
916    }
917    if (tsFunctionDeclaration.name) {
918      this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration);
919    }
920    if (tsFunctionDeclaration.body) {
921      this.reportThisKeywordsInScope(tsFunctionDeclaration.body);
922    }
923    const funcDeclParent = tsFunctionDeclaration.parent;
924    if (!ts.isSourceFile(funcDeclParent) && !ts.isModuleBlock(funcDeclParent)) {
925      const autofix = this.autofixer?.fixNestedFunction(tsFunctionDeclaration);
926      this.incrementCounters(tsFunctionDeclaration, FaultID.LocalFunction, autofix);
927    }
928    if (tsFunctionDeclaration.asteriskToken) {
929      this.incrementCounters(node, FaultID.GeneratorFunction);
930    }
931    if (TsUtils.hasSendableDecoratorFunctionOverload(tsFunctionDeclaration)) {
932      if (!this.isSendableDecoratorValid(tsFunctionDeclaration)) {
933        return;
934      }
935      TsUtils.getNonSendableDecorators(tsFunctionDeclaration)?.forEach((decorator) => {
936        this.incrementCounters(decorator, FaultID.SendableFunctionDecorator);
937      });
938      if (!TsUtils.hasSendableDecorator(tsFunctionDeclaration)) {
939        this.incrementCounters(tsFunctionDeclaration, FaultID.SendableFunctionOverloadDecorator);
940      }
941      this.scanCapturedVarsInSendableScope(
942        tsFunctionDeclaration,
943        tsFunctionDeclaration,
944        FaultID.SendableFunctionImportedVariables
945      );
946    }
947  }
948
949  private handleMissingReturnType(
950    funcLikeDecl: ts.FunctionLikeDeclaration | ts.MethodSignature
951  ): [boolean, ts.TypeNode | undefined] {
952    if (this.options.useRtLogic && funcLikeDecl.type) {
953      return [false, funcLikeDecl.type];
954    }
955
956    // Note: Return type can't be inferred for function without body.
957    const isSignature = ts.isMethodSignature(funcLikeDecl);
958    if (isSignature || !funcLikeDecl.body) {
959      // Ambient flag is not exposed, so we apply dirty hack to make it visible
960      const isDeclareDeclaration = TsUtils.isAmbientNode(funcLikeDecl);
961      if ((isSignature || isDeclareDeclaration) && !funcLikeDecl.type) {
962        this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference);
963      }
964      return [false, undefined];
965    }
966
967    return this.tryAutofixMissingReturnType(funcLikeDecl);
968  }
969
970  private tryAutofixMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration): [boolean, ts.TypeNode | undefined] {
971    if (!funcLikeDecl.body) {
972      return [false, undefined];
973    }
974
975    let autofix: Autofix[] | undefined;
976    let newRetTypeNode: ts.TypeNode | undefined;
977    const isFuncExpr = ts.isFunctionExpression(funcLikeDecl);
978
979    /*
980     * Currently, ArkTS can't infer return type of function, when expression
981     * in the return statement is a call to a function or method whose return
982     * value type is omitted. In that case, we attempt to prepare an autofix.
983     */
984    let hasLimitedRetTypeInference = this.hasLimitedTypeInferenceFromReturnExpr(funcLikeDecl.body);
985    const tsSignature = this.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl);
986    if (tsSignature) {
987      const tsRetType = this.tsTypeChecker.getReturnTypeOfSignature(tsSignature);
988      if (
989        !tsRetType ||
990        !this.options.arkts2 && TsUtils.isUnsupportedType(tsRetType) ||
991        this.options.arkts2 && this.tsUtils.isUnsupportedTypeArkts2(tsRetType)
992      ) {
993        hasLimitedRetTypeInference = true;
994      } else if (hasLimitedRetTypeInference) {
995        newRetTypeNode = this.tsTypeChecker.typeToTypeNode(tsRetType, funcLikeDecl, ts.NodeBuilderFlags.None);
996        if (this.autofixer !== undefined && newRetTypeNode && !isFuncExpr) {
997          autofix = this.autofixer.fixMissingReturnType(funcLikeDecl, newRetTypeNode);
998        }
999      }
1000    }
1001
1002    /*
1003     * Don't report here if in function expression context.
1004     * See handleFunctionExpression for details.
1005     */
1006    if (hasLimitedRetTypeInference && !isFuncExpr) {
1007      this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference, autofix);
1008    }
1009
1010    return [hasLimitedRetTypeInference && !newRetTypeNode, newRetTypeNode];
1011  }
1012
1013  private hasLimitedTypeInferenceFromReturnExpr(funBody: ts.ConciseBody): boolean {
1014    let hasLimitedTypeInference = false;
1015    const callback = (node: ts.Node): void => {
1016      if (hasLimitedTypeInference) {
1017        return;
1018      }
1019      if (
1020        ts.isReturnStatement(node) &&
1021        node.expression &&
1022        this.tsUtils.isCallToFunctionWithOmittedReturnType(TsUtils.unwrapParenthesized(node.expression))
1023      ) {
1024        hasLimitedTypeInference = true;
1025      }
1026    };
1027    // Don't traverse other nested function-like declarations.
1028    const stopCondition = (node: ts.Node): boolean => {
1029      return (
1030        ts.isFunctionDeclaration(node) ||
1031        ts.isFunctionExpression(node) ||
1032        ts.isMethodDeclaration(node) ||
1033        ts.isAccessor(node) ||
1034        ts.isArrowFunction(node)
1035      );
1036    };
1037    if (ts.isBlock(funBody)) {
1038      forEachNodeInSubtree(funBody, callback, stopCondition);
1039    } else {
1040      const tsExpr = TsUtils.unwrapParenthesized(funBody);
1041      hasLimitedTypeInference = this.tsUtils.isCallToFunctionWithOmittedReturnType(tsExpr);
1042    }
1043    return hasLimitedTypeInference;
1044  }
1045
1046  private isValidTypeForUnaryArithmeticOperator(type: ts.Type): boolean {
1047    const typeFlags = type.getFlags();
1048    const numberLiteralFlags = ts.TypeFlags.BigIntLiteral | ts.TypeFlags.NumberLiteral;
1049    const numberLikeFlags = ts.TypeFlags.BigIntLike | ts.TypeFlags.NumberLike;
1050    const isNumberLike = !!(typeFlags & (numberLiteralFlags | numberLikeFlags));
1051
1052    const isAllowedNumericType = this.tsUtils.isStdBigIntType(type) || this.tsUtils.isStdNumberType(type);
1053
1054    return isNumberLike || isAllowedNumericType;
1055  }
1056
1057  private handlePrefixUnaryExpression(node: ts.Node): void {
1058    const tsUnaryArithm = node as ts.PrefixUnaryExpression;
1059    const tsUnaryOp = tsUnaryArithm.operator;
1060    const tsUnaryOperand = tsUnaryArithm.operand;
1061    if (
1062      tsUnaryOp === ts.SyntaxKind.PlusToken ||
1063      tsUnaryOp === ts.SyntaxKind.MinusToken ||
1064      tsUnaryOp === ts.SyntaxKind.TildeToken
1065    ) {
1066      const tsOperatndType = this.tsTypeChecker.getTypeAtLocation(tsUnaryOperand);
1067      const isTilde = tsUnaryOp === ts.SyntaxKind.TildeToken;
1068      const isInvalidTilde =
1069        isTilde && ts.isNumericLiteral(tsUnaryOperand) && !this.tsUtils.isIntegerConstantValue(tsUnaryOperand);
1070      if (!this.isValidTypeForUnaryArithmeticOperator(tsOperatndType) || isInvalidTilde) {
1071        this.incrementCounters(node, FaultID.UnaryArithmNotNumber);
1072      }
1073    }
1074  }
1075
1076  private handleBinaryExpression(node: ts.Node): void {
1077    const tsBinaryExpr = node as ts.BinaryExpression;
1078    const tsLhsExpr = tsBinaryExpr.left;
1079    const tsRhsExpr = tsBinaryExpr.right;
1080    if (isAssignmentOperator(tsBinaryExpr.operatorToken)) {
1081      this.processBinaryAssignment(node, tsLhsExpr, tsRhsExpr);
1082    }
1083    const leftOperandType = this.tsTypeChecker.getTypeAtLocation(tsLhsExpr);
1084    const typeNode = this.tsUtils.getVariableDeclarationTypeNode(tsLhsExpr);
1085    switch (tsBinaryExpr.operatorToken.kind) {
1086      // FaultID.BitOpWithWrongType - removed as rule #61
1087      case ts.SyntaxKind.CommaToken:
1088        this.processBinaryComma(tsBinaryExpr);
1089        break;
1090      case ts.SyntaxKind.InstanceOfKeyword:
1091        this.processBinaryInstanceOf(node, tsLhsExpr, leftOperandType);
1092        break;
1093      case ts.SyntaxKind.InKeyword:
1094        this.incrementCounters(tsBinaryExpr.operatorToken, FaultID.InOperator);
1095        break;
1096      case ts.SyntaxKind.EqualsToken:
1097        this.checkAssignmentMatching(tsBinaryExpr, leftOperandType, tsRhsExpr);
1098        this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr);
1099        break;
1100      default:
1101    }
1102  }
1103
1104  private processBinaryAssignment(node: ts.Node, tsLhsExpr: ts.Expression, tsRhsExpr: ts.Expression): void {
1105    this.handleDestructuringAssignment(node, tsLhsExpr, tsRhsExpr);
1106
1107    if (ts.isPropertyAccessExpression(tsLhsExpr)) {
1108      const tsLhsSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr);
1109      const tsLhsBaseSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr.expression);
1110      if (tsLhsSymbol && tsLhsSymbol.flags & ts.SymbolFlags.Method) {
1111        this.incrementCounters(tsLhsExpr, FaultID.MethodReassignment);
1112      }
1113      if (
1114        (this.options.arkts2 || TsUtils.isMethodAssignment(tsLhsSymbol)) &&
1115        tsLhsBaseSymbol &&
1116        (tsLhsBaseSymbol.flags & ts.SymbolFlags.Function) !== 0
1117      ) {
1118        this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction);
1119      }
1120    }
1121  }
1122
1123  private handleDestructuringAssignment(node: ts.Node, tsLhsExpr: ts.Expression, tsRhsExpr: ts.Expression): void {
1124    if (ts.isObjectLiteralExpression(tsLhsExpr)) {
1125      const autofix = this.autofixer?.fixObjectLiteralExpressionDestructAssignment(node as ts.BinaryExpression);
1126      this.incrementCounters(node, FaultID.DestructuringAssignment, autofix);
1127    } else if (ts.isArrayLiteralExpression(tsLhsExpr)) {
1128      // Array destructuring is allowed only for Arrays/Tuples and without spread operator.
1129      const rhsType = this.tsTypeChecker.getTypeAtLocation(tsRhsExpr);
1130      const isArrayOrTuple =
1131        this.tsUtils.isOrDerivedFrom(rhsType, this.tsUtils.isArray) ||
1132        this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple);
1133      const hasNestedObjectDestructuring = TsUtils.hasNestedObjectDestructuring(tsLhsExpr);
1134
1135      if (
1136        !this.options.useRelaxedRules ||
1137        !isArrayOrTuple ||
1138        hasNestedObjectDestructuring ||
1139        TsUtils.destructuringAssignmentHasSpreadOperator(tsLhsExpr)
1140      ) {
1141        const autofix = this.autofixer?.fixArrayBindingPatternAssignment(node as ts.BinaryExpression, isArrayOrTuple);
1142        this.incrementCounters(node, FaultID.DestructuringAssignment, autofix);
1143      }
1144    }
1145  }
1146
1147  private processBinaryComma(tsBinaryExpr: ts.BinaryExpression): void {
1148    // CommaOpertor is allowed in 'for' statement initalizer and incrementor
1149    let tsExprNode: ts.Node = tsBinaryExpr;
1150    let tsParentNode = tsExprNode.parent;
1151    while (tsParentNode && tsParentNode.kind === ts.SyntaxKind.BinaryExpression) {
1152      tsExprNode = tsParentNode;
1153      tsParentNode = tsExprNode.parent;
1154      if ((tsExprNode as ts.BinaryExpression).operatorToken.kind === ts.SyntaxKind.CommaToken) {
1155        // Need to return if one comma enclosed in expression with another comma to avoid multiple reports on one line
1156        return;
1157      }
1158    }
1159    if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ForStatement) {
1160      const tsForNode = tsParentNode as ts.ForStatement;
1161      if (tsExprNode === tsForNode.initializer || tsExprNode === tsForNode.incrementor) {
1162        return;
1163      }
1164    }
1165    if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ExpressionStatement) {
1166      const autofix = this.autofixer?.fixCommaOperator(tsExprNode);
1167      this.incrementCounters(tsExprNode, FaultID.CommaOperator, autofix);
1168      return;
1169    }
1170
1171    this.incrementCounters(tsBinaryExpr as ts.Node, FaultID.CommaOperator);
1172  }
1173
1174  private processBinaryInstanceOf(node: ts.Node, tsLhsExpr: ts.Expression, leftOperandType: ts.Type): void {
1175    const leftExpr = TsUtils.unwrapParenthesized(tsLhsExpr);
1176    const leftSymbol = this.tsUtils.trueSymbolAtLocation(leftExpr);
1177
1178    /*
1179     * In STS, the left-hand side expression may be of any reference type, otherwise
1180     * a compile-time error occurs. In addition, the left operand in STS cannot be a type.
1181     */
1182    if (tsLhsExpr.kind === ts.SyntaxKind.ThisKeyword) {
1183      return;
1184    }
1185
1186    if (TsUtils.isPrimitiveType(leftOperandType) || ts.isTypeNode(leftExpr) || TsUtils.isTypeSymbol(leftSymbol)) {
1187      this.incrementCounters(node, FaultID.InstanceofUnsupported);
1188    }
1189  }
1190
1191  private handleVariableDeclarationList(node: ts.Node): void {
1192    const varDeclFlags = ts.getCombinedNodeFlags(node);
1193    if (!(varDeclFlags & (ts.NodeFlags.Let | ts.NodeFlags.Const))) {
1194      const autofix = this.autofixer?.fixVarDeclaration(node as ts.VariableDeclarationList);
1195      this.incrementCounters(node, FaultID.VarDeclaration, autofix);
1196    }
1197  }
1198
1199  private handleVariableDeclaration(node: ts.Node): void {
1200    const tsVarDecl = node as ts.VariableDeclaration;
1201    if (
1202      !this.options.useRtLogic ||
1203      ts.isVariableDeclarationList(tsVarDecl.parent) && ts.isVariableStatement(tsVarDecl.parent.parent)
1204    ) {
1205      this.handleDeclarationDestructuring(tsVarDecl);
1206    }
1207
1208    // Check variable declaration for duplicate name.
1209    this.checkVarDeclForDuplicateNames(tsVarDecl.name);
1210
1211    if (tsVarDecl.type && tsVarDecl.initializer) {
1212      this.checkAssignmentMatching(
1213        tsVarDecl,
1214        this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type),
1215        tsVarDecl.initializer
1216      );
1217    }
1218    this.handleEsObjectDelaration(tsVarDecl);
1219    this.handleDeclarationInferredType(tsVarDecl);
1220    this.handleDefiniteAssignmentAssertion(tsVarDecl);
1221  }
1222
1223  private handleDeclarationDestructuring(decl: ts.VariableDeclaration | ts.ParameterDeclaration): void {
1224    const faultId = ts.isVariableDeclaration(decl) ? FaultID.DestructuringDeclaration : FaultID.DestructuringParameter;
1225    if (ts.isObjectBindingPattern(decl.name)) {
1226      const autofix = this.autofixer?.fixObjectBindingPatternDeclarations(decl, faultId);
1227      this.incrementCounters(decl, faultId, autofix);
1228    } else if (ts.isArrayBindingPattern(decl.name)) {
1229      // Array destructuring is allowed only for Arrays/Tuples and without spread operator.
1230      const rhsType = this.tsTypeChecker.getTypeAtLocation(decl.initializer ?? decl.name);
1231      const isArrayOrTuple =
1232        rhsType &&
1233        (this.tsUtils.isOrDerivedFrom(rhsType, this.tsUtils.isArray) ||
1234          this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple));
1235      const hasNestedObjectDestructuring = TsUtils.hasNestedObjectDestructuring(decl.name);
1236
1237      if (
1238        !this.options.useRelaxedRules ||
1239        !isArrayOrTuple ||
1240        hasNestedObjectDestructuring ||
1241        TsUtils.destructuringDeclarationHasSpreadOperator(decl.name)
1242      ) {
1243        const autofix = this.autofixer?.fixArrayBindingPatternDeclarations(decl, isArrayOrTuple);
1244        this.incrementCounters(decl, faultId, autofix);
1245      }
1246    }
1247  }
1248
1249  private checkVarDeclForDuplicateNames(tsBindingName: ts.BindingName): void {
1250    if (ts.isIdentifier(tsBindingName)) {
1251      // The syntax kind of the declaration is defined here by the parent of 'BindingName' node.
1252      this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind);
1253      return;
1254    }
1255    for (const tsBindingElem of tsBindingName.elements) {
1256      if (ts.isOmittedExpression(tsBindingElem)) {
1257        continue;
1258      }
1259
1260      this.checkVarDeclForDuplicateNames(tsBindingElem.name);
1261    }
1262  }
1263
1264  private handleEsObjectDelaration(node: ts.VariableDeclaration): void {
1265    const isDeclaredESObject = !!node.type && TsUtils.isEsObjectType(node.type);
1266    const initalizerTypeNode = node.initializer && this.tsUtils.getVariableDeclarationTypeNode(node.initializer);
1267    const isInitializedWithESObject = !!initalizerTypeNode && TsUtils.isEsObjectType(initalizerTypeNode);
1268    const isLocal = TsUtils.isInsideBlock(node);
1269    if ((isDeclaredESObject || isInitializedWithESObject) && !isLocal) {
1270      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
1271      this.incrementCounters(node, faultId);
1272      return;
1273    }
1274
1275    if (node.initializer) {
1276      this.handleEsObjectAssignment(node, node.type, node.initializer);
1277    }
1278  }
1279
1280  private handleEsObjectAssignment(node: ts.Node, nodeDeclType: ts.TypeNode | undefined, initializer: ts.Node): void {
1281    const isTypeAnnotated = !!nodeDeclType;
1282    const isDeclaredESObject = isTypeAnnotated && TsUtils.isEsObjectType(nodeDeclType);
1283    const initalizerTypeNode = this.tsUtils.getVariableDeclarationTypeNode(initializer);
1284    const isInitializedWithESObject = !!initalizerTypeNode && TsUtils.isEsObjectType(initalizerTypeNode);
1285    if (isTypeAnnotated && !isDeclaredESObject && isInitializedWithESObject) {
1286      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
1287      this.incrementCounters(node, faultId);
1288      return;
1289    }
1290
1291    if (isDeclaredESObject && !this.tsUtils.isValueAssignableToESObject(initializer)) {
1292      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
1293      this.incrementCounters(node, faultId);
1294    }
1295  }
1296
1297  private handleCatchClause(node: ts.Node): void {
1298    const tsCatch = node as ts.CatchClause;
1299
1300    /*
1301     * In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'.
1302     * It is not compatible with STS 'catch' where the exception variable has to be of type
1303     * Error or derived from it.
1304     * So each 'catch' which has explicit type for the exception object goes to problems.
1305     */
1306    if (tsCatch.variableDeclaration?.type) {
1307      const autofix = this.autofixer?.dropTypeOnVarDecl(tsCatch.variableDeclaration);
1308      this.incrementCounters(node, FaultID.CatchWithUnsupportedType, autofix);
1309    }
1310  }
1311
1312  private handleClassDeclaration(node: ts.Node): void {
1313    // early exit via exception if cancellation was requested
1314    this.options.cancellationToken?.throwIfCancellationRequested();
1315
1316    const tsClassDecl = node as ts.ClassDeclaration;
1317    if (tsClassDecl.name) {
1318      this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl);
1319    }
1320    this.countClassMembersWithDuplicateName(tsClassDecl);
1321
1322    const isSendableClass = TsUtils.hasSendableDecorator(tsClassDecl);
1323    if (isSendableClass) {
1324      TsUtils.getNonSendableDecorators(tsClassDecl)?.forEach((decorator) => {
1325        this.incrementCounters(decorator, FaultID.SendableClassDecorator);
1326      });
1327      tsClassDecl.typeParameters?.forEach((typeParamDecl) => {
1328        this.checkSendableTypeParameter(typeParamDecl);
1329      });
1330    }
1331
1332    if (tsClassDecl.heritageClauses) {
1333      for (const hClause of tsClassDecl.heritageClauses) {
1334        if (!hClause) {
1335          continue;
1336        }
1337        this.checkClassDeclarationHeritageClause(hClause, isSendableClass);
1338      }
1339    }
1340
1341    // Check captured variables for sendable class
1342    if (isSendableClass) {
1343      tsClassDecl.members.forEach((classMember) => {
1344        this.scanCapturedVarsInSendableScope(classMember, tsClassDecl, FaultID.SendableCapturedVars);
1345      });
1346    }
1347
1348    this.processClassStaticBlocks(tsClassDecl);
1349  }
1350
1351  private checkClassDeclarationHeritageClause(hClause: ts.HeritageClause, isSendableClass: boolean): void {
1352    for (const tsTypeExpr of hClause.types) {
1353
1354      /*
1355       * Always resolve type from 'tsTypeExpr' node, not from 'tsTypeExpr.expression' node,
1356       * as for the latter, type checker will return incorrect type result for classes in
1357       * 'extends' clause. Additionally, reduce reference, as mostly type checker returns
1358       * the TypeReference type objects for classes and interfaces.
1359       */
1360      const tsExprType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(tsTypeExpr));
1361      const isSendableBaseType = this.tsUtils.isSendableClassOrInterface(tsExprType);
1362      if (tsExprType.isClass() && hClause.token === ts.SyntaxKind.ImplementsKeyword) {
1363        this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass);
1364      }
1365      if (!isSendableClass) {
1366        // Non-Sendable class can not implements sendable interface / extends sendable class
1367        if (isSendableBaseType) {
1368          this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance);
1369        }
1370        continue;
1371      }
1372
1373      /*
1374       * Sendable class can implements any interface / extends only sendable class
1375       * Sendable class can not extends sendable class variable(local / import)
1376       */
1377      if (hClause.token === ts.SyntaxKind.ExtendsKeyword) {
1378        if (!isSendableBaseType) {
1379          this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance);
1380          continue;
1381        }
1382        if (!this.isValidSendableClassExtends(tsTypeExpr)) {
1383          this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance);
1384        }
1385      }
1386    }
1387  }
1388
1389  private isValidSendableClassExtends(tsTypeExpr: ts.ExpressionWithTypeArguments): boolean {
1390    const expr = tsTypeExpr.expression;
1391    const sym = this.tsTypeChecker.getSymbolAtLocation(expr);
1392    if (sym && (sym.flags & ts.SymbolFlags.Class) === 0) {
1393      // handle non-class situation(local / import)
1394      if ((sym.flags & ts.SymbolFlags.Alias) !== 0) {
1395
1396        /*
1397         * Sendable class can not extends imported sendable class variable
1398         * Sendable class can extends imported sendable class
1399         */
1400        const realSym = this.tsTypeChecker.getAliasedSymbol(sym);
1401        if (realSym && (realSym.flags & ts.SymbolFlags.Class) === 0) {
1402          return false;
1403        }
1404        return true;
1405      }
1406      return false;
1407    }
1408    return true;
1409  }
1410
1411  private checkSendableTypeParameter(typeParamDecl: ts.TypeParameterDeclaration): void {
1412    const defaultTypeNode = typeParamDecl.default;
1413    if (defaultTypeNode) {
1414      if (!this.tsUtils.isSendableTypeNode(defaultTypeNode)) {
1415        this.incrementCounters(defaultTypeNode, FaultID.SendableGenericTypes);
1416      }
1417    }
1418  }
1419
1420  private processClassStaticBlocks(classDecl: ts.ClassDeclaration): void {
1421    let staticBlocksCntr = 0;
1422    const staticBlockNodes: ts.Node[] = [];
1423    for (const element of classDecl.members) {
1424      if (ts.isClassStaticBlockDeclaration(element)) {
1425        staticBlockNodes[staticBlocksCntr] = element;
1426        staticBlocksCntr++;
1427      }
1428    }
1429    if (staticBlocksCntr > 1) {
1430      const autofix = this.autofixer?.fixMultipleStaticBlocks(staticBlockNodes);
1431      // autofixes for all additional static blocks are the same
1432      for (let i = 1; i < staticBlocksCntr; i++) {
1433        this.incrementCounters(staticBlockNodes[i], FaultID.MultipleStaticBlocks, autofix);
1434      }
1435    }
1436  }
1437
1438  private handleModuleDeclaration(node: ts.Node): void {
1439    // early exit via exception if cancellation was requested
1440    this.options.cancellationToken?.throwIfCancellationRequested();
1441
1442    const tsModuleDecl = node as ts.ModuleDeclaration;
1443
1444    this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl);
1445
1446    const tsModuleBody = tsModuleDecl.body;
1447    const tsModifiers = ts.getModifiers(tsModuleDecl);
1448    if (tsModuleBody) {
1449      if (ts.isModuleBlock(tsModuleBody)) {
1450        this.handleModuleBlock(tsModuleBody);
1451      }
1452    }
1453
1454    if (
1455      !(tsModuleDecl.flags & ts.NodeFlags.Namespace) &&
1456      TsUtils.hasModifier(tsModifiers, ts.SyntaxKind.DeclareKeyword)
1457    ) {
1458      this.incrementCounters(tsModuleDecl, FaultID.ShorthandAmbientModuleDecl);
1459    }
1460
1461    if (ts.isStringLiteral(tsModuleDecl.name) && tsModuleDecl.name.text.includes('*')) {
1462      this.incrementCounters(tsModuleDecl, FaultID.WildcardsInModuleName);
1463    }
1464  }
1465
1466  private handleModuleBlock(moduleBlock: ts.ModuleBlock): void {
1467    for (const tsModuleStmt of moduleBlock.statements) {
1468      switch (tsModuleStmt.kind) {
1469        case ts.SyntaxKind.VariableStatement:
1470        case ts.SyntaxKind.FunctionDeclaration:
1471        case ts.SyntaxKind.ClassDeclaration:
1472        case ts.SyntaxKind.InterfaceDeclaration:
1473        case ts.SyntaxKind.TypeAliasDeclaration:
1474        case ts.SyntaxKind.EnumDeclaration:
1475        case ts.SyntaxKind.ExportDeclaration:
1476          break;
1477
1478        /*
1479         * Nested namespace declarations are prohibited
1480         * but there is no cookbook recipe for it!
1481         */
1482        case ts.SyntaxKind.ModuleDeclaration:
1483          break;
1484        default:
1485          this.incrementCounters(tsModuleStmt, FaultID.NonDeclarationInNamespace);
1486          break;
1487      }
1488    }
1489  }
1490
1491  private handleTypeAliasDeclaration(node: ts.Node): void {
1492    const tsTypeAlias = node as ts.TypeAliasDeclaration;
1493    this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias);
1494    if (TsUtils.hasSendableDecorator(tsTypeAlias)) {
1495      if (!this.isSendableDecoratorValid(tsTypeAlias)) {
1496        return;
1497      }
1498      TsUtils.getNonSendableDecorators(tsTypeAlias)?.forEach((decorator) => {
1499        this.incrementCounters(decorator, FaultID.SendableTypeAliasDecorator);
1500      });
1501      if (!ts.isFunctionTypeNode(tsTypeAlias.type)) {
1502        this.incrementCounters(tsTypeAlias.type, FaultID.SendableTypeAliasDeclaration);
1503      }
1504    }
1505  }
1506
1507  private handleImportClause(node: ts.Node): void {
1508    const tsImportClause = node as ts.ImportClause;
1509    if (tsImportClause.name) {
1510      this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause);
1511    }
1512  }
1513
1514  private handleImportSpecifier(node: ts.Node): void {
1515    const importSpec = node as ts.ImportSpecifier;
1516    this.countDeclarationsWithDuplicateName(importSpec.name, importSpec);
1517  }
1518
1519  private handleNamespaceImport(node: ts.Node): void {
1520    const tsNamespaceImport = node as ts.NamespaceImport;
1521    this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport);
1522  }
1523
1524  private handleTypeAssertionExpression(node: ts.Node): void {
1525    const tsTypeAssertion = node as ts.TypeAssertion;
1526    if (tsTypeAssertion.type.getText() === 'const') {
1527      this.incrementCounters(tsTypeAssertion, FaultID.ConstAssertion);
1528    } else {
1529      const autofix = this.autofixer?.fixTypeAssertion(tsTypeAssertion);
1530      this.incrementCounters(node, FaultID.TypeAssertion, autofix);
1531    }
1532  }
1533
1534  private handleMethodDeclaration(node: ts.Node): void {
1535    const tsMethodDecl = node as ts.MethodDeclaration;
1536    TsUtils.getDecoratorsIfInSendableClass(tsMethodDecl)?.forEach((decorator) => {
1537      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
1538    });
1539    let isStatic = false;
1540    if (tsMethodDecl.modifiers) {
1541      for (const mod of tsMethodDecl.modifiers) {
1542        if (mod.kind === ts.SyntaxKind.StaticKeyword) {
1543          isStatic = true;
1544          break;
1545        }
1546      }
1547    }
1548    if (tsMethodDecl.body && isStatic) {
1549      this.reportThisKeywordsInScope(tsMethodDecl.body);
1550    }
1551    if (!tsMethodDecl.type) {
1552      this.handleMissingReturnType(tsMethodDecl);
1553    }
1554    if (tsMethodDecl.asteriskToken) {
1555      this.incrementCounters(node, FaultID.GeneratorFunction);
1556    }
1557    this.filterOutDecoratorsDiagnostics(
1558      ts.getDecorators(tsMethodDecl),
1559      NON_RETURN_FUNCTION_DECORATORS,
1560      { begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end },
1561      FUNCTION_HAS_NO_RETURN_ERROR_CODE
1562    );
1563    if (this.options.arkts2 && tsMethodDecl.questionToken) {
1564      this.incrementCounters(tsMethodDecl.questionToken, FaultID.OptionalMethod);
1565    }
1566  }
1567
1568  private handleMethodSignature(node: ts.MethodSignature): void {
1569    const tsMethodSign = node;
1570    if (!tsMethodSign.type) {
1571      this.handleMissingReturnType(tsMethodSign);
1572    }
1573    if (this.options.arkts2 && tsMethodSign.questionToken) {
1574      this.incrementCounters(tsMethodSign.questionToken, FaultID.OptionalMethod);
1575    }
1576  }
1577
1578  private handleClassStaticBlockDeclaration(node: ts.Node): void {
1579    const classStaticBlockDecl = node as ts.ClassStaticBlockDeclaration;
1580    if (!ts.isClassDeclaration(classStaticBlockDecl.parent)) {
1581      return;
1582    }
1583    this.reportThisKeywordsInScope(classStaticBlockDecl.body);
1584  }
1585
1586  private handleIdentifier(node: ts.Node): void {
1587    const tsIdentifier = node as ts.Identifier;
1588    const tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier);
1589    if (!tsIdentSym) {
1590      return;
1591    }
1592    if (
1593      (tsIdentSym.flags & ts.SymbolFlags.Module) !== 0 &&
1594      (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0 &&
1595      tsIdentifier.text === 'globalThis'
1596    ) {
1597      const faultId = this.options.arkts2 ? FaultID.GlobalThisError : FaultID.GlobalThis;
1598      this.incrementCounters(node, faultId);
1599    } else {
1600      if (this.options.arkts2) {
1601        this.checkLimitedStdlibApi(tsIdentifier, tsIdentSym);
1602      }
1603      this.handleRestrictedValues(tsIdentifier, tsIdentSym);
1604    }
1605  }
1606
1607  // hard-coded alternative to TypeScriptLinter.advancedClassChecks
1608  private isAllowedClassValueContext(tsIdentifier: ts.Identifier): boolean {
1609    let ctx: ts.Node = tsIdentifier;
1610    while (ts.isPropertyAccessExpression(ctx.parent) || ts.isQualifiedName(ctx.parent)) {
1611      ctx = ctx.parent;
1612    }
1613    if (ts.isPropertyAssignment(ctx.parent) && ts.isObjectLiteralExpression(ctx.parent.parent)) {
1614      ctx = ctx.parent.parent;
1615    }
1616    if (ts.isArrowFunction(ctx.parent) && ctx.parent.body === ctx) {
1617      ctx = ctx.parent;
1618    }
1619
1620    if (ts.isCallExpression(ctx.parent) || ts.isNewExpression(ctx.parent)) {
1621      const callee = ctx.parent.expression;
1622      const isAny = TsUtils.isAnyType(this.tsTypeChecker.getTypeAtLocation(callee));
1623      const isDynamic = isAny || this.tsUtils.hasLibraryType(callee);
1624      if (callee !== ctx && isDynamic) {
1625        return true;
1626      }
1627    }
1628    return false;
1629  }
1630
1631  private isStdlibClassVarDecl(ident: ts.Identifier, sym: ts.Symbol): boolean {
1632
1633    /*
1634     * Most standard JS classes are defined in TS stdlib as ambient global
1635     * variables with interface constructor type and require special check
1636     * when they are being referenced in code.
1637     */
1638
1639    if (
1640      !isStdLibrarySymbol(sym) ||
1641      !sym.valueDeclaration ||
1642      !ts.isVariableDeclaration(sym.valueDeclaration) ||
1643      !TsUtils.isAmbientNode(sym.valueDeclaration)
1644    ) {
1645      return false;
1646    }
1647
1648    const classVarDeclType = StdClassVarDecls.get(sym.name);
1649    if (!classVarDeclType) {
1650      return false;
1651    }
1652    const declType = this.tsTypeChecker.getTypeAtLocation(ident);
1653    return declType.symbol && declType.symbol.name === classVarDeclType;
1654  }
1655
1656  private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol): void {
1657    const illegalValues =
1658      ts.SymbolFlags.ConstEnum |
1659      ts.SymbolFlags.RegularEnum |
1660      ts.SymbolFlags.ValueModule |
1661      (this.options.advancedClassChecks ? 0 : ts.SymbolFlags.Class);
1662
1663    /*
1664     * If module name is duplicated by another declaration, this increases the possibility
1665     * of finding a lot of false positives. Thus, do not check further in that case.
1666     */
1667    if ((tsIdentSym.flags & ts.SymbolFlags.ValueModule) !== 0) {
1668      if (!!tsIdentSym && TsUtils.symbolHasDuplicateName(tsIdentSym, ts.SyntaxKind.ModuleDeclaration)) {
1669        return;
1670      }
1671    }
1672
1673    if (
1674      (tsIdentSym.flags & illegalValues) === 0 &&
1675        !(this.options.arkts2 && this.isStdlibClassVarDecl(tsIdentifier, tsIdentSym)) ||
1676      isStruct(tsIdentSym) ||
1677      !identiferUseInValueContext(tsIdentifier, tsIdentSym)
1678    ) {
1679      return;
1680    }
1681
1682    if ((tsIdentSym.flags & ts.SymbolFlags.Class) !== 0) {
1683      if (!this.options.advancedClassChecks && this.isAllowedClassValueContext(tsIdentifier)) {
1684        return;
1685      }
1686    }
1687
1688    if (tsIdentSym.flags & ts.SymbolFlags.ValueModule) {
1689      this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject);
1690    } else {
1691      // missing EnumAsObject
1692      const faultId = this.options.arkts2 ? FaultID.ClassAsObjectError : FaultID.ClassAsObject;
1693      this.incrementCounters(tsIdentifier, faultId);
1694    }
1695  }
1696
1697  private isElementAcessAllowed(type: ts.Type, argType: ts.Type): boolean {
1698    if (type.isUnion()) {
1699      for (const t of type.types) {
1700        if (!this.isElementAcessAllowed(t, argType)) {
1701          return false;
1702        }
1703      }
1704      return true;
1705    }
1706
1707    const typeNode = this.tsTypeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None);
1708
1709    if (this.tsUtils.isArkTSCollectionsArrayLikeType(type)) {
1710      return this.tsUtils.isNumberLikeType(argType);
1711    }
1712
1713    return (
1714      this.tsUtils.isLibraryType(type) ||
1715      TsUtils.isAnyType(type) ||
1716      this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isIndexableArray) ||
1717      this.tsUtils.isOrDerivedFrom(type, TsUtils.isTuple) ||
1718      this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdRecordType) ||
1719      this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStringType) ||
1720      !this.options.arkts2 &&
1721        (this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdMapType) || TsUtils.isIntrinsicObjectType(type)) ||
1722      TsUtils.isEnumType(type) ||
1723      // we allow EsObject here beacuse it is reported later using FaultId.EsObjectType
1724      TsUtils.isEsObjectType(typeNode)
1725    );
1726  }
1727
1728  private handleElementAccessExpression(node: ts.Node): void {
1729    const tsElementAccessExpr = node as ts.ElementAccessExpression;
1730    const tsElementAccessExprSymbol = this.tsUtils.trueSymbolAtLocation(tsElementAccessExpr.expression);
1731    const tsElemAccessBaseExprType = this.tsUtils.getNonNullableType(
1732      this.tsUtils.getTypeOrTypeConstraintAtLocation(tsElementAccessExpr.expression)
1733    );
1734    const tsElemAccessArgType = this.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.argumentExpression);
1735
1736    const isSet = TsUtils.isSetExpression(tsElementAccessExpr);
1737    const isSetIndexable =
1738      isSet &&
1739      this.tsUtils.isSetIndexableType(
1740        tsElemAccessBaseExprType,
1741        tsElemAccessArgType,
1742        this.tsTypeChecker.getTypeAtLocation((tsElementAccessExpr.parent as ts.BinaryExpression).right)
1743      );
1744
1745    const isGet = !isSet;
1746    const isGetIndexable = isGet && this.tsUtils.isGetIndexableType(tsElemAccessBaseExprType, tsElemAccessArgType);
1747
1748    if (
1749      // unnamed types do not have symbol, so need to check that explicitly
1750      !this.tsUtils.isLibrarySymbol(tsElementAccessExprSymbol) &&
1751      !ts.isArrayLiteralExpression(tsElementAccessExpr.expression) &&
1752      !this.isElementAcessAllowed(tsElemAccessBaseExprType, tsElemAccessArgType) &&
1753      !(this.options.arkts2 && isGetIndexable) &&
1754      !(this.options.arkts2 && isSetIndexable)
1755    ) {
1756      const autofix = this.autofixer?.fixPropertyAccessByIndex(tsElementAccessExpr);
1757      this.incrementCounters(node, FaultID.PropertyAccessByIndex, autofix);
1758    }
1759
1760    if (this.tsUtils.hasEsObjectType(tsElementAccessExpr.expression)) {
1761      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
1762      this.incrementCounters(node, faultId);
1763    }
1764  }
1765
1766  private handleEnumMember(node: ts.Node): void {
1767    const tsEnumMember = node as ts.EnumMember;
1768    const tsEnumMemberType = this.tsTypeChecker.getTypeAtLocation(tsEnumMember);
1769    const constVal = this.tsTypeChecker.getConstantValue(tsEnumMember);
1770    if (tsEnumMember.initializer && !this.tsUtils.isValidEnumMemberInit(tsEnumMember.initializer)) {
1771      this.incrementCounters(node, FaultID.EnumMemberNonConstInit);
1772    }
1773    // check for type - all members should be of same type
1774    const enumDecl = tsEnumMember.parent;
1775    const firstEnumMember = enumDecl.members[0];
1776    const firstEnumMemberType = this.tsTypeChecker.getTypeAtLocation(firstEnumMember);
1777    const firstElewmVal = this.tsTypeChecker.getConstantValue(firstEnumMember);
1778
1779    /*
1780     * each string enum member has its own type
1781     * so check that value type is string
1782     */
1783    if (
1784      constVal !== undefined &&
1785      typeof constVal === 'string' &&
1786      firstElewmVal !== undefined &&
1787      typeof firstElewmVal === 'string'
1788    ) {
1789      return;
1790    }
1791    if (
1792      constVal !== undefined &&
1793      typeof constVal === 'number' &&
1794      firstElewmVal !== undefined &&
1795      typeof firstElewmVal === 'number'
1796    ) {
1797      return;
1798    }
1799    if (firstEnumMemberType !== tsEnumMemberType) {
1800      this.incrementCounters(node, FaultID.EnumMemberNonConstInit);
1801    }
1802  }
1803
1804  private handleExportAssignment(node: ts.Node): void {
1805    const exportAssignment = node as ts.ExportAssignment;
1806    if (exportAssignment.isExportEquals) {
1807      this.incrementCounters(node, FaultID.ExportAssignment);
1808    }
1809
1810    if (!TypeScriptLinter.inSharedModule(node)) {
1811      return;
1812    }
1813
1814    if (!this.tsUtils.isShareableEntity(exportAssignment.expression)) {
1815      this.incrementCounters(exportAssignment.expression, FaultID.SharedModuleExports);
1816    }
1817  }
1818
1819  private handleCallExpression(node: ts.Node): void {
1820    const tsCallExpr = node as ts.CallExpression;
1821
1822    const calleeSym = this.tsUtils.trueSymbolAtLocation(tsCallExpr.expression);
1823    const callSignature = this.tsTypeChecker.getResolvedSignature(tsCallExpr);
1824
1825    this.handleImportCall(tsCallExpr);
1826    this.handleRequireCall(tsCallExpr);
1827    if (calleeSym !== undefined) {
1828      if (!this.options.arkts2) {
1829        this.handleStdlibAPICall(tsCallExpr, calleeSym);
1830        this.handleFunctionApplyBindPropCall(tsCallExpr, calleeSym);
1831      }
1832      if (TsUtils.symbolHasEsObjectType(calleeSym)) {
1833        const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
1834        this.incrementCounters(tsCallExpr, faultId);
1835      }
1836      // Need to process Symbol call separately in order to not report two times when using Symbol API
1837      if (this.options.arkts2 && this.tsUtils.isStdSymbol(calleeSym)) {
1838        this.incrementCounters(tsCallExpr, FaultID.SymbolType);
1839      }
1840    }
1841    if (callSignature !== undefined && !this.tsUtils.isLibrarySymbol(calleeSym)) {
1842      this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature);
1843      this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature);
1844    }
1845    this.handleLibraryTypeCall(tsCallExpr);
1846
1847    if (
1848      ts.isPropertyAccessExpression(tsCallExpr.expression) &&
1849      this.tsUtils.hasEsObjectType(tsCallExpr.expression.expression)
1850    ) {
1851      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
1852      this.incrementCounters(node, faultId);
1853    }
1854  }
1855
1856  private handleEtsComponentExpression(node: ts.Node): void {
1857    // for all the checks we make EtsComponentExpression is compatible with the CallExpression
1858    const etsComponentExpression = node as ts.CallExpression;
1859    this.handleLibraryTypeCall(etsComponentExpression);
1860  }
1861
1862  private handleImportCall(tsCallExpr: ts.CallExpression): void {
1863    if (tsCallExpr.expression.kind !== ts.SyntaxKind.ImportKeyword) {
1864      return;
1865    }
1866
1867    // relax rule#133 "arkts-no-runtime-import"
1868    const tsArgs = tsCallExpr.arguments;
1869    if (tsArgs.length <= 1 || !ts.isObjectLiteralExpression(tsArgs[1])) {
1870      return;
1871    }
1872
1873    for (const tsProp of tsArgs[1].properties) {
1874      if (
1875        (ts.isPropertyAssignment(tsProp) || ts.isShorthandPropertyAssignment(tsProp)) &&
1876        tsProp.name.getText() === 'assert'
1877      ) {
1878        this.incrementCounters(tsProp, FaultID.ImportAssertion);
1879        break;
1880      }
1881    }
1882  }
1883
1884  private handleRequireCall(tsCallExpr: ts.CallExpression): void {
1885    if (
1886      ts.isIdentifier(tsCallExpr.expression) &&
1887      tsCallExpr.expression.text === 'require' &&
1888      ts.isVariableDeclaration(tsCallExpr.parent)
1889    ) {
1890      const tsType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression);
1891      if (TsUtils.isInterfaceType(tsType) && tsType.symbol.name === 'NodeRequire') {
1892        this.incrementCounters(tsCallExpr.parent, FaultID.ImportAssignment);
1893      }
1894    }
1895  }
1896
1897  private handleGenericCallWithNoTypeArgs(
1898    callLikeExpr: ts.CallExpression | ts.NewExpression,
1899    callSignature: ts.Signature
1900  ): void {
1901
1902    /*
1903     * Note: The PR!716 has led to a significant performance degradation.
1904     * Since initial problem was fixed in a more general way, this change
1905     * became redundant. Therefore, it was reverted. See #13721 comments
1906     * for a detailed analysis.
1907     */
1908    const tsSyntaxKind = ts.isNewExpression(callLikeExpr) ?
1909      ts.SyntaxKind.Constructor :
1910      ts.SyntaxKind.FunctionDeclaration;
1911    const signFlags = ts.NodeBuilderFlags.WriteTypeArgumentsOfSignature | ts.NodeBuilderFlags.IgnoreErrors;
1912
1913    const signDecl = this.tsTypeChecker.signatureToSignatureDeclaration(
1914      callSignature,
1915      tsSyntaxKind,
1916      undefined,
1917      signFlags
1918    );
1919    if (!signDecl?.typeArguments) {
1920      return;
1921    }
1922    const resolvedTypeArgs = signDecl.typeArguments;
1923    const startTypeArg = callLikeExpr.typeArguments?.length ?? 0;
1924    for (let i = startTypeArg; i < resolvedTypeArgs.length; ++i) {
1925      const typeNode = resolvedTypeArgs[i];
1926
1927      /*
1928       * if compiler infers 'unknown' type there are 2 possible cases:
1929       *   1. Compiler unable to infer type from arguments and use 'unknown'
1930       *   2. Compiler infer 'unknown' from arguments
1931       * We report error in both cases. It is ok because we cannot use 'unknown'
1932       * in ArkTS and already have separate check for it.
1933       */
1934      if (typeNode.kind === ts.SyntaxKind.UnknownKeyword) {
1935        this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs);
1936        break;
1937      }
1938    }
1939  }
1940
1941  private static readonly listFunctionApplyCallApis = [
1942    'Function.apply',
1943    'Function.call',
1944    'CallableFunction.apply',
1945    'CallableFunction.call'
1946  ];
1947
1948  private static readonly listFunctionBindApis = ['Function.bind', 'CallableFunction.bind'];
1949
1950  private handleFunctionApplyBindPropCall(tsCallExpr: ts.CallExpression, calleeSym: ts.Symbol): void {
1951    const exprName = this.tsTypeChecker.getFullyQualifiedName(calleeSym);
1952    if (TypeScriptLinter.listFunctionApplyCallApis.includes(exprName)) {
1953      this.incrementCounters(tsCallExpr, FaultID.FunctionApplyCall);
1954    }
1955    if (TypeScriptLinter.listFunctionBindApis.includes(exprName)) {
1956      const faultId = this.options.arkts2 ? FaultID.FunctionBindError : FaultID.FunctionBind;
1957      this.incrementCounters(tsCallExpr, faultId);
1958    }
1959  }
1960
1961  private handleStructIdentAndUndefinedInArgs(
1962    tsCallOrNewExpr: ts.CallExpression | ts.NewExpression,
1963    callSignature: ts.Signature
1964  ): void {
1965    if (!tsCallOrNewExpr.arguments) {
1966      return;
1967    }
1968    for (let argIndex = 0; argIndex < tsCallOrNewExpr.arguments.length; ++argIndex) {
1969      const tsArg = tsCallOrNewExpr.arguments[argIndex];
1970      const tsArgType = this.tsTypeChecker.getTypeAtLocation(tsArg);
1971      if (!tsArgType) {
1972        continue;
1973      }
1974      const paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length - 1;
1975      const tsParamSym = callSignature.parameters[paramIndex];
1976      if (!tsParamSym) {
1977        continue;
1978      }
1979      const tsParamDecl = tsParamSym.valueDeclaration;
1980      if (tsParamDecl && ts.isParameter(tsParamDecl)) {
1981        let tsParamType = this.tsTypeChecker.getTypeOfSymbolAtLocation(tsParamSym, tsParamDecl);
1982        if (tsParamDecl.dotDotDotToken && this.tsUtils.isGenericArrayType(tsParamType) && tsParamType.typeArguments) {
1983          tsParamType = tsParamType.typeArguments[0];
1984        }
1985        if (!tsParamType) {
1986          continue;
1987        }
1988        this.checkAssignmentMatching(tsArg, tsParamType, tsArg);
1989      }
1990    }
1991  }
1992
1993  private static readonly LimitedApis = new Map<string, { arr: Array<string> | null; fault: FaultID }>([
1994    ['global', { arr: LIMITED_STD_GLOBAL_API, fault: FaultID.LimitedStdLibApi }],
1995    ['Object', { arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi }],
1996    ['ObjectConstructor', { arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi }],
1997    ['Reflect', { arr: LIMITED_STD_REFLECT_API, fault: FaultID.LimitedStdLibApi }],
1998    ['ProxyHandler', { arr: LIMITED_STD_PROXYHANDLER_API, fault: FaultID.LimitedStdLibApi }],
1999    [SYMBOL, { arr: null, fault: FaultID.SymbolType }],
2000    [SYMBOL_CONSTRUCTOR, { arr: null, fault: FaultID.SymbolType }]
2001  ]);
2002
2003  private handleStdlibAPICall(callExpr: ts.CallExpression, calleeSym: ts.Symbol): void {
2004    const name = calleeSym.getName();
2005    const parName = this.tsUtils.getParentSymbolName(calleeSym);
2006    if (parName === undefined) {
2007      if (LIMITED_STD_GLOBAL_API.includes(name)) {
2008        this.incrementCounters(callExpr, FaultID.LimitedStdLibApi);
2009        return;
2010      }
2011      const escapedName = calleeSym.escapedName;
2012      if (escapedName === 'Symbol' || escapedName === 'SymbolConstructor') {
2013        this.incrementCounters(callExpr, FaultID.SymbolType);
2014      }
2015      return;
2016    }
2017    const lookup = TypeScriptLinter.LimitedApis.get(parName);
2018    if (
2019      lookup !== undefined &&
2020      (lookup.arr === null || lookup.arr.includes(name)) &&
2021      (!this.options.useRelaxedRules || !this.supportedStdCallApiChecker.isSupportedStdCallAPI(callExpr, parName, name))
2022    ) {
2023      this.incrementCounters(callExpr, lookup.fault);
2024    }
2025  }
2026
2027  private checkLimitedStdlibApi(node: ts.Identifier, symbol: ts.Symbol): void {
2028    const parName = this.tsUtils.getParentSymbolName(symbol);
2029    const entries = LIMITED_STD_API.get(parName);
2030    if (!entries) {
2031      return;
2032    }
2033    for (const entry of entries) {
2034      if (
2035        entry.api.includes(symbol.name) &&
2036        !this.supportedStdCallApiChecker.isSupportedStdCallAPI(node, parName, symbol.name)
2037      ) {
2038        this.incrementCounters(node, entry.faultId);
2039        return;
2040      }
2041    }
2042  }
2043
2044  private handleLibraryTypeCall(expr: ts.CallExpression | ts.NewExpression): void {
2045    if (!expr.arguments || !this.tscStrictDiagnostics || !this.sourceFile) {
2046      return;
2047    }
2048
2049    const file = path.normalize(this.sourceFile.fileName);
2050    const tscDiagnostics: readonly ts.Diagnostic[] | undefined = this.tscStrictDiagnostics.get(file);
2051    if (!tscDiagnostics?.length) {
2052      return;
2053    }
2054
2055    const isOhModulesEts = TsUtils.isOhModulesEtsSymbol(this.tsUtils.trueSymbolAtLocation(expr.expression));
2056    const deleteDiagnostics: Set<ts.Diagnostic> = new Set();
2057    LibraryTypeCallDiagnosticChecker.instance.filterDiagnostics(
2058      tscDiagnostics,
2059      expr,
2060      this.tsUtils.isLibraryType(this.tsTypeChecker.getTypeAtLocation(expr.expression)),
2061      (diagnostic, errorType) => {
2062
2063        /*
2064         * When a diagnostic meets the filter criteria, If it happens in an ets file in the 'oh_modules' directory.
2065         * the diagnostic is downgraded to warning. For other files, downgraded to nothing.
2066         */
2067        if (isOhModulesEts && errorType !== DiagnosticCheckerErrorType.UNKNOW) {
2068          diagnostic.category = ts.DiagnosticCategory.Warning;
2069        } else {
2070          deleteDiagnostics.add(diagnostic);
2071        }
2072      }
2073    );
2074
2075    if (!deleteDiagnostics.size) {
2076      return;
2077    }
2078
2079    this.tscStrictDiagnostics.set(
2080      file,
2081      tscDiagnostics.filter((item) => {
2082        return !deleteDiagnostics.has(item);
2083      })
2084    );
2085  }
2086
2087  private handleNewExpression(node: ts.Node): void {
2088    const tsNewExpr = node as ts.NewExpression;
2089
2090    if (this.options.advancedClassChecks || this.options.arkts2) {
2091      const calleeExpr = tsNewExpr.expression;
2092      const calleeType = this.tsTypeChecker.getTypeAtLocation(calleeExpr);
2093      if (
2094        !this.tsUtils.isClassTypeExpression(calleeExpr) &&
2095        !isStdLibraryType(calleeType) &&
2096        !this.tsUtils.isLibraryType(calleeType) &&
2097        !this.tsUtils.hasEsObjectType(calleeExpr)
2098      ) {
2099        // missing exact rule
2100        const faultId = this.options.arkts2 ? FaultID.DynamicCtorCall : FaultID.ClassAsObject;
2101        this.incrementCounters(calleeExpr, faultId);
2102      }
2103    }
2104    const sym = this.tsUtils.trueSymbolAtLocation(tsNewExpr.expression);
2105    const callSignature = this.tsTypeChecker.getResolvedSignature(tsNewExpr);
2106    if (callSignature !== undefined && !this.tsUtils.isLibrarySymbol(sym)) {
2107      this.handleStructIdentAndUndefinedInArgs(tsNewExpr, callSignature);
2108      this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature);
2109    }
2110    this.handleSendableGenericTypes(tsNewExpr);
2111  }
2112
2113  private handleSendableGenericTypes(node: ts.NewExpression): void {
2114    const type = this.tsTypeChecker.getTypeAtLocation(node);
2115    if (!this.tsUtils.isSendableClassOrInterface(type)) {
2116      return;
2117    }
2118
2119    const typeArgs = node.typeArguments;
2120    if (!typeArgs || typeArgs.length === 0) {
2121      return;
2122    }
2123
2124    for (const arg of typeArgs) {
2125      if (!this.tsUtils.isSendableTypeNode(arg)) {
2126        this.incrementCounters(arg, FaultID.SendableGenericTypes);
2127      }
2128    }
2129  }
2130
2131  private handleAsExpression(node: ts.Node): void {
2132    const tsAsExpr = node as ts.AsExpression;
2133    if (tsAsExpr.type.getText() === 'const') {
2134      this.incrementCounters(node, FaultID.ConstAssertion);
2135    }
2136    const targetType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType();
2137    const exprType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType();
2138    // check for rule#65:   'number as Number' and 'boolean as Boolean' are disabled
2139    if (
2140      this.tsUtils.isNumberLikeType(exprType) && this.tsUtils.isStdNumberType(targetType) ||
2141      TsUtils.isBooleanLikeType(exprType) && this.tsUtils.isStdBooleanType(targetType)
2142    ) {
2143      this.incrementCounters(node, FaultID.TypeAssertion);
2144    }
2145    if (
2146      !this.tsUtils.isSendableClassOrInterface(exprType) &&
2147      !this.tsUtils.isObject(exprType) &&
2148      !TsUtils.isAnyType(exprType) &&
2149      this.tsUtils.isSendableClassOrInterface(targetType)
2150    ) {
2151      this.incrementCounters(tsAsExpr, FaultID.SendableAsExpr);
2152    }
2153    if (this.tsUtils.isWrongSendableFunctionAssignment(targetType, exprType)) {
2154      this.incrementCounters(tsAsExpr, FaultID.SendableFunctionAsExpr);
2155    }
2156  }
2157
2158  private handleTypeReference(node: ts.Node): void {
2159    const typeRef = node as ts.TypeReferenceNode;
2160
2161    const isESObject = TsUtils.isEsObjectType(typeRef);
2162    const isPossiblyValidContext = TsUtils.isEsObjectPossiblyAllowed(typeRef);
2163    if (isESObject && !isPossiblyValidContext) {
2164      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
2165      this.incrementCounters(node, faultId);
2166      return;
2167    }
2168
2169    const typeName = this.tsUtils.entityNameToString(typeRef.typeName);
2170    const isStdUtilityType = LIMITED_STANDARD_UTILITY_TYPES.includes(typeName);
2171    if (isStdUtilityType) {
2172      this.incrementCounters(node, FaultID.UtilityType);
2173      return;
2174    }
2175
2176    this.checkPartialType(node);
2177
2178    const typeNameType = this.tsTypeChecker.getTypeAtLocation(typeRef.typeName);
2179    if (this.tsUtils.isSendableClassOrInterface(typeNameType)) {
2180      this.checkSendableTypeArguments(typeRef);
2181    }
2182  }
2183
2184  private checkPartialType(node: ts.Node): void {
2185    const typeRef = node as ts.TypeReferenceNode;
2186    // Using Partial<T> type is allowed only when its argument type is either Class or Interface.
2187    const isStdPartial = this.tsUtils.entityNameToString(typeRef.typeName) === 'Partial';
2188    if (!isStdPartial) {
2189      return;
2190    }
2191
2192    const hasSingleTypeArgument = !!typeRef.typeArguments && typeRef.typeArguments.length === 1;
2193    let argType;
2194    if (!this.options.useRtLogic) {
2195      const firstTypeArg = !!typeRef.typeArguments && hasSingleTypeArgument && typeRef.typeArguments[0];
2196      argType = firstTypeArg && this.tsTypeChecker.getTypeFromTypeNode(firstTypeArg);
2197    } else {
2198      argType = hasSingleTypeArgument && this.tsTypeChecker.getTypeFromTypeNode(typeRef.typeArguments[0]);
2199    }
2200
2201    if (argType && !argType.isClassOrInterface()) {
2202      this.incrementCounters(node, FaultID.UtilityType);
2203    }
2204  }
2205
2206  private checkSendableTypeArguments(typeRef: ts.TypeReferenceNode): void {
2207    if (typeRef.typeArguments) {
2208      for (const typeArg of typeRef.typeArguments) {
2209        if (!this.tsUtils.isSendableTypeNode(typeArg)) {
2210          this.incrementCounters(typeArg, FaultID.SendableGenericTypes);
2211        }
2212      }
2213    }
2214  }
2215
2216  private handleMetaProperty(node: ts.Node): void {
2217    const tsMetaProperty = node as ts.MetaProperty;
2218    if (tsMetaProperty.name.text === 'target') {
2219      this.incrementCounters(node, FaultID.NewTarget);
2220    }
2221  }
2222
2223  private handleSpreadOp(node: ts.Node): void {
2224
2225    /*
2226     * spread assignment is disabled
2227     * spread element is allowed only for arrays as rest parameter
2228     */
2229    if (ts.isSpreadElement(node)) {
2230      const spreadExprType = this.tsUtils.getTypeOrTypeConstraintAtLocation(node.expression);
2231      if (
2232        spreadExprType &&
2233        (this.options.useRtLogic || ts.isCallLikeExpression(node.parent) || ts.isArrayLiteralExpression(node.parent)) &&
2234        (this.tsUtils.isOrDerivedFrom(spreadExprType, this.tsUtils.isArray) ||
2235          this.tsUtils.isOrDerivedFrom(spreadExprType, this.tsUtils.isCollectionArrayType))
2236      ) {
2237        return;
2238      }
2239    }
2240    this.incrementCounters(node, FaultID.SpreadOperator);
2241  }
2242
2243  private handleConstructSignature(node: ts.Node): void {
2244    switch (node.parent.kind) {
2245      case ts.SyntaxKind.TypeLiteral:
2246        this.incrementCounters(node, FaultID.ConstructorType);
2247        break;
2248      case ts.SyntaxKind.InterfaceDeclaration:
2249        this.incrementCounters(node, FaultID.ConstructorIface);
2250        break;
2251      default:
2252    }
2253  }
2254
2255  private handleExpressionWithTypeArguments(node: ts.Node): void {
2256    const tsTypeExpr = node as ts.ExpressionWithTypeArguments;
2257    const symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression);
2258    if (!!symbol && TsUtils.isEsObjectSymbol(symbol)) {
2259      const faultId = this.options.arkts2 ? FaultID.EsObjectTypeError : FaultID.EsObjectType;
2260      this.incrementCounters(tsTypeExpr, faultId);
2261    }
2262  }
2263
2264  private handleComputedPropertyName(node: ts.Node): void {
2265    const computedProperty = node as ts.ComputedPropertyName;
2266    if (this.isSendableCompPropName(computedProperty)) {
2267      // cancel the '[Symbol.iterface]' restriction of 'sendable class/interface' in the 'collections.d.ts' file
2268      if (this.tsUtils.isSymbolIteratorExpression(computedProperty.expression)) {
2269        const declNode = computedProperty.parent?.parent;
2270        if (declNode && TsUtils.isArkTSCollectionsClassOrInterfaceDeclaration(declNode)) {
2271          return;
2272        }
2273      }
2274      this.incrementCounters(node, FaultID.SendableComputedPropName);
2275    } else if (!this.tsUtils.isValidComputedPropertyName(computedProperty, false)) {
2276      this.incrementCounters(node, FaultID.ComputedPropertyName);
2277    }
2278  }
2279
2280  private isSendableCompPropName(compProp: ts.ComputedPropertyName): boolean {
2281    const declNode = compProp.parent?.parent;
2282    if (declNode && ts.isClassDeclaration(declNode) && TsUtils.hasSendableDecorator(declNode)) {
2283      return true;
2284    } else if (declNode && ts.isInterfaceDeclaration(declNode)) {
2285      const declNodeType = this.tsTypeChecker.getTypeAtLocation(declNode);
2286      if (this.tsUtils.isSendableClassOrInterface(declNodeType)) {
2287        return true;
2288      }
2289    }
2290    return false;
2291  }
2292
2293  private handleGetAccessor(node: ts.GetAccessorDeclaration): void {
2294    TsUtils.getDecoratorsIfInSendableClass(node)?.forEach((decorator) => {
2295      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
2296    });
2297  }
2298
2299  private handleSetAccessor(node: ts.SetAccessorDeclaration): void {
2300    TsUtils.getDecoratorsIfInSendableClass(node)?.forEach((decorator) => {
2301      this.incrementCounters(decorator, FaultID.SendableClassDecorator);
2302    });
2303  }
2304
2305  /*
2306   * issue 13987:
2307   * When variable have no type annotation and no initial value, and 'noImplicitAny'
2308   * option is enabled, compiler attempts to infer type from variable references:
2309   * see https://github.com/microsoft/TypeScript/pull/11263.
2310   * In this case, we still want to report the error, since ArkTS doesn't allow
2311   * to omit both type annotation and initializer.
2312   */
2313  private proceedVarPropDeclaration(
2314    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
2315  ): boolean | undefined {
2316    if (
2317      (ts.isVariableDeclaration(decl) && ts.isVariableStatement(decl.parent.parent) ||
2318        ts.isPropertyDeclaration(decl)) &&
2319      !decl.initializer
2320    ) {
2321      if (
2322        ts.isPropertyDeclaration(decl) &&
2323        this.tsUtils.skipPropertyInferredTypeCheck(decl, this.sourceFile, this.options.isEtsFileCb)
2324      ) {
2325        return true;
2326      }
2327
2328      this.incrementCounters(decl, FaultID.AnyType);
2329      return true;
2330    }
2331    return undefined;
2332  }
2333
2334  private handleDeclarationInferredType(
2335    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
2336  ): void {
2337    // The type is explicitly specified, no need to check inferred type.
2338    if (decl.type) {
2339      return;
2340    }
2341
2342    /*
2343     * issue 13161:
2344     * In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since
2345     * ArkTS doesn't support these types, the type for such variable is simply omitted,
2346     * and we don't report it as an error. See TypeScriptLinter.handleCatchClause()
2347     * for reference.
2348     */
2349    if (ts.isCatchClause(decl.parent)) {
2350      return;
2351    }
2352    // Destructuring declarations are not supported, do not process them.
2353    if (ts.isArrayBindingPattern(decl.name) || ts.isObjectBindingPattern(decl.name)) {
2354      return;
2355    }
2356
2357    if (this.proceedVarPropDeclaration(decl)) {
2358      return;
2359    }
2360
2361    const type = this.tsTypeChecker.getTypeAtLocation(decl);
2362    if (type) {
2363      this.validateDeclInferredType(type, decl);
2364    }
2365  }
2366
2367  private handleDefiniteAssignmentAssertion(decl: ts.VariableDeclaration | ts.PropertyDeclaration): void {
2368    if (decl.exclamationToken === undefined) {
2369      return;
2370    }
2371
2372    if (decl.kind === ts.SyntaxKind.PropertyDeclaration) {
2373      const parentDecl = decl.parent;
2374      if (parentDecl.kind === ts.SyntaxKind.ClassDeclaration && TsUtils.hasSendableDecorator(parentDecl)) {
2375        this.incrementCounters(decl, FaultID.SendableDefiniteAssignment);
2376        return;
2377      }
2378    }
2379    const faultId = this.options.arkts2 ? FaultID.DefiniteAssignmentError : FaultID.DefiniteAssignment;
2380    this.incrementCounters(decl, faultId);
2381  }
2382
2383  private readonly validatedTypesSet = new Set<ts.Type>();
2384
2385  private checkAnyOrUnknownChildNode(node: ts.Node): boolean {
2386    if (node.kind === ts.SyntaxKind.AnyKeyword || node.kind === ts.SyntaxKind.UnknownKeyword) {
2387      return true;
2388    }
2389    for (const child of node.getChildren()) {
2390      if (this.checkAnyOrUnknownChildNode(child)) {
2391        return true;
2392      }
2393    }
2394    return false;
2395  }
2396
2397  private handleInferredObjectreference(
2398    type: ts.Type,
2399    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
2400  ): void {
2401    const typeArgs = this.tsTypeChecker.getTypeArguments(type as ts.TypeReference);
2402    if (typeArgs) {
2403      const haveAnyOrUnknownNodes = this.checkAnyOrUnknownChildNode(decl);
2404      if (!haveAnyOrUnknownNodes) {
2405        for (const typeArg of typeArgs) {
2406          this.validateDeclInferredType(typeArg, decl);
2407        }
2408      }
2409    }
2410  }
2411
2412  private validateDeclInferredType(
2413    type: ts.Type,
2414    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
2415  ): void {
2416    if (type.aliasSymbol !== undefined) {
2417      return;
2418    }
2419    if (TsUtils.isObjectType(type) && !!(type.objectFlags & ts.ObjectFlags.Reference)) {
2420      this.handleInferredObjectreference(type, decl);
2421      return;
2422    }
2423    if (this.validatedTypesSet.has(type)) {
2424      return;
2425    }
2426    if (type.isUnion()) {
2427      this.validatedTypesSet.add(type);
2428      for (const unionElem of type.types) {
2429        this.validateDeclInferredType(unionElem, decl);
2430      }
2431    }
2432    if (TsUtils.isAnyType(type)) {
2433      this.incrementCounters(decl, FaultID.AnyType);
2434    } else if (TsUtils.isUnknownType(type)) {
2435      this.incrementCounters(decl, FaultID.UnknownType);
2436    }
2437  }
2438
2439  private handleCommentDirectives(sourceFile: ts.SourceFile): void {
2440
2441    /*
2442     * We use a dirty hack to retrieve list of parsed comment directives by accessing
2443     * internal properties of SourceFile node.
2444     */
2445    /* CC-OFFNXT(no_explicit_any) std lib */
2446    // Handle comment directive '@ts-nocheck'
2447    const pragmas = (sourceFile as any).pragmas;
2448    if (pragmas && pragmas instanceof Map) {
2449      const noCheckPragma = pragmas.get('ts-nocheck');
2450      if (noCheckPragma) {
2451
2452        /*
2453         * The value is either a single entry or an array of entries.
2454         * Wrap up single entry with array to simplify processing.
2455         */
2456        /* CC-OFFNXT(no_explicit_any) std lib */
2457        const noCheckEntries: any[] = Array.isArray(noCheckPragma) ? noCheckPragma : [noCheckPragma];
2458        for (const entry of noCheckEntries) {
2459          this.processNoCheckEntry(entry);
2460        }
2461      }
2462    }
2463
2464    /* CC-OFFNXT(no_explicit_any) std lib */
2465    // Handle comment directives '@ts-ignore' and '@ts-expect-error'
2466    const commentDirectives = (sourceFile as any).commentDirectives;
2467    if (commentDirectives && Array.isArray(commentDirectives)) {
2468      for (const directive of commentDirectives) {
2469        if (directive.range?.pos === undefined || directive.range?.end === undefined) {
2470          continue;
2471        }
2472
2473        const range = directive.range as ts.TextRange;
2474        const kind: ts.SyntaxKind =
2475          sourceFile.text.slice(range.pos, range.pos + 2) === '/*' ?
2476            ts.SyntaxKind.MultiLineCommentTrivia :
2477            ts.SyntaxKind.SingleLineCommentTrivia;
2478        const commentRange: ts.CommentRange = {
2479          pos: range.pos,
2480          end: range.end,
2481          kind
2482        };
2483
2484        this.incrementCounters(commentRange, FaultID.ErrorSuppression);
2485      }
2486    }
2487  }
2488
2489  /* CC-OFFNXT(no_explicit_any) std lib */
2490  private processNoCheckEntry(entry: any): void {
2491    if (entry.range?.kind === undefined || entry.range?.pos === undefined || entry.range?.end === undefined) {
2492      return;
2493    }
2494
2495    this.incrementCounters(entry.range as ts.CommentRange, FaultID.ErrorSuppression);
2496  }
2497
2498  private reportThisKeywordsInScope(scope: ts.Block | ts.Expression): void {
2499    const callback = (node: ts.Node): void => {
2500      if (node.kind === ts.SyntaxKind.ThisKeyword) {
2501        this.incrementCounters(node, FaultID.FunctionContainsThis);
2502      }
2503    };
2504    const stopCondition = (node: ts.Node): boolean => {
2505      const isClassLike = ts.isClassDeclaration(node) || ts.isClassExpression(node);
2506      const isFunctionLike = ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node);
2507      const isModuleDecl = ts.isModuleDeclaration(node);
2508      return isClassLike || isFunctionLike || isModuleDecl;
2509    };
2510    forEachNodeInSubtree(scope, callback, stopCondition);
2511  }
2512
2513  private handleConstructorDeclaration(node: ts.Node): void {
2514    const ctorDecl = node as ts.ConstructorDeclaration;
2515    const paramProperties = ctorDecl.parameters.filter((x) => {
2516      return this.tsUtils.hasAccessModifier(x);
2517    });
2518    if (paramProperties.length === 0) {
2519      return;
2520    }
2521    let paramTypes: ts.TypeNode[] | undefined;
2522    if (ctorDecl.body) {
2523      paramTypes = this.collectCtorParamTypes(ctorDecl);
2524    }
2525    const autofix = this.autofixer?.fixCtorParameterProperties(ctorDecl, paramTypes);
2526    for (const param of paramProperties) {
2527      this.incrementCounters(param, FaultID.ParameterProperties, autofix);
2528    }
2529  }
2530
2531  private collectCtorParamTypes(ctorDecl: ts.ConstructorDeclaration): ts.TypeNode[] | undefined {
2532    const paramTypes: ts.TypeNode[] = [];
2533
2534    for (const param of ctorDecl.parameters) {
2535      let paramTypeNode = param.type;
2536      if (!paramTypeNode) {
2537        const paramType = this.tsTypeChecker.getTypeAtLocation(param);
2538        paramTypeNode = this.tsTypeChecker.typeToTypeNode(paramType, param, ts.NodeBuilderFlags.None);
2539      }
2540      if (!paramTypeNode || !this.tsUtils.isSupportedType(paramTypeNode)) {
2541        return undefined;
2542      }
2543      paramTypes.push(paramTypeNode);
2544    }
2545
2546    return paramTypes;
2547  }
2548
2549  private handlePrivateIdentifier(node: ts.Node): void {
2550    const ident = node as ts.PrivateIdentifier;
2551    const autofix = this.autofixer?.fixPrivateIdentifier(ident);
2552    this.incrementCounters(node, FaultID.PrivateIdentifier, autofix);
2553  }
2554
2555  private handleIndexSignature(node: ts.Node): void {
2556    if (!this.tsUtils.isAllowedIndexSignature(node as ts.IndexSignatureDeclaration)) {
2557      this.incrementCounters(node, FaultID.IndexMember);
2558    }
2559  }
2560
2561  private handleTypeLiteral(node: ts.Node): void {
2562    const typeLiteral = node as ts.TypeLiteralNode;
2563    const autofix = this.autofixer?.fixTypeliteral(typeLiteral);
2564    this.incrementCounters(node, FaultID.ObjectTypeLiteral, autofix);
2565  }
2566
2567  private scanCapturedVarsInSendableScope(startNode: ts.Node, scope: ts.Node, faultId: FaultID): void {
2568    const callback = (node: ts.Node): void => {
2569      // Namespace import will introduce closure in the es2abc compiler stage
2570      if (!ts.isIdentifier(node) || this.checkNamespaceImportVar(node)) {
2571        return;
2572      }
2573
2574      // The "b" of "A.b" should not be checked since it's load from object "A"
2575      const parent: ts.Node = node.parent;
2576      if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
2577        return;
2578      }
2579      // When overloading function, will misreport
2580      if (ts.isFunctionDeclaration(startNode) && startNode.name === node) {
2581        return;
2582      }
2583
2584      this.checkLocalDecl(node, scope, faultId);
2585    };
2586    // Type nodes should not checked because no closure will be introduced
2587    const stopCondition = (node: ts.Node): boolean => {
2588      // already existed 'arkts-sendable-class-decoratos' error
2589      if (ts.isDecorator(node) && node.parent === startNode) {
2590        return true;
2591      }
2592      return ts.isTypeReferenceNode(node);
2593    };
2594    forEachNodeInSubtree(startNode, callback, stopCondition);
2595  }
2596
2597  private checkLocalDecl(node: ts.Identifier, scope: ts.Node, faultId: FaultID): void {
2598    const trueSym = this.tsUtils.trueSymbolAtLocation(node);
2599    // Sendable decorator should be used in method of Sendable classes
2600    if (trueSym === undefined) {
2601      return;
2602    }
2603
2604    // Const enum member will be replaced by the exact value of it, no closure will be introduced
2605    if (TsUtils.isConstEnum(trueSym)) {
2606      return;
2607    }
2608
2609    const declarations = trueSym.getDeclarations();
2610    if (declarations?.length) {
2611      this.checkLocalDeclWithSendableClosure(node, scope, declarations[0], faultId);
2612    }
2613  }
2614
2615  private checkLocalDeclWithSendableClosure(
2616    node: ts.Identifier,
2617    scope: ts.Node,
2618    decl: ts.Declaration,
2619    faultId: FaultID
2620  ): void {
2621    const declPosition = decl.getStart();
2622    if (
2623      decl.getSourceFile().fileName !== node.getSourceFile().fileName ||
2624      declPosition !== undefined && declPosition >= scope.getStart() && declPosition < scope.getEnd()
2625    ) {
2626      return;
2627    }
2628
2629    if (this.isFileExportDecl(decl)) {
2630      return;
2631    }
2632
2633    if (this.isTopSendableClosure(decl)) {
2634      return;
2635    }
2636
2637    /**
2638     * The cases in condition will introduce closure if defined in the same file as the Sendable class. The following
2639     * cases are excluded because they are not allowed in ArkTS:
2640     * 1. ImportEqualDecalration
2641     * 2. BindingElement
2642     */
2643    if (
2644      ts.isVariableDeclaration(decl) ||
2645      ts.isFunctionDeclaration(decl) ||
2646      ts.isClassDeclaration(decl) ||
2647      ts.isInterfaceDeclaration(decl) ||
2648      ts.isEnumDeclaration(decl) ||
2649      ts.isModuleDeclaration(decl) ||
2650      ts.isParameter(decl)
2651    ) {
2652      this.incrementCounters(node, faultId);
2653    }
2654  }
2655
2656  private isTopSendableClosure(decl: ts.Declaration): boolean {
2657    if (!ts.isSourceFile(decl.parent)) {
2658      return false;
2659    }
2660    if (
2661      ts.isClassDeclaration(decl) &&
2662      this.tsUtils.isSendableClassOrInterface(this.tsTypeChecker.getTypeAtLocation(decl))
2663    ) {
2664      return true;
2665    }
2666    if (ts.isFunctionDeclaration(decl) && TsUtils.hasSendableDecoratorFunctionOverload(decl)) {
2667      return true;
2668    }
2669    return false;
2670  }
2671
2672  private checkNamespaceImportVar(node: ts.Node): boolean {
2673    // Namespace import cannot be determined by the true symbol
2674    const sym = this.tsTypeChecker.getSymbolAtLocation(node);
2675    const decls = sym?.getDeclarations();
2676    if (decls?.length) {
2677      if (ts.isNamespaceImport(decls[0])) {
2678        this.incrementCounters(node, FaultID.SendableCapturedVars);
2679        return true;
2680      }
2681    }
2682    return false;
2683  }
2684
2685  private isFileExportDecl(decl: ts.Declaration): boolean {
2686    const sourceFile = decl.getSourceFile();
2687    if (!this.fileExportDeclCaches) {
2688      this.fileExportDeclCaches = this.tsUtils.searchFileExportDecl(sourceFile);
2689    }
2690    return this.fileExportDeclCaches.has(decl);
2691  }
2692
2693  lint(sourceFile: ts.SourceFile): void {
2694    if (this.options.enableAutofix) {
2695      this.autofixer = new Autofixer(this.tsTypeChecker, this.tsUtils, sourceFile, this.options.cancellationToken);
2696    }
2697
2698    this.sourceFile = sourceFile;
2699    this.fileExportDeclCaches = undefined;
2700    this.visitSourceFile(this.sourceFile);
2701    this.handleCommentDirectives(this.sourceFile);
2702  }
2703
2704  private handleExportKeyword(node: ts.Node): void {
2705    const parentNode = node.parent;
2706    if (!TypeScriptLinter.inSharedModule(node) || ts.isModuleBlock(parentNode.parent)) {
2707      return;
2708    }
2709
2710    switch (parentNode.kind) {
2711      case ts.SyntaxKind.EnumDeclaration:
2712      case ts.SyntaxKind.InterfaceDeclaration:
2713      case ts.SyntaxKind.FunctionDeclaration:
2714      case ts.SyntaxKind.ClassDeclaration:
2715        if (!this.tsUtils.isShareableType(this.tsTypeChecker.getTypeAtLocation(parentNode))) {
2716          this.incrementCounters((parentNode as ts.NamedDeclaration).name ?? parentNode, FaultID.SharedModuleExports);
2717        }
2718        return;
2719      case ts.SyntaxKind.VariableStatement:
2720        for (const variableDeclaration of (parentNode as ts.VariableStatement).declarationList.declarations) {
2721          if (!this.tsUtils.isShareableEntity(variableDeclaration.name)) {
2722            this.incrementCounters(variableDeclaration.name, FaultID.SharedModuleExports);
2723          }
2724        }
2725        return;
2726      case ts.SyntaxKind.TypeAliasDeclaration:
2727        if (!this.tsUtils.isShareableEntity(parentNode)) {
2728          this.incrementCounters(parentNode, FaultID.SharedModuleExportsWarning);
2729        }
2730        return;
2731      default:
2732        this.incrementCounters(parentNode, FaultID.SharedModuleExports);
2733    }
2734  }
2735
2736  private handleExportDeclaration(node: ts.Node): void {
2737    if (!TypeScriptLinter.inSharedModule(node) || ts.isModuleBlock(node.parent)) {
2738      return;
2739    }
2740
2741    const exportDecl = node as ts.ExportDeclaration;
2742    if (exportDecl.exportClause === undefined) {
2743      this.incrementCounters(exportDecl, FaultID.SharedModuleNoWildcardExport);
2744      return;
2745    }
2746
2747    if (ts.isNamespaceExport(exportDecl.exportClause)) {
2748      if (!this.tsUtils.isShareableType(this.tsTypeChecker.getTypeAtLocation(exportDecl.exportClause.name))) {
2749        this.incrementCounters(exportDecl.exportClause.name, FaultID.SharedModuleExports);
2750      }
2751      return;
2752    }
2753
2754    for (const exportSpecifier of exportDecl.exportClause.elements) {
2755      if (!this.tsUtils.isShareableEntity(exportSpecifier.name)) {
2756        this.incrementCounters(exportSpecifier.name, FaultID.SharedModuleExports);
2757      }
2758    }
2759  }
2760
2761  private handleReturnStatement(node: ts.Node): void {
2762    // The return value must match the return type of the 'function'
2763    const returnStat = node as ts.ReturnStatement;
2764    const expr = returnStat.expression;
2765    if (!expr) {
2766      return;
2767    }
2768    const lhsType = this.tsTypeChecker.getContextualType(expr);
2769    if (!lhsType) {
2770      return;
2771    }
2772    this.checkAssignmentMatching(node, lhsType, expr, true);
2773  }
2774
2775  /**
2776   * 'arkts-no-structural-typing' check was missing in some scenarios,
2777   * in order not to cause incompatibility,
2778   * only need to strictly match the type of filling the check again
2779   */
2780  private checkAssignmentMatching(
2781    field: ts.Node,
2782    lhsType: ts.Type,
2783    rhsExpr: ts.Expression,
2784    isNewStructuralCheck: boolean = false
2785  ): void {
2786    const rhsType = this.tsTypeChecker.getTypeAtLocation(rhsExpr);
2787    // check that 'sendable typeAlias' is assigned correctly
2788    if (this.tsUtils.isWrongSendableFunctionAssignment(lhsType, rhsType)) {
2789      this.incrementCounters(field, FaultID.SendableFunctionAssignment);
2790    }
2791    const isStrict = this.tsUtils.needStrictMatchType(lhsType, rhsType);
2792    // 'isNewStructuralCheck' means that this assignment scenario was previously omitted, so only strict matches are checked now
2793    if (isNewStructuralCheck && !isStrict) {
2794      return;
2795    }
2796    if (this.tsUtils.needToDeduceStructuralIdentity(lhsType, rhsType, rhsExpr, isStrict)) {
2797      this.incrementCounters(field, FaultID.StructuralIdentity);
2798    }
2799  }
2800
2801  private handleDecorator(node: ts.Node): void {
2802    const decorator: ts.Decorator = node as ts.Decorator;
2803    if (TsUtils.getDecoratorName(decorator) === SENDABLE_DECORATOR) {
2804      const parent: ts.Node = decorator.parent;
2805      if (!parent || !SENDABLE_DECORATOR_NODES.includes(parent.kind)) {
2806        const autofix = this.autofixer?.removeDecorator(decorator);
2807        this.incrementCounters(decorator, FaultID.SendableDecoratorLimited, autofix);
2808      }
2809    }
2810  }
2811
2812  private isSendableDecoratorValid(decl: ts.FunctionDeclaration | ts.TypeAliasDeclaration): boolean {
2813    if (
2814      this.compatibleSdkVersion > SENDBALE_FUNCTION_START_VERSION ||
2815      this.compatibleSdkVersion === SENDBALE_FUNCTION_START_VERSION &&
2816        !SENDABLE_FUNCTION_UNSUPPORTED_STAGES_IN_API12.includes(this.compatibleSdkVersionStage)
2817    ) {
2818      return true;
2819    }
2820    const curDecorator = TsUtils.getSendableDecorator(decl);
2821    if (curDecorator) {
2822      this.incrementCounters(curDecorator, FaultID.SendableBetaCompatible);
2823    }
2824    return false;
2825  }
2826
2827  private handleImportType(node: ts.Node): void {
2828    if (!this.options.arkts2) {
2829      return;
2830    }
2831    this.incrementCounters(node, FaultID.ImportType);
2832  }
2833}
2834