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