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