Skip to content

Commit 19c1578

Browse files
authored
Port some more truncation check related code (#1373)
1 parent 23aad64 commit 19c1578

12 files changed

+168
-88
lines changed

internal/ast/ast.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type NodeFactory struct {
6767
importSpecifierPool core.Pool[ImportSpecifier]
6868
indexedAccessTypeNodePool core.Pool[IndexedAccessTypeNode]
6969
interfaceDeclarationPool core.Pool[InterfaceDeclaration]
70+
intersectionTypeNodePool core.Pool[IntersectionTypeNode]
7071
jsdocDeprecatedTagPool core.Pool[JSDocDeprecatedTag]
7172
jsdocParameterOrPropertyTagPool core.Pool[JSDocParameterOrPropertyTag]
7273
jsdocPool core.Pool[JSDoc]
@@ -7110,7 +7111,7 @@ func (f *NodeFactory) UpdateIntersectionTypeNode(node *IntersectionTypeNode, typ
71107111
}
71117112

71127113
func (f *NodeFactory) NewIntersectionTypeNode(types *NodeList) *Node {
7113-
data := &IntersectionTypeNode{}
7114+
data := f.intersectionTypeNodePool.New()
71147115
data.Types = types
71157116
return f.newNode(KindIntersectionType, data)
71167117
}

internal/checker/nodebuilder.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ func (b *NodeBuilder) enterContext(enclosingDeclaration *ast.Node, flags nodebui
2626
enclosingDeclaration: enclosingDeclaration,
2727
enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration),
2828
inferTypeParameters: make([]*Type, 0),
29-
visitedTypes: make(map[TypeId]bool),
3029
symbolDepth: make(map[CompositeSymbolIdentity]int),
3130
trackedSymbols: make([]*TrackedSymbolArgs, 0),
3231
reverseMappedStack: make([]*ast.Symbol, 0),

internal/checker/nodebuilderimpl.go

Lines changed: 131 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/microsoft/typescript-go/internal/ast"
10+
"github.com/microsoft/typescript-go/internal/collections"
1011
"github.com/microsoft/typescript-go/internal/core"
1112
"github.com/microsoft/typescript-go/internal/jsnum"
1213
"github.com/microsoft/typescript-go/internal/module"
@@ -63,7 +64,7 @@ type NodeBuilderContext struct {
6364
enclosingDeclaration *ast.Node
6465
enclosingFile *ast.SourceFile
6566
inferTypeParameters []*Type
66-
visitedTypes map[TypeId]bool
67+
visitedTypes collections.Set[TypeId]
6768
symbolDepth map[CompositeSymbolIdentity]int
6869
trackedSymbols []*TrackedSymbolArgs
6970
mapper *TypeMapper
@@ -212,12 +213,121 @@ func (b *nodeBuilderImpl) createElidedInformationPlaceholder() *ast.TypeNode {
212213
return b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
213214
}
214215

215-
func (b *nodeBuilderImpl) mapToTypeNodes(list []*Type) *ast.NodeList {
216+
func (b *nodeBuilderImpl) mapToTypeNodes(list []*Type, isBareList bool) *ast.NodeList {
216217
if len(list) == 0 {
217218
return nil
218219
}
219-
contents := core.Map(list, b.typeToTypeNode)
220-
return b.f.NewNodeList(contents)
220+
221+
if b.checkTruncationLength() {
222+
if !isBareList {
223+
var node *ast.Node
224+
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
225+
// addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length} elided ...`)
226+
node = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
227+
} else {
228+
node = b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/)
229+
}
230+
return b.f.NewNodeList([]*ast.Node{node})
231+
} else if len(list) > 2 {
232+
nodes := []*ast.Node{
233+
b.typeToTypeNode(list[0]),
234+
nil,
235+
b.typeToTypeNode(list[len(list)-1]),
236+
}
237+
238+
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
239+
// addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length - 2} more elided ...`)
240+
nodes[1] = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
241+
} else {
242+
text := fmt.Sprintf("... %d more ...", len(list)-2)
243+
nodes[1] = b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/)
244+
}
245+
return b.f.NewNodeList(nodes)
246+
}
247+
}
248+
249+
mayHaveNameCollisions := b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType == 0
250+
type seenName struct {
251+
t *Type
252+
i int
253+
}
254+
var seenNames *collections.MultiMap[string, seenName]
255+
if mayHaveNameCollisions {
256+
seenNames = &collections.MultiMap[string, seenName]{}
257+
}
258+
259+
result := make([]*ast.Node, 0, len(list))
260+
261+
for i, t := range list {
262+
if b.checkTruncationLength() && (i+2 < len(list)-1) {
263+
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
264+
// addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length} elided ...`)
265+
result = append(result, b.f.NewKeywordTypeNode(ast.KindAnyKeyword))
266+
} else {
267+
text := fmt.Sprintf("... %d more ...", len(list)-i)
268+
result = append(result, b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/))
269+
}
270+
typeNode := b.typeToTypeNode(list[len(list)-1])
271+
if typeNode != nil {
272+
result = append(result, typeNode)
273+
}
274+
break
275+
}
276+
b.ctx.approximateLength += 2 // Account for whitespace + separator
277+
typeNode := b.typeToTypeNode(t)
278+
if typeNode != nil {
279+
result = append(result, typeNode)
280+
if seenNames != nil && isIdentifierTypeReference(typeNode) {
281+
seenNames.Add(typeNode.AsTypeReferenceNode().TypeName.Text(), seenName{t, len(result) - 1})
282+
}
283+
}
284+
}
285+
286+
if seenNames != nil {
287+
// To avoid printing types like `[Foo, Foo]` or `Bar & Bar` where
288+
// occurrences of the same name actually come from different
289+
// namespaces, go through the single-identifier type reference nodes
290+
// we just generated, and see if any names were generated more than
291+
// once while referring to different types. If so, regenerate the
292+
// type node for each entry by that name with the
293+
// `UseFullyQualifiedType` flag enabled.
294+
restoreFlags := b.saveRestoreFlags()
295+
b.ctx.flags |= nodebuilder.FlagsUseFullyQualifiedType
296+
for types := range seenNames.Values() {
297+
if !arrayIsHomogeneous(types, func(a, b seenName) bool {
298+
return typesAreSameReference(a.t, b.t)
299+
}) {
300+
for _, seen := range types {
301+
result[seen.i] = b.typeToTypeNode(seen.t)
302+
}
303+
}
304+
}
305+
restoreFlags()
306+
}
307+
308+
return b.f.NewNodeList(result)
309+
}
310+
311+
func isIdentifierTypeReference(node *ast.Node) bool {
312+
return ast.IsTypeReferenceNode(node) && ast.IsIdentifier(node.AsTypeReferenceNode().TypeName)
313+
}
314+
315+
func arrayIsHomogeneous[T any](array []T, comparer func(a, B T) bool) bool {
316+
if len(array) < 2 {
317+
return true
318+
}
319+
first := array[0]
320+
for i := 1; i < len(array); i++ {
321+
target := array[i]
322+
if !comparer(first, target) {
323+
return false
324+
}
325+
}
326+
return true
327+
}
328+
329+
func typesAreSameReference(a, b *Type) bool {
330+
return a == b || a.symbol != nil && a.symbol == b.symbol || a.alias != nil && a.alias == b.alias
221331
}
222332

