Skip to content

Port some more truncation check related code #1373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type NodeFactory struct {
importSpecifierPool core.Pool[ImportSpecifier]
indexedAccessTypeNodePool core.Pool[IndexedAccessTypeNode]
interfaceDeclarationPool core.Pool[InterfaceDeclaration]
intersectionTypeNodePool core.Pool[IntersectionTypeNode]
jsdocDeprecatedTagPool core.Pool[JSDocDeprecatedTag]
jsdocParameterOrPropertyTagPool core.Pool[JSDocParameterOrPropertyTag]
jsdocPool core.Pool[JSDoc]
Expand Down Expand Up @@ -7108,7 +7109,7 @@ func (f *NodeFactory) UpdateIntersectionTypeNode(node *IntersectionTypeNode, typ
}

func (f *NodeFactory) NewIntersectionTypeNode(types *NodeList) *Node {
data := &IntersectionTypeNode{}
data := f.intersectionTypeNodePool.New()
data.Types = types
return f.newNode(KindIntersectionType, data)
}
Expand Down
1 change: 0 additions & 1 deletion internal/checker/nodebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func (b *NodeBuilder) enterContext(enclosingDeclaration *ast.Node, flags nodebui
enclosingDeclaration: enclosingDeclaration,
enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration),
inferTypeParameters: make([]*Type, 0),
visitedTypes: make(map[TypeId]bool),
symbolDepth: make(map[CompositeSymbolIdentity]int),
trackedSymbols: make([]*TrackedSymbolArgs, 0),
reverseMappedStack: make([]*ast.Symbol, 0),
Expand Down
152 changes: 131 additions & 21 deletions internal/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"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/module"
Expand Down Expand Up @@ -63,7 +64,7 @@ type NodeBuilderContext struct {
enclosingDeclaration *ast.Node
enclosingFile *ast.SourceFile
inferTypeParameters []*Type
visitedTypes map[TypeId]bool
visitedTypes collections.Set[TypeId]
symbolDepth map[CompositeSymbolIdentity]int
trackedSymbols []*TrackedSymbolArgs
mapper *TypeMapper
Expand Down Expand Up @@ -212,12 +213,121 @@ func (b *nodeBuilderImpl) createElidedInformationPlaceholder() *ast.TypeNode {
return b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
}

func (b *nodeBuilderImpl) mapToTypeNodes(list []*Type) *ast.NodeList {
func (b *nodeBuilderImpl) mapToTypeNodes(list []*Type, isBareList bool) *ast.NodeList {
if len(list) == 0 {
return nil
}
contents := core.Map(list, b.typeToTypeNode)
return b.f.NewNodeList(contents)

if b.checkTruncationLength() {
if !isBareList {
var node *ast.Node
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
// addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length} elided ...`)
node = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
} else {
node = b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/)
}
return b.f.NewNodeList([]*ast.Node{node})
} else if len(list) > 2 {
nodes := []*ast.Node{
b.typeToTypeNode(list[0]),
nil,
b.typeToTypeNode(list[len(list)-1]),
}

if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
// addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length - 2} more elided ...`)
nodes[1] = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
} else {
text := fmt.Sprintf("... %d more ...", len(list)-2)
nodes[1] = b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/)
}
return b.f.NewNodeList(nodes)
}
}

mayHaveNameCollisions := b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType == 0
type seenName struct {
t *Type
i int
}
var seenNames *collections.MultiMap[string, seenName]
if mayHaveNameCollisions {
seenNames = &collections.MultiMap[string, seenName]{}
}

result := make([]*ast.Node, 0, len(list))

for i, t := range list {
if b.checkTruncationLength() && (i+2 < len(list)-1) {
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
// addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length} elided ...`)
result = append(result, b.f.NewKeywordTypeNode(ast.KindAnyKeyword))
} else {
text := fmt.Sprintf("... %d more ...", len(list)-i)
result = append(result, b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/))
}
typeNode := b.typeToTypeNode(list[len(list)-1])
if typeNode != nil {
result = append(result, typeNode)
}
break
}
b.ctx.approximateLength += 2 // Account for whitespace + separator
typeNode := b.typeToTypeNode(t)
if typeNode != nil {
result = append(result, typeNode)
if seenNames != nil && isIdentifierTypeReference(typeNode) {
seenNames.Add(typeNode.AsTypeReferenceNode().TypeName.Text(), seenName{t, len(result) - 1})
}
}
}

if seenNames != nil {
// To avoid printing types like `[Foo, Foo]` or `Bar & Bar` where
// occurrences of the same name actually come from different
// namespaces, go through the single-identifier type reference nodes
// we just generated, and see if any names were generated more than
// once while referring to different types. If so, regenerate the
// type node for each entry by that name with the
// `UseFullyQualifiedType` flag enabled.
restoreFlags := b.saveRestoreFlags()
b.ctx.flags |= nodebuilder.FlagsUseFullyQualifiedType
for types := range seenNames.Values() {
if !arrayIsHomogeneous(types, func(a, b seenName) bool {
return typesAreSameReference(a.t, b.t)
}) {
for _, seen := range types {
result[seen.i] = b.typeToTypeNode(seen.t)
}
}
}
restoreFlags()
}

