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