223333
func (b *nodeBuilderImpl) setCommentRange(node *ast.Node, range_ *ast.Node) {
@@ -819,7 +929,7 @@ func (b *nodeBuilderImpl) lookupTypeParameterNodes(chain []*ast.Symbol, index in
819929
if targetMapper != nil {
820930
params = core.Map(params, targetMapper.Map)
821931
}
822-
return b.mapToTypeNodes(params)
932+
return b.mapToTypeNodes(params, false /*isBareList*/)
823933
} else {
824934
typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol)
825935
if len(typeParameterNodes) > 0 {
@@ -1314,6 +1424,7 @@ func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode {
13141424
templateTypeNode,
13151425
nil,
13161426
)
1427+
b.ctx.approximateLength += 10
13171428
b.e.AddEmitFlags(result, printer.EFSingleLine)
13181429

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

23022413
func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) bool {
23032414
isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool {
2304-
return ast.IsStatic(declaration)
2415+
return ast.IsStatic(declaration) && !b.ch.isLateBindableIndexSignature(ast.GetNameOfDeclaration(declaration))
23052416
})
23062417
isNonLocalFunctionSymbol := false
23072418
if symbol.Flags&ast.SymbolFlagsFunction != 0 {
@@ -2318,9 +2429,8 @@ func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, ty
23182429
}
23192430
if isStaticMethodSymbol || isNonLocalFunctionSymbol {
23202431
// typeof is allowed only for static/non local functions
2321-
_, visited := b.ctx.visitedTypes[typeId]
2322-
return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || visited) && (b.ctx.flags&nodebuilder.FlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration))
2323-
// And the build is going to succeed without visibility error or there is no structural fallback allowed
2432+
return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || b.ctx.visitedTypes.Has(typeId)) && // it is type of the symbol uses itself recursively
2433+
(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
23242434
}
23252435
return false
23262436
}
@@ -2339,7 +2449,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
23392449
return typeNode
23402450
}
23412451
}
2342-
if _, ok := b.ctx.visitedTypes[typeId]; ok {
2452+
if b.ctx.visitedTypes.Has(typeId) {
23432453
return b.createElidedInformationPlaceholder()
23442454
}
23452455
return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType)
@@ -2358,7 +2468,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
23582468
// } else
23592469
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) {
23602470
return b.symbolToTypeNode(symbol, isInstanceType, nil)
2361-
} else if _, ok := b.ctx.visitedTypes[typeId]; ok {
2471+
} else if b.ctx.visitedTypes.Has(typeId) {
23622472
// If type is an anonymous type literal in a type alias declaration, use type alias name
23632473
typeAlias := getTypeAliasForTypeLiteral(b.ch, t)
23642474
if typeAlias != nil {
@@ -2392,7 +2502,7 @@ func (b *nodeBuilderImpl) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes
23922502

23932503
func (b *nodeBuilderImpl) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode {
23942504
if t.flags&TypeFlagsUnion != 0 {
2395-
if _, ok := b.ctx.visitedTypes[t.id]; ok {
2505+
if b.ctx.visitedTypes.Has(t.id) {
23962506
if b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 {
23972507
b.ctx.encounteredError = true
23982508
b.ctx.tracker.ReportCyclicStructureError()
@@ -2486,7 +2596,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
24862596
})
24872597
if len(typeArguments) > 0 {
24882598
arity := b.ch.getTypeReferenceArity(t)
2489-
tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity])
2599+
tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity], false /*isBareList*/)
24902600
if tupleConstituentNodes != nil {
24912601
for i := 0; i < len(tupleConstituentNodes.Nodes); i++ {
24922602
flags := t.Target().AsTupleType().elementInfos[i].flags
@@ -2543,7 +2653,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
25432653
// the default outer type arguments), we don't show the group.
25442654

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

2585-
typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount])
2695+
typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount], false /*isBareList*/)
25862696
}
25872697
restoreFlags := b.saveRestoreFlags()
25882698
b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences
@@ -2614,7 +2724,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
26142724
// of types allows us to catch circular references to instantiations of the same anonymous type
26152725

