598 lines
24 KiB
Go
598 lines
24 KiB
Go
package checker
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/collections"
|
|
"github.com/microsoft/typescript-go/internal/core"
|
|
"github.com/microsoft/typescript-go/internal/jsnum"
|
|
"github.com/microsoft/typescript-go/internal/nodebuilder"
|
|
"github.com/microsoft/typescript-go/internal/scanner"
|
|
)
|
|
|
|
// isExpanding returns whether the node builder context is operating in hover-expansion mode.
|
|
func isExpanding(ctx *NodeBuilderContext) bool {
|
|
return ctx.maxExpansionDepth != -1
|
|
}
|
|
|
|
// expandSymbolForHover produces declaration nodes (class, interface, enum, module) for a symbol
|
|
// for expandable hover. This is a focused alternative to the full symbolTableToDeclarationStatements
|
|
// machinery used by declaration emit — it directly builds the declaration nodes hover needs
|
|
// without the declaration-emit scaffolding (deferred privates, symbol name remapping, export
|
|
// modifier computation, alias resolution, visited symbols tracking).
|
|
func (b *NodeBuilderImpl) expandSymbolForHover(symbol *ast.Symbol) []*ast.Node {
|
|
var results []*ast.Node
|
|
if symbol.Flags&ast.SymbolFlagsEnum != 0 {
|
|
if node := b.expandEnumDecl(symbol); node != nil {
|
|
results = append(results, node)
|
|
}
|
|
}
|
|
if symbol.Flags&ast.SymbolFlagsClass != 0 {
|
|
if node := b.expandClassDecl(symbol); node != nil {
|
|
results = append(results, node)
|
|
}
|
|
}
|
|
// Module/namespace before interface (matching Strada ordering for merged declarations)
|
|
if symbol.Flags&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0 {
|
|
if node := b.expandModuleDecl(symbol); node != nil {
|
|
results = append(results, node)
|
|
}
|
|
}
|
|
if symbol.Flags&ast.SymbolFlagsInterface != 0 && symbol.Flags&ast.SymbolFlagsClass == 0 {
|
|
if node := b.expandInterfaceDecl(symbol); node != nil {
|
|
results = append(results, node)
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
// expandEnumDecl produces an EnumDeclaration node with all members.
|
|
func (b *NodeBuilderImpl) expandEnumDecl(symbol *ast.Symbol) *ast.Node {
|
|
name := ast.SymbolName(symbol)
|
|
b.ctx.approximateLength += 9 + len(name)
|
|
memberProps := core.Filter(b.ch.getPropertiesOfType(b.ch.getTypeOfSymbol(symbol)), func(p *ast.Symbol) bool {
|
|
return p.Flags&ast.SymbolFlagsEnumMember != 0
|
|
})
|
|
var members []*ast.Node
|
|
for i, p := range memberProps {
|
|
if b.checkTruncationLengthIfExpanding() && i+3 < len(memberProps)-1 {
|
|
b.ctx.expansionTruncated = true
|
|
members = append(members, b.f.NewEnumMember(b.f.NewStringLiteral(fmt.Sprintf(" ... %d more ... ", len(memberProps)-i-1), 0), nil))
|
|
last := memberProps[len(memberProps)-1]
|
|
members = append(members, b.f.NewEnumMember(b.f.NewIdentifier(last.Name), b.enumMemberInitializer(last)))
|
|
break
|
|
}
|
|
memberDecl := core.Find(p.Declarations, ast.IsEnumMember)
|
|
var initializer *ast.Node
|
|
if memberDecl != nil && memberDecl.AsEnumMember().Initializer != nil {
|
|
initializer = b.f.DeepCloneNode(memberDecl.AsEnumMember().Initializer)
|
|
} else {
|
|
initializer = b.enumMemberInitializer(p)
|
|
}
|
|
b.ctx.approximateLength += 4 + len(p.Name)
|
|
if initializer != nil {
|
|
b.ctx.approximateLength += 5 // " = " + value estimate
|
|
}
|
|
members = append(members, b.f.NewEnumMember(b.f.NewIdentifier(p.Name), initializer))
|
|
}
|
|
|
|
constModifier := ast.ModifierFlagsNone
|
|
if isConstEnumSymbol(symbol) {
|
|
constModifier = ast.ModifierFlagsConst
|
|
}
|
|
var mods *ast.ModifierList
|
|
if constModifier != 0 {
|
|
mods = b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(constModifier, b.f.NewModifier))
|
|
}
|
|
return b.f.NewEnumDeclaration(mods, b.f.NewIdentifier(name), b.f.NewNodeList(members))
|
|
}
|
|
|
|
func (b *NodeBuilderImpl) enumMemberInitializer(p *ast.Symbol) *ast.Node {
|
|
memberDecl := core.Find(p.Declarations, ast.IsEnumMember)
|
|
if memberDecl == nil {
|
|
return nil
|
|
}
|
|
val := b.ch.GetConstantValue(memberDecl)
|
|
if val == nil {
|
|
return nil
|
|
}
|
|
switch v := val.(type) {
|
|
case string:
|
|
return b.f.NewStringLiteral(v, 0)
|
|
case jsnum.Number:
|
|
return b.f.NewNumericLiteral(v.String(), 0)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// expandClassDecl produces a ClassDeclaration node with heritage clauses and members.
|
|
func (b *NodeBuilderImpl) expandClassDecl(symbol *ast.Symbol) *ast.Node {
|
|
name := ast.SymbolName(symbol)
|
|
b.ctx.approximateLength += 9 + len(name)
|
|
|
|
classLikeDeclarations := core.Filter(symbol.Declarations, ast.IsClassLike)
|
|
originalDecl := core.FirstOrNil(classLikeDeclarations)
|
|
oldEnclosing := b.ctx.enclosingDeclaration
|
|
if originalDecl != nil {
|
|
b.ctx.enclosingDeclaration = originalDecl
|
|
}
|
|
defer func() { b.ctx.enclosingDeclaration = oldEnclosing }()
|
|
|
|
localParams := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
|
typeParamDecls := core.Map(localParams, func(p *Type) *ast.Node { return b.typeParameterToDeclaration(p) })
|
|
|
|
declaredType := b.ch.getDeclaredTypeOfClassOrInterface(symbol)
|
|
classType := b.ch.getTypeWithThisArgument(declaredType, nil, false)
|
|
baseTypes := b.ch.getBaseTypes(b.ch.getTargetType(classType))
|
|
staticType := b.ch.getTypeOfSymbol(symbol)
|
|
isClass := staticType.symbol != nil && staticType.symbol.ValueDeclaration != nil && ast.IsClassLike(staticType.symbol.ValueDeclaration)
|
|
var staticBaseType *Type
|
|
if isClass {
|
|
staticBaseType = b.ch.getBaseConstructorTypeOfClass(declaredType)
|
|
} else {
|
|
staticBaseType = b.ch.anyType
|
|
}
|
|
|
|
// Heritage clauses
|
|
heritageClauses := b.hoverHeritageClauses(classLikeDeclarations)
|
|
|
|
// Instance members via addPropertyToElementList (reusing existing serialization),
|
|
// then convert TypeElements to ClassElements and add class-specific modifiers
|
|
allProps := b.ch.getPropertiesOfType(classType)
|
|
symbolProps := b.filterInheritedProperties(classType, baseTypes, allProps)
|
|
publicProps := core.Filter(symbolProps, func(s *ast.Symbol) bool { return !isHashPrivate(s) })
|
|
hasPrivate := core.Some(symbolProps, isHashPrivate)
|
|
|
|
var instanceMembers []*ast.Node
|
|
instanceMembers = b.serializePropertiesWithTruncation(publicProps, instanceMembers)
|
|
instanceMembers = typeElementsToClassElements(b.f, instanceMembers)
|
|
instanceMembers = b.addClassModifiers(instanceMembers, false)
|
|
|
|
// Static members
|
|
staticProps := core.Filter(b.ch.getPropertiesOfType(staticType), func(p *ast.Symbol) bool {
|
|
return p.Flags&ast.SymbolFlagsPrototype == 0 && p.Name != "prototype" && !b.isNamespaceMember(p)
|
|
})
|
|
var staticMembers []*ast.Node
|
|
staticMembers = b.serializePropertiesWithTruncation(staticProps, staticMembers)
|
|
staticMembers = typeElementsToClassElements(b.f, staticMembers)
|
|
staticMembers = b.addClassModifiers(staticMembers, true)
|
|
|
|
// Hash-private members
|
|
var privateMembers []*ast.Node
|
|
if hasPrivate {
|
|
privateMembers = b.serializePropertiesWithTruncation(core.Filter(symbolProps, isHashPrivate), privateMembers)
|
|
privateMembers = typeElementsToClassElements(b.f, privateMembers)
|
|
}
|
|
|
|
// Constructors
|
|
constructors := b.serializeConstructors(staticType, staticBaseType, isClass, symbol)
|
|
|
|
// Index signatures
|
|
indexSigs := b.serializeIndexSignaturesOfType(classType, core.FirstOrNil(baseTypes))
|
|
|
|
allMembers := make([]*ast.Node, 0, len(indexSigs)+len(staticMembers)+len(constructors)+len(instanceMembers)+len(privateMembers))
|
|
allMembers = append(allMembers, indexSigs...)
|
|
allMembers = append(allMembers, staticMembers...)
|
|
allMembers = append(allMembers, constructors...)
|
|
allMembers = append(allMembers, instanceMembers...)
|
|
allMembers = append(allMembers, privateMembers...)
|
|
|
|
return b.f.NewClassDeclaration(nil, b.f.NewIdentifier(name), b.f.NewNodeList(typeParamDecls), b.f.NewNodeList(heritageClauses), b.f.NewNodeList(allMembers))
|
|
}
|
|
|
|
// addClassModifiers post-processes class member nodes to add class-specific modifiers
|
|
// (private, protected, public, abstract, static) based on the original symbol declarations.
|
|
func (b *NodeBuilderImpl) addClassModifiers(members []*ast.Node, isStatic bool) []*ast.Node {
|
|
for i, m := range members {
|
|
// Find the symbol for this member by matching the property name
|
|
var memberSymbol *ast.Symbol
|
|
memberName := m.Name()
|
|
if memberName != nil {
|
|
if sym, ok := b.idToSymbol[memberName]; ok {
|
|
memberSymbol = sym
|
|
}
|
|
}
|
|
if memberSymbol == nil {
|
|
continue
|
|
}
|
|
modFlags := getDeclarationModifierFlagsFromSymbol(memberSymbol) &^ ast.ModifierFlagsAsync
|
|
if isStatic {
|
|
modFlags |= ast.ModifierFlagsStatic
|
|
}
|
|
if modFlags != 0 && ast.CanHaveModifiers(m) {
|
|
existing := m.ModifierFlags()
|
|
if modFlags != existing {
|
|
members[i] = ast.ReplaceModifiers(b.f, m, b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(modFlags|existing, b.f.NewModifier)))
|
|
}
|
|
}
|
|
}
|
|
return members
|
|
}
|
|
|
|
// typeElementsToClassElements converts TypeElement nodes (PropertySignature, MethodSignature)
|
|
// to their ClassElement equivalents (PropertyDeclaration, MethodDeclaration) so they can be
|
|
// used as members of a ClassDeclaration. Nodes that are already ClassElements pass through unchanged.
|
|
func typeElementsToClassElements(f *ast.NodeFactory, members []*ast.Node) []*ast.Node {
|
|
for i, m := range members {
|
|
switch m.Kind {
|
|
case ast.KindPropertySignature:
|
|
ps := m.AsPropertySignatureDeclaration()
|
|
members[i] = f.NewPropertyDeclaration(m.Modifiers(), ps.Name(), ps.QuestionToken(), ps.Type, nil)
|
|
case ast.KindMethodSignature:
|
|
ms := m.AsMethodSignatureDeclaration()
|
|
members[i] = f.NewMethodDeclaration(m.Modifiers(), nil, ms.Name(), ms.QuestionToken(), ms.TypeParameters, ms.Parameters, ms.Type, nil, nil)
|
|
}
|
|
}
|
|
return members
|
|
}
|
|
|
|
// expandInterfaceDecl produces an InterfaceDeclaration with members.
|
|
// Reuses addPropertyToElementList for property serialization and
|
|
// signatureToSignatureDeclarationHelper for signatures.
|
|
func (b *NodeBuilderImpl) expandInterfaceDecl(symbol *ast.Symbol) *ast.Node {
|
|
name := ast.SymbolName(symbol)
|
|
b.ctx.approximateLength += 14 + len(name)
|
|
|
|
interfaceType := b.ch.getDeclaredTypeOfClassOrInterface(symbol)
|
|
interfaceDeclarations := core.Filter(symbol.Declarations, ast.IsInterfaceDeclaration)
|
|
localParams := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
|
typeParamDecls := core.Map(localParams, func(p *Type) *ast.Node { return b.typeParameterToDeclaration(p) })
|
|
baseTypes := b.ch.getBaseTypes(interfaceType)
|
|
var baseType *Type
|
|
if len(baseTypes) > 0 {
|
|
baseType = b.ch.getIntersectionType(baseTypes)
|
|
}
|
|
|
|
// Members: reuse existing serialization functions
|
|
resolved := b.ch.resolveStructuredTypeMembers(interfaceType)
|
|
var members []*ast.Node
|
|
|
|
// Index signatures, filtering those identical to base
|
|
members = append(members, b.serializeIndexSignaturesOfType(interfaceType, baseType)...)
|
|
// Construct signatures (skip abstract)
|
|
for _, sig := range resolved.ConstructSignatures() {
|
|
if sig.flags&SignatureFlagsAbstract != 0 {
|
|
continue
|
|
}
|
|
members = append(members, b.signatureToSignatureDeclarationHelper(sig, ast.KindConstructSignature, nil))
|
|
}
|
|
// Call signatures
|
|
for _, sig := range resolved.CallSignatures() {
|
|
members = append(members, b.signatureToSignatureDeclarationHelper(sig, ast.KindCallSignature, nil))
|
|
}
|
|
// Properties, filtering inherited
|
|
filteredProps := b.filterInheritedProperties(interfaceType, baseTypes, resolved.properties)
|
|
members = b.serializePropertiesWithTruncation(filteredProps, members)
|
|
|
|
// Heritage clauses
|
|
heritageClauses := b.hoverHeritageClauses(interfaceDeclarations)
|
|
|
|
return b.f.NewInterfaceDeclaration(nil, b.f.NewIdentifier(name), b.f.NewNodeList(typeParamDecls), b.f.NewNodeList(heritageClauses), b.f.NewNodeList(members))
|
|
}
|
|
|
|
func (b *NodeBuilderImpl) hoverHeritageClauses(declarations []*ast.Node) []*ast.Node {
|
|
var extendsTypes []*ast.Node
|
|
var implementsTypes []*ast.Node
|
|
for _, declaration := range declarations {
|
|
for _, heritageElement := range ast.GetExtendsHeritageClauseElements(declaration) {
|
|
extendsTypes = append(extendsTypes, b.f.DeepCloneNode(heritageElement.AsNode()))
|
|
}
|
|
for _, heritageElement := range ast.GetImplementsHeritageClauseElements(declaration) {
|
|
implementsTypes = append(implementsTypes, b.f.DeepCloneNode(heritageElement.AsNode()))
|
|
}
|
|
}
|
|
|
|
var heritageClauses []*ast.Node
|
|
if len(extendsTypes) > 0 {
|
|
heritageClauses = append(heritageClauses, b.f.NewHeritageClause(ast.KindExtendsKeyword, b.f.NewNodeList(extendsTypes)))
|
|
}
|
|
if len(implementsTypes) > 0 {
|
|
heritageClauses = append(heritageClauses, b.f.NewHeritageClause(ast.KindImplementsKeyword, b.f.NewNodeList(implementsTypes)))
|
|
}
|
|
return heritageClauses
|
|
}
|
|
|
|
// serializePropertiesWithTruncation iterates properties using addPropertyToElementList,
|
|
// with truncation checks matching Strada's createTypeNodesFromResolvedType behavior.
|
|
func (b *NodeBuilderImpl) serializePropertiesWithTruncation(properties []*ast.Symbol, elements []*ast.Node) []*ast.Node {
|
|
properties = core.Filter(properties, func(p *ast.Symbol) bool {
|
|
return p.Flags&ast.SymbolFlagsPrototype == 0
|
|
})
|
|
for i, p := range properties {
|
|
if b.checkTruncationLengthIfExpanding() && (i+3 < len(properties)-1) {
|
|
b.ctx.expansionTruncated = true
|
|
text := fmt.Sprintf("... %d more ...", len(properties)-i-1)
|
|
elements = append(elements, b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier(text), nil, nil, nil))
|
|
elements = b.addPropertyToElementList(properties[len(properties)-1], elements)
|
|
break
|
|
}
|
|
elements = b.addPropertyToElementList(p, elements)
|
|
}
|
|
return elements
|
|
}
|
|
|
|
// serializeConstructors builds constructor signature(s) for a class, with base type filtering.
|
|
func (b *NodeBuilderImpl) serializeConstructors(staticType *Type, staticBaseType *Type, isClass bool, symbol *ast.Symbol) []*ast.Node {
|
|
isNonConstructable := !isClass &&
|
|
symbol.ValueDeclaration != nil &&
|
|
ast.IsInJSFile(symbol.ValueDeclaration) &&
|
|
len(b.ch.getSignaturesOfType(staticType, SignatureKindConstruct)) == 0
|
|
if isNonConstructable {
|
|
b.ctx.approximateLength += 21
|
|
modifiers := ast.CreateModifiersFromModifierFlags(ast.ModifierFlagsPrivate, b.f.NewModifier)
|
|
return []*ast.Node{b.f.NewConstructorDeclaration(b.f.NewModifierList(modifiers), nil, b.f.NewNodeList(nil), nil, nil, nil)}
|
|
}
|
|
signatures := b.ch.getSignaturesOfType(staticType, SignatureKindConstruct)
|
|
if staticBaseType != nil {
|
|
baseSigs := b.ch.getSignaturesOfType(staticBaseType, SignatureKindConstruct)
|
|
if len(baseSigs) == 0 && core.Every(signatures, func(sig *Signature) bool { return len(sig.parameters) == 0 }) {
|
|
return nil
|
|
}
|
|
if len(baseSigs) == len(signatures) {
|
|
allMatch := true
|
|
for i := range baseSigs {
|
|
if b.ch.compareSignaturesIdentical(signatures[i], baseSigs[i], false, false, true, b.ch.compareTypesIdentical) != TernaryTrue {
|
|
allMatch = false
|
|
break
|
|
}
|
|
}
|
|
if allMatch {
|
|
return nil
|
|
}
|
|
}
|
|
var privateProtected ast.ModifierFlags
|
|
for _, sig := range signatures {
|
|
if sig.declaration != nil {
|
|
privateProtected |= sig.declaration.ModifierFlags() & (ast.ModifierFlagsPrivate | ast.ModifierFlagsProtected)
|
|
}
|
|
}
|
|
if privateProtected != 0 {
|
|
return []*ast.Node{b.f.NewConstructorDeclaration(
|
|
b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(privateProtected, b.f.NewModifier)),
|
|
nil, b.f.NewNodeList(nil), nil, nil, nil,
|
|
)}
|
|
}
|
|
} else if core.Every(signatures, func(sig *Signature) bool { return len(sig.parameters) == 0 }) {
|
|
return nil
|
|
}
|
|
var result []*ast.Node
|
|
for _, sig := range signatures {
|
|
b.ctx.approximateLength++
|
|
result = append(result, b.signatureToSignatureDeclarationHelper(sig, ast.KindConstructor, nil))
|
|
}
|
|
return result
|
|
}
|
|
|
|
// serializeIndexSignaturesOfType builds index signature declarations, filtering those identical to baseType.
|
|
func (b *NodeBuilderImpl) serializeIndexSignaturesOfType(input *Type, baseType *Type) []*ast.Node {
|
|
var result []*ast.Node
|
|
for _, info := range b.ch.getIndexInfosOfType(input) {
|
|
if baseType != nil {
|
|
baseInfo := b.ch.getIndexInfoOfType(baseType, info.keyType)
|
|
if baseInfo != nil && b.ch.isTypeIdenticalTo(info.valueType, baseInfo.valueType) {
|
|
continue
|
|
}
|
|
}
|
|
result = append(result, b.indexInfoToIndexSignatureDeclarationHelper(info, nil))
|
|
}
|
|
return result
|
|
}
|
|
|
|
// serializeNamespaceMember produces the appropriate declaration node for a namespace member
|
|
// based on its symbol flags (type alias, enum, class, interface, nested namespace, or variable).
|
|
func (b *NodeBuilderImpl) serializeNamespaceMember(resolved *ast.Symbol, name string) *ast.Node {
|
|
switch {
|
|
case resolved.Flags&ast.SymbolFlagsTypeAlias != 0:
|
|
return b.serializeTypeAliasForNamespace(resolved, name)
|
|
case resolved.Flags&ast.SymbolFlagsEnum != 0:
|
|
return b.expandEnumDecl(resolved)
|
|
case resolved.Flags&ast.SymbolFlagsClass != 0:
|
|
return b.expandClassDecl(resolved)
|
|
case resolved.Flags&ast.SymbolFlagsInterface != 0:
|
|
return b.expandInterfaceDecl(resolved)
|
|
case resolved.Flags&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0:
|
|
return b.expandModuleDecl(resolved)
|
|
default:
|
|
t := b.ch.getWidenedType(b.ch.getTypeOfSymbol(resolved))
|
|
b.ctx.approximateLength += len(name) + 5
|
|
return b.f.NewVariableStatement(
|
|
nil,
|
|
b.f.NewVariableDeclarationList(
|
|
b.f.NewNodeList([]*ast.Node{
|
|
b.f.NewVariableDeclaration(b.f.NewIdentifier(name), nil, b.serializeTypeForDeclaration(nil, t, resolved, true), nil),
|
|
}),
|
|
ast.NodeFlagsLet,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
// expandModuleDecl produces a ModuleDeclaration with exported members.
|
|
func (b *NodeBuilderImpl) expandModuleDecl(symbol *ast.Symbol) *ast.Node {
|
|
exports := b.ch.getExportsOfSymbol(symbol)
|
|
var members []*ast.Symbol
|
|
for _, sym := range exports {
|
|
// Filter to namespace-relevant members
|
|
if !b.isNamespaceMember(sym) {
|
|
continue
|
|
}
|
|
if !scanner.IsIdentifierText(sym.Name, core.LanguageVariantStandard) {
|
|
continue
|
|
}
|
|
members = append(members, sym)
|
|
}
|
|
b.ch.sortSymbols(members)
|
|
b.ctx.approximateLength += 14
|
|
|
|
// Use the same name as symbol display.
|
|
oldFlags := b.ctx.flags
|
|
defer func() { b.ctx.flags = oldFlags }()
|
|
b.ctx.flags |= nodebuilder.FlagsWriteTypeParametersInQualifiedName | nodebuilder.Flags(SymbolFormatFlagsUseOnlyExternalAliasing)
|
|
localName := b.symbolToNode(symbol, ast.SymbolFlagsAll)
|
|
b.ctx.flags = oldFlags
|
|
|
|
type hoverStatement struct {
|
|
node *ast.Node
|
|
isLocal bool // local declarations (e.g. alias targets) should not get export modifier
|
|
}
|
|
var bodyStmts []hoverStatement
|
|
var emittedLocals collections.Set[*ast.Symbol]
|
|
for i := 0; i < len(members); i++ {
|
|
m := members[i]
|
|
if b.checkTruncationLengthIfExpanding() && i+3 < len(members)-1 {
|
|
b.ctx.expansionTruncated = true
|
|
bodyStmts = append(bodyStmts, hoverStatement{node: b.f.NewExpressionStatement(b.f.NewIdentifier(fmt.Sprintf("... (%d more) ...", len(members)-i-1)))})
|
|
i = len(members) - 2 // skip to last member after i++ at end of iteration
|
|
continue
|
|
}
|
|
|
|
// Handle alias/re-export symbols
|
|
if m.Flags&ast.SymbolFlagsAlias != 0 {
|
|
aliasDecl := b.ch.getDeclarationOfAliasSymbol(m)
|
|
target := b.ch.getMergedSymbol(b.ch.getTargetOfAliasDeclaration(aliasDecl))
|
|
if target != nil {
|
|
// If the alias target is a local symbol (not itself an export), emit its declaration first
|
|
if target.Flags&(ast.SymbolFlagsBlockScopedVariable|ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsProperty) != 0 {
|
|
if emittedLocals.AddIfAbsent(target) {
|
|
localType := b.ch.getWidenedType(b.ch.getTypeOfSymbol(target))
|
|
b.ctx.approximateLength += len(target.Name) + 5
|
|
localStmt := b.f.NewVariableStatement(nil,
|
|
b.f.NewVariableDeclarationList(b.f.NewNodeList([]*ast.Node{
|
|
b.f.NewVariableDeclaration(b.f.NewIdentifier(target.Name), nil, b.serializeTypeForDeclaration(nil, localType, target, true), nil),
|
|
}), ast.NodeFlagsLet))
|
|
bodyStmts = append(bodyStmts, hoverStatement{node: localStmt, isLocal: true})
|
|
}
|
|
}
|
|
targetName := target.Name
|
|
b.ctx.approximateLength += 16 + len(m.Name)
|
|
var propertyName *ast.Node
|
|
if m.Name != targetName {
|
|
propertyName = b.f.NewIdentifier(targetName)
|
|
}
|
|
stmt := b.f.NewExportDeclaration(
|
|
nil, false,
|
|
b.f.NewNamedExports(b.f.NewNodeList([]*ast.Node{
|
|
b.f.NewExportSpecifier(false, propertyName, b.f.NewIdentifier(m.Name)),
|
|
})),
|
|
nil, nil,
|
|
)
|
|
bodyStmts = append(bodyStmts, hoverStatement{node: stmt})
|
|
continue
|
|
}
|
|
}
|
|
|
|
resolved := b.ch.resolveSymbol(m)
|
|
|
|
// Handle functions as function declarations
|
|
if resolved.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 {
|
|
t := b.ch.getTypeOfSymbol(resolved)
|
|
sigs := b.ch.getSignaturesOfType(t, SignatureKindCall)
|
|
for _, sig := range sigs {
|
|
b.ctx.approximateLength++
|
|
decl := b.signatureToSignatureDeclarationHelper(sig, ast.KindFunctionDeclaration, &SignatureToSignatureDeclarationOptions{
|
|
name: b.f.NewIdentifier(m.Name),
|
|
})
|
|
bodyStmts = append(bodyStmts, hoverStatement{node: decl})
|
|
}
|
|
// If the function also has namespace characteristics, emit an empty namespace.
|
|
merged := b.ch.getMergedSymbol(resolved)
|
|
hasModuleExports := merged.Flags&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0 && merged.Exports != nil && len(merged.Exports) != 0
|
|
if !hasModuleExports {
|
|
bodyStmts = append(bodyStmts, hoverStatement{node: b.f.NewModuleDeclaration(nil, ast.KindNamespaceKeyword, b.f.NewIdentifier(m.Name), b.f.NewModuleBlock(b.f.NewNodeList(nil)))})
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Handle remaining member kinds (type alias, enum, class, interface, namespace, variable)
|
|
if node := b.serializeNamespaceMember(resolved, m.Name); node != nil {
|
|
bodyStmts = append(bodyStmts, hoverStatement{node: node})
|
|
}
|
|
}
|
|
|
|
// Add export modifier to exported statements (skip local declarations and ExportDeclarations).
|
|
for i := range bodyStmts {
|
|
s := &bodyStmts[i]
|
|
if s.isLocal || ast.IsExportDeclaration(s.node) {
|
|
continue
|
|
}
|
|
if ast.CanHaveModifiers(s.node) {
|
|
mf := s.node.ModifierFlags() | ast.ModifierFlagsExport
|
|
s.node = ast.ReplaceModifiers(b.f, s.node, b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(mf, b.f.NewModifier)))
|
|
}
|
|
}
|
|
|
|
// Collect nodes, stripping export if all statements are exported.
|
|
bodyStatements := make([]*ast.Node, len(bodyStmts))
|
|
for i := range bodyStmts {
|
|
bodyStatements[i] = bodyStmts[i].node
|
|
}
|
|
allExported := len(bodyStatements) > 0 && core.Every(bodyStatements, func(d *ast.Node) bool {
|
|
return ast.HasSyntacticModifier(d, ast.ModifierFlagsExport)
|
|
})
|
|
if allExported {
|
|
for i, stmt := range bodyStatements {
|
|
if ast.CanHaveModifiers(stmt) {
|
|
mf := stmt.ModifierFlags() &^ ast.ModifierFlagsExport
|
|
bodyStatements[i] = ast.ReplaceModifiers(b.f, stmt, b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(mf, b.f.NewModifier)))
|
|
}
|
|
}
|
|
}
|
|
|
|
keyword := ast.KindNamespaceKeyword
|
|
if !ast.IsIdentifier(localName) {
|
|
keyword = ast.KindModuleKeyword
|
|
}
|
|
return b.f.NewModuleDeclaration(nil, keyword, localName, b.f.NewModuleBlock(b.f.NewNodeList(bodyStatements)))
|
|
}
|
|
|
|
// serializeTypeAliasForNamespace produces a TypeAliasDeclaration for a type alias inside a namespace body.
|
|
func (b *NodeBuilderImpl) serializeTypeAliasForNamespace(symbol *ast.Symbol, name string) *ast.Node {
|
|
aliasType := b.ch.getDeclaredTypeOfTypeAlias(symbol)
|
|
typeParams := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
|
typeParamDecls := core.Map(typeParams, func(p *Type) *ast.Node { return b.typeParameterToDeclaration(p) })
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags |= nodebuilder.FlagsInTypeAlias
|
|
typeNode := b.typeToTypeNode(aliasType)
|
|
restoreFlags()
|
|
b.ctx.approximateLength += 8 + len(name)
|
|
return b.f.NewTypeAliasDeclaration(nil, b.f.NewIdentifier(name), b.f.NewNodeList(typeParamDecls), typeNode)
|
|
}
|
|
|
|
// filterInheritedProperties removes properties already present in base types.
|
|
func (b *NodeBuilderImpl) filterInheritedProperties(t *Type, baseTypes []*Type, properties []*ast.Symbol) []*ast.Symbol {
|
|
if len(baseTypes) == 0 {
|
|
return properties
|
|
}
|
|
// Build a lookup from property name to symbol for parent-identity comparison.
|
|
propsByName := make(map[string]*ast.Symbol, len(properties))
|
|
for _, p := range properties {
|
|
propsByName[p.Name] = p
|
|
}
|
|
// Collect names of properties inherited unchanged from base types.
|
|
var inherited collections.Set[string]
|
|
for _, base := range baseTypes {
|
|
baseWithThis := b.ch.getTypeWithThisArgument(base, b.ch.getTargetType(t).AsInterfaceType().thisType, false)
|
|
for _, prop := range b.ch.getPropertiesOfType(baseWithThis) {
|
|
if existing, ok := propsByName[prop.Name]; ok && prop.Parent == existing.Parent {
|
|
inherited.Add(prop.Name)
|
|
}
|
|
}
|
|
}
|
|
if inherited.Len() == 0 {
|
|
return properties
|
|
}
|
|
return core.Filter(properties, func(p *ast.Symbol) bool {
|
|
return !inherited.Has(p.Name)
|
|
})
|
|
}
|
|
|
|
func (b *NodeBuilderImpl) isNamespaceMember(p *ast.Symbol) bool {
|
|
return p.Flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias) != 0 ||
|
|
!(p.Flags&ast.SymbolFlagsPrototype != 0 || p.Name == "prototype" || (p.ValueDeclaration != nil && ast.HasStaticModifier(p.ValueDeclaration) && ast.IsClassLike(p.ValueDeclaration.Parent)))
|
|
}
|
|
|
|
func isHashPrivate(s *ast.Symbol) bool {
|
|
return s.ValueDeclaration != nil && s.ValueDeclaration.Name() != nil && ast.IsPrivateIdentifier(s.ValueDeclaration.Name())
|
|
}
|