7
7
"strings"
8
8
9
9
"github.com/microsoft/typescript-go/internal/ast"
10
+ "github.com/microsoft/typescript-go/internal/collections"
10
11
"github.com/microsoft/typescript-go/internal/core"
11
12
"github.com/microsoft/typescript-go/internal/jsnum"
12
13
"github.com/microsoft/typescript-go/internal/module"
@@ -63,7 +64,7 @@ type NodeBuilderContext struct {
63
64
enclosingDeclaration * ast.Node
64
65
enclosingFile * ast.SourceFile
65
66
inferTypeParameters []* Type
66
- visitedTypes map [TypeId ]bool
67
+ visitedTypes collections. Set [TypeId ]
67
68
symbolDepth map [CompositeSymbolIdentity ]int
68
69
trackedSymbols []* TrackedSymbolArgs
69
70
mapper * TypeMapper
@@ -212,12 +213,121 @@ func (b *nodeBuilderImpl) createElidedInformationPlaceholder() *ast.TypeNode {
212
213
return b .f .NewKeywordTypeNode (ast .KindAnyKeyword )
213
214
}
214
215
215
- func (b * nodeBuilderImpl ) mapToTypeNodes (list []* Type ) * ast.NodeList {
216
+ func (b * nodeBuilderImpl ) mapToTypeNodes (list []* Type , isBareList bool ) * ast.NodeList {
216
217
if len (list ) == 0 {
217
218
return nil
218
219
}
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
221
331
}
222
332
223
333
func (b * nodeBuilderImpl ) setCommentRange (node * ast.Node , range_ * ast.Node ) {
@@ -819,7 +929,7 @@ func (b *nodeBuilderImpl) lookupTypeParameterNodes(chain []*ast.Symbol, index in
819
929
if targetMapper != nil {
820
930
params = core .Map (params , targetMapper .Map )
821
931
}
822
- return b .mapToTypeNodes (params )
932
+ return b .mapToTypeNodes (params , false /*isBareList*/ )
823
933
} else {
824
934
typeParameterNodes := b .typeParametersToTypeParameterDeclarations (symbol )
825
935
if len (typeParameterNodes ) > 0 {
@@ -1314,6 +1424,7 @@ func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode {
1314
1424
templateTypeNode ,
1315
1425
nil ,
1316
1426
)
1427
+ b .ctx .approximateLength += 10
1317
1428
b .e .AddEmitFlags (result , printer .EFSingleLine )
1318
1429
1319
1430
if b .ctx .flags & nodebuilder .FlagsGenerateNamesForShadowedTypeParams != 0 && b .isHomomorphicMappedTypeWithNonHomomorphicInstantiation (mapped ) {
@@ -2301,7 +2412,7 @@ func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol {
2301
2412
2302
2413
func (b * nodeBuilderImpl ) shouldWriteTypeOfFunctionSymbol (symbol * ast.Symbol , typeId TypeId ) bool {
2303
2414
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 ))
2305
2416
})
2306
2417
isNonLocalFunctionSymbol := false
2307
2418
if symbol .Flags & ast .SymbolFlagsFunction != 0 {
@@ -2318,9 +2429,8 @@ func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, ty
2318
2429
}
2319
2430
if isStaticMethodSymbol || isNonLocalFunctionSymbol {
2320
2431
// 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
2324
2434
}
2325
2435
return false
2326
2436
}
@@ -2339,7 +2449,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
2339
2449
return typeNode
2340
2450
}
2341
2451
}
2342
- if _ , ok := b .ctx .visitedTypes [ typeId ]; ok {
2452
+ if b .ctx .visitedTypes . Has ( typeId ) {
2343
2453
return b .createElidedInformationPlaceholder ()
2344
2454
}
2345
2455
return b .visitAndTransformType (t , (* nodeBuilderImpl ).createTypeNodeFromObjectType )
@@ -2358,7 +2468,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
2358
2468
// } else
2359
2469
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 ) {
2360
2470
return b .symbolToTypeNode (symbol , isInstanceType , nil )
2361
- } else if _ , ok := b .ctx .visitedTypes [ typeId ]; ok {
2471
+ } else if b .ctx .visitedTypes . Has ( typeId ) {
2362
2472
// If type is an anonymous type literal in a type alias declaration, use type alias name
2363
2473
typeAlias := getTypeAliasForTypeLiteral (b .ch , t )
2364
2474
if typeAlias != nil {
@@ -2392,7 +2502,7 @@ func (b *nodeBuilderImpl) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes
2392
2502
2393
2503
func (b * nodeBuilderImpl ) typeToTypeNodeOrCircularityElision (t * Type ) * ast.TypeNode {
2394
2504
if t .flags & TypeFlagsUnion != 0 {
2395
- if _ , ok := b .ctx .visitedTypes [ t .id ]; ok {
2505
+ if b .ctx .visitedTypes . Has ( t .id ) {
2396
2506
if b .ctx .flags & nodebuilder .FlagsAllowAnonymousIdentifier == 0 {
2397
2507
b .ctx .encounteredError = true
2398
2508
b .ctx .tracker .ReportCyclicStructureError ()
@@ -2486,7 +2596,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
2486
2596
})
2487
2597
if len (typeArguments ) > 0 {
2488
2598
arity := b .ch .getTypeReferenceArity (t )
2489
- tupleConstituentNodes := b .mapToTypeNodes (typeArguments [0 :arity ])
2599
+ tupleConstituentNodes := b .mapToTypeNodes (typeArguments [0 :arity ], false /*isBareList*/ )
2490
2600
if tupleConstituentNodes != nil {
2491
2601
for i := 0 ; i < len (tupleConstituentNodes .Nodes ); i ++ {
2492
2602
flags := t .Target ().AsTupleType ().elementInfos [i ].flags
@@ -2543,7 +2653,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
2543
2653
// the default outer type arguments), we don't show the group.
2544
2654
2545
2655
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*/ )
2547
2657
restoreFlags := b .saveRestoreFlags ()
2548
2658
b .ctx .flags |= nodebuilder .FlagsForbidIndexedAccessSymbolReferences
2549
2659
ref := b .symbolToTypeNode (parent , ast .SymbolFlagsType , typeArgumentSlice )
@@ -2582,7 +2692,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
2582
2692
}
2583
2693
}
2584
2694
2585
- typeArgumentNodes = b .mapToTypeNodes (typeArguments [i :typeParameterCount ])
2695
+ typeArgumentNodes = b .mapToTypeNodes (typeArguments [i :typeParameterCount ], false /*isBareList*/ )
2586
2696
}
2587
2697
restoreFlags := b .saveRestoreFlags ()
2588
2698
b .ctx .flags |= nodebuilder .FlagsForbidIndexedAccessSymbolReferences
@@ -2614,7 +2724,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
2614
2724
// of types allows us to catch circular references to instantiations of the same anonymous type
2615
2725
2616
2726
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 ) {
2618
2728
links := b .links .Get (b .ctx .enclosingDeclaration )
2619
2729
cachedResult , ok := links .serializedTypes [key ]
2620
2730
if ok {
@@ -2638,7 +2748,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
2638
2748
}
2639
2749
b .ctx .symbolDepth [* id ] = depth + 1
2640
2750
}
2641
- b .ctx .visitedTypes [ typeId ] = true
2751
+ b .ctx .visitedTypes . Add ( typeId )
2642
2752
prevTrackedSymbols := b .ctx .trackedSymbols
2643
2753
b .ctx .trackedSymbols = nil
2644
2754
startLength := b .ctx .approximateLength
@@ -2656,7 +2766,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeB
2656
2766
trackedSymbols : b .ctx .trackedSymbols ,
2657
2767
}
2658
2768
}
2659
- delete ( b .ctx .visitedTypes , typeId )
2769
+ b .ctx .visitedTypes . Delete ( typeId )
2660
2770
if id != nil {
2661
2771
b .ctx .symbolDepth [* id ] = depth
2662
2772
}
@@ -2828,7 +2938,7 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
2828
2938
2829
2939
if inTypeAlias == 0 && t .alias != nil && (b .ctx .flags & nodebuilder .FlagsUseAliasDefinedOutsideCurrentScope != 0 || b .ch .IsTypeSymbolAccessible (t .alias .Symbol (), b .ctx .enclosingDeclaration )) {
2830
2940
sym := t .alias .Symbol ()
2831
- typeArgumentNodes := b .mapToTypeNodes (t .alias .TypeArguments ())
2941
+ typeArgumentNodes := b .mapToTypeNodes (t .alias .TypeArguments (), false /*isBareList*/ )
2832
2942
if isReservedMemberName (sym .Name ) && sym .Flags & ast .SymbolFlagsClass == 0 {
2833
2943
return b .f .NewTypeReferenceNode (b .f .NewIdentifier ("" ), typeArgumentNodes )
2834
2944
}
@@ -2896,7 +3006,7 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
2896
3006
if len (types ) == 1 {
2897
3007
return b .typeToTypeNode (types [0 ])
2898
3008
}
2899
- typeNodes := b .mapToTypeNodes (types )
3009
+ typeNodes := b .mapToTypeNodes (types , true /*isBareList*/ )
2900
3010
if typeNodes != nil && len (typeNodes .Nodes ) > 0 {
2901
3011
if t .flags & TypeFlagsUnion != 0 {
2902
3012
return b .f .NewUnionTypeNode (typeNodes )
@@ -2970,5 +3080,5 @@ func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
2970
3080
// Direct serialization core functions for types, type aliases, and symbols
2971
3081
2972
3082
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*/ ))
2974
3084
}
0 commit comments