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