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