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