diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 4e082d7bcd..e2affc20dd 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -5654,6 +5654,7 @@ type SourceFile struct { IsDeclarationFile bool IsBound bool ModuleReferencesProcessed bool + HasNoDefaultLib bool UsesUriStyleNodeCoreModules core.Tristate SymbolCount int ClassifiableNames core.Set[string] @@ -5661,7 +5662,6 @@ type SourceFile struct { ModuleAugmentations []*ModuleName // []ModuleName PatternAmbientModules []PatternAmbientModule AmbientModuleNames []string - HasNoDefaultLib bool jsdocCache map[*Node][]*Node Pragmas []Pragma ReferencedFiles []*FileReference diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 7b122a98e7..0ef7838aba 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -821,15 +821,13 @@ func WalkUpParenthesizedTypes(node *TypeNode) *Node { // Walks up the parents of a node to find the containing SourceFile func GetSourceFileOfNode(node *Node) *SourceFile { - for { - if node == nil { - return nil - } - if node.Kind == KindSourceFile { - return node.AsSourceFile() - } + for node.Parent != nil { node = node.Parent } + if node.Kind == KindSourceFile { + return node.AsSourceFile() + } + return nil } // Walks up the parents of a node to find the ancestor that matches the callback diff --git a/internal/compiler/checker.go b/internal/compiler/checker.go index 81a6ad8316..3490a19259 100644 --- a/internal/compiler/checker.go +++ b/internal/compiler/checker.go @@ -486,6 +486,8 @@ type Checker struct { host CompilerHost compilerOptions *core.CompilerOptions files []*ast.SourceFile + fileIndexMap map[*ast.SourceFile]int + compareSymbols func(*ast.Symbol, *ast.Symbol) int typeCount uint32 symbolCount uint32 totalInstantiationCount uint32 @@ -732,6 +734,8 @@ func NewChecker(program *Program) *Checker { c.host = program.host c.compilerOptions = program.compilerOptions c.files = program.files + c.fileIndexMap = createFileIndexMap(c.files) + c.compareSymbols = c.compareSymbolsWorker // Closure optimization c.languageVersion = c.compilerOptions.GetEmitScriptTarget() c.moduleKind = c.compilerOptions.GetEmitModuleKind() c.legacyDecorators = c.compilerOptions.ExperimentalDecorators == core.TSTrue @@ -862,7 +866,7 @@ func NewChecker(program *Program) *Checker { c.emptyStringType = c.getStringLiteralType("") c.zeroType = c.getNumberLiteralType(0) c.zeroBigIntType = c.getBigIntLiteralType(PseudoBigInt{negative: false, base10Value: "0"}) - c.typeofType = c.getUnionType(core.Map(slices.Collect(maps.Keys(typeofNEFacts)), c.getStringLiteralType)) + c.typeofType = c.getUnionType(core.Map(slices.Sorted(maps.Keys(typeofNEFacts)), c.getStringLiteralType)) c.flowLoopCache = make(map[FlowLoopKey]*Type) c.flowNodeReachable = make(map[*ast.FlowNode]bool) c.flowNodePostSuper = make(map[*ast.FlowNode]bool) @@ -903,6 +907,14 @@ func NewChecker(program *Program) *Checker { return c } +func createFileIndexMap(files []*ast.SourceFile) map[*ast.SourceFile]int { + result := make(map[*ast.SourceFile]int, len(files)) + for i, file := range files { + result[file] = i + } + return result +} + func (c *Checker) reportUnreliableWorker(t *Type) *Type { if c.outofbandVarianceMarkerHandler != nil && (t == c.markerSuperType || t == c.markerSubType || t == c.markerOtherType) { c.outofbandVarianceMarkerHandler(true /*onlyUnreliable*/) @@ -15109,7 +15121,7 @@ func (c *Checker) getNamedMembers(members ast.SymbolTable) []*ast.Symbol { result = append(result, symbol) } } - sortSymbols(result) + c.sortSymbols(result) return result } @@ -17861,6 +17873,7 @@ func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, data TypeDat t.flags = flags t.objectFlags = objectFlags &^ (ObjectFlagsCouldContainTypeVariablesComputed | ObjectFlagsCouldContainTypeVariables | ObjectFlagsMembersResolved) t.id = TypeId(c.typeCount) + t.checker = c t.data = data return t } @@ -18601,14 +18614,7 @@ func (c *Checker) addTypeToUnion(typeSet []*Type, includes TypeFlags, t *Type) ( includes |= TypeFlagsIncludesNonWideningType } } else { - var index int - var ok bool - if len(typeSet) != 0 && t.id > typeSet[len(typeSet)-1].id { - index = len(typeSet) - } else { - index, ok = slices.BinarySearchFunc(typeSet, t, compareTypeIds) - } - if !ok { + if index, ok := slices.BinarySearchFunc(typeSet, t, compareTypes); !ok { typeSet = slices.Insert(typeSet, index, t) } } @@ -19350,12 +19356,12 @@ func (c *Checker) removeType(t *Type, targetType *Type) *Type { } func containsType(types []*Type, t *Type) bool { - _, ok := slices.BinarySearchFunc(types, t, compareTypeIds) + _, ok := slices.BinarySearchFunc(types, t, compareTypes) return ok } func insertType(types []*Type, t *Type) ([]*Type, bool) { - if i, ok := slices.BinarySearchFunc(types, t, compareTypeIds); !ok { + if i, ok := slices.BinarySearchFunc(types, t, compareTypes); !ok { return slices.Insert(types, i, t), true } return types, false diff --git a/internal/compiler/mapper.go b/internal/compiler/mapper.go index bdfe3c504e..771236394b 100644 --- a/internal/compiler/mapper.go +++ b/internal/compiler/mapper.go @@ -2,18 +2,31 @@ package compiler import "github.com/microsoft/typescript-go/internal/core" +// TypeMapperKind + +type TypeMapperKind int32 + +const ( + TypeMapperKindUnknown TypeMapperKind = iota + TypeMapperKindSimple + TypeMapperKindArray + TypeMapperKindMerged +) + // TypeMapper type TypeMapper struct { data TypeMapperData } -func (m *TypeMapper) Map(t *Type) *Type { return m.data.Map(t) } +func (m *TypeMapper) Map(t *Type) *Type { return m.data.Map(t) } +func (m *TypeMapper) Kind() TypeMapperKind { return m.data.Kind() } // TypeMapperData type TypeMapperData interface { Map(t *Type) *Type + Kind() TypeMapperKind } // Factory functions @@ -70,10 +83,19 @@ func (c *Checker) newBackreferenceMapper(context *InferenceContext, index int) * return newArrayToSingleTypeMapper(typeParameters, c.unknownType) } +// TypeMapperBase + +type TypeMapperBase struct { + TypeMapper +} + +func (m *TypeMapperBase) Map(t *Type) *Type { return t } +func (m *TypeMapperBase) Kind() TypeMapperKind { return TypeMapperKindUnknown } + // SimpleTypeMapper type SimpleTypeMapper struct { - TypeMapper + TypeMapperBase source *Type target *Type } @@ -93,10 +115,14 @@ func (m *SimpleTypeMapper) Map(t *Type) *Type { return t } +func (m *SimpleTypeMapper) Kind() TypeMapperKind { + return TypeMapperKindSimple +} + // ArrayTypeMapper type ArrayTypeMapper struct { - TypeMapper + TypeMapperBase sources []*Type targets []*Type } @@ -118,10 +144,14 @@ func (m *ArrayTypeMapper) Map(t *Type) *Type { return t } +func (m *ArrayTypeMapper) Kind() TypeMapperKind { + return TypeMapperKindArray +} + // ArrayToSingleTypeMapper type ArrayToSingleTypeMapper struct { - TypeMapper + TypeMapperBase sources []*Type target *Type } @@ -146,7 +176,7 @@ func (m *ArrayToSingleTypeMapper) Map(t *Type) *Type { // DeferredTypeMapper type DeferredTypeMapper struct { - TypeMapper + TypeMapperBase sources []*Type targets []func() *Type } @@ -171,7 +201,7 @@ func (m *DeferredTypeMapper) Map(t *Type) *Type { // FunctionTypeMapper type FunctionTypeMapper struct { - TypeMapper + TypeMapperBase fn func(*Type) *Type } @@ -189,7 +219,7 @@ func (m *FunctionTypeMapper) Map(t *Type) *Type { // MergedTypeMapper type MergedTypeMapper struct { - TypeMapper + TypeMapperBase m1 *TypeMapper m2 *TypeMapper } @@ -206,10 +236,14 @@ func (m *MergedTypeMapper) Map(t *Type) *Type { return m.m2.Map(m.m1.Map(t)) } +func (m *MergedTypeMapper) Kind() TypeMapperKind { + return TypeMapperKindMerged +} + // CompositeTypeMapper type CompositeTypeMapper struct { - TypeMapper + TypeMapperBase c *Checker m1 *TypeMapper m2 *TypeMapper @@ -235,7 +269,7 @@ func (m *CompositeTypeMapper) Map(t *Type) *Type { // InferenceTypeMapper type InferenceTypeMapper struct { - TypeMapper + TypeMapperBase c *Checker n *InferenceContext fixing bool diff --git a/internal/compiler/printer.go b/internal/compiler/printer.go index 8a0d84afa3..fb5a604e8f 100644 --- a/internal/compiler/printer.go +++ b/internal/compiler/printer.go @@ -112,6 +112,7 @@ func (p *Printer) printType(t *Type) { } func (p *Printer) printTypeNoAlias(t *Type) { + p.depth++ switch { case t.flags&TypeFlagsIntrinsic != 0: p.print(t.AsIntrinsicType().intrinsicName) @@ -138,14 +139,13 @@ func (p *Printer) printTypeNoAlias(t *Type) { case t.flags&TypeFlagsSubstitution != 0: p.printType(t.AsSubstitutionType().baseType) } + p.depth-- } func (p *Printer) printRecursive(t *Type, f func(*Printer, *Type)) { if !p.printing.Has(t) && p.depth < 10 { p.printing.Add(t) - p.depth++ f(p, t) - p.depth-- p.printing.Delete(t) } else { p.print("???") @@ -282,22 +282,37 @@ func (p *Printer) printTupleType(t *Type) { tail := false p.print("[") elementInfos := t.TargetTupleType().elementInfos - for i, t := range p.c.getTypeArguments(t) { + typeArguments := p.c.getTypeArguments(t) + for i, info := range elementInfos { + t := typeArguments[i] if tail { p.print(", ") } - info := elementInfos[i] if info.flags&ElementFlagsVariable != 0 { p.print("...") } - if info.flags&ElementFlagsOptional != 0 { - p.printTypeEx(t, ast.TypePrecedencePostfix) - p.print("?") - } else if info.flags&ElementFlagsRest != 0 { - p.printTypeEx(t, ast.TypePrecedencePostfix) - p.print("[]") + if info.labeledDeclaration != nil { + p.print(info.labeledDeclaration.Name().Text()) + if info.flags&ElementFlagsOptional != 0 { + p.print("?") + } + p.print(": ") + if info.flags&ElementFlagsRest != 0 { + p.printTypeEx(t, ast.TypePrecedencePostfix) + p.print("[]") + } else { + p.printType(t) + } } else { - p.printType(t) + if info.flags&ElementFlagsOptional != 0 { + p.printTypeEx(t, ast.TypePrecedencePostfix) + p.print("?") + } else if info.flags&ElementFlagsRest != 0 { + p.printTypeEx(t, ast.TypePrecedencePostfix) + p.print("[]") + } else { + p.printType(t) + } } tail = true } diff --git a/internal/compiler/types.go b/internal/compiler/types.go index 6e792e24b5..c35e867c42 100644 --- a/internal/compiler/types.go +++ b/internal/compiler/types.go @@ -381,21 +381,21 @@ const ( TypeFlagsNone TypeFlags = 0 TypeFlagsAny TypeFlags = 1 << 0 TypeFlagsUnknown TypeFlags = 1 << 1 - TypeFlagsString TypeFlags = 1 << 2 - TypeFlagsNumber TypeFlags = 1 << 3 - TypeFlagsBoolean TypeFlags = 1 << 4 - TypeFlagsEnum TypeFlags = 1 << 5 // Numeric computed enum member value - TypeFlagsBigInt TypeFlags = 1 << 6 - TypeFlagsStringLiteral TypeFlags = 1 << 7 - TypeFlagsNumberLiteral TypeFlags = 1 << 8 - TypeFlagsBooleanLiteral TypeFlags = 1 << 9 - TypeFlagsEnumLiteral TypeFlags = 1 << 10 // Always combined with StringLiteral, NumberLiteral, or Union - TypeFlagsBigIntLiteral TypeFlags = 1 << 11 - TypeFlagsESSymbol TypeFlags = 1 << 12 // Type of symbol primitive introduced in ES6 - TypeFlagsUniqueESSymbol TypeFlags = 1 << 13 // unique symbol - TypeFlagsVoid TypeFlags = 1 << 14 - TypeFlagsUndefined TypeFlags = 1 << 15 - TypeFlagsNull TypeFlags = 1 << 16 + TypeFlagsUndefined TypeFlags = 1 << 2 + TypeFlagsNull TypeFlags = 1 << 3 + TypeFlagsVoid TypeFlags = 1 << 4 + TypeFlagsString TypeFlags = 1 << 5 + TypeFlagsNumber TypeFlags = 1 << 6 + TypeFlagsBigInt TypeFlags = 1 << 7 + TypeFlagsBoolean TypeFlags = 1 << 8 + TypeFlagsESSymbol TypeFlags = 1 << 9 // Type of symbol primitive introduced in ES6 + TypeFlagsStringLiteral TypeFlags = 1 << 10 + TypeFlagsNumberLiteral TypeFlags = 1 << 11 + TypeFlagsBigIntLiteral TypeFlags = 1 << 12 + TypeFlagsBooleanLiteral TypeFlags = 1 << 13 + TypeFlagsUniqueESSymbol TypeFlags = 1 << 14 // unique symbol + TypeFlagsEnumLiteral TypeFlags = 1 << 15 // Always combined with StringLiteral, NumberLiteral, or Union + TypeFlagsEnum TypeFlags = 1 << 16 // Numeric computed enum member value (must be right after EnumLiteral, see getSortOrderFlags) TypeFlagsNever TypeFlags = 1 << 17 // Never type TypeFlagsTypeParameter TypeFlags = 1 << 18 // Type parameter TypeFlagsObject TypeFlags = 1 << 19 // Object type @@ -494,7 +494,7 @@ const ( ObjectFlagsPropagatingFlags = ObjectFlagsContainsWideningType | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsNonInferrableType ObjectFlagsInstantiatedMapped = ObjectFlagsMapped | ObjectFlagsInstantiated // Object flags that uniquely identify the kind of ObjectType - ObjectFlagsObjectTypeKindMask = ObjectFlagsClassOrInterface | ObjectFlagsReference | ObjectFlagsTuple | ObjectFlagsAnonymous | ObjectFlagsMapped | ObjectFlagsReverseMapped | ObjectFlagsEvolvingArray + ObjectFlagsObjectTypeKindMask = ObjectFlagsClassOrInterface | ObjectFlagsReference | ObjectFlagsTuple | ObjectFlagsAnonymous | ObjectFlagsMapped | ObjectFlagsReverseMapped | ObjectFlagsEvolvingArray | ObjectFlagsInstantiationExpressionType | ObjectFlagsSingleSignatureType // Flags that require TypeFlags.Object ObjectFlagsContainsSpread = 1 << 22 // Object literal contains spread operation ObjectFlagsObjectRestType = 1 << 23 // Originates in object rest declaration @@ -548,6 +548,7 @@ type Type struct { id TypeId symbol *ast.Symbol alias *TypeAlias + checker *Checker data TypeData // Type specific data } @@ -725,17 +726,42 @@ func (t *StructuredType) ConstructSignatures() []*Signature { return slices.Clip(t.signatures[t.callSignatureCount:]) } -// ObjectType (base of all instantiable object types) -// Instances of ObjectType or derived types have the following ObjectFlags: -// ObjectType (ObjectFlagsAnonymous) -// TypeReference (ObjectFlagsReference) -// InterfaceType (ObjectFlagsReference | (ObjectFlagsClass|ObjectFlagsInterface)) -// TupleType (ObjectFlagsReference | ObjectFlagsTuple) -// SingleSignatureType (ObjectFlagsAnonymous|ObjectFlagsSingleSignatureType) -// InstantiationExpressionType (ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType) -// MappedType (ObjectFlagsAnonymous|ObjectFlagsMapped) -// ReverseMapped (ObjectFlagsReverseMapped) -// EvolvingArray (ObjectFlagsEvolvingArray) +// Except for tuple type references and reverse mapped types, all object types have an associated symbol. +// Possible object type instances are listed in the following. + +// InterfaceType: +// ObjectFlagsClass: Originating non-generic class type +// ObjectFlagsClass|ObjectFlagsReference: Originating generic class type +// ObjectFlagsInterface: Originating non-generic interface type +// ObjectFlagsInterface|ObjectFlagsReference: Originating generic interface type + +// TupleType: +// ObjectFlagsReference|ObjectFlagsTuple: Originating generic tuple type (synthesized) + +// TypeReference +// ObjectFlagsReference: Instantiated generic class, interface, or tuple type + +// ObjectType: +// ObjectFlagsAnonymous: Originating anonymous object type +// ObjectFlagsAnonymous|ObjectFlagsInstantiated: Instantiated anonymous object type + +// MappedType: +// ObjectFlagsMapped: Originating mapped type +// ObjectFlagsMapped|ObjectFlagsInstantiated: Instantiated mapped type + +// InstantiationExpressionType: +// ObjectFlagsAnonymous|ObjectFlagsInstantiationExpression: Originating instantiation expression type +// ObjectFlagsAnonymous|ObjectFlagsInstantiated|ObjectFlagsInstantiationExpression: Instantiated instantiation expression type + +// SingleSignatureType: +// ObjectFlagsAnonymous|ObjectFlagsSingleSignatureType: Originating single signature type +// ObjectFlagsAnonymous|ObjectFlagsInstantiated|ObjectFlagsSingleSignatureType: Instantiated single signature type + +// ReverseMappedType: +// ObjectFlagsAnonymous|ObjectFlagsReverseMapped: Reverse mapped type + +// EvolvingArrayType: +// ObjectFlagsEvolvingArray: Evolving array type type ObjectType struct { StructuredType @@ -879,7 +905,6 @@ type UnionOrIntersectionType struct { propertyCache ast.SymbolTable propertyCacheWithoutFunctionPropertyAugment ast.SymbolTable resolvedProperties []*ast.Symbol - resolvedBaseConstraint *Type } func (t *UnionOrIntersectionType) AsUnionOrIntersectionType() *UnionOrIntersectionType { return t } diff --git a/internal/compiler/utilities.go b/internal/compiler/utilities.go index d5bf949a4e..f6130aafbc 100644 --- a/internal/compiler/utilities.go +++ b/internal/compiler/utilities.go @@ -1,6 +1,7 @@ package compiler import ( + "cmp" "maps" "math" "slices" @@ -1578,43 +1579,351 @@ func createSymbolTable(symbols []*ast.Symbol) ast.SymbolTable { return result } -func sortSymbols(symbols []*ast.Symbol) { - slices.SortFunc(symbols, compareSymbols) +func (c *Checker) sortSymbols(symbols []*ast.Symbol) { + slices.SortFunc(symbols, c.compareSymbols) } -func compareSymbols(s1, s2 *ast.Symbol) int { +func (c *Checker) compareSymbolsWorker(s1, s2 *ast.Symbol) int { if s1 == s2 { return 0 } - if s1.ValueDeclaration != nil && s2.ValueDeclaration != nil { - // Symbols with the same unmerged parent are always in the same file - if s1.Parent != s2.Parent { - f1 := ast.GetSourceFileOfNode(s1.ValueDeclaration) - f2 := ast.GetSourceFileOfNode(s2.ValueDeclaration) - if f1 != f2 { - // Compare the full paths (no two files should have the same full path) - return strings.Compare(string(f1.Path()), string(f2.Path())) + if s1 == nil { + return 1 + } + if s2 == nil { + return -1 + } + if len(s1.Declarations) != 0 && len(s2.Declarations) != 0 { + if r := c.compareNodes(s1.Declarations[0], s2.Declarations[0]); r != 0 { + return r + } + } else if len(s1.Declarations) != 0 { + return -1 + } else if len(s2.Declarations) != 0 { + return 1 + } + if r := strings.Compare(s1.Name, s2.Name); r != 0 { + return r + } + // Fall back to symbol IDs. This is a last resort that should happen only when symbols have + // no declaration and duplicate names. + return int(ast.GetSymbolId(s1)) - int(ast.GetSymbolId(s2)) +} + +func (c *Checker) compareNodes(n1, n2 *ast.Node) int { + if n1 == n2 { + return 0 + } + if n1 == nil { + return 1 + } + if n2 == nil { + return -1 + } + f1 := c.fileIndexMap[ast.GetSourceFileOfNode(n1)] + f2 := c.fileIndexMap[ast.GetSourceFileOfNode(n2)] + if f1 != f2 { + // Order by index of file in the containing program + return f1 - f2 + } + // In the same file, order by source position + return n1.Pos() - n2.Pos() +} + +func compareTypes(t1, t2 *Type) int { + if t1 == t2 { + return 0 + } + if t1 == nil { + return -1 + } + if t2 == nil { + return 1 + } + if t1.checker != t2.checker { + panic("Cannot compare types from different checkers") + } + // First sort in order of increasing type flags values. + if c := getSortOrderFlags(t1) - getSortOrderFlags(t2); c != 0 { + return c + } + // Order named types by name and, in the case of aliased types, by alias type arguments. + if c := compareTypeNames(t1, t2); c != 0 { + return c + } + // We have unnamed types or types with identical names. Now sort by data specific to the type. + switch { + case t1.flags&(TypeFlagsAny|TypeFlagsUnknown|TypeFlagsString|TypeFlagsNumber|TypeFlagsBoolean|TypeFlagsBigInt|TypeFlagsESSymbol|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsNever|TypeFlagsNonPrimitive) != 0: + // Only distinguished by type IDs, handled below. + case t1.flags&TypeFlagsObject != 0: + // Order unnamed or identically named object types by symbol. + if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { + return c + } + // When object types have the same or no symbol, order by kind. We order type references before other kinds. + if t1.objectFlags&ObjectFlagsReference != 0 && t2.objectFlags&ObjectFlagsReference != 0 { + r1 := t1.AsTypeReference() + r2 := t2.AsTypeReference() + if r1.target.objectFlags&ObjectFlagsTuple != 0 && r2.target.objectFlags&ObjectFlagsTuple != 0 { + // Tuple types have no associated symbol, instead we order by tuple element information. + if c := compareTupleTypes(r1.target.AsTupleType(), r2.target.AsTupleType()); c != 0 { + return c + } + } + // Here we know we have references to instantiations of the same type because we have matching targets. + if r1.node == nil && r2.node == nil { + // Non-deferred type references with the same target are sorted by their type argument lists. + if c := compareTypeLists(t1.AsTypeReference().resolvedTypeArguments, t2.AsTypeReference().resolvedTypeArguments); c != 0 { + return c + } + } else { + // Deferred type references with the same target are ordered by the source location of the reference. + if c := t1.checker.compareNodes(r1.node, r2.node); c != 0 { + return c + } + // Instantiations of the same deferred type reference are ordered by their associated type mappers + // (which reflect the mapping of in-scope type parameters to type arguments). + if c := compareTypeMappers(t1.AsObjectType().mapper, t2.AsObjectType().mapper); c != 0 { + return c + } + } + } else if t1.objectFlags&ObjectFlagsReference != 0 { + return -1 + } else if t2.objectFlags&ObjectFlagsReference != 0 { + return 1 + } else { + // Order unnamed non-reference object types by kind associated type mappers. Reverse mapped types have + // neither symbols nor mappers so they're ultimately ordered by unstable type IDs, but given their rarity + // this should be fine. + if c := int(t1.objectFlags&ObjectFlagsObjectTypeKindMask) - int(t2.objectFlags&ObjectFlagsObjectTypeKindMask); c != 0 { + return c + } + if c := compareTypeMappers(t1.AsObjectType().mapper, t2.AsObjectType().mapper); c != 0 { + return c } - // In the same file, compare source positions } - r := s1.ValueDeclaration.Pos() - s2.ValueDeclaration.Pos() - if r != 0 { - return r + case t1.flags&TypeFlagsUnion != 0: + // Unions are ordered by origin and then constituent type lists. + o1 := t1.AsUnionType().origin + o2 := t2.AsUnionType().origin + if o1 == nil && o2 == nil { + if c := compareTypeLists(t1.Types(), t2.Types()); c != 0 { + return c + } + } else if o1 == nil { + return 1 + } else if o2 == nil { + return -1 + } else { + if c := compareTypes(o1, o2); c != 0 { + return c + } + } + case t1.flags&TypeFlagsIntersection != 0: + // Intersections are ordered by their constituent type lists. + if c := compareTypeLists(t1.Types(), t2.Types()); c != 0 { + return c + } + case t1.flags&(TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0: + // Enum members are ordered by their symbol (and thus their declaration order). + if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { + return c + } + case t1.flags&TypeFlagsStringLiteral != 0: + // String literal types are ordered by their values. + if c := strings.Compare(t1.AsLiteralType().value.(string), t2.AsLiteralType().value.(string)); c != 0 { + return c + } + case t1.flags&TypeFlagsNumberLiteral != 0: + // Numeric literal types are ordered by their values. + if c := cmp.Compare(t1.AsLiteralType().value.(float64), t2.AsLiteralType().value.(float64)); c != 0 { + return c + } + case t1.flags&TypeFlagsBooleanLiteral != 0: + b1 := t1.AsLiteralType().value.(bool) + b2 := t2.AsLiteralType().value.(bool) + if b1 != b2 { + if b1 { + return 1 + } + return -1 + } + case t1.flags&TypeFlagsTypeParameter != 0: + if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { + return c + } + case t1.flags&TypeFlagsIndex != 0: + if c := compareTypes(t1.AsIndexType().target, t2.AsIndexType().target); c != 0 { + return c + } + if c := int(t1.AsIndexType().flags) - int(t2.AsIndexType().flags); c != 0 { + return c + } + case t1.flags&TypeFlagsIndexedAccess != 0: + if c := compareTypes(t1.AsIndexedAccessType().objectType, t2.AsIndexedAccessType().objectType); c != 0 { + return c + } + if c := compareTypes(t1.AsIndexedAccessType().indexType, t2.AsIndexedAccessType().indexType); c != 0 { + return c + } + case t1.flags&TypeFlagsConditional != 0: + if c := t1.checker.compareNodes(t1.AsConditionalType().root.node.AsNode(), t2.AsConditionalType().root.node.AsNode()); c != 0 { + return c } + if c := compareTypeMappers(t1.AsConditionalType().mapper, t2.AsConditionalType().mapper); c != 0 { + return c + } + case t1.flags&TypeFlagsSubstitution != 0: + if c := compareTypes(t1.AsSubstitutionType().baseType, t2.AsSubstitutionType().baseType); c != 0 { + return c + } + if c := compareTypes(t1.AsSubstitutionType().constraint, t2.AsSubstitutionType().constraint); c != 0 { + return c + } + case t1.flags&TypeFlagsTemplateLiteral != 0: + if c := slices.Compare(t1.AsTemplateLiteralType().texts, t2.AsTemplateLiteralType().texts); c != 0 { + return c + } + if c := compareTypeLists(t1.AsTemplateLiteralType().types, t2.AsTemplateLiteralType().types); c != 0 { + return c + } + case t1.flags&TypeFlagsStringMapping != 0: + if c := compareTypes(t1.AsStringMappingType().target, t2.AsStringMappingType().target); c != 0 { + return c + } + } + // Fall back to type IDs. This results in type creation order for built-in types. + return int(t1.id) - int(t2.id) +} + +func getSortOrderFlags(t *Type) int { + // We want enum literal and computed values to be ordered by their declarations, so we merge TypeFlagsEnum into + // TypeFlagsEnumLiteral and clear TypeFlagsEnum. + return int((t.flags&TypeFlagsEnum)>>1 | t.flags&^TypeFlagsEnum) +} + +func compareTypeNames(t1, t2 *Type) int { + s1 := getTypeNameSymbol(t1) + s2 := getTypeNameSymbol(t2) + if s1 == s2 { + if t1.alias != nil { + return compareTypeLists(t1.alias.typeArguments, t2.alias.typeArguments) + } + return 0 + } + if s1 == nil { + return 1 + } + if s2 == nil { + return -1 + } + return strings.Compare(s1.Name, s2.Name) +} + +func getTypeNameSymbol(t *Type) *ast.Symbol { + if t.alias != nil { + return t.alias.symbol + } + if t.flags&(TypeFlagsTypeParameter|TypeFlagsStringMapping) != 0 || t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + return t.symbol + } + return nil +} + +func getObjectTypeName(t *Type) *ast.Symbol { + if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + return t.symbol + } + return nil +} + +func compareTupleTypes(t1, t2 *TupleType) int { + if t1 == t2 { + return 0 } - // Symbols with value declarations sort before symbols without - if s1.ValueDeclaration != nil && s2.ValueDeclaration == nil { + if t1.readonly != t2.readonly { + return core.IfElse(t1.readonly, 1, -1) + } + if len(t1.elementInfos) != len(t2.elementInfos) { + return len(t1.elementInfos) - len(t2.elementInfos) + } + for i := range t1.elementInfos { + if c := int(t1.elementInfos[i].flags) - int(t2.elementInfos[i].flags); c != 0 { + return c + } + } + for i := range t1.elementInfos { + if c := compareElementLabels(t1.elementInfos[i].labeledDeclaration, t2.elementInfos[i].labeledDeclaration); c != 0 { + return c + } + } + return 0 +} + +func compareElementLabels(n1, n2 *ast.Node) int { + if n1 == n2 { + return 0 + } + if n1 == nil { return -1 } - if s1.ValueDeclaration == nil && s2.ValueDeclaration != nil { + if n2 == nil { return 1 } - // Sort by name - r := strings.Compare(s1.Name, s2.Name) - if r != 0 { - return r + return strings.Compare(n1.Name().Text(), n2.Name().Text()) +} + +func compareTypeLists(s1, s2 []*Type) int { + if len(s1) != len(s2) { + return len(s1) - len(s2) + } + for i, t1 := range s1 { + if c := compareTypes(t1, s2[i]); c != 0 { + return c + } } - panic("Symbols must have unique names to be sorted") + return 0 +} + +func compareTypeMappers(m1, m2 *TypeMapper) int { + if m1 == m2 { + return 0 + } + if m1 == nil { + return 1 + } + if m2 == nil { + return -1 + } + kind1 := m1.Kind() + kind2 := m2.Kind() + if kind1 != kind2 { + return int(kind1) - int(kind2) + } + switch kind1 { + case TypeMapperKindSimple: + m1 := m1.data.(*SimpleTypeMapper) + m2 := m2.data.(*SimpleTypeMapper) + if c := compareTypes(m1.source, m2.source); c != 0 { + return c + } + return compareTypes(m1.target, m2.target) + case TypeMapperKindArray: + m1 := m1.data.(*ArrayTypeMapper) + m2 := m2.data.(*ArrayTypeMapper) + if c := compareTypeLists(m1.sources, m2.sources); c != 0 { + return c + } + return compareTypeLists(m1.targets, m2.targets) + case TypeMapperKindMerged: + m1 := m1.data.(*MergedTypeMapper) + m2 := m2.data.(*MergedTypeMapper) + if c := compareTypeMappers(m1.m1, m2.m1); c != 0 { + return c + } + return compareTypeMappers(m1.m2, m2.m2) + } + return 0 } func getClassLikeDeclarationOfSymbol(symbol *ast.Symbol) *ast.Node {