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