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