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