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