• 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_0 {
18
19//import Utils = Utils;
20
21import FaultID = Problems.FaultID;
22import faultsAttrs = Problems.faultsAttrs;
23
24//import cookBookMsg = cookBookMsg;
25//import cookBookTag = ts.cookBookTag;
26
27//import LinterConfig = ts.LinterConfig;
28
29import Autofix = Autofixer.Autofix;
30//import Autofixer = ts.Autofixer;
31
32import Logger = ts.perfLogger;
33
34import DiagnosticChecker = DiagnosticCheckerNamespace.DiagnosticChecker;
35import ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE =
36      LibraryTypeCallDiagnosticCheckerNamespace.ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE;
37import TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE =
38      LibraryTypeCallDiagnosticCheckerNamespace.TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE;
39import NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE =
40      LibraryTypeCallDiagnosticCheckerNamespace.NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE;
41import LibraryTypeCallDiagnosticChecker =
42      LibraryTypeCallDiagnosticCheckerNamespace.LibraryTypeCallDiagnosticChecker;
43
44//const logger = Logger.getLogger();
45
46export interface ProblemInfo {
47  line: number;
48  column: number;
49  start: number;
50  end: number;
51  type: string;
52  severity: number;
53  problem: string;
54  suggest: string;
55  rule: string;
56  ruleTag: number;
57  autofixable: boolean;
58  autofix?: Autofix[];
59}
60
61export class TypeScriptLinter {
62  static ideMode: boolean;
63  static strictMode: boolean;
64  static logTscErrors: boolean;
65  static warningsAsErrors: boolean;
66  static lintEtsOnly: boolean;
67  static totalVisitedNodes: number;
68  static nodeCounters: number[];
69  static lineCounters: number[];
70
71  static totalErrorLines: number;
72  static errorLineNumbersString: string;
73  static totalWarningLines: number;
74  static warningLineNumbersString: string;
75  static reportDiagnostics = true;
76
77  // The SyntaxKind enum defines additional elements at the end of the enum
78  // that serve as markers (FirstX/LastX). Those elements are initialized
79  // with indices of the previously defined elements. As result, the enum
80  // may return incorrect name for a certain kind index (e.g. 'FirstStatement'
81  // instead of 'VariableStatement').
82  // The following code creates a map with correct syntax kind names.
83  // It can be used when need to print name of syntax kind of certain
84  // AST node in diagnostic messages.
85  //private static tsSyntaxKindNames: string[];
86
87  static problemsInfos: ProblemInfo[] = [];
88
89  static filteredDiagnosticMessages: DiagnosticMessageChain[] = [];
90
91  public static initGlobals(): void {
92    TypeScriptLinter.filteredDiagnosticMessages = []
93  }
94
95  public static initStatic(): void {
96    TypeScriptLinter.strictMode = true;
97    TypeScriptLinter.logTscErrors = false;
98    TypeScriptLinter.warningsAsErrors = false;
99    TypeScriptLinter.lintEtsOnly = true;
100    TypeScriptLinter.totalVisitedNodes = 0;
101    TypeScriptLinter.nodeCounters = [];
102    TypeScriptLinter.lineCounters = [];
103
104    TypeScriptLinter.totalErrorLines = 0;
105    TypeScriptLinter.totalWarningLines = 0;
106    TypeScriptLinter.errorLineNumbersString = "";
107    TypeScriptLinter.warningLineNumbersString = "";
108
109    Autofixer.autofixInfo.length = 0;
110
111    //TypeScriptLinter.tsSyntaxKindNames = [];
112    //const keys = Object.keys(ts.SyntaxKind);
113    //const keys: string[] = [];
114    //const values = Object.values(ts.SyntaxKind);
115    //const values: string[] = [];
116
117    /*
118    for (let i = 0; i < values.length; i++) {
119      const val = values[i];
120      const kindNum = typeof val === "string" ? parseInt(val) : val;
121      if (kindNum && !TypeScriptLinter.tsSyntaxKindNames[kindNum])
122        TypeScriptLinter.tsSyntaxKindNames[kindNum] = keys[i];
123    }
124    */
125
126    for (let i = 0; i < FaultID.LAST_ID; i++) {
127      TypeScriptLinter.nodeCounters[i] = 0;
128      TypeScriptLinter.lineCounters[i] = 0;
129    }
130
131    TypeScriptLinter.problemsInfos = [];
132  }
133
134  public static tsTypeChecker: TypeChecker;
135
136  currentErrorLine: number;
137  currentWarningLine: number;
138  staticBlocks: Set<string>;
139  libraryTypeCallDiagnosticChecker: LibraryTypeCallDiagnosticChecker;
140  skipArkTSStaticBlocksCheck: boolean;
141
142  constructor(private sourceFile: SourceFile,
143              /* private */ tsProgram: Program,
144              private tscStrictDiagnostics?: Map<Diagnostic[]>) {
145    TypeScriptLinter.tsTypeChecker = tsProgram.getLinterTypeChecker();
146    this.currentErrorLine = 0;
147    this.currentWarningLine = 0;
148    this.staticBlocks = new Set<string>();
149    this.libraryTypeCallDiagnosticChecker = new LibraryTypeCallDiagnosticChecker(TypeScriptLinter.filteredDiagnosticMessages);
150
151    const options = tsProgram.getCompilerOptions();
152    this.skipArkTSStaticBlocksCheck = false;
153    if (options.skipArkTSStaticBlocksCheck) {
154      this.skipArkTSStaticBlocksCheck = options.skipArkTSStaticBlocksCheck as boolean;
155    }
156  }
157
158  public static clearTsTypeChecker(): void {
159    TypeScriptLinter.tsTypeChecker = {} as TypeChecker;
160  }
161
162  readonly handlersMap = new Map([
163    [SyntaxKind.ObjectLiteralExpression, this.handleObjectLiteralExpression],
164    [SyntaxKind.ArrayLiteralExpression, this.handleArrayLiteralExpression],
165    [SyntaxKind.Parameter, this.handleParameter],
166    [SyntaxKind.EnumDeclaration, this.handleEnumDeclaration],
167    [SyntaxKind.InterfaceDeclaration, this.handleInterfaceDeclaration],
168    [SyntaxKind.ThrowStatement, this.handleThrowStatement], [SyntaxKind.ImportClause, this.handleImportClause],
169    [SyntaxKind.ForStatement, this.handleForStatement],
170    [SyntaxKind.ForInStatement, this.handleForInStatement],
171    [SyntaxKind.ForOfStatement, this.handleForOfStatement],
172    [SyntaxKind.ImportDeclaration, this.handleImportDeclaration],
173    [SyntaxKind.PropertyAccessExpression, this.handlePropertyAccessExpression],
174    [SyntaxKind.PropertyDeclaration, this.handlePropertyAssignmentOrDeclaration],
175    [SyntaxKind.PropertyAssignment, this.handlePropertyAssignmentOrDeclaration],
176    [SyntaxKind.FunctionExpression, this.handleFunctionExpression],
177    [SyntaxKind.ArrowFunction, this.handleArrowFunction],
178    [SyntaxKind.ClassExpression, this.handleClassExpression], [SyntaxKind.CatchClause, this.handleCatchClause],
179    [SyntaxKind.FunctionDeclaration, this.handleFunctionDeclaration],
180    [SyntaxKind.PrefixUnaryExpression, this.handlePrefixUnaryExpression],
181    [SyntaxKind.BinaryExpression, this.handleBinaryExpression],
182    [SyntaxKind.VariableDeclarationList, this.handleVariableDeclarationList],
183    [SyntaxKind.VariableDeclaration, this.handleVariableDeclaration],
184    [SyntaxKind.ClassDeclaration, this.handleClassDeclaration],
185    [SyntaxKind.ModuleDeclaration, this.handleModuleDeclaration],
186    [SyntaxKind.TypeAliasDeclaration, this.handleTypeAliasDeclaration],
187    [SyntaxKind.ImportSpecifier, this.handleImportSpecifier],
188    [SyntaxKind.NamespaceImport, this.handleNamespaceImport],
189    [SyntaxKind.TypeAssertionExpression, this.handleTypeAssertionExpression],
190    [SyntaxKind.MethodDeclaration, this.handleMethodDeclaration],
191    [SyntaxKind.Identifier, this.handleIdentifier],
192    [SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression],
193    [SyntaxKind.EnumMember, this.handleEnumMember], [SyntaxKind.TypeReference, this.handleTypeReference],
194    [SyntaxKind.ExportAssignment, this.handleExportAssignment],
195    [SyntaxKind.CallExpression, this.handleCallExpression], [SyntaxKind.MetaProperty, this.handleMetaProperty],
196    [SyntaxKind.NewExpression, this.handleNewExpression], [SyntaxKind.AsExpression, this.handleAsExpression],
197    [SyntaxKind.SpreadElement, this.handleSpreadOp], [SyntaxKind.SpreadAssignment, this.handleSpreadOp],
198    [SyntaxKind.GetAccessor, this.handleGetAccessor], [SyntaxKind.SetAccessor, this.handleSetAccessor],
199    [SyntaxKind.ConstructSignature, this.handleConstructSignature],
200    [SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments],
201    [ts.SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName],
202    [SyntaxKind.ClassStaticBlockDeclaration, this.handleClassStaticBlockDeclaration],
203  ]);
204
205  public incrementCounters(node: Node | CommentRange, faultId: number, autofixable = false, autofix?: Autofix[]): void {
206    if (!TypeScriptLinter.strictMode && faultsAttrs[faultId].migratable) { return; } // In relax mode skip migratable
207
208    const startPos = Utils.getStartPos(node);
209    const endPos = Utils.getEndPos(node);
210
211    TypeScriptLinter.nodeCounters[faultId]++;
212    // TSC counts lines and columns from zero
213    let { line, character } = this.sourceFile.getLineAndCharacterOfPosition(startPos);
214    ++line;
215    ++character;
216
217    const faultDescr = LinterConfig.nodeDesc[faultId];
218    const faultType = "unknown"; //TypeScriptLinter.tsSyntaxKindNames[node.kind];
219
220    const cookBookMsgNum = faultsAttrs[faultId] ? Number(faultsAttrs[faultId].cookBookRef) : 0;
221    const cookBookTg = cookBookTag[cookBookMsgNum];
222    let severity = Utils.ProblemSeverity.ERROR;
223    if (faultsAttrs[faultId] && faultsAttrs[faultId].warning) {
224      severity = Utils.ProblemSeverity.WARNING;
225    }
226    const badNodeInfo: ProblemInfo = {
227      line: line,
228      column: character,
229      start: startPos,
230      end: endPos,
231      type: faultType,
232      severity: severity,
233      problem: FaultID[faultId],
234      suggest: cookBookMsgNum > 0 ? cookBookMsg[cookBookMsgNum] : "",
235      rule: cookBookMsgNum > 0 && cookBookTg !== "" ? cookBookTg : faultDescr ? faultDescr : faultType,
236      ruleTag: cookBookMsgNum,
237      autofixable: autofixable,
238      autofix: autofix
239    };
240
241    TypeScriptLinter.problemsInfos.push(badNodeInfo);
242
243    if (!TypeScriptLinter.reportDiagnostics) {
244      //logger.info(
245      Logger.logEvent(
246        `Warning: ${this.sourceFile.fileName} (${line}, ${character}): ${faultDescr ? faultDescr : faultType}`
247      );
248    }
249
250    TypeScriptLinter.lineCounters[faultId]++;
251
252    if (faultsAttrs[faultId].warning) {
253      if (line !== this.currentWarningLine) {
254        this.currentWarningLine = line;
255        ++TypeScriptLinter.totalWarningLines;
256        TypeScriptLinter.warningLineNumbersString += line + ", " ;
257      }
258    }
259    else if (line !== this.currentErrorLine) {
260      this.currentErrorLine = line;
261      ++TypeScriptLinter.totalErrorLines;
262      TypeScriptLinter.errorLineNumbersString += line + ", ";
263    }
264  }
265
266  public visitTSNode(node: Node): void {
267    const self = this;
268    visitTSNodeImpl(node);
269    function visitTSNodeImpl(node: Node): void {
270      if (node === null || node.kind === null) {
271        return;
272      }
273      TypeScriptLinter.totalVisitedNodes++;
274
275      //if (TypeScriptLinter.tsSyntaxKindNames[node.kind] === "StructDeclaration") {
276      // if (node.kind === SyntaxKind.StructDeclaration) {
277      //if ( SyntaxKind[node.kind] === 'StructDeclaration') {
278      if(isStructDeclaration(node)) {
279        self.handleStructDeclaration(node);
280        return;
281      }
282
283      self.handleComments(node);
284
285      if (LinterConfig.terminalTokens.has(node.kind)) return;
286
287      const incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind);
288      if (incrementedType !== undefined) {
289        self.incrementCounters(node, incrementedType);
290      }
291      else {
292        const handler = self.handlersMap.get(node.kind);
293        if (handler !== undefined) {
294          handler.call(self, node);
295        }
296      }
297
298      forEachChild(node, visitTSNodeImpl);
299    }
300  }
301
302  private countInterfaceExtendsDifferentPropertyTypes(
303    node: Node,
304    prop2type: Map<string /*, string*/>,
305    propName: string,
306    type: TypeNode | undefined
307  ): void {
308    if (type) {
309      const methodType = type.getText();
310      const propType = prop2type.get(propName);
311      if (!propType) {
312        prop2type.set(propName, methodType);
313      }
314      else if (propType !== methodType) {
315        this.incrementCounters(node, FaultID.IntefaceExtendDifProps);
316      }
317    }
318  }
319
320  private countDeclarationsWithDuplicateName(
321    tsNode: Node, tsDeclNode: Node, tsDeclKind?: SyntaxKind
322  ): void {
323    const symbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsNode);
324
325    // If specific declaration kind is provided, check against it.
326    // Otherwise, use syntax kind of corresponding declaration node.
327    if (!!symbol && Utils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) {
328      this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName);
329    }
330  }
331
332  private countClassMembersWithDuplicateName(tsClassDecl: ClassDeclaration): void {
333    for (const tsCurrentMember of tsClassDecl.members) {
334      if (
335        !tsCurrentMember.name ||
336        !(isIdentifier(tsCurrentMember.name) || isPrivateIdentifier(tsCurrentMember.name))
337      ) {
338        continue;
339      }
340      for (const tsClassMember of tsClassDecl.members) {
341        if (tsCurrentMember === tsClassMember) continue;
342
343        if (
344          !tsClassMember.name ||
345          !(isIdentifier(tsClassMember.name) || isPrivateIdentifier(tsClassMember.name))
346        ) {
347          continue;
348        }
349        if (
350          isIdentifier(tsCurrentMember.name) &&
351          isPrivateIdentifier(tsClassMember.name) &&
352          tsCurrentMember.name.text === tsClassMember.name.text.substring(1)
353        ) {
354          this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName);
355          break;
356        }
357
358        if (
359          isPrivateIdentifier(tsCurrentMember.name) &&
360          isIdentifier(tsClassMember.name) &&
361          tsCurrentMember.name.text.substring(1) === tsClassMember.name.text
362        ) {
363          this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName);
364          break;
365        }
366      }
367    }
368  }
369
370  private functionContainsThis(tsNode: Node): boolean {
371    let found = false;
372
373    function visitNode(tsNode: Node): void {
374      // Stop visiting child nodes if finished searching.
375      if (found) return;
376
377      if (tsNode.kind === SyntaxKind.ThisKeyword) {
378        found = true;
379        return;
380      }
381
382      // Visit children nodes. Skip any local declaration that defines
383      // its own scope as it needs to be checked separately.
384      if (
385        !isClassDeclaration(tsNode) &&
386        !isClassExpression(tsNode) &&
387        !isModuleDeclaration(tsNode) &&
388        !isFunctionDeclaration(tsNode) &&
389        !isFunctionExpression(tsNode)
390      ) {
391        tsNode.forEachChild(visitNode);
392      }
393    }
394
395    visitNode(tsNode);
396
397    return found;
398  }
399
400  private isPrototypePropertyAccess(tsPropertyAccess: ts.PropertyAccessExpression, propAccessSym: ts.Symbol | undefined, baseExprSym: ts.Symbol | undefined, baseExprType: ts.Type): boolean {
401    if (!(isIdentifier(tsPropertyAccess.name) && tsPropertyAccess.name.text === "prototype")) {
402      return false;
403    }
404    // #13600: Relax prototype check when expression comes from interop.
405    let curPropAccess: Node = tsPropertyAccess;
406    while (curPropAccess && isPropertyAccessExpression(curPropAccess)) {
407      const baseExprSym = Utils.trueSymbolAtLocation(curPropAccess.expression);
408      if (Utils.isLibrarySymbol(baseExprSym)) {
409        return false;
410      }
411      curPropAccess = curPropAccess.expression;
412    }
413    if (ts.isIdentifier(curPropAccess) && curPropAccess.text !== 'prototype') {
414      const type = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(curPropAccess);
415      if (Utils.isAnyType(type)) {
416        return false;
417      }
418    }
419    // Check if property symbol is "Prototype"
420    if (Utils.isPrototypeSymbol(propAccessSym)) return true;
421
422    // Check if symbol of LHS-expression is Class or Function.
423    if (Utils.isTypeSymbol(baseExprSym) || Utils.isFunctionSymbol(baseExprSym)) {
424      return true;
425    }
426    // Check if type of LHS expression Function type or Any type.
427    // The latter check is to cover cases with multiple prototype
428    // chain (as the 'Prototype' property should be 'Any' type):
429    //      X.prototype.prototype.prototype = ...
430    const baseExprTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(
431      baseExprType, undefined, NodeBuilderFlags.None
432    );
433
434    return ((baseExprTypeNode && isFunctionTypeNode(baseExprTypeNode)) || Utils.isAnyType(baseExprType));
435  }
436
437  private interfaceInheritanceLint(node: Node, heritageClauses: NodeArray<HeritageClause>): void {
438    for (const hClause of heritageClauses) {
439      if (hClause.token !== SyntaxKind.ExtendsKeyword) continue;
440
441      const prop2type = new Map<string, string>();
442      for (const tsTypeExpr of hClause.types) {
443        const tsExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression);
444        if (tsExprType.isClass()) {
445          this.incrementCounters(node, FaultID.InterfaceExtendsClass);
446        }
447        else if (tsExprType.isClassOrInterface()) {
448          this.lintForInterfaceExtendsDifferentPorpertyTypes(node, tsExprType, prop2type);
449        }
450      }
451    }
452  }
453
454  private lintForInterfaceExtendsDifferentPorpertyTypes(
455    node: Node, tsExprType: Type, prop2type: Map</*string,*/ string>
456  ): void {
457    const props = tsExprType.getProperties();
458    for (const p of props) {
459      if (!p.declarations) continue;
460
461      const decl: Declaration = p.declarations[0];
462      if (decl.kind === SyntaxKind.MethodSignature) {
463        this.countInterfaceExtendsDifferentPropertyTypes(
464          node, prop2type, p.name, (decl as MethodSignature).type
465        );
466      }
467      else if (decl.kind === SyntaxKind.MethodDeclaration) {
468        this.countInterfaceExtendsDifferentPropertyTypes(
469          node, prop2type, p.name, (decl as MethodDeclaration).type
470        );
471      }
472      else if (decl.kind === SyntaxKind.PropertyDeclaration) {
473        this.countInterfaceExtendsDifferentPropertyTypes(
474          node, prop2type, p.name, (decl as PropertyDeclaration).type
475        );
476      }
477      else if (decl.kind === SyntaxKind.PropertySignature) {
478        this.countInterfaceExtendsDifferentPropertyTypes(
479          node, prop2type, p.name, (decl as PropertySignature).type
480        );
481      }
482    }
483  }
484
485  private handleObjectLiteralExpression(node: Node): void {
486    const objectLiteralExpr = node as ObjectLiteralExpression;
487
488    // If object literal is a part of destructuring assignment, then don't process it further.
489    if (Utils.isDestructuringAssignmentLHS(objectLiteralExpr)) {
490      return;
491    }
492    const objectLiteralType = TypeScriptLinter.tsTypeChecker.getContextualType(objectLiteralExpr);
493    if (!Utils.isStructObjectInitializer(objectLiteralExpr) &&
494        !Utils.isDynamicLiteralInitializer(objectLiteralExpr) &&
495        !Utils.isExpressionAssignableToType(objectLiteralType, objectLiteralExpr)
496      //  !Utils.validateObjectLiteralType(objectLiteralType) || Utils.hasMemberFunction(objectLiteralExpr) ||
497      //  !Utils.validateFields(objectLiteralType, objectLiteralExpr)
498    ) {
499      this.incrementCounters(node, FaultID.ObjectLiteralNoContextType);
500    }
501  }
502
503  private handleArrayLiteralExpression(node: Node): void {
504    // If array literal is a part of destructuring assignment, then
505    // don't process it further.
506    if (Utils.isDestructuringAssignmentLHS(node as ArrayLiteralExpression)) return;
507
508    const arrayLitNode = node as ArrayLiteralExpression;
509    let noContextTypeForArrayLiteral = false;
510
511    // check that array literal consists of inferrable types
512    // e.g. there is no element which is untyped object literals
513    const arrayLitElements = arrayLitNode.elements;
514    for(const element of arrayLitElements) {
515      if(element.kind === SyntaxKind.ObjectLiteralExpression) {
516        const objectLiteralType = TypeScriptLinter.tsTypeChecker.getContextualType(element);
517        if (!Utils.isDynamicLiteralInitializer(arrayLitNode) &&
518            !Utils.isExpressionAssignableToType(objectLiteralType, element)) {
519          noContextTypeForArrayLiteral = true;
520          break;
521        }
522      }
523    }
524
525    if (noContextTypeForArrayLiteral) {
526      this.incrementCounters(node, FaultID.ArrayLiteralNoContextType);
527    }
528  }
529
530  private handleParameter(node: Node): void {
531    const tsParam = node as ParameterDeclaration;
532    if (isArrayBindingPattern(tsParam.name) || isObjectBindingPattern(tsParam.name)) {
533      this.incrementCounters(node, FaultID.DestructuringParameter);
534    }
535    const tsParamMods = ts.getModifiers(tsParam); //tsParam.modifiers;
536    if (
537      tsParamMods &&
538      (Utils.hasModifier(tsParamMods, SyntaxKind.PublicKeyword) ||
539        Utils.hasModifier(tsParamMods, SyntaxKind.ProtectedKeyword) ||
540        Utils.hasModifier(tsParamMods, SyntaxKind.ReadonlyKeyword) ||
541        Utils.hasModifier(tsParamMods, SyntaxKind.PrivateKeyword))
542    ) {
543      this.incrementCounters(node, FaultID.ParameterProperties);
544    }
545    this.handleDecorators(ts.getDecorators(tsParam));
546
547    this.handleDeclarationInferredType(tsParam);
548  }
549
550  private handleEnumDeclaration(node: Node): void {
551    const enumNode = node as EnumDeclaration;
552    this.countDeclarationsWithDuplicateName(enumNode.name, enumNode);
553
554    const enumSymbol = Utils.trueSymbolAtLocation(enumNode.name);
555    if (!enumSymbol) return;
556
557    const enumDecls = enumSymbol.getDeclarations();
558    if (!enumDecls) return;
559
560    // Since type checker merges all declarations with the same name
561    // into one symbol, we need to check that there's more than one
562    // enum declaration related to that specific symbol.
563    // See 'countDeclarationsWithDuplicateName' method for details.
564    let enumDeclCount = 0;
565    for (const decl of enumDecls) {
566      if (decl.kind === SyntaxKind.EnumDeclaration) enumDeclCount++;
567    }
568
569    if (enumDeclCount > 1) this.incrementCounters(node, FaultID.EnumMerging);
570  }
571
572  private handleInterfaceDeclaration(node: Node): void {
573    const interfaceNode = node as InterfaceDeclaration;
574    const iSymbol = Utils.trueSymbolAtLocation(interfaceNode.name);
575    const iDecls = iSymbol ? iSymbol.getDeclarations() : null;
576    if (iDecls) {
577      // Since type checker merges all declarations with the same name
578      // into one symbol, we need to check that there's more than one
579      // interface declaration related to that specific symbol.
580      // See 'countDeclarationsWithDuplicateName' method for details.
581      let iDeclCount = 0;
582      for (const decl of iDecls) {
583        if (decl.kind === SyntaxKind.InterfaceDeclaration) iDeclCount++;
584      }
585
586      if (iDeclCount > 1) this.incrementCounters(node, FaultID.InterfaceMerging);
587    }
588
589    if (interfaceNode.heritageClauses) this.interfaceInheritanceLint(node, interfaceNode.heritageClauses);
590
591    this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode);
592  }
593
594  private handleThrowStatement(node: Node): void {
595    const throwStmt = node as ThrowStatement;
596    const throwExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(throwStmt.expression);
597    if (!throwExprType.isClassOrInterface() || !Utils.isDerivedFrom(throwExprType, Utils.CheckType.Error)) {
598      this.incrementCounters(node, FaultID.ThrowStatement);
599    }
600  }
601
602  private handleForStatement(node: Node): void {
603    const tsForStmt = node as ForStatement;
604    const tsForInit = tsForStmt.initializer;
605    if (tsForInit && (isArrayLiteralExpression(tsForInit) || isObjectLiteralExpression(tsForInit))) {
606      this.incrementCounters(tsForInit, FaultID.DestructuringAssignment);
607    }
608  }
609
610  private handleForInStatement(node: Node): void {
611    const tsForInStmt = node as ForInStatement;
612    const tsForInInit = tsForInStmt.initializer;
613    if (isArrayLiteralExpression(tsForInInit) || isObjectLiteralExpression(tsForInInit)) {
614      this.incrementCounters(tsForInInit, FaultID.DestructuringAssignment);
615    }
616    this.incrementCounters(node, FaultID.ForInStatement);
617  }
618
619  private handleForOfStatement(node: Node): void {
620    const tsForOfStmt = node as ForOfStatement;
621    const tsForOfInit = tsForOfStmt.initializer;
622    if (isArrayLiteralExpression(tsForOfInit) || isObjectLiteralExpression(tsForOfInit)) {
623      this.incrementCounters(tsForOfInit, FaultID.DestructuringAssignment);
624    }
625  }
626
627  private handleImportDeclaration(node: Node): void {
628    const importDeclNode = node as ImportDeclaration;
629    for (const stmt of importDeclNode.parent.statements) {
630      if (stmt === importDeclNode) {
631        break;
632      }
633      if (!isImportDeclaration(stmt)) {
634        this.incrementCounters(node, FaultID.ImportAfterStatement);
635        break;
636      }
637    }
638    const expr1 = importDeclNode.moduleSpecifier;
639    if (expr1.kind === SyntaxKind.StringLiteral) {
640      if (!importDeclNode.importClause) this.incrementCounters(node, FaultID.ImportFromPath);
641    }
642  }
643
644  private handlePropertyAccessExpression(node: Node): void {
645    if (ts.isCallExpression(node.parent) && node == node.parent.expression) {
646      return;
647    }
648
649    const propertyAccessNode = node as PropertyAccessExpression;
650    const exprSym = Utils.trueSymbolAtLocation(propertyAccessNode);
651    const baseExprSym = Utils.trueSymbolAtLocation(propertyAccessNode.expression);
652    const baseExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression);
653    if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) {
654      this.incrementCounters(propertyAccessNode.name, FaultID.Prototype);
655    }
656    if (!!exprSym && Utils.isSymbolAPI(exprSym) && !Utils.ALLOWED_STD_SYMBOL_API.includes(exprSym.getName())) {
657      this.incrementCounters(propertyAccessNode, FaultID.SymbolType);
658    }
659    if (!!baseExprSym  && Utils.symbolHasEsObjectType(baseExprSym)) {
660      this.incrementCounters(propertyAccessNode, FaultID.EsObjectType);
661    }
662  }
663
664  private handlePropertyAssignmentOrDeclaration(node: Node) {
665    const propName = (node as PropertyAssignment | PropertyDeclaration).name;
666
667    if (propName && (propName.kind === SyntaxKind.NumericLiteral || propName.kind === SyntaxKind.StringLiteral)) {
668      // We can use literals as property names only when creating Record or any interop instances.
669      let isRecordObjectInitializer = false;
670      let isLibraryType = false;
671      let isDynamic = false;
672      if (isPropertyAssignment(node)) {
673        let objectLiteralType = TypeScriptLinter.tsTypeChecker.getContextualType(node.parent);
674        if (objectLiteralType) {
675          isRecordObjectInitializer = Utils.isStdRecordType(objectLiteralType);
676          isLibraryType = Utils.isLibraryType(objectLiteralType);
677        }
678        isDynamic = isLibraryType || Utils.isDynamicLiteralInitializer(node.parent);
679      }
680
681      if (!isRecordObjectInitializer && !isDynamic) {
682        let autofix: Autofix[] | undefined = Autofixer.fixLiteralAsPropertyName(node);
683        const autofixable = autofix !== undefined;
684        if (!Autofixer.shouldAutofix(node, FaultID.LiteralAsPropertyName)) {
685          autofix = undefined;
686        }
687        this.incrementCounters(node, FaultID.LiteralAsPropertyName, autofixable, autofix);
688      }
689    }
690
691    if (isPropertyDeclaration(node)) {
692      const decorators = ts.getDecorators(node);
693      this.handleDecorators(decorators);
694      this.filterOutDecoratorsDiagnostics(
695          decorators,
696          Utils.NON_INITIALIZABLE_PROPERTY_DECORATORS,
697          { begin: propName.getStart(), end: propName.getStart() },
698          Utils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE
699        );
700      const classDecorators = ts.getDecorators(node.parent);
701      const propType = (node as ts.PropertyDeclaration).type?.getText();
702      this.filterOutDecoratorsDiagnostics(classDecorators, Utils.NON_INITIALIZABLE_PROPERTY_ClASS_DECORATORS,
703        {begin: propName.getStart(), end: propName.getStart()}, Utils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, propType);
704      this.handleDeclarationInferredType(node);
705      this.handleDefiniteAssignmentAssertion(node);
706    }
707  }
708
709  private filterOutDecoratorsDiagnostics(decorators: readonly Decorator[] | undefined,
710    expectedDecorators: readonly string[],
711    range: { begin: number, end: number},
712    code: number,
713    prop_type?: string) {
714    // Filter out non-initializable property decorators from strict diagnostics.
715    if (this.tscStrictDiagnostics && this.sourceFile) {
716      if (decorators?.some(x => {
717        let decoratorName = "";
718        if (isIdentifier(x.expression)) {
719          decoratorName = x.expression.text;
720        }
721        else if (isCallExpression(x.expression) && isIdentifier(x.expression.expression)) {
722          decoratorName = x.expression.expression.text;
723        }
724        // special case for property of type CustomDialogController of the @CustomDialog-decorated class
725        if (expectedDecorators.includes(Utils.NON_INITIALIZABLE_PROPERTY_ClASS_DECORATORS[0])) {
726          return expectedDecorators.includes(decoratorName) && prop_type === "CustomDialogController"
727        }
728        //return Utils.NON_INITIALIZABLE_PROPERTY_DECORATORS.includes(decoratorName);
729        return expectedDecorators.includes(decoratorName);
730      })) {
731        const file = normalizePath(this.sourceFile.fileName);
732        const tscDiagnostics = this.tscStrictDiagnostics.get(file);
733        if (tscDiagnostics) {
734          const filteredDiagnostics = tscDiagnostics.filter(
735            (val /*, idx, array */) => {
736              if (val.code !== code) return true;
737              if (val.start === undefined) return true;
738              if (val.start < range.begin) return true;
739              if (val.start > range.end) return true;
740              return false;
741            }
742          );
743          this.tscStrictDiagnostics.set(file, filteredDiagnostics);
744        }
745      }
746    }
747  }
748
749  private checkInRange(rangesToFilter: { begin: number, end: number }[], pos: number): boolean {
750    for (let i = 0; i < rangesToFilter.length; i++) {
751      if (pos >= rangesToFilter[i].begin && pos < rangesToFilter[i].end) {
752        return false;
753      }
754    }
755    return true;
756  }
757
758  private filterStrictDiagnostics(filters: { [code: number]: (pos: number) => boolean },
759    diagnosticChecker: DiagnosticChecker): boolean {
760    if (!this.tscStrictDiagnostics || !this.sourceFile) {
761      return false;
762    }
763    const file = normalizePath(this.sourceFile.fileName);
764    const tscDiagnostics = this.tscStrictDiagnostics.get(file);
765    if (!tscDiagnostics) {
766      return false;
767    }
768
769    const checkDiagnostic = (val: Diagnostic) => {
770      const checkInRange = filters[val.code];
771      if (!checkInRange) {
772        return true;
773      }
774      if (val.start === undefined || checkInRange(val.start)) {
775        return true;
776      }
777      return diagnosticChecker.checkDiagnosticMessage(val.messageText);
778    };
779
780    if (tscDiagnostics.every(checkDiagnostic)) {
781      return false;
782    }
783    this.tscStrictDiagnostics.set(file, tscDiagnostics.filter(checkDiagnostic));
784    return true;
785  }
786
787  private handleFunctionExpression(node: Node): void {
788    const funcExpr = node as FunctionExpression;
789    const isGenerator = funcExpr.asteriskToken !== undefined;
790    const containsThis = this.functionContainsThis(funcExpr.body);
791    const hasValidContext = Utils.hasPredecessor(funcExpr, isClassLike) ||
792                            Utils.hasPredecessor(funcExpr, isInterfaceDeclaration);
793    const isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0;
794    const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr);
795    const autofixable = !isGeneric && !isGenerator && !containsThis && !hasUnfixableReturnType;
796
797    let autofix: Autofix[] | undefined;
798    if (autofixable && Autofixer.shouldAutofix(node, FaultID.FunctionExpression)) {
799      autofix = [ Autofixer.fixFunctionExpression(funcExpr, funcExpr.parameters, newRetTypeNode) ];
800    }
801
802    this.incrementCounters(node, FaultID.FunctionExpression, autofixable, autofix);
803    if (isGeneric) {
804      this.incrementCounters(funcExpr, FaultID.LambdaWithTypeParameters);
805    }
806    if (isGenerator) {
807      this.incrementCounters(funcExpr, FaultID.GeneratorFunction);
808    }
809    if (containsThis && !hasValidContext) {
810      this.incrementCounters(funcExpr, FaultID.FunctionContainsThis);
811    }
812    if (hasUnfixableReturnType) {
813      this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference);
814    }
815  }
816
817  private handleArrowFunction(node: Node): void {
818    const arrowFunc = node as ArrowFunction;
819    const containsThis = this.functionContainsThis(arrowFunc.body);
820    const hasValidContext = Utils.hasPredecessor(arrowFunc, isClassLike) ||
821                            Utils.hasPredecessor(arrowFunc, isInterfaceDeclaration);
822    if (containsThis && !hasValidContext) {
823      this.incrementCounters(arrowFunc, FaultID.FunctionContainsThis);
824    }
825
826    const contextType = TypeScriptLinter.tsTypeChecker.getContextualType(arrowFunc);
827    if (!(contextType && Utils.isLibraryType(contextType))) {
828      if (!arrowFunc.type) {
829        this.handleMissingReturnType(arrowFunc);
830      }
831
832      if (arrowFunc.typeParameters && arrowFunc.typeParameters.length > 0) {
833        this.incrementCounters(node, FaultID.LambdaWithTypeParameters);
834      }
835    }
836  }
837
838  private handleClassExpression(node: Node): void {
839    //let tsClassExpr = node as ClassExpression;
840    this.incrementCounters(node, FaultID.ClassExpression);
841    //this.handleDecorators(tsClassExpr.decorators);
842  }
843
844  private handleFunctionDeclaration(node: Node) {
845    const tsFunctionDeclaration = node as FunctionDeclaration;
846    if (!tsFunctionDeclaration.type) this.handleMissingReturnType(tsFunctionDeclaration);
847    if (tsFunctionDeclaration.name) {
848      this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration);
849    }
850    if (tsFunctionDeclaration.body && this.functionContainsThis(tsFunctionDeclaration.body)) {
851      this.incrementCounters(node, FaultID.FunctionContainsThis);
852    }
853    if (!isSourceFile(tsFunctionDeclaration.parent) && !isModuleBlock(tsFunctionDeclaration.parent)) {
854      this.incrementCounters(tsFunctionDeclaration, FaultID.LocalFunction);
855    }
856    if (tsFunctionDeclaration.asteriskToken) this.incrementCounters(node, FaultID.GeneratorFunction);
857  }
858
859  private handleMissingReturnType(funcLikeDecl: FunctionLikeDeclaration): [boolean, TypeNode | undefined] {
860    // if (funcLikeDecl.type) return [false, funcLikeDecl.type];
861
862    // Note: Return type can't be inferred for function without body.
863    if (!funcLikeDecl.body) return [false, undefined];
864
865    let autofixable = false;
866    let autofix: Autofix[] | undefined;
867    let newRetTypeNode: TypeNode | undefined;
868    const isFuncExpr = isFunctionExpression(funcLikeDecl);
869
870    // Currently, ArkTS can't infer return type of function, when expression
871    // in the return statement is a call to a function or method whose return
872    // value type is omitted. In that case, we attempt to prepare an autofix.
873    let hasLimitedRetTypeInference = this.hasLimitedTypeInferenceFromReturnExpr(funcLikeDecl.body);
874
875    const tsSignature = TypeScriptLinter.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl);
876    if (tsSignature) {
877      const tsRetType = TypeScriptLinter.tsTypeChecker.getReturnTypeOfSignature(tsSignature);
878
879      if (!tsRetType || Utils.isUnsupportedType(tsRetType)) {
880        hasLimitedRetTypeInference = true;
881      }
882      else if (hasLimitedRetTypeInference) {
883        newRetTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(tsRetType, funcLikeDecl, NodeBuilderFlags.None);
884        autofixable = !!newRetTypeNode;
885
886        if (!isFuncExpr && newRetTypeNode && Autofixer.shouldAutofix(funcLikeDecl, FaultID.LimitedReturnTypeInference)) {
887          autofix = [Autofixer.fixReturnType(funcLikeDecl, newRetTypeNode)];
888        }
889      }
890    }
891
892    // Don't report here if in function expression context.
893    // See handleFunctionExpression for details.
894    if (hasLimitedRetTypeInference && !isFuncExpr) {
895      this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference, autofixable, autofix);
896    }
897    return [hasLimitedRetTypeInference && !newRetTypeNode, newRetTypeNode];
898  }
899
900  private hasLimitedTypeInferenceFromReturnExpr(funBody: ConciseBody): boolean {
901    let hasLimitedTypeInference = false;
902    function visitNode(tsNode: Node): void {
903      if (hasLimitedTypeInference) {
904        return;
905      }
906
907      if (
908        isReturnStatement(tsNode) && tsNode.expression &&
909        Utils.isCallToFunctionWithOmittedReturnType(Utils.unwrapParenthesized(tsNode.expression))
910      ) {
911        hasLimitedTypeInference = true;
912        return;
913      }
914
915      // Visit children nodes. Don't traverse other nested function-like declarations.
916      if (
917        !isFunctionDeclaration(tsNode) &&
918        !isFunctionExpression(tsNode) &&
919        !isMethodDeclaration(tsNode) &&
920        !isAccessor(tsNode) &&
921        !isArrowFunction(tsNode)
922      ) {
923        tsNode.forEachChild(visitNode);
924      }
925    }
926
927    if (isBlock(funBody)) {
928      visitNode(funBody);
929    }
930    else {
931      const tsExpr = Utils.unwrapParenthesized(funBody);
932      hasLimitedTypeInference = Utils.isCallToFunctionWithOmittedReturnType(tsExpr);
933    }
934
935    return hasLimitedTypeInference;
936  }
937
938  private handlePrefixUnaryExpression(node: Node) {
939    const tsUnaryArithm = node as PrefixUnaryExpression;
940    const tsUnaryOp = tsUnaryArithm.operator;
941    if (
942      tsUnaryOp === SyntaxKind.PlusToken ||
943      tsUnaryOp === SyntaxKind.MinusToken ||
944      tsUnaryOp === SyntaxKind.TildeToken
945    ) {
946      const tsOperatndType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsUnaryArithm.operand);
947      if (!(tsOperatndType.getFlags() & (TypeFlags.NumberLike | TypeFlags.BigIntLiteral)) ||
948            (tsUnaryOp === SyntaxKind.TildeToken && tsUnaryArithm.operand.kind === SyntaxKind.NumericLiteral &&
949              !Utils.isIntegerConstantValue(tsUnaryArithm.operand as NumericLiteral))
950          ) {
951        this.incrementCounters(node, FaultID.UnaryArithmNotNumber);
952      }
953    }
954  }
955
956  private handleBinaryExpression(node: Node): void {
957    const tsBinaryExpr = node as BinaryExpression;
958    const tsLhsExpr = tsBinaryExpr.left;
959    const tsRhsExpr = tsBinaryExpr.right;
960
961    if (Utils.isAssignmentOperator(tsBinaryExpr.operatorToken)) {
962      if (isObjectLiteralExpression(tsLhsExpr) || isArrayLiteralExpression(tsLhsExpr)) {
963        this.incrementCounters(node, FaultID.DestructuringAssignment);
964      }
965      if (isPropertyAccessExpression(tsLhsExpr)) {
966        const tsLhsSymbol = Utils.trueSymbolAtLocation(tsLhsExpr);
967        const tsLhsBaseSymbol = Utils.trueSymbolAtLocation(tsLhsExpr.expression);
968        if (tsLhsSymbol && (tsLhsSymbol.flags & SymbolFlags.Method)) {
969          this.incrementCounters(tsLhsExpr, FaultID.NoUndefinedPropAccess);
970        }
971        if (
972          Utils.isMethodAssignment(tsLhsSymbol) && tsLhsBaseSymbol &&
973          (tsLhsBaseSymbol.flags & SymbolFlags.Function) !== 0
974        ) {
975          this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction);
976        }
977      }
978    }
979
980    const leftOperandType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsLhsExpr);
981    const rightOperandType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsRhsExpr);
982
983    if (tsBinaryExpr.operatorToken.kind === SyntaxKind.PlusToken) {
984      if (Utils.isEnumMemberType(leftOperandType) && Utils.isEnumMemberType(rightOperandType)) {
985        if (
986            ((leftOperandType.flags & (TypeFlags.NumberLike)) && (rightOperandType.getFlags() & (TypeFlags.NumberLike))) ||
987            ((leftOperandType.flags & (TypeFlags.StringLike)) && (rightOperandType.getFlags() & (TypeFlags.StringLike)))
988          ) {
989          return;
990        }
991      }
992      else if (Utils.isNumberType(leftOperandType) && Utils.isNumberType(rightOperandType)) {
993        return;
994      }
995      else if (Utils.isStringLikeType(leftOperandType) || Utils.isStringLikeType(rightOperandType)) {
996        return;
997      }
998    }
999    else if (
1000      tsBinaryExpr.operatorToken.kind === SyntaxKind.AmpersandToken ||
1001      tsBinaryExpr.operatorToken.kind === SyntaxKind.BarToken ||
1002      tsBinaryExpr.operatorToken.kind === SyntaxKind.CaretToken ||
1003      tsBinaryExpr.operatorToken.kind === SyntaxKind.LessThanLessThanToken ||
1004      tsBinaryExpr.operatorToken.kind === SyntaxKind.GreaterThanGreaterThanToken ||
1005      tsBinaryExpr.operatorToken.kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken
1006    ) {
1007      if (!(Utils.isNumberType(leftOperandType) && Utils.isNumberType(rightOperandType))||
1008            (tsLhsExpr.kind === SyntaxKind.NumericLiteral && !Utils.isIntegerConstantValue(tsLhsExpr as NumericLiteral)) ||
1009            (tsRhsExpr.kind === SyntaxKind.NumericLiteral && !Utils.isIntegerConstantValue(tsRhsExpr as NumericLiteral))
1010          ) {
1011        return; //this.incrementCounters(node, FaultID.BitOpWithWrongType);
1012      }
1013    }
1014    else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.CommaToken) {
1015      // CommaOpertor is allowed in 'for' statement initalizer and incrementor
1016      let tsExprNode: Node = tsBinaryExpr;
1017      let tsParentNode = tsExprNode.parent;
1018      while (tsParentNode && tsParentNode.kind === SyntaxKind.BinaryExpression) {
1019        tsExprNode = tsParentNode;
1020        tsParentNode = tsExprNode.parent;
1021      }
1022
1023      if (tsParentNode && tsParentNode.kind === SyntaxKind.ForStatement) {
1024        const tsForNode = tsParentNode as ForStatement;
1025        if (tsExprNode === tsForNode.initializer || tsExprNode === tsForNode.incrementor) return;
1026      }
1027      this.incrementCounters(node, FaultID.CommaOperator);
1028    }
1029    else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.InstanceOfKeyword) {
1030      const leftExpr = Utils.unwrapParenthesized(tsBinaryExpr.left);
1031      const leftSymbol = Utils.trueSymbolAtLocation(leftExpr);
1032      // In STS, the left-hand side expression may be of any reference type, otherwise
1033      // a compile-time error occurs. In addition, the left operand in STS cannot be a type.
1034      if (tsLhsExpr.kind === SyntaxKind.ThisKeyword) {
1035        return;
1036      }
1037      if (Utils.isPrimitiveType(leftOperandType) || isTypeNode(leftExpr) || Utils.isTypeSymbol(leftSymbol)) {
1038        this.incrementCounters(node, FaultID.InstanceofUnsupported);
1039      }
1040    }
1041    else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.EqualsToken) {
1042      if (Utils.needToDeduceStructuralIdentity(rightOperandType, leftOperandType)) {
1043        this.incrementCounters(tsBinaryExpr, FaultID.StructuralIdentity);
1044      }
1045      const typeNode = Utils.getVariableDeclarationTypeNode(tsLhsExpr);
1046      this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr);
1047    }
1048  }
1049
1050  private handleVariableDeclarationList(node: Node): void {
1051    const varDeclFlags = getCombinedNodeFlags(node);
1052    if (!(varDeclFlags & (NodeFlags.Let | NodeFlags.Const))) {
1053      this.incrementCounters(node, FaultID.VarDeclaration);
1054    }
1055  }
1056
1057  private handleVariableDeclaration(node: Node): void {
1058    const tsVarDecl = node as VariableDeclaration;
1059    if (isArrayBindingPattern(tsVarDecl.name) || isObjectBindingPattern(tsVarDecl.name)) {
1060      this.incrementCounters(node, FaultID.DestructuringDeclaration);
1061    }
1062    {
1063      // Check variable declaration for duplicate name.
1064      const visitBindingPatternNames = (tsBindingName: BindingName): void => {
1065        if (isIdentifier(tsBindingName)) {
1066          // The syntax kind of the declaration is defined here by the parent of 'BindingName' node.
1067          this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind);
1068        }
1069        else {
1070          for (const tsBindingElem of tsBindingName.elements) {
1071            if (isOmittedExpression(tsBindingElem)) continue;
1072
1073            visitBindingPatternNames(tsBindingElem.name);
1074          }
1075        }
1076      };
1077
1078      visitBindingPatternNames(tsVarDecl.name);
1079    }
1080
1081    if (tsVarDecl.type && tsVarDecl.initializer) {
1082      const tsVarInit = tsVarDecl.initializer;
1083      const tsVarType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsVarDecl.type);
1084      const tsInitType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsVarInit);
1085      if (Utils.needToDeduceStructuralIdentity(tsInitType, tsVarType)) {
1086        this.incrementCounters(tsVarDecl, FaultID.StructuralIdentity);
1087      }
1088    }
1089
1090    this.handleEsObjectDelaration(tsVarDecl);
1091    this.handleDeclarationInferredType(tsVarDecl);
1092    this.handleDefiniteAssignmentAssertion(tsVarDecl);
1093  }
1094
1095  private handleEsObjectDelaration(node: ts.VariableDeclaration) {
1096    const isDeclaredESObject = !!node.type && Utils.isEsObjectType(node.type);
1097    const initalizerTypeNode = node.initializer && Utils.getVariableDeclarationTypeNode(node.initializer);
1098    const isInitializedWithESObject = !!initalizerTypeNode && Utils.isEsObjectType(initalizerTypeNode);
1099    const isLocal = Utils.isInsideBlock(node)
1100    if ((isDeclaredESObject || isInitializedWithESObject) && !isLocal) {
1101      this.incrementCounters(node, FaultID.EsObjectType);
1102      return;
1103    }
1104
1105    if (node.initializer) {
1106      this.handleEsObjectAssignment(node, node.type, node.initializer);
1107      return;
1108    }
1109  }
1110
1111  private handleEsObjectAssignment(node: ts.Node, nodeDeclType: ts.TypeNode | undefined, initializer: ts.Node) {
1112    const isTypeAnnotated = !!nodeDeclType;
1113    const isDeclaredESObject = !!nodeDeclType && Utils.isEsObjectType(nodeDeclType);
1114    const initalizerTypeNode = Utils.getVariableDeclarationTypeNode(initializer);
1115    const isInitializedWithESObject = !!initalizerTypeNode && Utils.isEsObjectType(initalizerTypeNode);
1116    if (isTypeAnnotated && !isDeclaredESObject && isInitializedWithESObject) {
1117      this.incrementCounters(node, FaultID.EsObjectType);
1118      return;
1119    }
1120
1121    if (isDeclaredESObject && !Utils.isValueAssignableToESObject(initializer)) {
1122      this.incrementCounters(node, FaultID.EsObjectType);
1123    }
1124  }
1125
1126
1127  private handleCatchClause(node: Node): void {
1128    const tsCatch = node as CatchClause;
1129    // In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'.
1130    // It is not compatible with STS 'catch' where the exception varilab has to be of type
1131    // 'Exception' or derived from it.
1132    // So each 'catch' which has explicite type for the exception object goes to problems in strict mode.
1133    if (tsCatch.variableDeclaration && tsCatch.variableDeclaration.type) {
1134      this.incrementCounters(node, FaultID.CatchWithUnsupportedType);
1135    }
1136  }
1137
1138  private handleClassDeclaration(node: Node): void {
1139    const tsClassDecl = node as ClassDeclaration;
1140    this.staticBlocks.clear();
1141
1142    if (tsClassDecl.name) {
1143      this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl);
1144    }
1145    this.countClassMembersWithDuplicateName(tsClassDecl);
1146
1147    const visitHClause = (hClause: HeritageClause) => {
1148      for (const tsTypeExpr of hClause.types) {
1149        const tsExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression);
1150        if (tsExprType.isClass() && hClause.token === SyntaxKind.ImplementsKeyword) {
1151          this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass);
1152        }
1153      }
1154    };
1155
1156    if (tsClassDecl.heritageClauses) {
1157      for (const hClause of tsClassDecl.heritageClauses) {
1158        if (!hClause) {
1159          continue;
1160        }
1161        visitHClause(hClause);
1162      }
1163    }
1164
1165    this.handleDecorators(ts.getDecorators(tsClassDecl));
1166
1167    if (!this.skipArkTSStaticBlocksCheck) {
1168      let hasStaticBlock = false;
1169      for (const element of tsClassDecl.members) {
1170        if (ts.isClassStaticBlockDeclaration(element)) {
1171          if (hasStaticBlock) {
1172            this.incrementCounters(element, FaultID.MultipleStaticBlocks);
1173          } else {
1174            hasStaticBlock = true;
1175          }
1176        }
1177      }
1178    }
1179  }
1180
1181  private handleModuleDeclaration(node: Node): void {
1182    const tsModuleDecl = node as ModuleDeclaration;
1183    this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl);
1184
1185    const tsModuleBody = tsModuleDecl.body;
1186    const tsModifiers = tsModuleDecl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method
1187    if (tsModuleBody) {
1188      if (isModuleBlock(tsModuleBody)) {
1189        for (const tsModuleStmt of tsModuleBody.statements) {
1190          switch (tsModuleStmt.kind) {
1191            case SyntaxKind.VariableStatement:
1192            case SyntaxKind.FunctionDeclaration:
1193            case SyntaxKind.ClassDeclaration:
1194            case SyntaxKind.InterfaceDeclaration:
1195            case SyntaxKind.TypeAliasDeclaration:
1196            case SyntaxKind.EnumDeclaration:
1197            case SyntaxKind.ExportDeclaration:
1198              break;
1199            // Nested namespace declarations are prohibited
1200            // but there is no cookbook recipe for it!
1201            case SyntaxKind.ModuleDeclaration:
1202              break;
1203            default:
1204              this.incrementCounters(tsModuleStmt, FaultID.NonDeclarationInNamespace);
1205              break;
1206          }
1207        }
1208      }
1209    }
1210
1211    if (!(tsModuleDecl.flags & NodeFlags.Namespace) &&
1212        Utils.hasModifier(tsModifiers, SyntaxKind.DeclareKeyword)) {
1213      this.incrementCounters(tsModuleDecl, FaultID.ShorthandAmbientModuleDecl);
1214    }
1215
1216    if (isStringLiteral(tsModuleDecl.name) && tsModuleDecl.name.text.includes("*")) {
1217      this.incrementCounters(tsModuleDecl, FaultID.WildcardsInModuleName);
1218    }
1219  }
1220
1221  private handleTypeAliasDeclaration(node: Node): void {
1222    const tsTypeAlias = node as TypeAliasDeclaration;
1223    this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias);
1224  }
1225
1226  private handleImportClause(node: Node): void {
1227    const tsImportClause = node as ImportClause;
1228    if (tsImportClause.name) {
1229      this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause);
1230    }
1231
1232    if (tsImportClause.namedBindings && isNamedImports(tsImportClause.namedBindings)) {
1233      const nonDefaultSpecs: ImportSpecifier[] = [];
1234      let defaultSpec: ImportSpecifier | undefined;
1235      for (const importSpec of tsImportClause.namedBindings.elements) {
1236        if (Utils.isDefaultImport(importSpec)) defaultSpec = importSpec;
1237        else nonDefaultSpecs.push(importSpec);
1238      }
1239      if (defaultSpec) {
1240        let autofix: Autofix[] | undefined;
1241        // if (Autofixer.shouldAutofix(defaultSpec, FaultID.DefaultImport))
1242        //  autofix = [ Autofixer.fixDefaultImport(tsImportClause, defaultSpec, nonDefaultSpecs) ];
1243        this.incrementCounters(defaultSpec, FaultID.DefaultImport, true, autofix);
1244      }
1245    }
1246  }
1247
1248  private handleImportSpecifier(node: Node): void {
1249    const tsImportSpecifier = node as ImportSpecifier;
1250    this.countDeclarationsWithDuplicateName(tsImportSpecifier.name, tsImportSpecifier);
1251  }
1252
1253  private handleNamespaceImport(node: Node): void {
1254    const tsNamespaceImport = node as NamespaceImport;
1255    this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport);
1256  }
1257
1258  private handleTypeAssertionExpression(node: Node): void {
1259    const tsTypeAssertion = node as TypeAssertion;
1260    if (tsTypeAssertion.type.getText() === "const") {
1261      this.incrementCounters(tsTypeAssertion, FaultID.ConstAssertion);
1262    }
1263    else {
1264      this.incrementCounters(node, FaultID.TypeAssertion);
1265    }
1266  }
1267
1268  private handleMethodDeclaration(node: Node): void {
1269    const tsMethodDecl = node as MethodDeclaration;
1270    const hasThis = this.functionContainsThis(tsMethodDecl);
1271    let isStatic = false;
1272    if (tsMethodDecl.modifiers) {
1273      for (const mod of tsMethodDecl.modifiers) {
1274        if (mod.kind === SyntaxKind.StaticKeyword) {
1275          isStatic = true;
1276          break;
1277        }
1278      }
1279    }
1280
1281    if (isStatic && hasThis) {
1282      this.incrementCounters(node, FaultID.FunctionContainsThis);
1283    }
1284    if (!tsMethodDecl.type) {
1285      this.handleMissingReturnType(tsMethodDecl);
1286    }
1287    if (tsMethodDecl.asteriskToken) {
1288      this.incrementCounters(node, FaultID.GeneratorFunction);
1289    }
1290    this.handleDecorators(ts.getDecorators(tsMethodDecl));
1291    this.filterOutDecoratorsDiagnostics(ts.getDecorators(tsMethodDecl), Utils.NON_RETURN_FUNCTION_DECORATORS,
1292      { begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end },
1293      Utils.FUNCTION_HAS_NO_RETURN_ERROR_CODE);
1294  }
1295
1296  private handleIdentifier(node: Node): void {
1297    const tsIdentifier = node as Identifier;
1298    const tsIdentSym = Utils.trueSymbolAtLocation(tsIdentifier);
1299
1300    if (!tsIdentSym) {
1301      return;
1302    }
1303
1304    if (
1305      (tsIdentSym.flags & ts.SymbolFlags.Module) !== 0 &&
1306      (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0 &&
1307      tsIdentifier.text === "globalThis"
1308    ) {
1309      this.incrementCounters(node, FaultID.GlobalThis);
1310    } else {
1311      this.handleRestrictedValues(tsIdentifier, tsIdentSym);
1312    }
1313  }
1314
1315  private isAllowedClassValueContext(tsIdentifier: Identifier /* Param not used ! ??, tsIdentSym: Symbol */): boolean {
1316    let ctx: Node = tsIdentifier;
1317    while (isPropertyAccessExpression(ctx.parent) || isQualifiedName(ctx.parent)) {
1318      ctx = ctx.parent;
1319    }
1320    if (isPropertyAssignment(ctx.parent) && isObjectLiteralExpression(ctx.parent.parent)) {
1321      ctx = ctx.parent.parent;
1322    }
1323    if (isArrowFunction(ctx.parent) && ctx.parent.body === ctx) {
1324      ctx = ctx.parent;
1325    }
1326
1327    if (isCallExpression(ctx.parent) || isNewExpression(ctx.parent)) {
1328      const callee = ctx.parent.expression;
1329      const isAny = Utils.isAnyType(TypeScriptLinter.tsTypeChecker.getTypeAtLocation(callee));
1330      const isDynamic = isAny || Utils.hasLibraryType(callee);
1331      if (callee !== ctx && isDynamic) {
1332        return true;
1333      }
1334    }
1335    return false;
1336  }
1337
1338  private handleRestrictedValues(tsIdentifier: Identifier, tsIdentSym: Symbol) {
1339    const illegalValues = SymbolFlags.ConstEnum | SymbolFlags.RegularEnum | SymbolFlags.ValueModule | SymbolFlags.Class;
1340    // If module name is duplicated by another declaration, this increases the possibility
1341    // of finding a lot of false positives. Thus, do not check further in that case.
1342    if ((tsIdentSym.flags & SymbolFlags.ValueModule) !== 0) {
1343      if (!!tsIdentSym && Utils.symbolHasDuplicateName(tsIdentSym, SyntaxKind.ModuleDeclaration)) {
1344        return;
1345      }
1346    }
1347    if ((tsIdentSym.flags & illegalValues) === 0 || Utils.isStruct(tsIdentSym) ||
1348        !this.identiferUseInValueContext(tsIdentifier, tsIdentSym)) {
1349          return;
1350    }
1351    if ((tsIdentSym.flags & SymbolFlags.Class) !== 0) {
1352      if (this.isAllowedClassValueContext(tsIdentifier /* param not used!?? , tsIdentSym */)) {
1353        return;
1354      }
1355    }
1356
1357    if (tsIdentSym.flags & SymbolFlags.ValueModule) {
1358      this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject);
1359    }
1360    else {
1361      // missing EnumAsObject
1362      this.incrementCounters(tsIdentifier, FaultID.ClassAsObject);
1363    }
1364  }
1365
1366  private identiferUseInValueContext(
1367    ident: Identifier,
1368    tsSym: Symbol
1369  ) {
1370    // If identifier is the right-most name of Property Access chain or Qualified name,
1371    // or it's a separate identifier expression, then identifier is being referenced as an value.
1372    let qualifiedStart: Node = ident;
1373    while (isPropertyAccessExpression(qualifiedStart.parent) || isQualifiedName(qualifiedStart.parent)) {
1374      qualifiedStart = qualifiedStart.parent;
1375    }
1376    const parent = qualifiedStart.parent;
1377    return !(
1378      // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery)
1379      (isTypeNode(parent) && !isTypeOfExpression(parent)) ||
1380      // ElementAccess is allowed for enum types
1381      this.isEnumPropAccess(ident, tsSym, parent) ||
1382      isExpressionWithTypeArguments(parent) ||
1383      isExportAssignment(parent) ||
1384      isExportSpecifier(parent) ||
1385      isMetaProperty(parent) ||
1386      isImportClause(parent) ||
1387      isClassLike(parent) ||
1388      isInterfaceDeclaration(parent) ||
1389      isModuleDeclaration(parent) ||
1390      isEnumDeclaration(parent) ||
1391      isNamespaceImport(parent) ||
1392      isImportSpecifier(parent) ||
1393      isImportEqualsDeclaration(parent) ||
1394      (isQualifiedName(qualifiedStart) && ident !== qualifiedStart.right) ||
1395      (isPropertyAccessExpression(qualifiedStart) &&
1396        ident !== qualifiedStart.name) ||
1397      (isNewExpression(qualifiedStart.parent) &&
1398        qualifiedStart === qualifiedStart.parent.expression) ||
1399      (isBinaryExpression(qualifiedStart.parent) &&
1400        qualifiedStart.parent.operatorToken.kind ===
1401        SyntaxKind.InstanceOfKeyword)
1402    );
1403  }
1404
1405  private isEnumPropAccess(ident: ts.Identifier, tsSym: ts.Symbol, context: ts.Node): boolean {
1406    return ts.isElementAccessExpression(context) && !!(tsSym.flags & ts.SymbolFlags.Enum) &&
1407      (context.expression == ident ||
1408        (ts.isPropertyAccessExpression(context.expression) && context.expression.name == ident));
1409  }
1410
1411  private handleElementAccessExpression(node: Node): void {
1412    const tsElementAccessExpr = node as ElementAccessExpression;
1413    const tsElemAccessBaseExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.expression);
1414    const tsElemAccessBaseExprTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(tsElemAccessBaseExprType, undefined, NodeBuilderFlags.None);
1415    const isDerivedFromArray = Utils.isDerivedFrom(tsElemAccessBaseExprType, Utils.CheckType.Array);
1416    const checkClassOrInterface = tsElemAccessBaseExprType.isClassOrInterface() &&
1417                                  !Utils.isGenericArrayType(tsElemAccessBaseExprType) &&
1418                                  !isDerivedFromArray;
1419    const checkThisOrSuper = Utils.isThisOrSuperExpr(tsElementAccessExpr.expression) && !isDerivedFromArray;
1420
1421    // if (this.tsUtils.isEnumType(tsElemAccessBaseExprType)) {
1422    //   implement argument expression type check
1423    //   let argType = this.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.argumentExpression);
1424    //   if (argType.aliasSymbol == this.tsUtils.trueSymbolAtLocation(tsElementAccessExpr.expression)) {
1425    //     return;
1426    //   }
1427    //   check if constant EnumMember inferred ...
1428    //   this.incrementCounters(node, FaultID.PropertyAccessByIndex, autofixable, autofix);
1429    // }
1430    if (
1431        !Utils.isLibraryType(tsElemAccessBaseExprType) && !Utils.isTypedArray(tsElemAccessBaseExprTypeNode) &&
1432          (checkClassOrInterface ||
1433            Utils.isObjectLiteralType(tsElemAccessBaseExprType) || checkThisOrSuper)
1434        ) {
1435      let autofix = Autofixer.fixPropertyAccessByIndex(node);
1436      const autofixable = autofix !== undefined;
1437      if (!Autofixer.shouldAutofix(node, FaultID.PropertyAccessByIndex)) {
1438        autofix = undefined;
1439      }
1440      this.incrementCounters(node, FaultID.PropertyAccessByIndex, autofixable, autofix);
1441    }
1442    if (Utils.hasEsObjectType(tsElementAccessExpr.expression)) {
1443      this.incrementCounters(node, FaultID.EsObjectType);
1444    }
1445  }
1446
1447  private handleEnumMember(node: Node): void {
1448    const tsEnumMember = node as EnumMember;
1449    const tsEnumMemberType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsEnumMember);
1450    const constVal = TypeScriptLinter.tsTypeChecker.getConstantValue(tsEnumMember);
1451
1452    if (tsEnumMember.initializer && !Utils.isValidEnumMemberInit(tsEnumMember.initializer)) {
1453      this.incrementCounters(node, FaultID.EnumMemberNonConstInit);
1454    }
1455    // check for type - all members should be of same type
1456    const enumDecl = tsEnumMember.parent;
1457    const firstEnumMember = enumDecl.members[0];
1458    const firstEnumMemberType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(firstEnumMember);
1459    const firstElewmVal = TypeScriptLinter.tsTypeChecker.getConstantValue(firstEnumMember);
1460    // each string enum member has its own type
1461    // so check that value type is string
1462    if(constVal !==undefined && typeof constVal === "string" &&
1463        firstElewmVal !==undefined && typeof firstElewmVal === "string") {
1464      return;
1465    }
1466    if (constVal !==undefined && typeof constVal === "number" &&
1467        firstElewmVal !==undefined && typeof firstElewmVal === "number") {
1468      return;
1469    }
1470    if(firstEnumMemberType !== tsEnumMemberType) {
1471      this.incrementCounters(node, FaultID.EnumMemberNonConstInit);
1472    }
1473  }
1474
1475  private handleExportAssignment(node: Node): void {
1476    // (nsizov): check exportEquals and determine if it's an actual `export assignment`
1477    //   or a `default export namespace` when this two cases will be determined in cookbook
1478    const exportAssignment = node as ExportAssignment;
1479    if (exportAssignment.isExportEquals) {
1480      this.incrementCounters(node, FaultID.ExportAssignment);
1481    }
1482  }
1483
1484  private handleCallExpression(node: Node): void {
1485    const tsCallExpr = node as CallExpression;
1486
1487    const calleeSym = Utils.trueSymbolAtLocation(tsCallExpr.expression);
1488    const calleeType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression);
1489    const callSignature = TypeScriptLinter.tsTypeChecker.getResolvedSignature(tsCallExpr);
1490
1491    this.handleImportCall(tsCallExpr);
1492    this.handleRequireCall(tsCallExpr);
1493    // NOTE: Keep handleFunctionApplyBindPropCall above handleGenericCallWithNoTypeArgs here!!!
1494    if (calleeSym !== undefined) {
1495      this.handleStdlibAPICall(tsCallExpr, calleeSym);
1496      this.handleFunctionApplyBindPropCall(tsCallExpr, calleeSym);
1497      if (Utils.symbolHasEsObjectType(calleeSym)) {
1498        this.incrementCounters(tsCallExpr, FaultID.EsObjectType);
1499      }
1500    }
1501    if (callSignature !== undefined) {
1502      if (!Utils.isLibrarySymbol(calleeSym)) {
1503        this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature);
1504      }
1505      this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature);
1506    }
1507    this.handleLibraryTypeCall(tsCallExpr, calleeType);
1508
1509    if (ts.isPropertyAccessExpression(tsCallExpr.expression) && Utils.hasEsObjectType(tsCallExpr.expression.expression)) {
1510      this.incrementCounters(node, FaultID.EsObjectType);
1511    }
1512  }
1513
1514  private handleImportCall(tsCallExpr: CallExpression): void {
1515    if (tsCallExpr.expression.kind === SyntaxKind.ImportKeyword) {
1516      // relax rule#133 "arkts-no-runtime-import"
1517      // this.incrementCounters(tsCallExpr, FaultID.DynamicImport);
1518      const tsArgs = tsCallExpr.arguments;
1519      if (tsArgs.length > 1 && isObjectLiteralExpression(tsArgs[1])) {
1520        const objLitExpr = tsArgs[1] as ObjectLiteralExpression;
1521        for (const tsProp of objLitExpr.properties) {
1522          if (isPropertyAssignment(tsProp) || isShorthandPropertyAssignment(tsProp)) {
1523            if (tsProp.name.getText() === "assert") {
1524              this.incrementCounters(tsProp, FaultID.ImportAssertion);
1525              break;
1526            }
1527          }
1528        }
1529      }
1530    }
1531  }
1532
1533  private handleRequireCall(tsCallExpr: CallExpression): void {
1534    if (
1535      isIdentifier(tsCallExpr.expression) &&
1536      tsCallExpr.expression.text === "require" &&
1537      isVariableDeclaration(tsCallExpr.parent)
1538    ) {
1539      const tsType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression);
1540      if (Utils.isInterfaceType(tsType) && tsType.symbol.name === "NodeRequire") {
1541        this.incrementCounters(tsCallExpr.parent, FaultID.ImportAssignment);
1542      }
1543    }
1544  }
1545
1546  private handleGenericCallWithNoTypeArgs(callLikeExpr: ts.CallExpression | ts.NewExpression, callSignature: ts.Signature) {
1547
1548    const tsSyntaxKind = isNewExpression(callLikeExpr) ? SyntaxKind.Constructor : SyntaxKind.FunctionDeclaration;
1549    const signDecl = TypeScriptLinter.tsTypeChecker.signatureToSignatureDeclaration(callSignature, tsSyntaxKind,
1550        undefined, NodeBuilderFlags.WriteTypeArgumentsOfSignature | NodeBuilderFlags.IgnoreErrors);
1551
1552    if (signDecl?.typeArguments) {
1553      const resolvedTypeArgs = signDecl.typeArguments;
1554
1555      const startTypeArg = callLikeExpr.typeArguments?.length ?? 0;
1556      for (let i = startTypeArg; i < resolvedTypeArgs.length; ++i) {
1557        const typeNode = resolvedTypeArgs[i];
1558        // if compiler infers 'unknown' type there are 2 possible cases:
1559        //   1. Compiler unable to infer type from arguments and use 'unknown'
1560        //   2. Compiler infer 'unknown' from arguments
1561        // We report error in both cases. It is ok because we cannot use 'unknown'
1562        // in ArkTS and already have separate check for it.
1563        if (typeNode.kind == ts.SyntaxKind.UnknownKeyword) {
1564          this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs);
1565          break;
1566        }
1567      }
1568    }
1569  }
1570
1571  private static listApplyBindCallApis = [
1572    "Function.apply",
1573    "Function.call",
1574    "Function.bind",
1575    "CallableFunction.apply",
1576    "CallableFunction.call",
1577    "CallableFunction.bind"
1578  ];
1579  private handleFunctionApplyBindPropCall(tsCallExpr: ts.CallExpression, calleeSym: ts.Symbol) {
1580    const exprName = TypeScriptLinter.tsTypeChecker.getFullyQualifiedName(calleeSym);
1581    if (TypeScriptLinter.listApplyBindCallApis.includes(exprName)) {
1582      this.incrementCounters(tsCallExpr, FaultID.FunctionApplyBindCall);
1583    }
1584  }
1585
1586  private handleStructIdentAndUndefinedInArgs(tsCallOrNewExpr: ts.CallExpression | ts.NewExpression, callSignature: ts.Signature) {
1587    if (!tsCallOrNewExpr.arguments) {
1588      return;
1589    }
1590
1591    for (let argIndex = 0; argIndex < tsCallOrNewExpr.arguments.length; ++argIndex) {
1592      const tsArg = tsCallOrNewExpr.arguments[argIndex];
1593      const tsArgType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsArg);
1594      if (!tsArgType) continue;
1595
1596      let paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length-1;
1597      let tsParamSym = callSignature.parameters[paramIndex];
1598      if (!tsParamSym) continue;
1599
1600      const tsParamDecl = tsParamSym.valueDeclaration;
1601      if (tsParamDecl && isParameter(tsParamDecl)) {
1602        let tsParamType = TypeScriptLinter.tsTypeChecker.getTypeOfSymbolAtLocation(tsParamSym, tsParamDecl);
1603        if (tsParamDecl.dotDotDotToken && Utils.isGenericArrayType(tsParamType) && tsParamType.typeArguments) {
1604          tsParamType = tsParamType.typeArguments[0];
1605        }
1606        if (!tsParamType) continue;
1607
1608        if (Utils.needToDeduceStructuralIdentity(tsArgType, tsParamType)){
1609          this.incrementCounters(tsArg, FaultID.StructuralIdentity);
1610        }
1611      }
1612    }
1613  }
1614
1615  // let re = new RegExp("^(" + arr.reduce((acc, v) => ((acc ? (acc + "|") : "") + v)) +")$")
1616  private static LimitedApis = new Map<string, {arr: Array<string> | null, fault: FaultID}> ([
1617    ["global", {arr: Utils.LIMITED_STD_GLOBAL_FUNC, fault: FaultID.LimitedStdLibApi}],
1618    ["Object", {arr: Utils.LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}],
1619    ["ObjectConstructor", {arr: Utils.LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}],
1620    ["Reflect", {arr: Utils.LIMITED_STD_REFLECT_API, fault: FaultID.LimitedStdLibApi}],
1621    ["ProxyHandler", {arr: Utils.LIMITED_STD_PROXYHANDLER_API, fault: FaultID.LimitedStdLibApi}],
1622    ["Symbol", {arr: null, fault: FaultID.SymbolType}],
1623    ["SymbolConstructor", {arr: null, fault: FaultID.SymbolType}],
1624  ])
1625
1626  private handleStdlibAPICall(callExpr: ts.CallExpression, calleeSym: ts.Symbol) {
1627    const name = calleeSym.getName();
1628    const parName = Utils.getParentSymbolName(calleeSym);
1629    if (parName === undefined) {
1630      if (Utils.LIMITED_STD_GLOBAL_FUNC.includes(name)) {
1631        this.incrementCounters(callExpr, FaultID.LimitedStdLibApi);
1632        return;
1633      }
1634      let escapedName = calleeSym.escapedName;
1635      if (escapedName === 'Symbol' || escapedName === 'SymbolConstructor') {
1636        this.incrementCounters(callExpr, FaultID.SymbolType);
1637      }
1638      return;
1639    }
1640    let lookup = TypeScriptLinter.LimitedApis.get(parName);
1641    if (lookup !== undefined && (lookup.arr === null || lookup.arr.includes(name))) {
1642      this.incrementCounters(callExpr, lookup.fault);
1643    };
1644  }
1645
1646  private findNonFilteringRangesFunctionCalls(callExpr: ts.CallExpression): { begin: number, end: number }[] {
1647    let args = callExpr.arguments;
1648    let result: { begin: number, end: number }[] = [];
1649    for (const arg of args) {
1650      if (ts.isArrowFunction(arg)) {
1651        let arrowFuncExpr = arg as ts.ArrowFunction;
1652        result.push({begin: arrowFuncExpr.body.pos, end: arrowFuncExpr.body.end});
1653      } else if (ts.isCallExpression(arg)) {
1654        result.push({begin: arg.arguments.pos, end: arg.arguments.end});
1655      }
1656      // there may be other cases
1657    }
1658    return result;
1659  }
1660
1661  private handleLibraryTypeCall(callExpr: ts.CallExpression, calleeType: ts.Type) {
1662    let inLibCall = Utils.isLibraryType(calleeType);
1663    const diagnosticMessages: Array<ts.DiagnosticMessageChain> = []
1664    this.libraryTypeCallDiagnosticChecker.configure(inLibCall, diagnosticMessages);
1665
1666    let nonFilteringRanges = this.findNonFilteringRangesFunctionCalls(callExpr);
1667    let rangesToFilter: { begin: number, end: number }[] = [];
1668    if (nonFilteringRanges.length !== 0) {
1669      let rangesSize = nonFilteringRanges.length;
1670      rangesToFilter.push({ begin: callExpr.arguments.pos, end: nonFilteringRanges[0].begin })
1671      rangesToFilter.push({ begin: nonFilteringRanges[rangesSize - 1].end, end: callExpr.arguments.end })
1672      for (let i = 0; i < rangesSize - 1; i++) {
1673        rangesToFilter.push({ begin: nonFilteringRanges[i].end, end: nonFilteringRanges[i + 1].begin })
1674      }
1675    } else {
1676      rangesToFilter.push({ begin: callExpr.arguments.pos, end: callExpr.arguments.end })
1677    }
1678
1679    this.filterStrictDiagnostics({
1680        [ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE]: (pos: number) => {
1681          return this.checkInRange([{begin: callExpr.pos, end: callExpr.end}], pos);
1682        },
1683        [NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE]: (pos: number) => {
1684          return this.checkInRange([{begin: callExpr.pos, end: callExpr.end}], pos);
1685        },
1686        [TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE]: (pos: number) => {
1687          return this.checkInRange(rangesToFilter, pos);
1688        }
1689      },
1690      this.libraryTypeCallDiagnosticChecker
1691    );
1692
1693    for (const msgChain of diagnosticMessages) {
1694      TypeScriptLinter.filteredDiagnosticMessages.push(msgChain);
1695    }
1696  }
1697
1698  private handleNewExpression(node: Node): void {
1699    const tsNewExpr = node as NewExpression;
1700    let callSignature = TypeScriptLinter.tsTypeChecker.getResolvedSignature(tsNewExpr);
1701    if (callSignature !== undefined) {
1702      this.handleStructIdentAndUndefinedInArgs(tsNewExpr, callSignature);
1703      this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature);
1704    }
1705  }
1706
1707  private handleAsExpression(node: Node): void {
1708    const tsAsExpr = node as AsExpression;
1709    if (tsAsExpr.type.getText() === "const") this.incrementCounters(node, FaultID.ConstAssertion);
1710
1711    const targetType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType();
1712    const exprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType();
1713    if (Utils.needToDeduceStructuralIdentity(exprType, targetType, true)) {
1714      this.incrementCounters(tsAsExpr, FaultID.StructuralIdentity);
1715    }
1716    // check for rule#65:   "number as Number" and "boolean as Boolean" are disabled
1717    if(
1718        (Utils.isNumberType(exprType) && targetType.getSymbol()?.getName() === "Number") ||
1719        (Utils.isBooleanType(exprType) && targetType.getSymbol()?.getName() === "Boolean")
1720    ) {
1721      this.incrementCounters(node, FaultID.TypeAssertion);
1722    }
1723  }
1724
1725  private handleTypeReference(node: Node): void {
1726    const typeRef = node as TypeReferenceNode;
1727
1728    const isESObject = Utils.isEsObjectType(typeRef);
1729    const isPossiblyValidContext = Utils.isEsObjectPossiblyAllowed(typeRef);
1730    if (isESObject && !isPossiblyValidContext) {
1731      this.incrementCounters(node, FaultID.EsObjectType);
1732      return;
1733    }
1734    const typeName = Utils.entityNameToString(typeRef.typeName);
1735    const isStdUtilityType = Utils.LIMITED_STANDARD_UTILITY_TYPES.includes(typeName);
1736    if (isStdUtilityType) {
1737      this.incrementCounters(node, FaultID.UtilityType);
1738      return;
1739    }
1740
1741    // Using Partial<T> type is allowed only when its argument type is either Class or Interface.
1742    const isStdPartial = Utils.entityNameToString(typeRef.typeName) === 'Partial';
1743    const hasSingleTypeArgument = !!typeRef.typeArguments && typeRef.typeArguments.length === 1;
1744    const firstTypeArg = !!typeRef.typeArguments && hasSingleTypeArgument && typeRef.typeArguments[0];
1745    const argType = firstTypeArg && TypeScriptLinter.tsTypeChecker.getTypeFromTypeNode(firstTypeArg);
1746    if (isStdPartial && argType && !argType.isClassOrInterface()) {
1747      this.incrementCounters(node, FaultID.UtilityType);
1748      return;
1749    }
1750  }
1751
1752  private handleMetaProperty(node: Node): void {
1753    const tsMetaProperty = node as MetaProperty;
1754    if (tsMetaProperty.name.text === "target") {
1755      this.incrementCounters(node, FaultID.NewTarget);
1756    }
1757  }
1758
1759  private handleStructDeclaration(node: Node): void {
1760    node.forEachChild(child => {
1761      // Skip synthetic constructor in Struct declaration.
1762      if (!isConstructorDeclaration(child)) this.visitTSNode(child);
1763    });
1764  }
1765
1766  private handleSpreadOp(node: Node) {
1767    // spread assignment is disabled
1768    // spread element is allowed only for arrays as rest parameter
1769    if (isSpreadElement(node)) {
1770      const spreadElemNode = node;
1771      const spreadExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(spreadElemNode.expression);
1772      if (spreadExprType) {
1773        const spreadExprTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(spreadExprType, undefined, NodeBuilderFlags.None);
1774        if (spreadExprTypeNode !== undefined &&
1775              (isCallLikeExpression(node.parent) || isArrayLiteralExpression(node.parent))) {
1776          if (isArrayTypeNode(spreadExprTypeNode) ||
1777              Utils.isTypedArray(spreadExprTypeNode) ||
1778              Utils.isDerivedFrom(spreadExprType, Utils.CheckType.Array)) {
1779            return;
1780          }
1781        }
1782      }
1783    }
1784    this.incrementCounters(node, FaultID.SpreadOperator);
1785  }
1786
1787  private handleConstructSignature(node: Node) {
1788    switch (node.parent.kind) {
1789      case SyntaxKind.TypeLiteral:
1790        this.incrementCounters(node, FaultID.ConstructorType);
1791        break;
1792      case SyntaxKind.InterfaceDeclaration:
1793        this.incrementCounters(node, FaultID.ConstructorIface);
1794        break;
1795      default:
1796        return;
1797    }
1798  }
1799
1800  private handleComments(node: Node) {
1801    // Note: Same comment may be owned by several nodes if their
1802    // start/end position matches. Thus, look for the most parental
1803    // owner of the specific comment (by the node's position).
1804    const srcText = node.getSourceFile().getFullText();
1805
1806    const parent = node.parent;
1807    if (!parent || parent.getFullStart() !== node.getFullStart()) {
1808      const leadingComments = getLeadingCommentRanges(srcText, node.getFullStart());
1809      if (leadingComments) {
1810        for (const comment of leadingComments) {
1811          this.checkErrorSuppressingAnnotation(comment, srcText);
1812        }
1813      }
1814    }
1815
1816    if (!parent || parent.getEnd() !== node.getEnd()) {
1817      const trailingComments = getTrailingCommentRanges(srcText, node.getEnd());
1818      if (trailingComments) {
1819        for (const comment of trailingComments) {
1820          this.checkErrorSuppressingAnnotation(comment, srcText);
1821        }
1822      }
1823    }
1824  }
1825
1826  private handleExpressionWithTypeArguments(node: Node) {
1827    const tsTypeExpr = node as ExpressionWithTypeArguments;
1828    const symbol = Utils.trueSymbolAtLocation(tsTypeExpr.expression);
1829    if (!!symbol && Utils.isEsObjectSymbol(symbol)) {
1830      this.incrementCounters(tsTypeExpr, FaultID.EsObjectType);
1831    }
1832  }
1833
1834  private handleComputedPropertyName(node: ts.Node) {
1835    const computedProperty = node as ts.ComputedPropertyName;
1836    const symbol = Utils.trueSymbolAtLocation(computedProperty.expression);
1837    if (!!symbol && Utils.isSymbolIterator(symbol)) {
1838      return
1839    }
1840    this.incrementCounters(node, FaultID.ComputedPropertyName);
1841  }
1842
1843  private checkErrorSuppressingAnnotation(comment: CommentRange, srcText: string) {
1844    const commentContent = comment.kind === SyntaxKind.MultiLineCommentTrivia
1845      ? srcText.slice(comment.pos + 2, comment.end - 2)
1846      : srcText.slice(comment.pos + 2, comment.end);
1847    // if comment is multiline end closing '*/' is not at the same line as '@ts-xxx' - do nothing (#13851)
1848    if (commentContent.endsWith('\n')) {
1849      return
1850    }
1851    const trimmedContent = commentContent.trim();
1852    if (trimmedContent.startsWith("@ts-ignore") ||
1853        trimmedContent.startsWith("@ts-nocheck") ||
1854        trimmedContent.startsWith("@ts-expect-error")) {
1855      this.incrementCounters(comment, FaultID.ErrorSuppression);
1856    }
1857  }
1858
1859  private handleDecorators(decorators: readonly Decorator[] | undefined): void {
1860    if (!decorators) return;
1861
1862    for (const decorator of decorators) {
1863      let decoratorName = "";
1864      if (isIdentifier(decorator.expression)) {
1865        decoratorName = decorator.expression.text;
1866      }
1867      else if (isCallExpression(decorator.expression) && isIdentifier(decorator.expression.expression)) {
1868        decoratorName = decorator.expression.expression.text;
1869      }
1870      if (!Utils.ARKUI_DECORATORS.includes(decoratorName)) {
1871        this.incrementCounters(decorator, FaultID.UnsupportedDecorators);
1872      }
1873    }
1874  }
1875
1876  private handleGetAccessor(node: Node) {
1877    this.handleDecorators(ts.getDecorators(node as GetAccessorDeclaration));
1878  }
1879
1880  private handleSetAccessor(node: Node) {
1881    this.handleDecorators(ts.getDecorators(node as SetAccessorDeclaration));
1882  }
1883
1884  private handleDeclarationInferredType(
1885    decl: VariableDeclaration | PropertyDeclaration | ParameterDeclaration
1886  ) {
1887    // The type is explicitly specified, no need to check inferred type.
1888    if (decl.type) return;
1889
1890    // issue 13161:
1891    // In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since
1892    // ArkTS doesn't support these types, the type for such variable is simply omitted,
1893    // and we don't report it as an error.
1894    if (isCatchClause(decl.parent)) return;
1895
1896    //Destructuring declarations are not supported, do not process them.
1897    if (isArrayBindingPattern(decl.name) || isObjectBindingPattern(decl.name)) return;
1898
1899    const type = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(decl);
1900    if (type) this.validateDeclInferredType(type, decl);
1901  }
1902
1903  private handleDefiniteAssignmentAssertion(decl: VariableDeclaration | PropertyDeclaration) {
1904    if (decl.exclamationToken !== undefined) {
1905      this.incrementCounters(decl, FaultID.DefiniteAssignment);
1906    }
1907  }
1908
1909  private validatedTypesSet = new Set<Type>();
1910
1911  private checkAnyOrUnknownChildNode(node: ts.Node): boolean {
1912    if (node.kind === ts.SyntaxKind.AnyKeyword ||
1913        node.kind === ts.SyntaxKind.UnknownKeyword) {
1914      return true;
1915    }
1916    const isAnyOrUnknown = forEachChild(node, (child) => {
1917      if (this.checkAnyOrUnknownChildNode(child)) {
1918        return true;
1919      }
1920      return undefined;
1921    });
1922    return !!isAnyOrUnknown;
1923  }
1924
1925  private handleInferredObjectreference(
1926    type: ts.Type,
1927    decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration
1928  ) {
1929    const typeArgs = TypeScriptLinter.tsTypeChecker.getTypeArguments(type as ts.TypeReference);
1930    if (typeArgs) {
1931      const haveAnyOrUnknownNodes = this.checkAnyOrUnknownChildNode(decl);
1932      if (!haveAnyOrUnknownNodes) {
1933        for (const typeArg of typeArgs) {
1934          this.validateDeclInferredType(typeArg, decl);
1935        }
1936      }
1937    }
1938  }
1939
1940  private validateDeclInferredType(
1941    type: Type,
1942    decl: VariableDeclaration | PropertyDeclaration | ParameterDeclaration
1943  ): void {
1944    if (type.aliasSymbol !== undefined) {
1945      return;
1946    }
1947    const isObject = type.flags & ts.TypeFlags.Object;
1948    const isReference = (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference;
1949    if (isObject && isReference) {
1950      this.handleInferredObjectreference(type, decl);
1951      return;
1952    }
1953    if (this.validatedTypesSet.has(type)) {
1954      return;
1955    }
1956    if (type.isUnion()) {
1957      this.validatedTypesSet.add(type);
1958      for (let unionElem of type.types) {
1959        this.validateDeclInferredType(unionElem, decl);
1960      }
1961    }
1962
1963    if (Utils.isAnyType(type)) {
1964      this.incrementCounters(decl, FaultID.AnyType);
1965    }
1966    else if (Utils.isUnknownType(type)) {
1967      this.incrementCounters(decl, FaultID.UnknownType);
1968    }
1969  }
1970
1971  private handleClassStaticBlockDeclaration(node: ts.Node): void {
1972    if (this.skipArkTSStaticBlocksCheck) {
1973      return;
1974    }
1975    const classStaticBlockDecl = node as ts.ClassStaticBlockDeclaration;
1976    if (!ts.isClassDeclaration(classStaticBlockDecl.parent)) {
1977      return;
1978    }
1979    if (this.functionContainsThis(node)) {
1980      this.incrementCounters(node, FaultID.FunctionContainsThis);
1981    }
1982  }
1983
1984  public lint(): void {
1985    this.visitTSNode(this.sourceFile);
1986  }
1987
1988}
1989}
1990}
1991