26162726
key := CompositeTypeCacheIdentity{typeId, b.ctx.flags, b.ctx.internalFlags}
2617-
if b.links.Has(b.ctx.enclosingDeclaration) {
2727+
if b.ctx.enclosingDeclaration != nil && b.links.Has(b.ctx.enclosingDeclaration) {
26182728
links := b.links.Get(b.ctx.enclosingDeclaration)
26192729
cachedResult, ok := links.serializedTypes[key]
26202730
if ok {
@@ -2638,7 +2748,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
26382748
}
26392749
b.ctx.symbolDepth[*id] = depth + 1
26402750
}
2641-
b.ctx.visitedTypes[typeId] = true
2751+
b.ctx.visitedTypes.Add(typeId)
26422752
prevTrackedSymbols := b.ctx.trackedSymbols
26432753
b.ctx.trackedSymbols = nil
26442754
startLength := b.ctx.approximateLength
@@ -2656,7 +2766,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
26562766
trackedSymbols: b.ctx.trackedSymbols,
26572767
}
26582768
}
2659-
delete(b.ctx.visitedTypes, typeId)
2769+
b.ctx.visitedTypes.Delete(typeId)
26602770
if id != nil {
26612771
b.ctx.symbolDepth[*id] = depth
26622772
}
@@ -2828,7 +2938,7 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
28282938

