• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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