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