28292939
if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) {
28302940
sym := t.alias.Symbol()
2831-
typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments())
2941+
typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments(), false /*isBareList*/)
28322942
if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 {
28332943
return b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), typeArgumentNodes)
28342944
}
@@ -2896,7 +3006,7 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
28963006
if len(types) == 1 {
28973007
return b.typeToTypeNode(types[0])
28983008
}
2899-
typeNodes := b.mapToTypeNodes(types)
3009+
typeNodes := b.mapToTypeNodes(types, true /*isBareList*/)
29003010
if typeNodes != nil && len(typeNodes.Nodes) > 0 {
29013011
if t.flags&TypeFlagsUnion != 0 {
29023012
return b.f.NewUnionTypeNode(typeNodes)
@@ -2970,5 +3080,5 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
29703080
// Direct serialization core functions for types, type aliases, and symbols
29713081

29723082
func (t *TypeAlias) ToTypeReferenceNode(b *nodeBuilderImpl) *ast.Node {
2973-
return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments()))
3083+
return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments(), false /*isBareList*/))
29743084
}

internal/checker/printer.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@ func getTrailingSemicolonDeferringWriter(writer printer.EmitTextWriter) printer.
164164
}
165165

166166
func (c *Checker) TypeToString(t *Type) string {
167-
return c.typeToStringEx(t, nil, TypeFormatFlagsAllowUniqueESSymbolType|TypeFormatFlagsUseAliasDefinedOutsideCurrentScope)
167+
return c.typeToString(t, nil)
168+
}
169+
170+
func (c *Checker) typeToString(t *Type, enclosingDeclaration *ast.Node) string {
171+
return c.typeToStringEx(t, enclosingDeclaration, TypeFormatFlagsAllowUniqueESSymbolType|TypeFormatFlagsUseAliasDefinedOutsideCurrentScope)
168172
}
169173

170174
func toNodeBuilderFlags(flags TypeFormatFlags) nodebuilder.Flags {

internal/checker/relater.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,13 +1270,13 @@ func isNonPrimitiveType(t *Type) bool {
12701270
func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, string) {
12711271
var leftStr string
12721272
if c.symbolValueDeclarationIsContextSensitive(left.symbol) {
1273-
leftStr = c.typeToStringEx(left, left.symbol.ValueDeclaration, TypeFormatFlagsNone)
1273+
leftStr = c.typeToString(left, left.symbol.ValueDeclaration)
12741274
} else {
12751275
leftStr = c.TypeToString(left)
12761276
}
12771277
var rightStr string
12781278
if c.symbolValueDeclarationIsContextSensitive(right.symbol) {
1279-
rightStr = c.typeToStringEx(right, right.symbol.ValueDeclaration, TypeFormatFlagsNone)
1279+
rightStr = c.typeToString(right, right.symbol.ValueDeclaration)
12801280
} else {
12811281
rightStr = c.TypeToString(right)
12821282
}

internal/collections/multimap.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ func (s *MultiMap[K, V]) Keys() iter.Seq[K] {
6060
return maps.Keys(s.M)
6161
}
6262

63+
func (s *MultiMap[K, V]) Values() iter.Seq[[]V] {
64+
return maps.Values(s.M)
65+
}
66+
6367
func (s *MultiMap[K, V]) Clear() {
6468
clear(s.M)
6569
}

testdata/baselines/reference/submodule/compiler/declarationEmitClassMemberWithComputedPropertyName.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ declare class Foo {
7575
static p1: number;
7676
p1: number;
7777
}
78-
export declare const t1: (typeof Foo)[typeof k1];
78+
export declare const t1: () => number;
7979
export declare const t2: () => string;
80-
export declare const t3: typeof Foo.foo;
80+
export declare const t3: () => number;
8181
export declare const t4: () => string;
8282
export declare const t5: typeof Foo.m1;
8383
export declare const t6: () => void;

testdata/baselines/reference/submodule/compiler/declarationEmitClassMemberWithComputedPropertyName.js.diff

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)