Files
kjol/tools/tsgo/internal/checker/nodebuilder.go
2026-07-09 16:50:43 -04:00

303 lines
14 KiB
Go

package checker
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/nodebuilder"
"github.com/microsoft/typescript-go/internal/printer"
)
type NodeBuilder struct {
ctxStack []*NodeBuilderContext
host Host
impl *NodeBuilderImpl
verbosity *VerbosityContext // nil for non-hover callers
}
// VerbosityContext controls hover-expansion behavior in the node builder.
// A nil VerbosityContext means no expansion (non-hover callers).
// Level 0 = default hover (maxExpansionDepth = 0; detects expandability without expanding).
// Level 1+ = expansion enabled (maxExpansionDepth = Level).
type VerbosityContext struct {
Level int // 0 = default (no expansion), 1+ = expansion depth
MaxTruncationLength int // 0 = use default
CanIncreaseVerbosity bool // output: whether increasing Level would reveal more
Truncated bool // output: whether output was truncated
}
// EmitContext implements NodeBuilderInterface.
func (b *NodeBuilder) EmitContext() *printer.EmitContext {
return b.impl.e
}
func (b *NodeBuilder) enterContext(enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) {
verbosityLevel := -1
maxTruncationLength := 0
if b.verbosity != nil {
verbosityLevel = b.verbosity.Level
maxTruncationLength = b.verbosity.MaxTruncationLength
}
b.ctxStack = append(b.ctxStack, b.impl.ctx)
b.impl.ctx = &NodeBuilderContext{
host: b.host,
tracker: tracker,
flags: flags,
internalFlags: internalFlags,
maxExpansionDepth: verbosityLevel,
maxTruncationLength: maxTruncationLength,
enclosingDeclaration: enclosingDeclaration,
enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration),
inferTypeParameters: make([]*Type, 0),
symbolDepth: make(map[CompositeSymbolIdentity]int),
trackedSymbols: make([]*TrackedSymbolArgs, 0),
reverseMappedStack: make([]*ast.Symbol, 0),
enclosingSymbolTypes: make(map[ast.SymbolId]*Type),
remappedSymbolReferences: make(map[ast.SymbolId]*ast.Symbol),
}
tracker = NewSymbolTrackerImpl(b.impl.ctx, tracker)
b.impl.ctx.tracker = tracker
}
// propagateVerbosityOut copies expansion signals from the context to the VerbosityContext output.
func (b *NodeBuilder) propagateVerbosityOut() {
if b.verbosity != nil {
// Only set to true, never clear — multiple calls share the same VerbosityContext
if b.impl.ctx.canIncreaseExpansionDepth {
b.verbosity.CanIncreaseVerbosity = true
}
if b.impl.ctx.expansionTruncated {
b.verbosity.Truncated = true
}
}
}
func (b *NodeBuilder) popContext() {
stackSize := len(b.ctxStack)
if stackSize == 0 {
b.impl.ctx = nil
} else {
b.impl.ctx = b.ctxStack[stackSize-1]
b.ctxStack = b.ctxStack[:stackSize-1]
}
}
func (b *NodeBuilder) exitContext(result *ast.Node) *ast.Node {
b.propagateVerbosityOut()
b.exitContextCheck()
defer b.popContext()
if b.impl.ctx.encounteredError {
return nil
}
return result
}
func (b *NodeBuilder) exitContextSlice(result []*ast.Node) []*ast.Node {
b.propagateVerbosityOut()
b.exitContextCheck()
defer b.popContext()
if b.impl.ctx.encounteredError {
return nil
}
return result
}
func (b *NodeBuilder) exitContextCheck() {
if b.impl.ctx.truncating && b.impl.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
b.impl.ctx.tracker.ReportTruncationError()
}
}
// IndexInfoToIndexSignatureDeclaration implements NodeBuilderInterface.
func (b *NodeBuilder) IndexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.indexInfoToIndexSignatureDeclarationHelper(info, nil))
}
// SerializeReturnTypeForSignature implements NodeBuilderInterface.
func (b *NodeBuilder) SerializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
signature := b.impl.ch.getSignatureFromDeclaration(signatureDeclaration)
_, cleanup := b.impl.enterSignatureScope(signature)
result := b.impl.serializeReturnTypeForSignature(signature, true)
cleanup()
return b.exitContext(result)
}
func (b *NodeBuilder) SerializeTypeParametersForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration)
typeParams := b.SymbolToTypeParameterDeclarations(symbol, enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContextSlice(typeParams)
}
// SerializeTypeForDeclaration implements NodeBuilderInterface.
func (b *NodeBuilder) SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.serializeTypeForDeclaration(declaration, nil, symbol, true))
}
// SerializeTypeForExpression implements NodeBuilderInterface.
func (b *NodeBuilder) SerializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.serializeTypeForExpression(expr))
}
// SignatureToSignatureDeclaration implements NodeBuilderInterface.
func (b *NodeBuilder) SignatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.signatureToSignatureDeclarationHelper(signature, kind, nil))
}
// ExpandSymbolForHover produces declaration nodes for a symbol with verbosity level support.
func (b *NodeBuilder) ExpandSymbolForHover(symbol *ast.Symbol, meaning ast.SymbolFlags) []*ast.Node {
b.enterContext(nil, nodebuilder.FlagsIgnoreErrors|nodebuilder.FlagsMultilineObjectLiterals|nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope, nodebuilder.InternalFlagsNone, nil)
// Push the declared type onto the type stack to prevent re-expansion.
// We push a nil sentinel after the real type so that isTypeOnStack
// (which skips the last element) still checks declaredType.
declaredType := b.impl.ch.getDeclaredTypeOfSymbol(symbol)
b.impl.ctx.typeStack = append(b.impl.ctx.typeStack, declaredType)
b.impl.ctx.typeStack = append(b.impl.ctx.typeStack, nil)
nodes := b.impl.expandSymbolForHover(symbol)
b.impl.ctx.typeStack = b.impl.ctx.typeStack[:len(b.impl.ctx.typeStack)-2]
b.propagateVerbosityOut()
// Simplify declarations by applying original modifiers
result := make([]*ast.Node, 0, len(nodes))
for _, node := range nodes {
switch node.Kind {
case ast.KindClassDeclaration:
result = append(result, simplifyClassDeclaration(b.impl.f, node, symbol))
case ast.KindEnumDeclaration:
result = append(result, simplifyModifiers(b.impl.f, node, ast.IsEnumDeclaration, symbol))
case ast.KindInterfaceDeclaration:
if meaning&ast.SymbolFlagsInterface != 0 {
result = append(result, simplifyModifiers(b.impl.f, node, ast.IsInterfaceDeclaration, symbol))
}
case ast.KindModuleDeclaration:
result = append(result, simplifyModifiers(b.impl.f, node, ast.IsModuleDeclaration, symbol))
}
}
return b.exitContextSlice(result)
}
func simplifyClassDeclaration(f *ast.NodeFactory, classDecl *ast.Node, symbol *ast.Symbol) *ast.Node {
classDeclarations := core.Filter(symbol.Declarations, ast.IsClassLike)
var originalClassDecl *ast.Node
if len(classDeclarations) > 0 {
originalClassDecl = classDeclarations[0]
} else {
originalClassDecl = classDecl
}
modifiers := originalClassDecl.ModifierFlags() & ^(ast.ModifierFlagsExport | ast.ModifierFlagsAmbient)
isAnonymous := ast.IsClassExpression(originalClassDecl)
if isAnonymous {
cd := classDecl.AsClassDeclaration()
classDecl = f.UpdateClassDeclaration(
cd,
classDecl.Modifiers(),
nil,
cd.TypeParameters,
cd.HeritageClauses,
cd.Members,
)
}
return ast.ReplaceModifiers(f, classDecl, f.NewModifierList(ast.CreateModifiersFromModifierFlags(modifiers, f.NewModifier)))
}
func simplifyModifiers(f *ast.NodeFactory, newDecl *ast.Node, isDeclKind func(*ast.Node) bool, symbol *ast.Symbol) *ast.Node {
decls := core.Filter(symbol.Declarations, isDeclKind)
var declWithModifiers *ast.Node
if len(decls) > 0 {
declWithModifiers = decls[0]
} else {
declWithModifiers = newDecl
}
modifiers := declWithModifiers.ModifierFlags() & ^(ast.ModifierFlagsExport | ast.ModifierFlagsAmbient)
return ast.ReplaceModifiers(f, newDecl, f.NewModifierList(ast.CreateModifiersFromModifierFlags(modifiers, f.NewModifier)))
}
// SymbolToEntityName implements NodeBuilderInterface.
func (b *NodeBuilder) SymbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.symbolToName(symbol, meaning, false))
}
// SymbolToExpression implements NodeBuilderInterface.
func (b *NodeBuilder) SymbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.symbolToExpression(symbol, meaning))
}
// SymbolToNode implements NodeBuilderInterface.
func (b *NodeBuilder) SymbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.symbolToNode(symbol, meaning))
}
// SymbolToParameterDeclaration implements NodeBuilderInterface.
func (b NodeBuilder) SymbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.symbolToParameterDeclaration(symbol, false))
}
// SymbolToTypeParameterDeclarations implements NodeBuilderInterface.
func (b *NodeBuilder) SymbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContextSlice(b.impl.symbolToTypeParameterDeclarations(symbol))
}
// TypeParameterToDeclaration implements NodeBuilderInterface.
func (b *NodeBuilder) TypeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.typeParameterToDeclaration(parameter))
}
// TypePredicateToTypePredicateNode implements NodeBuilderInterface.
func (b *NodeBuilder) TypePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.typePredicateToTypePredicateNode(predicate))
}
// TypeToTypeNode implements NodeBuilderInterface.
func (b *NodeBuilder) TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.typeToTypeNode(typ))
}
func (b *NodeBuilder) TryJSTypeNodeToTypeNode(node *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
return b.exitContext(b.impl.tryJSTypeNodeToTypeNode(node))
}
// var _ NodeBuilderInterface = NewNodeBuilderAPI(nil, nil)
func NewNodeBuilder(ch *Checker, e *printer.EmitContext) *NodeBuilder {
return NewNodeBuilderEx(ch, e, nil /*idToSymbol*/)
}
func NewNodeBuilderEx(ch *Checker, e *printer.EmitContext, idToSymbol map[*ast.IdentifierNode]*ast.Symbol) *NodeBuilder {
impl := newNodeBuilderImpl(ch, e, idToSymbol)
return &NodeBuilder{impl: impl, ctxStack: make([]*NodeBuilderContext, 0, 1), host: ch.program}
}
func (c *Checker) getNodeBuilder() (*NodeBuilder, func()) {
releaseNodes := func() {
c.typeToStringNodebuilder.EmitContext().Factory.ReleaseArenas() // Allow any allocated nodes to be freed if they're no longer in a cache
}
if c.typeToStringNodebuilder != nil {
return c.typeToStringNodebuilder, releaseNodes
}
c.typeToStringNodebuilder = c.getNodeBuilderEx(nil /*idToSymbol*/)
return c.typeToStringNodebuilder, releaseNodes
}
func (c *Checker) getNodeBuilderEx(idToSymbol map[*ast.IdentifierNode]*ast.Symbol) *NodeBuilder {
b := NewNodeBuilderEx(c, printer.NewEmitContext(), idToSymbol)
return b
}