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