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 */ 15import { 16 ArrayLiteralExpression, AsExpression, BinaryExpression, BinaryOperatorToken, CallExpression, canHaveModifiers, 17 CatchClause, ClassDeclaration, CommentRange, ComputedPropertyName, ConditionalExpression, ConstructorDeclaration, 18 Declaration, Decorator, Diagnostic, ElementAccessExpression, EntityName, EnumMember, ESMap, Expression, 19 ExpressionWithTypeArguments, Extension, flattenDiagnosticMessageText, ForInStatement, FunctionDeclaration, 20 FunctionExpression, getAllDecorators, getAnyExtensionFromPath, getBaseFileName, getCombinedNodeFlags, getDecorators, 21 getLineAndCharacterOfPosition, getModifiers, getPathComponents, HasDecorators, hasIndexSignature, HasModifiers, 22 Identifier, idText, IfStatement, ImportSpecifier, IndexSignatureDeclaration, isArrayLiteralExpression, 23 isArrayTypeNode, isArrowFunction, isAsExpression, isBinaryExpression, isBlock, isCallExpression, 24 isCallLikeExpression, isClassDeclaration, isComputedPropertyName, isConstructorDeclaration, isEnumDeclaration, 25 isExportAssignment, isExportDeclaration, isExpressionStatement, isForInStatement, isForOfStatement, isForStatement, 26 isFunctionDeclaration, isFunctionExpression, isFunctionTypeNode, isGetAccessorDeclaration, isIdentifier, 27 isImportDeclaration, isInterfaceDeclaration, isIntersectionTypeNode, isLiteralTypeNode, isMemberName, 28 isMethodDeclaration, isModuleBlock, isNamedExports, isNamedTupleMember, isNumericLiteral, isObjectLiteralExpression, 29 isParenthesizedExpression, isParenthesizedTypeNode, isPropertyAccessExpression, isPropertyAssignment, 30 isPropertyDeclaration, isPropertySignature, isSetAccessorDeclaration, isSourceFile, isStringLiteral, isStringLiteralLike, isTupleTypeNode, 31 isTypeAliasDeclaration, isTypeLiteralNode, isTypeNode, isTypeQueryNode, isTypeReferenceNode, isUnionTypeNode, 32 isVariableDeclaration, isVariableDeclarationList, isVariableStatement, Map, MethodDeclaration, Modifier, 33 NamedDeclaration, Node, NodeArray, NodeBuilderFlags, NodeFlags, normalizePath, NumericLiteral, ObjectFlags, ObjectLiteralExpression, ObjectType, 34 PrefixUnaryExpression, PrefixUnaryOperator, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, 35 ResolvedModuleFull, ScriptKind, Set, SourceFile, Statement, Symbol, SymbolFlags, symbolName, SyntaxKind, Type, 36 TypeAliasDeclaration, TypeAssertion, TypeChecker, TypeFlags, TypeNode, TypeReference, UnionType, 37 WithStatement, 38} from "../_namespaces/ts"; 39import { AutofixInfo, FaultID, ProblemInfo } from "../_namespaces/ts.ArkTSLinter_1_1"; 40 41//import * as path from 'node:path'; 42//import * as ts from 'typescript'; 43//import ProblemInfo = ts.ProblemInfo; 44//import TypeScriptLinter = TypeScriptLinter; 45 46export const PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE = 2564; 47 48export const NON_INITIALIZABLE_PROPERTY_DECORATORS = ['Link', 'Consume', 'ObjectLink', 'Prop', 'BuilderParam', 'Param', 'Event', 'Require']; 49 50export const NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS = ['CustomDialog'] 51 52export const LIMITED_STANDARD_UTILITY_TYPES = [ 53 "Awaited", "Pick", "Omit", "Exclude", "Extract", "NonNullable", "Parameters", 54 "ConstructorParameters", "ReturnType", "InstanceType", "ThisParameterType", "OmitThisParameter", 55 "ThisType", "Uppercase", "Lowercase", "Capitalize", "Uncapitalize", 56]; 57 58export const ALLOWED_STD_SYMBOL_API = ["iterator"] 59 60export const ARKTS_IGNORE_DIRS = ['node_modules', 'oh_modules', 'build', '.preview']; 61export const ARKTS_IGNORE_FILES = ['hvigorfile.ts']; 62 63export const ARKTS_IGNORE_DIRS_OH_MODULES = 'oh_modules'; 64 65export const SENDABLE_DECORATOR = 'Sendable'; 66 67export const SENDABLE_INTERFACE = 'ISendable'; 68 69export const PROMISE = 'Promise'; 70 71export const SENDABLE_DECORATOR_NODES = [ 72 SyntaxKind.ClassDeclaration, 73 SyntaxKind.FunctionDeclaration, 74 SyntaxKind.TypeAliasDeclaration 75]; 76 77export const SENDABLE_CLOSURE_DECLS = [ 78 SyntaxKind.ClassDeclaration, 79 SyntaxKind.FunctionDeclaration 80]; 81 82export const ARKTS_COLLECTIONS_D_ETS = '@arkts.collections.d.ets'; 83 84export const COLLECTIONS_NAMESPACE = 'collections'; 85 86export const ARKTS_LANG_D_ETS = '@arkts.lang.d.ets'; 87 88export const LANG_NAMESPACE = 'lang'; 89 90export const ISENDABLE_TYPE = 'ISendable'; 91 92export const USE_SHARED = 'use shared'; 93 94export const D_TS = '.d.ts'; 95 96export const TASKPOOL = 'taskpool'; 97 98export const TASKGROUP = 'TaskGroup'; 99 100export const TASKPOOL_API = ['Task', 'LongTask', 'GenericsTask', 'execute', 'addTask']; 101 102export const TASK_LIST = ['Task', 'LongTask', 'GenericsTask', 'TaskGroup']; 103 104export const CONCURRENT_DECORATOR = 'Concurrent'; 105 106export const USE_CONCURRENT = 'use concurrent' 107 108 109let typeChecker: TypeChecker; 110export function setTypeChecker(tsTypeChecker: TypeChecker): void { 111 typeChecker = tsTypeChecker; 112} 113 114export function clearTypeChecker(): void { 115 typeChecker = {} as TypeChecker; 116} 117 118let testMode = false; 119export function setTestMode(tsTestMode: boolean): void { 120 testMode = tsTestMode; 121} 122 123let mixCompile = false; 124export function setMixCompile(isMixCompile: boolean): void { 125 mixCompile = isMixCompile; 126} 127 128export function getStartPos(nodeOrComment: Node | CommentRange): number { 129 return (nodeOrComment.kind === SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === SyntaxKind.MultiLineCommentTrivia) 130 ? (nodeOrComment as CommentRange).pos 131 : (nodeOrComment as Node).getStart(); 132} 133 134export function getEndPos(nodeOrComment: Node | CommentRange): number { 135 return (nodeOrComment.kind === SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === SyntaxKind.MultiLineCommentTrivia) 136 ? (nodeOrComment as CommentRange).end 137 : (nodeOrComment as Node).getEnd(); 138} 139 140export function getHighlightRange(nodeOrComment: Node | CommentRange, faultId: number): [number, number] { 141 return ( 142 highlightRangeHandlers.get(faultId)?.call(undefined, nodeOrComment) ?? [ 143 getStartPos(nodeOrComment), 144 getEndPos(nodeOrComment) 145 ] 146 ); 147} 148 149const highlightRangeHandlers = new Map([ 150 [FaultID.VarDeclaration, getVarDeclarationHighlightRange], 151 [FaultID.CatchWithUnsupportedType, getCatchWithUnsupportedTypeHighlightRange], 152 [FaultID.ForInStatement, getForInStatementHighlightRange], 153 [FaultID.WithStatement, getWithStatementHighlightRange], 154 [FaultID.DeleteOperator, getDeleteOperatorHighlightRange], 155 [FaultID.TypeQuery, getTypeQueryHighlightRange], 156 [FaultID.InstanceofUnsupported, getInstanceofUnsupportedHighlightRange], 157 [FaultID.ConstAssertion, getConstAssertionHighlightRange], 158 [FaultID.LimitedReturnTypeInference, getLimitedReturnTypeInferenceHighlightRange], 159 [FaultID.LocalFunction, getLocalFunctionHighlightRange], 160 [FaultID.FunctionBind, getFunctionApplyCallHighlightRange], 161 [FaultID.FunctionApplyCall, getFunctionApplyCallHighlightRange], 162 [FaultID.DeclWithDuplicateName, getDeclWithDuplicateNameHighlightRange], 163 [FaultID.ObjectLiteralNoContextType, getObjectLiteralNoContextTypeHighlightRange], 164 [FaultID.ClassExpression, getClassExpressionHighlightRange], 165 [FaultID.MultipleStaticBlocks, getMultipleStaticBlocksHighlightRange], 166 [FaultID.SendableDefiniteAssignment, getSendableDefiniteAssignmentHighlightRange] 167]); 168 169export function getVarDeclarationHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 170 return getKeywordHighlightRange(nodeOrComment, 'var'); 171} 172 173export function getCatchWithUnsupportedTypeHighlightRange( 174 nodeOrComment: Node | CommentRange 175): [number, number] | undefined { 176 const catchClauseNode = (nodeOrComment as CatchClause).variableDeclaration; 177 if (catchClauseNode !== undefined) { 178 return [catchClauseNode.getStart(), catchClauseNode.getEnd()]; 179 } 180 181 return undefined; 182} 183 184export function getForInStatementHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 185 return [ 186 getEndPos((nodeOrComment as ForInStatement).initializer) + 1, 187 getStartPos((nodeOrComment as ForInStatement).expression) - 1 188 ]; 189} 190 191export function getWithStatementHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 192 return [getStartPos(nodeOrComment), (nodeOrComment as WithStatement).statement.getStart() - 1]; 193} 194 195export function getDeleteOperatorHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 196 return getKeywordHighlightRange(nodeOrComment, 'delete'); 197} 198 199export function getTypeQueryHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 200 return getKeywordHighlightRange(nodeOrComment, 'typeof'); 201} 202 203export function getInstanceofUnsupportedHighlightRange( 204 nodeOrComment: Node | CommentRange 205): [number, number] | undefined { 206 return getKeywordHighlightRange((nodeOrComment as BinaryExpression).operatorToken, 'instanceof'); 207} 208 209export function getConstAssertionHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 210 if (nodeOrComment.kind === SyntaxKind.AsExpression) { 211 return [ 212 (nodeOrComment as AsExpression).expression.getEnd() + 1, 213 (nodeOrComment as AsExpression).type.getStart() - 1 214 ]; 215 } 216 return [ 217 (nodeOrComment as TypeAssertion).expression.getEnd() + 1, 218 (nodeOrComment as TypeAssertion).type.getEnd() + 1 219 ]; 220} 221 222export function getLimitedReturnTypeInferenceHighlightRange( 223 nodeOrComment: Node | CommentRange 224): [number, number] | undefined { 225 let node: Node | undefined; 226 if (nodeOrComment.kind === SyntaxKind.FunctionExpression) { 227 // we got error about return type so it should be present 228 node = (nodeOrComment as FunctionExpression).type; 229 } else if (nodeOrComment.kind === SyntaxKind.FunctionDeclaration) { 230 node = (nodeOrComment as FunctionDeclaration).name; 231 } else if (nodeOrComment.kind === SyntaxKind.MethodDeclaration) { 232 node = (nodeOrComment as MethodDeclaration).name; 233 } 234 if (node !== undefined) { 235 return [node.getStart(), node.getEnd()]; 236 } 237 238 return undefined; 239} 240 241export function getLocalFunctionHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 242 return getKeywordHighlightRange(nodeOrComment, 'function'); 243} 244 245export function getFunctionApplyCallHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 246 const pointPos = (nodeOrComment as Node).getText().lastIndexOf('.'); 247 return [getStartPos(nodeOrComment) + pointPos + 1, getEndPos(nodeOrComment)]; 248} 249 250export function getDeclWithDuplicateNameHighlightRange( 251 nodeOrComment: Node | CommentRange 252): [number, number] | undefined { 253 // in case of private identifier no range update is needed 254 const nameNode: Node | undefined = (nodeOrComment as NamedDeclaration).name; 255 if (nameNode !== undefined) { 256 return [nameNode.getStart(), nameNode.getEnd()]; 257 } 258 259 return undefined; 260} 261 262export function getObjectLiteralNoContextTypeHighlightRange( 263 nodeOrComment: Node | CommentRange 264): [number, number] | undefined { 265 return getKeywordHighlightRange(nodeOrComment, '{'); 266} 267 268export function getClassExpressionHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 269 return getKeywordHighlightRange(nodeOrComment, 'class'); 270} 271 272export function getMultipleStaticBlocksHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined { 273 return getKeywordHighlightRange(nodeOrComment, 'static'); 274} 275 276// highlight ranges for Sendable rules 277export function getSendableDefiniteAssignmentHighlightRange( 278 nodeOrComment: Node | CommentRange 279): [number, number] | undefined { 280 const name = (nodeOrComment as PropertyDeclaration).name; 281 const exclamationToken = (nodeOrComment as PropertyDeclaration).exclamationToken; 282 return [name.getStart(), exclamationToken ? exclamationToken.getEnd() : name.getEnd()]; 283} 284 285export function getKeywordHighlightRange(nodeOrComment: Node | CommentRange, keyword: string): [number, number] { 286 const start = getStartPos(nodeOrComment); 287 return [start, start + keyword.length]; 288} 289 290export function isAssignmentOperator(tsBinOp: BinaryOperatorToken): boolean { 291 return tsBinOp.kind >= SyntaxKind.FirstAssignment && tsBinOp.kind <= SyntaxKind.LastAssignment; 292} 293 294export function isType(tsType: TypeNode | undefined, checkType: string): boolean { 295 if (tsType === undefined || !isTypeReferenceNode(tsType)) { 296 return false; 297 } 298 return entityNameToString(tsType.typeName) === checkType; 299} 300 301export function entityNameToString(name: EntityName): string { 302 if (isIdentifier(name)) { 303 return name.escapedText.toString(); 304 } 305 else { 306 return entityNameToString(name.left) + entityNameToString(name.right); 307 } 308} 309 310export function isNumberLikeType(tsType: Type): boolean { 311 return (tsType.getFlags() & TypeFlags.NumberLike) !== 0; 312} 313 314export function isBooleanLikeType(tsType: Type): boolean { 315 return (tsType.getFlags() & TypeFlags.BooleanLike) !== 0; 316} 317 318export function isStringLikeType(tsType: Type): boolean { 319 if (tsType.isUnion()) { 320 for (const tsCompType of tsType.types) { 321 if ((tsCompType.flags & TypeFlags.StringLike) === 0) return false; 322 } 323 return true; 324 } 325 return (tsType.getFlags() & TypeFlags.StringLike) !== 0; 326} 327 328export function isStringType(tsType: Type): boolean { 329 if ((tsType.getFlags() & TypeFlags.String) !== 0) { 330 return true; 331 } 332 333 if (!isTypeReference(tsType)) { 334 return false; 335 } 336 337 const symbol = tsType.symbol; 338 const name = typeChecker.getFullyQualifiedName(symbol); 339 return name === 'String' && isGlobalSymbol(symbol); 340} 341 342export function isPrimitiveEnumMemberType(type: Type, primitiveType: TypeFlags): boolean { 343 const isNonPrimitive = (type.flags & TypeFlags.NonPrimitive) !== 0; 344 if (!isEnumMemberType(type) || isNonPrimitive) { 345 return false; 346 } 347 return (type.flags & primitiveType) !== 0; 348} 349 350export function unwrapParenthesizedType(tsType: TypeNode): TypeNode { 351 while (isParenthesizedTypeNode(tsType)) { 352 tsType = tsType.type; 353 } 354 return tsType; 355} 356 357export function findParentIf(asExpr: AsExpression): IfStatement | null { 358 let node = asExpr.parent; 359 while (node) { 360 if (node.kind === SyntaxKind.IfStatement) { 361 return node as IfStatement; 362 } 363 node = node.parent; 364 } 365 366 return null; 367} 368 369export function isDestructuringAssignmentLHS( 370 tsExpr: ArrayLiteralExpression | ObjectLiteralExpression 371): boolean { 372 // Check whether given expression is the LHS part of the destructuring 373 // assignment (or is a nested element of destructuring pattern). 374 let tsParent = tsExpr.parent; 375 let tsCurrentExpr: Node = tsExpr; 376 while (tsParent) { 377 if ( 378 isBinaryExpression(tsParent) && isAssignmentOperator(tsParent.operatorToken) && 379 tsParent.left === tsCurrentExpr 380 ) { 381 return true; 382 } 383 if ( 384 (isForStatement(tsParent) || isForInStatement(tsParent) || isForOfStatement(tsParent)) && 385 tsParent.initializer && tsParent.initializer === tsCurrentExpr 386 ) { 387 return true; 388 } 389 tsCurrentExpr = tsParent; 390 tsParent = tsParent.parent; 391 } 392 393 return false; 394} 395 396export function isEnumType(tsType: Type): boolean { 397 // when type equals `typeof <Enum>`, only symbol contains information about it's type. 398 const isEnumSymbol = tsType.symbol && isEnum(tsType.symbol); 399 // otherwise, we should analyze flags of the type itself 400 const isEnumType = !!(tsType.flags & TypeFlags.Enum) || !!(tsType.flags & TypeFlags.EnumLiteral); 401 return isEnumSymbol || isEnumType; 402} 403 404export function isEnum(tsSymbol: Symbol): boolean { 405 return !!(tsSymbol.flags & SymbolFlags.Enum); 406} 407 408export function isEnumMemberType(tsType: Type): boolean { 409 // Note: For some reason, test (tsType.flags & TypeFlags.Enum) != 0 doesn't work here. 410 // Must use SymbolFlags to figure out if this is an enum type. 411 return tsType.symbol && (tsType.symbol.flags & SymbolFlags.EnumMember) !== 0; 412} 413 414export function isObjectLiteralType(tsType: Type): boolean { 415 return tsType.symbol && (tsType.symbol.flags & SymbolFlags.ObjectLiteral) !== 0; 416} 417 418export function hasModifier(tsModifiers: readonly Modifier[] | undefined, tsModifierKind: number): boolean { 419 // Sanity check. 420 if (!tsModifiers) return false; 421 422 for (const tsModifier of tsModifiers) { 423 if (tsModifier.kind === tsModifierKind) return true; 424 } 425 426 return false; 427} 428 429export function unwrapParenthesized(tsExpr: Expression): Expression { 430 let unwrappedExpr = tsExpr; 431 while (isParenthesizedExpression(unwrappedExpr)) { 432 unwrappedExpr = unwrappedExpr.expression; 433 } 434 return unwrappedExpr; 435} 436 437export function followIfAliased(sym: Symbol): Symbol { 438 if ((sym.getFlags() & SymbolFlags.Alias) !== 0) { 439 return typeChecker.getAliasedSymbol(sym); 440 } 441 return sym; 442} 443 444let trueSymbolAtLocationCache = new Map<Node, Symbol | null>(); 445 446export function trueSymbolAtLocation(node: Node): Symbol | undefined { 447 let cache = trueSymbolAtLocationCache; 448 let val = cache.get(node); 449 if (val !== undefined) { 450 return val !== null ? val : undefined; 451 } 452 let sym = typeChecker.getSymbolAtLocation(node); 453 if (sym === undefined) { 454 cache.set(node, null); 455 return undefined; 456 } 457 sym = followIfAliased(sym); 458 cache.set(node, sym); 459 return sym; 460} 461 462export function clearTrueSymbolAtLocationCache(): void { 463 trueSymbolAtLocationCache.clear(); 464} 465 466export function isTypeDeclSyntaxKind(kind: SyntaxKind) { 467 return isStructDeclarationKind(kind) || 468 kind === SyntaxKind.EnumDeclaration || 469 kind === SyntaxKind.ClassDeclaration || 470 kind === SyntaxKind.InterfaceDeclaration || 471 kind === SyntaxKind.TypeAliasDeclaration; 472} 473 474export function symbolHasDuplicateName(symbol: Symbol, tsDeclKind: SyntaxKind): boolean { 475 // Type Checker merges all declarations with the same name in one scope into one symbol. 476 // Thus, check whether the symbol of certain declaration has any declaration with 477 // different syntax kind. 478 const symbolDecls = symbol?.getDeclarations(); 479 if (symbolDecls) { 480 for (const symDecl of symbolDecls) { 481 const declKind = symDecl.kind; 482 // we relax arkts-unique-names for namespace collision with class/interface/enum/type/struct 483 const isNamespaceTypeCollision = 484 (isTypeDeclSyntaxKind(declKind) && tsDeclKind === SyntaxKind.ModuleDeclaration) || 485 (isTypeDeclSyntaxKind(tsDeclKind) && declKind === SyntaxKind.ModuleDeclaration); 486 487 // Don't count declarations with 'Identifier' syntax kind as those 488 // usually depict declaring an object's property through assignment. 489 if (declKind !== SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) return true; 490 } 491 } 492 493 return false; 494} 495 496export function isReferenceType(tsType: Type): boolean { 497 const f = tsType.getFlags(); 498 return ( 499 (f & TypeFlags.InstantiableNonPrimitive) !== 0 || (f & TypeFlags.Object) !== 0 || 500 (f & TypeFlags.Boolean) !== 0 || (f & TypeFlags.Enum) !== 0 || (f & TypeFlags.NonPrimitive) !== 0 || 501 (f & TypeFlags.Number) !== 0 || (f & TypeFlags.String) !== 0 502 ); 503} 504 505export function isPrimitiveType(type: Type): boolean { 506 const f = type.getFlags(); 507 return ( 508 (f & TypeFlags.Boolean) !== 0 || (f & TypeFlags.BooleanLiteral) !== 0 || 509 (f & TypeFlags.Number) !== 0 || (f & TypeFlags.NumberLiteral) !== 0 510 // In ArkTS 'string' is not a primitive type. So for the common subset 'string' 511 // should be considered as a reference type. That is why next line is commented out. 512 //(f & TypeFlags.String) != 0 || (f & TypeFlags.StringLiteral) != 0 513 ); 514} 515 516export function isPrimitiveLiteralType(type: Type): boolean { 517 return !!( 518 type.flags & 519 (TypeFlags.BooleanLiteral | 520 TypeFlags.NumberLiteral | 521 TypeFlags.StringLiteral | 522 TypeFlags.BigIntLiteral) 523 ); 524} 525 526export function isPurePrimitiveLiteralType(type: Type): boolean { 527 return isPrimitiveLiteralType(type) && !(type.flags & TypeFlags.EnumLiteral); 528} 529 530export function isTypeSymbol(symbol: Symbol | undefined): boolean { 531 return ( 532 !!symbol && !!symbol.flags && 533 ((symbol.flags & SymbolFlags.Class) !== 0 || (symbol.flags & SymbolFlags.Interface) !== 0) 534 ); 535} 536 537// Check whether type is generic 'Array<T>' type defined in TypeScript standard library. 538export function isGenericArrayType(tsType: Type): tsType is TypeReference { 539 return ( 540 isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 && 541 tsType.getSymbol()?.getName() === 'Array' 542 ); 543} 544 545export function isReadonlyArrayType(tsType: Type): boolean { 546 return ( 547 isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 && 548 (tsType.getSymbol()?.getName() === 'ReadonlyArray') 549 ); 550} 551 552export function isConcatArrayType(tsType: Type): boolean { 553 return ( 554 isStdLibraryType(tsType) && 555 isTypeReference(tsType) && 556 tsType.typeArguments?.length === 1 && 557 tsType.target.typeParameters?.length === 1 && 558 (tsType.getSymbol()?.getName() === 'ConcatArray') 559 ); 560} 561 562export function isArrayLikeType(tsType: Type): boolean { 563 return ( 564 isStdLibraryType(tsType) && 565 isTypeReference(tsType) && 566 tsType.typeArguments?.length === 1 && 567 tsType.target.typeParameters?.length === 1 && 568 (tsType.getSymbol()?.getName() === 'ArrayLike') 569 ); 570} 571 572export function isTypedArray(tsType: Type, allowTypeArrays: string[]): boolean { 573 const symbol = tsType.symbol; 574 if (!symbol) { 575 return false; 576 } 577 const name = typeChecker.getFullyQualifiedName(symbol); 578 if (isGlobalSymbol(symbol) && allowTypeArrays.includes(name)) { 579 return true; 580 } 581 const decl = getDeclaration(symbol); 582 return ( 583 !!decl && 584 isArkTSCollectionsClassOrInterfaceDeclaration(decl) && 585 allowTypeArrays.includes(symbol.getName()) 586 ); 587} 588 589export function isArray(tsType: Type): boolean { 590 return isGenericArrayType(tsType) || isReadonlyArrayType(tsType) || isTypedArray(tsType, TYPED_ARRAYS); 591} 592 593export function isCollectionArrayType(tsType: Type): boolean { 594 return isTypedArray(tsType, TYPED_COLLECTIONS); 595} 596 597export function isIndexableArray(tsType: Type): boolean { 598 return ( 599 isGenericArrayType(tsType) || 600 isReadonlyArrayType(tsType) || 601 isConcatArrayType(tsType) || 602 isArrayLikeType(tsType) || 603 isTypedArray(tsType, TYPED_ARRAYS) || 604 isTypedArray(tsType, TYPED_COLLECTIONS) 605 ); 606} 607 608export function isTuple(tsType: Type): boolean { 609 return isTypeReference(tsType) && !!(tsType.objectFlags & ObjectFlags.Tuple); 610} 611 612// does something similar to relatedByInheritanceOrIdentical function 613export function isOrDerivedFrom(tsType: Type, checkType: CheckType, checkedBaseTypes?: Set<Type>): boolean { 614 if (isTypeReference(tsType) && tsType.target !== tsType) { 615 tsType = tsType.target; 616 } 617 if (checkType(tsType)) { 618 return true; 619 } 620 if (!tsType.symbol || !tsType.symbol.declarations) { 621 return false; 622 } 623 624 // Avoid type recursion in heritage by caching checked types. 625 (checkedBaseTypes ||= new Set<Type>()).add(tsType); 626 627 for (const tsTypeDecl of tsType.symbol.declarations) { 628 const isClassOrInterfaceDecl = isClassDeclaration(tsTypeDecl) || isInterfaceDeclaration(tsTypeDecl); 629 const isDerived = isClassOrInterfaceDecl && !!tsTypeDecl.heritageClauses; 630 if (!isDerived) { 631 continue; 632 } 633 for (const heritageClause of tsTypeDecl.heritageClauses) { 634 if (processParentTypesCheck(heritageClause.types, checkType, checkedBaseTypes)) { 635 return true; 636 } 637 } 638 } 639 640 return false; 641} 642 643export function isTypeReference(tsType: Type): tsType is TypeReference { 644 return ( 645 (tsType.getFlags() & TypeFlags.Object) !== 0 && 646 ((tsType as ObjectType).objectFlags & ObjectFlags.Reference) !== 0 647 ); 648} 649 650export function isNullType(tsTypeNode: TypeNode): boolean { 651 return (isLiteralTypeNode(tsTypeNode) && tsTypeNode.literal.kind === SyntaxKind.NullKeyword); 652} 653 654export function isThisOrSuperExpr(tsExpr: Expression): boolean { 655 return (tsExpr.kind === SyntaxKind.ThisKeyword || tsExpr.kind === SyntaxKind.SuperKeyword); 656} 657 658export function isPrototypeSymbol(symbol: Symbol | undefined): boolean { 659 return (!!symbol && !!symbol.flags && (symbol.flags & SymbolFlags.Prototype) !== 0); 660} 661 662export function isFunctionSymbol(symbol: Symbol | undefined): boolean { 663 return (!!symbol && !!symbol.flags && (symbol.flags & SymbolFlags.Function) !== 0); 664} 665 666export function isInterfaceType(tsType: Type | undefined): boolean { 667 return ( 668 !!tsType && !!tsType.symbol && !!tsType.symbol.flags && 669 (tsType.symbol.flags & SymbolFlags.Interface) !== 0 670 ); 671} 672 673export function isAnyType(tsType: Type): tsType is TypeReference { 674 return (tsType.getFlags() & TypeFlags.Any) !== 0; 675} 676 677export function isUnknownType(tsType: Type): boolean { 678 return (tsType.getFlags() & TypeFlags.Unknown) !== 0; 679} 680 681export function isUnsupportedType(tsType: Type): boolean { 682 return ( 683 !!tsType.flags && ((tsType.flags & TypeFlags.Any) !== 0 || (tsType.flags & TypeFlags.Unknown) !== 0 || 684 (tsType.flags & TypeFlags.Intersection) !== 0) 685 ); 686} 687 688export function isUnsupportedUnionType(tsType: Type): boolean { 689 if (tsType.isUnion()) { 690 return !isNullableUnionType(tsType) && !isBooleanUnionType(tsType); 691 } 692 return false; 693} 694 695function isNullableUnionType(tsUnionType: UnionType): boolean { 696 for (const t of tsUnionType.types) { 697 if (!!(t.flags & TypeFlags.Undefined) || !!(t.flags & TypeFlags.Null)) { 698 return true; 699 } 700 } 701 return false; 702} 703 704function isBooleanUnionType(tsUnionType: UnionType): boolean { 705 // For some reason, 'boolean' type is also represented as as union 706 // of 'true' and 'false' literal types. This form of 'union' type 707 // should be considered as supported. 708 const tsCompTypes = tsUnionType.types; 709 return ( 710 tsUnionType.flags === (TypeFlags.Boolean | TypeFlags.Union) && tsCompTypes.length === 2 && 711 tsCompTypes[0].flags === TypeFlags.BooleanLiteral && (tsCompTypes[1].flags === TypeFlags.BooleanLiteral) 712 ); 713} 714 715export function isFunctionOrMethod(tsSymbol: Symbol | undefined): boolean { 716 return ( 717 !!tsSymbol && 718 ((tsSymbol.flags & SymbolFlags.Function) !== 0 || (tsSymbol.flags & SymbolFlags.Method) !== 0) 719 ); 720} 721 722export function isMethodAssignment(tsSymbol: Symbol | undefined): boolean { 723 return ( 724 !!tsSymbol && 725 ((tsSymbol.flags & SymbolFlags.Method) !== 0 && (tsSymbol.flags & SymbolFlags.Assignment) !== 0) 726 ); 727} 728 729export function getDeclaration(tsSymbol: Symbol | undefined): Declaration | undefined { 730 if (tsSymbol && tsSymbol.declarations && tsSymbol.declarations.length > 0) { 731 return tsSymbol.declarations[0]; 732 } 733 return undefined; 734} 735 736function isVarDeclaration(tsDecl: Node): boolean { 737 return isVariableDeclaration(tsDecl) && isVariableDeclarationList(tsDecl.parent); 738} 739 740export function isValidEnumMemberInit(tsExpr: Expression): boolean { 741 if (isNumberConstantValue(tsExpr.parent as EnumMember)) { 742 return true; 743 } 744 if (isStringConstantValue(tsExpr.parent as EnumMember)) { 745 return true; 746 } 747 return isCompileTimeExpression(tsExpr); 748} 749 750export function isCompileTimeExpression(tsExpr: Expression): boolean { 751 if ( 752 isParenthesizedExpression(tsExpr) || 753 (isAsExpression(tsExpr) && tsExpr.type.kind === SyntaxKind.NumberKeyword)) { 754 return isCompileTimeExpression(tsExpr.expression); 755 } 756 switch (tsExpr.kind) { 757 case SyntaxKind.PrefixUnaryExpression: 758 return isPrefixUnaryExprValidEnumMemberInit(tsExpr as PrefixUnaryExpression); 759 case SyntaxKind.ParenthesizedExpression: 760 case SyntaxKind.BinaryExpression: 761 return isBinaryExprValidEnumMemberInit(tsExpr as BinaryExpression); 762 case SyntaxKind.ConditionalExpression: 763 return isConditionalExprValidEnumMemberInit(tsExpr as ConditionalExpression); 764 case SyntaxKind.Identifier: 765 return isIdentifierValidEnumMemberInit(tsExpr as Identifier); 766 case SyntaxKind.NumericLiteral: 767 return true; 768 case SyntaxKind.StringLiteral: 769 return true; 770 case SyntaxKind.PropertyAccessExpression: { 771 // if enum member is in current enum declaration try to get value 772 // if it comes from another enum consider as constant 773 const propertyAccess = tsExpr as PropertyAccessExpression; 774 if(isNumberConstantValue(propertyAccess)) { 775 return true; 776 } 777 const leftHandSymbol = typeChecker.getSymbolAtLocation(propertyAccess.expression); 778 if(!leftHandSymbol) { 779 return false; 780 } 781 const decls = leftHandSymbol.getDeclarations(); 782 if (!decls || decls.length !== 1) { 783 return false; 784 } 785 return isEnumDeclaration(decls[0]); 786 } 787 default: 788 return false; 789 } 790} 791 792function isPrefixUnaryExprValidEnumMemberInit(tsExpr: PrefixUnaryExpression): boolean { 793 return (isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && isCompileTimeExpression(tsExpr.operand)); 794} 795 796function isBinaryExprValidEnumMemberInit(tsExpr: BinaryExpression): boolean { 797 return ( 798 isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && isCompileTimeExpression(tsExpr.left) && 799 isCompileTimeExpression(tsExpr.right) 800 ); 801} 802 803function isConditionalExprValidEnumMemberInit(tsExpr: ConditionalExpression): boolean { 804 return (isCompileTimeExpression(tsExpr.whenTrue) && isCompileTimeExpression(tsExpr.whenFalse)); 805} 806 807function isIdentifierValidEnumMemberInit(tsExpr: Identifier): boolean { 808 const tsSymbol = typeChecker.getSymbolAtLocation(tsExpr); 809 const tsDecl = getDeclaration(tsSymbol); 810 return (!!tsDecl && 811 ((isVarDeclaration(tsDecl) && isConst(tsDecl.parent)) || 812 (tsDecl.kind === SyntaxKind.EnumMember) 813 ) 814 ); 815} 816 817function isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: PrefixUnaryOperator): boolean { 818 return ( 819 tsPrefixUnaryOp === SyntaxKind.PlusToken || tsPrefixUnaryOp === SyntaxKind.MinusToken || 820 tsPrefixUnaryOp === SyntaxKind.TildeToken 821 ); 822} 823 824function isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: BinaryOperatorToken): boolean { 825 return ( 826 tsBinaryOp.kind === SyntaxKind.AsteriskToken || tsBinaryOp.kind === SyntaxKind.SlashToken || 827 tsBinaryOp.kind === SyntaxKind.PercentToken || tsBinaryOp.kind === SyntaxKind.MinusToken || 828 tsBinaryOp.kind === SyntaxKind.PlusToken || tsBinaryOp.kind === SyntaxKind.LessThanLessThanToken || 829 tsBinaryOp.kind === SyntaxKind.GreaterThanGreaterThanToken || tsBinaryOp.kind === SyntaxKind.BarBarToken || 830 tsBinaryOp.kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken || 831 tsBinaryOp.kind === SyntaxKind.AmpersandToken || tsBinaryOp.kind === SyntaxKind.CaretToken || 832 tsBinaryOp.kind === SyntaxKind.BarToken || tsBinaryOp.kind === SyntaxKind.AmpersandAmpersandToken 833 ); 834} 835 836export function isConst(tsNode: Node): boolean { 837 return !!(getCombinedNodeFlags(tsNode) & NodeFlags.Const); 838} 839 840export function isNumberConstantValue( 841 tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression | NumericLiteral 842): boolean { 843 844 const tsConstValue = (tsExpr.kind === SyntaxKind.NumericLiteral) ? 845 Number(tsExpr.getText()) : 846 typeChecker.getConstantValue(tsExpr); 847 848 return tsConstValue !== undefined && typeof tsConstValue === 'number'; 849} 850 851export function isIntegerConstantValue( 852 tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression | NumericLiteral 853): boolean { 854 855 const tsConstValue = (tsExpr.kind === SyntaxKind.NumericLiteral) ? 856 Number(tsExpr.getText()) : 857 typeChecker.getConstantValue(tsExpr); 858 return ( 859 tsConstValue !== undefined && typeof tsConstValue === 'number' && 860 tsConstValue.toFixed(0) === tsConstValue.toString() 861 ); 862} 863 864export function isStringConstantValue( 865 tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression 866): boolean { 867 const tsConstValue = typeChecker.getConstantValue(tsExpr); 868 return ( 869 tsConstValue !== undefined && typeof tsConstValue === 'string' 870 ); 871} 872 873// Returns true if typeA is a subtype of typeB 874export function relatedByInheritanceOrIdentical(typeA: Type, typeB: Type): boolean { 875 if (isTypeReference(typeA) && typeA.target !== typeA) { typeA = typeA.target; } 876 if (isTypeReference(typeB) && typeB.target !== typeB) { typeB = typeB.target; } 877 878 if (typeA === typeB || isObject(typeB)) { return true; } 879 if (!typeA.symbol || !typeA.symbol.declarations) { return false; } 880 881 const isBISendable = isISendableInterface(typeB); 882 for (const typeADecl of typeA.symbol.declarations) { 883 if (isBISendable && isClassDeclaration(typeADecl) && hasSendableDecorator(typeADecl)) { 884 return true; 885 } 886 if ( 887 (!isClassDeclaration(typeADecl) && !isInterfaceDeclaration(typeADecl)) || 888 !typeADecl.heritageClauses 889 ) { continue; } 890 for (const heritageClause of typeADecl.heritageClauses) { 891 const processInterfaces = typeA.isClass() ? (heritageClause.token !== SyntaxKind.ExtendsKeyword) : true; 892 if (processParentTypes(heritageClause.types, typeB, processInterfaces)) return true; 893 } 894 } 895 896 return false; 897} 898 899export function reduceReference(t: Type): Type { 900 return isTypeReference(t) && t.target !== t ? t.target : t; 901} 902 903function needToDeduceStructuralIdentityHandleUnions( 904 lhsType: Type, 905 rhsType: Type, 906 rhsExpr: Expression, 907 isStrict: boolean 908): boolean { 909 if (rhsType.isUnion()) { 910 // Each Class/Interface of the RHS union type must be compatible with LHS type. 911 for (const compType of rhsType.types) { 912 if (needToDeduceStructuralIdentity(lhsType, compType, rhsExpr, isStrict)) { 913 return true; 914 } 915 } 916 return false; 917 } 918 919 if (lhsType.isUnion()) { 920 // RHS type needs to be compatible with at least one type of the LHS union. 921 for (const compType of lhsType.types) { 922 if (!needToDeduceStructuralIdentity(compType, rhsType, rhsExpr, isStrict)) { 923 return false; 924 } 925 } 926 return true; 927 } 928 // should be unreachable 929 return false; 930} 931 932// return true if two class types are not related by inheritance and structural identity check is needed 933export function needToDeduceStructuralIdentity( 934 lhsType: Type, 935 rhsType: Type, 936 rhsExpr: Expression, 937 isStrict: boolean = false 938): boolean { 939 lhsType = getNonNullableType(lhsType); 940 rhsType = getNonNullableType(rhsType); 941 if (isLibraryType(lhsType)) { 942 return false; 943 } 944 if (isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { 945 return false; 946 } 947 // #14569: Check for Function type. 948 if (areCompatibleFunctionals(lhsType, rhsType)) { 949 return false; 950 } 951 if (rhsType.isUnion() || lhsType.isUnion()) { 952 return needToDeduceStructuralIdentityHandleUnions(lhsType, rhsType, rhsExpr, isStrict); 953 } 954 // if isStrict, things like generics need to be checked 955 if (isStrict) { 956 if (isTypeReference(rhsType) && !!(rhsType.objectFlags & ObjectFlags.ArrayLiteral)) { 957 // The 'arkts-sendable-obj-init' rule already exists. Wait for the new 'strict type' to be modified. 958 return false; 959 } 960 lhsType = reduceReference(lhsType); 961 rhsType = reduceReference(rhsType); 962 } 963 return lhsType.isClassOrInterface() && rhsType.isClassOrInterface() && 964 !relatedByInheritanceOrIdentical(rhsType, lhsType); 965} 966 967// Does the 'arkts-no-structure-typing' rule need to be strictly enforced to complete previously missed scenarios 968export function needStrictMatchType(lhsType: Type, rhsType: Type): boolean { 969 if (isStrictSendableMatch(lhsType, rhsType)) { 970 return true; 971 } 972 // add other requirements with strict type requirements here 973 return false; 974} 975 976// For compatibility, left must all ClassOrInterface is sendable, right must has non-sendable ClassorInterface 977function isStrictSendableMatch(lhsType: Type, rhsType: Type): boolean { 978 let isStrictLhs = false; 979 if (lhsType.isUnion()) { 980 for (let compType of lhsType.types) { 981 compType = reduceReference(compType); 982 if (!compType.isClassOrInterface()) { 983 continue; 984 } 985 if (!isSendableClassOrInterface(compType)) { 986 return false; 987 } 988 isStrictLhs = true; 989 } 990 } else { 991 isStrictLhs = isSendableClassOrInterface(lhsType); 992 } 993 return isStrictLhs && typeContainsNonSendableClassOrInterface(rhsType); 994} 995 996export function hasPredecessor(node: Node, predicate: (node: Node) => boolean): boolean { 997 let parent = node.parent; 998 while (parent !== undefined) { 999 if (predicate(parent)) { 1000 return true; 1001 } 1002 parent = parent.parent; 1003 } 1004 return false; 1005} 1006 1007export function processParentTypes(parentTypes: NodeArray<ExpressionWithTypeArguments>, typeB: Type, processInterfaces: boolean): boolean { 1008 for (const baseTypeExpr of parentTypes) { 1009 let baseType = getTypeAtLocationForLinter(baseTypeExpr); 1010 if (isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; 1011 if (baseType && (baseType.isClass() !== processInterfaces) && relatedByInheritanceOrIdentical(baseType, typeB)) return true; 1012 } 1013 return false; 1014} 1015 1016function processParentTypesCheck( 1017 parentTypes: NodeArray<Expression>, 1018 checkType: CheckType, 1019 checkedBaseTypes: Set<Type> 1020): boolean { 1021 for (const baseTypeExpr of parentTypes) { 1022 let baseType = getTypeAtLocationForLinter(baseTypeExpr); 1023 if (isTypeReference(baseType) && baseType.target !== baseType) { 1024 baseType = baseType.target; 1025 } 1026 if ( 1027 baseType && 1028 !checkedBaseTypes.has(baseType) && 1029 isOrDerivedFrom(baseType, checkType, checkedBaseTypes) 1030 ) { 1031 return true; 1032 } 1033 } 1034 return false; 1035} 1036 1037 1038export function isObject(tsType: Type): boolean { 1039 if (!tsType) { 1040 return false; 1041 } 1042 if (tsType.symbol && (tsType.isClassOrInterface() && tsType.symbol.name === "Object")) { 1043 return true; 1044 } 1045 const node = typeChecker.typeToTypeNode(tsType, undefined, NodeBuilderFlags.AllowEmptyTuple); 1046 return node !== undefined && node.kind === SyntaxKind.ObjectKeyword; 1047} 1048 1049export function logTscDiagnostic(diagnostics: readonly Diagnostic[], log: (message: any, ...args: any[]) => void): void { 1050 diagnostics.forEach((diagnostic) => { 1051 let message = flattenDiagnosticMessageText(diagnostic.messageText, "\n"); 1052 1053 if (diagnostic.file && diagnostic.start) { 1054 const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); 1055 message = `${diagnostic.file.fileName} (${line + 1}, ${character + 1}): ${message}`; 1056 } 1057 1058 log(message); 1059 }); 1060} 1061 1062export function encodeProblemInfo(problem: ProblemInfo): string { 1063 return `${problem.problem}%${problem.start}%${problem.end}`; 1064} 1065 1066export function decodeAutofixInfo(info: string): AutofixInfo { 1067 const infos = info.split("%"); 1068 return { problemID: infos[0], start: Number.parseInt(infos[1]), end: Number.parseInt(infos[2]) }; 1069} 1070 1071export function isCallToFunctionWithOmittedReturnType(tsExpr: Expression): boolean { 1072 if (isCallExpression(tsExpr)) { 1073 const tsCallSignature = typeChecker.getResolvedSignature(tsExpr); 1074 if (tsCallSignature) { 1075 const tsSignDecl = tsCallSignature.getDeclaration(); 1076 // `tsSignDecl` is undefined when `getResolvedSignature` returns `unknownSignature` 1077 if (!tsSignDecl || !tsSignDecl.type) return true; 1078 } 1079 } 1080 1081 return false; 1082} 1083 1084function hasReadonlyFields(type: Type): boolean { 1085 if (type.symbol.members === undefined) return false; // No members -> no readonly fields 1086 1087 let result = false; 1088 1089 type.symbol.members.forEach((value /*, key*/) => { 1090 if ( 1091 value.declarations !== undefined && value.declarations.length > 0 && 1092 isPropertyDeclaration(value.declarations[0]) 1093 ) { 1094 const propmMods = getModifiers(value.declarations[0] as PropertyDeclaration);//value.declarations[0].modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 1095 if (hasModifier(propmMods, SyntaxKind.ReadonlyKeyword)) { 1096 result = true; 1097 return; 1098 } 1099 } 1100 }); 1101 1102 return result; 1103} 1104 1105function hasDefaultCtor(type: Type): boolean { 1106 if (type.symbol.members === undefined) return true; // No members -> no explicite constructors -> there is default ctor 1107 1108 let hasCtor = false; // has any constructor 1109 let hasDefaultCtor = false; // has default constructor 1110 1111 type.symbol.members.forEach((value /*, key*/) => { 1112 if ((value.flags & SymbolFlags.Constructor) !== 0) { 1113 hasCtor = true; 1114 1115 if (value.declarations !== undefined && value.declarations.length > 0) { 1116 const declCtor = value.declarations[0] as ConstructorDeclaration; 1117 if (declCtor.parameters.length === 0) { 1118 hasDefaultCtor = true; 1119 return; 1120 } 1121 } 1122 } 1123 }); 1124 1125 return !hasCtor || hasDefaultCtor; // Has no any explicite constructor -> has implicite default constructor. 1126} 1127 1128function isAbstractClass(type: Type): boolean { 1129 if (type.isClass() && type.symbol.declarations && type.symbol.declarations.length > 0) { 1130 const declClass = type.symbol.declarations[0] as ClassDeclaration; 1131 const classMods = getModifiers(declClass); //declClass.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 1132 if (hasModifier(classMods, SyntaxKind.AbstractKeyword)) { 1133 return true; 1134 } 1135 } 1136 1137 return false; 1138} 1139 1140export function validateObjectLiteralType(type: Type | undefined): boolean { 1141 if (!type) return false; 1142 1143 type = getTargetType(type); 1144 return ( 1145 type !== undefined && type.isClassOrInterface() && hasDefaultCtor(type) && 1146 !hasReadonlyFields(type) && !isAbstractClass(type) 1147 ); 1148} 1149 1150export function isStructDeclarationKind(kind: SyntaxKind) { 1151 return kind === SyntaxKind.StructDeclaration; 1152} 1153 1154export function isStructDeclaration(node: Node) { 1155 return isStructDeclarationKind(node.kind); 1156} 1157export function isStructObjectInitializer(objectLiteral: ObjectLiteralExpression): boolean { 1158 if(isCallLikeExpression(objectLiteral.parent)) { 1159 const signature = typeChecker.getResolvedSignature(objectLiteral.parent); 1160 const signDecl = signature?.declaration; 1161 return !!signDecl && isConstructorDeclaration(signDecl) && isStructDeclaration(signDecl.parent); 1162 } 1163 return false; 1164} 1165 1166export function hasMethods(type: Type): boolean { 1167 const properties = typeChecker.getPropertiesOfType(type); 1168 if (properties?.length) { 1169 for (const prop of properties) { 1170 if (prop.getFlags() & SymbolFlags.Method) return true; 1171 } 1172 }; 1173 1174 return false; 1175} 1176 1177function findProperty(type: Type, name: string): Symbol | undefined { 1178 const properties = typeChecker.getPropertiesOfType(type); 1179 if(properties.length) { 1180 for (const prop of properties) { 1181 if (prop.name === name) return prop; 1182 } 1183 } 1184 return undefined; 1185} 1186 1187export function checkTypeSet(typeSet: Type, predicate: CheckType): boolean { 1188 if (!typeSet.isUnionOrIntersection()) { 1189 return predicate(typeSet); 1190 } 1191 for (let elemType of typeSet.types) { 1192 if (checkTypeSet(elemType, predicate)) { 1193 return true; 1194 } 1195 } 1196 return false; 1197} 1198 1199export function getNonNullableType(t: Type): Type { 1200 if (t.isUnion()) { 1201 return t.getNonNullableType(); 1202 } 1203 return t; 1204} 1205 1206export function isObjectLiteralAssignable(lhsType: Type | undefined, rhsExpr: ObjectLiteralExpression): boolean { 1207 if (lhsType === undefined) { 1208 return false; 1209 } 1210 1211 // Always check with the non-nullable variant of lhs type. 1212 lhsType = getNonNullableType(lhsType); 1213 1214 if (lhsType.isUnion()) { 1215 for (const compType of lhsType.types) { 1216 if (isObjectLiteralAssignable(compType, rhsExpr)) { 1217 return true; 1218 } 1219 } 1220 } 1221 1222 // Allow initializing with anything when the type 1223 // originates from the library. 1224 if ((isAnyType(lhsType) || isLibraryType(lhsType)) && !isStaticSourceFileForLinter(lhsType)) { 1225 return true; 1226 } 1227 1228 // issue 13412: 1229 // Allow initializing with a dynamic object when the LHS type 1230 // is primitive or defined in standard library. 1231 if (isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { 1232 return true; 1233 } 1234 1235 // For Partial<T>, Required<T>, Readonly<T> types, validate their argument type. 1236 if (isStdPartialType(lhsType) || isStdRequiredType(lhsType) || isStdReadonlyType(lhsType)) { 1237 if (lhsType.aliasTypeArguments && lhsType.aliasTypeArguments.length === 1) { 1238 lhsType = lhsType.aliasTypeArguments[0]; 1239 } else { 1240 return false; 1241 } 1242 } 1243 1244 // Allow initializing Record objects with object initializer. 1245 // Record supports any type for a its value, but the key value 1246 // must be either a string or number literal. 1247 if (isStdRecordType(lhsType)) { 1248 return validateRecordObjectKeys(rhsExpr); 1249 } 1250 1251 return validateObjectLiteralType(lhsType) && !hasMethods(lhsType) && validateFields(lhsType, rhsExpr); 1252} 1253 1254function isDynamicObjectAssignedToStdType(lhsType: Type, rhsExpr: Expression): boolean { 1255 if (isStdLibraryType(lhsType) || isPrimitiveType(lhsType)) { 1256 const rhsSym = isCallExpression(rhsExpr) 1257 ? getSymbolOfCallExpression(rhsExpr) 1258 : typeChecker.getSymbolAtLocation(rhsExpr); 1259 1260 if (rhsSym && isLibrarySymbol(rhsSym)) return true; 1261 } 1262 return false; 1263} 1264 1265function getTargetType(type: Type): Type { 1266 return (type.getFlags() & TypeFlags.Object) && 1267 (type as ObjectType).objectFlags & ObjectFlags.Reference ? (type as TypeReference).target : type; 1268} 1269 1270export function isLiteralType(type: Type): boolean { 1271 return type.isLiteral() || (type.flags & TypeFlags.BooleanLiteral) !== 0; 1272} 1273 1274export function validateFields(objectType: Type, objectLiteral: ObjectLiteralExpression): boolean { 1275 for (const prop of objectLiteral.properties) { 1276 if (isPropertyAssignment(prop)) { 1277 if (!validateField(objectType, prop)) { 1278 return false; 1279 } 1280 } 1281 }; 1282 1283 return true; 1284} 1285 1286function validateField(type: Type, prop: PropertyAssignment): boolean { 1287 // Issue 15497: Use unescaped property name to find correpsponding property. 1288 const propNameSymbol = typeChecker.getSymbolAtLocation(prop.name); 1289 const propName = propNameSymbol ? 1290 symbolName(propNameSymbol) : 1291 isMemberName(prop.name) ? 1292 idText(prop.name) : 1293 prop.name.getText(); 1294 const propSym = findProperty(type, propName); 1295 if (!propSym || !propSym.declarations?.length) { 1296 return false; 1297 } 1298 1299 const propType = typeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]); 1300 const initExpr = unwrapParenthesized(prop.initializer); 1301 const rhsType = getTypeAtLocationForLinter(initExpr); 1302 if (isObjectLiteralExpression(initExpr)) { 1303 if (!isObjectLiteralAssignable(propType, initExpr)) { 1304 return false; 1305 } 1306 } else { 1307 // Only check for structural sub-typing. 1308 if (needToDeduceStructuralIdentity( 1309 propType, 1310 rhsType, 1311 initExpr, 1312 needStrictMatchType(propType, rhsType) 1313 )) { 1314 return false; 1315 } 1316 1317 if (isWrongSendableFunctionAssignment(propType, rhsType)) { 1318 return false; 1319 } 1320 } 1321 1322 return true; 1323} 1324 1325function isSupportedTypeNodeKind(kind: SyntaxKind): boolean { 1326 return kind !== SyntaxKind.AnyKeyword && kind !== SyntaxKind.UnknownKeyword && 1327 kind !== SyntaxKind.SymbolKeyword && kind !== SyntaxKind.IndexedAccessType && 1328 kind !== SyntaxKind.ConditionalType && kind !== SyntaxKind.MappedType && 1329 kind !== SyntaxKind.InferType; 1330} 1331 1332export function isSupportedType(typeNode: TypeNode): boolean { 1333 if (isParenthesizedTypeNode(typeNode)) return isSupportedType(typeNode.type); 1334 1335 if (isArrayTypeNode(typeNode)) return isSupportedType(typeNode.elementType); 1336 1337 if (isTypeReferenceNode(typeNode) && typeNode.typeArguments) { 1338 for (const typeArg of typeNode.typeArguments) { 1339 if (!isSupportedType(typeArg)) { return false; } 1340 } 1341 return true; 1342 } 1343 1344 if (isUnionTypeNode(typeNode)) { 1345 for (const unionTypeElem of typeNode.types) { 1346 if (!isSupportedType(unionTypeElem)) { return false; } 1347 } 1348 return true; 1349 } 1350 1351 if (isTupleTypeNode(typeNode)) { 1352 for (const elem of typeNode.elements) { 1353 if (isTypeNode(elem) && !isSupportedType(elem)) return false; 1354 if (isNamedTupleMember(elem) && !isSupportedType(elem.type)) return false; 1355 } 1356 return true; 1357 } 1358 1359 return !isTypeLiteralNode(typeNode) && !isTypeQueryNode(typeNode) && 1360 !isIntersectionTypeNode(typeNode) && isSupportedTypeNodeKind(typeNode.kind); 1361} 1362 1363export function isStruct(symbol: Symbol) { 1364 if (!symbol.declarations) { 1365 return false; 1366 } 1367 for (const decl of symbol.declarations) { 1368 if (isStructDeclaration(decl)) { 1369 return true; 1370 } 1371 } 1372 return false; 1373} 1374 1375function validateRecordObjectKeys(objectLiteral: ObjectLiteralExpression): boolean { 1376 for (const prop of objectLiteral.properties) { 1377 if (!prop.name) { 1378 return false; 1379 } 1380 const isValidComputedProperty = isComputedPropertyName(prop.name) && isValidComputedPropertyName(prop.name, true); 1381 if (!isStringLiteral(prop.name) && !isNumericLiteral(prop.name) && !isValidComputedProperty) { 1382 return false; 1383 } 1384 } 1385 return true; 1386} 1387 1388/* Not need in Tsc 4.9 1389export function getDecorators(node: Node): readonly Decorator[] | undefined { 1390if (node.decorators) { 1391return filter(node.decorators, isDecorator); 1392} 1393} 1394*/ 1395 1396export type CheckType = ((t: Type) => boolean); 1397 1398export const ES_OBJECT = "ESObject"; 1399 1400export const LIMITED_STD_GLOBAL_FUNC = [ 1401 "eval" 1402]; 1403export const LIMITED_STD_OBJECT_API = [ 1404 "__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "assign", "create", 1405 "defineProperties", "defineProperty", "freeze", "fromEntries", "getOwnPropertyDescriptor", 1406 "getOwnPropertyDescriptors", "getOwnPropertySymbols", "getPrototypeOf", "hasOwnProperty", "is", 1407 "isExtensible", "isFrozen", "isPrototypeOf", "isSealed", "preventExtensions", "propertyIsEnumerable", 1408 "seal", "setPrototypeOf" 1409]; 1410export const LIMITED_STD_REFLECT_API = [ 1411 "apply", "construct", "defineProperty", "deleteProperty", "getOwnPropertyDescriptor", "getPrototypeOf", 1412 "isExtensible", "preventExtensions", "setPrototypeOf" 1413]; 1414export const LIMITED_STD_PROXYHANDLER_API = [ 1415 "apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", 1416 "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf" 1417]; 1418 1419export const FUNCTION_HAS_NO_RETURN_ERROR_CODE = 2366; 1420export const NON_RETURN_FUNCTION_DECORATORS = ["AnimatableExtend", "Builder", "Extend", "Styles" ]; 1421 1422export const STANDARD_LIBRARIES = [ 1423 "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts", "lib.webworker.importscripd.ts", 1424 "lib.webworker.iterable.d.ts", "lib.scripthost.d.ts", "lib.decorators.d.ts", "lib.decorators.legacy.d.ts", 1425 "lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.generator.d.ts", 1426 "lib.es2015.iterable.d.ts", "lib.es2015.promise.d.ts", "lib.es2015.proxy.d.ts", "lib.es2015.reflect.d.ts", 1427 "lib.es2015.symbol.d.ts", "lib.es2015.symbol.wellknown.d.ts", "lib.es2016.array.include.d.ts", 1428 "lib.es2017.object.d.ts", "lib.es2017.sharedmemory.d.ts", "lib.es2017.string.d.ts", "lib.es2017.intl.d.ts", 1429 "lib.es2017.typedarrays.d.ts", "lib.es2018.asyncgenerator.d.ts", "lib.es2018.asynciterable.d.ts", 1430 "lib.es2018.intl.d.ts", "lib.es2018.promise.d.ts", "lib.es2018.regexp.d.ts", "lib.es2019.array.d.ts", 1431 "lib.es2019.object.d.ts", "lib.es2019.string.d.ts", "lib.es2019.symbol.d.ts", "lib.es2019.intl.d.ts", 1432 "lib.es2020.bigint.d.ts", "lib.es2020.date.d.ts", "lib.es2020.promise.d.ts", "lib.es2020.sharedmemory.d.ts", 1433 "lib.es2020.string.d.ts", "lib.es2020.symbol.wellknown.d.ts", "lib.es2020.intl.d.ts", "lib.es2020.number.d.ts", 1434 "lib.es2021.promise.d.ts", "lib.es2021.string.d.ts", "lib.es2021.weakref.d.ts", "lib.es2021.intl.d.ts", 1435 "lib.es2022.array.d.ts", "lib.es2022.error.d.ts", "lib.es2022.intl.d.ts", "lib.es2022.object.d.ts", 1436 "lib.es2022.sharedmemory.d.ts", "lib.es2022.string.d.ts", "lib.es2022.regexp.d.ts", "lib.es2023.array.d.ts", 1437]; 1438 1439export const TYPED_ARRAYS = [ 1440 'Int8Array', 1441 'Uint8Array', 1442 'Uint8ClampedArray', 1443 'Int16Array', 1444 'Uint16Array', 1445 'Int32Array', 1446 'Uint32Array', 1447 'Float32Array', 1448 'Float64Array', 1449 'BigInt64Array', 1450 'BigUint64Array', 1451 ]; 1452 1453export const TYPED_COLLECTIONS = [ 1454 'BitVector' 1455 ]; 1456 1457let parentSymbolCache: ESMap<Symbol, string | undefined> | undefined = new Map<Symbol, string | undefined>(); 1458export function getParentSymbolName(symbol: Symbol): string | undefined { 1459 parentSymbolCache = parentSymbolCache ? parentSymbolCache : new Map<Symbol, string | undefined>(); 1460 const cached = parentSymbolCache.get(symbol); 1461 if (cached) { 1462 return cached; 1463 } 1464 const name = typeChecker.getFullyQualifiedName(symbol); 1465 const dotPosition = name.lastIndexOf("."); 1466 const result = (dotPosition === -1) ? undefined : name.substring(0, dotPosition); 1467 parentSymbolCache.set(symbol, result); 1468 return result; 1469} 1470 1471export function isGlobalSymbol(symbol: Symbol): boolean { 1472 const parentName = getParentSymbolName(symbol); 1473 return !parentName || parentName === "global"; 1474} 1475 1476export function isSymbolAPI(symbol: Symbol): boolean { 1477 const parentName = getParentSymbolName(symbol); 1478 let name = parentName ? parentName : symbol.escapedName; 1479 return name === 'Symbol' || name === "SymbolConstructor"; 1480} 1481 1482export function isStdSymbol(symbol: Symbol): boolean { 1483 const name = typeChecker.getFullyQualifiedName(symbol); 1484 return name === 'Symbol' && isGlobalSymbol(symbol); 1485} 1486 1487export function isSymbolIterator(symbol: Symbol): boolean { 1488 const name = symbol.name; 1489 const parName = getParentSymbolName(symbol); 1490 return (parName === 'Symbol' || parName === 'SymbolConstructor') && name === 'iterator'; 1491} 1492 1493export function isSymbolIteratorExpression(expr: Expression): boolean { 1494 const symbol = trueSymbolAtLocation(expr); 1495 return !!symbol && isSymbolIterator(symbol); 1496} 1497 1498export function isDefaultImport(importSpec: ImportSpecifier): boolean { 1499 return importSpec?.propertyName?.text === "default"; 1500} 1501export function hasAccessModifier(decl: Declaration): boolean { 1502 const modifiers = getModifiers(decl as HasModifiers); //decl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 1503 return ( 1504 !!modifiers && 1505 (hasModifier(modifiers, SyntaxKind.PublicKeyword) || 1506 hasModifier(modifiers, SyntaxKind.ProtectedKeyword) || 1507 hasModifier(modifiers, SyntaxKind.PrivateKeyword)) 1508 ); 1509} 1510 1511export function getModifier(modifiers: readonly Modifier[] | undefined, modifierKind: SyntaxKind): Modifier | undefined { 1512 if (!modifiers) return undefined; 1513 return modifiers.find(x => x.kind === modifierKind); 1514} 1515 1516export function getAccessModifier(modifiers: readonly Modifier[] | undefined): Modifier | undefined { 1517 return getModifier(modifiers, SyntaxKind.PublicKeyword) ?? 1518 getModifier(modifiers, SyntaxKind.ProtectedKeyword) ?? 1519 getModifier(modifiers, SyntaxKind.PrivateKeyword); 1520} 1521 1522export function isStdRecordType(type: Type): boolean { 1523 // In TypeScript, 'Record<K, T>' is defined as type alias to a mapped type. 1524 // Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' 1525 // in this case will resolve to origin 'Record' symbol. 1526 if (type.aliasSymbol) { 1527 const target = (type as TypeReference).target; 1528 if (target) { 1529 const sym = target.aliasSymbol; 1530 return !!sym && sym.getName() === "Record" && isGlobalSymbol(sym); 1531 } 1532 } 1533 1534 return false; 1535} 1536 1537export function isStdMapType(type: Type): boolean { 1538 const sym = type.symbol; 1539 return !!sym && sym.getName() === "Map" && isGlobalSymbol(sym); 1540} 1541 1542export function isStdErrorType(type: Type): boolean { 1543 const symbol = type.symbol; 1544 if (!symbol) { 1545 return false; 1546 } 1547 const name = typeChecker.getFullyQualifiedName(symbol); 1548 return name === 'Error' && isGlobalSymbol(symbol); 1549} 1550 1551export function isStdPartialType(type: Type): boolean { 1552 const sym = type.aliasSymbol; 1553 return !!sym && sym.getName() === "Partial" && isGlobalSymbol(sym); 1554} 1555 1556export function isStdRequiredType(type: Type): boolean { 1557 const sym = type.aliasSymbol; 1558 return !!sym && sym.getName() === "Required" && isGlobalSymbol(sym); 1559} 1560 1561export function isStdReadonlyType(type: Type): boolean { 1562 const sym = type.aliasSymbol; 1563 return !!sym && sym.getName() === "Readonly" && isGlobalSymbol(sym); 1564} 1565 1566export function isLibraryType(type: Type): boolean { 1567 const nonNullableType = type.getNonNullableType(); 1568 if (nonNullableType.isUnion()) { 1569 for (const componentType of nonNullableType.types) { 1570 if (!isLibraryType(componentType)) { 1571 return false; 1572 } 1573 } 1574 return true; 1575 } 1576 return isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol()); 1577} 1578 1579export function hasLibraryType(node: Node): boolean { 1580 return isLibraryType(getTypeAtLocationForLinter(node)); 1581} 1582 1583export function isLibrarySymbol(sym: Symbol | undefined) { 1584 if (sym && sym.declarations && sym.declarations.length > 0) { 1585 const srcFile = sym.declarations[0].getSourceFile(); 1586 if (!srcFile) { 1587 return false; 1588 } 1589 const fileName = srcFile.fileName; 1590 1591 // Symbols from both *.ts and *.d.ts files should obey interop rules. 1592 // We disable such behavior for *.ts files in the test mode due to lack of 'ets' 1593 // extension support. 1594 const ext = getAnyExtensionFromPath(fileName); 1595 const isThirdPartyCode = 1596 ARKTS_IGNORE_DIRS.some(ignore => srcFilePathContainsDirectory(srcFile, ignore)) || 1597 ARKTS_IGNORE_FILES.some(ignore => getBaseFileName(fileName) === ignore); 1598 const isEts = (ext === '.ets'); 1599 const isTs = (ext === '.ts' && !srcFile.isDeclarationFile); 1600 const isStatic = (isEts || (isTs && testMode)) && !isThirdPartyCode; 1601 const isStdLib = STANDARD_LIBRARIES.includes(getBaseFileName(fileName).toLowerCase()); 1602 // We still need to confirm support for certain API from the 1603 // TypeScript standard library in ArkTS. Thus, for now do not 1604 // count standard library modules as dynamic. 1605 return !isStatic && !isStdLib; 1606 } 1607 1608 return false; 1609} 1610 1611const srcFilePathComponents = new Map<SourceFile, string[]>(); 1612export function srcFilePathContainsDirectory(srcFile: SourceFile, dir: string): boolean { 1613 let pathComps = srcFilePathComponents.get(srcFile); 1614 if (!pathComps) { 1615 pathComps = getPathComponents(normalizePath(srcFile.fileName)); 1616 srcFilePathComponents.set(srcFile, pathComps); 1617 } 1618 for (const subdir of pathComps) { 1619 if (subdir === dir) { 1620 return true; 1621 } 1622 } 1623 return false; 1624} 1625 1626export function pathContainsDirectory(targetPath: string, dir: string): boolean { 1627 for (const subdir of getPathComponents(targetPath)) { 1628 if (subdir === dir) { 1629 return true; 1630 } 1631 } 1632 return false; 1633} 1634 1635export function getScriptKind(srcFile: SourceFile): ScriptKind { 1636 const fileName = srcFile.fileName; 1637 const ext = getAnyExtensionFromPath(fileName); 1638 switch (ext.toLowerCase()) { 1639 case Extension.Js: 1640 return ScriptKind.JS; 1641 case Extension.Jsx: 1642 return ScriptKind.JSX; 1643 case Extension.Ts: 1644 return ScriptKind.TS; 1645 case Extension.Tsx: 1646 return ScriptKind.TSX; 1647 case Extension.Json: 1648 return ScriptKind.JSON; 1649 default: 1650 return ScriptKind.Unknown; 1651 } 1652} 1653 1654export function isStdLibraryType(type: Type): boolean { 1655 return isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol()); 1656} 1657 1658export function isStdLibrarySymbol(sym: Symbol | undefined) { 1659 if (sym && sym.declarations && sym.declarations.length > 0) { 1660 const srcFile = sym.declarations[0].getSourceFile(); 1661 return srcFile && 1662 STANDARD_LIBRARIES.includes(getBaseFileName(srcFile.fileName).toLowerCase()); 1663 } 1664 1665 return false; 1666} 1667 1668export function isIntrinsicObjectType(type: Type): boolean { 1669 return !!(type.flags & TypeFlags.NonPrimitive); 1670} 1671 1672export function isOhModulesEtsSymbol(sym: Symbol | undefined): boolean { 1673 const sourceFile = sym?.declarations?.[0]?.getSourceFile(); 1674 return ( 1675 !!sourceFile && 1676 sourceFile.scriptKind === ScriptKind.ETS && 1677 srcFilePathContainsDirectory(sourceFile, ARKTS_IGNORE_DIRS_OH_MODULES) 1678 ); 1679} 1680 1681export function isDynamicType(type: Type | undefined): boolean | undefined { 1682 if (type === undefined) { 1683 return false; 1684 } 1685 1686 // Return 'true' if it is an object of library type initialization, otherwise 1687 // return 'false' if it is not an object of standard library type one. 1688 // In the case of standard library type we need to determine context. 1689 1690 // Check the non-nullable version of type to eliminate 'undefined' type 1691 // from the union type elements. 1692 type = type.getNonNullableType(); 1693 1694 1695 if (type.isUnion()) { 1696 for (const compType of type.types) { 1697 const isDynamic = isDynamicType(compType); 1698 if (isDynamic || isDynamic === undefined) { 1699 return isDynamic; 1700 } 1701 } 1702 return false; 1703 } 1704 1705 if (isLibraryType(type) && !isStaticSourceFileForLinter(type)) { 1706 return true; 1707 } 1708 1709 if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !isAnyType(type)) { 1710 return false; 1711 } 1712 1713 return undefined; 1714} 1715 1716export function isObjectType(type: Type): type is ObjectType { 1717 return !!(type.flags & TypeFlags.Object); 1718} 1719 1720export function isAnonymous(type: Type): boolean { 1721 if (isObjectType(type)) { 1722 return !!(type.objectFlags & ObjectFlags.Anonymous); 1723 } 1724 return false; 1725 } 1726 1727 1728export function isDynamicLiteralInitializer(expr: Expression): boolean { 1729 if (!isObjectLiteralExpression(expr) && !isArrayLiteralExpression(expr)) { 1730 return false; 1731 } 1732 1733 // Handle nested literals: 1734 // { f: { ... } } 1735 let curNode: Node = expr; 1736 while (isObjectLiteralExpression(curNode) || isArrayLiteralExpression(curNode)) { 1737 const exprType = typeChecker.getContextualType(curNode); 1738 if (exprType !== undefined && !isAnonymous(exprType)) { 1739 const res = isDynamicType(exprType); 1740 if (res !== undefined) { 1741 return res; 1742 } 1743 } 1744 1745 curNode = curNode.parent; 1746 if (isPropertyAssignment(curNode)) { 1747 curNode = curNode.parent; 1748 } 1749 } 1750 1751 // Handle calls with literals: 1752 // foo({ ... }) 1753 if (isCallExpression(curNode)) { 1754 const callExpr = curNode; 1755 const type = getTypeAtLocationForLinter(callExpr.expression); 1756 1757 // this check is a hack to fix #13474, only for tac 4.2 1758 if (isAnyType(type)) return true; 1759 1760 let sym: Symbol | undefined = type.symbol; 1761 if (isLibrarySymbol(sym)) { 1762 return true; 1763 } 1764 1765 // #13483: 1766 // x.foo({ ... }), where 'x' is a variable exported from some library: 1767 if (isPropertyAccessExpression(callExpr.expression)) { 1768 sym = typeChecker.getSymbolAtLocation(callExpr.expression.expression); 1769 if (sym && sym.getFlags() & SymbolFlags.Alias) { 1770 sym = typeChecker.getAliasedSymbol(sym); 1771 if (isLibrarySymbol(sym)) { 1772 return true; 1773 } 1774 } 1775 } 1776 } 1777 1778 // Handle property assignments with literals: 1779 // obj.f = { ... } 1780 if (isBinaryExpression(curNode)) { 1781 const binExpr = curNode; 1782 if (isPropertyAccessExpression(binExpr.left)) { 1783 const propAccessExpr = binExpr.left; 1784 const type = getTypeAtLocationForLinter(propAccessExpr.expression); 1785 return isLibrarySymbol(type.symbol); 1786 } 1787 } 1788 1789 return false; 1790} 1791 1792export function isEsObjectType(typeNode: TypeNode | undefined): boolean { 1793 return !!typeNode && isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName) && 1794 typeNode.typeName.text === ES_OBJECT; 1795} 1796 1797export function isInsideBlock(node: Node): boolean { 1798 let par = node.parent 1799 while (par) { 1800 if (isBlock(par)) { 1801 return true; 1802 } 1803 par = par.parent; 1804 } 1805 return false; 1806} 1807 1808export function isValueAssignableToESObject(node: Node): boolean { 1809 if (isArrayLiteralExpression(node) || isObjectLiteralExpression(node)) { 1810 return false; 1811 } 1812 const valueType = getTypeAtLocationForLinter(node); 1813 return isUnsupportedType(valueType) || isAnonymousType(valueType) 1814} 1815 1816export function getVariableDeclarationTypeNode(node: Node): TypeNode | undefined { 1817 let sym = trueSymbolAtLocation(node); 1818 if (sym === undefined) { 1819 return undefined; 1820 } 1821 return getSymbolDeclarationTypeNode(sym); 1822} 1823 1824export function getSymbolDeclarationTypeNode(sym: Symbol): TypeNode | undefined { 1825 const decl = getDeclaration(sym); 1826 if (!!decl && isVariableDeclaration(decl)) { 1827 return decl.type; 1828 } 1829 return undefined; 1830} 1831 1832export function hasEsObjectType(node: Node): boolean { 1833 const typeNode = getVariableDeclarationTypeNode(node); 1834 return typeNode !== undefined && isEsObjectType(typeNode); 1835} 1836 1837export function symbolHasEsObjectType(sym: Symbol): boolean { 1838 const typeNode = getSymbolDeclarationTypeNode(sym); 1839 return typeNode !== undefined && isEsObjectType(typeNode); 1840} 1841 1842export function isEsObjectSymbol(sym: Symbol): boolean { 1843 const decl = getDeclaration(sym); 1844 return !!decl && isTypeAliasDeclaration(decl) && decl.name.escapedText === ES_OBJECT && 1845 decl.type.kind === SyntaxKind.AnyKeyword; 1846} 1847 1848export function isAnonymousType(type: Type): boolean { 1849 if (type.isUnionOrIntersection()) { 1850 for (const compType of type.types) { 1851 if (isAnonymousType(compType)) { 1852 return true; 1853 } 1854 } 1855 return false; 1856 } 1857 1858 return (type.flags & TypeFlags.Object) !== 0 && 1859 ((type as ObjectType).objectFlags & ObjectFlags.Anonymous) !== 0; 1860} 1861 1862export function getSymbolOfCallExpression(callExpr: CallExpression): Symbol | undefined { 1863 const signature = typeChecker.getResolvedSignature(callExpr); 1864 const signDecl = signature?.getDeclaration(); 1865 if (signDecl && signDecl.name) { 1866 return typeChecker.getSymbolAtLocation(signDecl.name); 1867 } 1868 return undefined; 1869} 1870 1871export function typeIsRecursive(topType: Type, type: Type | undefined = undefined): boolean { 1872 if (type === undefined) { 1873 type = topType; 1874 } 1875 else if (type === topType) { 1876 return true; 1877 } 1878 else if (type.aliasSymbol) { 1879 return false; 1880 } 1881 1882 if (type.isUnion()) { 1883 for (const unionElem of type.types) { 1884 if (typeIsRecursive(topType, unionElem)) { 1885 return true; 1886 } 1887 } 1888 } 1889 if (type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.Reference) { 1890 const typeArgs = typeChecker.getTypeArguments(type as TypeReference); 1891 if (typeArgs) { 1892 for (const typeArg of typeArgs) { 1893 if (typeIsRecursive(topType, typeArg)) { 1894 return true; 1895 } 1896 } 1897 } 1898 } 1899 return false; 1900} 1901 1902export function getTypeOrTypeConstraintAtLocation(expr: Expression): Type { 1903 let type = getTypeAtLocationForLinter(expr); 1904 if (type.isTypeParameter()) { 1905 let constraint = type.getConstraint(); 1906 if (constraint) { 1907 return constraint; 1908 } 1909 } 1910 return type; 1911} 1912 1913function areCompatibleFunctionals(lhsType: Type, rhsType: Type): boolean { 1914 return ( 1915 (isStdFunctionType(lhsType) || isFunctionalType(lhsType)) && 1916 (isStdFunctionType(rhsType) || isFunctionalType(rhsType)) 1917 ); 1918} 1919 1920function isFunctionalType(type: Type): boolean { 1921 const callSigns = type.getCallSignatures(); 1922 return callSigns && callSigns.length > 0; 1923} 1924 1925function isStdFunctionType(type: Type): boolean { 1926 const sym = type.getSymbol(); 1927 return !!sym && sym.getName() === 'Function' && isGlobalSymbol(sym); 1928} 1929 1930export function isStdBigIntType(type: Type): boolean { 1931 const sym = type.symbol; 1932 return !!sym && sym.getName() === 'BigInt' && isGlobalSymbol(sym); 1933} 1934 1935export function isStdNumberType(type: Type): boolean { 1936 const sym = type.symbol; 1937 return !!sym && sym.getName() === 'Number' && isGlobalSymbol(sym); 1938} 1939 1940export function isStdBooleanType(type: Type): boolean { 1941 const sym = type.symbol; 1942 return !!sym && sym.getName() === 'Boolean' && isGlobalSymbol(sym); 1943} 1944 1945export function isEnumStringLiteral(expr: Expression): boolean { 1946 const symbol = trueSymbolAtLocation(expr); 1947 const isEnumMember = !!symbol && !!(symbol.flags & SymbolFlags.EnumMember); 1948 const type = getTypeAtLocationForLinter(expr); 1949 const isStringEnumLiteral = isEnumType(type) && !!(type.flags & TypeFlags.StringLiteral); 1950 return isEnumMember && isStringEnumLiteral; 1951} 1952 1953export function isValidComputedPropertyName(computedProperty: ComputedPropertyName, isRecordObjectInitializer = false): boolean { 1954 const expr = computedProperty.expression; 1955 if (!isRecordObjectInitializer) { 1956 if (isSymbolIteratorExpression(expr)) { 1957 return true; 1958 } 1959 } 1960 return isStringLiteralLike(expr) || isEnumStringLiteral(computedProperty.expression); 1961} 1962 1963export function isAllowedIndexSignature(node: IndexSignatureDeclaration): boolean { 1964 1965 /* 1966 * For now, relax index signature only for specific array-like types 1967 * with the following signature: 'collections.Array<T>.[_: number]: T'. 1968 */ 1969 1970 if (node.parameters.length !== 1) { 1971 return false; 1972 } 1973 1974 const paramType = getTypeAtLocationForLinter(node.parameters[0]); 1975 if ((paramType.flags & TypeFlags.Number) === 0) { 1976 return false; 1977 } 1978 1979 return isArkTSCollectionsArrayLikeDeclaration(node.parent); 1980} 1981 1982export function isArkTSCollectionsArrayLikeType(type: Type): boolean { 1983 const symbol = type.aliasSymbol ?? type.getSymbol(); 1984 if (symbol?.declarations === undefined || symbol.declarations.length < 1) { 1985 return false; 1986 } 1987 1988 return isArkTSCollectionsArrayLikeDeclaration(symbol.declarations[0]); 1989} 1990 1991function isArkTSCollectionsArrayLikeDeclaration(decl: Declaration): boolean { 1992 if (!isArkTSCollectionsClassOrInterfaceDeclaration(decl)) { 1993 return false; 1994 } 1995 if (!hasIndexSignature(getTypeAtLocationForLinter(decl))) { 1996 return false; 1997 } 1998 return true; 1999} 2000 2001export function isArkTSCollectionsClassOrInterfaceDeclaration(decl: Node): boolean { 2002 if (!isClassDeclaration(decl) && !isInterfaceDeclaration(decl) || !decl.name) { 2003 return false; 2004 } 2005 if (!isModuleBlock(decl.parent) || decl.parent.parent.name.text !== COLLECTIONS_NAMESPACE) { 2006 return false; 2007 } 2008 if (getBaseFileName(decl.getSourceFile().fileName).toLowerCase() !== ARKTS_COLLECTIONS_D_ETS) { 2009 return false; 2010 } 2011 return true; 2012} 2013 2014export function getDecoratorName(decorator: Decorator): string { 2015 let decoratorName = ''; 2016 if (isIdentifier(decorator.expression)) { 2017 decoratorName = decorator.expression.text; 2018 } else if (isCallExpression(decorator.expression) && isIdentifier(decorator.expression.expression)) { 2019 decoratorName = decorator.expression.expression.text; 2020 } 2021 return decoratorName; 2022} 2023 2024export function unwrapParenthesizedTypeNode(typeNode: TypeNode): TypeNode { 2025 let unwrappedTypeNode = typeNode; 2026 while (isParenthesizedTypeNode(unwrappedTypeNode)) { 2027 unwrappedTypeNode = unwrappedTypeNode.type; 2028 } 2029 return unwrappedTypeNode; 2030} 2031 2032export function isSendableTypeNode(typeNode: TypeNode, isShared: boolean = false): boolean { 2033 2034 /* 2035 * In order to correctly identify the usage of the enum member or 2036 * const enum in type annotation, we need to handle union type and 2037 * type alias cases by processing the type node and checking the 2038 * symbol in case of type reference node. 2039 */ 2040 2041 typeNode = unwrapParenthesizedTypeNode(typeNode); 2042 2043 // Only a sendable union type is supported 2044 if (isUnionTypeNode(typeNode)) { 2045 return typeNode.types.every((elemType) => { 2046 return isSendableTypeNode(elemType, isShared); 2047 }); 2048 } 2049 2050 const sym = isTypeReferenceNode(typeNode) ? 2051 trueSymbolAtLocation(typeNode.typeName) : 2052 undefined; 2053 2054 if (sym && sym.getFlags() & SymbolFlags.TypeAlias) { 2055 const typeDecl = getDeclaration(sym); 2056 if (typeDecl && isTypeAliasDeclaration(typeDecl)) { 2057 return isSendableTypeNode(typeDecl.type, isShared); 2058 } 2059 } 2060 2061 // Const enum type is supported 2062 if (isConstEnum(sym)) { 2063 return true; 2064 } 2065 const type: Type = typeChecker.getTypeFromTypeNode(typeNode); 2066 2067 // In shared module, literal forms of primitive data types can be exported 2068 if (isShared && isPurePrimitiveLiteralType(type)) { 2069 return true; 2070 } 2071 2072 return isSendableType(type); 2073} 2074 2075export function isSendableType(type: Type): boolean { 2076 if ((type.flags & (TypeFlags.Boolean | TypeFlags.Number | TypeFlags.String | 2077 TypeFlags.BigInt | TypeFlags.Null | TypeFlags.Undefined | 2078 TypeFlags.TypeParameter)) !== 0) { 2079 return true; 2080 } 2081 if (isSendableTypeAlias(type)) { 2082 return true; 2083 } 2084 if (isSendableFunction(type)) { 2085 return true; 2086 } 2087 2088 return isSendableClassOrInterface(type); 2089} 2090 2091export function isShareableType(tsType: Type): boolean { 2092 const sym = tsType.getSymbol(); 2093 if (isConstEnum(sym)) { 2094 return true; 2095 } 2096 2097 if (!!sym && sym.flags === SymbolFlags.EnumMember) { 2098 if (isConstEnum(sym.parent)) { 2099 return true; 2100 } 2101 } 2102 2103 if (tsType.isUnion()) { 2104 return tsType.types.every((elemType) => { 2105 return isShareableType(elemType); 2106 }); 2107 } 2108 2109 if (isPurePrimitiveLiteralType(tsType)) { 2110 return true; 2111 } 2112 2113 return isSendableType(tsType); 2114} 2115 2116export function isSendableClassOrInterface(type: Type): boolean { 2117 const sym = type.getSymbol(); 2118 if (!sym) { 2119 return false; 2120 } 2121 2122 const targetType = reduceReference(type); 2123 2124 // class with @Sendable decorator 2125 if (targetType.isClass()) { 2126 if (sym.declarations?.length) { 2127 const decl = sym.declarations[0]; 2128 if (isClassDeclaration(decl)) { 2129 return hasSendableDecorator(decl); 2130 } 2131 } 2132 } 2133 // ISendable interface, or a class/interface that implements/extends ISendable interface 2134 return isOrDerivedFrom(type, isISendableInterface); 2135} 2136 2137export function typeContainsSendableClassOrInterface(type: Type): boolean { 2138 // Only check type contains sendable class / interface 2139 if ((type.flags & TypeFlags.Union) !== 0) { 2140 return !!(type as UnionType)?.types?.some((type) => { 2141 return typeContainsSendableClassOrInterface(type); 2142 }); 2143 } 2144 2145 return isSendableClassOrInterface(type); 2146} 2147 2148export function typeContainsNonSendableClassOrInterface(type: Type): boolean { 2149 if (type.isUnion()) { 2150 return type.types.some((compType) => { 2151 return typeContainsNonSendableClassOrInterface(compType); 2152 }); 2153 } 2154 type = reduceReference(type); 2155 return type.isClassOrInterface() && !isSendableClassOrInterface(type); 2156} 2157 2158export function isConstEnum(sym: Symbol | undefined): boolean { 2159 return !!sym && sym.flags === SymbolFlags.ConstEnum; 2160} 2161 2162export function isSendableUnionType(type: UnionType): boolean { 2163 const types = type?.types; 2164 if (!types) { 2165 return false; 2166 } 2167 2168 return types.every((type) => { 2169 return isSendableType(type); 2170 }); 2171} 2172 2173export function hasSendableDecorator(decl: ClassDeclaration | FunctionDeclaration | TypeAliasDeclaration): boolean { 2174 const decorators = getAllDecorators(decl); 2175 return decorators !== undefined && decorators.some((x) => { 2176 return getDecoratorName(x) === SENDABLE_DECORATOR; 2177 }); 2178} 2179 2180export function getNonSendableDecorators(decl: ClassDeclaration | FunctionDeclaration | TypeAliasDeclaration): Decorator[] | undefined { 2181 const decorators = getAllDecorators(decl); 2182 return decorators?.filter((x) => { 2183 return getDecoratorName(x) !== SENDABLE_DECORATOR; 2184 }); 2185} 2186 2187export function getSendableDecorator(decl: ClassDeclaration | FunctionDeclaration | TypeAliasDeclaration): Decorator | undefined { 2188 const decorators = getAllDecorators(decl); 2189 return decorators?.find((x) => { 2190 return getDecoratorName(x) === SENDABLE_DECORATOR; 2191 }); 2192} 2193 2194export function getDecoratorsIfInSendableClass(declaration: HasDecorators): readonly Decorator[] | undefined { 2195 const classNode = getClassNodeFromDeclaration(declaration); 2196 if (classNode === undefined || !hasSendableDecorator(classNode)) { 2197 return undefined; 2198 } 2199 return getDecorators(declaration); 2200} 2201 2202function getClassNodeFromDeclaration(declaration: HasDecorators): ClassDeclaration | undefined { 2203 if (declaration.kind === SyntaxKind.Parameter) { 2204 return isClassDeclaration(declaration.parent.parent) ? declaration.parent.parent : undefined; 2205 } 2206 return isClassDeclaration(declaration.parent) ? declaration.parent : undefined; 2207} 2208 2209export function isISendableInterface(type: Type): boolean { 2210 const symbol = type.aliasSymbol ?? type.getSymbol(); 2211 if (symbol?.declarations === undefined || symbol.declarations.length < 1) { 2212 return false; 2213 } 2214 2215 return isArkTSISendableDeclaration(symbol.declarations[0]); 2216} 2217 2218function isArkTSISendableDeclaration(decl: Declaration): boolean { 2219 if (!isInterfaceDeclaration(decl) || !decl.name || decl.name.text !== ISENDABLE_TYPE) { 2220 return false; 2221 } 2222 2223 if (!isModuleBlock(decl.parent) || decl.parent.parent.name.text !== LANG_NAMESPACE) { 2224 return false; 2225 } 2226 2227 if (getBaseFileName(decl.getSourceFile().fileName).toLowerCase() !== ARKTS_LANG_D_ETS) { 2228 return false; 2229 } 2230 2231 return true; 2232} 2233 2234export function isSharedModule(sourceFile: SourceFile): boolean { 2235 const statements = sourceFile.statements; 2236 for (let statement of statements) { 2237 if (isImportDeclaration(statement)) { 2238 continue; 2239 } 2240 2241 return ( 2242 isExpressionStatement(statement) && 2243 isStringLiteral(statement.expression) && 2244 statement.expression.text === USE_SHARED 2245 ); 2246 } 2247 2248 return false; 2249} 2250 2251export function getDeclarationNode(node: Node): Declaration | undefined { 2252 const sym = trueSymbolAtLocation(node); 2253 return getDeclaration(sym); 2254} 2255 2256function isFunctionLikeDeclaration(node: Declaration): boolean { 2257 return isFunctionDeclaration(node) || isMethodDeclaration(node) || 2258 isGetAccessorDeclaration(node) || isSetAccessorDeclaration(node) || isConstructorDeclaration(node) || 2259 isFunctionExpression(node) || isArrowFunction(node); 2260} 2261 2262export function isShareableEntity(node: Node): boolean { 2263 const decl = getDeclarationNode(node); 2264 const typeNode = (decl as any)?.type; 2265 return (typeNode && !isFunctionLikeDeclaration(decl!)) ? 2266 isSendableTypeNode(typeNode, true) : 2267 isShareableType(getTypeAtLocationForLinter(decl ? decl : node)); 2268} 2269 2270export function isSendableClassOrInterfaceEntity(node: Node): boolean { 2271 const decl = getDeclarationNode(node); 2272 if (!decl) { 2273 return false; 2274 } 2275 2276 if (isClassDeclaration(decl)) { 2277 return hasSendableDecorator(decl); 2278 } 2279 2280 if (isInterfaceDeclaration(decl)) { 2281 return isOrDerivedFrom(getTypeAtLocationForLinter(decl), isISendableInterface); 2282 } 2283 return false; 2284} 2285 2286export function isInImportWhiteList(resolvedModule: ResolvedModuleFull): boolean { 2287 if ( 2288 !resolvedModule.resolvedFileName || 2289 getBaseFileName(resolvedModule.resolvedFileName) !== ARKTS_LANG_D_ETS && 2290 getBaseFileName(resolvedModule.resolvedFileName) !== ARKTS_COLLECTIONS_D_ETS 2291 ) { 2292 return false; 2293 } 2294 return true; 2295} 2296 2297// If it is an overloaded function, all declarations for that function are found 2298export function hasSendableDecoratorFunctionOverload(decl: FunctionDeclaration): boolean { 2299 const decorators = getFunctionOverloadDecorators(decl); 2300 return !!decorators?.some((x) => { 2301 return getDecoratorName(x) === SENDABLE_DECORATOR; 2302 }); 2303} 2304 2305function getFunctionOverloadDecorators(funcDecl: FunctionDeclaration): readonly Decorator[] | undefined { 2306 const decls = funcDecl.symbol.getDeclarations(); 2307 if (!decls?.length) { 2308 return undefined; 2309 } 2310 let result: Decorator[] = []; 2311 decls.forEach((decl) => { 2312 if (!isFunctionDeclaration(decl)) { 2313 return; 2314 } 2315 const decorators = getAllDecorators(decl); 2316 if (decorators) { 2317 result = result.concat(decorators); 2318 } 2319 }); 2320 return result.length ? result : undefined; 2321} 2322 2323export function isSendableFunction(type: Type): boolean { 2324 const callSigns = type.getCallSignatures(); 2325 if (!callSigns?.length) { 2326 return false; 2327 } 2328 const decl = callSigns[0].declaration; 2329 if (!decl || !isFunctionDeclaration(decl)) { 2330 return false; 2331 } 2332 return hasSendableDecoratorFunctionOverload(decl); 2333} 2334 2335 2336export function isSendableTypeAlias(type: Type): boolean { 2337 const decl = getTypsAliasOriginalDecl(type); 2338 return !!decl && hasSendableDecorator(decl); 2339} 2340 2341export function hasSendableTypeAlias(type: Type) :boolean { 2342 if (type.isUnion()) { 2343 return type.types.some((compType) => { 2344 return hasSendableTypeAlias(compType); 2345 }); 2346 }; 2347 return isSendableTypeAlias(type); 2348} 2349 2350export function isNonSendableFunctionTypeAlias(type: Type): boolean { 2351 const decl = getTypsAliasOriginalDecl(type); 2352 return !!decl && isFunctionTypeNode(decl.type) && !hasSendableDecorator(decl); 2353} 2354 2355// If the alias refers to another alias, the search continues 2356function getTypsAliasOriginalDecl(type: Type): TypeAliasDeclaration | undefined { 2357 if (!type.aliasSymbol) { 2358 return undefined; 2359 } 2360 const decl = getDeclaration(type.aliasSymbol); 2361 if (!decl || !isTypeAliasDeclaration(decl)) { 2362 return undefined; 2363 } 2364 if (isTypeReferenceNode(decl.type)) { 2365 const targetType = getTypeAtLocationForLinter(decl.type.typeName); 2366 if (targetType.aliasSymbol && (targetType.aliasSymbol.getFlags() & SymbolFlags.TypeAlias)) { 2367 return getTypsAliasOriginalDecl(targetType); 2368 } 2369 } 2370 return decl; 2371} 2372 2373// not allow 'lhsType' contains 'sendable typeAlias' && 'rhsType' contains 'non-sendable function/non-sendable function typeAlias' 2374export function isWrongSendableFunctionAssignment( 2375 lhsType: Type, 2376 rhsType: Type 2377): boolean { 2378 lhsType = getNonNullableType(lhsType); 2379 rhsType = getNonNullableType(rhsType); 2380 if (!hasSendableTypeAlias(lhsType)) { 2381 return false; 2382 } 2383 2384 if (rhsType.isUnion()) { 2385 return rhsType.types.some((compType) => { 2386 return isInvalidSendableFunctionAssignmentType(compType); 2387 }); 2388 } 2389 return isInvalidSendableFunctionAssignmentType(rhsType); 2390} 2391 2392function isInvalidSendableFunctionAssignmentType(type: Type): boolean { 2393 if (type.aliasSymbol) { 2394 return isNonSendableFunctionTypeAlias(type); 2395 } 2396 if (isFunctionalType(type)) { 2397 return !isSendableFunction(type); 2398 } 2399 return false; 2400} 2401 2402// Search for and save the exported declaration in the specified file, re-exporting another module will not be included. 2403export function searchFileExportDecl(sourceFile: SourceFile, targetDecls?: SyntaxKind[]): Set<Node> { 2404 const exportDeclSet = new Set<Node>(); 2405 const appendDecl = (decl: Node | undefined): void => { 2406 if ( 2407 !decl || 2408 targetDecls && !targetDecls.includes(decl.kind) 2409 ) { 2410 return; 2411 } 2412 exportDeclSet.add(decl); 2413 }; 2414 2415 sourceFile.statements.forEach((statement: Statement) => { 2416 if (isExportAssignment(statement)) { 2417 // handle the case:"export default declName;" 2418 if (statement.isExportEquals) { 2419 return; 2420 } 2421 appendDecl(getDeclarationNode(statement.expression)); 2422 } else if (isExportDeclaration(statement)) { 2423 // handle the case:"export { declName1, declName2 };" 2424 if (!statement.exportClause || !isNamedExports(statement.exportClause)) { 2425 return; 2426 } 2427 statement.exportClause.elements.forEach((specifier) => { 2428 appendDecl(getDeclarationNode(specifier.propertyName ?? specifier.name)); 2429 }); 2430 } else if (canHaveModifiers(statement)) { 2431 // handle the case:"export const/class/function... decalName;" 2432 if (!hasModifier(getModifiers(statement), SyntaxKind.ExportKeyword)) { 2433 return; 2434 } 2435 if (!isVariableStatement(statement)) { 2436 appendDecl(statement); 2437 return; 2438 } 2439 for (const exportDecl of statement.declarationList.declarations) { 2440 appendDecl(exportDecl); 2441 } 2442 } 2443 }); 2444 return exportDeclSet; 2445} 2446 2447export function clearUtilsGlobalvariables(): void { 2448 parentSymbolCache?.clear(); 2449 parentSymbolCache = undefined; 2450} 2451 2452 2453export function isTaskPoolApi(exprSym: Symbol | undefined, node: Node): boolean { 2454 if (!isPropertyAccessExpression(node)) { 2455 return false; 2456 } 2457 const propertyAccessNode = node as PropertyAccessExpression; 2458 const baseExprSym = trueSymbolAtLocation(propertyAccessNode.expression); 2459 if (!exprSym || !baseExprSym) { 2460 return false; 2461 } 2462 if ((baseExprSym.name === TASKPOOL || baseExprSym.name === TASKGROUP) && TASKPOOL_API.includes(exprSym.name)) { 2463 return true; 2464 } 2465 const baseExprType = typeChecker.getTypeOfSymbol(baseExprSym); 2466 if (typeChecker.typeToString(baseExprType) === TASKGROUP && TASKPOOL_API.includes(exprSym.name)) { 2467 return true; 2468 } 2469 return false; 2470} 2471 2472export function isConcurrentFunction(type: Type): boolean { 2473 const callSigns = type.getCallSignatures(); 2474 if (!callSigns?.length) { 2475 return false; 2476 } 2477 const decl = callSigns[0].declaration; 2478 if (!decl || !isFunctionDeclaration(decl)) { 2479 return false; 2480 } 2481 return hasConcurrentDecoratorFunctionOverload(decl) || hasUseConcurrentDirective(decl); 2482} 2483 2484export function hasConcurrentDecoratorFunctionOverload(decl: FunctionDeclaration): boolean { 2485 const decorators = getFunctionOverloadDecorators(decl); 2486 return !!decorators?.some((x) => { 2487 return getDecoratorName(x) === CONCURRENT_DECORATOR; 2488 }); 2489} 2490 2491export function hasUseConcurrentDirective(decl: FunctionDeclaration): boolean { 2492 const body = decl.body; 2493 if (!body || !isBlock(body)) { 2494 return false; 2495 } 2496 2497 const firstStatement = body.statements[0]; 2498 if (!firstStatement) { 2499 return false; 2500 } 2501 2502 if (isExpressionStatement(firstStatement) && isStringLiteral(firstStatement.expression)) { 2503 return firstStatement.expression.text.trim() === USE_CONCURRENT; 2504 } 2505 2506 return false; 2507} 2508 2509export function isDeclarationSymbol(sym: Symbol | undefined): boolean { 2510 if (sym && sym.declarations && sym.declarations.length > 0) { 2511 const srcFile = sym.declarations[0].getSourceFile(); 2512 if (!srcFile) { 2513 return false; 2514 } 2515 return srcFile.isDeclarationFile; 2516 } 2517 return false; 2518} 2519 2520export function checkTaskpoolFunction(arg: Expression, argType: Type, argSym: Symbol| undefined): boolean { 2521 if (isFunctionSymbol(argSym)) { 2522 return !isConcurrentFunction(argType); 2523 } 2524 2525 const symbol = trueSymbolAtLocation(arg); 2526 const decl = getDeclaration(symbol); 2527 return !!decl && (isMethodDeclaration(decl) || isClassDeclaration(decl) || isObjectConstructor(decl)); 2528} 2529 2530function isObjectConstructor(decl: Declaration): boolean { 2531 if ( 2532 isPropertySignature(decl) && 2533 decl.name.getText() === 'constructor' && 2534 isInterfaceDeclaration(decl.parent) && 2535 decl.parent.name.getText() === 'Object' 2536 ) { 2537 const sourcefile = decl.getSourceFile(); 2538 return !!sourcefile && sourcefile.fileName.endsWith('lib.es5.d.ts'); 2539 } 2540 return false; 2541} 2542 2543export function getTypeAtLocationForLinter(node: Node): Type { 2544 if (isSourceFile(node) && typeChecker.createIntrinsicType) { 2545 return typeChecker.createIntrinsicType(TypeFlags.Any, 'error'); 2546 } 2547 return typeChecker.getTypeAtLocation(node); 2548} 2549 2550function isStaticSourceFileForLinter(type: Type): boolean { 2551 if (mixCompile && typeChecker.isStaticSourceFile) { 2552 return typeChecker.isStaticSourceFile(type.symbol?.declarations?.[0]?.getSourceFile()); 2553 } 2554 return false; 2555}