return b.f.NewNodeList(result)
}

func isIdentifierTypeReference(node *ast.Node) bool {
return ast.IsTypeReferenceNode(node) && ast.IsIdentifier(node.AsTypeReferenceNode().TypeName)
}

func arrayIsHomogeneous[T any](array []T, comparer func(a, B T) bool) bool {
if len(array) < 2 {
return true
}
first := array[0]
for i := 1; i < len(array); i++ {
target := array[i]
if !comparer(first, target) {
return false
}
}
return true
}

func typesAreSameReference(a, b *Type) bool {
return a == b || a.symbol != nil && a.symbol == b.symbol || a.alias != nil && a.alias == b.alias
}

func (b *nodeBuilderImpl) setCommentRange(node *ast.Node, range_ *ast.Node) {
Expand Down Expand Up @@ -819,7 +929,7 @@ func (b *nodeBuilderImpl) lookupTypeParameterNodes(chain []*ast.Symbol, index in
if targetMapper != nil {
params = core.Map(params, targetMapper.Map)
}
return b.mapToTypeNodes(params)
return b.mapToTypeNodes(params, false /*isBareList*/)
} else {
typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol)
if len(typeParameterNodes) > 0 {
Expand Down Expand Up @@ -1314,6 +1424,7 @@ func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode {
templateTypeNode,
nil,
)
b.ctx.approximateLength += 10
b.e.AddEmitFlags(result, printer.EFSingleLine)

if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) {
Expand Down Expand Up @@ -2301,7 +2412,7 @@ func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol {

func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) bool {
isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool {
return ast.IsStatic(declaration)
return ast.IsStatic(declaration) && !b.ch.isLateBindableIndexSignature(ast.GetNameOfDeclaration(declaration))
})
isNonLocalFunctionSymbol := false
if symbol.Flags&ast.SymbolFlagsFunction != 0 {
Expand All @@ -2318,9 +2429,8 @@ func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, ty
}
if isStaticMethodSymbol || isNonLocalFunctionSymbol {
// typeof is allowed only for static/non local functions
_, visited := b.ctx.visitedTypes[typeId]
return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || visited) && (b.ctx.flags&nodebuilder.FlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration))
// And the build is going to succeed without visibility error or there is no structural fallback allowed
return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || b.ctx.visitedTypes.Has(typeId)) && // it is type of the symbol uses itself recursively
(b.ctx.flags&nodebuilder.FlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration)) // And the build is going to succeed without visibility error or there is no structural fallback allowed
}
return false
}
Expand All @@ -2339,7 +2449,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
return typeNode
}
}
if _, ok := b.ctx.visitedTypes[typeId]; ok {
if b.ctx.visitedTypes.Has(typeId) {
return b.createElidedInformationPlaceholder()
}
return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType)
Expand All @@ -2358,7 +2468,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
// } else
if symbol.Flags&ast.SymbolFlagsClass != 0 && b.ch.getBaseTypeVariableOfClass(symbol) == nil && !(symbol.ValueDeclaration != nil && ast.IsClassLike(symbol.ValueDeclaration) && b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && (!ast.IsClassDeclaration(symbol.ValueDeclaration) || b.ch.IsSymbolAccessible(symbol, b.ctx.enclosingDeclaration, isInstanceType, false /*shouldComputeAliasesToMakeVisible*/).Accessibility != printer.SymbolAccessibilityAccessible)) || symbol.Flags&(ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 || b.shouldWriteTypeOfFunctionSymbol(symbol, typeId) {
return b.symbolToTypeNode(symbol, isInstanceType, nil)
} else if _, ok := b.ctx.visitedTypes[typeId]; ok {
} else if b.ctx.visitedTypes.Has(typeId) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
typeAlias := getTypeAliasForTypeLiteral(b.ch, t)
if typeAlias != nil {
Expand Down Expand Up @@ -2392,7 +2502,7 @@ func (b *nodeBuilderImpl) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes

func (b *nodeBuilderImpl) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode {
if t.flags&TypeFlagsUnion != 0 {
if _, ok := b.ctx.visitedTypes[t.id]; ok {
if b.ctx.visitedTypes.Has(t.id) {
if b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 {
b.ctx.encounteredError = true
b.ctx.tracker.ReportCyclicStructureError()
Expand Down Expand Up @@ -2486,7 +2596,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
})
if len(typeArguments) > 0 {
arity := b.ch.getTypeReferenceArity(t)
tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity])
tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity], false /*isBareList*/)
if tupleConstituentNodes != nil {
for i := 0; i < len(tupleConstituentNodes.Nodes); i++ {
flags := t.Target().AsTupleType().elementInfos[i].flags
Expand Down Expand Up @@ -2543,7 +2653,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
// the default outer type arguments), we don't show the group.

if !slices.Equal(outerTypeParameters[start:i], typeArguments[start:i]) {
typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i])
typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i], false /*isBareList*/)
restoreFlags := b.saveRestoreFlags()
b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences
ref := b.symbolToTypeNode(parent, ast.SymbolFlagsType, typeArgumentSlice)
Expand Down Expand Up @@ -2582,7 +2692,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
}
}

typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount])
typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount], false /*isBareList*/)
}
restoreFlags := b.saveRestoreFlags()
b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences
Expand Down Expand Up @@ -2614,7 +2724,7 @@ 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.links.Has(b.ctx.enclosingDeclaration) {
if 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 All @@ -2638,7 +2748,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
}
b.ctx.symbolDepth[*id] = depth + 1
}
b.ctx.visitedTypes[typeId] = true
b.ctx.visitedTypes.Add(typeId)
prevTrackedSymbols := b.ctx.trackedSymbols
b.ctx.trackedSymbols = nil
startLength := b.ctx.approximateLength
Expand All @@ -2656,7 +2766,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
trackedSymbols: b.ctx.trackedSymbols,
}
}
delete(b.ctx.visitedTypes, typeId)
b.ctx.visitedTypes.Delete(typeId)
if id != nil {
b.ctx.symbolDepth[*id] = depth
}
Expand Down Expand Up @@ -2828,7 +2938,7 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {

if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) {
sym := t.alias.Symbol()
typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments())
typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments(), false /*isBareList*/)
if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 {
return b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), typeArgumentNodes)
}
Expand Down Expand Up @@ -2896,7 +3006,7 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
if len(types) == 1 {
return b.typeToTypeNode(types[0])
}
typeNodes := b.mapToTypeNodes(types)
typeNodes := b.mapToTypeNodes(types, true /*isBareList*/)
if typeNodes != nil && len(typeNodes.Nodes) > 0 {
if t.flags&TypeFlagsUnion != 0 {
return b.f.NewUnionTypeNode(typeNodes)
Expand Down Expand Up @@ -2970,5 +3080,5 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
// Direct serialization core functions for types, type aliases, and symbols

func (t *TypeAlias) ToTypeReferenceNode(b *nodeBuilderImpl) *ast.Node {
return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments()))
return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments(), false /*isBareList*/))
}
6 changes: 5 additions & 1 deletion internal/checker/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,11 @@ func getTrailingSemicolonDeferringWriter(writer printer.EmitTextWriter) printer.
}

func (c *Checker) TypeToString(t *Type) string {
return c.typeToStringEx(t, nil, TypeFormatFlagsAllowUniqueESSymbolType|TypeFormatFlagsUseAliasDefinedOutsideCurrentScope)
return c.typeToString(t, nil)
}

func (c *Checker) typeToString(t *Type, enclosingDeclaration *ast.Node) string {
return c.typeToStringEx(t, enclosingDeclaration, TypeFormatFlagsAllowUniqueESSymbolType|TypeFormatFlagsUseAliasDefinedOutsideCurrentScope)
}

func toNodeBuilderFlags(flags TypeFormatFlags) nodebuilder.Flags {
Expand Down
4 changes: 2 additions & 2 deletions internal/checker/relater.go
Original file line number Diff line number Diff line change
Expand Up @@ -1270,13 +1270,13 @@ func isNonPrimitiveType(t *Type) bool {
func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, string) {
var leftStr string
if c.symbolValueDeclarationIsContextSensitive(left.symbol) {
leftStr = c.typeToStringEx(left, left.symbol.ValueDeclaration, TypeFormatFlagsNone)
leftStr = c.typeToString(left, left.symbol.ValueDeclaration)
} else {
leftStr = c.TypeToString(left)
}
var rightStr string
if c.symbolValueDeclarationIsContextSensitive(right.symbol) {
rightStr = c.typeToStringEx(right, right.symbol.ValueDeclaration, TypeFormatFlagsNone)
rightStr = c.typeToString(right, right.symbol.ValueDeclaration)
} else {
rightStr = c.TypeToString(right)
}
Expand Down
4 changes: 4 additions & 0 deletions internal/collections/multimap.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func (s *MultiMap[K, V]) Keys() iter.Seq[K] {
return maps.Keys(s.M)
}

func (s *MultiMap[K, V]) Values() iter.Seq[[]V] {
return maps.Values(s.M)
}

func (s *MultiMap[K, V]) Clear() {
clear(s.M)
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ declare class Foo {
static p1: number;
p1: number;
}
export declare const t1: (typeof Foo)[typeof k1];
export declare const t1: () => number;
export declare const t2: () => string;
export declare const t3: typeof Foo.foo;
export declare const t3: () => number;
export declare const t4: () => string;
export declare const t5: typeof Foo.m1;
export declare const t6: () => void;
Expand Down

This file was deleted.

Loading