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