1861 lines
62 KiB
Go
1861 lines
62 KiB
Go
package checker
|
|
|
|
import (
|
|
"cmp"
|
|
"slices"
|
|
"strings"
|
|
"sync"
|
|
"unicode/utf8"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/module"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
func NewDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
|
|
var file *ast.SourceFile
|
|
var loc core.TextRange
|
|
if node != nil {
|
|
file = ast.GetSourceFileOfNode(node)
|
|
loc = scanner.GetErrorRangeForNode(file, node)
|
|
}
|
|
return ast.NewDiagnostic(file, loc, message, args...)
|
|
}
|
|
|
|
func NewDiagnosticChainForNode(chain *ast.Diagnostic, node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
|
|
if chain != nil {
|
|
return ast.NewDiagnosticChain(chain, message, args...)
|
|
}
|
|
return NewDiagnosticForNode(node, message, args...)
|
|
}
|
|
|
|
func findInMap[K comparable, V any](m map[K]V, predicate func(V) bool) V {
|
|
for _, value := range m {
|
|
if predicate(value) {
|
|
return value
|
|
}
|
|
}
|
|
return *new(V)
|
|
}
|
|
|
|
func isCompoundAssignment(token ast.Kind) bool {
|
|
return token >= ast.KindFirstCompoundAssignment && token <= ast.KindLastCompoundAssignment
|
|
}
|
|
|
|
func tokenIsIdentifierOrKeyword(token ast.Kind) bool {
|
|
return token >= ast.KindIdentifier
|
|
}
|
|
|
|
func tokenIsIdentifierOrKeywordOrGreaterThan(token ast.Kind) bool {
|
|
return token == ast.KindGreaterThanToken || tokenIsIdentifierOrKeyword(token)
|
|
}
|
|
|
|
func hasOverrideModifier(node *ast.Node) bool {
|
|
return ast.HasSyntacticModifier(node, ast.ModifierFlagsOverride)
|
|
}
|
|
|
|
func hasAbstractModifier(node *ast.Node) bool {
|
|
return ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract)
|
|
}
|
|
|
|
func hasAmbientModifier(node *ast.Node) bool {
|
|
return ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient)
|
|
}
|
|
|
|
func hasAsyncModifier(node *ast.Node) bool {
|
|
return ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync)
|
|
}
|
|
|
|
func getSelectedModifierFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags {
|
|
return node.ModifierFlags() & flags
|
|
}
|
|
|
|
func hasReadonlyModifier(node *ast.Node) bool {
|
|
return ast.HasModifier(node, ast.ModifierFlagsReadonly)
|
|
}
|
|
|
|
func isStaticPrivateIdentifierProperty(s *ast.Symbol) bool {
|
|
return s.ValueDeclaration != nil && ast.IsPrivateIdentifierClassElementDeclaration(s.ValueDeclaration) && ast.IsStatic(s.ValueDeclaration)
|
|
}
|
|
|
|
func isEmptyObjectLiteral(expression *ast.Node) bool {
|
|
return expression.Kind == ast.KindObjectLiteralExpression && len(expression.AsObjectLiteralExpression().Properties.Nodes) == 0
|
|
}
|
|
|
|
type AssignmentKind int32
|
|
|
|
const (
|
|
AssignmentKindNone AssignmentKind = iota
|
|
AssignmentKindDefinite
|
|
AssignmentKindCompound
|
|
)
|
|
|
|
type AssignmentTarget = ast.Node // BinaryExpression | PrefixUnaryExpression | PostfixUnaryExpression | ForInOrOfStatement
|
|
|
|
func getAssignmentTargetKind(node *ast.Node) AssignmentKind {
|
|
target := ast.GetAssignmentTarget(node)
|
|
if target == nil {
|
|
return AssignmentKindNone
|
|
}
|
|
switch target.Kind {
|
|
case ast.KindBinaryExpression:
|
|
binaryOperator := target.AsBinaryExpression().OperatorToken.Kind
|
|
if binaryOperator == ast.KindEqualsToken || ast.IsLogicalOrCoalescingAssignmentOperator(binaryOperator) {
|
|
return AssignmentKindDefinite
|
|
}
|
|
return AssignmentKindCompound
|
|
case ast.KindPrefixUnaryExpression, ast.KindPostfixUnaryExpression:
|
|
return AssignmentKindCompound
|
|
case ast.KindForInStatement, ast.KindForOfStatement:
|
|
return AssignmentKindDefinite
|
|
}
|
|
panic("Unhandled case in getAssignmentTargetKind")
|
|
}
|
|
|
|
func isDeleteTarget(node *ast.Node) bool {
|
|
if !ast.IsAccessExpression(node) {
|
|
return false
|
|
}
|
|
node = ast.WalkUpParenthesizedExpressions(node.Parent)
|
|
return node != nil && node.Kind == ast.KindDeleteExpression
|
|
}
|
|
|
|
func isInCompoundLikeAssignment(node *ast.Node) bool {
|
|
target := ast.GetAssignmentTarget(node)
|
|
return target != nil && ast.IsAssignmentExpression(target /*excludeCompoundAssignment*/, true) && isCompoundLikeAssignment(target)
|
|
}
|
|
|
|
func isCompoundLikeAssignment(assignment *ast.Node) bool {
|
|
right := ast.SkipParentheses(assignment.AsBinaryExpression().Right)
|
|
return right.Kind == ast.KindBinaryExpression && isShiftOperatorOrHigher(right.AsBinaryExpression().OperatorToken.Kind)
|
|
}
|
|
|
|
func getAssertedTypeNode(node *ast.Node) *ast.Node {
|
|
switch node.Kind {
|
|
case ast.KindAsExpression:
|
|
return node.AsAsExpression().Type
|
|
case ast.KindSatisfiesExpression:
|
|
return node.AsSatisfiesExpression().Type
|
|
case ast.KindTypeAssertionExpression:
|
|
return node.AsTypeAssertion().Type
|
|
}
|
|
panic("Unhandled case in getAssertedTypeNode")
|
|
}
|
|
|
|
func isConstAssertion(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindAsExpression, ast.KindTypeAssertionExpression:
|
|
return isConstTypeReference(getAssertedTypeNode(node))
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isConstTypeReference(node *ast.Node) bool {
|
|
return ast.IsTypeReferenceNode(node) && len(node.TypeArguments()) == 0 && ast.IsIdentifier(node.AsTypeReferenceNode().TypeName) && node.AsTypeReferenceNode().TypeName.Text() == "const"
|
|
}
|
|
|
|
func GetSingleVariableOfVariableStatement(node *ast.Node) *ast.Node {
|
|
if !ast.IsVariableStatement(node) {
|
|
return nil
|
|
}
|
|
return core.FirstOrNil(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes)
|
|
}
|
|
|
|
func isTypeReferenceIdentifier(node *ast.Node) bool {
|
|
for node.Parent.Kind == ast.KindQualifiedName {
|
|
node = node.Parent
|
|
}
|
|
return ast.IsTypeReferenceNode(node.Parent)
|
|
}
|
|
|
|
func IsInTypeQuery(node *ast.Node) bool {
|
|
// TypeScript 1.0 spec (April 2014): 3.6.3
|
|
// A type query consists of the keyword typeof followed by an expression.
|
|
// The expression is restricted to a single identifier or a sequence of identifiers separated by periods
|
|
return ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult {
|
|
switch n.Kind {
|
|
case ast.KindTypeQuery:
|
|
return ast.FindAncestorTrue
|
|
case ast.KindIdentifier, ast.KindQualifiedName:
|
|
return ast.FindAncestorFalse
|
|
}
|
|
return ast.FindAncestorQuit
|
|
}) != nil
|
|
}
|
|
|
|
func getNameFromImportDeclaration(node *ast.Node) *ast.Node {
|
|
switch node.Kind {
|
|
case ast.KindImportSpecifier:
|
|
return node.AsImportSpecifier().Name()
|
|
case ast.KindNamespaceImport:
|
|
return node.AsNamespaceImport().Name()
|
|
case ast.KindImportClause:
|
|
return node.AsImportClause().Name()
|
|
case ast.KindImportEqualsDeclaration:
|
|
return node.AsImportEqualsDeclaration().Name()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func nodeCanBeDecorated(useLegacyDecorators bool, node *ast.Node, parent *ast.Node, grandparent *ast.Node) bool {
|
|
// private names cannot be used with decorators yet
|
|
if useLegacyDecorators && node.Name() != nil && ast.IsPrivateIdentifier(node.Name()) {
|
|
return false
|
|
}
|
|
switch node.Kind {
|
|
case ast.KindClassDeclaration:
|
|
// class declarations are valid targets
|
|
return true
|
|
case ast.KindClassExpression:
|
|
// class expressions are valid targets for native decorators
|
|
return !useLegacyDecorators
|
|
case ast.KindPropertyDeclaration:
|
|
// property declarations are valid if their parent is a class declaration.
|
|
return parent != nil && (useLegacyDecorators && ast.IsClassDeclaration(parent) ||
|
|
!useLegacyDecorators && ast.IsClassLike(parent) && !hasAbstractModifier(node) && !hasAmbientModifier(node))
|
|
case ast.KindGetAccessor, ast.KindSetAccessor, ast.KindMethodDeclaration:
|
|
// if this method has a body and its parent is a class declaration, this is a valid target.
|
|
return parent != nil && node.Body() != nil && (useLegacyDecorators && ast.IsClassDeclaration(parent) ||
|
|
!useLegacyDecorators && ast.IsClassLike(parent))
|
|
case ast.KindParameter:
|
|
// TODO(rbuckton): Parameter decorator support for ES decorators must wait until it is standardized
|
|
if !useLegacyDecorators {
|
|
return false
|
|
}
|
|
// if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target.
|
|
return parent != nil && parent.Body() != nil &&
|
|
(parent.Kind == ast.KindConstructor || parent.Kind == ast.KindMethodDeclaration || parent.Kind == ast.KindSetAccessor) &&
|
|
ast.GetThisParameter(parent) != node && grandparent != nil && grandparent.Kind == ast.KindClassDeclaration
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func canHaveLocals(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindArrowFunction, ast.KindBlock, ast.KindCallSignature, ast.KindCaseBlock, ast.KindCatchClause,
|
|
ast.KindClassStaticBlockDeclaration, ast.KindConditionalType, ast.KindConstructor, ast.KindConstructorType,
|
|
ast.KindConstructSignature, ast.KindForStatement, ast.KindForInStatement, ast.KindForOfStatement, ast.KindFunctionDeclaration,
|
|
ast.KindFunctionExpression, ast.KindFunctionType, ast.KindGetAccessor, ast.KindIndexSignature,
|
|
ast.KindJSDocSignature, ast.KindMappedType,
|
|
ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindModuleDeclaration, ast.KindSetAccessor, ast.KindSourceFile,
|
|
ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isShorthandAmbientModuleSymbol(moduleSymbol *ast.Symbol) bool {
|
|
return isShorthandAmbientModule(moduleSymbol.ValueDeclaration)
|
|
}
|
|
|
|
func isShorthandAmbientModule(node *ast.Node) bool {
|
|
// The only kind of module that can be missing a body is a shorthand ambient module.
|
|
return node != nil && node.Kind == ast.KindModuleDeclaration && node.AsModuleDeclaration().Body == nil
|
|
}
|
|
|
|
func getAliasDeclarationFromName(node *ast.Node) *ast.Node {
|
|
switch node.Parent.Kind {
|
|
case ast.KindImportClause, ast.KindImportSpecifier, ast.KindNamespaceImport, ast.KindExportSpecifier, ast.KindExportAssignment, ast.KindJSExportAssignment,
|
|
ast.KindImportEqualsDeclaration, ast.KindNamespaceExport:
|
|
return node.Parent
|
|
case ast.KindQualifiedName:
|
|
return getAliasDeclarationFromName(node.Parent)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func entityNameToString(name *ast.Node) string {
|
|
switch name.Kind {
|
|
case ast.KindThisKeyword:
|
|
return "this"
|
|
case ast.KindIdentifier, ast.KindPrivateIdentifier:
|
|
if ast.NodeIsSynthesized(name) {
|
|
return name.Text()
|
|
}
|
|
return scanner.GetTextOfNode(name)
|
|
case ast.KindQualifiedName:
|
|
return entityNameToString(name.AsQualifiedName().Left) + "." + entityNameToString(name.AsQualifiedName().Right)
|
|
case ast.KindPropertyAccessExpression:
|
|
return entityNameToString(name.AsPropertyAccessExpression().Expression) + "." + entityNameToString(name.AsPropertyAccessExpression().Name())
|
|
case ast.KindJsxNamespacedName:
|
|
return entityNameToString(name.AsJsxNamespacedName().Namespace) + ":" + entityNameToString(name.AsJsxNamespacedName().Name())
|
|
}
|
|
panic("Unhandled case in entityNameToString")
|
|
}
|
|
|
|
func getContainingQualifiedNameNode(node *ast.Node) *ast.Node {
|
|
for ast.IsQualifiedName(node.Parent) {
|
|
node = node.Parent
|
|
}
|
|
return node
|
|
}
|
|
|
|
func isSideEffectImport(node *ast.Node) bool {
|
|
ancestor := ast.FindAncestor(node, ast.IsImportDeclaration)
|
|
return ancestor != nil && ancestor.AsImportDeclaration().ImportClause == nil
|
|
}
|
|
|
|
func getExternalModuleRequireArgument(node *ast.Node) *ast.Node {
|
|
if ast.IsVariableDeclarationInitializedToRequire(node) {
|
|
return node.AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isRightSideOfAccessExpression(node *ast.Node) bool {
|
|
return node.Parent != nil && (ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node ||
|
|
ast.IsElementAccessExpression(node.Parent) && node.Parent.AsElementAccessExpression().ArgumentExpression == node)
|
|
}
|
|
|
|
func isTopLevelInExternalModuleAugmentation(node *ast.Node) bool {
|
|
return node != nil && node.Parent != nil && ast.IsModuleBlock(node.Parent) && ast.IsExternalModuleAugmentation(node.Parent.Parent)
|
|
}
|
|
|
|
func isSyntacticDefault(node *ast.Node) bool {
|
|
return (ast.IsExportAssignment(node) && !node.AsExportAssignment().IsExportEquals) ||
|
|
ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) ||
|
|
ast.IsExportSpecifier(node) ||
|
|
ast.IsNamespaceExport(node)
|
|
}
|
|
|
|
func hasExportAssignmentSymbol(moduleSymbol *ast.Symbol) bool {
|
|
return moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] != nil
|
|
}
|
|
|
|
func isTypeAlias(node *ast.Node) bool {
|
|
return ast.IsTypeOrJSTypeAliasDeclaration(node)
|
|
}
|
|
|
|
func hasOnlyExpressionInitializer(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindVariableDeclaration, ast.KindParameter, ast.KindBindingElement, ast.KindPropertyDeclaration, ast.KindPropertyAssignment, ast.KindEnumMember:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func hasDotDotDotToken(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindParameter:
|
|
return node.AsParameterDeclaration().DotDotDotToken != nil
|
|
case ast.KindBindingElement:
|
|
return node.AsBindingElement().DotDotDotToken != nil
|
|
case ast.KindNamedTupleMember:
|
|
return node.AsNamedTupleMember().DotDotDotToken != nil
|
|
case ast.KindJsxExpression:
|
|
return node.AsJsxExpression().DotDotDotToken != nil
|
|
}
|
|
return false
|
|
}
|
|
|
|
func IsTypeAny(t *Type) bool {
|
|
return t != nil && t.flags&TypeFlagsAny != 0
|
|
}
|
|
|
|
func isJSDocOptionalParameter(node *ast.ParameterDeclaration) bool {
|
|
return false // !!!
|
|
}
|
|
|
|
func isExclamationToken(node *ast.Node) bool {
|
|
return node != nil && node.Kind == ast.KindExclamationToken
|
|
}
|
|
|
|
func isOptionalDeclaration(declaration *ast.Node) bool {
|
|
switch declaration.Kind {
|
|
case ast.KindParameter:
|
|
return declaration.AsParameterDeclaration().QuestionToken != nil
|
|
case ast.KindPropertyDeclaration:
|
|
return ast.IsQuestionToken(declaration.AsPropertyDeclaration().PostfixToken)
|
|
case ast.KindPropertySignature:
|
|
return ast.IsQuestionToken(declaration.AsPropertySignatureDeclaration().PostfixToken)
|
|
case ast.KindMethodDeclaration:
|
|
return ast.IsQuestionToken(declaration.AsMethodDeclaration().PostfixToken)
|
|
case ast.KindMethodSignature:
|
|
return ast.IsQuestionToken(declaration.AsMethodSignatureDeclaration().PostfixToken)
|
|
case ast.KindPropertyAssignment:
|
|
return ast.IsQuestionToken(declaration.AsPropertyAssignment().PostfixToken)
|
|
case ast.KindShorthandPropertyAssignment:
|
|
return ast.IsQuestionToken(declaration.AsShorthandPropertyAssignment().PostfixToken)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isEmptyArrayLiteral(expression *ast.Node) bool {
|
|
return ast.IsArrayLiteralExpression(expression) && len(expression.AsArrayLiteralExpression().Elements.Nodes) == 0
|
|
}
|
|
|
|
func declarationBelongsToPrivateAmbientMember(declaration *ast.Node) bool {
|
|
root := ast.GetRootDeclaration(declaration)
|
|
memberDeclaration := root
|
|
if root.Kind == ast.KindParameter {
|
|
memberDeclaration = root.Parent
|
|
}
|
|
return isPrivateWithinAmbient(memberDeclaration)
|
|
}
|
|
|
|
func isPrivateWithinAmbient(node *ast.Node) bool {
|
|
return (ast.HasModifier(node, ast.ModifierFlagsPrivate) || ast.IsPrivateIdentifierClassElementDeclaration(node)) && node.Flags&ast.NodeFlagsAmbient != 0
|
|
}
|
|
|
|
func isTypeAssertion(node *ast.Node) bool {
|
|
return ast.IsAssertionExpression(ast.SkipParentheses(node))
|
|
}
|
|
|
|
func createSymbolTable(symbols []*ast.Symbol) ast.SymbolTable {
|
|
if len(symbols) == 0 {
|
|
return nil
|
|
}
|
|
result := make(ast.SymbolTable)
|
|
for _, symbol := range symbols {
|
|
result[symbol.Name] = symbol
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (c *Checker) sortSymbols(symbols []*ast.Symbol) {
|
|
slices.SortFunc(symbols, c.compareSymbols)
|
|
}
|
|
|
|
func (c *Checker) compareSymbolsWorker(s1, s2 *ast.Symbol) int {
|
|
if s1 == s2 {
|
|
return 0
|
|
}
|
|
if s1 == nil {
|
|
return 1
|
|
}
|
|
if s2 == nil {
|
|
return -1
|
|
}
|
|
if len(s1.Declarations) != 0 && len(s2.Declarations) != 0 {
|
|
if r := c.compareNodes(s1.Declarations[0], s2.Declarations[0]); r != 0 {
|
|
return r
|
|
}
|
|
} else if len(s1.Declarations) != 0 {
|
|
return -1
|
|
} else if len(s2.Declarations) != 0 {
|
|
return 1
|
|
}
|
|
if r := strings.Compare(s1.Name, s2.Name); r != 0 {
|
|
return r
|
|
}
|
|
// Fall back to symbol IDs. This is a last resort that should happen only when symbols have
|
|
// no declaration and duplicate names.
|
|
return int(ast.GetSymbolId(s1)) - int(ast.GetSymbolId(s2))
|
|
}
|
|
|
|
func (c *Checker) compareNodes(n1, n2 *ast.Node) int {
|
|
if n1 == n2 {
|
|
return 0
|
|
}
|
|
if n1 == nil {
|
|
return 1
|
|
}
|
|
if n2 == nil {
|
|
return -1
|
|
}
|
|
s1 := ast.GetSourceFileOfNode(n1)
|
|
s2 := ast.GetSourceFileOfNode(n2)
|
|
if s1 != s2 {
|
|
f1 := c.fileIndexMap[s1]
|
|
f2 := c.fileIndexMap[s2]
|
|
// Order by index of file in the containing program
|
|
return f1 - f2
|
|
}
|
|
// In the same file, order by source position
|
|
return n1.Pos() - n2.Pos()
|
|
}
|
|
|
|
func CompareTypes(t1, t2 *Type) int {
|
|
if t1 == t2 {
|
|
return 0
|
|
}
|
|
if t1 == nil {
|
|
return -1
|
|
}
|
|
if t2 == nil {
|
|
return 1
|
|
}
|
|
if t1.checker != t2.checker {
|
|
panic("Cannot compare types from different checkers")
|
|
}
|
|
// First sort in order of increasing type flags values.
|
|
if c := getSortOrderFlags(t1) - getSortOrderFlags(t2); c != 0 {
|
|
return c
|
|
}
|
|
// Order named types by name and, in the case of aliased types, by alias type arguments.
|
|
if c := compareTypeNames(t1, t2); c != 0 {
|
|
return c
|
|
}
|
|
// We have unnamed types or types with identical names. Now sort by data specific to the type.
|
|
switch {
|
|
case t1.flags&(TypeFlagsAny|TypeFlagsUnknown|TypeFlagsString|TypeFlagsNumber|TypeFlagsBoolean|TypeFlagsBigInt|TypeFlagsESSymbol|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsNever|TypeFlagsNonPrimitive) != 0:
|
|
// Only distinguished by type IDs, handled below.
|
|
case t1.flags&TypeFlagsObject != 0:
|
|
// Order unnamed or identically named object types by symbol.
|
|
if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 {
|
|
return c
|
|
}
|
|
// When object types have the same or no symbol, order by kind. We order type references before other kinds.
|
|
if t1.objectFlags&ObjectFlagsReference != 0 && t2.objectFlags&ObjectFlagsReference != 0 {
|
|
r1 := t1.AsTypeReference()
|
|
r2 := t2.AsTypeReference()
|
|
if r1.target.objectFlags&ObjectFlagsTuple != 0 && r2.target.objectFlags&ObjectFlagsTuple != 0 {
|
|
// Tuple types have no associated symbol, instead we order by tuple element information.
|
|
if c := compareTupleTypes(r1.target.AsTupleType(), r2.target.AsTupleType()); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
// Here we know we have references to instantiations of the same type because we have matching targets.
|
|
if r1.node == nil && r2.node == nil {
|
|
// Non-deferred type references with the same target are sorted by their type argument lists.
|
|
if c := compareTypeLists(t1.AsTypeReference().resolvedTypeArguments, t2.AsTypeReference().resolvedTypeArguments); c != 0 {
|
|
return c
|
|
}
|
|
} else {
|
|
// Deferred type references with the same target are ordered by the source location of the reference.
|
|
if c := t1.checker.compareNodes(r1.node, r2.node); c != 0 {
|
|
return c
|
|
}
|
|
// Instantiations of the same deferred type reference are ordered by their associated type mappers
|
|
// (which reflect the mapping of in-scope type parameters to type arguments).
|
|
if c := compareTypeMappers(t1.AsObjectType().mapper, t2.AsObjectType().mapper); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
} else if t1.objectFlags&ObjectFlagsReference != 0 {
|
|
return -1
|
|
} else if t2.objectFlags&ObjectFlagsReference != 0 {
|
|
return 1
|
|
} else {
|
|
// Order unnamed non-reference object types by kind associated type mappers. Reverse mapped types have
|
|
// neither symbols nor mappers so they're ultimately ordered by unstable type IDs, but given their rarity
|
|
// this should be fine.
|
|
if c := int(t1.objectFlags&ObjectFlagsObjectTypeKindMask) - int(t2.objectFlags&ObjectFlagsObjectTypeKindMask); c != 0 {
|
|
return c
|
|
}
|
|
if c := compareTypeMappers(t1.AsObjectType().mapper, t2.AsObjectType().mapper); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
case t1.flags&TypeFlagsUnion != 0:
|
|
// Unions are ordered by origin and then constituent type lists.
|
|
o1 := t1.AsUnionType().origin
|
|
o2 := t2.AsUnionType().origin
|
|
if o1 == nil && o2 == nil {
|
|
if c := compareTypeLists(t1.Types(), t2.Types()); c != 0 {
|
|
return c
|
|
}
|
|
} else if o1 == nil {
|
|
return 1
|
|
} else if o2 == nil {
|
|
return -1
|
|
} else {
|
|
if c := CompareTypes(o1, o2); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
case t1.flags&TypeFlagsIntersection != 0:
|
|
// Intersections are ordered by their constituent type lists.
|
|
if c := compareTypeLists(t1.Types(), t2.Types()); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&(TypeFlagsEnum|TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0:
|
|
// Enum members are ordered by their symbol (and thus their declaration order).
|
|
if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsStringLiteral != 0:
|
|
// String literal types are ordered by their values.
|
|
if c := strings.Compare(t1.AsLiteralType().value.(string), t2.AsLiteralType().value.(string)); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsNumberLiteral != 0:
|
|
// Numeric literal types are ordered by their values.
|
|
if c := cmp.Compare(t1.AsLiteralType().value.(jsnum.Number), t2.AsLiteralType().value.(jsnum.Number)); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsBooleanLiteral != 0:
|
|
b1 := t1.AsLiteralType().value.(bool)
|
|
b2 := t2.AsLiteralType().value.(bool)
|
|
if b1 != b2 {
|
|
if b1 {
|
|
return 1
|
|
}
|
|
return -1
|
|
}
|
|
case t1.flags&TypeFlagsTypeParameter != 0:
|
|
if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsIndex != 0:
|
|
if c := CompareTypes(t1.AsIndexType().target, t2.AsIndexType().target); c != 0 {
|
|
return c
|
|
}
|
|
if c := int(t1.AsIndexType().flags) - int(t2.AsIndexType().flags); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsIndexedAccess != 0:
|
|
if c := CompareTypes(t1.AsIndexedAccessType().objectType, t2.AsIndexedAccessType().objectType); c != 0 {
|
|
return c
|
|
}
|
|
if c := CompareTypes(t1.AsIndexedAccessType().indexType, t2.AsIndexedAccessType().indexType); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsConditional != 0:
|
|
if c := t1.checker.compareNodes(t1.AsConditionalType().root.node.AsNode(), t2.AsConditionalType().root.node.AsNode()); c != 0 {
|
|
return c
|
|
}
|
|
if c := compareTypeMappers(t1.AsConditionalType().mapper, t2.AsConditionalType().mapper); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsSubstitution != 0:
|
|
if c := CompareTypes(t1.AsSubstitutionType().baseType, t2.AsSubstitutionType().baseType); c != 0 {
|
|
return c
|
|
}
|
|
if c := CompareTypes(t1.AsSubstitutionType().constraint, t2.AsSubstitutionType().constraint); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsTemplateLiteral != 0:
|
|
if c := slices.Compare(t1.AsTemplateLiteralType().texts, t2.AsTemplateLiteralType().texts); c != 0 {
|
|
return c
|
|
}
|
|
if c := compareTypeLists(t1.AsTemplateLiteralType().types, t2.AsTemplateLiteralType().types); c != 0 {
|
|
return c
|
|
}
|
|
case t1.flags&TypeFlagsStringMapping != 0:
|
|
if c := CompareTypes(t1.AsStringMappingType().target, t2.AsStringMappingType().target); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
// Fall back to type IDs. This results in type creation order for built-in types.
|
|
return int(t1.id) - int(t2.id)
|
|
}
|
|
|
|
func getSortOrderFlags(t *Type) int {
|
|
// Return TypeFlagsEnum for all enum-like unit types (they'll be sorted by their symbols)
|
|
if t.flags&(TypeFlagsEnumLiteral|TypeFlagsEnum) != 0 && t.flags&TypeFlagsUnion == 0 {
|
|
return int(TypeFlagsEnum)
|
|
}
|
|
return int(t.flags)
|
|
}
|
|
|
|
func compareTypeNames(t1, t2 *Type) int {
|
|
s1 := getTypeNameSymbol(t1)
|
|
s2 := getTypeNameSymbol(t2)
|
|
if s1 == s2 {
|
|
if t1.alias != nil {
|
|
return compareTypeLists(t1.alias.typeArguments, t2.alias.typeArguments)
|
|
}
|
|
return 0
|
|
}
|
|
if s1 == nil {
|
|
return 1
|
|
}
|
|
if s2 == nil {
|
|
return -1
|
|
}
|
|
return strings.Compare(s1.Name, s2.Name)
|
|
}
|
|
|
|
func getTypeNameSymbol(t *Type) *ast.Symbol {
|
|
if t.alias != nil {
|
|
return t.alias.symbol
|
|
}
|
|
if t.flags&(TypeFlagsTypeParameter|TypeFlagsStringMapping) != 0 || t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 {
|
|
return t.symbol
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getObjectTypeName(t *Type) *ast.Symbol {
|
|
if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 {
|
|
return t.symbol
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func compareTupleTypes(t1, t2 *TupleType) int {
|
|
if t1 == t2 {
|
|
return 0
|
|
}
|
|
if t1.readonly != t2.readonly {
|
|
return core.IfElse(t1.readonly, 1, -1)
|
|
}
|
|
if len(t1.elementInfos) != len(t2.elementInfos) {
|
|
return len(t1.elementInfos) - len(t2.elementInfos)
|
|
}
|
|
for i := range t1.elementInfos {
|
|
if c := int(t1.elementInfos[i].flags) - int(t2.elementInfos[i].flags); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
for i := range t1.elementInfos {
|
|
if c := compareElementLabels(t1.elementInfos[i].labeledDeclaration, t2.elementInfos[i].labeledDeclaration); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func compareElementLabels(n1, n2 *ast.Node) int {
|
|
if n1 == n2 {
|
|
return 0
|
|
}
|
|
if n1 == nil {
|
|
return -1
|
|
}
|
|
if n2 == nil {
|
|
return 1
|
|
}
|
|
return strings.Compare(n1.Name().Text(), n2.Name().Text())
|
|
}
|
|
|
|
func compareTypeLists(s1, s2 []*Type) int {
|
|
if len(s1) != len(s2) {
|
|
return len(s1) - len(s2)
|
|
}
|
|
for i, t1 := range s1 {
|
|
if c := CompareTypes(t1, s2[i]); c != 0 {
|
|
return c
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func compareTypeMappers(m1, m2 *TypeMapper) int {
|
|
if m1 == m2 {
|
|
return 0
|
|
}
|
|
if m1 == nil {
|
|
return 1
|
|
}
|
|
if m2 == nil {
|
|
return -1
|
|
}
|
|
kind1 := m1.Kind()
|
|
kind2 := m2.Kind()
|
|
if kind1 != kind2 {
|
|
return int(kind1) - int(kind2)
|
|
}
|
|
switch kind1 {
|
|
case TypeMapperKindSimple:
|
|
m1 := m1.data.(*SimpleTypeMapper)
|
|
m2 := m2.data.(*SimpleTypeMapper)
|
|
if c := CompareTypes(m1.source, m2.source); c != 0 {
|
|
return c
|
|
}
|
|
return CompareTypes(m1.target, m2.target)
|
|
case TypeMapperKindArray:
|
|
m1 := m1.data.(*ArrayTypeMapper)
|
|
m2 := m2.data.(*ArrayTypeMapper)
|
|
if c := compareTypeLists(m1.sources, m2.sources); c != 0 {
|
|
return c
|
|
}
|
|
return compareTypeLists(m1.targets, m2.targets)
|
|
case TypeMapperKindMerged:
|
|
m1 := m1.data.(*MergedTypeMapper)
|
|
m2 := m2.data.(*MergedTypeMapper)
|
|
if c := compareTypeMappers(m1.m1, m2.m1); c != 0 {
|
|
return c
|
|
}
|
|
return compareTypeMappers(m1.m2, m2.m2)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func getDeclarationModifierFlagsFromSymbol(s *ast.Symbol) ast.ModifierFlags {
|
|
return getDeclarationModifierFlagsFromSymbolEx(s, false /*isWrite*/)
|
|
}
|
|
|
|
func getDeclarationModifierFlagsFromSymbolEx(s *ast.Symbol, isWrite bool) ast.ModifierFlags {
|
|
if s.ValueDeclaration != nil {
|
|
var declaration *ast.Node
|
|
if isWrite {
|
|
declaration = core.Find(s.Declarations, ast.IsSetAccessorDeclaration)
|
|
}
|
|
if declaration == nil && s.Flags&ast.SymbolFlagsGetAccessor != 0 {
|
|
declaration = core.Find(s.Declarations, ast.IsGetAccessorDeclaration)
|
|
}
|
|
if declaration == nil {
|
|
declaration = s.ValueDeclaration
|
|
}
|
|
flags := ast.GetCombinedModifierFlags(declaration)
|
|
if s.Parent != nil && s.Parent.Flags&ast.SymbolFlagsClass != 0 {
|
|
return flags
|
|
}
|
|
return flags & ^ast.ModifierFlagsAccessibilityModifier
|
|
}
|
|
if s.CheckFlags&ast.CheckFlagsSynthetic != 0 {
|
|
var accessModifier ast.ModifierFlags
|
|
switch {
|
|
case s.CheckFlags&ast.CheckFlagsContainsPrivate != 0:
|
|
accessModifier = ast.ModifierFlagsPrivate
|
|
case s.CheckFlags&ast.CheckFlagsContainsPublic != 0:
|
|
accessModifier = ast.ModifierFlagsPublic
|
|
default:
|
|
accessModifier = ast.ModifierFlagsProtected
|
|
}
|
|
var staticModifier ast.ModifierFlags
|
|
if s.CheckFlags&ast.CheckFlagsContainsStatic != 0 {
|
|
staticModifier = ast.ModifierFlagsStatic
|
|
}
|
|
return accessModifier | staticModifier
|
|
}
|
|
if s.Flags&ast.SymbolFlagsPrototype != 0 {
|
|
return ast.ModifierFlagsPublic | ast.ModifierFlagsStatic
|
|
}
|
|
return ast.ModifierFlagsNone
|
|
}
|
|
|
|
func isExponentiationOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindAsteriskAsteriskToken
|
|
}
|
|
|
|
func isMultiplicativeOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindAsteriskToken || kind == ast.KindSlashToken || kind == ast.KindPercentToken
|
|
}
|
|
|
|
func isMultiplicativeOperatorOrHigher(kind ast.Kind) bool {
|
|
return isExponentiationOperator(kind) || isMultiplicativeOperator(kind)
|
|
}
|
|
|
|
func isAdditiveOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindPlusToken || kind == ast.KindMinusToken
|
|
}
|
|
|
|
func isAdditiveOperatorOrHigher(kind ast.Kind) bool {
|
|
return isAdditiveOperator(kind) || isMultiplicativeOperatorOrHigher(kind)
|
|
}
|
|
|
|
func isShiftOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindLessThanLessThanToken || kind == ast.KindGreaterThanGreaterThanToken ||
|
|
kind == ast.KindGreaterThanGreaterThanGreaterThanToken
|
|
}
|
|
|
|
func isShiftOperatorOrHigher(kind ast.Kind) bool {
|
|
return isShiftOperator(kind) || isAdditiveOperatorOrHigher(kind)
|
|
}
|
|
|
|
func isRelationalOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindLessThanToken || kind == ast.KindLessThanEqualsToken || kind == ast.KindGreaterThanToken ||
|
|
kind == ast.KindGreaterThanEqualsToken || kind == ast.KindInstanceOfKeyword || kind == ast.KindInKeyword
|
|
}
|
|
|
|
func isRelationalOperatorOrHigher(kind ast.Kind) bool {
|
|
return isRelationalOperator(kind) || isShiftOperatorOrHigher(kind)
|
|
}
|
|
|
|
func isEqualityOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindEqualsEqualsToken || kind == ast.KindEqualsEqualsEqualsToken ||
|
|
kind == ast.KindExclamationEqualsToken || kind == ast.KindExclamationEqualsEqualsToken
|
|
}
|
|
|
|
func isEqualityOperatorOrHigher(kind ast.Kind) bool {
|
|
return isEqualityOperator(kind) || isRelationalOperatorOrHigher(kind)
|
|
}
|
|
|
|
func isBitwiseOperator(kind ast.Kind) bool {
|
|
return kind == ast.KindAmpersandToken || kind == ast.KindBarToken || kind == ast.KindCaretToken
|
|
}
|
|
|
|
func isBitwiseOperatorOrHigher(kind ast.Kind) bool {
|
|
return isBitwiseOperator(kind) || isEqualityOperatorOrHigher(kind)
|
|
}
|
|
|
|
func isLogicalOperatorOrHigher(kind ast.Kind) bool {
|
|
return ast.IsLogicalBinaryOperator(kind) || isBitwiseOperatorOrHigher(kind)
|
|
}
|
|
|
|
func isAssignmentOperatorOrHigher(kind ast.Kind) bool {
|
|
return kind == ast.KindQuestionQuestionToken || isLogicalOperatorOrHigher(kind) || ast.IsAssignmentOperator(kind)
|
|
}
|
|
|
|
func isBinaryOperator(kind ast.Kind) bool {
|
|
return isAssignmentOperatorOrHigher(kind) || kind == ast.KindCommaToken
|
|
}
|
|
|
|
func isObjectLiteralType(t *Type) bool {
|
|
return t.objectFlags&ObjectFlagsObjectLiteral != 0
|
|
}
|
|
|
|
func isDeclarationReadonly(declaration *ast.Node) bool {
|
|
return ast.GetCombinedModifierFlags(declaration)&ast.ModifierFlagsReadonly != 0 && !ast.IsParameterPropertyDeclaration(declaration, declaration.Parent)
|
|
}
|
|
|
|
type orderedSet[T comparable] struct {
|
|
valuesByKey map[T]struct{}
|
|
values []T
|
|
}
|
|
|
|
func (s *orderedSet[T]) contains(value T) bool {
|
|
_, ok := s.valuesByKey[value]
|
|
return ok
|
|
}
|
|
|
|
func (s *orderedSet[T]) add(value T) {
|
|
if s.valuesByKey == nil {
|
|
s.valuesByKey = make(map[T]struct{})
|
|
}
|
|
s.valuesByKey[value] = struct{}{}
|
|
s.values = append(s.values, value)
|
|
}
|
|
|
|
func getContainingFunctionOrClassStaticBlock(node *ast.Node) *ast.Node {
|
|
return ast.FindAncestor(node.Parent, ast.IsFunctionLikeOrClassStaticBlockDeclaration)
|
|
}
|
|
|
|
func isNodeDescendantOf(node *ast.Node, ancestor *ast.Node) bool {
|
|
for node != nil {
|
|
if node == ancestor {
|
|
return true
|
|
}
|
|
node = node.Parent
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isTypeUsableAsPropertyName(t *Type) bool {
|
|
return t.flags&TypeFlagsStringOrNumberLiteralOrUnique != 0
|
|
}
|
|
|
|
/**
|
|
* Gets the symbolic name for a member from its type.
|
|
*/
|
|
func getPropertyNameFromType(t *Type) string {
|
|
switch {
|
|
case t.flags&TypeFlagsStringLiteral != 0:
|
|
return t.AsLiteralType().value.(string)
|
|
case t.flags&TypeFlagsNumberLiteral != 0:
|
|
return t.AsLiteralType().value.(jsnum.Number).String()
|
|
case t.flags&TypeFlagsUniqueESSymbol != 0:
|
|
return t.AsUniqueESSymbolType().name
|
|
}
|
|
panic("Unhandled case in getPropertyNameFromType")
|
|
}
|
|
|
|
func isNumericLiteralName(name string) bool {
|
|
// The intent of numeric names is that
|
|
// - they are names with text in a numeric form, and that
|
|
// - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit',
|
|
// acquired by applying the abstract 'ToNumber' operation on the name's text.
|
|
//
|
|
// The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name.
|
|
// In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold.
|
|
//
|
|
// Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)'
|
|
// according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'.
|
|
// Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names
|
|
// because their 'ToString' representation is not equal to their original text.
|
|
// This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1.
|
|
//
|
|
// Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'.
|
|
// The '+' prefix operator is equivalent here to applying the abstract ToNumber operation.
|
|
// Applying the 'toString()' method on a number gives us the abstract ToString operation on a number.
|
|
//
|
|
// Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
|
|
// This is desired behavior, because when indexing with them as numeric entities, you are indexing
|
|
// with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
|
|
return jsnum.FromString(name).String() == name
|
|
}
|
|
|
|
func isThisProperty(node *ast.Node) bool {
|
|
return (ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && node.Expression().Kind == ast.KindThisKeyword
|
|
}
|
|
|
|
func isValidNumberString(s string, roundTripOnly bool) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
n := jsnum.FromString(s)
|
|
return !n.IsNaN() && !n.IsInf() && (!roundTripOnly || n.String() == s)
|
|
}
|
|
|
|
func isValidBigIntString(s string, roundTripOnly bool) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
scanner := scanner.NewScanner()
|
|
scanner.SetSkipTrivia(false)
|
|
success := true
|
|
scanner.SetOnError(func(diagnostic *diagnostics.Message, start, length int, args ...any) {
|
|
success = false
|
|
})
|
|
scanner.SetText(s + "n")
|
|
result := scanner.Scan()
|
|
negative := result == ast.KindMinusToken
|
|
if negative {
|
|
result = scanner.Scan()
|
|
}
|
|
flags := scanner.TokenFlags()
|
|
// validate that
|
|
// * scanning proceeded without error
|
|
// * a bigint can be scanned, and that when it is scanned, it is
|
|
// * the full length of the input string (so the scanner is one character beyond the augmented input length)
|
|
// * it does not contain a numeric separator (the `BigInt` constructor does not accept a numeric separator in its input)
|
|
return success && result == ast.KindBigIntLiteral && scanner.TokenEnd() == len(s)+1 && flags&ast.TokenFlagsContainsSeparator == 0 &&
|
|
(!roundTripOnly || s == pseudoBigIntToString(jsnum.NewPseudoBigInt(jsnum.ParsePseudoBigInt(scanner.TokenValue()), negative)))
|
|
}
|
|
|
|
func isValidESSymbolDeclaration(node *ast.Node) bool {
|
|
if ast.IsVariableDeclaration(node) {
|
|
return ast.IsVarConst(node) && ast.IsIdentifier(node.AsVariableDeclaration().Name()) && isVariableDeclarationInVariableStatement(node)
|
|
}
|
|
if ast.IsPropertyDeclaration(node) {
|
|
return hasReadonlyModifier(node) && ast.HasStaticModifier(node)
|
|
}
|
|
return ast.IsPropertySignatureDeclaration(node) && hasReadonlyModifier(node)
|
|
}
|
|
|
|
func isVariableDeclarationInVariableStatement(node *ast.Node) bool {
|
|
return ast.IsVariableDeclarationList(node.Parent) && ast.IsVariableStatement(node.Parent.Parent)
|
|
}
|
|
|
|
func IsKnownSymbol(symbol *ast.Symbol) bool {
|
|
return isLateBoundName(symbol.Name)
|
|
}
|
|
|
|
func isLateBoundName(name string) bool {
|
|
return len(name) >= 2 && name[0] == '\xfe' && name[1] == '@'
|
|
}
|
|
|
|
func isObjectOrArrayLiteralType(t *Type) bool {
|
|
return t.objectFlags&(ObjectFlagsObjectLiteral|ObjectFlagsArrayLiteral) != 0
|
|
}
|
|
|
|
func getContainingClassExcludingClassDecorators(node *ast.Node) *ast.ClassLikeDeclaration {
|
|
decorator := ast.FindAncestorOrQuit(node.Parent, func(n *ast.Node) ast.FindAncestorResult {
|
|
if ast.IsClassLike(n) {
|
|
return ast.FindAncestorQuit
|
|
}
|
|
if ast.IsDecorator(n) {
|
|
return ast.FindAncestorTrue
|
|
}
|
|
return ast.FindAncestorFalse
|
|
})
|
|
if decorator != nil && ast.IsClassLike(decorator.Parent) {
|
|
return ast.GetContainingClass(decorator.Parent)
|
|
}
|
|
if decorator != nil {
|
|
return ast.GetContainingClass(decorator)
|
|
}
|
|
return ast.GetContainingClass(node)
|
|
}
|
|
|
|
func isThisTypeParameter(t *Type) bool {
|
|
return t.flags&TypeFlagsTypeParameter != 0 && t.AsTypeParameter().isThisType
|
|
}
|
|
|
|
func isClassInstanceProperty(node *ast.Node) bool {
|
|
return node.Parent != nil && ast.IsClassLike(node.Parent) && ast.IsPropertyDeclaration(node) && !ast.HasAccessorModifier(node)
|
|
}
|
|
|
|
func isThisInitializedObjectBindingExpression(node *ast.Node) bool {
|
|
return node != nil && (ast.IsShorthandPropertyAssignment(node) || ast.IsPropertyAssignment(node)) && ast.IsBinaryExpression(node.Parent.Parent) &&
|
|
node.Parent.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken &&
|
|
node.Parent.Parent.AsBinaryExpression().Right.Kind == ast.KindThisKeyword
|
|
}
|
|
|
|
func isThisInitializedDeclaration(node *ast.Node) bool {
|
|
return node != nil && ast.IsVariableDeclaration(node) && node.AsVariableDeclaration().Initializer != nil && node.AsVariableDeclaration().Initializer.Kind == ast.KindThisKeyword
|
|
}
|
|
|
|
func isInfinityOrNaNString(name string) bool {
|
|
return name == "Infinity" || name == "-Infinity" || name == "NaN"
|
|
}
|
|
|
|
func (c *Checker) isConstantVariable(symbol *ast.Symbol) bool {
|
|
return symbol.Flags&ast.SymbolFlagsVariable != 0 && (c.getDeclarationNodeFlagsFromSymbol(symbol)&ast.NodeFlagsConstant) != 0
|
|
}
|
|
|
|
func (c *Checker) isParameterOrMutableLocalVariable(symbol *ast.Symbol) bool {
|
|
// Return true if symbol is a parameter, a catch clause variable, or a mutable local variable
|
|
if symbol.ValueDeclaration != nil {
|
|
declaration := ast.GetRootDeclaration(symbol.ValueDeclaration)
|
|
return declaration != nil && (ast.IsParameter(declaration) || ast.IsVariableDeclaration(declaration) && (ast.IsCatchClause(declaration.Parent) || c.isMutableLocalVariableDeclaration(declaration)))
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Checker) isMutableLocalVariableDeclaration(declaration *ast.Node) bool {
|
|
// Return true if symbol is a non-exported and non-global `let` variable
|
|
return declaration.Parent.Flags&ast.NodeFlagsLet != 0 && !(ast.GetCombinedModifierFlags(declaration)&ast.ModifierFlagsExport != 0 || declaration.Parent.Parent.Kind == ast.KindVariableStatement && ast.IsGlobalSourceFile(declaration.Parent.Parent.Parent))
|
|
}
|
|
|
|
func isInAmbientOrTypeNode(node *ast.Node) bool {
|
|
return node.Flags&ast.NodeFlagsAmbient != 0 || ast.FindAncestor(node, func(n *ast.Node) bool {
|
|
return ast.IsInterfaceDeclaration(n) || ast.IsTypeOrJSTypeAliasDeclaration(n) || ast.IsTypeLiteralNode(n)
|
|
}) != nil
|
|
}
|
|
|
|
func isLiteralExpressionOfObject(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression, ast.KindRegularExpressionLiteral,
|
|
ast.KindFunctionExpression, ast.KindClassExpression:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func canHaveFlowNode(node *ast.Node) bool {
|
|
return node.FlowNodeData() != nil
|
|
}
|
|
|
|
func isNonNullAccess(node *ast.Node) bool {
|
|
return ast.IsAccessExpression(node) && ast.IsNonNullExpression(node.Expression())
|
|
}
|
|
|
|
func getTagNameOfNode(node *ast.Node) *ast.Node {
|
|
switch node.Kind {
|
|
case ast.KindJsxOpeningElement:
|
|
return node.AsJsxOpeningElement().TagName
|
|
case ast.KindJsxClosingElement:
|
|
return node.AsJsxClosingElement().TagName
|
|
case ast.KindJsxSelfClosingElement:
|
|
return node.AsJsxSelfClosingElement().TagName
|
|
}
|
|
panic("Unhandled case in getTagNameOfNode")
|
|
}
|
|
|
|
func getBindingElementPropertyName(node *ast.Node) *ast.Node {
|
|
name := node.AsBindingElement().PropertyName
|
|
if name != nil {
|
|
return name
|
|
}
|
|
return node.Name()
|
|
}
|
|
|
|
func hasContextSensitiveParameters(node *ast.Node) bool {
|
|
// Functions with type parameters are not context sensitive.
|
|
if node.TypeParameters() == nil {
|
|
// Functions with any parameters that lack type annotations are context sensitive.
|
|
if core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() == nil }) {
|
|
return true
|
|
}
|
|
if !ast.IsArrowFunction(node) {
|
|
// If the first parameter is not an explicit 'this' parameter, then the function has
|
|
// an implicit 'this' parameter which is subject to contextual typing.
|
|
parameter := core.FirstOrNil(node.Parameters())
|
|
if parameter == nil || !ast.IsThisParameter(parameter) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isCallChain(node *ast.Node) bool {
|
|
return ast.IsCallExpression(node) && node.Flags&ast.NodeFlagsOptionalChain != 0
|
|
}
|
|
|
|
func (c *Checker) callLikeExpressionMayHaveTypeArguments(node *ast.Node) bool {
|
|
return ast.IsCallOrNewExpression(node) || ast.IsTaggedTemplateExpression(node) || ast.IsJsxOpeningLikeElement(node)
|
|
}
|
|
|
|
func isSuperCall(n *ast.Node) bool {
|
|
return ast.IsCallExpression(n) && n.Expression().Kind == ast.KindSuperKeyword
|
|
}
|
|
|
|
/**
|
|
* Determines whether a node is a property or element access expression for `super`.
|
|
*
|
|
* @internal
|
|
*/
|
|
func isSuperProperty(node *ast.Node) bool {
|
|
return ast.IsAccessExpression(node) && node.Expression().Kind == ast.KindSuperKeyword
|
|
}
|
|
|
|
func getMembersOfDeclaration(node *ast.Node) []*ast.Node {
|
|
switch node.Kind {
|
|
case ast.KindInterfaceDeclaration:
|
|
return node.AsInterfaceDeclaration().Members.Nodes
|
|
case ast.KindClassDeclaration:
|
|
return node.AsClassDeclaration().Members.Nodes
|
|
case ast.KindClassExpression:
|
|
return node.AsClassExpression().Members.Nodes
|
|
case ast.KindTypeLiteral:
|
|
return node.AsTypeLiteralNode().Members.Nodes
|
|
case ast.KindObjectLiteralExpression:
|
|
return node.AsObjectLiteralExpression().Properties.Nodes
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type FunctionFlags uint32
|
|
|
|
const (
|
|
FunctionFlagsNormal FunctionFlags = 0
|
|
FunctionFlagsGenerator FunctionFlags = 1 << 0
|
|
FunctionFlagsAsync FunctionFlags = 1 << 1
|
|
FunctionFlagsInvalid FunctionFlags = 1 << 2
|
|
FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator
|
|
)
|
|
|
|
func getFunctionFlags(node *ast.Node) FunctionFlags {
|
|
if node == nil {
|
|
return FunctionFlagsInvalid
|
|
}
|
|
data := node.BodyData()
|
|
if data == nil {
|
|
return FunctionFlagsInvalid
|
|
}
|
|
flags := FunctionFlagsNormal
|
|
switch node.Kind {
|
|
case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindMethodDeclaration:
|
|
if data.AsteriskToken != nil {
|
|
flags |= FunctionFlagsGenerator
|
|
}
|
|
fallthrough
|
|
case ast.KindArrowFunction:
|
|
if ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) {
|
|
flags |= FunctionFlagsAsync
|
|
}
|
|
}
|
|
if data.Body == nil {
|
|
flags |= FunctionFlagsInvalid
|
|
}
|
|
return flags
|
|
}
|
|
|
|
func isInRightSideOfImportOrExportAssignment(node *ast.EntityName) bool {
|
|
for node.Parent.Kind == ast.KindQualifiedName {
|
|
node = node.Parent
|
|
}
|
|
|
|
return node.Parent.Kind == ast.KindImportEqualsDeclaration && node.Parent.AsImportEqualsDeclaration().ModuleReference == node ||
|
|
(node.Parent.Kind == ast.KindExportAssignment || node.Parent.Kind == ast.KindJSExportAssignment) && node.Parent.AsExportAssignment().Expression == node
|
|
}
|
|
|
|
func isJsxIntrinsicTagName(tagName *ast.Node) bool {
|
|
return ast.IsIdentifier(tagName) && scanner.IsIntrinsicJsxName(tagName.Text()) || ast.IsJsxNamespacedName(tagName)
|
|
}
|
|
|
|
func getContainingObjectLiteral(f *ast.SignatureDeclaration) *ast.Node {
|
|
if (f.Kind == ast.KindMethodDeclaration ||
|
|
f.Kind == ast.KindGetAccessor ||
|
|
f.Kind == ast.KindSetAccessor) && f.Parent.Kind == ast.KindObjectLiteralExpression {
|
|
return f.Parent
|
|
} else if f.Kind == ast.KindFunctionExpression && f.Parent.Kind == ast.KindPropertyAssignment {
|
|
return f.Parent.Parent
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isImportTypeQualifierPart(node *ast.Node) *ast.Node {
|
|
parent := node.Parent
|
|
for ast.IsQualifiedName(parent) {
|
|
node = parent
|
|
parent = parent.Parent
|
|
}
|
|
|
|
if parent != nil && parent.Kind == ast.KindImportType && parent.AsImportTypeNode().Qualifier == node {
|
|
return parent
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func isInNameOfExpressionWithTypeArguments(node *ast.Node) bool {
|
|
for node.Parent.Kind == ast.KindPropertyAccessExpression {
|
|
node = node.Parent
|
|
}
|
|
|
|
return node.Parent.Kind == ast.KindExpressionWithTypeArguments
|
|
}
|
|
|
|
func getIndexSymbolFromSymbolTable(symbolTable ast.SymbolTable) *ast.Symbol {
|
|
return symbolTable[ast.InternalSymbolNameIndex]
|
|
}
|
|
|
|
// Indicates whether the result of an `Expression` will be unused.
|
|
// NOTE: This requires a node with a valid `parent` pointer.
|
|
func expressionResultIsUnused(node *ast.Node) bool {
|
|
for {
|
|
parent := node.Parent
|
|
// walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression
|
|
if ast.IsParenthesizedExpression(parent) {
|
|
node = parent
|
|
continue
|
|
}
|
|
// result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop
|
|
if ast.IsExpressionStatement(parent) || ast.IsVoidExpression(parent) || ast.IsForStatement(parent) && (parent.Initializer() == node || parent.AsForStatement().Incrementor == node) {
|
|
return true
|
|
}
|
|
if ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindCommaToken {
|
|
// left side of comma is always unused
|
|
if node == parent.AsBinaryExpression().Left {
|
|
return true
|
|
}
|
|
// right side of comma is unused if parent is unused
|
|
node = parent
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
func pseudoBigIntToString(value jsnum.PseudoBigInt) string {
|
|
return value.String()
|
|
}
|
|
|
|
func getSuperContainer(node *ast.Node, stopOnFunctions bool) *ast.Node {
|
|
for {
|
|
node = node.Parent
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
switch node.Kind {
|
|
case ast.KindComputedPropertyName:
|
|
node = node.Parent
|
|
case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction:
|
|
if !stopOnFunctions {
|
|
continue
|
|
}
|
|
fallthrough
|
|
case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor,
|
|
ast.KindGetAccessor, ast.KindSetAccessor, ast.KindClassStaticBlockDeclaration:
|
|
return node
|
|
case ast.KindDecorator:
|
|
// Decorators are always applied outside of the body of a class or method.
|
|
if ast.IsParameter(node.Parent) && ast.IsClassElement(node.Parent.Parent) {
|
|
// If the decorator's parent is a Parameter, we resolve the this container from
|
|
// the grandparent class declaration.
|
|
node = node.Parent.Parent
|
|
} else if ast.IsClassElement(node.Parent) {
|
|
// If the decorator's parent is a class element, we resolve the 'this' container
|
|
// from the parent class declaration.
|
|
node = node.Parent
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func forEachYieldExpression(body *ast.Node, visitor func(expr *ast.Node)) {
|
|
var traverse func(*ast.Node) bool
|
|
traverse = func(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindYieldExpression:
|
|
visitor(node)
|
|
operand := node.Expression()
|
|
if operand != nil {
|
|
traverse(operand)
|
|
}
|
|
case ast.KindEnumDeclaration, ast.KindInterfaceDeclaration, ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration:
|
|
// These are not allowed inside a generator now, but eventually they may be allowed
|
|
// as local types. Regardless, skip them to avoid the work.
|
|
default:
|
|
if ast.IsFunctionLike(node) {
|
|
if node.Name() != nil && ast.IsComputedPropertyName(node.Name()) {
|
|
// Note that we will not include methods/accessors of a class because they would require
|
|
// first descending into the class. This is by design.
|
|
traverse(node.Name().Expression())
|
|
}
|
|
} else if !ast.IsPartOfTypeNode(node) {
|
|
// This is the general case, which should include mostly expressions and statements.
|
|
// Also includes NodeArrays.
|
|
node.ForEachChild(traverse)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
traverse(body)
|
|
}
|
|
|
|
func SkipTypeChecking(sourceFile *ast.SourceFile, options *core.CompilerOptions, host Program, ignoreNoCheck bool) bool {
|
|
return (!ignoreNoCheck && options.NoCheck.IsTrue()) ||
|
|
options.SkipLibCheck.IsTrue() && sourceFile.IsDeclarationFile ||
|
|
options.SkipDefaultLibCheck.IsTrue() && host.IsSourceFileDefaultLibrary(sourceFile.Path()) ||
|
|
host.IsSourceFromProjectReference(sourceFile.Path()) ||
|
|
!canIncludeBindAndCheckDiagnostics(sourceFile, options)
|
|
}
|
|
|
|
func canIncludeBindAndCheckDiagnostics(sourceFile *ast.SourceFile, options *core.CompilerOptions) bool {
|
|
if sourceFile.CheckJsDirective != nil && !sourceFile.CheckJsDirective.Enabled {
|
|
return false
|
|
}
|
|
|
|
if sourceFile.ScriptKind == core.ScriptKindTS || sourceFile.ScriptKind == core.ScriptKindTSX || sourceFile.ScriptKind == core.ScriptKindExternal {
|
|
return true
|
|
}
|
|
|
|
isJS := sourceFile.ScriptKind == core.ScriptKindJS || sourceFile.ScriptKind == core.ScriptKindJSX
|
|
isCheckJS := isJS && ast.IsCheckJSEnabledForFile(sourceFile, options)
|
|
isPlainJS := ast.IsPlainJSFile(sourceFile, options.CheckJs)
|
|
|
|
// By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External
|
|
// - plain JS: .js files with no // ts-check and checkJs: undefined
|
|
// - check JS: .js files with either // ts-check or checkJs: true
|
|
// - external: files that are added by plugins
|
|
return isPlainJS || isCheckJS || sourceFile.ScriptKind == core.ScriptKindDeferred
|
|
}
|
|
|
|
func getEnclosingContainer(node *ast.Node) *ast.Node {
|
|
return ast.FindAncestor(node.Parent, func(n *ast.Node) bool {
|
|
return binder.GetContainerFlags(n)&binder.ContainerFlagsIsContainer != 0
|
|
})
|
|
}
|
|
|
|
func getDeclarationsOfKind(symbol *ast.Symbol, kind ast.Kind) []*ast.Node {
|
|
return core.Filter(symbol.Declarations, func(d *ast.Node) bool { return d.Kind == kind })
|
|
}
|
|
|
|
func hasType(node *ast.Node) bool {
|
|
return node.Type() != nil
|
|
}
|
|
|
|
func getNonRestParameterCount(sig *Signature) int {
|
|
return len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0)
|
|
}
|
|
|
|
func minAndMax[T any](slice []T, getValue func(value T) int) (int, int) {
|
|
var minValue, maxValue int
|
|
for i, element := range slice {
|
|
value := getValue(element)
|
|
if i == 0 {
|
|
minValue = value
|
|
maxValue = value
|
|
} else {
|
|
minValue = min(minValue, value)
|
|
maxValue = max(maxValue, value)
|
|
}
|
|
}
|
|
return minValue, maxValue
|
|
}
|
|
|
|
func getNonModifierTokenRangeOfNode(node *ast.Node) core.TextRange {
|
|
pos := node.Pos()
|
|
if node.Modifiers() != nil {
|
|
if last := ast.FindLastVisibleNode(node.Modifiers().Nodes); last != nil {
|
|
pos = last.Pos()
|
|
}
|
|
}
|
|
return scanner.GetRangeOfTokenAtPosition(ast.GetSourceFileOfNode(node), pos)
|
|
}
|
|
|
|
type FeatureMapEntry struct {
|
|
lib string
|
|
props []string
|
|
}
|
|
|
|
var getFeatureMap = sync.OnceValue(func() map[string][]FeatureMapEntry {
|
|
return map[string][]FeatureMapEntry{
|
|
"Array": {
|
|
{lib: "es2015", props: []string{"find", "findIndex", "fill", "copyWithin", "entries", "keys", "values"}},
|
|
{lib: "es2016", props: []string{"includes"}},
|
|
{lib: "es2019", props: []string{"flat", "flatMap"}},
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Iterator": {
|
|
{lib: "es2015", props: []string{}},
|
|
},
|
|
"AsyncIterator": {
|
|
{lib: "es2015", props: []string{}},
|
|
},
|
|
"ArrayBuffer": {
|
|
{lib: "es2024", props: []string{
|
|
"maxByteLength",
|
|
"resizable",
|
|
"resize",
|
|
"detached",
|
|
"transfer",
|
|
"transferToFixedLength",
|
|
}},
|
|
},
|
|
"Atomics": {
|
|
{lib: "es2017", props: []string{
|
|
"add",
|
|
"and",
|
|
"compareExchange",
|
|
"exchange",
|
|
"isLockFree",
|
|
"load",
|
|
"or",
|
|
"store",
|
|
"sub",
|
|
"wait",
|
|
"notify",
|
|
"xor",
|
|
}},
|
|
{lib: "es2024", props: []string{
|
|
"waitAsync",
|
|
}},
|
|
},
|
|
"SharedArrayBuffer": {
|
|
{lib: "es2017", props: []string{
|
|
"byteLength",
|
|
"slice",
|
|
}},
|
|
{lib: "es2024", props: []string{
|
|
"growable",
|
|
"maxByteLength",
|
|
"grow",
|
|
}},
|
|
},
|
|
"AsyncIterable": {
|
|
{lib: "es2018", props: []string{}},
|
|
},
|
|
"AsyncIterableIterator": {
|
|
{lib: "es2018", props: []string{}},
|
|
},
|
|
"AsyncGenerator": {
|
|
{lib: "es2018", props: []string{}},
|
|
},
|
|
"AsyncGeneratorFunction": {
|
|
{lib: "es2018", props: []string{}},
|
|
},
|
|
"RegExp": {
|
|
{lib: "es2015", props: []string{"flags", "sticky", "unicode"}},
|
|
{lib: "es2018", props: []string{"dotAll"}},
|
|
{lib: "es2024", props: []string{"unicodeSets"}},
|
|
},
|
|
"Reflect": {
|
|
{lib: "es2015", props: []string{"apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"}},
|
|
},
|
|
"ArrayConstructor": {
|
|
{lib: "es2015", props: []string{"from", "of"}},
|
|
{lib: "esnext", props: []string{"fromAsync"}},
|
|
},
|
|
"ObjectConstructor": {
|
|
{lib: "es2015", props: []string{"assign", "getOwnPropertySymbols", "keys", "is", "setPrototypeOf"}},
|
|
{lib: "es2017", props: []string{"values", "entries", "getOwnPropertyDescriptors"}},
|
|
{lib: "es2019", props: []string{"fromEntries"}},
|
|
{lib: "es2022", props: []string{"hasOwn"}},
|
|
{lib: "es2024", props: []string{"groupBy"}},
|
|
},
|
|
"NumberConstructor": {
|
|
{lib: "es2015", props: []string{"isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt"}},
|
|
},
|
|
"Math": {
|
|
{lib: "es2015", props: []string{"clz32", "imul", "sign", "log10", "log2", "log1p", "expm1", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "hypot", "trunc", "fround", "cbrt"}},
|
|
},
|
|
"Map": {
|
|
{lib: "es2015", props: []string{"entries", "keys", "values"}},
|
|
},
|
|
"MapConstructor": {
|
|
{lib: "es2024", props: []string{"groupBy"}},
|
|
},
|
|
"Set": {
|
|
{lib: "es2015", props: []string{"entries", "keys", "values"}},
|
|
{lib: "esnext", props: []string{
|
|
"union",
|
|
"intersection",
|
|
"difference",
|
|
"symmetricDifference",
|
|
"isSubsetOf",
|
|
"isSupersetOf",
|
|
"isDisjointFrom",
|
|
}},
|
|
},
|
|
"PromiseConstructor": {
|
|
{lib: "es2015", props: []string{"all", "race", "reject", "resolve"}},
|
|
{lib: "es2020", props: []string{"allSettled"}},
|
|
{lib: "es2021", props: []string{"any"}},
|
|
{lib: "es2024", props: []string{"withResolvers"}},
|
|
},
|
|
"Symbol": {
|
|
{lib: "es2015", props: []string{"for", "keyFor"}},
|
|
{lib: "es2019", props: []string{"description"}},
|
|
},
|
|
"WeakMap": {
|
|
{lib: "es2015", props: []string{"entries", "keys", "values"}},
|
|
},
|
|
"WeakSet": {
|
|
{lib: "es2015", props: []string{"entries", "keys", "values"}},
|
|
},
|
|
"String": {
|
|
{lib: "es2015", props: []string{"codePointAt", "includes", "endsWith", "normalize", "repeat", "startsWith", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "sup"}},
|
|
{lib: "es2017", props: []string{"padStart", "padEnd"}},
|
|
{lib: "es2019", props: []string{"trimStart", "trimEnd", "trimLeft", "trimRight"}},
|
|
{lib: "es2020", props: []string{"matchAll"}},
|
|
{lib: "es2021", props: []string{"replaceAll"}},
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2024", props: []string{"isWellFormed", "toWellFormed"}},
|
|
},
|
|
"StringConstructor": {
|
|
{lib: "es2015", props: []string{"fromCodePoint", "raw"}},
|
|
},
|
|
"DateTimeFormat": {
|
|
{lib: "es2017", props: []string{"formatToParts"}},
|
|
},
|
|
"Promise": {
|
|
{lib: "es2015", props: []string{}},
|
|
{lib: "es2018", props: []string{"finally"}},
|
|
},
|
|
"RegExpMatchArray": {
|
|
{lib: "es2018", props: []string{"groups"}},
|
|
},
|
|
"RegExpExecArray": {
|
|
{lib: "es2018", props: []string{"groups"}},
|
|
},
|
|
"Intl": {
|
|
{lib: "es2018", props: []string{"PluralRules"}},
|
|
},
|
|
"NumberFormat": {
|
|
{lib: "es2018", props: []string{"formatToParts"}},
|
|
},
|
|
"SymbolConstructor": {
|
|
{lib: "es2020", props: []string{"matchAll"}},
|
|
{lib: "esnext", props: []string{
|
|
"metadata",
|
|
"dispose",
|
|
"asyncDispose",
|
|
}},
|
|
},
|
|
"DataView": {
|
|
{lib: "es2020", props: []string{"setBigInt64", "setBigUint64", "getBigInt64", "getBigUint64"}},
|
|
},
|
|
"BigInt": {
|
|
{lib: "es2020", props: []string{}},
|
|
},
|
|
"RelativeTimeFormat": {
|
|
{lib: "es2020", props: []string{"format", "formatToParts", "resolvedOptions"}},
|
|
},
|
|
"Int8Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Uint8Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Uint8ClampedArray": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Int16Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Uint16Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Int32Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Uint32Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Float32Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Float64Array": {
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"BigInt64Array": {
|
|
{lib: "es2020", props: []string{}},
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"BigUint64Array": {
|
|
{lib: "es2020", props: []string{}},
|
|
{lib: "es2022", props: []string{"at"}},
|
|
{lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}},
|
|
},
|
|
"Error": {
|
|
{lib: "es2022", props: []string{"cause"}},
|
|
},
|
|
}
|
|
})
|
|
|
|
func rangeOfTypeParameters(sourceFile *ast.SourceFile, typeParameters *ast.NodeList) core.TextRange {
|
|
return core.NewTextRange(typeParameters.Pos()-1, min(len(sourceFile.Text()), scanner.SkipTrivia(sourceFile.Text(), typeParameters.End())+1))
|
|
}
|
|
|
|
func tryGetPropertyAccessOrIdentifierToString(expr *ast.Node) string {
|
|
switch {
|
|
case ast.IsPropertyAccessExpression(expr):
|
|
baseStr := tryGetPropertyAccessOrIdentifierToString(expr.Expression())
|
|
if baseStr != "" {
|
|
return baseStr + "." + entityNameToString(expr.Name())
|
|
}
|
|
case ast.IsElementAccessExpression(expr):
|
|
baseStr := tryGetPropertyAccessOrIdentifierToString(expr.Expression())
|
|
if baseStr != "" && ast.IsPropertyName(expr.AsElementAccessExpression().ArgumentExpression) {
|
|
return baseStr + "." + ast.GetPropertyNameForPropertyNameNode(expr.AsElementAccessExpression().ArgumentExpression)
|
|
}
|
|
case ast.IsIdentifier(expr):
|
|
return expr.Text()
|
|
case ast.IsJsxNamespacedName(expr):
|
|
return entityNameToString(expr)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getFirstJSDocTag(node *ast.Node, f func(*ast.Node) bool) *ast.Node {
|
|
for _, jsdoc := range node.JSDoc(nil) {
|
|
tags := jsdoc.AsJSDoc().Tags
|
|
if tags != nil {
|
|
for _, tag := range tags.Nodes {
|
|
if f(tag) {
|
|
return tag
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getJSDocDeprecatedTag(node *ast.Node) *ast.Node {
|
|
return getFirstJSDocTag(node, ast.IsJSDocDeprecatedTag)
|
|
}
|
|
|
|
func allDeclarationsInSameSourceFile(symbol *ast.Symbol) bool {
|
|
if len(symbol.Declarations) > 1 {
|
|
var sourceFile *ast.SourceFile
|
|
for i, d := range symbol.Declarations {
|
|
if i == 0 {
|
|
sourceFile = ast.GetSourceFileOfNode(d)
|
|
} else if ast.GetSourceFileOfNode(d) != sourceFile {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func containsNonMissingUndefinedType(c *Checker, t *Type) bool {
|
|
var candidate *Type
|
|
if t.flags&TypeFlagsUnion != 0 {
|
|
candidate = t.AsUnionType().types[0]
|
|
} else {
|
|
candidate = t
|
|
}
|
|
return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType
|
|
}
|
|
|
|
func getAnyImportSyntax(node *ast.Node) *ast.Node {
|
|
var importNode *ast.Node
|
|
switch node.Kind {
|
|
case ast.KindImportEqualsDeclaration:
|
|
importNode = node
|
|
case ast.KindImportClause:
|
|
importNode = node.Parent
|
|
case ast.KindNamespaceImport:
|
|
importNode = node.Parent.Parent
|
|
case ast.KindImportSpecifier:
|
|
importNode = node.Parent.Parent.Parent
|
|
default:
|
|
return nil
|
|
}
|
|
return importNode
|
|
}
|
|
|
|
// A reserved member name consists of the byte 0xFE (which is an invalid UTF-8 encoding) followed by one or more
|
|
// characters where the first character is not '@' or '#'. The '@' character indicates that the name is denoted by
|
|
// a well known ES Symbol instance and the '#' character indicates that the name is a PrivateIdentifier.
|
|
func isReservedMemberName(name string) bool {
|
|
return len(name) >= 2 && name[0] == '\xFE' && name[1] != '@' && name[1] != '#'
|
|
}
|
|
|
|
func introducesArgumentsExoticObject(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor,
|
|
ast.KindSetAccessor, ast.KindFunctionDeclaration, ast.KindFunctionExpression:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func symbolsToArray(symbols ast.SymbolTable) []*ast.Symbol {
|
|
var result []*ast.Symbol
|
|
for id, symbol := range symbols {
|
|
if !isReservedMemberName(id) {
|
|
result = append(result, symbol)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// See comment on `declareModuleMember` in `binder.go`.
|
|
func GetCombinedLocalAndExportSymbolFlags(symbol *ast.Symbol) ast.SymbolFlags {
|
|
if symbol.ExportSymbol != nil {
|
|
return symbol.Flags | symbol.ExportSymbol.Flags
|
|
}
|
|
return symbol.Flags
|
|
}
|
|
|
|
func SkipAlias(symbol *ast.Symbol, checker *Checker) *ast.Symbol {
|
|
if symbol.Flags&ast.SymbolFlagsAlias != 0 {
|
|
return checker.GetAliasedSymbol(symbol)
|
|
}
|
|
return symbol
|
|
}
|
|
|
|
// True if the symbol is for an external module, as opposed to a namespace.
|
|
func IsExternalModuleSymbol(moduleSymbol *ast.Symbol) bool {
|
|
firstRune, _ := utf8.DecodeRuneInString(moduleSymbol.Name)
|
|
return moduleSymbol.Flags&ast.SymbolFlagsModule != 0 && firstRune == '"'
|
|
}
|
|
|
|
func (c *Checker) isCanceled() bool {
|
|
return c.ctx != nil && c.ctx.Err() != nil
|
|
}
|
|
|
|
func (c *Checker) checkNotCanceled() {
|
|
if c.wasCanceled {
|
|
panic("Checker was previously cancelled")
|
|
}
|
|
}
|
|
|
|
func (c *Checker) getPackagesMap() map[string]bool {
|
|
if c.packagesMap == nil {
|
|
c.packagesMap = make(map[string]bool)
|
|
resolvedModules := c.program.GetResolvedModules()
|
|
for _, resolvedModulesInFile := range resolvedModules {
|
|
for _, module := range resolvedModulesInFile {
|
|
if module.PackageId.Name != "" {
|
|
c.packagesMap[module.PackageId.Name] = c.packagesMap[module.PackageId.Name] || module.Extension == tspath.ExtensionDts
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return c.packagesMap
|
|
}
|
|
|
|
func (c *Checker) typesPackageExists(packageName string) bool {
|
|
packagesMap := c.getPackagesMap()
|
|
_, ok := packagesMap[module.GetTypesPackageName(packageName)]
|
|
return ok
|
|
}
|
|
|
|
func (c *Checker) packageBundlesTypes(packageName string) bool {
|
|
packagesMap := c.getPackagesMap()
|
|
hasTypes, _ := packagesMap[packageName]
|
|
return hasTypes
|
|
}
|
|
|
|
func ValueToString(value any) string {
|
|
switch value := value.(type) {
|
|
case string:
|
|
return "\"" + printer.EscapeString(value, '"') + "\""
|
|
case jsnum.Number:
|
|
return value.String()
|
|
case bool:
|
|
return core.IfElse(value, "true", "false")
|
|
case jsnum.PseudoBigInt:
|
|
return value.String() + "n"
|
|
}
|
|
panic("unhandled value type in valueToString")
|
|
}
|
|
|
|
func nodeStartsNewLexicalEnvironment(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindConstructor, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindArrowFunction,
|
|
ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindModuleDeclaration, ast.KindSourceFile:
|
|
return true
|
|
}
|
|
return false
|
|
}
|