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
223333func (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
23022413func (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
23932503func (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
29723082func (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}
0 commit comments