• 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_1 {
18
19import FaultID = Problems.FaultID;
20import AutofixInfo = Common.AutofixInfo;
21
22export namespace Utils {
23//import * as path from 'node:path';
24//import * as ts from 'typescript';
25//import ProblemInfo = ts.ProblemInfo;
26//import TypeScriptLinter = TypeScriptLinter;
27
28export const PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE = 2564;
29
30export const NON_INITIALIZABLE_PROPERTY_DECORATORS = ['Link', 'Consume', 'ObjectLink', 'Prop', 'BuilderParam', 'Param', 'Event'];
31
32export const NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS = ['CustomDialog']
33
34export const LIMITED_STANDARD_UTILITY_TYPES = [
35  "Awaited", "Pick", "Omit", "Exclude", "Extract", "NonNullable", "Parameters",
36  "ConstructorParameters", "ReturnType", "InstanceType", "ThisParameterType", "OmitThisParameter",
37  "ThisType", "Uppercase", "Lowercase", "Capitalize", "Uncapitalize",
38];
39
40export const ALLOWED_STD_SYMBOL_API = ["iterator"]
41
42export const ARKTS_IGNORE_DIRS = ['node_modules', 'oh_modules', 'build', '.preview'];
43export const ARKTS_IGNORE_FILES = ['hvigorfile.ts'];
44
45export const SENDABLE_DECORATOR = 'Sendable';
46
47export const SENDABLE_INTERFACE = 'ISendable';
48
49export const SENDABLE_DECORATOR_NODES = [
50  ts.SyntaxKind.ClassDeclaration,
51  ts.SyntaxKind.FunctionDeclaration,
52  ts.SyntaxKind.TypeAliasDeclaration
53];
54
55export const SENDABLE_CLOSURE_DECLS = [
56  ts.SyntaxKind.ClassDeclaration,
57  ts.SyntaxKind.FunctionDeclaration
58];
59
60export const ARKTS_COLLECTIONS_D_ETS = '@arkts.collections.d.ets';
61
62export const COLLECTIONS_NAMESPACE = 'collections';
63
64export const ARKTS_LANG_D_ETS = '@arkts.lang.d.ets';
65
66export const LANG_NAMESPACE = 'lang';
67
68export const ISENDABLE_TYPE = 'ISendable';
69
70export const USE_SHARED = 'use shared';
71
72export const D_TS = '.d.ts';
73
74let typeChecker: TypeChecker;
75export function setTypeChecker(tsTypeChecker: TypeChecker): void {
76  typeChecker = tsTypeChecker;
77}
78
79export function clearTypeChecker(): void {
80  typeChecker = {} as TypeChecker;
81}
82
83let testMode = false;
84export function setTestMode(tsTestMode: boolean): void {
85  testMode = tsTestMode;
86}
87
88export function getStartPos(nodeOrComment: Node | CommentRange): number {
89  return (nodeOrComment.kind === SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === SyntaxKind.MultiLineCommentTrivia)
90    ? (nodeOrComment as CommentRange).pos
91    : (nodeOrComment as Node).getStart();
92}
93
94export function getEndPos(nodeOrComment: Node | CommentRange): number {
95  return (nodeOrComment.kind === SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === SyntaxKind.MultiLineCommentTrivia)
96    ? (nodeOrComment as CommentRange).end
97    : (nodeOrComment as Node).getEnd();
98}
99
100export function getHighlightRange(nodeOrComment: Node | CommentRange, faultId: number): [number, number] {
101  return (
102    highlightRangeHandlers.get(faultId)?.call(undefined, nodeOrComment) ?? [
103      getStartPos(nodeOrComment),
104      getEndPos(nodeOrComment)
105    ]
106  );
107}
108
109const highlightRangeHandlers = new Map([
110  [FaultID.VarDeclaration, getVarDeclarationHighlightRange],
111  [FaultID.CatchWithUnsupportedType, getCatchWithUnsupportedTypeHighlightRange],
112  [FaultID.ForInStatement, getForInStatementHighlightRange],
113  [FaultID.WithStatement, getWithStatementHighlightRange],
114  [FaultID.DeleteOperator, getDeleteOperatorHighlightRange],
115  [FaultID.TypeQuery, getTypeQueryHighlightRange],
116  [FaultID.InstanceofUnsupported, getInstanceofUnsupportedHighlightRange],
117  [FaultID.ConstAssertion, getConstAssertionHighlightRange],
118  [FaultID.LimitedReturnTypeInference, getLimitedReturnTypeInferenceHighlightRange],
119  [FaultID.LocalFunction, getLocalFunctionHighlightRange],
120  [FaultID.FunctionBind, getFunctionApplyCallHighlightRange],
121  [FaultID.FunctionApplyCall, getFunctionApplyCallHighlightRange],
122  [FaultID.DeclWithDuplicateName, getDeclWithDuplicateNameHighlightRange],
123  [FaultID.ObjectLiteralNoContextType, getObjectLiteralNoContextTypeHighlightRange],
124  [FaultID.ClassExpression, getClassExpressionHighlightRange],
125  [FaultID.MultipleStaticBlocks, getMultipleStaticBlocksHighlightRange],
126  [FaultID.SendableDefiniteAssignment, getSendableDefiniteAssignmentHighlightRange]
127]);
128
129export function getVarDeclarationHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
130  return getKeywordHighlightRange(nodeOrComment, 'var');
131}
132
133export function getCatchWithUnsupportedTypeHighlightRange(
134  nodeOrComment: Node | CommentRange
135): [number, number] | undefined {
136  const catchClauseNode = (nodeOrComment as CatchClause).variableDeclaration;
137  if (catchClauseNode !== undefined) {
138    return [catchClauseNode.getStart(), catchClauseNode.getEnd()];
139  }
140
141  return undefined;
142}
143
144export function getForInStatementHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
145  return [
146    getEndPos((nodeOrComment as ForInStatement).initializer) + 1,
147    getStartPos((nodeOrComment as ForInStatement).expression) - 1
148  ];
149}
150
151export function getWithStatementHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
152  return [getStartPos(nodeOrComment), (nodeOrComment as WithStatement).statement.getStart() - 1];
153}
154
155export function getDeleteOperatorHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
156  return getKeywordHighlightRange(nodeOrComment, 'delete');
157}
158
159export function getTypeQueryHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
160  return getKeywordHighlightRange(nodeOrComment, 'typeof');
161}
162
163export function getInstanceofUnsupportedHighlightRange(
164  nodeOrComment: Node | CommentRange
165): [number, number] | undefined {
166  return getKeywordHighlightRange((nodeOrComment as BinaryExpression).operatorToken, 'instanceof');
167}
168
169export function getConstAssertionHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
170  if (nodeOrComment.kind === SyntaxKind.AsExpression) {
171    return [
172      (nodeOrComment as AsExpression).expression.getEnd() + 1,
173      (nodeOrComment as AsExpression).type.getStart() - 1
174    ];
175  }
176  return [
177    (nodeOrComment as TypeAssertion).expression.getEnd() + 1,
178    (nodeOrComment as TypeAssertion).type.getEnd() + 1
179  ];
180}
181
182export function getLimitedReturnTypeInferenceHighlightRange(
183  nodeOrComment: Node | CommentRange
184): [number, number] | undefined {
185  let node: Node | undefined;
186  if (nodeOrComment.kind === SyntaxKind.FunctionExpression) {
187    // we got error about return type so it should be present
188    node = (nodeOrComment as FunctionExpression).type;
189  } else if (nodeOrComment.kind === SyntaxKind.FunctionDeclaration) {
190    node = (nodeOrComment as FunctionDeclaration).name;
191  } else if (nodeOrComment.kind === SyntaxKind.MethodDeclaration) {
192    node = (nodeOrComment as MethodDeclaration).name;
193  }
194  if (node !== undefined) {
195    return [node.getStart(), node.getEnd()];
196  }
197
198  return undefined;
199}
200
201export function getLocalFunctionHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
202  return getKeywordHighlightRange(nodeOrComment, 'function');
203}
204
205export function getFunctionApplyCallHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
206  const pointPos = (nodeOrComment as Node).getText().lastIndexOf('.');
207  return [getStartPos(nodeOrComment) + pointPos + 1, getEndPos(nodeOrComment)];
208}
209
210export function getDeclWithDuplicateNameHighlightRange(
211  nodeOrComment: Node | CommentRange
212): [number, number] | undefined {
213  // in case of private identifier no range update is needed
214  const nameNode: Node | undefined = (nodeOrComment as NamedDeclaration).name;
215  if (nameNode !== undefined) {
216    return [nameNode.getStart(), nameNode.getEnd()];
217  }
218
219  return undefined;
220}
221
222export function getObjectLiteralNoContextTypeHighlightRange(
223  nodeOrComment: Node | CommentRange
224): [number, number] | undefined {
225  return getKeywordHighlightRange(nodeOrComment, '{');
226}
227
228export function getClassExpressionHighlightRange(nodeOrComment: Node | CommentRange): [number, number] | undefined {
229  return getKeywordHighlightRange(nodeOrComment, 'class');
230}
231
232export function getMultipleStaticBlocksHighlightRange(nodeOrComment: ts.Node | ts.CommentRange): [number, number] | undefined {
233  return getKeywordHighlightRange(nodeOrComment, 'static');
234}
235
236// highlight ranges for Sendable rules
237export function getSendableDefiniteAssignmentHighlightRange(
238  nodeOrComment: ts.Node | ts.CommentRange
239): [number, number] | undefined {
240  const name = (nodeOrComment as ts.PropertyDeclaration).name;
241  const exclamationToken = (nodeOrComment as ts.PropertyDeclaration).exclamationToken;
242  return [name.getStart(), exclamationToken ? exclamationToken.getEnd() : name.getEnd()];
243}
244
245export function getKeywordHighlightRange(nodeOrComment: Node | CommentRange, keyword: string): [number, number] {
246  const start = getStartPos(nodeOrComment);
247  return [start, start + keyword.length];
248}
249
250export function isAssignmentOperator(tsBinOp: BinaryOperatorToken): boolean {
251  return tsBinOp.kind >= SyntaxKind.FirstAssignment && tsBinOp.kind <= SyntaxKind.LastAssignment;
252}
253
254export function isType(tsType: TypeNode | undefined, checkType: string): boolean {
255  if (tsType === undefined || !isTypeReferenceNode(tsType)) {
256    return false;
257  }
258  return entityNameToString(tsType.typeName) === checkType;
259}
260
261export function entityNameToString(name: EntityName): string {
262  if (isIdentifier(name)) {
263    return name.escapedText.toString();
264  }
265  else {
266    return entityNameToString(name.left) + entityNameToString(name.right);
267  }
268}
269
270export function isNumberLikeType(tsType: Type): boolean {
271  return (tsType.getFlags() & TypeFlags.NumberLike) !== 0;
272}
273
274export function isBooleanLikeType(tsType: Type): boolean {
275  return (tsType.getFlags() & TypeFlags.BooleanLike) !== 0;
276}
277
278export function isStringLikeType(tsType: Type): boolean {
279  if (tsType.isUnion()) {
280    for (const tsCompType of tsType.types) {
281      if ((tsCompType.flags & TypeFlags.StringLike) === 0) return false;
282    }
283    return true;
284  }
285  return (tsType.getFlags() & TypeFlags.StringLike) !== 0;
286}
287
288export function isStringType(tsType: ts.Type): boolean {
289  if ((tsType.getFlags() & ts.TypeFlags.String) !== 0) {
290    return true;
291  }
292
293  if (!isTypeReference(tsType)) {
294    return false;
295  }
296
297  const symbol = tsType.symbol;
298  const name = typeChecker.getFullyQualifiedName(symbol);
299  return name === 'String' && isGlobalSymbol(symbol);
300}
301
302export function isPrimitiveEnumMemberType(type: Type, primitiveType: TypeFlags): boolean {
303  const isNonPrimitive = (type.flags & TypeFlags.NonPrimitive) !== 0;
304  if (!isEnumMemberType(type) || isNonPrimitive) {
305    return false;
306  }
307  return (type.flags & primitiveType) !== 0;
308}
309
310export function unwrapParenthesizedType(tsType: TypeNode): TypeNode {
311  while (isParenthesizedTypeNode(tsType)) {
312    tsType = tsType.type;
313  }
314  return tsType;
315}
316
317export function findParentIf(asExpr: AsExpression): IfStatement | null {
318  let node = asExpr.parent;
319  while (node) {
320    if (node.kind === SyntaxKind.IfStatement) {
321      return node as IfStatement;
322    }
323    node = node.parent;
324  }
325
326  return null;
327}
328
329export function isDestructuringAssignmentLHS(
330  tsExpr: ArrayLiteralExpression | ObjectLiteralExpression
331): boolean {
332  // Check whether given expression is the LHS part of the destructuring
333  // assignment (or is a nested element of destructuring pattern).
334  let tsParent = tsExpr.parent;
335  let tsCurrentExpr: Node = tsExpr;
336  while (tsParent) {
337    if (
338      isBinaryExpression(tsParent) && isAssignmentOperator(tsParent.operatorToken) &&
339      tsParent.left === tsCurrentExpr
340    ) {
341      return true;
342    }
343    if (
344      (isForStatement(tsParent) || isForInStatement(tsParent) || isForOfStatement(tsParent)) &&
345      tsParent.initializer && tsParent.initializer === tsCurrentExpr
346    ) {
347      return true;
348    }
349    tsCurrentExpr = tsParent;
350    tsParent = tsParent.parent;
351  }
352
353  return false;
354}
355
356export function isEnumType(tsType: ts.Type): boolean {
357  // when type equals `typeof <Enum>`, only symbol contains information about it's type.
358  const isEnumSymbol = tsType.symbol && isEnum(tsType.symbol);
359  // otherwise, we should analyze flags of the type itself
360  const isEnumType = !!(tsType.flags & ts.TypeFlags.Enum) || !!(tsType.flags & ts.TypeFlags.EnumLiteral);
361  return isEnumSymbol || isEnumType;
362}
363
364export function isEnum(tsSymbol: ts.Symbol): boolean {
365  return !!(tsSymbol.flags & ts.SymbolFlags.Enum);
366}
367
368export function isEnumMemberType(tsType: Type): boolean {
369  // Note: For some reason, test (tsType.flags & TypeFlags.Enum) != 0 doesn't work here.
370  // Must use SymbolFlags to figure out if this is an enum type.
371  return tsType.symbol && (tsType.symbol.flags & SymbolFlags.EnumMember) !== 0;
372}
373
374export function isObjectLiteralType(tsType: Type): boolean {
375  return tsType.symbol && (tsType.symbol.flags & SymbolFlags.ObjectLiteral) !== 0;
376}
377
378export function hasModifier(tsModifiers: readonly Modifier[] | undefined, tsModifierKind: number): boolean {
379  // Sanity check.
380  if (!tsModifiers) return false;
381
382  for (const tsModifier of tsModifiers) {
383    if (tsModifier.kind === tsModifierKind) return true;
384  }
385
386  return false;
387}
388
389export function unwrapParenthesized(tsExpr: Expression): Expression {
390  let unwrappedExpr = tsExpr;
391  while (isParenthesizedExpression(unwrappedExpr)) {
392    unwrappedExpr = unwrappedExpr.expression;
393  }
394  return unwrappedExpr;
395}
396
397export function followIfAliased(sym: Symbol): Symbol {
398  if ((sym.getFlags() & SymbolFlags.Alias) !== 0) {
399    return typeChecker.getAliasedSymbol(sym);
400  }
401  return sym;
402}
403
404let trueSymbolAtLocationCache = new Map<ts.Node, ts.Symbol | null>();
405
406export function trueSymbolAtLocation(node: Node): Symbol | undefined {
407  let cache = trueSymbolAtLocationCache;
408  let val = cache.get(node);
409  if (val !== undefined) {
410    return val !== null ? val : undefined;
411  }
412  let sym = typeChecker.getSymbolAtLocation(node);
413  if (sym === undefined) {
414    cache.set(node, null);
415    return undefined;
416  }
417  sym = followIfAliased(sym);
418  cache.set(node, sym);
419  return sym;
420}
421
422export function clearTrueSymbolAtLocationCache(): void {
423  trueSymbolAtLocationCache.clear();
424}
425
426export function isTypeDeclSyntaxKind(kind: SyntaxKind) {
427  return isStructDeclarationKind(kind) ||
428    kind === SyntaxKind.EnumDeclaration ||
429    kind === SyntaxKind.ClassDeclaration ||
430    kind === SyntaxKind.InterfaceDeclaration ||
431    kind === SyntaxKind.TypeAliasDeclaration;
432}
433
434export function symbolHasDuplicateName(symbol: Symbol, tsDeclKind: SyntaxKind): boolean {
435  // Type Checker merges all declarations with the same name in one scope into one symbol.
436  // Thus, check whether the symbol of certain declaration has any declaration with
437  // different syntax kind.
438  const symbolDecls = symbol?.getDeclarations();
439  if (symbolDecls) {
440    for (const symDecl of symbolDecls) {
441      const declKind = symDecl.kind;
442      // we relax arkts-unique-names for namespace collision with class/interface/enum/type/struct
443      const isNamespaceTypeCollision =
444        (isTypeDeclSyntaxKind(declKind) && tsDeclKind === SyntaxKind.ModuleDeclaration) ||
445        (isTypeDeclSyntaxKind(tsDeclKind) && declKind === SyntaxKind.ModuleDeclaration);
446
447      // Don't count declarations with 'Identifier' syntax kind as those
448      // usually depict declaring an object's property through assignment.
449      if (declKind !== SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) return true;
450    }
451  }
452
453  return false;
454}
455
456export function isReferenceType(tsType: Type): boolean {
457  const f = tsType.getFlags();
458  return (
459    (f & TypeFlags.InstantiableNonPrimitive) !== 0 || (f & TypeFlags.Object) !== 0 ||
460    (f & TypeFlags.Boolean) !== 0 || (f & TypeFlags.Enum) !== 0 || (f & TypeFlags.NonPrimitive) !== 0 ||
461    (f & TypeFlags.Number) !== 0 || (f & TypeFlags.String) !== 0
462  );
463}
464
465export function isPrimitiveType(type: Type): boolean {
466  const f = type.getFlags();
467  return (
468    (f & TypeFlags.Boolean) !== 0 || (f & TypeFlags.BooleanLiteral) !== 0 ||
469    (f & TypeFlags.Number) !== 0 || (f & TypeFlags.NumberLiteral) !== 0
470    // In ArkTS 'string' is not a primitive type. So for the common subset 'string'
471    // should be considered as a reference type. That is why next line is commented out.
472    //(f & TypeFlags.String) != 0 || (f & TypeFlags.StringLiteral) != 0
473  );
474}
475
476export function isPrimitiveLiteralType(type: ts.Type): boolean {
477  return !!(
478    type.flags &
479    (ts.TypeFlags.BooleanLiteral |
480      ts.TypeFlags.NumberLiteral |
481      ts.TypeFlags.StringLiteral |
482      ts.TypeFlags.BigIntLiteral)
483  );
484}
485
486export function isPurePrimitiveLiteralType(type: ts.Type): boolean {
487  return isPrimitiveLiteralType(type) && !(type.flags & ts.TypeFlags.EnumLiteral);
488}
489
490export function isTypeSymbol(symbol: Symbol | undefined): boolean {
491  return (
492    !!symbol && !!symbol.flags &&
493    ((symbol.flags & SymbolFlags.Class) !== 0 || (symbol.flags & SymbolFlags.Interface) !== 0)
494  );
495}
496
497// Check whether type is generic 'Array<T>' type defined in TypeScript standard library.
498export function isGenericArrayType(tsType: Type): tsType is TypeReference {
499  return (
500    isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 &&
501    tsType.getSymbol()?.getName() === "Array"
502  );
503}
504
505export function isReadonlyArrayType(tsType: Type): boolean {
506  return (
507    isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 &&
508    (tsType.getSymbol()?.getName() === 'ReadonlyArray')
509  );
510}
511
512export function isTypedArray(tsType: ts.Type): boolean {
513  const symbol = tsType.symbol;
514  if (!symbol) {
515    return false;
516  }
517  const name = typeChecker.getFullyQualifiedName(symbol);
518  if (isGlobalSymbol(symbol) && TYPED_ARRAYS.includes(name)) {
519    return true;
520  }
521  const decl = getDeclaration(symbol);
522  return (
523    !!decl &&
524    isArkTSCollectionsClassOrInterfaceDeclaration(decl) &&
525    TYPED_ARRAYS.includes(symbol.getName())
526  );
527}
528
529export function isArray(tsType: ts.Type): boolean {
530  return isGenericArrayType(tsType) || isReadonlyArrayType(tsType) || isTypedArray(tsType);
531}
532
533export function isTuple(tsType: ts.Type): boolean {
534  return isTypeReference(tsType) && !!(tsType.objectFlags & ts.ObjectFlags.Tuple);
535}
536
537// does something similar to relatedByInheritanceOrIdentical function
538export function isOrDerivedFrom(tsType: ts.Type, checkType: CheckType, checkedBaseTypes?: Set<ts.Type>): boolean {
539  if (isTypeReference(tsType) && tsType.target !== tsType) {
540    tsType = tsType.target;
541  }
542  if (checkType(tsType)) {
543    return true;
544  }
545  if (!tsType.symbol || !tsType.symbol.declarations) {
546    return false;
547  }
548
549  // Avoid type recursion in heritage by caching checked types.
550  (checkedBaseTypes ||= new Set<ts.Type>()).add(tsType);
551
552  for (const tsTypeDecl of tsType.symbol.declarations) {
553    const isClassOrInterfaceDecl = ts.isClassDeclaration(tsTypeDecl) || ts.isInterfaceDeclaration(tsTypeDecl);
554    const isDerived = isClassOrInterfaceDecl && !!tsTypeDecl.heritageClauses;
555    if (!isDerived) {
556      continue;
557    }
558    for (const heritageClause of tsTypeDecl.heritageClauses) {
559      if (processParentTypesCheck(heritageClause.types, checkType, checkedBaseTypes)) {
560        return true;
561      }
562    }
563  }
564
565  return false;
566}
567
568export function isTypeReference(tsType: Type): tsType is TypeReference {
569  return (
570    (tsType.getFlags() & TypeFlags.Object) !== 0 &&
571    ((tsType as ObjectType).objectFlags & ObjectFlags.Reference) !== 0
572  );
573}
574
575export function isNullType(tsTypeNode: TypeNode): boolean {
576  return (isLiteralTypeNode(tsTypeNode) && tsTypeNode.literal.kind === SyntaxKind.NullKeyword);
577}
578
579export function isThisOrSuperExpr(tsExpr: Expression): boolean {
580  return (tsExpr.kind === SyntaxKind.ThisKeyword || tsExpr.kind === SyntaxKind.SuperKeyword);
581}
582
583export function isPrototypeSymbol(symbol: Symbol | undefined): boolean {
584  return (!!symbol && !!symbol.flags && (symbol.flags & SymbolFlags.Prototype) !== 0);
585}
586
587export function isFunctionSymbol(symbol: Symbol | undefined): boolean {
588  return (!!symbol && !!symbol.flags && (symbol.flags & SymbolFlags.Function) !== 0);
589}
590
591export function isInterfaceType(tsType: Type | undefined): boolean {
592  return (
593    !!tsType && !!tsType.symbol && !!tsType.symbol.flags &&
594    (tsType.symbol.flags & SymbolFlags.Interface) !== 0
595  );
596}
597
598export function isAnyType(tsType: Type): tsType is TypeReference {
599  return (tsType.getFlags() & TypeFlags.Any) !== 0;
600}
601
602export function isUnknownType(tsType: Type): boolean {
603  return (tsType.getFlags() & TypeFlags.Unknown) !== 0;
604}
605
606export function isUnsupportedType(tsType: Type): boolean {
607  return (
608    !!tsType.flags && ((tsType.flags & TypeFlags.Any) !== 0 || (tsType.flags & TypeFlags.Unknown) !== 0 ||
609    (tsType.flags & TypeFlags.Intersection) !== 0)
610  );
611}
612
613export function isUnsupportedUnionType(tsType: Type): boolean {
614  if (tsType.isUnion()) {
615    return !isNullableUnionType(tsType) && !isBooleanUnionType(tsType);
616  }
617  return false;
618}
619
620function isNullableUnionType(tsUnionType: UnionType): boolean {
621  for (const t of tsUnionType.types) {
622    if (!!(t.flags & ts.TypeFlags.Undefined) || !!(t.flags & ts.TypeFlags.Null)) {
623      return true;
624    }
625  }
626  return false;
627}
628
629function isBooleanUnionType(tsUnionType: UnionType): boolean {
630  // For some reason, 'boolean' type is also represented as as union
631  // of 'true' and 'false' literal types. This form of 'union' type
632  // should be considered as supported.
633  const tsCompTypes = tsUnionType.types;
634  return (
635    tsUnionType.flags === (TypeFlags.Boolean | TypeFlags.Union) && tsCompTypes.length === 2 &&
636    tsCompTypes[0].flags === TypeFlags.BooleanLiteral && (tsCompTypes[1].flags === TypeFlags.BooleanLiteral)
637  );
638}
639
640export function isFunctionOrMethod(tsSymbol: Symbol | undefined): boolean {
641  return (
642    !!tsSymbol &&
643    ((tsSymbol.flags & SymbolFlags.Function) !== 0 || (tsSymbol.flags & SymbolFlags.Method) !== 0)
644  );
645}
646
647export function isMethodAssignment(tsSymbol: Symbol | undefined): boolean {
648  return (
649    !!tsSymbol &&
650    ((tsSymbol.flags & SymbolFlags.Method) !== 0 && (tsSymbol.flags & SymbolFlags.Assignment) !== 0)
651  );
652}
653
654export function getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined {
655  if (tsSymbol && tsSymbol.declarations && tsSymbol.declarations.length > 0) {
656    return tsSymbol.declarations[0];
657  }
658  return undefined;
659}
660
661function isVarDeclaration(tsDecl: Node): boolean {
662  return isVariableDeclaration(tsDecl) && isVariableDeclarationList(tsDecl.parent);
663}
664
665export function isValidEnumMemberInit(tsExpr: Expression): boolean {
666  if (isNumberConstantValue(tsExpr.parent as EnumMember)) {
667    return true;
668  }
669  if (isStringConstantValue(tsExpr.parent as EnumMember)) {
670    return true;
671  }
672  return isCompileTimeExpression(tsExpr);
673}
674
675export function isCompileTimeExpression(tsExpr: Expression): boolean {
676  if (
677    isParenthesizedExpression(tsExpr) ||
678    (isAsExpression(tsExpr) && tsExpr.type.kind === SyntaxKind.NumberKeyword)) {
679    return isCompileTimeExpression(tsExpr.expression);
680  }
681  switch (tsExpr.kind) {
682    case SyntaxKind.PrefixUnaryExpression:
683      return isPrefixUnaryExprValidEnumMemberInit(tsExpr as PrefixUnaryExpression);
684    case SyntaxKind.ParenthesizedExpression:
685    case SyntaxKind.BinaryExpression:
686      return isBinaryExprValidEnumMemberInit(tsExpr as BinaryExpression);
687    case SyntaxKind.ConditionalExpression:
688      return isConditionalExprValidEnumMemberInit(tsExpr as ConditionalExpression);
689    case SyntaxKind.Identifier:
690      return isIdentifierValidEnumMemberInit(tsExpr as Identifier);
691    case SyntaxKind.NumericLiteral:
692      return true;
693    case SyntaxKind.StringLiteral:
694        return true;
695    case SyntaxKind.PropertyAccessExpression: {
696      // if enum member is in current enum declaration try to get value
697      // if it comes from another enum consider as constant
698      const propertyAccess = tsExpr as PropertyAccessExpression;
699      if(isNumberConstantValue(propertyAccess)) {
700        return true;
701      }
702      const leftHandSymbol = typeChecker.getSymbolAtLocation(propertyAccess.expression);
703      if(!leftHandSymbol) {
704        return false;
705      }
706      const decls = leftHandSymbol.getDeclarations();
707      if (!decls || decls.length !== 1) {
708        return false;
709      }
710      return isEnumDeclaration(decls[0]);
711    }
712    default:
713      return false;
714  }
715}
716
717function isPrefixUnaryExprValidEnumMemberInit(tsExpr: PrefixUnaryExpression): boolean {
718  return (isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && isCompileTimeExpression(tsExpr.operand));
719}
720
721function isBinaryExprValidEnumMemberInit(tsExpr: BinaryExpression): boolean {
722  return (
723    isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && isCompileTimeExpression(tsExpr.left) &&
724    isCompileTimeExpression(tsExpr.right)
725  );
726}
727
728function isConditionalExprValidEnumMemberInit(tsExpr: ConditionalExpression): boolean {
729  return (isCompileTimeExpression(tsExpr.whenTrue) && isCompileTimeExpression(tsExpr.whenFalse));
730}
731
732function isIdentifierValidEnumMemberInit(tsExpr: Identifier): boolean {
733  const tsSymbol = typeChecker.getSymbolAtLocation(tsExpr);
734  const tsDecl = getDeclaration(tsSymbol);
735  return (!!tsDecl &&
736            ((isVarDeclaration(tsDecl) && isConst(tsDecl.parent)) ||
737              (tsDecl.kind === SyntaxKind.EnumMember)
738            )
739  );
740}
741
742function isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: PrefixUnaryOperator): boolean {
743  return (
744    tsPrefixUnaryOp === SyntaxKind.PlusToken || tsPrefixUnaryOp === SyntaxKind.MinusToken ||
745    tsPrefixUnaryOp === SyntaxKind.TildeToken
746  );
747}
748
749function isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: BinaryOperatorToken): boolean {
750  return (
751    tsBinaryOp.kind === SyntaxKind.AsteriskToken || tsBinaryOp.kind === SyntaxKind.SlashToken ||
752    tsBinaryOp.kind === SyntaxKind.PercentToken || tsBinaryOp.kind === SyntaxKind.MinusToken ||
753    tsBinaryOp.kind === SyntaxKind.PlusToken || tsBinaryOp.kind === SyntaxKind.LessThanLessThanToken ||
754    tsBinaryOp.kind === SyntaxKind.GreaterThanGreaterThanToken || tsBinaryOp.kind === SyntaxKind.BarBarToken ||
755    tsBinaryOp.kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken ||
756    tsBinaryOp.kind === SyntaxKind.AmpersandToken || tsBinaryOp.kind === SyntaxKind.CaretToken ||
757    tsBinaryOp.kind === SyntaxKind.BarToken || tsBinaryOp.kind === SyntaxKind.AmpersandAmpersandToken
758  );
759}
760
761export function isConst(tsNode: Node): boolean {
762  return !!(getCombinedNodeFlags(tsNode) & NodeFlags.Const);
763}
764
765export function isNumberConstantValue(
766  tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression | NumericLiteral
767): boolean {
768
769  const tsConstValue = (tsExpr.kind === SyntaxKind.NumericLiteral) ?
770    Number(tsExpr.getText()) :
771    typeChecker.getConstantValue(tsExpr);
772
773  return tsConstValue !== undefined && typeof tsConstValue === "number";
774}
775
776export function isIntegerConstantValue(
777  tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression | NumericLiteral
778): boolean {
779
780  const tsConstValue = (tsExpr.kind === SyntaxKind.NumericLiteral) ?
781    Number(tsExpr.getText()) :
782    typeChecker.getConstantValue(tsExpr);
783  return (
784    tsConstValue !== undefined && typeof tsConstValue === "number" &&
785    tsConstValue.toFixed(0) === tsConstValue.toString()
786  );
787}
788
789export function isStringConstantValue(
790  tsExpr: EnumMember | PropertyAccessExpression | ElementAccessExpression
791): boolean {
792  const tsConstValue = typeChecker.getConstantValue(tsExpr);
793  return (
794    tsConstValue !== undefined && typeof tsConstValue === "string"
795  );
796}
797
798// Returns true if typeA is a subtype of typeB
799export function relatedByInheritanceOrIdentical(typeA: Type, typeB: Type): boolean {
800  if (isTypeReference(typeA) && typeA.target !== typeA) { typeA = typeA.target; }
801  if (isTypeReference(typeB) && typeB.target !== typeB) { typeB = typeB.target; }
802
803  if (typeA === typeB || isObject(typeB)) { return true; }
804  if (!typeA.symbol || !typeA.symbol.declarations) { return false; }
805
806  const isBISendable = isISendableInterface(typeB);
807  for (const typeADecl of typeA.symbol.declarations) {
808    if (isBISendable && ts.isClassDeclaration(typeADecl) && hasSendableDecorator(typeADecl)) {
809      return true;
810    }
811    if (
812      (!isClassDeclaration(typeADecl) && !isInterfaceDeclaration(typeADecl)) ||
813      !typeADecl.heritageClauses
814    ) { continue; }
815    for (const heritageClause of typeADecl.heritageClauses) {
816      const processInterfaces = typeA.isClass() ? (heritageClause.token !== SyntaxKind.ExtendsKeyword) : true;
817      if (processParentTypes(heritageClause.types, typeB, processInterfaces)) return true;
818    }
819  }
820
821  return false;
822}
823
824export function reduceReference(t: ts.Type): ts.Type {
825  return isTypeReference(t) && t.target !== t ? t.target : t;
826}
827
828function needToDeduceStructuralIdentityHandleUnions(
829  lhsType: Type,
830  rhsType: Type,
831  rhsExpr: Expression,
832  isStrict: boolean
833): boolean {
834  if (rhsType.isUnion()) {
835    // Each Class/Interface of the RHS union type must be compatible with LHS type.
836    for (const compType of rhsType.types) {
837      if (needToDeduceStructuralIdentity(lhsType, compType, rhsExpr, isStrict)) {
838        return true;
839      }
840    }
841    return false;
842  }
843
844  if (lhsType.isUnion()) {
845    // RHS type needs to be compatible with at least one type of the LHS union.
846    for (const compType of lhsType.types) {
847      if (!needToDeduceStructuralIdentity(compType, rhsType, rhsExpr, isStrict)) {
848        return false;
849      }
850    }
851    return true;
852  }
853  // should be unreachable
854  return false;
855}
856
857// return true if two class types are not related by inheritance and structural identity check is needed
858export function needToDeduceStructuralIdentity(
859  lhsType: Type,
860  rhsType: Type,
861  rhsExpr: Expression,
862  isStrict: boolean = false
863): boolean {
864  lhsType = getNonNullableType(lhsType);
865  rhsType = getNonNullableType(rhsType);
866  if (isLibraryType(lhsType)) {
867    return false;
868  }
869  if (isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) {
870    return false;
871  }
872  // #14569: Check for Function type.
873  if (areCompatibleFunctionals(lhsType, rhsType)) {
874    return false;
875  }
876  if (rhsType.isUnion() || lhsType.isUnion()) {
877    return needToDeduceStructuralIdentityHandleUnions(lhsType, rhsType, rhsExpr, isStrict);
878  }
879  // if isStrict, things like generics need to be checked
880  if (isStrict) {
881    if (isTypeReference(rhsType) && !!(rhsType.objectFlags & ts.ObjectFlags.ArrayLiteral)) {
882      // The 'arkts-sendable-obj-init' rule already exists. Wait for the new 'strict type' to be modified.
883      return false;
884    }
885    lhsType = reduceReference(lhsType);
886    rhsType = reduceReference(rhsType);
887  }
888  return lhsType.isClassOrInterface() && rhsType.isClassOrInterface() &&
889    !relatedByInheritanceOrIdentical(rhsType, lhsType);
890}
891
892// Does the 'arkts-no-structure-typing' rule need to be strictly enforced to complete previously missed scenarios
893export function needStrictMatchType(lhsType: ts.Type, rhsType: ts.Type): boolean {
894  if (isStrictSendableMatch(lhsType, rhsType)) {
895    return true;
896  }
897  // add other requirements with strict type requirements here
898  return false;
899}
900
901// For compatibility, left must all ClassOrInterface is sendable, right must has non-sendable ClassorInterface
902function isStrictSendableMatch(lhsType: ts.Type, rhsType: ts.Type): boolean {
903  let isStrictLhs = false;
904  if (lhsType.isUnion()) {
905    for (let compType of lhsType.types) {
906      compType = reduceReference(compType);
907      if (!compType.isClassOrInterface()) {
908        continue;
909      }
910      if (!isSendableClassOrInterface(compType)) {
911        return false;
912      }
913      isStrictLhs = true;
914    }
915  } else {
916    isStrictLhs = isSendableClassOrInterface(lhsType);
917  }
918  return isStrictLhs && typeContainsNonSendableClassOrInterface(rhsType);
919}
920
921export function hasPredecessor(node: Node, predicate: (node: Node) => boolean): boolean {
922  let parent = node.parent;
923  while (parent !== undefined) {
924    if (predicate(parent)) {
925      return true;
926    }
927    parent = parent.parent;
928  }
929  return false;
930}
931
932export function processParentTypes(parentTypes: NodeArray<ExpressionWithTypeArguments>, typeB: Type, processInterfaces: boolean): boolean {
933  for (const baseTypeExpr of parentTypes) {
934    let baseType = typeChecker.getTypeAtLocation(baseTypeExpr);
935    if (isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target;
936    if (baseType && (baseType.isClass() !== processInterfaces) && relatedByInheritanceOrIdentical(baseType, typeB)) return true;
937  }
938  return false;
939}
940
941function processParentTypesCheck(
942  parentTypes: ts.NodeArray<ts.Expression>,
943  checkType: CheckType,
944  checkedBaseTypes: Set<ts.Type>
945): boolean {
946  for (const baseTypeExpr of parentTypes) {
947    let baseType = typeChecker.getTypeAtLocation(baseTypeExpr);
948    if (isTypeReference(baseType) && baseType.target !== baseType) {
949      baseType = baseType.target;
950    }
951    if (
952      baseType &&
953      !checkedBaseTypes.has(baseType) &&
954      isOrDerivedFrom(baseType, checkType, checkedBaseTypes)
955    ) {
956      return true;
957    }
958  }
959  return false;
960}
961
962
963export function isObject(tsType: Type): boolean {
964  if (!tsType) {
965    return false;
966  }
967  if (tsType.symbol && (tsType.isClassOrInterface() && tsType.symbol.name === "Object")) {
968    return true;
969  }
970  const node = typeChecker.typeToTypeNode(tsType, undefined, undefined);
971  return node !== undefined && node.kind === SyntaxKind.ObjectKeyword;
972}
973
974export function logTscDiagnostic(diagnostics: readonly Diagnostic[], log: (message: any, ...args: any[]) => void): void {
975  diagnostics.forEach((diagnostic) => {
976    let message = flattenDiagnosticMessageText(diagnostic.messageText, "\n");
977
978    if (diagnostic.file && diagnostic.start) {
979      const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
980      message = `${diagnostic.file.fileName} (${line + 1}, ${character + 1}): ${message}`;
981    }
982
983    log(message);
984  });
985}
986
987export function encodeProblemInfo(problem: ProblemInfo): string {
988  return `${problem.problem}%${problem.start}%${problem.end}`;
989}
990
991export function decodeAutofixInfo(info: string): AutofixInfo {
992  const infos = info.split("%");
993  return { problemID: infos[0], start: Number.parseInt(infos[1]), end: Number.parseInt(infos[2]) };
994}
995
996export function isCallToFunctionWithOmittedReturnType(tsExpr: Expression): boolean {
997  if (isCallExpression(tsExpr)) {
998    const tsCallSignature = typeChecker.getResolvedSignature(tsExpr);
999    if (tsCallSignature) {
1000      const tsSignDecl = tsCallSignature.getDeclaration();
1001      // `tsSignDecl` is undefined when `getResolvedSignature` returns `unknownSignature`
1002      if (!tsSignDecl || !tsSignDecl.type) return true;
1003    }
1004  }
1005
1006  return false;
1007}
1008
1009function hasReadonlyFields(type: Type): boolean {
1010  if (type.symbol.members === undefined) return false; // No members -> no readonly fields
1011
1012  let result = false;
1013
1014  type.symbol.members.forEach((value /*, key*/) => {
1015    if (
1016      value.declarations !== undefined && value.declarations.length > 0 &&
1017      isPropertyDeclaration(value.declarations[0])
1018    ) {
1019      const propmMods = ts.getModifiers(value.declarations[0] as ts.PropertyDeclaration);//value.declarations[0].modifiers; // TSC 4.2 doesn't have 'getModifiers()' method
1020      if (hasModifier(propmMods, SyntaxKind.ReadonlyKeyword)) {
1021        result = true;
1022        return;
1023      }
1024    }
1025  });
1026
1027  return result;
1028}
1029
1030function hasDefaultCtor(type: Type): boolean {
1031  if (type.symbol.members === undefined) return true; // No members -> no explicite constructors -> there is default ctor
1032
1033  let hasCtor = false; // has any constructor
1034  let hasDefaultCtor = false; // has default constructor
1035
1036  type.symbol.members.forEach((value /*, key*/) => {
1037    if ((value.flags & SymbolFlags.Constructor) !== 0) {
1038      hasCtor = true;
1039
1040      if (value.declarations !== undefined && value.declarations.length > 0) {
1041        const declCtor = value.declarations[0] as ConstructorDeclaration;
1042        if (declCtor.parameters.length === 0) {
1043          hasDefaultCtor = true;
1044          return;
1045        }
1046      }
1047    }
1048  });
1049
1050  return !hasCtor || hasDefaultCtor; // Has no any explicite constructor -> has implicite default constructor.
1051}
1052
1053function isAbstractClass(type: Type): boolean {
1054  if (type.isClass() && type.symbol.declarations && type.symbol.declarations.length > 0) {
1055    const declClass = type.symbol.declarations[0] as ClassDeclaration;
1056    const classMods = ts.getModifiers(declClass); //declClass.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method
1057    if (hasModifier(classMods, SyntaxKind.AbstractKeyword)) {
1058      return true;
1059    }
1060  }
1061
1062  return false;
1063}
1064
1065export function validateObjectLiteralType(type: Type | undefined): boolean {
1066  if (!type) return false;
1067
1068  type = getTargetType(type);
1069  return (
1070    type !== undefined && type.isClassOrInterface() && hasDefaultCtor(type) &&
1071    !hasReadonlyFields(type) && !isAbstractClass(type)
1072  );
1073}
1074
1075export function isStructDeclarationKind(kind: SyntaxKind) {
1076  return kind === SyntaxKind.StructDeclaration;
1077}
1078
1079export function isStructDeclaration(node: Node) {
1080  return isStructDeclarationKind(node.kind);
1081}
1082export function isStructObjectInitializer(objectLiteral: ObjectLiteralExpression): boolean {
1083  if(isCallLikeExpression(objectLiteral.parent)) {
1084    const signature = typeChecker.getResolvedSignature(objectLiteral.parent);
1085    const signDecl = signature?.declaration;
1086    return !!signDecl && isConstructorDeclaration(signDecl) && isStructDeclaration(signDecl.parent);
1087  }
1088  return false;
1089}
1090
1091export function hasMethods(type: Type): boolean {
1092  const properties = typeChecker.getPropertiesOfType(type);
1093  if (properties?.length) {
1094    for (const prop of properties) {
1095      if (prop.getFlags() & SymbolFlags.Method) return true;
1096    }
1097  };
1098
1099  return false;
1100}
1101
1102function findProperty(type: Type, name: string): Symbol | undefined {
1103  const properties = typeChecker.getPropertiesOfType(type);
1104  if(properties.length) {
1105    for (const prop of properties) {
1106      if (prop.name === name) return prop;
1107    }
1108  }
1109  return undefined;
1110}
1111
1112export function checkTypeSet(typeSet: ts.Type, predicate: CheckType): boolean {
1113  if (!typeSet.isUnionOrIntersection()) {
1114    return predicate(typeSet);
1115  }
1116  for (let elemType of typeSet.types) {
1117    if (checkTypeSet(elemType, predicate)) {
1118      return true;
1119    }
1120  }
1121  return false;
1122}
1123
1124export function getNonNullableType(t: ts.Type): ts.Type {
1125  if (t.isUnion()) {
1126    return t.getNonNullableType();
1127  }
1128  return t;
1129}
1130
1131export function isObjectLiteralAssignable(lhsType: ts.Type | undefined, rhsExpr: ts.ObjectLiteralExpression): boolean {
1132  if (lhsType === undefined) {
1133    return false;
1134  }
1135
1136  // Always check with the non-nullable variant of lhs type.
1137  lhsType = getNonNullableType(lhsType);
1138
1139  if (lhsType.isUnion()) {
1140    for (const compType of lhsType.types) {
1141      if (isObjectLiteralAssignable(compType, rhsExpr)) {
1142        return true;
1143      }
1144    }
1145  }
1146
1147  // Allow initializing with anything when the type
1148  // originates from the library.
1149  if (isAnyType(lhsType) || isLibraryType(lhsType)) {
1150    return true;
1151  }
1152
1153  // issue 13412:
1154  // Allow initializing with a dynamic object when the LHS type
1155  // is primitive or defined in standard library.
1156  if (isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) {
1157    return true;
1158  }
1159
1160  // For Partial<T>, Required<T>, Readonly<T> types, validate their argument type.
1161  if (isStdPartialType(lhsType) || isStdRequiredType(lhsType) || isStdReadonlyType(lhsType)) {
1162    if (lhsType.aliasTypeArguments && lhsType.aliasTypeArguments.length === 1) {
1163      lhsType = lhsType.aliasTypeArguments[0];
1164    } else {
1165      return false;
1166    }
1167  }
1168
1169  // Allow initializing Record objects with object initializer.
1170  // Record supports any type for a its value, but the key value
1171  // must be either a string or number literal.
1172  if (isStdRecordType(lhsType)) {
1173    return validateRecordObjectKeys(rhsExpr);
1174  }
1175
1176  return validateObjectLiteralType(lhsType) && !hasMethods(lhsType) && validateFields(lhsType, rhsExpr);
1177}
1178
1179function isDynamicObjectAssignedToStdType(lhsType: Type, rhsExpr: Expression): boolean {
1180  if (isStdLibraryType(lhsType) || isPrimitiveType(lhsType)) {
1181    const rhsSym = isCallExpression(rhsExpr)
1182      ? getSymbolOfCallExpression(rhsExpr)
1183      : typeChecker.getSymbolAtLocation(rhsExpr);
1184
1185    if (rhsSym && isLibrarySymbol(rhsSym)) return true;
1186  }
1187  return false;
1188}
1189
1190function getTargetType(type: Type): Type {
1191  return (type.getFlags() & TypeFlags.Object) &&
1192    (type as ObjectType).objectFlags & ObjectFlags.Reference ? (type as TypeReference).target : type;
1193}
1194
1195export function isLiteralType(type: Type): boolean {
1196  return type.isLiteral() || (type.flags & TypeFlags.BooleanLiteral) !== 0;
1197}
1198
1199export function validateFields(objectType: Type, objectLiteral: ObjectLiteralExpression): boolean {
1200  for (const prop of objectLiteral.properties) {
1201    if (isPropertyAssignment(prop)) {
1202      if (!validateField(objectType, prop)) {
1203        return false;
1204      }
1205    }
1206  };
1207
1208  return true;
1209}
1210
1211function validateField(type: ts.Type, prop: ts.PropertyAssignment): boolean {
1212  // Issue 15497: Use unescaped property name to find correpsponding property.
1213  const propNameSymbol = typeChecker.getSymbolAtLocation(prop.name);
1214  const propName = propNameSymbol ?
1215    ts.symbolName(propNameSymbol) :
1216    ts.isMemberName(prop.name) ?
1217      ts.idText(prop.name) :
1218      prop.name.getText();
1219  const propSym = findProperty(type, propName);
1220  if (!propSym || !propSym.declarations?.length) {
1221    return false;
1222  }
1223
1224  const propType = typeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]);
1225  const initExpr = unwrapParenthesized(prop.initializer);
1226  const rhsType = typeChecker.getTypeAtLocation(initExpr);
1227  if (ts.isObjectLiteralExpression(initExpr)) {
1228    if (!isObjectLiteralAssignable(propType, initExpr)) {
1229      return false;
1230    }
1231  } else {
1232    // Only check for structural sub-typing.
1233    if (needToDeduceStructuralIdentity(
1234      propType,
1235      rhsType,
1236      initExpr,
1237      needStrictMatchType(propType, rhsType)
1238    )) {
1239      return false;
1240    }
1241
1242    if (isWrongSendableFunctionAssignment(propType, rhsType)) {
1243      return false;
1244    }
1245  }
1246
1247  return true;
1248}
1249
1250function isSupportedTypeNodeKind(kind: SyntaxKind): boolean {
1251  return kind !== SyntaxKind.AnyKeyword && kind !== SyntaxKind.UnknownKeyword &&
1252    kind !== SyntaxKind.SymbolKeyword && kind !== SyntaxKind.IndexedAccessType &&
1253    kind !== SyntaxKind.ConditionalType && kind !== SyntaxKind.MappedType &&
1254    kind !== SyntaxKind.InferType;
1255
1256}
1257
1258export function isSupportedType(typeNode: TypeNode): boolean {
1259  if (isParenthesizedTypeNode(typeNode)) return isSupportedType(typeNode.type);
1260
1261  if (isArrayTypeNode(typeNode)) return isSupportedType(typeNode.elementType);
1262
1263  if (isTypeReferenceNode(typeNode) && typeNode.typeArguments) {
1264    for (const typeArg of typeNode.typeArguments) {
1265      if (!isSupportedType(typeArg)) { return false; }
1266    }
1267    return true;
1268  }
1269
1270  if (isUnionTypeNode(typeNode)) {
1271    for (const unionTypeElem of typeNode.types) {
1272      if (!isSupportedType(unionTypeElem)) { return false; }
1273    }
1274    return true;
1275  }
1276
1277  if (isTupleTypeNode(typeNode)) {
1278    for (const elem of typeNode.elements) {
1279      if (isTypeNode(elem) && !isSupportedType(elem)) return false;
1280      if (isNamedTupleMember(elem) && !isSupportedType(elem.type)) return false;
1281    }
1282    return true;
1283  }
1284
1285  return !isTypeLiteralNode(typeNode) && !isTypeQueryNode(typeNode) &&
1286    !isIntersectionTypeNode(typeNode) && isSupportedTypeNodeKind(typeNode.kind);
1287}
1288
1289export function isStruct(symbol: Symbol) {
1290  if (!symbol.declarations) {
1291    return false;
1292  }
1293  for (const decl of symbol.declarations) {
1294    if (isStructDeclaration(decl)) {
1295      return true;
1296    }
1297  }
1298  return false;
1299}
1300
1301function validateRecordObjectKeys(objectLiteral: ObjectLiteralExpression): boolean {
1302  for (const prop of objectLiteral.properties) {
1303    if (!prop.name) {
1304      return false;
1305    }
1306    const isValidComputedProperty = isComputedPropertyName(prop.name) && isValidComputedPropertyName(prop.name, true);
1307    if (!isStringLiteral(prop.name) && !isNumericLiteral(prop.name) && !isValidComputedProperty) {
1308      return false;
1309    }
1310  }
1311  return true;
1312}
1313
1314/* Not need in Tsc 4.9
1315export function getDecorators(node: Node): readonly Decorator[] | undefined {
1316  if (node.decorators) {
1317    return filter(node.decorators, isDecorator);
1318  }
1319}
1320*/
1321
1322export type CheckType = ((t: Type) => boolean);
1323
1324export const ES_OBJECT = "ESObject";
1325
1326export const LIMITED_STD_GLOBAL_FUNC = [
1327  "eval"
1328];
1329export const LIMITED_STD_OBJECT_API = [
1330  "__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "assign", "create",
1331  "defineProperties", "defineProperty", "freeze", "fromEntries", "getOwnPropertyDescriptor",
1332  "getOwnPropertyDescriptors", "getOwnPropertySymbols", "getPrototypeOf", "hasOwnProperty", "is",
1333  "isExtensible", "isFrozen", "isPrototypeOf", "isSealed", "preventExtensions", "propertyIsEnumerable",
1334  "seal", "setPrototypeOf"
1335];
1336export const LIMITED_STD_REFLECT_API = [
1337 "apply", "construct", "defineProperty", "deleteProperty", "getOwnPropertyDescriptor", "getPrototypeOf",
1338    "isExtensible", "preventExtensions", "setPrototypeOf"
1339];
1340export const LIMITED_STD_PROXYHANDLER_API = [
1341  "apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf",
1342  "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"
1343];
1344
1345export const FUNCTION_HAS_NO_RETURN_ERROR_CODE = 2366;
1346export const NON_RETURN_FUNCTION_DECORATORS = ["AnimatableExtend", "Builder", "Extend", "Styles" ];
1347
1348export const STANDARD_LIBRARIES = [
1349  "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts", "lib.webworker.importscripd.ts",
1350  "lib.webworker.iterable.d.ts", "lib.scripthost.d.ts", "lib.decorators.d.ts", "lib.decorators.legacy.d.ts",
1351  "lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.generator.d.ts",
1352  "lib.es2015.iterable.d.ts", "lib.es2015.promise.d.ts", "lib.es2015.proxy.d.ts", "lib.es2015.reflect.d.ts",
1353  "lib.es2015.symbol.d.ts", "lib.es2015.symbol.wellknown.d.ts", "lib.es2016.array.include.d.ts",
1354  "lib.es2017.object.d.ts", "lib.es2017.sharedmemory.d.ts", "lib.es2017.string.d.ts", "lib.es2017.intl.d.ts",
1355  "lib.es2017.typedarrays.d.ts", "lib.es2018.asyncgenerator.d.ts", "lib.es2018.asynciterable.d.ts",
1356  "lib.es2018.intl.d.ts", "lib.es2018.promise.d.ts", "lib.es2018.regexp.d.ts", "lib.es2019.array.d.ts",
1357  "lib.es2019.object.d.ts", "lib.es2019.string.d.ts", "lib.es2019.symbol.d.ts", "lib.es2019.intl.d.ts",
1358  "lib.es2020.bigint.d.ts", "lib.es2020.date.d.ts", "lib.es2020.promise.d.ts", "lib.es2020.sharedmemory.d.ts",
1359  "lib.es2020.string.d.ts", "lib.es2020.symbol.wellknown.d.ts", "lib.es2020.intl.d.ts", "lib.es2020.number.d.ts",
1360  "lib.es2021.promise.d.ts", "lib.es2021.string.d.ts", "lib.es2021.weakref.d.ts", "lib.es2021.intl.d.ts",
1361  "lib.es2022.array.d.ts", "lib.es2022.error.d.ts", "lib.es2022.intl.d.ts", "lib.es2022.object.d.ts",
1362  "lib.es2022.sharedmemory.d.ts", "lib.es2022.string.d.ts", "lib.es2022.regexp.d.ts", "lib.es2023.array.d.ts",
1363];
1364
1365export const TYPED_ARRAYS = [
1366  "Int8Array",
1367  "Uint8Array",
1368  "Uint8ClampedArray",
1369  "Int16Array",
1370  "Uint16Array",
1371  "Int32Array",
1372  "Uint32Array",
1373  "Float32Array",
1374  "Float64Array",
1375  "BigInt64Array",
1376  "BigUint64Array",
1377  ];
1378
1379let parentSymbolCache: ESMap<Symbol, string | undefined> | undefined = new Map<Symbol, string | undefined>();
1380export function getParentSymbolName(symbol: Symbol): string | undefined {
1381  parentSymbolCache = parentSymbolCache ? parentSymbolCache : new Map<Symbol, string | undefined>();
1382  const cached = parentSymbolCache.get(symbol);
1383  if (cached) {
1384    return cached;
1385  }
1386  const name = typeChecker.getFullyQualifiedName(symbol);
1387  const dotPosition = name.lastIndexOf(".");
1388  const result = (dotPosition === -1) ? undefined : name.substring(0, dotPosition);
1389  parentSymbolCache.set(symbol, result);
1390  return result;
1391}
1392
1393export function isGlobalSymbol(symbol: Symbol): boolean {
1394  const parentName = getParentSymbolName(symbol);
1395  return !parentName || parentName === "global";
1396}
1397
1398export function isSymbolAPI(symbol: Symbol): boolean {
1399  const parentName = getParentSymbolName(symbol);
1400  let name = parentName ? parentName : symbol.escapedName;
1401  return name === 'Symbol' || name === "SymbolConstructor";
1402}
1403
1404export function isStdSymbol(symbol: ts.Symbol): boolean {
1405  const name = typeChecker.getFullyQualifiedName(symbol);
1406  return name === 'Symbol' && isGlobalSymbol(symbol);
1407}
1408
1409export function isSymbolIterator(symbol: ts.Symbol): boolean {
1410  const name = symbol.name;
1411  const parName = getParentSymbolName(symbol);
1412  return (parName === 'Symbol' || parName === 'SymbolConstructor') && name === 'iterator'
1413}
1414
1415export function isSymbolIteratorExpression(expr: ts.Expression): boolean {
1416  const symbol = trueSymbolAtLocation(expr);
1417  return !!symbol && isSymbolIterator(symbol);
1418}
1419
1420export function isDefaultImport(importSpec: ImportSpecifier): boolean {
1421  return importSpec?.propertyName?.text === "default";
1422}
1423export function hasAccessModifier(decl: Declaration): boolean {
1424  const modifiers = ts.getModifiers(decl as HasModifiers); //decl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method
1425  return (
1426    !!modifiers &&
1427    (hasModifier(modifiers, SyntaxKind.PublicKeyword) ||
1428      hasModifier(modifiers, SyntaxKind.ProtectedKeyword) ||
1429      hasModifier(modifiers, SyntaxKind.PrivateKeyword))
1430  );
1431}
1432
1433export function getModifier(modifiers: readonly Modifier[] | undefined, modifierKind: SyntaxKind): Modifier | undefined {
1434  if (!modifiers) return undefined;
1435  return modifiers.find(x => x.kind === modifierKind);
1436}
1437
1438export function getAccessModifier(modifiers: readonly Modifier[] | undefined): Modifier | undefined {
1439  return getModifier(modifiers, SyntaxKind.PublicKeyword) ??
1440    getModifier(modifiers, SyntaxKind.ProtectedKeyword) ??
1441    getModifier(modifiers, SyntaxKind.PrivateKeyword);
1442}
1443
1444export function isStdRecordType(type: Type): boolean {
1445  // In TypeScript, 'Record<K, T>' is defined as type alias to a mapped type.
1446  // Thus, it should have 'aliasSymbol' and 'target' properties. The 'target'
1447  // in this case will resolve to origin 'Record' symbol.
1448  if (type.aliasSymbol) {
1449    const target = (type as TypeReference).target;
1450    if (target) {
1451      const sym = target.aliasSymbol;
1452      return !!sym && sym.getName() === "Record" && isGlobalSymbol(sym);
1453    }
1454  }
1455
1456  return false;
1457}
1458
1459export function isStdMapType(type: Type): boolean {
1460  const sym = type.symbol;
1461  return !!sym && sym.getName() === "Map" && isGlobalSymbol(sym);
1462}
1463
1464export function isStdErrorType(type: ts.Type): boolean {
1465  const symbol = type.symbol;
1466  if (!symbol) {
1467    return false;
1468  }
1469  const name = typeChecker.getFullyQualifiedName(symbol);
1470  return name === 'Error' && isGlobalSymbol(symbol);
1471}
1472
1473export function isStdPartialType(type: Type): boolean {
1474  const sym = type.aliasSymbol;
1475  return !!sym && sym.getName() === "Partial" && isGlobalSymbol(sym);
1476}
1477
1478export function isStdRequiredType(type: Type): boolean {
1479  const sym = type.aliasSymbol;
1480  return !!sym && sym.getName() === "Required" && isGlobalSymbol(sym);
1481}
1482
1483export function isStdReadonlyType(type: Type): boolean {
1484  const sym = type.aliasSymbol;
1485  return !!sym && sym.getName() === "Readonly" && isGlobalSymbol(sym);
1486}
1487
1488export function isLibraryType(type: Type): boolean {
1489  const nonNullableType = type.getNonNullableType();
1490  if (nonNullableType.isUnion()) {
1491    for (const componentType of nonNullableType.types) {
1492      if (!isLibraryType(componentType)) {
1493        return false;
1494      }
1495    }
1496    return true;
1497  }
1498  return isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol());
1499}
1500
1501export function hasLibraryType(node: Node): boolean {
1502  return isLibraryType(typeChecker.getTypeAtLocation(node));
1503}
1504
1505export function isLibrarySymbol(sym: Symbol | undefined) {
1506  if (sym && sym.declarations && sym.declarations.length > 0) {
1507    const srcFile = sym.declarations[0].getSourceFile();
1508    if (!srcFile) {
1509      return false;
1510    }
1511    const fileName = srcFile.fileName;
1512
1513    // Symbols from both *.ts and *.d.ts files should obey interop rules.
1514    // We disable such behavior for *.ts files in the test mode due to lack of 'ets'
1515    // extension support.
1516    const ext = getAnyExtensionFromPath(fileName);
1517    const isThirdPartyCode =
1518      ARKTS_IGNORE_DIRS.some(ignore => srcFilePathContainsDirectory(srcFile, ignore)) ||
1519      ARKTS_IGNORE_FILES.some(ignore => getBaseFileName(fileName) === ignore);
1520    const isEts = (ext === '.ets');
1521    const isTs = (ext === '.ts' && !srcFile.isDeclarationFile);
1522    const isStatic = (isEts || (isTs && testMode)) && !isThirdPartyCode;
1523    const isStdLib = STANDARD_LIBRARIES.includes(getBaseFileName(fileName).toLowerCase());
1524    // We still need to confirm support for certain API from the
1525    // TypeScript standard library in ArkTS. Thus, for now do not
1526    // count standard library modules as dynamic.
1527    return !isStatic && !isStdLib;
1528  }
1529
1530  return false;
1531}
1532
1533const srcFilePathComponents = new Map<SourceFile, string[]>();
1534export function srcFilePathContainsDirectory(srcFile: SourceFile, dir: string): boolean {
1535  let pathComps = srcFilePathComponents.get(srcFile);
1536  if (!pathComps) {
1537    pathComps = getPathComponents(Utils.normalizePath(srcFile.fileName));
1538    srcFilePathComponents.set(srcFile, pathComps);
1539  }
1540  for (const subdir of pathComps) {
1541    if (subdir === dir) {
1542      return true;
1543    }
1544  }
1545  return false;
1546}
1547
1548export function pathContainsDirectory(targetPath: string, dir: string): boolean {
1549  for (const subdir of getPathComponents(targetPath)) {
1550    if (subdir === dir) {
1551      return true;
1552    }
1553  }
1554  return false;
1555}
1556
1557export function getScriptKind(srcFile: SourceFile): ScriptKind {
1558  const fileName = srcFile.fileName;
1559  const ext = getAnyExtensionFromPath(fileName);
1560  switch (ext.toLowerCase()) {
1561    case Extension.Js:
1562      return ScriptKind.JS;
1563    case Extension.Jsx:
1564      return ScriptKind.JSX;
1565    case Extension.Ts:
1566      return ScriptKind.TS;
1567    case Extension.Tsx:
1568      return ScriptKind.TSX;
1569    case Extension.Json:
1570      return ScriptKind.JSON;
1571    default:
1572      return ScriptKind.Unknown;
1573  }
1574}
1575
1576export function isStdLibraryType(type: Type): boolean {
1577  return isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol());
1578}
1579
1580export function isStdLibrarySymbol(sym: Symbol | undefined) {
1581  if (sym && sym.declarations && sym.declarations.length > 0) {
1582    const srcFile = sym.declarations[0].getSourceFile();
1583    return srcFile &&
1584      STANDARD_LIBRARIES.includes(getBaseFileName(srcFile.fileName).toLowerCase());
1585  }
1586
1587  return false;
1588}
1589
1590export function isIntrinsicObjectType(type: Type): boolean {
1591  return !!(type.flags & TypeFlags.NonPrimitive);
1592}
1593
1594export function isDynamicType(type: Type | undefined): boolean | undefined {
1595  if (type === undefined) {
1596    return false;
1597  }
1598
1599  // Return 'true' if it is an object of library type initialization, otherwise
1600  // return 'false' if it is not an object of standard library type one.
1601  // In the case of standard library type we need to determine context.
1602
1603  // Check the non-nullable version of type to eliminate 'undefined' type
1604  // from the union type elements.
1605  type = type.getNonNullableType();
1606
1607
1608  if (type.isUnion()) {
1609    for (const compType of type.types) {
1610      const isDynamic = isDynamicType(compType);
1611      if (isDynamic || isDynamic === undefined) {
1612        return isDynamic;
1613      }
1614    }
1615    return false;
1616  }
1617
1618  if (isLibraryType(type)) {
1619    return true;
1620  }
1621
1622  if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !isAnyType(type)) {
1623    return false;
1624  }
1625
1626  return undefined;
1627}
1628
1629export function isObjectType(type: ts.Type): type is ts.ObjectType {
1630  return !!(type.flags & ts.TypeFlags.Object);
1631}
1632
1633export function isAnonymous(type: ts.Type): boolean {
1634  if (isObjectType(type)) {
1635    return !!(type.objectFlags & ts.ObjectFlags.Anonymous);
1636  }
1637  return false;
1638}
1639
1640
1641export function isDynamicLiteralInitializer(expr: Expression): boolean {
1642  if (!isObjectLiteralExpression(expr) && !isArrayLiteralExpression(expr)) {
1643    return false;
1644  }
1645
1646  // Handle nested literals:
1647  // { f: { ... } }
1648  let curNode: Node = expr;
1649  while (isObjectLiteralExpression(curNode) || isArrayLiteralExpression(curNode)) {
1650    const exprType = typeChecker.getContextualType(curNode);
1651    if (exprType !== undefined && !isAnonymous(exprType)) {
1652      const res = isDynamicType(exprType);
1653      if (res !== undefined) {
1654        return res;
1655      }
1656    }
1657
1658    curNode = curNode.parent;
1659    if (isPropertyAssignment(curNode)) {
1660      curNode = curNode.parent;
1661    }
1662  }
1663
1664  // Handle calls with literals:
1665  // foo({ ... })
1666  if (isCallExpression(curNode)) {
1667    const callExpr = curNode;
1668    const type = typeChecker.getTypeAtLocation(callExpr.expression);
1669
1670    // this check is a hack to fix #13474, only for tac 4.2
1671    if (isAnyType(type)) return true;
1672
1673    let sym: Symbol | undefined = type.symbol;
1674    if(isLibrarySymbol(sym)) {
1675      return true;
1676    }
1677
1678    // #13483:
1679    // x.foo({ ... }), where 'x' is a variable exported from some library:
1680    if (isPropertyAccessExpression(callExpr.expression)) {
1681      sym = typeChecker.getSymbolAtLocation(callExpr.expression.expression);
1682      if (sym && sym.getFlags() & SymbolFlags.Alias) {
1683        sym = typeChecker.getAliasedSymbol(sym);
1684        if (isLibrarySymbol(sym)) {
1685          return true;
1686        }
1687      }
1688    }
1689  }
1690
1691  // Handle property assignments with literals:
1692  // obj.f = { ... }
1693  if (isBinaryExpression(curNode)) {
1694    const binExpr = curNode;
1695    if (isPropertyAccessExpression(binExpr.left)) {
1696      const propAccessExpr = binExpr.left;
1697      const type = typeChecker.getTypeAtLocation(propAccessExpr.expression);
1698      return isLibrarySymbol(type.symbol);
1699    }
1700  }
1701
1702  return false;
1703}
1704
1705export function isEsObjectType(typeNode: ts.TypeNode | undefined): boolean {
1706  return !!typeNode && ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) &&
1707    typeNode.typeName.text === ES_OBJECT;
1708}
1709
1710export function isInsideBlock(node: ts.Node): boolean {
1711  let par = node.parent
1712  while (par) {
1713    if (ts.isBlock(par)) {
1714      return true;
1715    }
1716    par = par.parent;
1717  }
1718  return false;
1719}
1720
1721export function  isEsObjectPossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean {
1722  return ts.isVariableDeclaration(typeRef.parent);
1723}
1724
1725export function  isValueAssignableToESObject(node: ts.Node): boolean {
1726  if (ts.isArrayLiteralExpression(node) || ts.isObjectLiteralExpression(node)) {
1727    return false;
1728  }
1729  const valueType = typeChecker.getTypeAtLocation(node);
1730  return isUnsupportedType(valueType) || isAnonymousType(valueType)
1731}
1732
1733export function getVariableDeclarationTypeNode(node: Node): TypeNode | undefined {
1734  let sym = trueSymbolAtLocation(node);
1735    if (sym === undefined) {
1736      return undefined;
1737    }
1738    return getSymbolDeclarationTypeNode(sym);
1739  }
1740
1741export function getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined {
1742    const decl = getDeclaration(sym);
1743  if (!!decl && isVariableDeclaration(decl)) {
1744    return decl.type;
1745  }
1746  return undefined;
1747}
1748
1749export function hasEsObjectType(node: Node): boolean {
1750  const typeNode = getVariableDeclarationTypeNode(node);
1751  return typeNode !== undefined && isEsObjectType(typeNode);
1752}
1753
1754export function symbolHasEsObjectType(sym: ts.Symbol): boolean {
1755  const typeNode = getSymbolDeclarationTypeNode(sym);
1756  return typeNode !== undefined && isEsObjectType(typeNode);
1757}
1758
1759export function isEsObjectSymbol(sym: Symbol): boolean {
1760  const decl = getDeclaration(sym);
1761  return !!decl && isTypeAliasDeclaration(decl) && decl.name.escapedText === ES_OBJECT &&
1762    decl.type.kind === SyntaxKind.AnyKeyword;
1763}
1764
1765export function isAnonymousType(type: Type): boolean {
1766  if (type.isUnionOrIntersection()) {
1767    for (const compType of type.types) {
1768      if (isAnonymousType(compType)) {
1769        return true;
1770      }
1771    }
1772    return false;
1773  }
1774
1775  return (type.flags & TypeFlags.Object) !== 0 &&
1776    ((type as ObjectType).objectFlags & ObjectFlags.Anonymous) !== 0;
1777}
1778
1779export function getSymbolOfCallExpression(callExpr: CallExpression): Symbol | undefined {
1780  const signature = typeChecker.getResolvedSignature(callExpr);
1781  const signDecl = signature?.getDeclaration();
1782  if (signDecl && signDecl.name) {
1783    return typeChecker.getSymbolAtLocation(signDecl.name);
1784  }
1785  return undefined;
1786}
1787
1788export function typeIsRecursive(topType: Type, type: Type | undefined = undefined): boolean {
1789  if (type === undefined) {
1790    type = topType;
1791  }
1792  else if (type === topType) {
1793    return true;
1794  }
1795  else if (type.aliasSymbol) {
1796    return false;
1797  }
1798
1799  if (type.isUnion()) {
1800    for (const unionElem of type.types) {
1801      if (typeIsRecursive(topType, unionElem)) {
1802        return true;
1803      }
1804    }
1805  }
1806  if (type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.Reference) {
1807    const typeArgs = typeChecker.getTypeArguments(type as TypeReference);
1808    if (typeArgs) {
1809      for (const typeArg of typeArgs) {
1810        if (typeIsRecursive(topType, typeArg)) {
1811          return true;
1812        }
1813      }
1814    }
1815  }
1816  return false;
1817}
1818
1819export function getTypeOrTypeConstraintAtLocation(expr: ts.Expression): ts.Type {
1820  let type = typeChecker.getTypeAtLocation(expr);
1821  if (type.isTypeParameter()) {
1822    let constraint = type.getConstraint();
1823    if (constraint) {
1824      return constraint;
1825    }
1826  }
1827  return type;
1828}
1829
1830function areCompatibleFunctionals(lhsType: ts.Type, rhsType: ts.Type): boolean {
1831  return (
1832    (isStdFunctionType(lhsType) || isFunctionalType(lhsType)) &&
1833    (isStdFunctionType(rhsType) || isFunctionalType(rhsType))
1834  );
1835}
1836
1837function isFunctionalType(type: ts.Type): boolean {
1838  const callSigns = type.getCallSignatures();
1839  return callSigns && callSigns.length > 0;
1840}
1841
1842function isStdFunctionType(type: ts.Type): boolean {
1843  const sym = type.getSymbol();
1844  return !!sym && sym.getName() === 'Function' && isGlobalSymbol(sym);
1845}
1846
1847export function isStdBigIntType(type: ts.Type): boolean {
1848  const sym = type.symbol;
1849  return !!sym && sym.getName() === 'BigInt' && isGlobalSymbol(sym);
1850}
1851
1852export function isStdNumberType(type: ts.Type): boolean {
1853  const sym = type.symbol;
1854  return !!sym && sym.getName() === 'Number' && isGlobalSymbol(sym);
1855}
1856
1857export function isStdBooleanType(type: ts.Type): boolean {
1858  const sym = type.symbol;
1859  return !!sym && sym.getName() === 'Boolean' && isGlobalSymbol(sym);
1860}
1861
1862export function isEnumStringLiteral(expr: ts.Expression): boolean {
1863  const symbol = trueSymbolAtLocation(expr);
1864  const isEnumMember = !!symbol && !!(symbol.flags & ts.SymbolFlags.EnumMember);
1865  const type = typeChecker.getTypeAtLocation(expr);
1866  const isStringEnumLiteral = isEnumType(type) && !!(type.flags & ts.TypeFlags.StringLiteral);
1867  return isEnumMember && isStringEnumLiteral;
1868}
1869
1870export function isValidComputedPropertyName(computedProperty: ComputedPropertyName, isRecordObjectInitializer = false): boolean {
1871  const expr = computedProperty.expression;
1872  if (!isRecordObjectInitializer) {
1873    if (isSymbolIteratorExpression(expr)) {
1874      return true;
1875    }
1876  }
1877  return isStringLiteralLike(expr) || isEnumStringLiteral(computedProperty.expression);
1878}
1879
1880export function isAllowedIndexSignature(node: ts.IndexSignatureDeclaration): boolean {
1881
1882  /*
1883   * For now, relax index signature only for specific array-like types
1884   * with the following signature: 'collections.Array<T>.[_: number]: T'.
1885   */
1886
1887  if (node.parameters.length !== 1) {
1888    return false;
1889  }
1890
1891  const paramType = typeChecker.getTypeAtLocation(node.parameters[0]);
1892  if ((paramType.flags & ts.TypeFlags.Number) === 0) {
1893    return false;
1894  }
1895
1896  return isArkTSCollectionsArrayLikeDeclaration(node.parent);
1897}
1898
1899export function isArkTSCollectionsArrayLikeType(type: ts.Type): boolean {
1900  const symbol = type.aliasSymbol ?? type.getSymbol();
1901  if (symbol?.declarations === undefined || symbol.declarations.length < 1) {
1902    return false;
1903  }
1904
1905  return isArkTSCollectionsArrayLikeDeclaration(symbol.declarations[0]);
1906}
1907
1908function isArkTSCollectionsArrayLikeDeclaration(decl: ts.Declaration): boolean {
1909  if (!isArkTSCollectionsClassOrInterfaceDeclaration(decl)) {
1910    return false;
1911  }
1912  if (!ts.hasIndexSignature(typeChecker.getTypeAtLocation(decl))) {
1913    return false;
1914  }
1915  return true;
1916}
1917
1918export function isArkTSCollectionsClassOrInterfaceDeclaration(decl: ts.Node): boolean {
1919  if (!ts.isClassDeclaration(decl) && !ts.isInterfaceDeclaration(decl) || !decl.name) {
1920    return false;
1921  }
1922  if (!ts.isModuleBlock(decl.parent) || decl.parent.parent.name.text !== COLLECTIONS_NAMESPACE) {
1923    return false;
1924  }
1925  if (getBaseFileName(decl.getSourceFile().fileName).toLowerCase() !== ARKTS_COLLECTIONS_D_ETS) {
1926    return false;
1927  }
1928  return true;
1929}
1930
1931export function getDecoratorName(decorator: ts.Decorator): string {
1932  let decoratorName = '';
1933  if (ts.isIdentifier(decorator.expression)) {
1934    decoratorName = decorator.expression.text;
1935  } else if (ts.isCallExpression(decorator.expression) && ts.isIdentifier(decorator.expression.expression)) {
1936    decoratorName = decorator.expression.expression.text;
1937  }
1938  return decoratorName;
1939}
1940
1941export function unwrapParenthesizedTypeNode(typeNode: ts.TypeNode): ts.TypeNode {
1942  let unwrappedTypeNode = typeNode;
1943  while (ts.isParenthesizedTypeNode(unwrappedTypeNode)) {
1944    unwrappedTypeNode = unwrappedTypeNode.type;
1945  }
1946  return unwrappedTypeNode;
1947}
1948
1949export function isSendableTypeNode(typeNode: ts.TypeNode, isShared: boolean = false): boolean {
1950
1951  /*
1952   * In order to correctly identify the usage of the enum member or
1953   * const enum in type annotation, we need to handle union type and
1954   * type alias cases by processing the type node and checking the
1955   * symbol in case of type reference node.
1956   */
1957
1958  typeNode = unwrapParenthesizedTypeNode(typeNode);
1959
1960  // Only a sendable union type is supported
1961  if (ts.isUnionTypeNode(typeNode)) {
1962    return typeNode.types.every((elemType) => {
1963      return isSendableTypeNode(elemType, isShared);
1964    });
1965  }
1966
1967  const sym = ts.isTypeReferenceNode(typeNode) ?
1968    trueSymbolAtLocation(typeNode.typeName) :
1969    undefined;
1970
1971  if (sym && sym.getFlags() & ts.SymbolFlags.TypeAlias) {
1972    const typeDecl = getDeclaration(sym);
1973    if (typeDecl && ts.isTypeAliasDeclaration(typeDecl)) {
1974      return isSendableTypeNode(typeDecl.type, isShared);
1975    }
1976  }
1977
1978  // Const enum type is supported
1979  if (isConstEnum(sym)) {
1980    return true;
1981  }
1982  const type: ts.Type = typeChecker.getTypeFromTypeNode(typeNode);
1983
1984  // In shared module, literal forms of primitive data types can be exported
1985  if (isShared && isPurePrimitiveLiteralType(type)) {
1986    return true;
1987  }
1988
1989  return isSendableType(type);
1990}
1991
1992export function isSendableType(type: ts.Type): boolean {
1993  if ((type.flags & (ts.TypeFlags.Boolean | ts.TypeFlags.Number | ts.TypeFlags.String |
1994    ts.TypeFlags.BigInt | ts.TypeFlags.Null | ts.TypeFlags.Undefined |
1995    ts.TypeFlags.TypeParameter)) !== 0) {
1996    return true;
1997  }
1998  if (isSendableTypeAlias(type)) {
1999    return true;
2000  }
2001  if (isSendableFunction(type)) {
2002    return true;
2003  }
2004
2005  return isSendableClassOrInterface(type);
2006}
2007
2008export function isShareableType(tsType: ts.Type): boolean {
2009  const sym = tsType.getSymbol();
2010  if (isConstEnum(sym)) {
2011    return true;
2012  }
2013
2014  if (tsType.isUnion()) {
2015    return tsType.types.every((elemType) => {
2016      return isShareableType(elemType);
2017    });
2018  }
2019
2020  if (isPurePrimitiveLiteralType(tsType)) {
2021    return true;
2022  }
2023
2024  return isSendableType(tsType);
2025}
2026
2027export function isSendableClassOrInterface(type: ts.Type): boolean {
2028  const sym = type.getSymbol();
2029  if (!sym) {
2030    return false;
2031  }
2032
2033  const targetType = reduceReference(type);
2034
2035  // class with @Sendable decorator
2036  if (targetType.isClass()) {
2037    if (sym.declarations?.length) {
2038      const decl = sym.declarations[0];
2039      if (ts.isClassDeclaration(decl)) {
2040        return hasSendableDecorator(decl);
2041      }
2042    }
2043  }
2044  // ISendable interface, or a class/interface that implements/extends ISendable interface
2045  return isOrDerivedFrom(type, isISendableInterface);
2046}
2047
2048export function typeContainsSendableClassOrInterface(type: ts.Type): boolean {
2049  // Only check type contains sendable class / interface
2050  if ((type.flags & ts.TypeFlags.Union) !== 0) {
2051    return !!(type as ts.UnionType)?.types?.some((type) => {
2052      return typeContainsSendableClassOrInterface(type);
2053    });
2054  }
2055
2056  return isSendableClassOrInterface(type);
2057}
2058
2059export function typeContainsNonSendableClassOrInterface(type: ts.Type): boolean {
2060  if (type.isUnion()) {
2061    return type.types.some((compType) => {
2062      return typeContainsNonSendableClassOrInterface(compType);
2063    });
2064  }
2065  type = reduceReference(type);
2066  return type.isClassOrInterface() && !isSendableClassOrInterface(type);
2067}
2068
2069export function isConstEnum(sym: ts.Symbol | undefined): boolean {
2070  return !!sym && sym.flags === ts.SymbolFlags.ConstEnum;
2071}
2072
2073export function isSendableUnionType(type: ts.UnionType): boolean {
2074  const types = type?.types;
2075  if (!types) {
2076    return false;
2077  }
2078
2079  return types.every((type) => {
2080    return isSendableType(type);
2081  });
2082}
2083
2084export function hasSendableDecorator(decl: ts.ClassDeclaration | ts.FunctionDeclaration | ts.TypeAliasDeclaration): boolean {
2085  const decorators = ts.getAllDecorators(decl);
2086  return decorators !== undefined && decorators.some((x) => {
2087    return getDecoratorName(x) === SENDABLE_DECORATOR;
2088  });
2089}
2090
2091export function getNonSendableDecorators(decl: ts.ClassDeclaration | ts.FunctionDeclaration | ts.TypeAliasDeclaration): ts.Decorator[] | undefined {
2092  const decorators = ts.getAllDecorators(decl);
2093  return decorators?.filter((x) => {
2094    return getDecoratorName(x) !== SENDABLE_DECORATOR;
2095  });
2096}
2097
2098export function getSendableDecorator(decl: ts.ClassDeclaration | ts.FunctionDeclaration | ts.TypeAliasDeclaration): ts.Decorator | undefined {
2099  const decorators = ts.getAllDecorators(decl);
2100  return decorators?.find((x) => {
2101    return getDecoratorName(x) === SENDABLE_DECORATOR;
2102  });
2103}
2104
2105export function getDecoratorsIfInSendableClass(declaration: ts.HasDecorators): readonly ts.Decorator[] | undefined {
2106  const classNode = getClassNodeFromDeclaration(declaration);
2107  if (classNode === undefined || !hasSendableDecorator(classNode)) {
2108    return undefined;
2109  }
2110  return ts.getDecorators(declaration);
2111}
2112
2113function getClassNodeFromDeclaration(declaration: ts.HasDecorators): ts.ClassDeclaration | undefined {
2114  if (declaration.kind === ts.SyntaxKind.Parameter) {
2115    return ts.isClassDeclaration(declaration.parent.parent) ? declaration.parent.parent : undefined;
2116  }
2117  return ts.isClassDeclaration(declaration.parent) ? declaration.parent : undefined;
2118}
2119
2120export function isISendableInterface(type: ts.Type): boolean {
2121  const symbol = type.aliasSymbol ?? type.getSymbol();
2122  if (symbol?.declarations === undefined || symbol.declarations.length < 1) {
2123    return false;
2124  }
2125
2126  return isArkTSISendableDeclaration(symbol.declarations[0]);
2127}
2128
2129function isArkTSISendableDeclaration(decl: ts.Declaration): boolean {
2130    if (!ts.isInterfaceDeclaration(decl) || !decl.name || decl.name.text !== ISENDABLE_TYPE) {
2131      return false;
2132    }
2133
2134    if (!ts.isModuleBlock(decl.parent) || decl.parent.parent.name.text !== LANG_NAMESPACE) {
2135      return false;
2136    }
2137
2138    if (getBaseFileName(decl.getSourceFile().fileName).toLowerCase() !== ARKTS_LANG_D_ETS) {
2139      return false;
2140    }
2141
2142    return true;
2143}
2144
2145export function isSharedModule(sourceFile: ts.SourceFile): boolean {
2146  const statements = sourceFile.statements;
2147  for (let statement of statements) {
2148    if (ts.isImportDeclaration(statement)) {
2149      continue;
2150    }
2151
2152    return (
2153      ts.isExpressionStatement(statement) &&
2154      ts.isStringLiteral(statement.expression) &&
2155      statement.expression.text === USE_SHARED
2156    );
2157  }
2158
2159  return false;
2160}
2161
2162export function getDeclarationNode(node: ts.Node): ts.Declaration | undefined {
2163  const sym = trueSymbolAtLocation(node);
2164  return getDeclaration(sym);
2165}
2166
2167function isFunctionLikeDeclaration(node: ts.Declaration): boolean {
2168  return ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) ||
2169    ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node) || ts.isConstructorDeclaration(node) ||
2170    ts.isFunctionExpression(node) || ts.isArrowFunction(node);
2171}
2172
2173export function isShareableEntity(node: ts.Node): boolean {
2174  const decl = getDeclarationNode(node);
2175  const typeNode = (decl as any)?.type;
2176  return (typeNode && !isFunctionLikeDeclaration(decl!)) ?
2177    isSendableTypeNode(typeNode, true) :
2178    isShareableType(typeChecker.getTypeAtLocation(decl ? decl : node));
2179}
2180
2181export function isSendableClassOrInterfaceEntity(node: ts.Node): boolean {
2182  const decl = getDeclarationNode(node);
2183  if (!decl) {
2184    return false;
2185  }
2186
2187  if (ts.isClassDeclaration(decl)) {
2188    return hasSendableDecorator(decl);
2189  }
2190
2191  if (ts.isInterfaceDeclaration(decl)) {
2192    return isOrDerivedFrom(typeChecker.getTypeAtLocation(decl), isISendableInterface);
2193  }
2194  return false;
2195}
2196
2197export function isInImportWhiteList(resolvedModule: ResolvedModuleFull): boolean {
2198  if (
2199    !resolvedModule.resolvedFileName ||
2200    ts.getBaseFileName(resolvedModule.resolvedFileName) !== ARKTS_LANG_D_ETS &&
2201    ts.getBaseFileName(resolvedModule.resolvedFileName) !== ARKTS_COLLECTIONS_D_ETS
2202  ) {
2203    return false;
2204  }
2205  return true;
2206}
2207
2208// If it is an overloaded function, all declarations for that function are found
2209export function hasSendableDecoratorFunctionOverload(decl: ts.FunctionDeclaration): boolean {
2210  const decorators = getFunctionOverloadDecorators(decl);
2211  return !!decorators?.some((x) => {
2212    return getDecoratorName(x) === SENDABLE_DECORATOR;
2213  });
2214}
2215
2216function getFunctionOverloadDecorators(funcDecl: ts.FunctionDeclaration): readonly ts.Decorator[] | undefined {
2217  const decls = funcDecl.symbol.getDeclarations();
2218  if (!decls?.length) {
2219    return undefined;
2220  }
2221  let result: ts.Decorator[] = [];
2222  decls.forEach((decl) => {
2223    if (!ts.isFunctionDeclaration(decl)) {
2224      return;
2225    }
2226    const decorators = ts.getAllDecorators(decl);
2227    if (decorators) {
2228      result = result.concat(decorators);
2229    }
2230  });
2231  return result.length ? result : undefined;
2232}
2233
2234export function isSendableFunction(type: ts.Type): boolean {
2235  const callSigns = type.getCallSignatures();
2236  if (!callSigns?.length) {
2237    return false;
2238  }
2239  const decl = callSigns[0].declaration;
2240  if (!decl || !ts.isFunctionDeclaration(decl)) {
2241    return false;
2242  }
2243  return hasSendableDecoratorFunctionOverload(decl);
2244}
2245
2246
2247export function isSendableTypeAlias(type: ts.Type): boolean {
2248  const decl = getTypsAliasOriginalDecl(type);
2249  return !!decl && hasSendableDecorator(decl);
2250}
2251
2252export function hasSendableTypeAlias(type: ts.Type) :boolean {
2253  if (type.isUnion()) {
2254    return type.types.some((compType) => {
2255      return hasSendableTypeAlias(compType);
2256    });
2257  };
2258  return isSendableTypeAlias(type);
2259}
2260
2261export function isNonSendableFunctionTypeAlias(type: ts.Type): boolean {
2262  const decl = getTypsAliasOriginalDecl(type);
2263  return !!decl && ts.isFunctionTypeNode(decl.type) && !hasSendableDecorator(decl);
2264}
2265
2266// If the alias refers to another alias, the search continues
2267function getTypsAliasOriginalDecl(type: ts.Type): ts.TypeAliasDeclaration | undefined {
2268  if (!type.aliasSymbol) {
2269    return undefined;
2270  }
2271  const decl = getDeclaration(type.aliasSymbol);
2272  if (!decl || !ts.isTypeAliasDeclaration(decl)) {
2273    return undefined;
2274  }
2275  if (ts.isTypeReferenceNode(decl.type)) {
2276    const targetType = typeChecker.getTypeAtLocation(decl.type.typeName);
2277    if (targetType.aliasSymbol && (targetType.aliasSymbol.getFlags() & ts.SymbolFlags.TypeAlias)) {
2278      return getTypsAliasOriginalDecl(targetType);
2279    }
2280  }
2281  return decl;
2282}
2283
2284// not allow 'lhsType' contains 'sendable typeAlias' && 'rhsType' contains 'non-sendable function/non-sendable function typeAlias'
2285export function isWrongSendableFunctionAssignment(
2286  lhsType: ts.Type,
2287  rhsType: ts.Type
2288): boolean {
2289  lhsType = getNonNullableType(lhsType);
2290  rhsType = getNonNullableType(rhsType);
2291  if (!hasSendableTypeAlias(lhsType)) {
2292    return false;
2293  }
2294
2295  if (rhsType.isUnion()) {
2296    return rhsType.types.some((compType) => {
2297      return isInvalidSendableFunctionAssignmentType(compType);
2298    });
2299  }
2300  return isInvalidSendableFunctionAssignmentType(rhsType);
2301}
2302
2303function isInvalidSendableFunctionAssignmentType(type: ts.Type): boolean {
2304  if (type.aliasSymbol) {
2305    return isNonSendableFunctionTypeAlias(type);
2306  }
2307  if (isFunctionalType(type)) {
2308    return !isSendableFunction(type);
2309  }
2310  return false;
2311}
2312
2313// Search for and save the exported declaration in the specified file, re-exporting another module will not be included.
2314export function searchFileExportDecl(sourceFile: ts.SourceFile, targetDecls?: ts.SyntaxKind[]): Set<ts.Node> {
2315  const exportDeclSet = new Set<ts.Node>();
2316  const appendDecl = (decl: ts.Node | undefined): void => {
2317    if (
2318      !decl ||
2319       targetDecls && !targetDecls.includes(decl.kind)
2320    ) {
2321      return;
2322    }
2323    exportDeclSet.add(decl);
2324  };
2325
2326  sourceFile.statements.forEach((statement: ts.Statement) => {
2327    if (ts.isExportAssignment(statement)) {
2328      // handle the case:"export default declName;"
2329      if (statement.isExportEquals) {
2330        return;
2331      }
2332      appendDecl(getDeclarationNode(statement.expression));
2333    } else if (ts.isExportDeclaration(statement)) {
2334      // handle the case:"export { declName1, declName2 };"
2335      if (!statement.exportClause || !ts.isNamedExports(statement.exportClause)) {
2336        return;
2337      }
2338      statement.exportClause.elements.forEach((specifier) => {
2339        appendDecl(getDeclarationNode(specifier.propertyName ?? specifier.name));
2340      });
2341    } else if (ts.canHaveModifiers(statement)) {
2342      // handle the case:"export const/class/function... decalName;"
2343      if (!hasModifier(ts.getModifiers(statement), ts.SyntaxKind.ExportKeyword)) {
2344        return;
2345      }
2346      if (!ts.isVariableStatement(statement)) {
2347        appendDecl(statement);
2348        return;
2349      }
2350      for (const exportDecl of statement.declarationList.declarations) {
2351        appendDecl(exportDecl);
2352      }
2353    }
2354  });
2355  return exportDeclSet;
2356}
2357
2358let normalizedPathCache: ESMap<string, string> | undefined = new Map<string, string>();
2359export function normalizePath(path: string): string {
2360  normalizedPathCache = normalizedPathCache ? normalizedPathCache : new Map<string, string>();
2361  const cached = normalizedPathCache.get(path);
2362  if (cached) {
2363    return cached;
2364  }
2365  const normalized = ts.normalizePath(path);
2366  normalizedPathCache.set(path, normalized);
2367  return normalized;
2368}
2369
2370export function clearUtilsGlobalvariables(): void {
2371  parentSymbolCache?.clear();
2372  parentSymbolCache = undefined;
2373  normalizedPathCache?.clear();
2374  normalizedPathCache = undefined;
2375}
2376}
2377}
2378}
2379