Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions pkg/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2281,16 +2281,33 @@ func (b *NodeBuilderImpl) trackComputedName(accessExpression *ast.Node, enclosin
}
}

type propertyNameNodeKind int

const (
propertyNameNodeKindIdentifier propertyNameNodeKind = iota
propertyNameNodeKindNumericLiteral
propertyNameNodeKindStringLiteral
)

func classifyPropertyName(name string, stringNamed bool, isMethod bool) propertyNameNodeKind {
if isMethod && name == "new" {
return propertyNameNodeKindStringLiteral
}
if scanner.IsIdentifierText(name, core.LanguageVariantStandard) {
return propertyNameNodeKindIdentifier
}
return core.IfElse(!stringNamed && isNumericLiteralName(name) && jsnum.FromString(name) >= 0, propertyNameNodeKindNumericLiteral, propertyNameNodeKindStringLiteral)
}

func (b *NodeBuilderImpl) createPropertyNameNodeForIdentifierOrLiteral(name string, singleQuote bool, stringNamed bool, isMethod bool, symbol *ast.Symbol) *ast.Node {
isMethodNamedNew := isMethod && name == "new"
if !isMethodNamedNew && scanner.IsIdentifierText(name, core.LanguageVariantStandard) {
switch classifyPropertyName(name, stringNamed, isMethod) {
case propertyNameNodeKindIdentifier:
return b.newIdentifier(name, symbol)
}
if !stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 {
case propertyNameNodeKindNumericLiteral:
return b.f.NewNumericLiteral(name, ast.TokenFlagsNone)
default:
return b.f.NewStringLiteral(name, core.IfElse(singleQuote, ast.TokenFlagsSingleQuote, ast.TokenFlagsNone))
}
result := b.f.NewStringLiteral(name, core.IfElse(singleQuote, ast.TokenFlagsSingleQuote, ast.TokenFlagsNone))
return result
}

func (b *NodeBuilderImpl) isStringNamed(d *ast.Declaration) bool {
Expand Down Expand Up @@ -3000,7 +3017,9 @@ func (b *NodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *NodeB
// of types allows us to catch circular references to instantiations of the same anonymous type

key := CompositeTypeCacheIdentity{typeId, b.ctx.flags, b.ctx.internalFlags}
if b.ctx.maxExpansionDepth <= 0 && b.ctx.enclosingDeclaration != nil && b.links.Has(b.ctx.enclosingDeclaration) {
// Don't rely on type cache if we're expanding a type, because we need to compute `canIncreaseExpansionDepth`.
canUseCache := b.ctx.maxExpansionDepth < 0
if canUseCache && b.ctx.enclosingDeclaration != nil && b.links.Has(b.ctx.enclosingDeclaration) {
links := b.links.Get(b.ctx.enclosingDeclaration)
cachedResult, ok := links.serializedTypes[key]
if ok {
Expand Down Expand Up @@ -3030,7 +3049,7 @@ func (b *NodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *NodeB
startLength := b.ctx.approximateLength
result := transform(b, t)
addedLength := b.ctx.approximateLength - startLength
if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError {
if canUseCache && !b.ctx.reportedDiagnostic && !b.ctx.encounteredError {
links := b.links.Get(b.ctx.enclosingDeclaration)
if links.serializedTypes == nil {
links.serializedTypes = make(map[CompositeTypeCacheIdentity]*SerializedTypeEntry)
Expand Down
48 changes: 24 additions & 24 deletions pkg/checker/nodecopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/buke/typescript-go-internal/pkg/core"
"github.com/buke/typescript-go-internal/pkg/nodebuilder"
"github.com/buke/typescript-go-internal/pkg/printer"
"github.com/buke/typescript-go-internal/pkg/scanner"
)

func (b *NodeBuilderImpl) reuseNode(node *ast.Node) *ast.Node {
Expand All @@ -22,35 +21,36 @@ func (b *NodeBuilderImpl) tryJSTypeNodeToTypeNode(node *ast.Node) *ast.Node {
return b.reuseNode(node)
}

// a wrapper around `reuseNode` for property names. It handles renaming `new` to `"new"` so we don't
// accidentally emit constructor signatures when we don't mean to, and normalizes string-literal
// property names whose text is a valid identifier into identifiers, matching the behavior of
// `createPropertyNameNodeForIdentifierOrLiteral` used when constructing fresh property name nodes
// (so that reused names emit consistently regardless of whether their containing type was reused
// from source or rebuilt from a type).
func (b *NodeBuilderImpl) reuseName(node *ast.Node) *ast.Node {
func (b *NodeBuilderImpl) reuseName(node *ast.Node, isMethod bool) *ast.Node {
res := b.reuseNode(node)
if res == nil {
return res
}
if res.Kind == ast.KindIdentifier && node.AsIdentifier().Text == "new" {
str := b.f.NewStringLiteral("new", ast.TokenFlagsNone)
b.e.SetOriginal(str, res)
return b.setTextRange(str, res)

text, ok := ast.TryGetTextOfPropertyName(res)
if !ok {
return res
}
if res.Kind == ast.KindStringLiteral {
text := res.AsStringLiteral().Text
// Skip normalization for "new" so that reused names like `"new"(): void` on a
// method signature are not converted to an identifier (which would become a
// construct signature). This mirrors the `isMethodNamedNew` guard in
// createPropertyNameNodeForIdentifierOrLiteral.
if text != "new" && scanner.IsIdentifierText(text, core.LanguageVariantStandard) {
ident := b.newIdentifier(text, nil)
b.e.SetOriginal(ident, res)
return b.setTextRange(ident, res)
}

kind := classifyPropertyName(text, ast.IsStringLiteral(res), isMethod)
if ast.IsIdentifier(res) && kind == propertyNameNodeKindIdentifier {
return res
}
return res
if ast.IsStringLiteral(res) && kind == propertyNameNodeKindStringLiteral {
return res
}

var renamed *ast.Node
switch kind {
case propertyNameNodeKindIdentifier:
renamed = b.newIdentifier(text, nil)
case propertyNameNodeKindStringLiteral:
renamed = b.f.NewStringLiteral(text, ast.TokenFlagsNone)
default:
return res
}
b.e.SetOriginal(renamed, res)
return b.setTextRange(renamed, res)
}

func (b *NodeBuilderImpl) reuseTypeNode(node *ast.Node) *ast.Node {
Expand Down
24 changes: 18 additions & 6 deletions pkg/checker/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,11 @@ func (c *Checker) typeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags
combinedFlags = combinedFlags | nodebuilder.FlagsNoTruncation
}
nodeBuilder := c.getNodeBuilder()
if vc != nil {
nodeBuilder.verbosity = vc
}
oldVerbosity := nodeBuilder.verbosity
nodeBuilder.verbosity = vc
defer func() {
nodeBuilder.verbosity = oldVerbosity
}()
typeNode := nodeBuilder.TypeToTypeNode(t, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil)
if typeNode == nil {
panic("should always get typenode")
Expand Down Expand Up @@ -320,9 +322,11 @@ func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration
}

nodeBuilder := c.getNodeBuilder()
if vc != nil {
nodeBuilder.verbosity = vc
}
oldVerbosity := nodeBuilder.verbosity
nodeBuilder.verbosity = vc
defer func() {
nodeBuilder.verbosity = oldVerbosity
}()
combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName
sig := nodeBuilder.SignatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil)
p := createPrinterWithRemoveCommentsOmitTrailingSemicolonNeverAsciiEscape(nodeBuilder.EmitContext())
Expand Down Expand Up @@ -412,7 +416,11 @@ func (c *Checker) SignatureToSignatureDeclaration(signature *Signature, kind ast
// ExpandSymbolForHover produces declaration strings for a symbol with verbosity support for expandable hover.
func (c *Checker) ExpandSymbolForHover(symbol *ast.Symbol, meaning ast.SymbolFlags, vc *VerbosityContext) string {
nodeBuilder := c.getNodeBuilder()
oldVerbosity := nodeBuilder.verbosity
nodeBuilder.verbosity = vc
defer func() {
nodeBuilder.verbosity = oldVerbosity
}()
nodes := nodeBuilder.ExpandSymbolForHover(symbol, meaning)
if len(nodes) == 0 {
return ""
Expand All @@ -435,7 +443,11 @@ func (c *Checker) ExpandSymbolForHover(symbol *ast.Symbol, meaning ast.SymbolFla
// TypeParameterToStringEx renders a type parameter declaration (e.g. "T extends Foo") with optional verbosity support.
func (c *Checker) TypeParameterToStringEx(t *Type, enclosingDeclaration *ast.Node, vc *VerbosityContext) string {
nodeBuilder := c.getNodeBuilder()
oldVerbosity := nodeBuilder.verbosity
nodeBuilder.verbosity = vc
defer func() {
nodeBuilder.verbosity = oldVerbosity
}()
typeParamNode := nodeBuilder.TypeParameterToDeclaration(t, enclosingDeclaration, nodebuilder.FlagsIgnoreErrors, nodebuilder.InternalFlagsNone, nil)
if typeParamNode == nil {
return c.TypeToString(t)
Expand Down
10 changes: 5 additions & 5 deletions pkg/checker/pseudotypenodebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
if isConst {
newProp = b.f.NewPropertySignatureDeclaration(
modifiers,
b.reuseName(e.Name),
b.reuseName(e.Name, false /*isMethod*/),
nil,
b.f.NewFunctionTypeNode(
typeParams,
Expand All @@ -237,7 +237,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
}
newProp = b.f.NewMethodSignatureDeclaration(
modifiers,
b.reuseName(e.Name),
b.reuseName(e.Name, true /*isMethod*/),
nil,
typeParams,
b.pseudoParametersToNodeList(d.Parameters),
Expand All @@ -247,7 +247,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
d := e.AsPseudoPropertyAssignment()
newProp = b.f.NewPropertySignatureDeclaration(
modifiers,
b.reuseName(e.Name),
b.reuseName(e.Name, false /*isMethod*/),
nil,
b.pseudoTypeToNode(d.Type),
nil,
Expand All @@ -256,7 +256,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
d := e.AsPseudoSetAccessor()
newProp = b.f.NewSetAccessorDeclaration(
nil,
b.reuseName(e.Name),
b.reuseName(e.Name, false /*isMethod*/),
nil,
b.f.NewNodeList([]*ast.Node{b.pseudoParameterToNode(d.Parameter)}),
nil,
Expand All @@ -267,7 +267,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
d := e.AsPseudoGetAccessor()
newProp = b.f.NewGetAccessorDeclaration(
nil,
b.reuseName(e.Name),
b.reuseName(e.Name, false /*isMethod*/),
nil,
nil,
b.pseudoTypeToNode(d.Type),
Expand Down
Loading