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 // we pretend to be non strict mode to avoid incompatibilities with IDE/RT linter, 870 // where execution environments differ. in IDE this error will be reported anyways by 871 // StrictModeError 872 const isRhsUndefined: boolean = !!(rhsType.flags & ts.TypeFlags.Undefined); 873 const isRhsNull: boolean = !!(rhsType.flags & ts.TypeFlags.Null); 874 if (isRhsUndefined || isRhsNull) { 875 return true; 876 } 877 878 // Allow initializing with anything when the type 879 // originates from the library. 880 if (isAnyType(lhsType) || isLibraryType(lhsType)) { 881 return true; 882 } 883 884 // If type is a literal type, compare its base type. 885 lhsType = typeChecker.getBaseTypeOfLiteralType(lhsType); 886 rhsType = typeChecker.getBaseTypeOfLiteralType(rhsType); 887 888 // issue 13114: 889 // Const enum values are convertible to string/number type. 890 // Note: This check should appear before calling TypeChecker.getBaseTypeOfLiteralType() 891 // to ensure that lhsType has its original form, as it can be a literal type with 892 // specific number or string value, which shouldn't pass this check. 893 if (isEnumAssignment(lhsType, rhsType)) { 894 return true; 895 } 896 897 // issue 13033: 898 // If both types are functional, they are considered compatible. 899 if (areCompatibleFunctionals(lhsType, rhsType)) { 900 return true; 901 } 902 903 return lhsType === rhsType || relatedByInheritanceOrIdentical(rhsType, getTargetType(lhsType)); 904} 905 906function isDynamicObjectAssignedToStdType(lhsType: Type, rhsExpr: Expression): boolean { 907 if (isStdLibraryType(lhsType) || isPrimitiveType(lhsType)) { 908 const rhsSym = isCallExpression(rhsExpr) 909 ? getSymbolOfCallExpression(rhsExpr) 910 : typeChecker.getSymbolAtLocation(rhsExpr); 911 912 if (rhsSym && isLibrarySymbol(rhsSym)) return true; 913 } 914 return false; 915} 916 917function isObjectLiteralAssignable(lhsType: Type, rhsExpr: Expression): boolean { 918 if (isObjectLiteralExpression(rhsExpr)) { 919 return validateObjectLiteralType(lhsType) && !hasMethods(lhsType) && 920 validateFields(lhsType, rhsExpr); 921 } 922 return false; 923} 924 925function isEnumAssignment(lhsType: Type, rhsType: Type) { 926 const isNumberEnum = isPrimitiveEnumType(rhsType, TypeFlags.NumberLiteral) || 927 isPrimitiveEnumMemberType(rhsType, TypeFlags.NumberLiteral); 928 const isStringEnum = isPrimitiveEnumType(rhsType, TypeFlags.StringLiteral) || 929 isPrimitiveEnumMemberType(rhsType, TypeFlags.StringLiteral); 930 return (isNumberType(lhsType) && isNumberEnum) || (isStringType(lhsType) && isStringEnum); 931} 932 933function areCompatibleFunctionals(lhsType: Type, rhsType: Type) { 934 return (isStdFunctionType(lhsType) || isFunctionalType(lhsType)) && 935 (isStdFunctionType(rhsType) || isFunctionalType(rhsType)); 936} 937 938function isFunctionalType(type: Type): boolean { 939 const callSigns = type.getCallSignatures(); 940 return callSigns && callSigns.length > 0; 941} 942 943function isStdFunctionType(type: Type) { 944 const sym = type.getSymbol(); 945 return sym && sym.getName() === "Function" && isGlobalSymbol(sym); 946} 947 948function getTargetType(type: Type): Type { 949 return (type.getFlags() & TypeFlags.Object) && 950 (type as ObjectType).objectFlags & ObjectFlags.Reference ? (type as TypeReference).target : type; 951} 952 953export function isLiteralType(type: Type): boolean { 954 return type.isLiteral() || (type.flags & TypeFlags.BooleanLiteral) !== 0; 955} 956 957export function validateFields(type: Type, objectLiteral: ObjectLiteralExpression): boolean { 958 for (const prop of objectLiteral.properties) { 959 if (isPropertyAssignment(prop)) { 960 const propAssignment = prop; 961 const propName = propAssignment.name.getText(); 962 const propSym = findProperty(type, propName); 963 if (!propSym || !propSym.declarations?.length) return false; 964 965 const propType = typeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]); 966 if (!isExpressionAssignableToType(propType, propAssignment.initializer)) { 967 return false; 968 } 969 } 970 }; 971 972 return true; 973} 974 975function isSupportedTypeNodeKind(kind: SyntaxKind): boolean { 976 return kind !== SyntaxKind.AnyKeyword && kind !== SyntaxKind.UnknownKeyword && 977 kind !== SyntaxKind.SymbolKeyword && kind !== SyntaxKind.IndexedAccessType && 978 kind !== SyntaxKind.ConditionalType && kind !== SyntaxKind.MappedType && 979 kind !== SyntaxKind.InferType; 980 981} 982 983export function isSupportedType(typeNode: TypeNode): boolean { 984 if (isParenthesizedTypeNode(typeNode)) return isSupportedType(typeNode.type); 985 986 if (isArrayTypeNode(typeNode)) return isSupportedType(typeNode.elementType); 987 988 if (isTypeReferenceNode(typeNode) && typeNode.typeArguments) { 989 for (const typeArg of typeNode.typeArguments) { 990 if (!isSupportedType(typeArg)) { return false; } 991 } 992 return true; 993 } 994 995 if (isUnionTypeNode(typeNode)) { 996 for (const unionTypeElem of typeNode.types) { 997 if (!isSupportedType(unionTypeElem)) { return false; } 998 } 999 return true; 1000 } 1001 1002 if (isTupleTypeNode(typeNode)) { 1003 for (const elem of typeNode.elements) { 1004 if (isTypeNode(elem) && !isSupportedType(elem)) return false; 1005 if (isNamedTupleMember(elem) && !isSupportedType(elem.type)) return false; 1006 } 1007 return true; 1008 } 1009 1010 return !isTypeLiteralNode(typeNode) && !isTypeQueryNode(typeNode) && 1011 !isIntersectionTypeNode(typeNode) && isSupportedTypeNodeKind(typeNode.kind); 1012} 1013 1014export function isStruct(symbol: Symbol) { 1015 if (!symbol.declarations) { 1016 return false; 1017 } 1018 for (const decl of symbol.declarations) { 1019 if (isStructDeclaration(decl)) { 1020 return true; 1021 } 1022 } 1023 return false; 1024} 1025 1026function validateRecordObjectKeys(objectLiteral: ObjectLiteralExpression): boolean { 1027 for (const prop of objectLiteral.properties) { 1028 if (!prop.name || (!isStringLiteral(prop.name) && !isNumericLiteral(prop.name))) { return false; } 1029 } 1030 return true; 1031} 1032 1033export function getDecorators(node: Node): readonly Decorator[] | undefined { 1034 if (node.decorators) { 1035 return filter(node.decorators, isDecorator); 1036 } 1037} 1038 1039export enum CheckType { 1040 Array, 1041 String = "String", 1042 Set = "Set", 1043 Map = "Map", 1044 Error = "Error", 1045}; 1046 1047export const ES_OBJECT = "ESObject"; 1048 1049export const LIMITED_STD_GLOBAL_FUNC = [ 1050 "eval" 1051]; 1052export const LIMITED_STD_OBJECT_API = [ 1053 "__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "assign", "create", 1054 "defineProperties", "defineProperty", "freeze", "fromEntries", "getOwnPropertyDescriptor", 1055 "getOwnPropertyDescriptors", "getOwnPropertySymbols", "getPrototypeOf", "hasOwnProperty", "is", 1056 "isExtensible", "isFrozen", "isPrototypeOf", "isSealed", "preventExtensions", "propertyIsEnumerable", 1057 "seal", "setPrototypeOf" 1058]; 1059export const LIMITED_STD_REFLECT_API = [ 1060 "apply", "construct", "defineProperty", "deleteProperty", "getOwnPropertyDescriptor", "getPrototypeOf", 1061 "isExtensible", "preventExtensions", "setPrototypeOf" 1062]; 1063export const LIMITED_STD_PROXYHANDLER_API = [ 1064 "apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", 1065 "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf" 1066]; 1067export const ARKUI_DECORATORS = [ 1068 "AnimatableExtend", 1069 "Builder", 1070 "BuilderParam", 1071 "Component", 1072 "Concurrent", 1073 "Consume", 1074 "CustomDialog", 1075 "Entry", 1076 "Extend", 1077 "Link", 1078 "LocalStorageLink", 1079 "LocalStorageProp", 1080 "ObjectLink", 1081 "Observed", 1082 "Preview", 1083 "Prop", 1084 "Provide", 1085 "Reusable", 1086 "State", 1087 "StorageLink", 1088 "StorageProp", 1089 "Styles", 1090 "Watch", 1091]; 1092 1093export const FUNCTION_HAS_NO_RETURN_ERROR_CODE = 2366; 1094export const NON_RETURN_FUNCTION_DECORATORS = ["AnimatableExtend", "Builder", "Extend", "Styles" ]; 1095 1096export const STANDARD_LIBRARIES = [ 1097 "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts", "lib.webworker.importscripd.ts", 1098 "lib.webworker.iterable.d.ts", "lib.scripthost.d.ts", "lib.decorators.d.ts", "lib.decorators.legacy.d.ts", 1099 "lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.generator.d.ts", 1100 "lib.es2015.iterable.d.ts", "lib.es2015.promise.d.ts", "lib.es2015.proxy.d.ts", "lib.es2015.reflect.d.ts", 1101 "lib.es2015.symbol.d.ts", "lib.es2015.symbol.wellknown.d.ts", "lib.es2016.array.include.d.ts", 1102 "lib.es2017.object.d.ts", "lib.es2017.sharedmemory.d.ts", "lib.es2017.string.d.ts", "lib.es2017.intl.d.ts", 1103 "lib.es2017.typedarrays.d.ts", "lib.es2018.asyncgenerator.d.ts", "lib.es2018.asynciterable.d.ts", 1104 "lib.es2018.intl.d.ts", "lib.es2018.promise.d.ts", "lib.es2018.regexp.d.ts", "lib.es2019.array.d.ts", 1105 "lib.es2019.object.d.ts", "lib.es2019.string.d.ts", "lib.es2019.symbol.d.ts", "lib.es2019.intl.d.ts", 1106 "lib.es2020.bigint.d.ts", "lib.es2020.date.d.ts", "lib.es2020.promise.d.ts", "lib.es2020.sharedmemory.d.ts", 1107 "lib.es2020.string.d.ts", "lib.es2020.symbol.wellknown.d.ts", "lib.es2020.intl.d.ts", "lib.es2020.number.d.ts", 1108 "lib.es2021.promise.d.ts", "lib.es2021.string.d.ts", "lib.es2021.weakref.d.ts", "lib.es2021.intl.d.ts", 1109 "lib.es2022.array.d.ts", "lib.es2022.error.d.ts", "lib.es2022.intl.d.ts", "lib.es2022.object.d.ts", 1110 "lib.es2022.sharedmemory.d.ts", "lib.es2022.string.d.ts", "lib.es2022.regexp.d.ts", "lib.es2023.array.d.ts", 1111]; 1112 1113export const TYPED_ARRAYS = [ 1114 "Int8Array", 1115 "Uint8Array", 1116 "Uint8ClampedArray", 1117 "Int16Array", 1118 "Uint16Array", 1119 "Int32Array", 1120 "Uint32Array", 1121 "Float32Array", 1122 "Float64Array", 1123 "BigInt64Array", 1124 "BigUint64Array", 1125 ]; 1126 1127export function getParentSymbolName(symbol: Symbol): string | undefined { 1128 const name = typeChecker.getFullyQualifiedName(symbol); 1129 const dotPosition = name.lastIndexOf("."); 1130 return (dotPosition === -1) ? undefined : name.substring(0, dotPosition); 1131} 1132 1133export function isGlobalSymbol(symbol: Symbol): boolean { 1134 const parentName = getParentSymbolName(symbol); 1135 return !parentName || parentName === "global"; 1136} 1137 1138export function isSymbolAPI(symbol: Symbol): boolean { 1139 const parentName = getParentSymbolName(symbol); 1140 let name = parentName ? parentName : symbol.escapedName; 1141 return name === 'Symbol' || name === "SymbolConstructor"; 1142} 1143 1144export function isStdSymbol(symbol: ts.Symbol): boolean { 1145 const name = TypeScriptLinter.tsTypeChecker.getFullyQualifiedName(symbol) 1146 return name === 'Symbol' && isGlobalSymbol(symbol); 1147} 1148 1149export function isSymbolIterator(symbol: ts.Symbol): boolean { 1150 const name = symbol.name; 1151 const parName = getParentSymbolName(symbol); 1152 return (parName === 'Symbol' || parName === 'SymbolConstructor') && name === 'iterator' 1153} 1154 1155export function isDefaultImport(importSpec: ImportSpecifier): boolean { 1156 return importSpec?.propertyName?.text === "default"; 1157} 1158 1159export function hasAccessModifier(decl: Declaration): boolean { 1160 const modifiers = decl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 1161 return ( 1162 !!modifiers && 1163 (hasModifier(modifiers, SyntaxKind.PublicKeyword) || 1164 hasModifier(modifiers, SyntaxKind.ProtectedKeyword) || 1165 hasModifier(modifiers, SyntaxKind.PrivateKeyword)) 1166 ); 1167} 1168 1169export function getModifier(modifiers: readonly Modifier[] | undefined, modifierKind: SyntaxKind): Modifier | undefined { 1170 if (!modifiers) return undefined; 1171 return modifiers.find(x => x.kind === modifierKind); 1172} 1173 1174export function getAccessModifier(modifiers: readonly Modifier[] | undefined): Modifier | undefined { 1175 return getModifier(modifiers, SyntaxKind.PublicKeyword) ?? 1176 getModifier(modifiers, SyntaxKind.ProtectedKeyword) ?? 1177 getModifier(modifiers, SyntaxKind.PrivateKeyword); 1178} 1179 1180export function isStdRecordType(type: Type): boolean { 1181 // In TypeScript, 'Record<K, T>' is defined as type alias to a mapped type. 1182 // Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' 1183 // in this case will resolve to origin 'Record' symbol. 1184 if (type.aliasSymbol) { 1185 const target = (type as TypeReference).target; 1186 if (target) { 1187 const sym = target.aliasSymbol; 1188 return !!sym && sym.getName() === "Record" && isGlobalSymbol(sym); 1189 } 1190 } 1191 1192 return false; 1193} 1194 1195export function isStdPartialType(type: Type): boolean { 1196 const sym = type.aliasSymbol; 1197 return !!sym && sym.getName() === "Partial" && isGlobalSymbol(sym); 1198} 1199 1200export function isStdRequiredType(type: Type): boolean { 1201 const sym = type.aliasSymbol; 1202 return !!sym && sym.getName() === "Required" && isGlobalSymbol(sym); 1203} 1204 1205export function isStdReadonlyType(type: Type): boolean { 1206 const sym = type.aliasSymbol; 1207 return !!sym && sym.getName() === "Readonly" && isGlobalSymbol(sym); 1208} 1209 1210export function isLibraryType(type: Type): boolean { 1211 const nonNullableType = type.getNonNullableType(); 1212 if (nonNullableType.isUnion()) { 1213 for (const componentType of nonNullableType.types) { 1214 if (!isLibraryType(componentType)) { 1215 return false; 1216 } 1217 } 1218 return true; 1219 } 1220 return isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol()); 1221} 1222 1223export function hasLibraryType(node: Node): boolean { 1224 return isLibraryType(typeChecker.getTypeAtLocation(node)); 1225} 1226 1227export function isLibrarySymbol(sym: Symbol | undefined) { 1228 if (sym && sym.declarations && sym.declarations.length > 0) { 1229 const srcFile = sym.declarations[0].getSourceFile(); 1230 if (!srcFile) { 1231 return false; 1232 } 1233 const fileName = srcFile.fileName; 1234 1235 // Symbols from both *.ts and *.d.ts files should obey interop rules. 1236 // We disable such behavior for *.ts files in the test mode due to lack of 'ets' 1237 // extension support. 1238 const ext = getAnyExtensionFromPath(fileName); 1239 const isThirdPartyCode = 1240 ARKTS_IGNORE_DIRS.some(ignore => pathContainsDirectory(normalizePath(fileName), ignore)) || 1241 ARKTS_IGNORE_FILES.some(ignore => getBaseFileName(fileName) === ignore); 1242 const isEts = (ext === '.ets'); 1243 const isTs = (ext === '.ts' && !srcFile.isDeclarationFile); 1244 const isStatic = (isEts || (isTs && testMode)) && !isThirdPartyCode; 1245 // We still need to confirm support for certain API from the 1246 // TypeScript standard library in ArkTS. Thus, for now do not 1247 // count standard library modules. 1248 return !isStatic && 1249 !STANDARD_LIBRARIES.includes(getBaseFileName(srcFile.fileName).toLowerCase()); 1250 } 1251 1252 return false; 1253} 1254 1255export function pathContainsDirectory(targetPath: string, dir: string): boolean { 1256 for (const subdir of getPathComponents(targetPath)) { 1257 if (subdir === dir) { 1258 return true; 1259 } 1260 } 1261 return false; 1262} 1263 1264export function getScriptKind(srcFile: SourceFile): ScriptKind { 1265 const fileName = srcFile.fileName; 1266 const ext = getAnyExtensionFromPath(fileName); 1267 switch (ext.toLowerCase()) { 1268 case Extension.Js: 1269 return ScriptKind.JS; 1270 case Extension.Jsx: 1271 return ScriptKind.JSX; 1272 case Extension.Ts: 1273 return ScriptKind.TS; 1274 case Extension.Tsx: 1275 return ScriptKind.TSX; 1276 case Extension.Json: 1277 return ScriptKind.JSON; 1278 default: 1279 return ScriptKind.Unknown; 1280 } 1281} 1282 1283export function isStdLibraryType(type: Type): boolean { 1284 return isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol()); 1285} 1286 1287export function isStdLibrarySymbol(sym: Symbol | undefined) { 1288 if (sym && sym.declarations && sym.declarations.length > 0) { 1289 const srcFile = sym.declarations[0].getSourceFile(); 1290 return srcFile && 1291 STANDARD_LIBRARIES.includes(getBaseFileName(srcFile.fileName).toLowerCase()); 1292 } 1293 1294 return false; 1295} 1296 1297export function isIntrinsicObjectType(type: Type): boolean { 1298 return !!(type.flags & TypeFlags.NonPrimitive); 1299} 1300 1301export function isDynamicType(type: Type | undefined): boolean | undefined { 1302 if (type === undefined) { 1303 return false; 1304 } 1305 1306 // Return 'true' if it is an object of library type initialization, otherwise 1307 // return 'false' if it is not an object of standard library type one. 1308 // In the case of standard library type we need to determine context. 1309 1310 // Check the non-nullable version of type to eliminate 'undefined' type 1311 // from the union type elements. 1312 type = type.getNonNullableType(); 1313 1314 1315 if (type.isUnion()) { 1316 for (const compType of type.types) { 1317 const isDynamic = isDynamicType(compType); 1318 if (isDynamic || isDynamic === undefined) { 1319 return isDynamic; 1320 } 1321 } 1322 return false; 1323 } 1324 1325 if (isLibraryType(type)) { 1326 return true; 1327 } 1328 1329 if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !isAnyType(type)) { 1330 return false; 1331 } 1332 1333 return undefined; 1334} 1335 1336export function isDynamicLiteralInitializer(expr: Expression): boolean { 1337 if (!isObjectLiteralExpression(expr) && !isArrayLiteralExpression(expr)) { 1338 return false; 1339 } 1340 1341 // Handle nested literals: 1342 // { f: { ... } } 1343 let curNode: Node = expr; 1344 while (isObjectLiteralExpression(curNode) || isArrayLiteralExpression(curNode)) { 1345 const exprType = typeChecker.getContextualType(curNode); 1346 if (exprType !== undefined) { 1347 const res = isDynamicType(exprType); 1348 if (res !== undefined) { 1349 return res; 1350 } 1351 } 1352 1353 curNode = curNode.parent; 1354 if (isPropertyAssignment(curNode)) { 1355 curNode = curNode.parent; 1356 } 1357 } 1358 1359 // Handle calls with literals: 1360 // foo({ ... }) 1361 if (isCallExpression(curNode)) { 1362 const callExpr = curNode; 1363 const type = typeChecker.getTypeAtLocation(callExpr.expression); 1364 1365 // this check is a hack to fix #13474, only for tac 4.2 1366 if (isAnyType(type)) return true; 1367 1368 let sym: Symbol | undefined = type.symbol; 1369 if(isLibrarySymbol(sym)) { 1370 return true; 1371 } 1372 1373 // #13483: 1374 // x.foo({ ... }), where 'x' is a variable exported from some library: 1375 if (isPropertyAccessExpression(callExpr.expression)) { 1376 sym = typeChecker.getSymbolAtLocation(callExpr.expression.expression); 1377 if (sym && sym.getFlags() & SymbolFlags.Alias) { 1378 sym = typeChecker.getAliasedSymbol(sym); 1379 if (isLibrarySymbol(sym)) { 1380 return true; 1381 } 1382 } 1383 } 1384 } 1385 1386 // Handle property assignments with literals: 1387 // obj.f = { ... } 1388 if (isBinaryExpression(curNode)) { 1389 const binExpr = curNode; 1390 if (isPropertyAccessExpression(binExpr.left)) { 1391 const propAccessExpr = binExpr.left; 1392 const type = typeChecker.getTypeAtLocation(propAccessExpr.expression); 1393 return isLibrarySymbol(type.symbol); 1394 } 1395 } 1396 1397 return false; 1398} 1399 1400export function isEsObjectType(typeNode: TypeNode): boolean { 1401 return isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName) && 1402 typeNode.typeName.text === ES_OBJECT; 1403} 1404 1405export function isInsideBlock(node: ts.Node): boolean { 1406 let par = node.parent 1407 while (par) { 1408 if (ts.isBlock(par)) { 1409 return true; 1410 } 1411 par = par.parent; 1412 } 1413 return false; 1414} 1415 1416export function isEsObjectPossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean { 1417 return ts.isVariableDeclaration(typeRef.parent); 1418} 1419 1420export function isValueAssignableToESObject(node: ts.Node): boolean { 1421 if (ts.isArrayLiteralExpression(node) || ts.isObjectLiteralExpression(node)) { 1422 return false; 1423 } 1424 const valueType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(node); 1425 return isUnsupportedType(valueType) || isAnonymousType(valueType) 1426} 1427 1428export function getVariableDeclarationTypeNode(node: Node): TypeNode | undefined { 1429 let sym = trueSymbolAtLocation(node); 1430 if (sym === undefined) { 1431 return undefined; 1432 } 1433 return getSymbolDeclarationTypeNode(sym); 1434 } 1435 1436export function getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined { 1437 const decl = getDeclaration(sym); 1438 if (!!decl && isVariableDeclaration(decl)) { 1439 return decl.type; 1440 } 1441 return undefined; 1442} 1443 1444export function hasEsObjectType(node: Node): boolean { 1445 const typeNode = getVariableDeclarationTypeNode(node); 1446 return typeNode !== undefined && isEsObjectType(typeNode); 1447} 1448 1449export function symbolHasEsObjectType(sym: ts.Symbol): boolean { 1450 const typeNode = getSymbolDeclarationTypeNode(sym); 1451 return typeNode !== undefined && isEsObjectType(typeNode); 1452} 1453 1454export function isEsObjectSymbol(sym: Symbol): boolean { 1455 const decl = getDeclaration(sym); 1456 return !!decl && isTypeAliasDeclaration(decl) && decl.name.escapedText === ES_OBJECT && 1457 decl.type.kind === SyntaxKind.AnyKeyword; 1458} 1459 1460export function isAnonymousType(type: Type): boolean { 1461 if (type.isUnionOrIntersection()) { 1462 for (const compType of type.types) { 1463 if (isAnonymousType(compType)) { 1464 return true; 1465 } 1466 } 1467 return false; 1468 } 1469 1470 return (type.flags & TypeFlags.Object) !== 0 && 1471 ((type as ObjectType).objectFlags & ObjectFlags.Anonymous) !== 0; 1472} 1473 1474export function getSymbolOfCallExpression(callExpr: CallExpression): Symbol | undefined { 1475 const signature = typeChecker.getResolvedSignature(callExpr); 1476 const signDecl = signature?.getDeclaration(); 1477 if (signDecl && signDecl.name) { 1478 return typeChecker.getSymbolAtLocation(signDecl.name); 1479 } 1480 return undefined; 1481} 1482 1483export function typeIsRecursive(topType: Type, type: Type | undefined = undefined): boolean { 1484 if (type === undefined) { 1485 type = topType; 1486 } 1487 else if (type === topType) { 1488 return true; 1489 } 1490 else if (type.aliasSymbol) { 1491 return false; 1492 } 1493 1494 if (type.isUnion()) { 1495 for (const unionElem of type.types) { 1496 if (typeIsRecursive(topType, unionElem)) { 1497 return true; 1498 } 1499 } 1500 } 1501 if (type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.Reference) { 1502 const typeArgs = typeChecker.getTypeArguments(type as TypeReference); 1503 if (typeArgs) { 1504 for (const typeArg of typeArgs) { 1505 if (typeIsRecursive(topType, typeArg)) { 1506 return true; 1507 } 1508 } 1509 } 1510 } 1511 return false; 1512} 1513 1514 1515} 1516} 1517