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