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 Utils { 18//import * as path from 'node:path'; 19//import * as ts from 'typescript'; 20//import ProblemInfo = ts.ProblemInfo; 21//import TypeScriptLinter = TypeScriptLinter; 22import AutofixInfo = Common.AutofixInfo; 23 24export const PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE = 2564; 25 26export const NON_INITIALIZABLE_PROPERTY_DECORATORS = ["Link", "Consume", "ObjectLink", "Prop", "BuilderParam"]; 27 28export const NON_INITIALIZABLE_PROPERTY_ClASS_DECORATORS = ['CustomDialog'] 29 30export const LIMITED_STANDARD_UTILITY_TYPES = [ 31 "Awaited", "Pick", "Omit", "Exclude", "Extract", "NonNullable", "Parameters", 32 "ConstructorParameters", "ReturnType", "InstanceType", "ThisParameterType", "OmitThisParameter", 33 "ThisType", "Uppercase", "Lowercase", "Capitalize", "Uncapitalize", 34]; 35 36export const ALLOWED_STD_SYMBOL_API = ["iterator"] 37 38export enum ProblemSeverity { WARNING = 1, ERROR = 2 } 39 40export const ARKTS_IGNORE_DIRS = ['node_modules', 'oh_modules', 'build', '.preview']; 41export const ARKTS_IGNORE_FILES = ['hvigorfile.ts']; 42 43let typeChecker: TypeChecker; 44export function setTypeChecker(tsTypeChecker: TypeChecker): void { 45 typeChecker = tsTypeChecker; 46} 47 48let testMode = false; 49export function setTestMode(tsTestMode: boolean): void { 50 testMode = tsTestMode; 51} 52 53export function getStartPos(nodeOrComment: Node | CommentRange): number { 54 return (nodeOrComment.kind === SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === SyntaxKind.MultiLineCommentTrivia) 55 ? (nodeOrComment as CommentRange).pos 56 : (nodeOrComment as Node).getStart(); 57} 58 59export function getEndPos(nodeOrComment: Node | CommentRange): number { 60 return (nodeOrComment.kind === SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === SyntaxKind.MultiLineCommentTrivia) 61 ? (nodeOrComment as CommentRange).end 62 : (nodeOrComment as Node).getEnd(); 63} 64 65export function isAssignmentOperator(tsBinOp: BinaryOperatorToken): boolean { 66 return tsBinOp.kind >= SyntaxKind.FirstAssignment && tsBinOp.kind <= SyntaxKind.LastAssignment; 67} 68 69export function isTypedArray(tsType: TypeNode | undefined): boolean { 70 if (tsType === undefined || !isTypeReferenceNode(tsType)) { 71 return false; 72 } 73 return TYPED_ARRAYS.includes(entityNameToString(tsType.typeName)); 74} 75 76export function isType(tsType: TypeNode | undefined, checkType: string): boolean { 77 if (tsType === undefined || !isTypeReferenceNode(tsType)) { 78 return false; 79 } 80 return entityNameToString(tsType.typeName) === checkType; 81} 82 83export function entityNameToString(name: EntityName): string { 84 if (isIdentifier(name)) { 85 return name.escapedText.toString(); 86 } 87 else { 88 return entityNameToString(name.left) + entityNameToString(name.right); 89 } 90} 91 92export function isNumberType(tsType: Type): boolean { 93 if (tsType.isUnion()) { 94 for (const tsCompType of tsType.types) { 95 if ((tsCompType.flags & TypeFlags.NumberLike) === 0) return false; 96 } 97 return true; 98 } 99 return (tsType.getFlags() & TypeFlags.NumberLike) !== 0; 100} 101 102export function isBooleanType(tsType: Type): boolean { 103 return (tsType.getFlags() & TypeFlags.BooleanLike) !== 0; 104} 105 106export function isStringLikeType(tsType: Type): boolean { 107 if (tsType.isUnion()) { 108 for (const tsCompType of tsType.types) { 109 if ((tsCompType.flags & TypeFlags.StringLike) === 0) return false; 110 } 111 return true; 112 } 113 return (tsType.getFlags() & TypeFlags.StringLike) !== 0; 114} 115 116export function isStringType(type: Type): boolean { 117 return (type.getFlags() & TypeFlags.String) !== 0; 118} 119 120export function isPrimitiveEnumType(type: Type, primitiveType: TypeFlags): boolean { 121 const isNonPrimitive = (type.flags & TypeFlags.NonPrimitive) !== 0; 122 if (!isEnumType(type) || !type.isUnion() || isNonPrimitive) { 123 return false; 124 } 125 for (const t of type.types) { 126 if ((t.flags & primitiveType) === 0) { 127 return false; 128 } 129 } 130 return true; 131} 132 133export function isPrimitiveEnumMemberType(type: Type, primitiveType: TypeFlags): boolean { 134 const isNonPrimitive = (type.flags & TypeFlags.NonPrimitive) !== 0; 135 if (!isEnumMemberType(type) || isNonPrimitive) { 136 return false; 137 } 138 return (type.flags & primitiveType) !== 0; 139} 140 141export function unwrapParenthesizedType(tsType: TypeNode): TypeNode { 142 while (isParenthesizedTypeNode(tsType)) { 143 tsType = tsType.type; 144 } 145 return tsType; 146} 147 148export function findParentIf(asExpr: AsExpression): IfStatement | null { 149 let node = asExpr.parent; 150 while (node) { 151 if (node.kind === SyntaxKind.IfStatement) { 152 return node as IfStatement; 153 } 154 node = node.parent; 155 } 156 157 return null; 158} 159 160export function isDestructuringAssignmentLHS( 161 tsExpr: ArrayLiteralExpression | ObjectLiteralExpression 162): boolean { 163 // Check whether given expression is the LHS part of the destructuring 164 // assignment (or is a nested element of destructuring pattern). 165 let tsParent = tsExpr.parent; 166 let tsCurrentExpr: Node = tsExpr; 167 while (tsParent) { 168 if ( 169 isBinaryExpression(tsParent) && isAssignmentOperator(tsParent.operatorToken) && 170 tsParent.left === tsCurrentExpr 171 ) { 172 return true; 173 } 174 if ( 175 (isForStatement(tsParent) || isForInStatement(tsParent) || isForOfStatement(tsParent)) && 176 tsParent.initializer && tsParent.initializer === tsCurrentExpr 177 ) { 178 return true; 179 } 180 tsCurrentExpr = tsParent; 181 tsParent = tsParent.parent; 182 } 183 184 return false; 185} 186 187export function isEnumType(tsType: Type): boolean { 188 // Note: For some reason, test (tsType.flags & TypeFlags.Enum) != 0 doesn't work here. 189 // Must use SymbolFlags to figure out if this is an enum type. 190 return tsType.symbol && (tsType.symbol.flags & SymbolFlags.Enum) !== 0; 191} 192 193export function isEnumMemberType(tsType: Type): boolean { 194 // Note: For some reason, test (tsType.flags & TypeFlags.Enum) != 0 doesn't work here. 195 // Must use SymbolFlags to figure out if this is an enum type. 196 return tsType.symbol && (tsType.symbol.flags & SymbolFlags.EnumMember) !== 0; 197 } 198 199export function isObjectLiteralType(tsType: Type): boolean { 200 return tsType.symbol && (tsType.symbol.flags & SymbolFlags.ObjectLiteral) !== 0; 201} 202 203export function isNumberLikeType(tsType: Type): boolean { 204 return (tsType.getFlags() & TypeFlags.NumberLike) !== 0; 205} 206 207export function hasModifier(tsModifiers: readonly Modifier[] | undefined, tsModifierKind: number): boolean { 208 // Sanity check. 209 if (!tsModifiers) return false; 210 211 for (const tsModifier of tsModifiers) { 212 if (tsModifier.kind === tsModifierKind) return true; 213 } 214 215 return false; 216} 217 218export function unwrapParenthesized(tsExpr: Expression): Expression { 219 let unwrappedExpr = tsExpr; 220 while (isParenthesizedExpression(unwrappedExpr)) { 221 unwrappedExpr = unwrappedExpr.expression; 222 } 223 return unwrappedExpr; 224} 225 226export function followIfAliased(sym: Symbol): Symbol { 227 if ((sym.getFlags() & SymbolFlags.Alias) !== 0) { 228 return typeChecker.getAliasedSymbol(sym); 229 } 230 return sym; 231} 232 233let trueSymbolAtLocationCache = new Map<ts.Node, ts.Symbol | null>(); 234 235export function trueSymbolAtLocation(node: Node): Symbol | undefined { 236 let cache = trueSymbolAtLocationCache; 237 let val = cache.get(node); 238 if (val !== undefined) { 239 return val !== null ? val : undefined; 240 } 241 let sym = typeChecker.getSymbolAtLocation(node); 242 if (sym === undefined) { 243 cache.set(node, null); 244 return undefined; 245 } 246 sym = followIfAliased(sym); 247 cache.set(node, sym); 248 return sym; 249} 250 251export function isTypeDeclSyntaxKind(kind: SyntaxKind) { 252 return isStructDeclarationKind(kind) || 253 kind === SyntaxKind.EnumDeclaration || 254 kind === SyntaxKind.ClassDeclaration || 255 kind === SyntaxKind.InterfaceDeclaration || 256 kind === SyntaxKind.TypeAliasDeclaration; 257} 258 259export function symbolHasDuplicateName(symbol: Symbol, tsDeclKind: SyntaxKind): boolean { 260 // Type Checker merges all declarations with the same name in one scope into one symbol. 261 // Thus, check whether the symbol of certain declaration has any declaration with 262 // different syntax kind. 263 const symbolDecls = symbol?.getDeclarations(); 264 if (symbolDecls) { 265 for (const symDecl of symbolDecls) { 266 const declKind = symDecl.kind; 267 // we relax arkts-unique-names for namespace collision with class/interface/enum/type/struct 268 const isNamespaceTypeCollision = 269 (isTypeDeclSyntaxKind(declKind) && tsDeclKind === SyntaxKind.ModuleDeclaration) || 270 (isTypeDeclSyntaxKind(tsDeclKind) && declKind === SyntaxKind.ModuleDeclaration); 271 272 // Don't count declarations with 'Identifier' syntax kind as those 273 // usually depict declaring an object's property through assignment. 274 if (declKind !== SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) return true; 275 } 276 } 277 278 return false; 279} 280 281export function isReferenceType(tsType: Type): boolean { 282 const f = tsType.getFlags(); 283 return ( 284 (f & TypeFlags.InstantiableNonPrimitive) !== 0 || (f & TypeFlags.Object) !== 0 || 285 (f & TypeFlags.Boolean) !== 0 || (f & TypeFlags.Enum) !== 0 || (f & TypeFlags.NonPrimitive) !== 0 || 286 (f & TypeFlags.Number) !== 0 || (f & TypeFlags.String) !== 0 287 ); 288} 289 290export function isPrimitiveType(type: Type): boolean { 291 const f = type.getFlags(); 292 return ( 293 (f & TypeFlags.Boolean) !== 0 || (f & TypeFlags.BooleanLiteral) !== 0 || 294 (f & TypeFlags.Number) !== 0 || (f & TypeFlags.NumberLiteral) !== 0 295 // In ArkTS 'string' is not a primitive type. So for the common subset 'string' 296 // should be considered as a reference type. That is why next line is commented out. 297 //(f & TypeFlags.String) != 0 || (f & TypeFlags.StringLiteral) != 0 298 ); 299} 300 301export function isTypeSymbol(symbol: Symbol | undefined): boolean { 302 return ( 303 !!symbol && !!symbol.flags && 304 ((symbol.flags & SymbolFlags.Class) !== 0 || (symbol.flags & SymbolFlags.Interface) !== 0) 305 ); 306} 307 308// Check whether type is generic 'Array<T>' type defined in TypeScript standard library. 309export function isGenericArrayType(tsType: Type): tsType is TypeReference { 310 return ( 311 isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 && 312 tsType.getSymbol()?.getName() === "Array" 313 ); 314} 315 316// does something similar to relatedByInheritanceOrIdentical function 317export function isDerivedFrom(tsType: Type, checkType: CheckType): tsType is TypeReference { 318 if (isTypeReference(tsType) && tsType.target !== tsType) tsType = tsType.target; 319 320 const tsTypeNode = typeChecker.typeToTypeNode(tsType, undefined, NodeBuilderFlags.None); 321 if (checkType === CheckType.Array && (isGenericArrayType(tsType) || isTypedArray(tsTypeNode))) return true; 322 if (checkType !== CheckType.Array && isType(tsTypeNode, checkType.toString())) return true; 323 if (!tsType.symbol || !tsType.symbol.declarations) return false; 324 325 for (const tsTypeDecl of tsType.symbol.declarations) { 326 if ( 327 (!isClassDeclaration(tsTypeDecl) && !isInterfaceDeclaration(tsTypeDecl)) || 328 !tsTypeDecl.heritageClauses 329 ) continue; 330 for (const heritageClause of tsTypeDecl.heritageClauses) { 331 if (processParentTypesCheck(heritageClause.types, checkType)) return true; 332 } 333 } 334 335 return false; 336} 337 338export function isTypeReference(tsType: Type): tsType is TypeReference { 339 return ( 340 (tsType.getFlags() & TypeFlags.Object) !== 0 && 341 ((tsType as ObjectType).objectFlags & ObjectFlags.Reference) !== 0 342 ); 343} 344 345export function isNullType(tsTypeNode: TypeNode): boolean { 346 return (isLiteralTypeNode(tsTypeNode) && tsTypeNode.literal.kind === SyntaxKind.NullKeyword); 347} 348 349export function isThisOrSuperExpr(tsExpr: Expression): boolean { 350 return (tsExpr.kind === SyntaxKind.ThisKeyword || tsExpr.kind === SyntaxKind.SuperKeyword); 351} 352 353export function isPrototypeSymbol(symbol: Symbol | undefined): boolean { 354 return (!!symbol && !!symbol.flags && (symbol.flags & SymbolFlags.Prototype) !== 0); 355} 356 357export function isFunctionSymbol(symbol: Symbol | undefined): boolean { 358 return (!!symbol && !!symbol.flags && (symbol.flags & SymbolFlags.Function) !== 0); 359} 360 361export function isInterfaceType(tsType: Type | undefined): boolean { 362 return ( 363 !!tsType && !!tsType.symbol && !!tsType.symbol.flags && 364 (tsType.symbol.flags & SymbolFlags.Interface) !== 0 365 ); 366} 367 368export function isAnyType(tsType: Type): tsType is TypeReference { 369 return (tsType.getFlags() & TypeFlags.Any) !== 0; 370} 371 372export function isUnknownType(tsType: Type): boolean { 373 return (tsType.getFlags() & TypeFlags.Unknown) !== 0; 374} 375 376export function isUnsupportedType(tsType: Type): boolean { 377 return ( 378 !!tsType.flags && ((tsType.flags & TypeFlags.Any) !== 0 || (tsType.flags & TypeFlags.Unknown) !== 0 || 379 (tsType.flags & TypeFlags.Intersection) !== 0) 380 ); 381} 382 383export function isUnsupportedUnionType(tsType: Type): boolean { 384 if (tsType.isUnion()) { 385 return !isNullableUnionType(tsType) && !isBooleanUnionType(tsType); 386 } 387 return false; 388} 389 390function isNullableUnionType(tsUnionType: UnionType): boolean { 391 const tsTypes = tsUnionType.types; 392 return ( 393 tsTypes.length === 2 && 394 ((tsTypes[0].flags & TypeFlags.Null) !== 0 || (tsTypes[1].flags & TypeFlags.Null) !== 0) 395 ); 396} 397 398function isBooleanUnionType(tsUnionType: UnionType): boolean { 399 // For some reason, 'boolean' type is also represented as as union 400 // of 'true' and 'false' literal types. This form of 'union' type 401 // should be considered as supported. 402 const tsCompTypes = tsUnionType.types; 403 return ( 404 tsUnionType.flags === (TypeFlags.Boolean | TypeFlags.Union) && tsCompTypes.length === 2 && 405 tsCompTypes[0].flags === TypeFlags.BooleanLiteral && (tsCompTypes[1].flags === TypeFlags.BooleanLiteral) 406 ); 407} 408 409export function isFunctionOrMethod(tsSymbol: Symbol | undefined): boolean { 410 return ( 411 !!tsSymbol && 412 ((tsSymbol.flags & SymbolFlags.Function) !== 0 || (tsSymbol.flags & SymbolFlags.Method) !== 0) 413 ); 414} 415 416export function isMethodAssignment(tsSymbol: Symbol | undefined): boolean { 417 return ( 418 !!tsSymbol && 419 ((tsSymbol.flags & SymbolFlags.Method) !== 0 && (tsSymbol.flags & SymbolFlags.Assignment) !== 0) 420 ); 421} 422 423export function getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined { 424 if (tsSymbol && tsSymbol.declarations && tsSymbol.declarations.length > 0) { 425 return tsSymbol.declarations[0]; 426 } 427 return undefined; 428} 429 430function isVarDeclaration(tsDecl: Node): boolean { 431 return isVariableDeclaration(tsDecl) && isVariableDeclarationList(tsDecl.parent); 432} 433 434export function isValidEnumMemberInit(tsExpr: Expression): boolean { 435 if (isNumberConstantValue(tsExpr.parent as EnumMember)) { 436 return true; 437 } 438 if (isStringConstantValue(tsExpr.parent as EnumMember)) { 439 return true; 440 } 441 return isCompileTimeExpression(tsExpr); 442} 443 444export function isCompileTimeExpression(tsExpr: Expression): boolean { 445 if ( 446 isParenthesizedExpression(tsExpr) || 447 (isAsExpression(tsExpr) && tsExpr.type.kind === SyntaxKind.NumberKeyword)) { 448 return isCompileTimeExpression(tsExpr.expression); 449 } 450 switch (tsExpr.kind) { 451 case SyntaxKind.PrefixUnaryExpression: 452 return isPrefixUnaryExprValidEnumMemberInit(tsExpr as PrefixUnaryExpression); 453 case SyntaxKind.ParenthesizedExpression: 454 case SyntaxKind.BinaryExpression: 455 return isBinaryExprValidEnumMemberInit(tsExpr as BinaryExpression); 456 case SyntaxKind.ConditionalExpression: 457 return isConditionalExprValidEnumMemberInit(tsExpr as ConditionalExpression); 458 case SyntaxKind.Identifier: 459 return isIdentifierValidEnumMemberInit(tsExpr as Identifier); 460 case SyntaxKind.NumericLiteral: 461 return true; 462 case SyntaxKind.StringLiteral: 463 return true; 464 case SyntaxKind.PropertyAccessExpression: { 465 // if enum member is in current enum declaration try to get value 466 // if it comes from another enum consider as constant 467 const propertyAccess = tsExpr as PropertyAccessExpression; 468 if(isNumberConstantValue(propertyAccess)) { 469 return true; 470 } 471 const leftHandSymbol = typeChecker.getSymbolAtLocation(propertyAccess.expression); 472 if(!leftHandSymbol) { 473 return false; 474 } 475 const decls = leftHandSymbol.getDeclarations(); 476 if (!decls || decls.length !== 1) { 477 return false; 478 } 479 return isEnumDeclaration(decls[0]); 480 } 481 default: 482 return false; 483 } 484} 485 486function isPrefixUnaryExprValidEnumMemberInit(tsExpr: PrefixUnaryExpression): boolean { 487 return (isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && isCompileTimeExpression(tsExpr.operand)); 488} 489 490function isBinaryExprValidEnumMemberInit(tsExpr: BinaryExpression): boolean { 491 return ( 492 isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && isCompileTimeExpression(tsExpr.left) && 493 isCompileTimeExpression(tsExpr.right) 494 ); 495} 496 497function isConditionalExprValidEnumMemberInit(tsExpr: ConditionalExpression): boolean { 498 return (isCompileTimeExpression(tsExpr.whenTrue) && isCompileTimeExpression(tsExpr.whenFalse)); 499} 500 501function isIdentifierValidEnumMemberInit(tsExpr: Identifier): boolean { 502 const tsSymbol = typeChecker.getSymbolAtLocation(tsExpr); 503 const tsDecl = getDeclaration(tsSymbol); 504 return (!!tsDecl && 505 ((isVarDeclaration(tsDecl) && isConst(tsDecl.parent)) || 506 (tsDecl.kind === SyntaxKind.EnumMember) 507 ) 508 ); 509} 510 511function isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: PrefixUnaryOperator): boolean { 512 return ( 513 tsPrefixUnaryOp === SyntaxKind.PlusToken || tsPrefixUnaryOp === SyntaxKind.MinusToken || 514 tsPrefixUnaryOp === SyntaxKind.TildeToken 515 ); 516} 517 518function isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: BinaryOperatorToken): boolean { 519 return ( 520 tsBinaryOp.kind === SyntaxKind.AsteriskToken || tsBinaryOp.kind === SyntaxKind.SlashToken || 521 tsBinaryOp.kind === SyntaxKind.PercentToken || tsBinaryOp.kind === SyntaxKind.MinusToken || 522 tsBinaryOp.kind === SyntaxKind.PlusToken || tsBinaryOp.kind === SyntaxKind.LessThanLessThanToken || 523 tsBinaryOp.kind === SyntaxKind.GreaterThanGreaterThanToken || tsBinaryOp.kind === SyntaxKind.BarBarToken || 524 tsBinaryOp.kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken || 525 tsBinaryOp.kind === SyntaxKind.AmpersandToken || tsBinaryOp.kind === SyntaxKind.CaretToken || 526 tsBinaryOp.kind === SyntaxKind.BarToken || tsBinaryOp.kind === SyntaxKind.AmpersandAmpersandToken 527 ); 528} 529 530export function isConst(tsNode: Node): boolean { 531 return !!(getCombinedNodeFlags(tsNode) & NodeFlags.Const); 532} 533 534export function isNumberConstantValue( 535 tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression | NumericLiteral 536): boolean { 537 538 const tsConstValue = (tsExpr.kind === SyntaxKind.NumericLiteral) ? 539 Number(tsExpr.getText()) : 540 typeChecker.getConstantValue(tsExpr); 541 542 return tsConstValue !== undefined && typeof tsConstValue === "number"; 543} 544 545export function isIntegerConstantValue( 546 tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression | NumericLiteral 547): boolean { 548 549 const tsConstValue = (tsExpr.kind === SyntaxKind.NumericLiteral) ? 550 Number(tsExpr.getText()) : 551 typeChecker.getConstantValue(tsExpr); 552 return ( 553 tsConstValue !== undefined && typeof tsConstValue === "number" && 554 tsConstValue.toFixed(0) === tsConstValue.toString() 555 ); 556} 557 558export function isStringConstantValue( 559 tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression 560): boolean { 561 const tsConstValue = typeChecker.getConstantValue(tsExpr); 562 return ( 563 tsConstValue !== undefined && typeof tsConstValue === "string" 564 ); 565} 566 567// Returns true iff typeA is a subtype of typeB 568export function relatedByInheritanceOrIdentical(typeA: Type, typeB: Type): boolean { 569 if (isTypeReference(typeA) && typeA.target !== typeA) { typeA = typeA.target; } 570 if (isTypeReference(typeB) && typeB.target !== typeB) { typeB = typeB.target; } 571 572 if (typeA === typeB || isObjectType(typeB)) { return true; } 573 if (!typeA.symbol || !typeA.symbol.declarations) { return false; } 574 575 for (const typeADecl of typeA.symbol.declarations) { 576 if ( 577 (!isClassDeclaration(typeADecl) && !isInterfaceDeclaration(typeADecl)) || 578 !typeADecl.heritageClauses 579 ) { continue; } 580 for (const heritageClause of typeADecl.heritageClauses) { 581 const processInterfaces = typeA.isClass() ? (heritageClause.token !== SyntaxKind.ExtendsKeyword) : true; 582 if (processParentTypes(heritageClause.types, typeB, processInterfaces)) return true; 583 } 584 } 585 586 return false; 587} 588 589// return true if two class types are not related by inheritance and structural identity check is needed 590export function needToDeduceStructuralIdentity(typeFrom: Type, typeTo: Type, allowPromotion = false): boolean { 591 if (isLibraryType(typeTo)) { 592 return false; 593 } 594 595 let res = typeTo.isClassOrInterface() && typeFrom.isClassOrInterface() && !relatedByInheritanceOrIdentical(typeFrom, typeTo); 596 597 if (allowPromotion) { 598 res &&= !relatedByInheritanceOrIdentical(typeTo, typeFrom); 599 } 600 601 return res; 602} 603 604export function hasPredecessor(node: Node, predicate: (node: Node) => boolean): boolean { 605 let parent = node.parent; 606 while (parent !== undefined) { 607 if (predicate(parent)) { 608 return true; 609 } 610 parent = parent.parent; 611 } 612 return false; 613} 614 615export function processParentTypes(parentTypes: NodeArray<ExpressionWithTypeArguments>, typeB: Type, processInterfaces: boolean): boolean { 616 for (const baseTypeExpr of parentTypes) { 617 let baseType = typeChecker.getTypeAtLocation(baseTypeExpr); 618 if (isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; 619 if (baseType && (baseType.isClass() !== processInterfaces) && relatedByInheritanceOrIdentical(baseType, typeB)) return true; 620 } 621 return false; 622} 623 624export function processParentTypesCheck(parentTypes: NodeArray<ExpressionWithTypeArguments>, checkType: CheckType): boolean { 625 for (const baseTypeExpr of parentTypes) { 626 let baseType = typeChecker.getTypeAtLocation(baseTypeExpr); 627 if (isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; 628 if (baseType && isDerivedFrom(baseType, checkType)) return true; 629 } 630 return false; 631} 632 633 634export function isObjectType(tsType: Type): boolean { 635 if (!tsType) { 636 return false; 637 } 638 if (tsType.symbol && (tsType.isClassOrInterface() && tsType.symbol.name === "Object")) { 639 return true; 640 } 641 const node = typeChecker.typeToTypeNode(tsType, undefined, undefined); 642 return node !== undefined && node.kind === SyntaxKind.ObjectKeyword; 643} 644 645export function logTscDiagnostic(diagnostics: readonly Diagnostic[], log: (message: any, ...args: any[]) => void): void { 646 diagnostics.forEach((diagnostic) => { 647 let message = flattenDiagnosticMessageText(diagnostic.messageText, "\n"); 648 649 if (diagnostic.file && diagnostic.start) { 650 const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); 651 message = `${diagnostic.file.fileName} (${line + 1}, ${character + 1}): ${message}`; 652 } 653 654 log(message); 655 }); 656} 657 658export function encodeProblemInfo(problem: ProblemInfo): string { 659 return `${problem.problem}%${problem.start}%${problem.end}`; 660} 661 662export function decodeAutofixInfo(info: string): AutofixInfo { 663 const infos = info.split("%"); 664 return { problemID: infos[0], start: Number.parseInt(infos[1]), end: Number.parseInt(infos[2]) }; 665} 666 667export function isCallToFunctionWithOmittedReturnType(tsExpr: Expression): boolean { 668 if (isCallExpression(tsExpr)) { 669 const tsCallSignature = typeChecker.getResolvedSignature(tsExpr); 670 if (tsCallSignature) { 671 const tsSignDecl = tsCallSignature.getDeclaration(); 672 // `tsSignDecl` is undefined when `getResolvedSignature` returns `unknownSignature` 673 if (!tsSignDecl || !tsSignDecl.type) return true; 674 } 675 } 676 677 return false; 678} 679 680function hasReadonlyFields(type: Type): boolean { 681 if (type.symbol.members === undefined) return false; // No members -> no readonly fields 682 683 let result = false; 684 685 type.symbol.members.forEach((value /*, key*/) => { 686 if ( 687 value.declarations !== undefined && value.declarations.length > 0 && 688 isPropertyDeclaration(value.declarations[0]) 689 ) { 690 const propmMods = value.declarations[0].modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 691 if (hasModifier(propmMods, SyntaxKind.ReadonlyKeyword)) { 692 result = true; 693 return; 694 } 695 } 696 }); 697 698 return result; 699} 700 701function hasDefaultCtor(type: Type): boolean { 702 if (type.symbol.members === undefined) return true; // No members -> no explicite constructors -> there is default ctor 703 704 let hasCtor = false; // has any constructor 705 let hasDefaultCtor = false; // has default constructor 706 707 type.symbol.members.forEach((value /*, key*/) => { 708 if ((value.flags & SymbolFlags.Constructor) !== 0) { 709 hasCtor = true; 710 711 if (value.declarations !== undefined && value.declarations.length > 0) { 712 const declCtor = value.declarations[0] as ConstructorDeclaration; 713 if (declCtor.parameters.length === 0) { 714 hasDefaultCtor = true; 715 return; 716 } 717 } 718 } 719 }); 720 721 return !hasCtor || hasDefaultCtor; // Has no any explicite constructor -> has implicite default constructor. 722} 723 724function isAbstractClass(type: Type): boolean { 725 if (type.isClass() && type.symbol.declarations && type.symbol.declarations.length > 0) { 726 const declClass = type.symbol.declarations[0] as ClassDeclaration; 727 const classMods = declClass.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 728 if (hasModifier(classMods, SyntaxKind.AbstractKeyword)) { 729 return true; 730 } 731 } 732 733 return false; 734} 735 736export function validateObjectLiteralType(type: Type | undefined): boolean { 737 if (!type) return false; 738 739 type = getTargetType(type); 740 return ( 741 type !== undefined && type.isClassOrInterface() && hasDefaultCtor(type) && 742 !hasReadonlyFields(type) && !isAbstractClass(type) 743 ); 744} 745 746export function isStructDeclarationKind(kind: SyntaxKind) { 747 return kind === SyntaxKind.StructDeclaration; 748} 749 750export function isStructDeclaration(node: Node) { 751 return isStructDeclarationKind(node.kind); 752} 753export function isStructObjectInitializer(objectLiteral: ObjectLiteralExpression): boolean { 754 if(isCallLikeExpression(objectLiteral.parent)) { 755 const signature = typeChecker.getResolvedSignature(objectLiteral.parent); 756 const signDecl = signature?.declaration; 757 return !!signDecl && isConstructorDeclaration(signDecl) && isStructDeclaration(signDecl.parent); 758 } 759 return false; 760} 761 762export function hasMethods(type: Type): boolean { 763 const properties = typeChecker.getPropertiesOfType(type); 764 if (properties?.length) { 765 for (const prop of properties) { 766 if (prop.getFlags() & SymbolFlags.Method) return true; 767 } 768 }; 769 770 return false; 771} 772 773function findProperty(type: Type, name: string): Symbol | undefined { 774 const properties = typeChecker.getPropertiesOfType(type); 775 if(properties.length) { 776 for (const prop of properties) { 777 if (prop.name === name) return prop; 778 } 779 } 780 return undefined; 781} 782 783 784function getNonNullableType(t: ts.Type) { 785 if (t.isUnion()) { 786 return t.getNonNullableType(); 787 } 788 return t; 789} 790 791export function isExpressionAssignableToType(lhsType: ts.Type | undefined, rhsExpr: ts.Expression): boolean { 792 if (lhsType === undefined) { 793 return false; 794 } 795 796 let nonNullableLhs = getNonNullableType(lhsType); 797 798 // Allow initializing with anything when the type 799 // originates from the library. 800 if (isAnyType(nonNullableLhs) || isLibraryType(nonNullableLhs)) { 801 return true; 802 } 803 804 // issue 13412: 805 // Allow initializing with a dynamic object when the LHS type 806 // is primitive or defined in standard library. 807 if (isDynamicObjectAssignedToStdType(nonNullableLhs, rhsExpr)) { 808 return true; 809 } 810 811 // Allow initializing Record objects with object initializer. 812 // Record supports any type for a its value, but the key value 813 // must be either a string or number literal. 814 if (isStdRecordType(nonNullableLhs) && ts.isObjectLiteralExpression(rhsExpr)) { 815 return validateRecordObjectKeys(rhsExpr); 816 } 817 818 // For Partial<T>, Required<T>, Readonly<T> types, validate their argument type. 819 if (isStdPartialType(nonNullableLhs) || isStdRequiredType(nonNullableLhs) || isStdReadonlyType(nonNullableLhs)) { 820 if (nonNullableLhs.aliasTypeArguments && nonNullableLhs.aliasTypeArguments.length === 1) { 821 nonNullableLhs = nonNullableLhs.aliasTypeArguments[0]; 822 } else { 823 return false; 824 } 825 } 826 827 let rhsType = getNonNullableType(typeChecker.getTypeAtLocation(rhsExpr)); 828 829 if (rhsType.isUnion()) { 830 let res = true; 831 for (const compType of rhsType.types) { 832 res &&= areTypesAssignable(lhsType, compType) 833 } 834 return res; 835 } 836 837 if (lhsType.isUnion()) { 838 for (const compType of lhsType.types) { 839 if (isExpressionAssignableToType(compType, rhsExpr)) { 840 return true; 841 } 842 } 843 } 844 845 if (ts.isObjectLiteralExpression(rhsExpr)) { 846 return isObjectLiteralAssignable(nonNullableLhs, rhsExpr); 847 } 848 849 return areTypesAssignable(lhsType, rhsType) 850} 851 852function areTypesAssignable(lhsType: ts.Type, rhsType: ts.Type): boolean { 853 if (rhsType.isUnion()) { 854 let res = true; 855 for (const compType of rhsType.types) { 856 res &&= areTypesAssignable(lhsType, compType) 857 } 858 return res; 859 } 860 861 if (lhsType.isUnion()) { 862 for (const compType of lhsType.types) { 863 if (areTypesAssignable(compType, rhsType)) { 864 return true; 865 } 866 } 867 } 868 869 // Allow initializing with anything when the type 870 // originates from the library. 871 if (isAnyType(lhsType) || isLibraryType(lhsType)) { 872 return true; 873 } 874 875 // If type is a literal type, compare its base type. 876 lhsType = typeChecker.getBaseTypeOfLiteralType(lhsType); 877 rhsType = typeChecker.getBaseTypeOfLiteralType(rhsType); 878 879 // issue 13114: 880 // Const enum values are convertible to string/number type. 881 // Note: This check should appear before calling TypeChecker.getBaseTypeOfLiteralType() 882 // to ensure that lhsType has its original form, as it can be a literal type with 883 // specific number or string value, which shouldn't pass this check. 884 if (isEnumAssignment(lhsType, rhsType)) { 885 return true; 886 } 887 888 // issue 13033: 889 // If both types are functional, they are considered compatible. 890 if (areCompatibleFunctionals(lhsType, rhsType)) { 891 return true; 892 } 893 894 return lhsType === rhsType || relatedByInheritanceOrIdentical(rhsType, getTargetType(lhsType)); 895} 896 897function isDynamicObjectAssignedToStdType(lhsType: Type, rhsExpr: Expression): boolean { 898 if (isStdLibraryType(lhsType) || isPrimitiveType(lhsType)) { 899 const rhsSym = isCallExpression(rhsExpr) 900 ? getSymbolOfCallExpression(rhsExpr) 901 : typeChecker.getSymbolAtLocation(rhsExpr); 902 903 if (rhsSym && isLibrarySymbol(rhsSym)) return true; 904 } 905 return false; 906} 907 908function isObjectLiteralAssignable(lhsType: Type, rhsExpr: Expression): boolean { 909 if (isObjectLiteralExpression(rhsExpr)) { 910 return validateObjectLiteralType(lhsType) && !hasMethods(lhsType) && 911 validateFields(lhsType, rhsExpr); 912 } 913 return false; 914} 915 916function isEnumAssignment(lhsType: Type, rhsType: Type) { 917 const isNumberEnum = isPrimitiveEnumType(rhsType, TypeFlags.NumberLiteral) || 918 isPrimitiveEnumMemberType(rhsType, TypeFlags.NumberLiteral); 919 const isStringEnum = isPrimitiveEnumType(rhsType, TypeFlags.StringLiteral) || 920 isPrimitiveEnumMemberType(rhsType, TypeFlags.StringLiteral); 921 return (isNumberType(lhsType) && isNumberEnum) || (isStringType(lhsType) && isStringEnum); 922} 923 924function areCompatibleFunctionals(lhsType: Type, rhsType: Type) { 925 return (isStdFunctionType(lhsType) || isFunctionalType(lhsType)) && 926 (isStdFunctionType(rhsType) || isFunctionalType(rhsType)); 927} 928 929function isFunctionalType(type: Type): boolean { 930 const callSigns = type.getCallSignatures(); 931 return callSigns && callSigns.length > 0; 932} 933 934function isStdFunctionType(type: Type) { 935 const sym = type.getSymbol(); 936 return sym && sym.getName() === "Function" && isGlobalSymbol(sym); 937} 938 939function getTargetType(type: Type): Type { 940 return (type.getFlags() & TypeFlags.Object) && 941 (type as ObjectType).objectFlags & ObjectFlags.Reference ? (type as TypeReference).target : type; 942} 943 944export function isLiteralType(type: Type): boolean { 945 return type.isLiteral() || (type.flags & TypeFlags.BooleanLiteral) !== 0; 946} 947 948export function validateFields(type: Type, objectLiteral: ObjectLiteralExpression): boolean { 949 for (const prop of objectLiteral.properties) { 950 if (isPropertyAssignment(prop)) { 951 const propAssignment = prop; 952 const propName = propAssignment.name.getText(); 953 const propSym = findProperty(type, propName); 954 if (!propSym || !propSym.declarations?.length) return false; 955 956 const propType = typeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]); 957 if (!isExpressionAssignableToType(propType, propAssignment.initializer)) { 958 return false; 959 } 960 } 961 }; 962 963 return true; 964} 965 966function isSupportedTypeNodeKind(kind: SyntaxKind): boolean { 967 return kind !== SyntaxKind.AnyKeyword && kind !== SyntaxKind.UnknownKeyword && 968 kind !== SyntaxKind.SymbolKeyword && kind !== SyntaxKind.IndexedAccessType && 969 kind !== SyntaxKind.ConditionalType && kind !== SyntaxKind.MappedType && 970 kind !== SyntaxKind.InferType; 971 972} 973 974export function isSupportedType(typeNode: TypeNode): boolean { 975 if (isParenthesizedTypeNode(typeNode)) return isSupportedType(typeNode.type); 976 977 if (isArrayTypeNode(typeNode)) return isSupportedType(typeNode.elementType); 978 979 if (isTypeReferenceNode(typeNode) && typeNode.typeArguments) { 980 for (const typeArg of typeNode.typeArguments) { 981 if (!isSupportedType(typeArg)) { return false; } 982 } 983 return true; 984 } 985 986 if (isUnionTypeNode(typeNode)) { 987 for (const unionTypeElem of typeNode.types) { 988 if (!isSupportedType(unionTypeElem)) { return false; } 989 } 990 return true; 991 } 992 993 if (isTupleTypeNode(typeNode)) { 994 for (const elem of typeNode.elements) { 995 if (isTypeNode(elem) && !isSupportedType(elem)) return false; 996 if (isNamedTupleMember(elem) && !isSupportedType(elem.type)) return false; 997 } 998 return true; 999 } 1000 1001 return !isTypeLiteralNode(typeNode) && !isTypeQueryNode(typeNode) && 1002 !isIntersectionTypeNode(typeNode) && isSupportedTypeNodeKind(typeNode.kind); 1003} 1004 1005export function isStruct(symbol: Symbol) { 1006 if (!symbol.declarations) { 1007 return false; 1008 } 1009 for (const decl of symbol.declarations) { 1010 if (isStructDeclaration(decl)) { 1011 return true; 1012 } 1013 } 1014 return false; 1015} 1016 1017function validateRecordObjectKeys(objectLiteral: ObjectLiteralExpression): boolean { 1018 for (const prop of objectLiteral.properties) { 1019 if (!prop.name || (!isStringLiteral(prop.name) && !isNumericLiteral(prop.name))) { return false; } 1020 } 1021 return true; 1022} 1023 1024export function getDecorators(node: Node): readonly Decorator[] | undefined { 1025 if (node.decorators) { 1026 return filter(node.decorators, isDecorator); 1027 } 1028} 1029 1030export enum CheckType { 1031 Array, 1032 String = "String", 1033 Set = "Set", 1034 Map = "Map", 1035 Error = "Error", 1036}; 1037 1038export const ES_OBJECT = "ESObject"; 1039 1040export const LIMITED_STD_GLOBAL_FUNC = [ 1041 "eval", "isFinite", "isNaN", "parseFloat", "parseInt", /*"encodeURI", "encodeURIComponent", "Encode", "decodeURI", 1042 "decodeURIComponent", "Decode", "escape", "unescape", "ParseHexOctet"*/ 1043]; 1044export const LIMITED_STD_GLOBAL_VAR = ["Infinity", "NaN"]; 1045export const LIMITED_STD_OBJECT_API = [ 1046 "__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "assign", "create", 1047 "defineProperties", "defineProperty", "freeze", "fromEntries", "getOwnPropertyDescriptor", 1048 "getOwnPropertyDescriptors", "getOwnPropertySymbols", "getPrototypeOf", "hasOwnProperty", "is", 1049 "isExtensible", "isFrozen", "isPrototypeOf", "isSealed", "preventExtensions", "propertyIsEnumerable", 1050 "seal", "setPrototypeOf" 1051]; 1052export const LIMITED_STD_REFLECT_API = [ 1053 "apply", "construct", "defineProperty", "deleteProperty", "getOwnPropertyDescriptor", "getPrototypeOf", 1054 "isExtensible", "preventExtensions", "setPrototypeOf" 1055]; 1056export const LIMITED_STD_PROXYHANDLER_API = [ 1057 "apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", 1058 "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf" 1059]; 1060export const LIMITED_STD_ARRAYBUFFER_API = ["isView"]; 1061 1062export const ARKUI_DECORATORS = [ 1063 "AnimatableExtend", 1064 "Builder", 1065 "BuilderParam", 1066 "Component", 1067 "Concurrent", 1068 "Consume", 1069 "CustomDialog", 1070 "Entry", 1071 "Extend", 1072 "Link", 1073 "LocalStorageLink", 1074 "LocalStorageProp", 1075 "ObjectLink", 1076 "Observed", 1077 "Preview", 1078 "Prop", 1079 "Provide", 1080 "Reusable", 1081 "State", 1082 "StorageLink", 1083 "StorageProp", 1084 "Styles", 1085 "Watch", 1086]; 1087 1088export const FUNCTION_HAS_NO_RETURN_ERROR_CODE = 2366; 1089export const NON_RETURN_FUNCTION_DECORATORS = ["AnimatableExtend", "Builder", "Extend", "Styles" ]; 1090 1091export const STANDARD_LIBRARIES = [ 1092 "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts", "lib.webworker.importscripd.ts", 1093 "lib.webworker.iterable.d.ts", "lib.scripthost.d.ts", "lib.decorators.d.ts", "lib.decorators.legacy.d.ts", 1094 "lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.generator.d.ts", 1095 "lib.es2015.iterable.d.ts", "lib.es2015.promise.d.ts", "lib.es2015.proxy.d.ts", "lib.es2015.reflect.d.ts", 1096 "lib.es2015.symbol.d.ts", "lib.es2015.symbol.wellknown.d.ts", "lib.es2016.array.include.d.ts", 1097 "lib.es2017.object.d.ts", "lib.es2017.sharedmemory.d.ts", "lib.es2017.string.d.ts", "lib.es2017.intl.d.ts", 1098 "lib.es2017.typedarrays.d.ts", "lib.es2018.asyncgenerator.d.ts", "lib.es2018.asynciterable.d.ts", 1099 "lib.es2018.intl.d.ts", "lib.es2018.promise.d.ts", "lib.es2018.regexp.d.ts", "lib.es2019.array.d.ts", 1100 "lib.es2019.object.d.ts", "lib.es2019.string.d.ts", "lib.es2019.symbol.d.ts", "lib.es2019.intl.d.ts", 1101 "lib.es2020.bigint.d.ts", "lib.es2020.date.d.ts", "lib.es2020.promise.d.ts", "lib.es2020.sharedmemory.d.ts", 1102 "lib.es2020.string.d.ts", "lib.es2020.symbol.wellknown.d.ts", "lib.es2020.intl.d.ts", "lib.es2020.number.d.ts", 1103 "lib.es2021.promise.d.ts", "lib.es2021.string.d.ts", "lib.es2021.weakref.d.ts", "lib.es2021.intl.d.ts", 1104 "lib.es2022.array.d.ts", "lib.es2022.error.d.ts", "lib.es2022.intl.d.ts", "lib.es2022.object.d.ts", 1105 "lib.es2022.sharedmemory.d.ts", "lib.es2022.string.d.ts", "lib.es2022.regexp.d.ts", "lib.es2023.array.d.ts", 1106]; 1107 1108export const TYPED_ARRAYS = [ 1109 "Int8Array", 1110 "Uint8Array", 1111 "Uint8ClampedArray", 1112 "Int16Array", 1113 "Uint16Array", 1114 "Int32Array", 1115 "Uint32Array", 1116 "Float32Array", 1117 "Float64Array", 1118 "BigInt64Array", 1119 "BigUint64Array", 1120 ]; 1121 1122export function getParentSymbolName(symbol: Symbol): string | undefined { 1123 const name = typeChecker.getFullyQualifiedName(symbol); 1124 const dotPosition = name.lastIndexOf("."); 1125 return (dotPosition === -1) ? undefined : name.substring(0, dotPosition); 1126} 1127 1128export function isGlobalSymbol(symbol: Symbol): boolean { 1129 const parentName = getParentSymbolName(symbol); 1130 return !parentName || parentName === "global"; 1131} 1132export function isStdObjectAPI(symbol: Symbol): boolean { 1133 const parentName = getParentSymbolName(symbol); 1134 return !!parentName && (parentName === "Object" || parentName === "ObjectConstructor"); 1135} 1136export function isStdReflectAPI(symbol: Symbol): boolean { 1137 const parentName = getParentSymbolName(symbol); 1138 return !!parentName && (parentName === "Reflect"); 1139} 1140export function isStdProxyHandlerAPI(symbol: Symbol): boolean { 1141 const parentName = getParentSymbolName(symbol); 1142 return !!parentName && (parentName === "ProxyHandler"); 1143} 1144export function isStdArrayAPI(symbol: Symbol): boolean { 1145 const parentName = getParentSymbolName(symbol); 1146 return !!parentName && (parentName === "Array" || parentName === "ArrayConstructor"); 1147} 1148export function isStdArrayBufferAPI(symbol: Symbol): boolean { 1149 const parentName = getParentSymbolName(symbol); 1150 return !!parentName && (parentName === "ArrayBuffer" || parentName === "ArrayBufferConstructor"); 1151} 1152 1153export function isSymbolAPI(symbol: Symbol): boolean { 1154 const parentName = getParentSymbolName(symbol); 1155 let name = parentName ? parentName : symbol.escapedName; 1156 return name === 'Symbol' || name === "SymbolConstructor"; 1157} 1158 1159export function isDefaultImport(importSpec: ImportSpecifier): boolean { 1160 return importSpec?.propertyName?.text === "default"; 1161} 1162export function hasAccessModifier(decl: Declaration): boolean { 1163 const modifiers = decl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 1164 return ( 1165 !!modifiers && 1166 (hasModifier(modifiers, SyntaxKind.PublicKeyword) || 1167 hasModifier(modifiers, SyntaxKind.ProtectedKeyword) || 1168 hasModifier(modifiers, SyntaxKind.PrivateKeyword)) 1169 ); 1170} 1171 1172export function getModifier(modifiers: readonly Modifier[] | undefined, modifierKind: SyntaxKind): Modifier | undefined { 1173 if (!modifiers) return undefined; 1174 return modifiers.find(x => x.kind === modifierKind); 1175} 1176 1177export function getAccessModifier(modifiers: readonly Modifier[] | undefined): Modifier | undefined { 1178 return getModifier(modifiers, SyntaxKind.PublicKeyword) ?? 1179 getModifier(modifiers, SyntaxKind.ProtectedKeyword) ?? 1180 getModifier(modifiers, SyntaxKind.PrivateKeyword); 1181} 1182 1183export function isStdRecordType(type: Type): boolean { 1184 // In TypeScript, 'Record<K, T>' is defined as type alias to a mapped type. 1185 // Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' 1186 // in this case will resolve to origin 'Record' symbol. 1187 if (type.aliasSymbol) { 1188 const target = (type as TypeReference).target; 1189 if (target) { 1190 const sym = target.aliasSymbol; 1191 return !!sym && sym.getName() === "Record" && isGlobalSymbol(sym); 1192 } 1193 } 1194 1195 return false; 1196} 1197 1198export function isStdPartialType(type: Type): boolean { 1199 const sym = type.aliasSymbol; 1200 return !!sym && sym.getName() === "Partial" && isGlobalSymbol(sym); 1201} 1202 1203export function isStdRequiredType(type: Type): boolean { 1204 const sym = type.aliasSymbol; 1205 return !!sym && sym.getName() === "Required" && isGlobalSymbol(sym); 1206} 1207 1208export function isStdReadonlyType(type: Type): boolean { 1209 const sym = type.aliasSymbol; 1210 return !!sym && sym.getName() === "Readonly" && isGlobalSymbol(sym); 1211} 1212 1213export function isLibraryType(type: Type): boolean { 1214 const nonNullableType = type.getNonNullableType(); 1215 if (nonNullableType.isUnion()) { 1216 for (const componentType of nonNullableType.types) { 1217 if (!isLibraryType(componentType)) { 1218 return false; 1219 } 1220 } 1221 return true; 1222 } 1223 return isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol()); 1224} 1225 1226export function hasLibraryType(node: Node): boolean { 1227 return isLibraryType(typeChecker.getTypeAtLocation(node)); 1228} 1229 1230export function isLibrarySymbol(sym: Symbol | undefined) { 1231 if (sym && sym.declarations && sym.declarations.length > 0) { 1232 const srcFile = sym.declarations[0].getSourceFile(); 1233 if (!srcFile) { 1234 return false; 1235 } 1236 const fileName = srcFile.fileName; 1237 1238 // Symbols from both *.ts and *.d.ts files should obey interop rules. 1239 // We disable such behavior for *.ts files in the test mode due to lack of 'ets' 1240 // extension support. 1241 const ext = getAnyExtensionFromPath(fileName); 1242 const isThirdPartyCode = 1243 ARKTS_IGNORE_DIRS.some(ignore => pathContainsDirectory(normalizePath(fileName), ignore)) || 1244 ARKTS_IGNORE_FILES.some(ignore => getBaseFileName(fileName) === ignore); 1245 const isEts = (ext === '.ets'); 1246 const isTs = (ext === '.ts' && !srcFile.isDeclarationFile); 1247 const isStatic = (isEts || (isTs && testMode)) && !isThirdPartyCode; 1248 // We still need to confirm support for certain API from the 1249 // TypeScript standard library in ArkTS. Thus, for now do not 1250 // count standard library modules. 1251 return !isStatic && 1252 !STANDARD_LIBRARIES.includes(getBaseFileName(srcFile.fileName).toLowerCase()); 1253 } 1254 1255 return false; 1256} 1257 1258export function pathContainsDirectory(targetPath: string, dir: string): boolean { 1259 for (const subdir of getPathComponents(targetPath)) { 1260 if (subdir === dir) { 1261 return true; 1262 } 1263 } 1264 return false; 1265} 1266 1267export function getScriptKind(srcFile: SourceFile): ScriptKind { 1268 const fileName = srcFile.fileName; 1269 const ext = getAnyExtensionFromPath(fileName); 1270 switch (ext.toLowerCase()) { 1271 case Extension.Js: 1272 return ScriptKind.JS; 1273 case Extension.Jsx: 1274 return ScriptKind.JSX; 1275 case Extension.Ts: 1276 return ScriptKind.TS; 1277 case Extension.Tsx: 1278 return ScriptKind.TSX; 1279 case Extension.Json: 1280 return ScriptKind.JSON; 1281 default: 1282 return ScriptKind.Unknown; 1283 } 1284} 1285 1286export function isStdLibraryType(type: Type): boolean { 1287 return isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol()); 1288} 1289 1290export function isStdLibrarySymbol(sym: Symbol | undefined) { 1291 if (sym && sym.declarations && sym.declarations.length > 0) { 1292 const srcFile = sym.declarations[0].getSourceFile(); 1293 return srcFile && 1294 STANDARD_LIBRARIES.includes(getBaseFileName(srcFile.fileName).toLowerCase()); 1295 } 1296 1297 return false; 1298} 1299 1300export function isIntrinsicObjectType(type: Type): boolean { 1301 return !!(type.flags & TypeFlags.NonPrimitive); 1302} 1303 1304export function isDynamicType(type: Type | undefined): boolean | undefined { 1305 if (type === undefined) { 1306 return false; 1307 } 1308 1309 // Return 'true' if it is an object of library type initialization, otherwise 1310 // return 'false' if it is not an object of standard library type one. 1311 // In the case of standard library type we need to determine context. 1312 1313 // Check the non-nullable version of type to eliminate 'undefined' type 1314 // from the union type elements. 1315 type = type.getNonNullableType(); 1316 1317 1318 if (type.isUnion()) { 1319 for (const compType of type.types) { 1320 const isDynamic = isDynamicType(compType); 1321 if (isDynamic || isDynamic === undefined) { 1322 return isDynamic; 1323 } 1324 } 1325 return false; 1326 } 1327 1328 if (isLibraryType(type)) { 1329 return true; 1330 } 1331 1332 if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !isAnyType(type)) { 1333 return false; 1334 } 1335 1336 return undefined; 1337} 1338 1339export function isDynamicLiteralInitializer(expr: Expression): boolean { 1340 if (!isObjectLiteralExpression(expr) && !isArrayLiteralExpression(expr)) { 1341 return false; 1342 } 1343 1344 // Handle nested literals: 1345 // { f: { ... } } 1346 let curNode: Node = expr; 1347 while (isObjectLiteralExpression(curNode) || isArrayLiteralExpression(curNode)) { 1348 const exprType = typeChecker.getContextualType(curNode); 1349 if (exprType !== undefined) { 1350 const res = isDynamicType(exprType); 1351 if (res !== undefined) { 1352 return res; 1353 } 1354 } 1355 1356 curNode = curNode.parent; 1357 if (isPropertyAssignment(curNode)) { 1358 curNode = curNode.parent; 1359 } 1360 } 1361 1362 // Handle calls with literals: 1363 // foo({ ... }) 1364 if (isCallExpression(curNode)) { 1365 const callExpr = curNode; 1366 const type = typeChecker.getTypeAtLocation(callExpr.expression); 1367 1368 // this check is a hack to fix #13474, only for tac 4.2 1369 if (isAnyType(type)) return true; 1370 1371 let sym: Symbol | undefined = type.symbol; 1372 if(isLibrarySymbol(sym)) { 1373 return true; 1374 } 1375 1376 // #13483: 1377 // x.foo({ ... }), where 'x' is a variable exported from some library: 1378 if (isPropertyAccessExpression(callExpr.expression)) { 1379 sym = typeChecker.getSymbolAtLocation(callExpr.expression.expression); 1380 if (sym && sym.getFlags() & SymbolFlags.Alias) { 1381 sym = typeChecker.getAliasedSymbol(sym); 1382 if (isLibrarySymbol(sym)) { 1383 return true; 1384 } 1385 } 1386 } 1387 } 1388 1389 // Handle property assignments with literals: 1390 // obj.f = { ... } 1391 if (isBinaryExpression(curNode)) { 1392 const binExpr = curNode; 1393 if (isPropertyAccessExpression(binExpr.left)) { 1394 const propAccessExpr = binExpr.left; 1395 const type = typeChecker.getTypeAtLocation(propAccessExpr.expression); 1396 return isLibrarySymbol(type.symbol); 1397 } 1398 } 1399 1400 return false; 1401} 1402 1403export function isEsObjectType(typeNode: TypeNode): boolean { 1404 return isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName) && 1405 typeNode.typeName.text === ES_OBJECT; 1406} 1407 1408export function isEsObjectAllowed(typeRef: TypeReferenceNode): boolean { 1409 let node = typeRef.parent; 1410 1411 if (!isVarDeclaration(node)) { 1412 return false; 1413 } 1414 1415 while (node) { 1416 if (isBlock(node)) { 1417 return true; 1418 } 1419 node = node.parent; 1420 } 1421 return false; 1422} 1423 1424export function getVariableDeclarationTypeNode(node: Node): TypeNode | undefined { 1425 let sym = trueSymbolAtLocation(node); 1426 if (sym === undefined) { 1427 return undefined; 1428 } 1429 return getSymbolDeclarationTypeNode(sym); 1430 } 1431 1432export function getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined { 1433 const decl = getDeclaration(sym); 1434 if (!!decl && isVariableDeclaration(decl)) { 1435 return decl.type; 1436 } 1437 return undefined; 1438} 1439 1440export function hasEsObjectType(node: Node): boolean { 1441 const typeNode = getVariableDeclarationTypeNode(node); 1442 return typeNode !== undefined && isEsObjectType(typeNode); 1443} 1444 1445export function symbolHasEsObjectType(sym: ts.Symbol): boolean { 1446 const typeNode = getSymbolDeclarationTypeNode(sym); 1447 return typeNode !== undefined && isEsObjectType(typeNode); 1448} 1449 1450export function isEsObjectSymbol(sym: Symbol): boolean { 1451 const decl = getDeclaration(sym); 1452 return !!decl && isTypeAliasDeclaration(decl) && decl.name.escapedText === ES_OBJECT && 1453 decl.type.kind === SyntaxKind.AnyKeyword; 1454} 1455 1456export function isAnonymousType(type: Type): boolean { 1457 if (type.isUnionOrIntersection()) { 1458 for (const compType of type.types) { 1459 if (isAnonymousType(compType)) { 1460 return true; 1461 } 1462 } 1463 return false; 1464 } 1465 1466 return (type.flags & TypeFlags.Object) !== 0 && 1467 ((type as ObjectType).objectFlags & ObjectFlags.Anonymous) !== 0; 1468} 1469 1470export function getSymbolOfCallExpression(callExpr: CallExpression): Symbol | undefined { 1471 const signature = typeChecker.getResolvedSignature(callExpr); 1472 const signDecl = signature?.getDeclaration(); 1473 if (signDecl && signDecl.name) { 1474 return typeChecker.getSymbolAtLocation(signDecl.name); 1475 } 1476 return undefined; 1477} 1478 1479export function typeIsRecursive(topType: Type, type: Type | undefined = undefined): boolean { 1480 if (type === undefined) { 1481 type = topType; 1482 } 1483 else if (type === topType) { 1484 return true; 1485 } 1486 else if (type.aliasSymbol) { 1487 return false; 1488 } 1489 1490 if (type.isUnion()) { 1491 for (const unionElem of type.types) { 1492 if (typeIsRecursive(topType, unionElem)) { 1493 return true; 1494 } 1495 } 1496 } 1497 if (type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.Reference) { 1498 const typeArgs = typeChecker.getTypeArguments(type as TypeReference); 1499 if (typeArgs) { 1500 for (const typeArg of typeArgs) { 1501 if (typeIsRecursive(topType, typeArg)) { 1502 return true; 1503 } 1504 } 1505 } 1506 } 1507 return false; 1508} 1509 1510 1511} 1512} 1513