2025-11-08 09:37:30 +03:00

3893 lines
121 KiB
Go

package ast
import (
"fmt"
"slices"
"strings"
"sync"
"sync/atomic"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
)
// Atomic ids
var (
nextNodeId atomic.Uint64
nextSymbolId atomic.Uint64
)
func GetNodeId(node *Node) NodeId {
id := node.id.Load()
if id == 0 {
// Worst case, we burn a few ids if we have to CAS.
id = nextNodeId.Add(1)
if !node.id.CompareAndSwap(0, id) {
id = node.id.Load()
}
}
return NodeId(id)
}
func GetSymbolId(symbol *Symbol) SymbolId {
id := symbol.id.Load()
if id == 0 {
// Worst case, we burn a few ids if we have to CAS.
id = nextSymbolId.Add(1)
if !symbol.id.CompareAndSwap(0, id) {
id = symbol.id.Load()
}
}
return SymbolId(id)
}
func GetSymbolTable(data *SymbolTable) SymbolTable {
if *data == nil {
*data = make(SymbolTable)
}
return *data
}
func GetMembers(symbol *Symbol) SymbolTable {
return GetSymbolTable(&symbol.Members)
}
func GetExports(symbol *Symbol) SymbolTable {
return GetSymbolTable(&symbol.Exports)
}
func GetLocals(container *Node) SymbolTable {
return GetSymbolTable(&container.LocalsContainerData().Locals)
}
// Determines if a node is missing (either `nil` or empty)
func NodeIsMissing(node *Node) bool {
return node == nil || node.Loc.Pos() == node.Loc.End() && node.Loc.Pos() >= 0 && node.Kind != KindEndOfFile
}
// Determines if a node is present
func NodeIsPresent(node *Node) bool {
return !NodeIsMissing(node)
}
// Determines if a node contains synthetic positions
func NodeIsSynthesized(node *Node) bool {
return PositionIsSynthesized(node.Loc.Pos()) || PositionIsSynthesized(node.Loc.End())
}
func RangeIsSynthesized(loc core.TextRange) bool {
return PositionIsSynthesized(loc.Pos()) || PositionIsSynthesized(loc.End())
}
// Determines whether a position is synthetic
func PositionIsSynthesized(pos int) bool {
return pos < 0
}
func FindLastVisibleNode(nodes []*Node) *Node {
fromEnd := 1
for fromEnd <= len(nodes) && nodes[len(nodes)-fromEnd].Flags&NodeFlagsReparsed != 0 {
fromEnd++
}
if fromEnd <= len(nodes) {
return nodes[len(nodes)-fromEnd]
}
return nil
}
func NodeKindIs(node *Node, kinds ...Kind) bool {
return slices.Contains(kinds, node.Kind)
}
func IsModifierKind(token Kind) bool {
switch token {
case KindAbstractKeyword,
KindAccessorKeyword,
KindAsyncKeyword,
KindConstKeyword,
KindDeclareKeyword,
KindDefaultKeyword,
KindExportKeyword,
KindInKeyword,
KindPublicKeyword,
KindPrivateKeyword,
KindProtectedKeyword,
KindReadonlyKeyword,
KindStaticKeyword,
KindOutKeyword,
KindOverrideKeyword:
return true
}
return false
}
func IsModifier(node *Node) bool {
return IsModifierKind(node.Kind)
}
func IsModifierLike(node *Node) bool {
return IsModifier(node) || IsDecorator(node)
}
func IsKeywordKind(token Kind) bool {
return KindFirstKeyword <= token && token <= KindLastKeyword
}
func IsPunctuationKind(token Kind) bool {
return KindFirstPunctuation <= token && token <= KindLastPunctuation
}
func IsAssignmentOperator(token Kind) bool {
return token >= KindFirstAssignment && token <= KindLastAssignment
}
func IsAssignmentExpression(node *Node, excludeCompoundAssignment bool) bool {
if node.Kind == KindBinaryExpression {
expr := node.AsBinaryExpression()
return (expr.OperatorToken.Kind == KindEqualsToken || !excludeCompoundAssignment && IsAssignmentOperator(expr.OperatorToken.Kind)) &&
IsLeftHandSideExpression(expr.Left)
}
return false
}
func GetRightMostAssignedExpression(node *Node) *Node {
for IsAssignmentExpression(node, true /*excludeCompoundAssignment*/) {
node = node.AsBinaryExpression().Right
}
return node
}
func IsDestructuringAssignment(node *Node) bool {
if IsAssignmentExpression(node, true /*excludeCompoundAssignment*/) {
kind := node.AsBinaryExpression().Left.Kind
return kind == KindObjectLiteralExpression || kind == KindArrayLiteralExpression
}
return false
}
// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property
// assignment in an object literal that is an assignment target, or if it is parented by an array literal that is
// an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'.
// (Note that `p` is not a target in the above examples, only `a`.)
func IsAssignmentTarget(node *Node) bool {
return GetAssignmentTarget(node) != nil
}
// Returns the BinaryExpression, PrefixUnaryExpression, PostfixUnaryExpression, or ForInOrOfStatement that references
// the given node as an assignment target
func GetAssignmentTarget(node *Node) *Node {
for {
parent := node.Parent
switch parent.Kind {
case KindBinaryExpression:
if IsAssignmentOperator(parent.AsBinaryExpression().OperatorToken.Kind) && parent.AsBinaryExpression().Left == node {
return parent
}
return nil
case KindPrefixUnaryExpression:
if parent.AsPrefixUnaryExpression().Operator == KindPlusPlusToken || parent.AsPrefixUnaryExpression().Operator == KindMinusMinusToken {
return parent
}
return nil
case KindPostfixUnaryExpression:
if parent.AsPostfixUnaryExpression().Operator == KindPlusPlusToken || parent.AsPostfixUnaryExpression().Operator == KindMinusMinusToken {
return parent
}
return nil
case KindForInStatement, KindForOfStatement:
if parent.AsForInOrOfStatement().Initializer == node {
return parent
}
return nil
case KindParenthesizedExpression, KindArrayLiteralExpression, KindSpreadElement, KindNonNullExpression:
node = parent
case KindSpreadAssignment:
node = parent.Parent
case KindShorthandPropertyAssignment:
if parent.AsShorthandPropertyAssignment().Name() != node {
return nil
}
node = parent.Parent
case KindPropertyAssignment:
if parent.AsPropertyAssignment().Name() == node {
return nil
}
node = parent.Parent
default:
return nil
}
}
}
func IsLogicalBinaryOperator(token Kind) bool {
return token == KindBarBarToken || token == KindAmpersandAmpersandToken
}
func IsLogicalOrCoalescingBinaryOperator(token Kind) bool {
return IsLogicalBinaryOperator(token) || token == KindQuestionQuestionToken
}
func IsLogicalOrCoalescingBinaryExpression(expr *Node) bool {
return IsBinaryExpression(expr) && IsLogicalOrCoalescingBinaryOperator(expr.AsBinaryExpression().OperatorToken.Kind)
}
func IsLogicalOrCoalescingAssignmentOperator(token Kind) bool {
return token == KindBarBarEqualsToken || token == KindAmpersandAmpersandEqualsToken || token == KindQuestionQuestionEqualsToken
}
func IsLogicalOrCoalescingAssignmentExpression(expr *Node) bool {
return IsBinaryExpression(expr) && IsLogicalOrCoalescingAssignmentOperator(expr.AsBinaryExpression().OperatorToken.Kind)
}
func IsLogicalExpression(node *Node) bool {
for {
if node.Kind == KindParenthesizedExpression {
node = node.AsParenthesizedExpression().Expression
} else if node.Kind == KindPrefixUnaryExpression && node.AsPrefixUnaryExpression().Operator == KindExclamationToken {
node = node.AsPrefixUnaryExpression().Operand
} else {
return IsLogicalOrCoalescingBinaryExpression(node)
}
}
}
func IsTokenKind(token Kind) bool {
return KindFirstToken <= token && token <= KindLastToken
}
func IsAccessor(node *Node) bool {
return node.Kind == KindGetAccessor || node.Kind == KindSetAccessor
}
func IsPropertyNameLiteral(node *Node) bool {
switch node.Kind {
case KindIdentifier,
KindStringLiteral,
KindNoSubstitutionTemplateLiteral,
KindNumericLiteral:
return true
}
return false
}
func IsMemberName(node *Node) bool {
return node.Kind == KindIdentifier || node.Kind == KindPrivateIdentifier
}
func IsEntityName(node *Node) bool {
return node.Kind == KindIdentifier || node.Kind == KindQualifiedName
}
func IsPropertyName(node *Node) bool {
switch node.Kind {
case KindIdentifier,
KindPrivateIdentifier,
KindStringLiteral,
KindNumericLiteral,
KindComputedPropertyName:
return true
}
return false
}
// Return true if the given identifier is classified as an IdentifierName by inspecting the parent of the node
func IsIdentifierName(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindPropertyDeclaration, KindPropertySignature, KindMethodDeclaration, KindMethodSignature, KindGetAccessor,
KindSetAccessor, KindEnumMember, KindPropertyAssignment, KindPropertyAccessExpression:
return parent.Name() == node
case KindQualifiedName:
return parent.AsQualifiedName().Right == node
case KindBindingElement:
return parent.AsBindingElement().PropertyName == node
case KindImportSpecifier:
return parent.AsImportSpecifier().PropertyName == node
case KindExportSpecifier, KindJsxAttribute, KindJsxSelfClosingElement, KindJsxOpeningElement, KindJsxClosingElement:
return true
}
return false
}
func IsPushOrUnshiftIdentifier(node *Node) bool {
text := node.Text()
return text == "push" || text == "unshift"
}
func IsBooleanLiteral(node *Node) bool {
return node.Kind == KindTrueKeyword || node.Kind == KindFalseKeyword
}
func IsLiteralKind(kind Kind) bool {
return KindFirstLiteralToken <= kind && kind <= KindLastLiteralToken
}
func IsLiteralExpression(node *Node) bool {
return IsLiteralKind(node.Kind)
}
func IsStringLiteralLike(node *Node) bool {
switch node.Kind {
case KindStringLiteral, KindNoSubstitutionTemplateLiteral:
return true
}
return false
}
func IsStringOrNumericLiteralLike(node *Node) bool {
return IsStringLiteralLike(node) || IsNumericLiteral(node)
}
func IsSignedNumericLiteral(node *Node) bool {
if node.Kind == KindPrefixUnaryExpression {
node := node.AsPrefixUnaryExpression()
return (node.Operator == KindPlusToken || node.Operator == KindMinusToken) && IsNumericLiteral(node.Operand)
}
return false
}
// Determines if a node is part of an OptionalChain
func IsOptionalChain(node *Node) bool {
if node.Flags&NodeFlagsOptionalChain != 0 {
switch node.Kind {
case KindPropertyAccessExpression,
KindElementAccessExpression,
KindCallExpression,
KindNonNullExpression:
return true
}
}
return false
}
func getQuestionDotToken(node *Expression) *TokenNode {
switch node.Kind {
case KindPropertyAccessExpression:
return node.AsPropertyAccessExpression().QuestionDotToken
case KindElementAccessExpression:
return node.AsElementAccessExpression().QuestionDotToken
case KindCallExpression:
return node.AsCallExpression().QuestionDotToken
}
panic("Unhandled case in getQuestionDotToken")
}
// Determines if node is the root expression of an OptionalChain
func IsOptionalChainRoot(node *Expression) bool {
return IsOptionalChain(node) && !IsNonNullExpression(node) && getQuestionDotToken(node) != nil
}
// Determines whether a node is the outermost `OptionalChain` in an ECMAScript `OptionalExpression`:
//
// 1. For `a?.b.c`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.`)
// 2. For `a?.b!`, the outermost chain is `a?.b` (`b` is the end of the chain starting at `a?.`)
// 3. For `(a?.b.c).d`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.` since parens end the chain)
// 4. For `a?.b.c?.d`, both `a?.b.c` and `a?.b.c?.d` are outermost (`c` is the end of the chain starting at `a?.`, and `d` is
// the end of the chain starting at `c?.`)
// 5. For `a?.(b?.c).d`, both `b?.c` and `a?.(b?.c)d` are outermost (`c` is the end of the chain starting at `b`, and `d` is
// the end of the chain starting at `a?.`)
func IsOutermostOptionalChain(node *Expression) bool {
parent := node.Parent
return !IsOptionalChain(parent) || // cases 1, 2, and 3
IsOptionalChainRoot(parent) || // case 4
node != parent.Expression() // case 5
}
// Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`).
func IsExpressionOfOptionalChainRoot(node *Node) bool {
return IsOptionalChainRoot(node.Parent) && node.Parent.Expression() == node
}
func IsNullishCoalesce(node *Node) bool {
return node.Kind == KindBinaryExpression && node.AsBinaryExpression().OperatorToken.Kind == KindQuestionQuestionToken
}
func IsAssertionExpression(node *Node) bool {
kind := node.Kind
return kind == KindTypeAssertionExpression || kind == KindAsExpression
}
func isLeftHandSideExpressionKind(kind Kind) bool {
switch kind {
case KindPropertyAccessExpression, KindElementAccessExpression, KindNewExpression, KindCallExpression,
KindJsxElement, KindJsxSelfClosingElement, KindJsxFragment, KindTaggedTemplateExpression, KindArrayLiteralExpression,
KindParenthesizedExpression, KindObjectLiteralExpression, KindClassExpression, KindFunctionExpression, KindIdentifier,
KindPrivateIdentifier, KindRegularExpressionLiteral, KindNumericLiteral, KindBigIntLiteral, KindStringLiteral,
KindNoSubstitutionTemplateLiteral, KindTemplateExpression, KindFalseKeyword, KindNullKeyword, KindThisKeyword,
KindTrueKeyword, KindSuperKeyword, KindNonNullExpression, KindExpressionWithTypeArguments, KindMetaProperty,
KindImportKeyword, KindMissingDeclaration:
return true
}
return false
}
// Determines whether a node is a LeftHandSideExpression based only on its kind.
func IsLeftHandSideExpression(node *Node) bool {
return isLeftHandSideExpressionKind(node.Kind)
}
func isUnaryExpressionKind(kind Kind) bool {
switch kind {
case KindPrefixUnaryExpression,
KindPostfixUnaryExpression,
KindDeleteExpression,
KindTypeOfExpression,
KindVoidExpression,
KindAwaitExpression,
KindTypeAssertionExpression:
return true
}
return isLeftHandSideExpressionKind(kind)
}
// Determines whether a node is a UnaryExpression based only on its kind.
func IsUnaryExpression(node *Node) bool {
return isUnaryExpressionKind(node.Kind)
}
func isExpressionKind(kind Kind) bool {
switch kind {
case KindConditionalExpression,
KindYieldExpression,
KindArrowFunction,
KindBinaryExpression,
KindSpreadElement,
KindAsExpression,
KindOmittedExpression,
KindCommaListExpression,
KindPartiallyEmittedExpression,
KindSatisfiesExpression:
return true
}
return isUnaryExpressionKind(kind)
}
// Determines whether a node is an expression based only on its kind.
func IsExpression(node *Node) bool {
return isExpressionKind(node.Kind)
}
func IsCommaExpression(node *Node) bool {
return node.Kind == KindBinaryExpression && node.AsBinaryExpression().OperatorToken.Kind == KindCommaToken
}
func IsCommaSequence(node *Node) bool {
// !!!
// New compiler just has binary expressinons.
// Maybe this should consider KindCommaListExpression even though we don't generate them.
return IsCommaExpression(node)
}
func IsIterationStatement(node *Node, lookInLabeledStatements bool) bool {
switch node.Kind {
case KindForStatement,
KindForInStatement,
KindForOfStatement,
KindDoStatement,
KindWhileStatement:
return true
case KindLabeledStatement:
return lookInLabeledStatements && IsIterationStatement((node.AsLabeledStatement()).Statement, lookInLabeledStatements)
}
return false
}
// Determines if a node is a property or element access expression
func IsAccessExpression(node *Node) bool {
return node.Kind == KindPropertyAccessExpression || node.Kind == KindElementAccessExpression
}
func isFunctionLikeDeclarationKind(kind Kind) bool {
switch kind {
case KindFunctionDeclaration,
KindMethodDeclaration,
KindConstructor,
KindGetAccessor,
KindSetAccessor,
KindFunctionExpression,
KindArrowFunction:
return true
}
return false
}
// Determines if a node is function-like (but is not a signature declaration)
func IsFunctionLikeDeclaration(node *Node) bool {
// TODO(rbuckton): Move `node != nil` test to call sites
return node != nil && isFunctionLikeDeclarationKind(node.Kind)
}
func IsFunctionLikeKind(kind Kind) bool {
switch kind {
case KindMethodSignature,
KindCallSignature,
KindJSDocSignature,
KindConstructSignature,
KindIndexSignature,
KindFunctionType,
KindConstructorType:
return true
}
return isFunctionLikeDeclarationKind(kind)
}
// Determines if a node is function- or signature-like.
func IsFunctionLike(node *Node) bool {
// TODO(rbuckton): Move `node != nil` test to call sites
return node != nil && IsFunctionLikeKind(node.Kind)
}
func IsFunctionLikeOrClassStaticBlockDeclaration(node *Node) bool {
return node != nil && (IsFunctionLike(node) || IsClassStaticBlockDeclaration(node))
}
func IsFunctionOrSourceFile(node *Node) bool {
return IsFunctionLike(node) || IsSourceFile(node)
}
func IsClassLike(node *Node) bool {
return node.Kind == KindClassDeclaration || node.Kind == KindClassExpression
}
func IsClassOrInterfaceLike(node *Node) bool {
return node.Kind == KindClassDeclaration || node.Kind == KindClassExpression || node.Kind == KindInterfaceDeclaration
}
func IsClassElement(node *Node) bool {
switch node.Kind {
case KindConstructor,
KindPropertyDeclaration,
KindMethodDeclaration,
KindGetAccessor,
KindSetAccessor,
KindIndexSignature,
KindClassStaticBlockDeclaration,
KindSemicolonClassElement:
return true
}
return false
}
func isMethodOrAccessor(node *Node) bool {
switch node.Kind {
case KindMethodDeclaration, KindGetAccessor, KindSetAccessor:
return true
}
return false
}
func IsPrivateIdentifierClassElementDeclaration(node *Node) bool {
return (IsPropertyDeclaration(node) || isMethodOrAccessor(node)) && IsPrivateIdentifier(node.Name())
}
func IsObjectLiteralOrClassExpressionMethodOrAccessor(node *Node) bool {
kind := node.Kind
return (kind == KindMethodDeclaration || kind == KindGetAccessor || kind == KindSetAccessor) &&
(node.Parent.Kind == KindObjectLiteralExpression || node.Parent.Kind == KindClassExpression)
}
func IsTypeElement(node *Node) bool {
switch node.Kind {
case KindConstructSignature,
KindCallSignature,
KindPropertySignature,
KindMethodSignature,
KindIndexSignature,
KindGetAccessor,
KindSetAccessor,
KindNotEmittedTypeElement:
return true
}
return false
}
func IsObjectLiteralElement(node *Node) bool {
switch node.Kind {
case KindPropertyAssignment,
KindShorthandPropertyAssignment,
KindSpreadAssignment,
KindMethodDeclaration,
KindGetAccessor,
KindSetAccessor:
return true
}
return false
}
func IsObjectLiteralMethod(node *Node) bool {
return node != nil && node.Kind == KindMethodDeclaration && node.Parent.Kind == KindObjectLiteralExpression
}
func IsAutoAccessorPropertyDeclaration(node *Node) bool {
return IsPropertyDeclaration(node) && HasAccessorModifier(node)
}
func IsParameterPropertyDeclaration(node *Node, parent *Node) bool {
return IsParameter(node) && HasSyntacticModifier(node, ModifierFlagsParameterPropertyModifier) && parent.Kind == KindConstructor
}
func IsJsxChild(node *Node) bool {
switch node.Kind {
case KindJsxElement,
KindJsxExpression,
KindJsxSelfClosingElement,
KindJsxText,
KindJsxFragment:
return true
}
return false
}
func IsJsxAttributeLike(node *Node) bool {
return IsJsxAttribute(node) || IsJsxSpreadAttribute(node)
}
func isDeclarationStatementKind(kind Kind) bool {
switch kind {
case KindFunctionDeclaration,
KindMissingDeclaration,
KindClassDeclaration,
KindInterfaceDeclaration,
KindTypeAliasDeclaration,
KindJSTypeAliasDeclaration,
KindEnumDeclaration,
KindModuleDeclaration,
KindImportDeclaration,
KindJSImportDeclaration,
KindImportEqualsDeclaration,
KindExportDeclaration,
KindExportAssignment,
KindJSExportAssignment,
KindCommonJSExport,
KindNamespaceExportDeclaration:
return true
}
return false
}
// Determines whether a node is a DeclarationStatement. Ideally this does not use Parent pointers, but it may use them
// to rule out a Block node that is part of `try` or `catch` or is the Block-like body of a function.
//
// NOTE: ECMA262 would just call this a Declaration
func IsDeclarationStatement(node *Node) bool {
return isDeclarationStatementKind(node.Kind)
}
func isStatementKindButNotDeclarationKind(kind Kind) bool {
switch kind {
case KindBreakStatement,
KindContinueStatement,
KindDebuggerStatement,
KindDoStatement,
KindExpressionStatement,
KindEmptyStatement,
KindForInStatement,
KindForOfStatement,
KindForStatement,
KindIfStatement,
KindLabeledStatement,
KindReturnStatement,
KindSwitchStatement,
KindThrowStatement,
KindTryStatement,
KindVariableStatement,
KindWhileStatement,
KindWithStatement,
KindNotEmittedStatement:
return true
}
return false
}
// Determines whether a node is a Statement that is not also a Declaration. Ideally this does not use Parent pointers,
// but it may use them to rule out a Block node that is part of `try` or `catch` or is the Block-like body of a function.
//
// NOTE: ECMA262 would just call this a Statement
func IsStatementButNotDeclaration(node *Node) bool {
return isStatementKindButNotDeclarationKind(node.Kind)
}
// Determines whether a node is a Statement. Ideally this does not use Parent pointers, but it may use
// them to rule out a Block node that is part of `try` or `catch` or is the Block-like body of a function.
//
// NOTE: ECMA262 would call this either a StatementListItem or ModuleListItem
func IsStatement(node *Node) bool {
kind := node.Kind
return isStatementKindButNotDeclarationKind(kind) || isDeclarationStatementKind(kind) || isBlockStatement(node)
}
// Determines whether a node is a BlockStatement. If parents are available, this ensures the Block is
// not part of a `try` statement, `catch` clause, or the Block-like body of a function
func isBlockStatement(node *Node) bool {
if node.Kind != KindBlock {
return false
}
if node.Parent != nil && (node.Parent.Kind == KindTryStatement || node.Parent.Kind == KindCatchClause) {
return false
}
return !IsFunctionBlock(node)
}
// Determines whether a node is the Block-like body of a function by walking the parent of the node
func IsFunctionBlock(node *Node) bool {
return node != nil && node.Kind == KindBlock && node.Parent != nil && IsFunctionLike(node.Parent)
}
func GetStatementsOfBlock(block *Node) *StatementList {
switch block.Kind {
case KindBlock:
return block.AsBlock().Statements
case KindModuleBlock:
return block.AsModuleBlock().Statements
case KindSourceFile:
return block.AsSourceFile().Statements
}
panic("Unhandled case in getStatementsOfBlock")
}
func IsBlockOrCatchScoped(declaration *Node) bool {
return GetCombinedNodeFlags(declaration)&NodeFlagsBlockScoped != 0 || IsCatchClauseVariableDeclarationOrBindingElement(declaration)
}
func IsCatchClauseVariableDeclarationOrBindingElement(declaration *Node) bool {
node := GetRootDeclaration(declaration)
return node.Kind == KindVariableDeclaration && node.Parent.Kind == KindCatchClause
}
func IsTypeNodeKind(kind Kind) bool {
switch kind {
case KindAnyKeyword,
KindUnknownKeyword,
KindNumberKeyword,
KindBigIntKeyword,
KindObjectKeyword,
KindBooleanKeyword,
KindStringKeyword,
KindSymbolKeyword,
KindVoidKeyword,
KindUndefinedKeyword,
KindNeverKeyword,
KindIntrinsicKeyword,
KindExpressionWithTypeArguments,
KindJSDocAllType,
KindJSDocNullableType,
KindJSDocNonNullableType,
KindJSDocOptionalType,
KindJSDocVariadicType:
return true
}
return kind >= KindFirstTypeNode && kind <= KindLastTypeNode
}
func IsTypeNode(node *Node) bool {
return IsTypeNodeKind(node.Kind)
}
func IsJSDocKind(kind Kind) bool {
return KindFirstJSDocNode <= kind && kind <= KindLastJSDocNode
}
func isJSDocTypeAssertion(_ *Node) bool {
return false // !!!
}
func IsPrologueDirective(node *Node) bool {
return node.Kind == KindExpressionStatement &&
node.AsExpressionStatement().Expression.Kind == KindStringLiteral
}
type OuterExpressionKinds int16
const (
OEKParentheses OuterExpressionKinds = 1 << 0
OEKTypeAssertions OuterExpressionKinds = 1 << 1
OEKNonNullAssertions OuterExpressionKinds = 1 << 2
OEKPartiallyEmittedExpressions OuterExpressionKinds = 1 << 3
OEKExpressionsWithTypeArguments OuterExpressionKinds = 1 << 4
OEKSatisfies OuterExpressionKinds = 1 << 5
OEKExcludeJSDocTypeAssertion = 1 << 6
OEKAssertions = OEKTypeAssertions | OEKNonNullAssertions | OEKSatisfies
OEKAll = OEKParentheses | OEKAssertions | OEKPartiallyEmittedExpressions | OEKExpressionsWithTypeArguments
)
// Determines whether node is an "outer expression" of the provided kinds
func IsOuterExpression(node *Expression, kinds OuterExpressionKinds) bool {
switch node.Kind {
case KindParenthesizedExpression:
return kinds&OEKParentheses != 0 && !(kinds&OEKExcludeJSDocTypeAssertion != 0 && isJSDocTypeAssertion(node))
case KindTypeAssertionExpression, KindAsExpression:
return kinds&OEKTypeAssertions != 0
case KindSatisfiesExpression:
return kinds&(OEKExpressionsWithTypeArguments|OEKSatisfies) != 0
case KindExpressionWithTypeArguments:
return kinds&OEKExpressionsWithTypeArguments != 0
case KindNonNullExpression:
return kinds&OEKNonNullAssertions != 0
case KindPartiallyEmittedExpression:
return kinds&OEKPartiallyEmittedExpressions != 0
}
return false
}
// Descends into an expression, skipping past "outer expressions" of the provided kinds
func SkipOuterExpressions(node *Expression, kinds OuterExpressionKinds) *Expression {
for IsOuterExpression(node, kinds) {
node = node.Expression()
}
return node
}
// Skips past the parentheses of an expression
func SkipParentheses(node *Expression) *Expression {
return SkipOuterExpressions(node, OEKParentheses)
}
func SkipTypeParentheses(node *Node) *Node {
for IsParenthesizedTypeNode(node) {
node = node.AsParenthesizedTypeNode().Type
}
return node
}
func SkipPartiallyEmittedExpressions(node *Expression) *Expression {
return SkipOuterExpressions(node, OEKPartiallyEmittedExpressions)
}
// Walks up the parents of a parenthesized expression to find the containing node
func WalkUpParenthesizedExpressions(node *Expression) *Node {
for node != nil && node.Kind == KindParenthesizedExpression {
node = node.Parent
}
return node
}
// Walks up the parents of a parenthesized type to find the containing node
func WalkUpParenthesizedTypes(node *TypeNode) *Node {
for node != nil && node.Kind == KindParenthesizedType {
node = node.Parent
}
return node
}
// Walks up the parents of a node to find the containing SourceFile
func GetSourceFileOfNode(node *Node) *SourceFile {
for node != nil {
if node.Kind == KindSourceFile {
return node.AsSourceFile()
}
node = node.Parent
}
return nil
}
var setParentInChildrenPool = sync.Pool{
New: func() any {
return newParentInChildrenSetter()
},
}
func newParentInChildrenSetter() func(node *Node) bool {
// Consolidate state into one allocation.
// Similar to https://go.dev/cl/552375.
var state struct {
parent *Node
visit func(*Node) bool
}
state.visit = func(node *Node) bool {
if state.parent != nil {
node.Parent = state.parent
}
saveParent := state.parent
state.parent = node
node.ForEachChild(state.visit)
state.parent = saveParent
return false
}
return state.visit
}
func SetParentInChildren(node *Node) {
fn := setParentInChildrenPool.Get().(func(node *Node) bool)
defer setParentInChildrenPool.Put(fn)
fn(node)
}
// This should never be called outside the parser
func SetImportsOfSourceFile(node *SourceFile, imports []*LiteralLikeNode) {
node.imports = imports
}
// Walks up the parents of a node to find the ancestor that matches the callback
func FindAncestor(node *Node, callback func(*Node) bool) *Node {
for node != nil {
if callback(node) {
return node
}
node = node.Parent
}
return nil
}
// Walks up the parents of a node to find the ancestor that matches the kind
func FindAncestorKind(node *Node, kind Kind) *Node {
for node != nil {
if node.Kind == kind {
return node
}
node = node.Parent
}
return nil
}
type FindAncestorResult int32
const (
FindAncestorFalse FindAncestorResult = iota
FindAncestorTrue
FindAncestorQuit
)
func ToFindAncestorResult(b bool) FindAncestorResult {
if b {
return FindAncestorTrue
}
return FindAncestorFalse
}
// Walks up the parents of a node to find the ancestor that matches the callback
func FindAncestorOrQuit(node *Node, callback func(*Node) FindAncestorResult) *Node {
for node != nil {
switch callback(node) {
case FindAncestorQuit:
return nil
case FindAncestorTrue:
return node
}
node = node.Parent
}
return nil
}
func IsNodeDescendantOf(node *Node, ancestor *Node) bool {
for node != nil {
if node == ancestor {
return true
}
node = node.Parent
}
return false
}
func ModifierToFlag(token Kind) ModifierFlags {
switch token {
case KindStaticKeyword:
return ModifierFlagsStatic
case KindPublicKeyword:
return ModifierFlagsPublic
case KindProtectedKeyword:
return ModifierFlagsProtected
case KindPrivateKeyword:
return ModifierFlagsPrivate
case KindAbstractKeyword:
return ModifierFlagsAbstract
case KindAccessorKeyword:
return ModifierFlagsAccessor
case KindExportKeyword:
return ModifierFlagsExport
case KindDeclareKeyword:
return ModifierFlagsAmbient
case KindConstKeyword:
return ModifierFlagsConst
case KindDefaultKeyword:
return ModifierFlagsDefault
case KindAsyncKeyword:
return ModifierFlagsAsync
case KindReadonlyKeyword:
return ModifierFlagsReadonly
case KindOverrideKeyword:
return ModifierFlagsOverride
case KindInKeyword:
return ModifierFlagsIn
case KindOutKeyword:
return ModifierFlagsOut
case KindDecorator:
return ModifierFlagsDecorator
}
return ModifierFlagsNone
}
func ModifiersToFlags(modifiers []*Node) ModifierFlags {
var flags ModifierFlags
for _, modifier := range modifiers {
flags |= ModifierToFlag(modifier.Kind)
}
return flags
}
func HasSyntacticModifier(node *Node, flags ModifierFlags) bool {
return node.ModifierFlags()&flags != 0
}
func HasAccessorModifier(node *Node) bool {
return HasSyntacticModifier(node, ModifierFlagsAccessor)
}
func HasStaticModifier(node *Node) bool {
return HasSyntacticModifier(node, ModifierFlagsStatic)
}
func IsStatic(node *Node) bool {
// https://tc39.es/ecma262/#sec-static-semantics-isstatic
return IsClassElement(node) && HasStaticModifier(node) || IsClassStaticBlockDeclaration(node)
}
func CanHaveSymbol(node *Node) bool {
switch node.Kind {
case KindArrowFunction, KindBinaryExpression, KindBindingElement, KindCallExpression, KindCallSignature,
KindClassDeclaration, KindClassExpression, KindClassStaticBlockDeclaration, KindConstructor, KindConstructorType,
KindConstructSignature, KindElementAccessExpression, KindEnumDeclaration, KindEnumMember, KindExportAssignment,
KindExportDeclaration, KindExportSpecifier, KindFunctionDeclaration, KindFunctionExpression, KindFunctionType,
KindGetAccessor, KindImportClause, KindImportEqualsDeclaration, KindImportSpecifier, KindIndexSignature,
KindInterfaceDeclaration, KindJSExportAssignment, KindJSTypeAliasDeclaration, KindCommonJSExport,
KindJsxAttribute, KindJsxAttributes, KindJsxSpreadAttribute, KindMappedType, KindMethodDeclaration,
KindMethodSignature, KindModuleDeclaration, KindNamedTupleMember, KindNamespaceExport, KindNamespaceExportDeclaration,
KindNamespaceImport, KindNewExpression, KindNoSubstitutionTemplateLiteral, KindNumericLiteral, KindObjectLiteralExpression,
KindParameter, KindPropertyAccessExpression, KindPropertyAssignment, KindPropertyDeclaration, KindPropertySignature,
KindSetAccessor, KindShorthandPropertyAssignment, KindSourceFile, KindSpreadAssignment, KindStringLiteral,
KindTypeAliasDeclaration, KindTypeLiteral, KindTypeParameter, KindVariableDeclaration:
return true
}
return false
}
func CanHaveIllegalDecorators(node *Node) bool {
switch node.Kind {
case KindPropertyAssignment, KindShorthandPropertyAssignment,
KindFunctionDeclaration, KindConstructor,
KindIndexSignature, KindClassStaticBlockDeclaration,
KindMissingDeclaration, KindVariableStatement,
KindInterfaceDeclaration, KindTypeAliasDeclaration,
KindEnumDeclaration, KindModuleDeclaration,
KindImportEqualsDeclaration, KindImportDeclaration, KindJSImportDeclaration,
KindNamespaceExportDeclaration, KindExportDeclaration,
KindExportAssignment:
return true
}
return false
}
func CanHaveIllegalModifiers(node *Node) bool {
switch node.Kind {
case KindClassStaticBlockDeclaration,
KindPropertyAssignment,
KindShorthandPropertyAssignment,
KindMissingDeclaration,
KindNamespaceExportDeclaration:
return true
}
return false
}
func CanHaveModifiers(node *Node) bool {
switch node.Kind {
case KindTypeParameter,
KindParameter,
KindPropertySignature,
KindPropertyDeclaration,
KindMethodSignature,
KindMethodDeclaration,
KindConstructor,
KindGetAccessor,
KindSetAccessor,
KindIndexSignature,
KindConstructorType,
KindFunctionExpression,
KindArrowFunction,
KindClassExpression,
KindVariableStatement,
KindFunctionDeclaration,
KindClassDeclaration,
KindInterfaceDeclaration,
KindTypeAliasDeclaration,
KindEnumDeclaration,
KindModuleDeclaration,
KindImportEqualsDeclaration,
KindImportDeclaration,
KindJSImportDeclaration,
KindExportAssignment,
KindExportDeclaration:
return true
}
return false
}
func CanHaveDecorators(node *Node) bool {
switch node.Kind {
case KindParameter,
KindPropertyDeclaration,
KindMethodDeclaration,
KindGetAccessor,
KindSetAccessor,
KindClassExpression,
KindClassDeclaration:
return true
}
return false
}
func IsFunctionOrModuleBlock(node *Node) bool {
return IsSourceFile(node) || IsModuleBlock(node) || IsBlock(node) && IsFunctionLike(node.Parent)
}
func IsFunctionExpressionOrArrowFunction(node *Node) bool {
return IsFunctionExpression(node) || IsArrowFunction(node)
}
// Warning: This has the same semantics as the forEach family of functions in that traversal terminates
// in the event that 'visitor' returns true.
func ForEachReturnStatement(body *Node, visitor func(stmt *Node) bool) bool {
var traverse func(*Node) bool
traverse = func(node *Node) bool {
switch node.Kind {
case KindReturnStatement:
return visitor(node)
case KindCaseBlock, KindBlock, KindIfStatement, KindDoStatement, KindWhileStatement, KindForStatement, KindForInStatement,
KindForOfStatement, KindWithStatement, KindSwitchStatement, KindCaseClause, KindDefaultClause, KindLabeledStatement,
KindTryStatement, KindCatchClause:
return node.ForEachChild(traverse)
}
return false
}
return traverse(body)
}
func GetRootDeclaration(node *Node) *Node {
for node.Kind == KindBindingElement {
node = node.Parent.Parent
}
return node
}
func getCombinedFlags[T ~uint32](node *Node, getFlags func(*Node) T) T {
node = GetRootDeclaration(node)
flags := getFlags(node)
if node.Kind == KindVariableDeclaration {
node = node.Parent
}
if node != nil && node.Kind == KindVariableDeclarationList {
flags |= getFlags(node)
node = node.Parent
}
if node != nil && node.Kind == KindVariableStatement {
flags |= getFlags(node)
}
return flags
}
func GetCombinedModifierFlags(node *Node) ModifierFlags {
return getCombinedFlags(node, (*Node).ModifierFlags)
}
func GetCombinedNodeFlags(node *Node) NodeFlags {
return getCombinedFlags(node, getNodeFlags)
}
func getNodeFlags(node *Node) NodeFlags {
return node.Flags
}
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of an `await using` declaration.
func IsVarAwaitUsing(node *Node) bool {
return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsAwaitUsing
}
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `using` declaration.
func IsVarUsing(node *Node) bool {
return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsUsing
}
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `const` declaration.
func IsVarConst(node *Node) bool {
return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsConst
}
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `const`, `using` or `await using` declaration.
func IsVarConstLike(node *Node) bool {
switch GetCombinedNodeFlags(node) & NodeFlagsBlockScoped {
case NodeFlagsConst, NodeFlagsUsing, NodeFlagsAwaitUsing:
return true
}
return false
}
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `let` declaration.
func IsVarLet(node *Node) bool {
return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsLet
}
func IsImportMeta(node *Node) bool {
if node.Kind == KindMetaProperty {
return node.AsMetaProperty().KeywordToken == KindImportKeyword && node.AsMetaProperty().Name().AsIdentifier().Text == "meta"
}
return false
}
func WalkUpBindingElementsAndPatterns(binding *Node) *Node {
node := binding.Parent
for IsBindingElement(node.Parent) {
node = node.Parent.Parent
}
return node.Parent
}
func IsSourceFileJS(file *SourceFile) bool {
return file.ScriptKind == core.ScriptKindJS || file.ScriptKind == core.ScriptKindJSX
}
func IsInJSFile(node *Node) bool {
return node != nil && node.Flags&NodeFlagsJavaScriptFile != 0
}
func IsDeclaration(node *Node) bool {
if node.Kind == KindTypeParameter {
return node.Parent != nil
}
return IsDeclarationNode(node)
}
// True if `name` is the name of a declaration node
func IsDeclarationName(name *Node) bool {
return !IsSourceFile(name) && !IsBindingPattern(name) && IsDeclaration(name.Parent) && name.Parent.Name() == name
}
// Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`.
func IsDeclarationNameOrImportPropertyName(name *Node) bool {
switch name.Parent.Kind {
case KindImportSpecifier, KindExportSpecifier:
return IsIdentifier(name) || name.Kind == KindStringLiteral
default:
return IsDeclarationName(name)
}
}
func IsLiteralComputedPropertyDeclarationName(node *Node) bool {
return IsStringOrNumericLiteralLike(node) &&
node.Parent.Kind == KindComputedPropertyName &&
IsDeclaration(node.Parent.Parent)
}
func IsExternalModuleImportEqualsDeclaration(node *Node) bool {
return node.Kind == KindImportEqualsDeclaration && node.AsImportEqualsDeclaration().ModuleReference.Kind == KindExternalModuleReference
}
func IsModuleOrEnumDeclaration(node *Node) bool {
return node.Kind == KindModuleDeclaration || node.Kind == KindEnumDeclaration
}
func IsLiteralImportTypeNode(node *Node) bool {
return IsImportTypeNode(node) && IsLiteralTypeNode(node.AsImportTypeNode().Argument) && IsStringLiteral(node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal)
}
func IsJsxTagName(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindJsxOpeningElement, KindJsxClosingElement, KindJsxSelfClosingElement:
return parent.TagName() == node
}
return false
}
func IsImportOrExportSpecifier(node *Node) bool {
return IsImportSpecifier(node) || IsExportSpecifier(node)
}
func isVoidZero(node *Node) bool {
return IsVoidExpression(node) && IsNumericLiteral(node.Expression()) && node.Expression().Text() == "0"
}
func IsVoidExpression(node *Node) bool {
return node.Kind == KindVoidExpression
}
func IsExportsIdentifier(node *Node) bool {
return IsIdentifier(node) && node.Text() == "exports"
}
func IsModuleIdentifier(node *Node) bool {
return IsIdentifier(node) && node.Text() == "module"
}
func IsThisIdentifier(node *Node) bool {
return IsIdentifier(node) && node.Text() == "this"
}
func IsThisParameter(node *Node) bool {
return IsParameter(node) && node.Name() != nil && IsThisIdentifier(node.Name())
}
func IsBindableStaticAccessExpression(node *Node, excludeThisKeyword bool) bool {
return IsPropertyAccessExpression(node) &&
(!excludeThisKeyword && node.Expression().Kind == KindThisKeyword || IsIdentifier(node.Name()) && IsBindableStaticNameExpression(node.Expression() /*excludeThisKeyword*/, true)) ||
IsBindableStaticElementAccessExpression(node, excludeThisKeyword)
}
func IsBindableStaticElementAccessExpression(node *Node, excludeThisKeyword bool) bool {
return IsLiteralLikeElementAccess(node) &&
((!excludeThisKeyword && node.Expression().Kind == KindThisKeyword) ||
IsEntityNameExpression(node.Expression()) ||
IsBindableStaticAccessExpression(node.Expression(), true /*excludeThisKeyword*/))
}
func IsLiteralLikeElementAccess(node *Node) bool {
return IsElementAccessExpression(node) && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression)
}
func IsBindableStaticNameExpression(node *Node, excludeThisKeyword bool) bool {
return IsEntityNameExpression(node) || IsBindableStaticAccessExpression(node, excludeThisKeyword)
}
// Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions
// throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand)
func GetElementOrPropertyAccessName(node *Node) *Node {
switch node.Kind {
case KindPropertyAccessExpression:
if IsIdentifier(node.Name()) {
return node.Name()
}
return nil
case KindElementAccessExpression:
if arg := SkipParentheses(node.AsElementAccessExpression().ArgumentExpression); IsStringOrNumericLiteralLike(arg) {
return arg
}
return nil
}
panic("Unhandled case in GetElementOrPropertyAccessName")
}
func GetInitializerOfBinaryExpression(expr *BinaryExpression) *Expression {
for IsBinaryExpression(expr.Right) {
expr = expr.Right.AsBinaryExpression()
}
return expr.Right.Expression()
}
func IsExpressionWithTypeArgumentsInClassExtendsClause(node *Node) bool {
return TryGetClassExtendingExpressionWithTypeArguments(node) != nil
}
func TryGetClassExtendingExpressionWithTypeArguments(node *Node) *ClassLikeDeclaration {
cls, isImplements := TryGetClassImplementingOrExtendingExpressionWithTypeArguments(node)
if cls != nil && !isImplements {
return cls
}
return nil
}
func TryGetClassImplementingOrExtendingExpressionWithTypeArguments(node *Node) (class *ClassLikeDeclaration, isImplements bool) {
if IsExpressionWithTypeArguments(node) {
if IsHeritageClause(node.Parent) && IsClassLike(node.Parent.Parent) {
return node.Parent.Parent, node.Parent.AsHeritageClause().Token == KindImplementsKeyword
}
}
return nil, false
}
func GetNameOfDeclaration(declaration *Node) *Node {
if declaration == nil {
return nil
}
nonAssignedName := GetNonAssignedNameOfDeclaration(declaration)
if nonAssignedName != nil {
return nonAssignedName
}
if IsFunctionExpression(declaration) || IsArrowFunction(declaration) || IsClassExpression(declaration) {
return getAssignedName(declaration)
}
return nil
}
func GetImportClauseOfDeclaration(declaration *Declaration) *ImportClause {
switch declaration.Kind {
case KindImportDeclaration:
return declaration.AsImportDeclaration().ImportClause.AsImportClause()
case KindJSDocImportTag:
return declaration.AsJSDocImportTag().ImportClause.AsImportClause()
}
return nil
}
func GetNonAssignedNameOfDeclaration(declaration *Node) *Node {
// !!!
switch declaration.Kind {
case KindBinaryExpression:
bin := declaration.AsBinaryExpression()
kind := GetAssignmentDeclarationKind(bin)
if kind == JSDeclarationKindProperty || kind == JSDeclarationKindThisProperty {
if name := GetElementOrPropertyAccessName(bin.Left); name != nil {
return name
} else {
return bin.Left
}
}
return nil
case KindExportAssignment, KindJSExportAssignment:
expr := declaration.AsExportAssignment().Expression
if IsIdentifier(expr) {
return expr
}
return nil
}
return declaration.Name()
}
func getAssignedName(node *Node) *Node {
parent := node.Parent
if parent != nil {
switch parent.Kind {
case KindPropertyAssignment:
return parent.AsPropertyAssignment().Name()
case KindBindingElement:
return parent.AsBindingElement().Name()
case KindBinaryExpression:
if node == parent.AsBinaryExpression().Right {
left := parent.AsBinaryExpression().Left
switch left.Kind {
case KindIdentifier:
return left
case KindPropertyAccessExpression:
return left.AsPropertyAccessExpression().Name()
case KindElementAccessExpression:
arg := SkipParentheses(left.AsElementAccessExpression().ArgumentExpression)
if IsStringOrNumericLiteralLike(arg) {
return arg
}
}
}
case KindCommonJSExport:
return parent.AsCommonJSExport().Name()
case KindVariableDeclaration:
name := parent.AsVariableDeclaration().Name()
if IsIdentifier(name) {
return name
}
}
}
return nil
}
type JSDeclarationKind int
const (
JSDeclarationKindNone JSDeclarationKind = iota
/// module.exports = expr
JSDeclarationKindModuleExports
/// exports.name = expr
/// module.exports.name = expr
JSDeclarationKindExportsProperty
/// className.prototype.name = expr
JSDeclarationKindPrototypeProperty
/// this.name = expr
JSDeclarationKindThisProperty
/// F.name = expr, F[name] = expr
JSDeclarationKindProperty
)
func GetAssignmentDeclarationKind(bin *BinaryExpression) JSDeclarationKind {
if bin.OperatorToken.Kind != KindEqualsToken || !IsAccessExpression(bin.Left) {
return JSDeclarationKindNone
}
if IsInJSFile(bin.Left) && IsModuleExportsAccessExpression(bin.Left) {
return JSDeclarationKindModuleExports
} else if IsInJSFile(bin.Left) &&
(IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) &&
GetElementOrPropertyAccessName(bin.Left) != nil {
return JSDeclarationKindExportsProperty
}
if IsInJSFile(bin.Left) && bin.Left.Expression().Kind == KindThisKeyword {
return JSDeclarationKindThisProperty
}
if bin.Left.Kind == KindPropertyAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) && IsIdentifier(bin.Left.Name()) ||
bin.Left.Kind == KindElementAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) {
return JSDeclarationKindProperty
}
return JSDeclarationKindNone
}
/**
* A declaration has a dynamic name if all of the following are true:
* 1. The declaration has a computed property name.
* 2. The computed name is *not* expressed as a StringLiteral.
* 3. The computed name is *not* expressed as a NumericLiteral.
* 4. The computed name is *not* expressed as a PlusToken or MinusToken
* immediately followed by a NumericLiteral.
*/
func HasDynamicName(declaration *Node) bool {
name := GetNameOfDeclaration(declaration)
return name != nil && IsDynamicName(name)
}
func IsDynamicName(name *Node) bool {
var expr *Node
switch name.Kind {
case KindComputedPropertyName:
expr = name.AsComputedPropertyName().Expression
case KindElementAccessExpression:
expr = SkipParentheses(name.AsElementAccessExpression().ArgumentExpression)
default:
return false
}
return !IsStringOrNumericLiteralLike(expr) && !IsSignedNumericLiteral(expr)
}
func IsEntityNameExpression(node *Node) bool {
return IsEntityNameExpressionEx(node, false /*allowJS*/)
}
func IsEntityNameExpressionEx(node *Node, allowJS bool) bool {
if node.Kind == KindIdentifier || IsPropertyAccessEntityNameExpression(node, allowJS) {
return true
}
if allowJS {
return node.Kind == KindThisKeyword || isElementAccessEntityNameExpression(node, allowJS)
}
return false
}
func IsPropertyAccessEntityNameExpression(node *Node, allowJS bool) bool {
if node.Kind == KindPropertyAccessExpression {
expr := node.AsPropertyAccessExpression()
return expr.Name().Kind == KindIdentifier && IsEntityNameExpressionEx(expr.Expression, allowJS)
}
return false
}
func isElementAccessEntityNameExpression(node *Node, allowJS bool) bool {
if node.Kind == KindElementAccessExpression {
expr := node.AsElementAccessExpression()
if IsStringOrNumericLiteralLike(SkipParentheses(expr.ArgumentExpression)) {
return IsEntityNameExpressionEx(expr.Expression, allowJS)
}
}
return false
}
func IsDottedName(node *Node) bool {
switch node.Kind {
case KindIdentifier, KindThisKeyword, KindSuperKeyword, KindMetaProperty:
return true
case KindPropertyAccessExpression, KindParenthesizedExpression:
return IsDottedName(node.Expression())
}
return false
}
func HasSamePropertyAccessName(node1, node2 *Node) bool {
if node1.Kind == KindIdentifier && node2.Kind == KindIdentifier {
return node1.Text() == node2.Text()
} else if node1.Kind == KindPropertyAccessExpression && node2.Kind == KindPropertyAccessExpression {
return node1.AsPropertyAccessExpression().Name().Text() == node2.AsPropertyAccessExpression().Name().Text() &&
HasSamePropertyAccessName(node1.AsPropertyAccessExpression().Expression, node2.AsPropertyAccessExpression().Expression)
}
return false
}
func IsAmbientModule(node *Node) bool {
return IsModuleDeclaration(node) && (node.AsModuleDeclaration().Name().Kind == KindStringLiteral || IsGlobalScopeAugmentation(node))
}
func IsExternalModule(file *SourceFile) bool {
return file.ExternalModuleIndicator != nil
}
func IsExternalOrCommonJSModule(file *SourceFile) bool {
return file.ExternalModuleIndicator != nil || file.CommonJSModuleIndicator != nil
}
// TODO: Should we deprecate `IsExternalOrCommonJSModule` in favor of this function?
func IsEffectiveExternalModule(node *SourceFile, compilerOptions *core.CompilerOptions) bool {
return IsExternalModule(node) || (isCommonJSContainingModuleKind(compilerOptions.GetEmitModuleKind()) && node.CommonJSModuleIndicator != nil)
}
func isCommonJSContainingModuleKind(kind core.ModuleKind) bool {
return kind == core.ModuleKindCommonJS || core.ModuleKindNode16 <= kind && kind <= core.ModuleKindNodeNext
}
func IsExternalModuleIndicator(node *Statement) bool {
// Exported top-level member indicates moduleness
return IsAnyImportOrReExport(node) || IsExportAssignment(node) || HasSyntacticModifier(node, ModifierFlagsExport)
}
func IsExportNamespaceAsDefaultDeclaration(node *Node) bool {
if IsExportDeclaration(node) {
decl := node.AsExportDeclaration()
return IsNamespaceExport(decl.ExportClause) && ModuleExportNameIsDefault(decl.ExportClause.Name())
}
return false
}
func IsGlobalScopeAugmentation(node *Node) bool {
return IsModuleDeclaration(node) && node.AsModuleDeclaration().Keyword == KindGlobalKeyword
}
func IsModuleAugmentationExternal(node *Node) bool {
// external module augmentation is a ambient module declaration that is either:
// - defined in the top level scope and source file is an external module
// - defined inside ambient module declaration located in the top level scope and source file not an external module
switch node.Parent.Kind {
case KindSourceFile:
return IsExternalModule(node.Parent.AsSourceFile())
case KindModuleBlock:
grandParent := node.Parent.Parent
return IsAmbientModule(grandParent) && IsSourceFile(grandParent.Parent) && !IsExternalModule(grandParent.Parent.AsSourceFile())
}
return false
}
func IsModuleWithStringLiteralName(node *Node) bool {
return IsModuleDeclaration(node) && node.Name().Kind == KindStringLiteral
}
func GetContainingClass(node *Node) *Node {
return FindAncestor(node.Parent, IsClassLike)
}
func GetExtendsHeritageClauseElement(node *Node) *ExpressionWithTypeArgumentsNode {
return core.FirstOrNil(GetExtendsHeritageClauseElements(node))
}
func GetExtendsHeritageClauseElements(node *Node) []*ExpressionWithTypeArgumentsNode {
return GetHeritageElements(node, KindExtendsKeyword)
}
func GetImplementsHeritageClauseElements(node *Node) []*ExpressionWithTypeArgumentsNode {
return GetHeritageElements(node, KindImplementsKeyword)
}
func GetHeritageElements(node *Node, kind Kind) []*Node {
clause := GetHeritageClause(node, kind)
if clause != nil {
return clause.AsHeritageClause().Types.Nodes
}
return nil
}
func GetHeritageClause(node *Node, kind Kind) *Node {
clauses := getHeritageClauses(node)
if clauses != nil {
for _, clause := range clauses.Nodes {
if clause.AsHeritageClause().Token == kind {
return clause
}
}
}
return nil
}
func getHeritageClauses(node *Node) *NodeList {
switch node.Kind {
case KindClassDeclaration:
return node.AsClassDeclaration().HeritageClauses
case KindClassExpression:
return node.AsClassExpression().HeritageClauses
case KindInterfaceDeclaration:
return node.AsInterfaceDeclaration().HeritageClauses
}
return nil
}
func IsPartOfTypeQuery(node *Node) bool {
for node.Kind == KindQualifiedName || node.Kind == KindIdentifier {
node = node.Parent
}
return node.Kind == KindTypeQuery
}
/**
* This function returns true if the this node's root declaration is a parameter.
* For example, passing a `ParameterDeclaration` will return true, as will passing a
* binding element that is a child of a `ParameterDeclaration`.
*
* If you are looking to test that a `Node` is a `ParameterDeclaration`, use `isParameter`.
*/
func IsPartOfParameterDeclaration(node *Node) bool {
return GetRootDeclaration(node).Kind == KindParameter
}
func IsInTopLevelContext(node *Node) bool {
// The name of a class or function declaration is a BindingIdentifier in its surrounding scope.
if IsIdentifier(node) {
parent := node.Parent
if (IsClassDeclaration(parent) || IsFunctionDeclaration(parent)) && parent.Name() == node {
node = parent
}
}
container := GetThisContainer(node, true /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
return IsSourceFile(container)
}
func GetThisContainer(node *Node, includeArrowFunctions bool, includeClassComputedPropertyName bool) *Node {
for {
node = node.Parent
if node == nil {
panic("nil parent in getThisContainer")
}
switch node.Kind {
case KindComputedPropertyName:
if includeClassComputedPropertyName && IsClassLike(node.Parent.Parent) {
return node
}
node = node.Parent.Parent
case KindDecorator:
if node.Parent.Kind == KindParameter && 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 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
}
case KindArrowFunction:
if includeArrowFunctions {
return node
}
case KindFunctionDeclaration, KindFunctionExpression, KindModuleDeclaration, KindClassStaticBlockDeclaration,
KindPropertyDeclaration, KindPropertySignature, KindMethodDeclaration, KindMethodSignature, KindConstructor,
KindGetAccessor, KindSetAccessor, KindCallSignature, KindConstructSignature, KindIndexSignature,
KindEnumDeclaration, KindSourceFile:
return node
}
}
}
func GetSuperContainer(node *Node, stopOnFunctions bool) *Node {
for node = node.Parent; node != nil; node = node.Parent {
switch node.Kind {
case KindComputedPropertyName:
node = node.Parent
case KindFunctionDeclaration, KindFunctionExpression, KindArrowFunction:
if !stopOnFunctions {
continue
}
return node
case KindPropertyDeclaration, KindPropertySignature, KindMethodDeclaration, KindMethodSignature, KindConstructor, KindGetAccessor, KindSetAccessor, KindClassStaticBlockDeclaration:
return node
case KindDecorator:
// Decorators are always applied outside of the body of a class or method.
if node.Parent.Kind == KindParameter && 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 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
}
}
}
return nil
}
func GetImmediatelyInvokedFunctionExpression(fn *Node) *Node {
if IsFunctionExpressionOrArrowFunction(fn) {
prev := fn
parent := fn.Parent
for IsParenthesizedExpression(parent) {
prev = parent
parent = parent.Parent
}
if IsCallExpression(parent) && parent.AsCallExpression().Expression == prev {
return parent
}
}
return nil
}
func IsEnumConst(node *Node) bool {
return GetCombinedModifierFlags(node)&ModifierFlagsConst != 0
}
func ExportAssignmentIsAlias(node *Node) bool {
e := node.AsExportAssignment().Expression
return IsEntityNameExpression(e) || IsClassExpression(e)
}
func IsInstanceOfExpression(node *Node) bool {
return IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == KindInstanceOfKeyword
}
func IsAnyImportOrReExport(node *Node) bool {
return IsAnyImportSyntax(node) || IsExportDeclaration(node)
}
func IsAnyImportSyntax(node *Node) bool {
return NodeKindIs(node, KindImportDeclaration, KindJSImportDeclaration, KindImportEqualsDeclaration)
}
func IsJsonSourceFile(file *SourceFile) bool {
return file.ScriptKind == core.ScriptKindJSON
}
func IsInJsonFile(node *Node) bool {
return node.Flags&NodeFlagsJsonFile != 0
}
func GetExternalModuleName(node *Node) *Expression {
switch node.Kind {
case KindImportDeclaration, KindJSImportDeclaration:
return node.AsImportDeclaration().ModuleSpecifier
case KindExportDeclaration:
return node.AsExportDeclaration().ModuleSpecifier
case KindImportEqualsDeclaration:
if node.AsImportEqualsDeclaration().ModuleReference.Kind == KindExternalModuleReference {
return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().Expression
}
return nil
case KindImportType:
return getImportTypeNodeLiteral(node)
case KindCallExpression:
return core.FirstOrNil(node.AsCallExpression().Arguments.Nodes)
case KindModuleDeclaration:
if IsStringLiteral(node.AsModuleDeclaration().Name()) {
return node.AsModuleDeclaration().Name()
}
return nil
}
panic("Unhandled case in getExternalModuleName")
}
func GetImportAttributes(node *Node) *Node {
switch node.Kind {
case KindImportDeclaration, KindJSImportDeclaration:
return node.AsImportDeclaration().Attributes
case KindExportDeclaration:
return node.AsExportDeclaration().Attributes
}
panic("Unhandled case in getImportAttributes")
}
func getImportTypeNodeLiteral(node *Node) *Node {
if IsImportTypeNode(node) {
importTypeNode := node.AsImportTypeNode()
if IsLiteralTypeNode(importTypeNode.Argument) {
literalTypeNode := importTypeNode.Argument.AsLiteralTypeNode()
if IsStringLiteral(literalTypeNode.Literal) {
return literalTypeNode.Literal
}
}
}
return nil
}
func IsExpressionNode(node *Node) bool {
switch node.Kind {
case KindSuperKeyword, KindNullKeyword, KindTrueKeyword, KindFalseKeyword, KindRegularExpressionLiteral,
KindArrayLiteralExpression, KindObjectLiteralExpression, KindPropertyAccessExpression, KindElementAccessExpression,
KindCallExpression, KindNewExpression, KindTaggedTemplateExpression, KindAsExpression, KindTypeAssertionExpression,
KindSatisfiesExpression, KindNonNullExpression, KindParenthesizedExpression, KindFunctionExpression,
KindClassExpression, KindArrowFunction, KindVoidExpression, KindDeleteExpression, KindTypeOfExpression,
KindPrefixUnaryExpression, KindPostfixUnaryExpression, KindBinaryExpression, KindConditionalExpression,
KindSpreadElement, KindTemplateExpression, KindOmittedExpression, KindJsxElement, KindJsxSelfClosingElement,
KindJsxFragment, KindYieldExpression, KindAwaitExpression:
return true
case KindMetaProperty:
// `import.defer` in `import.defer(...)` is not an expression
return !IsImportCall(node.Parent) || node.Parent.AsCallExpression().Expression != node
case KindExpressionWithTypeArguments:
return !IsHeritageClause(node.Parent)
case KindQualifiedName:
for node.Parent.Kind == KindQualifiedName {
node = node.Parent
}
return IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || isJSXTagName(node)
case KindPrivateIdentifier:
return IsBinaryExpression(node.Parent) && node.Parent.AsBinaryExpression().Left == node && node.Parent.AsBinaryExpression().OperatorToken.Kind == KindInKeyword
case KindIdentifier:
if IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || isJSXTagName(node) {
return true
}
fallthrough
case KindNumericLiteral, KindBigIntLiteral, KindStringLiteral, KindNoSubstitutionTemplateLiteral, KindThisKeyword:
return IsInExpressionContext(node)
default:
return false
}
}
func IsInExpressionContext(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindVariableDeclaration:
return parent.AsVariableDeclaration().Initializer == node
case KindParameter:
return parent.AsParameterDeclaration().Initializer == node
case KindPropertyDeclaration:
return parent.AsPropertyDeclaration().Initializer == node
case KindPropertySignature:
return parent.AsPropertySignatureDeclaration().Initializer == node
case KindEnumMember:
return parent.AsEnumMember().Initializer == node
case KindPropertyAssignment:
return parent.AsPropertyAssignment().Initializer == node
case KindBindingElement:
return parent.AsBindingElement().Initializer == node
case KindExpressionStatement:
return parent.AsExpressionStatement().Expression == node
case KindIfStatement:
return parent.AsIfStatement().Expression == node
case KindDoStatement:
return parent.AsDoStatement().Expression == node
case KindWhileStatement:
return parent.AsWhileStatement().Expression == node
case KindReturnStatement:
return parent.AsReturnStatement().Expression == node
case KindWithStatement:
return parent.AsWithStatement().Expression == node
case KindSwitchStatement:
return parent.AsSwitchStatement().Expression == node
case KindCaseClause, KindDefaultClause:
return parent.AsCaseOrDefaultClause().Expression == node
case KindThrowStatement:
return parent.AsThrowStatement().Expression == node
case KindForStatement:
s := parent.AsForStatement()
return s.Initializer == node && s.Initializer.Kind != KindVariableDeclarationList || s.Condition == node || s.Incrementor == node
case KindForInStatement, KindForOfStatement:
s := parent.AsForInOrOfStatement()
return s.Initializer == node && s.Initializer.Kind != KindVariableDeclarationList || s.Expression == node
case KindTypeAssertionExpression:
return parent.AsTypeAssertion().Expression == node
case KindAsExpression:
return parent.AsAsExpression().Expression == node
case KindTemplateSpan:
return parent.AsTemplateSpan().Expression == node
case KindComputedPropertyName:
return parent.AsComputedPropertyName().Expression == node
case KindDecorator, KindJsxExpression, KindJsxSpreadAttribute, KindSpreadAssignment:
return true
case KindExpressionWithTypeArguments:
return parent.AsExpressionWithTypeArguments().Expression == node && !IsPartOfTypeNode(parent)
case KindShorthandPropertyAssignment:
return parent.AsShorthandPropertyAssignment().ObjectAssignmentInitializer == node
case KindSatisfiesExpression:
return parent.AsSatisfiesExpression().Expression == node
default:
return IsExpressionNode(parent)
}
}
func IsPartOfTypeNode(node *Node) bool {
kind := node.Kind
if kind >= KindFirstTypeNode && kind <= KindLastTypeNode {
return true
}
switch node.Kind {
case KindAnyKeyword, KindUnknownKeyword, KindNumberKeyword, KindBigIntKeyword, KindStringKeyword,
KindBooleanKeyword, KindSymbolKeyword, KindObjectKeyword, KindUndefinedKeyword, KindNullKeyword,
KindNeverKeyword:
return true
case KindVoidKeyword:
return node.Parent.Kind != KindVoidExpression
case KindExpressionWithTypeArguments:
return isPartOfTypeExpressionWithTypeArguments(node)
case KindTypeParameter:
return node.Parent.Kind == KindMappedType || node.Parent.Kind == KindInferType
case KindIdentifier:
parent := node.Parent
if IsQualifiedName(parent) && parent.AsQualifiedName().Right == node {
return isPartOfTypeNodeInParent(parent)
}
if IsPropertyAccessExpression(parent) && parent.AsPropertyAccessExpression().Name() == node {
return isPartOfTypeNodeInParent(parent)
}
return isPartOfTypeNodeInParent(node)
case KindQualifiedName, KindPropertyAccessExpression, KindThisKeyword:
return isPartOfTypeNodeInParent(node)
}
return false
}
func isPartOfTypeNodeInParent(node *Node) bool {
parent := node.Parent
if parent.Kind == KindTypeQuery {
return false
}
if parent.Kind == KindImportType {
return !parent.AsImportTypeNode().IsTypeOf
}
// Do not recursively call isPartOfTypeNode on the parent. In the example:
//
// let a: A.B.C;
//
// Calling isPartOfTypeNode would consider the qualified name A.B a type node.
// Only C and A.B.C are type nodes.
if parent.Kind >= KindFirstTypeNode && parent.Kind <= KindLastTypeNode {
return true
}
switch parent.Kind {
case KindExpressionWithTypeArguments:
return isPartOfTypeExpressionWithTypeArguments(parent)
case KindTypeParameter:
return node == parent.AsTypeParameter().Constraint
case KindVariableDeclaration, KindParameter, KindPropertyDeclaration, KindPropertySignature, KindFunctionDeclaration,
KindFunctionExpression, KindArrowFunction, KindConstructor, KindMethodDeclaration, KindMethodSignature,
KindGetAccessor, KindSetAccessor, KindCallSignature, KindConstructSignature, KindIndexSignature,
KindTypeAssertionExpression:
return node == parent.Type()
case KindCallExpression, KindNewExpression, KindTaggedTemplateExpression:
return slices.Contains(parent.TypeArguments(), node)
}
return false
}
func isPartOfTypeExpressionWithTypeArguments(node *Node) bool {
parent := node.Parent
return IsHeritageClause(parent) && (!IsClassLike(parent.Parent) || parent.AsHeritageClause().Token == KindImplementsKeyword) ||
IsJSDocImplementsTag(parent) ||
IsJSDocAugmentsTag(parent)
}
func IsJSDocLinkLike(node *Node) bool {
return NodeKindIs(node, KindJSDocLink, KindJSDocLinkCode, KindJSDocLinkPlain)
}
func IsJSDocTag(node *Node) bool {
return node.Kind >= KindFirstJSDocTagNode && node.Kind <= KindLastJSDocTagNode
}
func isJSXTagName(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindJsxOpeningElement:
return parent.AsJsxOpeningElement().TagName == node
case KindJsxSelfClosingElement:
return parent.AsJsxSelfClosingElement().TagName == node
case KindJsxClosingElement:
return parent.AsJsxClosingElement().TagName == node
}
return false
}
func IsSuperCall(node *Node) bool {
return IsCallExpression(node) && node.AsCallExpression().Expression.Kind == KindSuperKeyword
}
func IsImportCall(node *Node) bool {
if !IsCallExpression(node) {
return false
}
e := node.AsCallExpression().Expression
return e.Kind == KindImportKeyword || IsMetaProperty(e) && e.AsMetaProperty().KeywordToken == KindImportKeyword && e.Text() == "defer"
}
func IsComputedNonLiteralName(name *Node) bool {
return IsComputedPropertyName(name) && !IsStringOrNumericLiteralLike(name.Expression())
}
func IsQuestionToken(node *Node) bool {
return node != nil && node.Kind == KindQuestionToken
}
func GetTextOfPropertyName(name *Node) string {
text, _ := TryGetTextOfPropertyName(name)
return text
}
func TryGetTextOfPropertyName(name *Node) (string, bool) {
switch name.Kind {
case KindIdentifier, KindPrivateIdentifier, KindStringLiteral, KindNumericLiteral, KindBigIntLiteral,
KindNoSubstitutionTemplateLiteral:
return name.Text(), true
case KindComputedPropertyName:
if IsStringOrNumericLiteralLike(name.Expression()) {
return name.Expression().Text(), true
}
case KindJsxNamespacedName:
return name.AsJsxNamespacedName().Namespace.Text() + ":" + name.Name().Text(), true
}
return "", false
}
func IsJSDocNode(node *Node) bool {
return node.Kind >= KindFirstJSDocNode && node.Kind <= KindLastJSDocNode
}
func IsNonWhitespaceToken(node *Node) bool {
return IsTokenKind(node.Kind) && !IsWhitespaceOnlyJsxText(node)
}
func IsWhitespaceOnlyJsxText(node *Node) bool {
return node.Kind == KindJsxText && node.AsJsxText().ContainsOnlyTriviaWhiteSpaces
}
func GetNewTargetContainer(node *Node) *Node {
container := GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
if container != nil {
switch container.Kind {
case KindConstructor, KindFunctionDeclaration, KindFunctionExpression:
return container
}
}
return nil
}
func GetEnclosingBlockScopeContainer(node *Node) *Node {
return FindAncestor(node.Parent, func(current *Node) bool {
return IsBlockScope(current, current.Parent)
})
}
func IsBlockScope(node *Node, parentNode *Node) bool {
switch node.Kind {
case KindSourceFile, KindCaseBlock, KindCatchClause, KindModuleDeclaration, KindForStatement, KindForInStatement, KindForOfStatement,
KindConstructor, KindMethodDeclaration, KindGetAccessor, KindSetAccessor, KindFunctionDeclaration, KindFunctionExpression,
KindArrowFunction, KindPropertyDeclaration, KindClassStaticBlockDeclaration:
return true
case KindBlock:
// function block is not considered block-scope container
// see comment in binder.ts: bind(...), case for SyntaxKind.Block
return !IsFunctionLikeOrClassStaticBlockDeclaration(parentNode)
}
return false
}
type SemanticMeaning int32
const (
SemanticMeaningNone SemanticMeaning = 0
SemanticMeaningValue SemanticMeaning = 1 << 0
SemanticMeaningType SemanticMeaning = 1 << 1
SemanticMeaningNamespace SemanticMeaning = 1 << 2
SemanticMeaningAll SemanticMeaning = SemanticMeaningValue | SemanticMeaningType | SemanticMeaningNamespace
)
func GetMeaningFromDeclaration(node *Node) SemanticMeaning {
switch node.Kind {
case KindVariableDeclaration:
return SemanticMeaningValue
case KindParameter,
KindBindingElement,
KindPropertyDeclaration,
KindPropertySignature,
KindPropertyAssignment,
KindShorthandPropertyAssignment,
KindMethodDeclaration,
KindMethodSignature,
KindConstructor,
KindGetAccessor,
KindSetAccessor,
KindFunctionDeclaration,
KindFunctionExpression,
KindArrowFunction,
KindCatchClause,
KindJsxAttribute:
return SemanticMeaningValue
case KindTypeParameter,
KindInterfaceDeclaration,
KindTypeAliasDeclaration,
KindJSTypeAliasDeclaration,
KindTypeLiteral:
return SemanticMeaningType
case KindEnumMember, KindClassDeclaration:
return SemanticMeaningValue | SemanticMeaningType
case KindModuleDeclaration:
if IsAmbientModule(node) {
return SemanticMeaningNamespace | SemanticMeaningValue
} else if GetModuleInstanceState(node) == ModuleInstanceStateInstantiated {
return SemanticMeaningNamespace | SemanticMeaningValue
} else {
return SemanticMeaningNamespace
}
case KindEnumDeclaration,
KindNamedImports,
KindImportSpecifier,
KindImportEqualsDeclaration,
KindImportDeclaration,
KindJSImportDeclaration,
KindExportAssignment,
KindJSExportAssignment,
KindExportDeclaration:
return SemanticMeaningAll
// An external module can be a Value
case KindSourceFile:
return SemanticMeaningNamespace | SemanticMeaningValue
}
return SemanticMeaningAll
}
func IsPropertyAccessOrQualifiedName(node *Node) bool {
return node.Kind == KindPropertyAccessExpression || node.Kind == KindQualifiedName
}
func IsLabelName(node *Node) bool {
return IsLabelOfLabeledStatement(node) || IsJumpStatementTarget(node)
}
func IsLabelOfLabeledStatement(node *Node) bool {
if !IsIdentifier(node) {
return false
}
if !IsLabeledStatement(node.Parent) {
return false
}
return node == node.Parent.Label()
}
func IsJumpStatementTarget(node *Node) bool {
if !IsIdentifier(node) {
return false
}
if !IsBreakOrContinueStatement(node.Parent) {
return false
}
return node == node.Parent.Label()
}
func IsBreakOrContinueStatement(node *Node) bool {
return NodeKindIs(node, KindBreakStatement, KindContinueStatement)
}
// GetModuleInstanceState is used during binding as well as in transformations and tests, and therefore may be invoked
// with a node that does not yet have its `Parent` pointer set. In this case, an `ancestors` represents a stack of
// virtual `Parent` pointers that can be used to walk up the tree. Since `getModuleInstanceStateForAliasTarget` may
// potentially walk up out of the provided `Node`, merely setting the parent pointers for a given `ModuleDeclaration`
// prior to invoking `GetModuleInstanceState` is not sufficient. It is, however, necessary that the `Parent` pointers
// for all ancestors of the `Node` provided to `GetModuleInstanceState` have been set.
// Push a virtual parent pointer onto `ancestors` and return it.
func pushAncestor(ancestors []*Node, parent *Node) []*Node {
return append(ancestors, parent)
}
// If a virtual `Parent` exists on the stack, returns the previous stack entry and the virtual `Parent“.
// Otherwise, we return `nil` and the value of `node.Parent`.
func popAncestor(ancestors []*Node, node *Node) ([]*Node, *Node) {
if len(ancestors) == 0 {
return nil, node.Parent
}
n := len(ancestors) - 1
return ancestors[:n], ancestors[n]
}
type ModuleInstanceState int32
const (
ModuleInstanceStateUnknown ModuleInstanceState = iota
ModuleInstanceStateNonInstantiated
ModuleInstanceStateInstantiated
ModuleInstanceStateConstEnumOnly
)
func GetModuleInstanceState(node *Node) ModuleInstanceState {
return getModuleInstanceState(node, nil, nil)
}
func getModuleInstanceState(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState {
module := node.AsModuleDeclaration()
if module.Body != nil {
return getModuleInstanceStateCached(module.Body, pushAncestor(ancestors, node), visited)
} else {
return ModuleInstanceStateInstantiated
}
}
func getModuleInstanceStateCached(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState {
if visited == nil {
visited = make(map[NodeId]ModuleInstanceState)
}
nodeId := GetNodeId(node)
if cached, ok := visited[nodeId]; ok {
if cached != ModuleInstanceStateUnknown {
return cached
}
return ModuleInstanceStateNonInstantiated
}
visited[nodeId] = ModuleInstanceStateUnknown
result := getModuleInstanceStateWorker(node, ancestors, visited)
visited[nodeId] = result
return result
}
func getModuleInstanceStateWorker(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState {
// A module is uninstantiated if it contains only
switch node.Kind {
case KindInterfaceDeclaration, KindTypeAliasDeclaration, KindJSTypeAliasDeclaration:
return ModuleInstanceStateNonInstantiated
case KindEnumDeclaration:
if IsEnumConst(node) {
return ModuleInstanceStateConstEnumOnly
}
case KindImportDeclaration, KindJSImportDeclaration, KindImportEqualsDeclaration:
if !HasSyntacticModifier(node, ModifierFlagsExport) {
return ModuleInstanceStateNonInstantiated
}
case KindExportDeclaration:
decl := node.AsExportDeclaration()
if decl.ModuleSpecifier == nil && decl.ExportClause != nil && decl.ExportClause.Kind == KindNamedExports {
state := ModuleInstanceStateNonInstantiated
ancestors = pushAncestor(ancestors, node)
ancestors = pushAncestor(ancestors, decl.ExportClause)
for _, specifier := range decl.ExportClause.AsNamedExports().Elements.Nodes {
specifierState := getModuleInstanceStateForAliasTarget(specifier, ancestors, visited)
if specifierState > state {
state = specifierState
}
if state == ModuleInstanceStateInstantiated {
return state
}
}
return state
}
case KindModuleBlock:
state := ModuleInstanceStateNonInstantiated
ancestors = pushAncestor(ancestors, node)
node.ForEachChild(func(n *Node) bool {
childState := getModuleInstanceStateCached(n, ancestors, visited)
switch childState {
case ModuleInstanceStateNonInstantiated:
return false
case ModuleInstanceStateConstEnumOnly:
state = ModuleInstanceStateConstEnumOnly
return false
case ModuleInstanceStateInstantiated:
state = ModuleInstanceStateInstantiated
return true
}
panic("Unhandled case in getModuleInstanceStateWorker")
})
return state
case KindModuleDeclaration:
return getModuleInstanceState(node, ancestors, visited)
}
return ModuleInstanceStateInstantiated
}
func getModuleInstanceStateForAliasTarget(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState {
spec := node.AsExportSpecifier()
name := spec.PropertyName
if name == nil {
name = spec.Name()
}
if name.Kind != KindIdentifier {
// Skip for invalid syntax like this: export { "x" }
return ModuleInstanceStateInstantiated
}
for ancestors, p := popAncestor(ancestors, node); p != nil; ancestors, p = popAncestor(ancestors, p) {
if IsBlock(p) || IsModuleBlock(p) || IsSourceFile(p) {
statements := GetStatementsOfBlock(p)
found := ModuleInstanceStateUnknown
statementsAncestors := pushAncestor(ancestors, p)
for _, statement := range statements.Nodes {
if NodeHasName(statement, name) {
state := getModuleInstanceStateCached(statement, statementsAncestors, visited)
if found == ModuleInstanceStateUnknown || state > found {
found = state
}
if found == ModuleInstanceStateInstantiated {
return found
}
if statement.Kind == KindImportEqualsDeclaration {
// Treat re-exports of import aliases as instantiated since they're ambiguous. This is consistent
// with `export import x = mod.x` being treated as instantiated:
// import x = mod.x;
// export { x };
found = ModuleInstanceStateInstantiated
}
}
}
if found != ModuleInstanceStateUnknown {
return found
}
}
}
// Couldn't locate, assume could refer to a value
return ModuleInstanceStateInstantiated
}
func NodeHasName(statement *Node, id *Node) bool {
name := statement.Name()
if name != nil {
return IsIdentifier(name) && name.AsIdentifier().Text == id.AsIdentifier().Text
}
if IsVariableStatement(statement) {
declarations := statement.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes
return core.Some(declarations, func(d *Node) bool { return NodeHasName(d, id) })
}
return false
}
func IsInternalModuleImportEqualsDeclaration(node *Node) bool {
return IsImportEqualsDeclaration(node) && node.AsImportEqualsDeclaration().ModuleReference.Kind != KindExternalModuleReference
}
func GetAssertedTypeNode(node *Node) *Node {
switch node.Kind {
case KindAsExpression:
return node.AsAsExpression().Type
case KindSatisfiesExpression:
return node.AsSatisfiesExpression().Type
case KindTypeAssertionExpression:
return node.AsTypeAssertion().Type
}
panic("Unhandled case in getAssertedTypeNode")
}
func IsConstAssertion(node *Node) bool {
switch node.Kind {
case KindAsExpression, KindTypeAssertionExpression:
return IsConstTypeReference(GetAssertedTypeNode(node))
}
return false
}
func IsConstTypeReference(node *Node) bool {
return IsTypeReferenceNode(node) && len(node.TypeArguments()) == 0 && IsIdentifier(node.AsTypeReferenceNode().TypeName) && node.AsTypeReferenceNode().TypeName.Text() == "const"
}
func IsGlobalSourceFile(node *Node) bool {
return node.Kind == KindSourceFile && !IsExternalOrCommonJSModule(node.AsSourceFile())
}
func IsParameterLike(node *Node) bool {
switch node.Kind {
case KindParameter, KindTypeParameter:
return true
}
return false
}
func GetDeclarationOfKind(symbol *Symbol, kind Kind) *Node {
for _, declaration := range symbol.Declarations {
if declaration.Kind == kind {
return declaration
}
}
return nil
}
func FindConstructorDeclaration(node *ClassLikeDeclaration) *Node {
for _, member := range node.ClassLikeData().Members.Nodes {
if IsConstructorDeclaration(member) && NodeIsPresent(member.AsConstructorDeclaration().Body) {
return member
}
}
return nil
}
func GetFirstIdentifier(node *Node) *Node {
switch node.Kind {
case KindIdentifier:
return node
case KindQualifiedName:
return GetFirstIdentifier(node.AsQualifiedName().Left)
case KindPropertyAccessExpression:
return GetFirstIdentifier(node.AsPropertyAccessExpression().Expression)
}
panic("Unhandled case in GetFirstIdentifier")
}
func GetNamespaceDeclarationNode(node *Node) *Node {
switch node.Kind {
case KindImportDeclaration, KindJSImportDeclaration:
importClause := node.AsImportDeclaration().ImportClause
if importClause != nil && importClause.AsImportClause().NamedBindings != nil && IsNamespaceImport(importClause.AsImportClause().NamedBindings) {
return importClause.AsImportClause().NamedBindings
}
case KindImportEqualsDeclaration:
return node
case KindExportDeclaration:
exportClause := node.AsExportDeclaration().ExportClause
if exportClause != nil && IsNamespaceExport(exportClause) {
return exportClause
}
default:
panic("Unhandled case in getNamespaceDeclarationNode")
}
return nil
}
func ModuleExportNameIsDefault(node *Node) bool {
return node.Text() == InternalSymbolNameDefault
}
func IsDefaultImport(node *Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) bool {
switch node.Kind {
case KindImportDeclaration, KindJSImportDeclaration:
importClause := node.AsImportDeclaration().ImportClause
return importClause != nil && importClause.AsImportClause().name != nil
}
return false
}
func GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind {
impliedNodeFormat := core.ResolutionModeNone
if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDmts, tspath.ExtensionMts, tspath.ExtensionMjs}) {
impliedNodeFormat = core.ResolutionModeESM
} else if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDcts, tspath.ExtensionCts, tspath.ExtensionCjs}) {
impliedNodeFormat = core.ResolutionModeCommonJS
} else if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDts, tspath.ExtensionTs, tspath.ExtensionTsx, tspath.ExtensionJs, tspath.ExtensionJsx}) {
impliedNodeFormat = core.IfElse(packageJsonType == "module", core.ResolutionModeESM, core.ResolutionModeCommonJS)
}
return impliedNodeFormat
}
func GetEmitModuleFormatOfFileWorker(fileName string, options *core.CompilerOptions, sourceFileMetaData SourceFileMetaData) core.ModuleKind {
result := GetImpliedNodeFormatForEmitWorker(fileName, options.GetEmitModuleKind(), sourceFileMetaData)
if result != core.ModuleKindNone {
return result
}
return options.GetEmitModuleKind()
}
func GetImpliedNodeFormatForEmitWorker(fileName string, emitModuleKind core.ModuleKind, sourceFileMetaData SourceFileMetaData) core.ResolutionMode {
if core.ModuleKindNode16 <= emitModuleKind && emitModuleKind <= core.ModuleKindNodeNext {
return sourceFileMetaData.ImpliedNodeFormat
}
if sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindCommonJS &&
(sourceFileMetaData.PackageJsonType == "commonjs" ||
tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionCjs, tspath.ExtensionCts})) {
return core.ModuleKindCommonJS
}
if sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindESNext &&
(sourceFileMetaData.PackageJsonType == "module" ||
tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionMjs, tspath.ExtensionMts})) {
return core.ModuleKindESNext
}
return core.ModuleKindNone
}
func GetDeclarationContainer(node *Node) *Node {
return FindAncestor(GetRootDeclaration(node), func(node *Node) bool {
switch node.Kind {
case KindVariableDeclaration,
KindVariableDeclarationList,
KindImportSpecifier,
KindNamedImports,
KindNamespaceImport,
KindImportClause:
return false
default:
return true
}
}).Parent
}
// Indicates that a symbol is an alias that does not merge with a local declaration.
// OR Is a JSContainer which may merge an alias with a local declaration
func IsNonLocalAlias(symbol *Symbol, excludes SymbolFlags) bool {
if symbol == nil {
return false
}
return symbol.Flags&(SymbolFlagsAlias|excludes) == SymbolFlagsAlias ||
symbol.Flags&SymbolFlagsAlias != 0 && symbol.Flags&SymbolFlagsAssignment != 0
}
// An alias symbol is created by one of the following declarations:
//
// import <symbol> = ...
// const <symbol> = ... (JS only)
// const { <symbol>, ... } = ... (JS only)
// import <symbol> from ...
// import * as <symbol> from ...
// import { x as <symbol> } from ...
// export { x as <symbol> } from ...
// export * as ns <symbol> from ...
// export = <EntityNameExpression>
// export default <EntityNameExpression>
// module.exports = <EntityNameExpression> (JS only)
func IsAliasSymbolDeclaration(node *Node) bool {
switch node.Kind {
case KindImportEqualsDeclaration, KindNamespaceExportDeclaration, KindNamespaceImport, KindNamespaceExport,
KindImportSpecifier, KindExportSpecifier:
return true
case KindImportClause:
return node.AsImportClause().Name() != nil
case KindExportAssignment, KindJSExportAssignment:
return ExportAssignmentIsAlias(node)
case KindVariableDeclaration, KindBindingElement:
return IsVariableDeclarationInitializedToRequire(node)
}
return false
}
func IsParseTreeNode(node *Node) bool {
return node.Flags&NodeFlagsSynthesized == 0
}
// Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only if requested
func GetNodeAtPosition(file *SourceFile, position int, includeJSDoc bool) *Node {
current := file.AsNode()
for {
var child *Node
if includeJSDoc {
for _, jsdoc := range current.JSDoc(file) {
if nodeContainsPosition(jsdoc, position) {
child = jsdoc
break
}
}
}
if child == nil {
current.ForEachChild(func(node *Node) bool {
if nodeContainsPosition(node, position) && node.Kind != KindJSExportAssignment && node.Kind != KindCommonJSExport {
child = node
return true
}
return false
})
}
if child == nil || IsMetaProperty(child) {
return current
}
current = child
}
}
func nodeContainsPosition(node *Node, position int) bool {
return node.Kind >= KindFirstNode && node.Pos() <= position && (position < node.End() || position == node.End() && node.Kind == KindEndOfFile)
}
func findImportOrRequire(text string, start int) (index int, size int) {
index = max(start, 0)
n := len(text)
for index < n {
next := strings.IndexAny(text[index:], "ir")
if next < 0 {
break
}
index += next
var expected string
if text[index] == 'i' {
size = 6
expected = "import"
} else {
size = 7
expected = "require"
}
if index+size <= n && text[index:index+size] == expected {
return index, size
}
index++
}
return -1, 0
}
func ForEachDynamicImportOrRequireCall(
file *SourceFile,
includeTypeSpaceImports bool,
requireStringLiteralLikeArgument bool,
cb func(node *Node, argument *Expression) bool,
) bool {
isJavaScriptFile := IsInJSFile(file.AsNode())
lastIndex, size := findImportOrRequire(file.Text(), 0)
for lastIndex >= 0 {
node := GetNodeAtPosition(file, lastIndex, isJavaScriptFile && includeTypeSpaceImports)
if isJavaScriptFile && IsRequireCall(node, requireStringLiteralLikeArgument) {
if cb(node, node.Arguments()[0]) {
return true
}
} else if IsImportCall(node) && len(node.Arguments()) > 0 && (!requireStringLiteralLikeArgument || IsStringLiteralLike(node.Arguments()[0])) {
if cb(node, node.Arguments()[0]) {
return true
}
} else if includeTypeSpaceImports && IsLiteralImportTypeNode(node) {
if cb(node, node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal) {
return true
}
}
// skip past import/require
lastIndex += size
lastIndex, size = findImportOrRequire(file.Text(), lastIndex)
}
return false
}
// Returns true if the node is a CallExpression to the identifier 'require' with
// exactly one argument (of the form 'require("name")').
// This function does not test if the node is in a JavaScript file or not.
func IsRequireCall(node *Node, requireStringLiteralLikeArgument bool) bool {
if !IsCallExpression(node) {
return false
}
call := node.AsCallExpression()
if !IsIdentifier(call.Expression) || call.Expression.Text() != "require" {
return false
}
if len(call.Arguments.Nodes) != 1 {
return false
}
return !requireStringLiteralLikeArgument || IsStringLiteralLike(call.Arguments.Nodes[0])
}
func IsRequireVariableStatement(node *Node) bool {
if IsVariableStatement(node) {
if declarations := node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes; len(declarations) > 0 {
return core.Every(declarations, IsVariableDeclarationInitializedToRequire)
}
}
return false
}
func GetJSXImplicitImportBase(compilerOptions *core.CompilerOptions, file *SourceFile) string {
jsxImportSourcePragma := GetPragmaFromSourceFile(file, "jsximportsource")
jsxRuntimePragma := GetPragmaFromSourceFile(file, "jsxruntime")
if GetPragmaArgument(jsxRuntimePragma, "factory") == "classic" {
return ""
}
if compilerOptions.Jsx == core.JsxEmitReactJSX ||
compilerOptions.Jsx == core.JsxEmitReactJSXDev ||
compilerOptions.JsxImportSource != "" ||
jsxImportSourcePragma != nil ||
GetPragmaArgument(jsxRuntimePragma, "factory") == "automatic" {
result := GetPragmaArgument(jsxImportSourcePragma, "factory")
if result == "" {
result = compilerOptions.JsxImportSource
}
if result == "" {
result = "react"
}
return result
}
return ""
}
func GetJSXRuntimeImport(base string, options *core.CompilerOptions) string {
if base == "" {
return base
}
return base + "/" + core.IfElse(options.Jsx == core.JsxEmitReactJSXDev, "jsx-dev-runtime", "jsx-runtime")
}
func GetPragmaFromSourceFile(file *SourceFile, name string) *Pragma {
var result *Pragma
if file != nil {
for i := range file.Pragmas {
if file.Pragmas[i].Name == name {
result = &file.Pragmas[i] // Last one wins
}
}
}
return result
}
func GetPragmaArgument(pragma *Pragma, name string) string {
if pragma != nil {
if arg, ok := pragma.Args[name]; ok {
return arg.Value
}
}
return ""
}
// Of the form: `const x = require("x")` or `const { x } = require("x")` or with `var` or `let`
// The variable must not be exported and must not have a type annotation, even a jsdoc one.
// The initializer must be a call to `require` with a string literal or a string literal-like argument.
func IsVariableDeclarationInitializedToRequire(node *Node) bool {
if !IsInJSFile(node) {
return false
}
if node.Kind == KindBindingElement {
node = node.Parent.Parent
}
if node.Kind != KindVariableDeclaration {
return false
}
return node.Parent.Parent.ModifierFlags()&ModifierFlagsExport == 0 &&
node.AsVariableDeclaration().Initializer != nil &&
node.Type() == nil &&
IsRequireCall(node.AsVariableDeclaration().Initializer, true /*requireStringLiteralLikeArgument*/)
}
func IsModuleExportsAccessExpression(node *Node) bool {
if IsAccessExpression(node) && IsModuleIdentifier(node.Expression()) {
if name := GetElementOrPropertyAccessName(node); name != nil {
return name.Text() == "exports"
}
}
return false
}
func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool {
if sourceFile.CheckJsDirective != nil {
return sourceFile.CheckJsDirective.Enabled
}
return compilerOptions.CheckJs == core.TSTrue
}
func IsPlainJSFile(file *SourceFile, checkJs core.Tristate) bool {
return file != nil && (file.ScriptKind == core.ScriptKindJS || file.ScriptKind == core.ScriptKindJSX) && file.CheckJsDirective == nil && checkJs == core.TSUnknown
}
func GetLeftmostAccessExpression(expr *Node) *Node {
for IsAccessExpression(expr) {
expr = expr.Expression()
}
return expr
}
func IsTypeOnlyImportDeclaration(node *Node) bool {
switch node.Kind {
case KindImportSpecifier:
return node.IsTypeOnly() || node.Parent.Parent.IsTypeOnly()
case KindNamespaceImport:
return node.Parent.IsTypeOnly()
case KindImportClause, KindImportEqualsDeclaration:
return node.IsTypeOnly()
}
return false
}
func isTypeOnlyExportDeclaration(node *Node) bool {
switch node.Kind {
case KindExportSpecifier:
return node.AsExportSpecifier().IsTypeOnly || node.Parent.Parent.AsExportDeclaration().IsTypeOnly
case KindExportDeclaration:
d := node.AsExportDeclaration()
return d.IsTypeOnly && d.ModuleSpecifier != nil && d.ExportClause == nil
case KindNamespaceExport:
return node.Parent.AsExportDeclaration().IsTypeOnly
}
return false
}
func IsTypeOnlyImportOrExportDeclaration(node *Node) bool {
return IsTypeOnlyImportDeclaration(node) || isTypeOnlyExportDeclaration(node)
}
func IsExclusivelyTypeOnlyImportOrExport(node *Node) bool {
switch node.Kind {
case KindExportDeclaration:
return node.AsExportDeclaration().IsTypeOnly
case KindImportDeclaration, KindJSImportDeclaration:
if importClause := node.AsImportDeclaration().ImportClause; importClause != nil {
return importClause.AsImportClause().IsTypeOnly()
}
case KindJSDocImportTag:
if importClause := node.AsJSDocImportTag().ImportClause; importClause != nil {
return importClause.AsImportClause().IsTypeOnly()
}
}
return false
}
func GetClassLikeDeclarationOfSymbol(symbol *Symbol) *Node {
return core.Find(symbol.Declarations, IsClassLike)
}
func IsCallLikeExpression(node *Node) bool {
switch node.Kind {
case KindJsxOpeningElement, KindJsxSelfClosingElement, KindJsxOpeningFragment, KindCallExpression, KindNewExpression,
KindTaggedTemplateExpression, KindDecorator:
return true
case KindBinaryExpression:
return node.AsBinaryExpression().OperatorToken.Kind == KindInstanceOfKeyword
}
return false
}
func IsJsxCallLike(node *Node) bool {
switch node.Kind {
case KindJsxOpeningElement, KindJsxSelfClosingElement, KindJsxOpeningFragment:
return true
}
return false
}
func IsCallLikeOrFunctionLikeExpression(node *Node) bool {
return IsCallLikeExpression(node) || IsFunctionExpressionOrArrowFunction(node)
}
func NodeHasKind(node *Node, kind Kind) bool {
if node == nil {
return false
}
return node.Kind == kind
}
func IsContextualKeyword(token Kind) bool {
return KindFirstContextualKeyword <= token && token <= KindLastContextualKeyword
}
func IsThisInTypeQuery(node *Node) bool {
if !IsThisIdentifier(node) {
return false
}
for IsQualifiedName(node.Parent) && node.Parent.AsQualifiedName().Left == node {
node = node.Parent
}
return node.Parent.Kind == KindTypeQuery
}
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `let` declaration.
func IsLet(node *Node) bool {
return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsLet
}
func IsClassMemberModifier(token Kind) bool {
return IsParameterPropertyModifier(token) || token == KindStaticKeyword ||
token == KindOverrideKeyword || token == KindAccessorKeyword
}
func IsParameterPropertyModifier(kind Kind) bool {
return ModifierToFlag(kind)&ModifierFlagsParameterPropertyModifier != 0
}
func ForEachChildAndJSDoc(node *Node, sourceFile *SourceFile, v Visitor) bool {
if node.Flags&NodeFlagsHasJSDoc != 0 {
if visitNodes(v, node.JSDoc(sourceFile)) {
return true
}
}
return node.ForEachChild(v)
}
func IsTypeReferenceType(node *Node) bool {
return node.Kind == KindTypeReference || node.Kind == KindExpressionWithTypeArguments
}
func IsVariableLike(node *Node) bool {
switch node.Kind {
case KindBindingElement, KindEnumMember, KindParameter, KindPropertyAssignment, KindPropertyDeclaration,
KindPropertySignature, KindShorthandPropertyAssignment, KindVariableDeclaration:
return true
}
return false
}
func HasInitializer(node *Node) bool {
switch node.Kind {
case KindVariableDeclaration, KindParameter, KindBindingElement, KindPropertyDeclaration,
KindPropertyAssignment, KindEnumMember, KindForStatement, KindForInStatement, KindForOfStatement,
KindJsxAttribute:
return node.Initializer() != nil
default:
return false
}
}
func GetTypeAnnotationNode(node *Node) *TypeNode {
switch node.Kind {
case KindVariableDeclaration, KindParameter, KindPropertySignature, KindPropertyDeclaration,
KindTypePredicate, KindParenthesizedType, KindTypeOperator, KindMappedType, KindTypeAssertionExpression,
KindAsExpression, KindSatisfiesExpression, KindTypeAliasDeclaration, KindJSTypeAliasDeclaration,
KindNamedTupleMember, KindOptionalType, KindRestType, KindTemplateLiteralTypeSpan, KindJSDocTypeExpression,
KindJSDocPropertyTag, KindJSDocNullableType, KindJSDocNonNullableType, KindJSDocOptionalType:
return node.Type()
default:
funcLike := node.FunctionLikeData()
if funcLike != nil {
return funcLike.Type
}
return nil
}
}
func IsObjectTypeDeclaration(node *Node) bool {
return IsClassLike(node) || IsInterfaceDeclaration(node) || IsTypeLiteralNode(node)
}
func IsClassOrTypeElement(node *Node) bool {
return IsClassElement(node) || IsTypeElement(node)
}
func GetClassExtendsHeritageElement(node *Node) *ExpressionWithTypeArgumentsNode {
heritageElements := GetHeritageElements(node, KindExtendsKeyword)
if len(heritageElements) > 0 {
return heritageElements[0]
}
return nil
}
func GetImplementsTypeNodes(node *Node) []*ExpressionWithTypeArgumentsNode {
return GetHeritageElements(node, KindImplementsKeyword)
}
func IsTypeKeywordToken(node *Node) bool {
return node.Kind == KindTypeKeyword
}
// See `IsJSDocSingleCommentNode`.
func IsJSDocSingleCommentNodeList(nodeList *NodeList) bool {
if nodeList == nil || len(nodeList.Nodes) == 0 {
return false
}
parent := nodeList.Nodes[0].Parent
return IsJSDocSingleCommentNode(parent) && nodeList == parent.CommentList()
}
// See `IsJSDocSingleCommentNode`.
func IsJSDocSingleCommentNodeComment(node *Node) bool {
if node == nil || node.Parent == nil {
return false
}
return IsJSDocSingleCommentNode(node.Parent) && node == node.Parent.CommentList().Nodes[0]
}
// In Strada, if a JSDoc node has a single comment, that comment is represented as a string property
// as a simplification, and therefore that comment is not visited by `forEachChild`.
func IsJSDocSingleCommentNode(node *Node) bool {
return hasComment(node.Kind) && node.CommentList() != nil && len(node.CommentList().Nodes) == 1
}
func IsValidTypeOnlyAliasUseSite(useSite *Node) bool {
return useSite.Flags&(NodeFlagsAmbient|NodeFlagsJSDoc) != 0 ||
IsPartOfTypeQuery(useSite) ||
isIdentifierInNonEmittingHeritageClause(useSite) ||
isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) ||
!(IsExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite))
}
func isIdentifierInNonEmittingHeritageClause(node *Node) bool {
if !IsIdentifier(node) {
return false
}
parent := node.Parent
for IsPropertyAccessExpression(parent) || IsExpressionWithTypeArguments(parent) {
parent = parent.Parent
}
return IsHeritageClause(parent) && (parent.AsHeritageClause().Token == KindImplementsKeyword || IsInterfaceDeclaration(parent.Parent))
}
func isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node *Node) bool {
for NodeKindIs(node, KindIdentifier, KindPropertyAccessExpression) {
node = node.Parent
}
if node.Kind != KindComputedPropertyName {
return false
}
if HasSyntacticModifier(node.Parent, ModifierFlagsAbstract) {
return true
}
return NodeKindIs(node.Parent.Parent, KindInterfaceDeclaration, KindTypeLiteral)
}
func isShorthandPropertyNameUseSite(useSite *Node) bool {
return IsIdentifier(useSite) && IsShorthandPropertyAssignment(useSite.Parent) && useSite.Parent.AsShorthandPropertyAssignment().Name() == useSite
}
func GetPropertyNameForPropertyNameNode(name *Node) string {
switch name.Kind {
case KindIdentifier, KindPrivateIdentifier, KindStringLiteral, KindNoSubstitutionTemplateLiteral,
KindNumericLiteral, KindBigIntLiteral, KindJsxNamespacedName:
return name.Text()
case KindComputedPropertyName:
nameExpression := name.AsComputedPropertyName().Expression
if IsStringOrNumericLiteralLike(nameExpression) {
return nameExpression.Text()
}
if IsSignedNumericLiteral(nameExpression) {
text := nameExpression.AsPrefixUnaryExpression().Operand.Text()
if nameExpression.AsPrefixUnaryExpression().Operator == KindMinusToken {
text = "-" + text
}
return text
}
return InternalSymbolNameMissing
}
panic("Unhandled case in getPropertyNameForPropertyNameNode")
}
func IsPartOfTypeOnlyImportOrExportDeclaration(node *Node) bool {
return FindAncestor(node, IsTypeOnlyImportOrExportDeclaration) != nil
}
func IsPartOfExclusivelyTypeOnlyImportOrExportDeclaration(node *Node) bool {
return FindAncestor(node, IsExclusivelyTypeOnlyImportOrExport) != nil
}
func IsEmittableImport(node *Node) bool {
switch node.Kind {
case KindImportDeclaration:
return node.AsImportDeclaration().ImportClause != nil && !node.AsImportDeclaration().ImportClause.IsTypeOnly()
case KindExportDeclaration:
return !node.AsExportDeclaration().IsTypeOnly
case KindImportEqualsDeclaration:
return !node.AsImportEqualsDeclaration().IsTypeOnly
case KindCallExpression:
return IsImportCall(node)
}
return false
}
func IsResolutionModeOverrideHost(node *Node) bool {
if node == nil {
return false
}
switch node.Kind {
case KindImportType, KindExportDeclaration, KindImportDeclaration, KindJSImportDeclaration:
return true
}
return false
}
func HasResolutionModeOverride(node *Node) bool {
if node == nil {
return false
}
var attributes *ImportAttributesNode
switch node.Kind {
case KindImportType:
attributes = node.AsImportTypeNode().Attributes
case KindImportDeclaration, KindJSImportDeclaration:
attributes = node.AsImportDeclaration().Attributes
case KindExportDeclaration:
attributes = node.AsExportDeclaration().Attributes
}
if attributes != nil {
_, ok := attributes.GetResolutionModeOverride()
return ok
}
return false
}
func IsStringTextContainingNode(node *Node) bool {
return node.Kind == KindStringLiteral || IsTemplateLiteralKind(node.Kind)
}
func IsTemplateLiteralKind(kind Kind) bool {
return KindFirstTemplateToken <= kind && kind <= KindLastTemplateToken
}
func IsTemplateLiteralToken(node *Node) bool {
return IsTemplateLiteralKind(node.Kind)
}
func GetExternalModuleImportEqualsDeclarationExpression(node *Node) *Node {
debug.Assert(IsExternalModuleImportEqualsDeclaration(node))
return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().Expression
}
func CreateModifiersFromModifierFlags(flags ModifierFlags, createModifier func(kind Kind) *Node) []*Node {
var result []*Node
if flags&ModifierFlagsExport != 0 {
result = append(result, createModifier(KindExportKeyword))
}
if flags&ModifierFlagsAmbient != 0 {
result = append(result, createModifier(KindDeclareKeyword))
}
if flags&ModifierFlagsDefault != 0 {
result = append(result, createModifier(KindDefaultKeyword))
}
if flags&ModifierFlagsConst != 0 {
result = append(result, createModifier(KindConstKeyword))
}
if flags&ModifierFlagsPublic != 0 {
result = append(result, createModifier(KindPublicKeyword))
}
if flags&ModifierFlagsPrivate != 0 {
result = append(result, createModifier(KindPrivateKeyword))
}
if flags&ModifierFlagsProtected != 0 {
result = append(result, createModifier(KindProtectedKeyword))
}
if flags&ModifierFlagsAbstract != 0 {
result = append(result, createModifier(KindAbstractKeyword))
}
if flags&ModifierFlagsStatic != 0 {
result = append(result, createModifier(KindStaticKeyword))
}
if flags&ModifierFlagsOverride != 0 {
result = append(result, createModifier(KindOverrideKeyword))
}
if flags&ModifierFlagsReadonly != 0 {
result = append(result, createModifier(KindReadonlyKeyword))
}
if flags&ModifierFlagsAccessor != 0 {
result = append(result, createModifier(KindAccessorKeyword))
}
if flags&ModifierFlagsAsync != 0 {
result = append(result, createModifier(KindAsyncKeyword))
}
if flags&ModifierFlagsIn != 0 {
result = append(result, createModifier(KindInKeyword))
}
if flags&ModifierFlagsOut != 0 {
result = append(result, createModifier(KindOutKeyword))
}
return result
}
func GetThisParameter(signature *Node) *Node {
// callback tags do not currently support this parameters
if len(signature.Parameters()) != 0 {
thisParameter := signature.Parameters()[0]
if IsThisParameter(thisParameter) {
return thisParameter
}
}
return nil
}
func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierList) *Node {
switch node.Kind {
case KindTypeParameter:
return factory.UpdateTypeParameterDeclaration(
node.AsTypeParameter(),
modifierArray,
node.Name(),
node.AsTypeParameter().Constraint,
node.AsTypeParameter().DefaultType,
)
case KindParameter:
return factory.UpdateParameterDeclaration(
node.AsParameterDeclaration(),
modifierArray,
node.AsParameterDeclaration().DotDotDotToken,
node.Name(),
node.AsParameterDeclaration().QuestionToken,
node.Type(),
node.Initializer(),
)
case KindConstructorType:
return factory.UpdateConstructorTypeNode(
node.AsConstructorTypeNode(),
modifierArray,
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
)
case KindPropertySignature:
return factory.UpdatePropertySignatureDeclaration(
node.AsPropertySignatureDeclaration(),
modifierArray,
node.Name(),
node.AsPropertySignatureDeclaration().PostfixToken,
node.Type(),
node.Initializer(),
)
case KindPropertyDeclaration:
return factory.UpdatePropertyDeclaration(
node.AsPropertyDeclaration(),
modifierArray,
node.Name(),
node.AsPropertyDeclaration().PostfixToken,
node.Type(),
node.Initializer(),
)
case KindMethodSignature:
return factory.UpdateMethodSignatureDeclaration(
node.AsMethodSignatureDeclaration(),
modifierArray,
node.Name(),
node.AsMethodSignatureDeclaration().PostfixToken,
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
)
case KindMethodDeclaration:
return factory.UpdateMethodDeclaration(
node.AsMethodDeclaration(),
modifierArray,
node.AsMethodDeclaration().AsteriskToken,
node.Name(),
node.AsMethodDeclaration().PostfixToken,
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsMethodDeclaration().FullSignature,
node.Body(),
)
case KindConstructor:
return factory.UpdateConstructorDeclaration(
node.AsConstructorDeclaration(),
modifierArray,
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsConstructorDeclaration().FullSignature,
node.Body(),
)
case KindGetAccessor:
return factory.UpdateGetAccessorDeclaration(
node.AsGetAccessorDeclaration(),
modifierArray,
node.Name(),
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsGetAccessorDeclaration().FullSignature,
node.Body(),
)
case KindSetAccessor:
return factory.UpdateSetAccessorDeclaration(
node.AsSetAccessorDeclaration(),
modifierArray,
node.Name(),
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsSetAccessorDeclaration().FullSignature,
node.Body(),
)
case KindIndexSignature:
return factory.UpdateIndexSignatureDeclaration(
node.AsIndexSignatureDeclaration(),
modifierArray,
node.ParameterList(),
node.Type(),
)
case KindFunctionExpression:
return factory.UpdateFunctionExpression(
node.AsFunctionExpression(),
modifierArray,
node.AsFunctionExpression().AsteriskToken,
node.Name(),
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsFunctionExpression().FullSignature,
node.Body(),
)
case KindArrowFunction:
return factory.UpdateArrowFunction(
node.AsArrowFunction(),
modifierArray,
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsArrowFunction().FullSignature,
node.AsArrowFunction().EqualsGreaterThanToken,
node.Body(),
)
case KindClassExpression:
return factory.UpdateClassExpression(
node.AsClassExpression(),
modifierArray,
node.Name(),
node.TypeParameterList(),
node.AsClassExpression().HeritageClauses,
node.MemberList(),
)
case KindVariableStatement:
return factory.UpdateVariableStatement(
node.AsVariableStatement(),
modifierArray,
node.AsVariableStatement().DeclarationList,
)
case KindFunctionDeclaration:
return factory.UpdateFunctionDeclaration(
node.AsFunctionDeclaration(),
modifierArray,
node.AsFunctionDeclaration().AsteriskToken,
node.Name(),
node.TypeParameterList(),
node.ParameterList(),
node.Type(),
node.AsFunctionDeclaration().FullSignature,
node.Body(),
)
case KindClassDeclaration:
return factory.UpdateClassDeclaration(
node.AsClassDeclaration(),
modifierArray,
node.Name(),
node.TypeParameterList(),
node.AsClassDeclaration().HeritageClauses,
node.MemberList(),
)
case KindInterfaceDeclaration:
return factory.UpdateInterfaceDeclaration(
node.AsInterfaceDeclaration(),
modifierArray,
node.Name(),
node.TypeParameterList(),
node.AsInterfaceDeclaration().HeritageClauses,
node.MemberList(),
)
case KindTypeAliasDeclaration:
return factory.UpdateTypeAliasDeclaration(
node.AsTypeAliasDeclaration(),
modifierArray,
node.Name(),
node.TypeParameterList(),
node.Type(),
)
case KindEnumDeclaration:
return factory.UpdateEnumDeclaration(
node.AsEnumDeclaration(),
modifierArray,
node.Name(),
node.MemberList(),
)
case KindModuleDeclaration:
return factory.UpdateModuleDeclaration(
node.AsModuleDeclaration(),
modifierArray,
node.AsModuleDeclaration().Keyword,
node.Name(),
node.Body(),
)
case KindImportEqualsDeclaration:
return factory.UpdateImportEqualsDeclaration(
node.AsImportEqualsDeclaration(),
modifierArray,
node.IsTypeOnly(),
node.Name(),
node.AsImportEqualsDeclaration().ModuleReference,
)
case KindImportDeclaration:
return factory.UpdateImportDeclaration(
node.AsImportDeclaration(),
modifierArray,
node.AsImportDeclaration().ImportClause,
node.AsImportDeclaration().ModuleSpecifier,
node.AsImportDeclaration().Attributes,
)
case KindExportAssignment:
return factory.UpdateExportAssignment(
node.AsExportAssignment(),
modifierArray,
node.Type(),
node.Expression(),
)
case KindExportDeclaration:
return factory.UpdateExportDeclaration(
node.AsExportDeclaration(),
modifierArray,
node.IsTypeOnly(),
node.AsExportDeclaration().ExportClause,
node.AsExportDeclaration().ModuleSpecifier,
node.AsExportDeclaration().Attributes,
)
}
panic(fmt.Sprintf("Node that does not have modifiers tried to have modifier replaced: %d", node.Kind))
}
func IsLateVisibilityPaintedStatement(node *Node) bool {
switch node.Kind {
case KindImportDeclaration,
KindJSImportDeclaration,
KindImportEqualsDeclaration,
KindVariableStatement,
KindClassDeclaration,
KindFunctionDeclaration,
KindModuleDeclaration,
KindTypeAliasDeclaration,
KindJSTypeAliasDeclaration,
KindInterfaceDeclaration,
KindEnumDeclaration:
return true
default:
return false
}
}
func IsExternalModuleAugmentation(node *Node) bool {
return IsAmbientModule(node) && IsModuleAugmentationExternal(node)
}
func GetSourceFileOfModule(module *Symbol) *SourceFile {
declaration := module.ValueDeclaration
if declaration == nil {
declaration = getNonAugmentationDeclaration(module)
}
return GetSourceFileOfNode(declaration)
}
func getNonAugmentationDeclaration(symbol *Symbol) *Node {
return core.Find(symbol.Declarations, func(d *Node) bool {
return !IsExternalModuleAugmentation(d) && !IsGlobalScopeAugmentation(d)
})
}
func IsTypeDeclaration(node *Node) bool {
switch node.Kind {
case KindTypeParameter, KindClassDeclaration, KindInterfaceDeclaration, KindTypeAliasDeclaration, KindJSTypeAliasDeclaration, KindEnumDeclaration:
return true
case KindImportClause:
return node.AsImportClause().PhaseModifier == KindTypeKeyword
case KindImportSpecifier:
return node.Parent.Parent.AsImportClause().PhaseModifier == KindTypeKeyword
case KindExportSpecifier:
return node.Parent.Parent.AsExportDeclaration().IsTypeOnly
default:
return false
}
}
func IsTypeDeclarationName(name *Node) bool {
return name.Kind == KindIdentifier &&
IsTypeDeclaration(name.Parent) &&
GetNameOfDeclaration(name.Parent) == name
}
func IsRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindQualifiedName:
return parent.AsQualifiedName().Right == node
case KindPropertyAccessExpression:
return parent.AsPropertyAccessExpression().Name() == node
case KindMetaProperty:
return parent.AsMetaProperty().Name() == node
}
return false
}
func ShouldTransformImportCall(fileName string, options *core.CompilerOptions, impliedNodeFormatForEmit core.ModuleKind) bool {
moduleKind := options.GetEmitModuleKind()
if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext || moduleKind == core.ModuleKindPreserve {
return false
}
return impliedNodeFormatForEmit < core.ModuleKindES2015
}
func HasQuestionToken(node *Node) bool {
return IsQuestionToken(node.PostfixToken())
}
func IsJsxOpeningLikeElement(node *Node) bool {
return IsJsxOpeningElement(node) || IsJsxSelfClosingElement(node)
}
func GetInvokedExpression(node *Node) *Node {
switch node.Kind {
case KindTaggedTemplateExpression:
return node.AsTaggedTemplateExpression().Tag
case KindJsxOpeningElement, KindJsxSelfClosingElement:
return node.TagName()
case KindBinaryExpression:
return node.AsBinaryExpression().Right
case KindJsxOpeningFragment:
return node
default:
return node.Expression()
}
}
func IsCallOrNewExpression(node *Node) bool {
return IsCallExpression(node) || IsNewExpression(node)
}
func IndexOfNode(nodes []*Node, node *Node) int {
index, ok := slices.BinarySearchFunc(nodes, node, compareNodePositions)
if ok {
return index
}
return -1
}
func compareNodePositions(n1, n2 *Node) int {
return n1.Pos() - n2.Pos()
}
func IsUnterminatedLiteral(node *Node) bool {
return IsLiteralKind(node.Kind) && node.LiteralLikeData().TokenFlags&TokenFlagsUnterminated != 0 ||
IsTemplateLiteralKind(node.Kind) && node.TemplateLiteralLikeData().TemplateFlags&TokenFlagsUnterminated != 0
}
// Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer.
func IsInitializedProperty(member *ClassElement) bool {
return member.Kind == KindPropertyDeclaration &&
member.Initializer() != nil
}
func IsTrivia(token Kind) bool {
return KindFirstTriviaToken <= token && token <= KindLastTriviaToken
}
func HasDecorators(node *Node) bool {
return HasSyntacticModifier(node, ModifierFlagsDecorator)
}
type hasFileNameImpl struct {
fileName string
path tspath.Path
}
func NewHasFileName(fileName string, path tspath.Path) HasFileName {
return &hasFileNameImpl{
fileName: fileName,
path: path,
}
}
func (h *hasFileNameImpl) FileName() string {
return h.fileName
}
func (h *hasFileNameImpl) Path() tspath.Path {
return h.path
}
func GetSemanticJsxChildren(children []*JsxChild) []*JsxChild {
return core.Filter(children, func(i *JsxChild) bool {
switch i.Kind {
case KindJsxExpression:
return i.Expression() != nil
case KindJsxText:
return !i.AsJsxText().ContainsOnlyTriviaWhiteSpaces
default:
return true
}
})
}
// Returns true if the node kind has a comment property.
func hasComment(kind Kind) bool {
switch kind {
case KindJSDoc, KindJSDocTag, KindJSDocAugmentsTag, KindJSDocImplementsTag,
KindJSDocDeprecatedTag, KindJSDocPublicTag, KindJSDocPrivateTag, KindJSDocProtectedTag,
KindJSDocReadonlyTag, KindJSDocOverrideTag, KindJSDocCallbackTag, KindJSDocOverloadTag,
KindJSDocParameterTag, KindJSDocPropertyTag, KindJSDocReturnTag, KindJSDocThisTag,
KindJSDocTypeTag, KindJSDocTemplateTag, KindJSDocTypedefTag, KindJSDocSeeTag,
KindJSDocSatisfiesTag, KindJSDocImportTag:
return true
default:
return false
}
}
func IsAssignmentPattern(node *Node) bool {
return node.Kind == KindArrayLiteralExpression || node.Kind == KindObjectLiteralExpression
}
func GetElementsOfBindingOrAssignmentPattern(name *Node) []*Node {
switch name.Kind {
case KindObjectBindingPattern, KindArrayBindingPattern:
// `a` in `{a}`
// `a` in `[a]`
return name.AsBindingPattern().Elements.Nodes
case KindArrayLiteralExpression:
// `a` in `[a]`
return name.AsArrayLiteralExpression().Elements.Nodes
case KindObjectLiteralExpression:
// `a` in `{a}`
return name.AsObjectLiteralExpression().Properties.Nodes
}
return nil
}
func IsDeclarationBindingElement(bindingElement *Node) bool {
switch bindingElement.Kind {
case KindVariableDeclaration, KindParameter, KindBindingElement:
return true
default:
return false
}
}
/**
* Gets the name of an BindingOrAssignmentElement.
*/
func GetTargetOfBindingOrAssignmentElement(bindingElement *Node) *Node {
if IsDeclarationBindingElement(bindingElement) {
// `a` in `let { a } = ...`
// `a` in `let { a = 1 } = ...`
// `b` in `let { a: b } = ...`
// `b` in `let { a: b = 1 } = ...`
// `a` in `let { ...a } = ...`
// `{b}` in `let { a: {b} } = ...`
// `{b}` in `let { a: {b} = 1 } = ...`
// `[b]` in `let { a: [b] } = ...`
// `[b]` in `let { a: [b] = 1 } = ...`
// `a` in `let [a] = ...`
// `a` in `let [a = 1] = ...`
// `a` in `let [...a] = ...`
// `{a}` in `let [{a}] = ...`
// `{a}` in `let [{a} = 1] = ...`
// `[a]` in `let [[a]] = ...`
// `[a]` in `let [[a] = 1] = ...`
return bindingElement.Name()
}
if IsObjectLiteralElement(bindingElement) {
switch bindingElement.Kind {
case KindPropertyAssignment:
// `b` in `({ a: b } = ...)`
// `b` in `({ a: b = 1 } = ...)`
// `{b}` in `({ a: {b} } = ...)`
// `{b}` in `({ a: {b} = 1 } = ...)`
// `[b]` in `({ a: [b] } = ...)`
// `[b]` in `({ a: [b] = 1 } = ...)`
// `b.c` in `({ a: b.c } = ...)`
// `b.c` in `({ a: b.c = 1 } = ...)`
// `b[0]` in `({ a: b[0] } = ...)`
// `b[0]` in `({ a: b[0] = 1 } = ...)`
return GetTargetOfBindingOrAssignmentElement(bindingElement.Initializer())
case KindShorthandPropertyAssignment:
// `a` in `({ a } = ...)`
// `a` in `({ a = 1 } = ...)`
return bindingElement.Name()
case KindSpreadAssignment:
// `a` in `({ ...a } = ...)`
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsSpreadAssignment().Expression)
}
// no target
return nil
}
if IsAssignmentExpression(bindingElement /*excludeCompoundAssignment*/, true) {
// `a` in `[a = 1] = ...`
// `{a}` in `[{a} = 1] = ...`
// `[a]` in `[[a] = 1] = ...`
// `a.b` in `[a.b = 1] = ...`
// `a[0]` in `[a[0] = 1] = ...`
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsBinaryExpression().Left)
}
if IsSpreadElement(bindingElement) {
// `a` in `[...a] = ...`
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsSpreadElement().Expression)
}
// `a` in `[a] = ...`
// `{a}` in `[{a}] = ...`
// `[a]` in `[[a]] = ...`
// `a.b` in `[a.b] = ...`
// `a[0]` in `[a[0]] = ...`
return bindingElement
}
func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node {
switch bindingElement.Kind {
case KindBindingElement:
// `a` in `let { a: b } = ...`
// `[a]` in `let { [a]: b } = ...`
// `"a"` in `let { "a": b } = ...`
// `1` in `let { 1: b } = ...`
if bindingElement.AsBindingElement().PropertyName != nil {
propertyName := bindingElement.AsBindingElement().PropertyName
// if ast.IsPrivateIdentifier(propertyName) {
// return Debug.failBadSyntaxKind(propertyName) // !!!
// }
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.AsComputedPropertyName().Expression) {
return propertyName.AsComputedPropertyName().Expression
}
return propertyName
}
case KindPropertyAssignment:
// `a` in `({ a: b } = ...)`
// `[a]` in `({ [a]: b } = ...)`
// `"a"` in `({ "a": b } = ...)`
// `1` in `({ 1: b } = ...)`
if bindingElement.Name() != nil {
propertyName := bindingElement.Name()
// if ast.IsPrivateIdentifier(propertyName) {
// return Debug.failBadSyntaxKind(propertyName) // !!!
// }
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.AsComputedPropertyName().Expression) {
return propertyName.AsComputedPropertyName().Expression
}
return propertyName
}
case KindSpreadAssignment:
// `a` in `({ ...a } = ...)`
// if ast.IsPrivateIdentifier(bindingElement.Name()) {
// return Debug.failBadSyntaxKind(bindingElement.Name()) // !!!
// }
return bindingElement.Name()
}
target := GetTargetOfBindingOrAssignmentElement(bindingElement)
if target != nil && IsPropertyName(target) {
return target
}
return nil
}
/**
* Walk an AssignmentPattern to determine if it contains object rest (`...`) syntax. We cannot rely on
* propagation of `TransformFlags.ContainsObjectRestOrSpread` since it isn't propagated by default in
* ObjectLiteralExpression and ArrayLiteralExpression since we do not know whether they belong to an
* AssignmentPattern at the time the nodes are parsed.
*/
func ContainsObjectRestOrSpread(node *Node) bool {
if node.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 {
return true
}
if node.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 {
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
// will not be correctly interpreted by the rest/spread transformer
for _, element := range GetElementsOfBindingOrAssignmentPattern(node) {
target := GetTargetOfBindingOrAssignmentElement(element)
if target != nil && IsAssignmentPattern(target) {
if target.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 {
return true
}
if target.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 {
if ContainsObjectRestOrSpread(target) {
return true
}
}
}
}
}
return false
}
func IsEmptyObjectLiteral(expression *Node) bool {
return expression.Kind == KindObjectLiteralExpression && len(expression.AsObjectLiteralExpression().Properties.Nodes) == 0
}
func IsEmptyArrayLiteral(expression *Node) bool {
return expression.Kind == KindArrayLiteralExpression && len(expression.AsArrayLiteralExpression().Elements.Nodes) == 0
}
func GetRestIndicatorOfBindingOrAssignmentElement(bindingElement *Node) *Node {
switch bindingElement.Kind {
case KindParameter:
return bindingElement.AsParameterDeclaration().DotDotDotToken
case KindBindingElement:
return bindingElement.AsBindingElement().DotDotDotToken
case KindSpreadElement, KindSpreadAssignment:
return bindingElement
}
return nil
}
func IsJSDocNameReferenceContext(node *Node) bool {
return node.Flags&NodeFlagsJSDoc != 0 && FindAncestor(node, func(node *Node) bool {
return IsJSDocNameReference(node) || IsJSDocLinkLike(node)
}) != nil
}
func IsImportOrImportEqualsDeclaration(node *Node) bool {
return IsImportDeclaration(node) || IsImportEqualsDeclaration(node)
}
func IsKeyword(token Kind) bool {
return KindFirstKeyword <= token && token <= KindLastKeyword
}
func IsNonContextualKeyword(token Kind) bool {
return IsKeyword(token) && !IsContextualKeyword(token)
}
func HasModifier(node *Node, flags ModifierFlags) bool {
return node.ModifierFlags()&flags != 0
}
func IsExpandoInitializer(initializer *Node) bool {
if initializer == nil {
return false
}
if IsFunctionExpressionOrArrowFunction(initializer) {
return true
}
if IsInJSFile(initializer) {
return IsClassExpression(initializer) || (IsObjectLiteralExpression(initializer) && len(initializer.AsObjectLiteralExpression().Properties.Nodes) == 0)
}
return false
}
func GetContainingFunction(node *Node) *Node {
return FindAncestor(node.Parent, IsFunctionLike)
}