367 lines
8.7 KiB
Go
367 lines
8.7 KiB
Go
package checker
|
|
|
|
import (
|
|
"maps"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/tracing"
|
|
)
|
|
|
|
// Tracer records types and trace events during type checking. A nil *Tracer
|
|
// is a valid no-op, so call sites can use `if tr := c.tracer; tr != nil` to
|
|
// gate work that only matters under --generateTrace.
|
|
type Tracer struct {
|
|
tracing *tracing.Tracing
|
|
recorder tracing.Tracer
|
|
checkerIndex int
|
|
}
|
|
|
|
// NewTracer creates a Tracer for the given checker index that records both
|
|
// type-creation events and trace events through the provided tracing session.
|
|
func NewTracer(tr *tracing.Tracing, checkerIndex int) *Tracer {
|
|
return &Tracer{tracing: tr, recorder: tr.NewTypeTracer(checkerIndex), checkerIndex: checkerIndex}
|
|
}
|
|
|
|
func (t *Tracer) RecordType(typ *Type) {
|
|
t.recorder.RecordType(wrapType(typ))
|
|
}
|
|
|
|
func (t *Tracer) Push(phase tracing.Phase, name string, args map[string]any, separateBeginAndEnd bool) func() {
|
|
if !separateBeginAndEnd {
|
|
return t.tracing.Push(phase, name, t.copyWithCheckerIndex(args), separateBeginAndEnd)
|
|
}
|
|
|
|
args, restore := t.temporarilyAddCheckerIndex(args)
|
|
pop := t.tracing.Push(phase, name, args, separateBeginAndEnd)
|
|
restore()
|
|
|
|
return func() {
|
|
_, restoreEndArgs := t.temporarilyAddCheckerIndex(args)
|
|
defer restoreEndArgs()
|
|
pop()
|
|
}
|
|
}
|
|
|
|
func (t *Tracer) Instant(phase tracing.Phase, name string, args map[string]any) {
|
|
t.tracing.Instant(phase, name, t.copyWithCheckerIndex(args))
|
|
}
|
|
|
|
func (t *Tracer) copyWithCheckerIndex(args map[string]any) map[string]any {
|
|
withCheckerIndex := make(map[string]any, len(args)+1)
|
|
maps.Copy(withCheckerIndex, args)
|
|
withCheckerIndex["checkerId"] = t.checkerIndex
|
|
return withCheckerIndex
|
|
}
|
|
|
|
func (t *Tracer) temporarilyAddCheckerIndex(args map[string]any) (map[string]any, func()) {
|
|
if args == nil {
|
|
args = map[string]any{}
|
|
}
|
|
|
|
previous, hadPrevious := args["checkerId"]
|
|
args["checkerId"] = t.checkerIndex
|
|
|
|
return args, func() {
|
|
if hadPrevious {
|
|
args["checkerId"] = previous
|
|
} else {
|
|
delete(args, "checkerId")
|
|
}
|
|
}
|
|
}
|
|
|
|
// tracedTypeAdapter adapts a Type to the tracing.TracedType interface
|
|
type tracedTypeAdapter struct {
|
|
t *Type
|
|
checker *Checker
|
|
}
|
|
|
|
var _ tracing.TracedType = (*tracedTypeAdapter)(nil)
|
|
|
|
func (a *tracedTypeAdapter) Id() uint32 {
|
|
return uint32(a.t.id)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) FormatFlags() []string {
|
|
return FormatTypeFlags(a.t.flags)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IsConditional() bool {
|
|
return a.t.flags&TypeFlagsConditional != 0
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) Symbol() *ast.Symbol {
|
|
return a.t.symbol
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) AliasSymbol() *ast.Symbol {
|
|
if a.t.alias == nil {
|
|
return nil
|
|
}
|
|
return a.t.alias.Symbol()
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) AliasTypeArguments() []tracing.TracedType {
|
|
if a.t.alias == nil {
|
|
return nil
|
|
}
|
|
return wrapTypes(a.t.alias.TypeArguments())
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IntrinsicName() string {
|
|
if a.t.flags&TypeFlagsIntrinsic == 0 {
|
|
return ""
|
|
}
|
|
data, ok := a.t.data.(*IntrinsicType)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return data.intrinsicName
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) UnionTypes() []tracing.TracedType {
|
|
if a.t.flags&TypeFlagsUnion == 0 {
|
|
return nil
|
|
}
|
|
return wrapTypes(a.t.AsUnionType().types)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IntersectionTypes() []tracing.TracedType {
|
|
if a.t.flags&TypeFlagsIntersection == 0 {
|
|
return nil
|
|
}
|
|
return wrapTypes(a.t.AsIntersectionType().types)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IndexType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsIndex == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsIndexType().target
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IndexedAccessObjectType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsIndexedAccess == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsIndexedAccessType().objectType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IndexedAccessIndexType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsIndexedAccess == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsIndexedAccessType().indexType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ConditionalCheckType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsConditional == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsConditionalType().checkType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ConditionalExtendsType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsConditional == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsConditionalType().extendsType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ConditionalTrueType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsConditional == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsConditionalType().resolvedTrueType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ConditionalFalseType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsConditional == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsConditionalType().resolvedFalseType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) SubstitutionBaseType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsSubstitution == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsSubstitutionType().baseType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) SubstitutionConstraintType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsSubstitution == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsSubstitutionType().constraint
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ReferenceTarget() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReference == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsTypeReference().target
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ReferenceTypeArguments() []tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReference == 0 {
|
|
return nil
|
|
}
|
|
return wrapTypes(a.t.AsTypeReference().resolvedTypeArguments)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ReferenceNode() *ast.Node {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReference == 0 {
|
|
return nil
|
|
}
|
|
return a.t.AsTypeReference().node
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ReverseMappedSourceType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReverseMapped == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsReverseMappedType().source
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ReverseMappedMappedType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReverseMapped == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsReverseMappedType().mappedType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) ReverseMappedConstraintType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReverseMapped == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsReverseMappedType().constraintType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) EvolvingArrayElementType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsEvolvingArray == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsEvolvingArrayType().elementType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) EvolvingArrayFinalType() tracing.TracedType {
|
|
if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsEvolvingArray == 0 {
|
|
return nil
|
|
}
|
|
t := a.t.AsEvolvingArrayType().finalArrayType
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return wrapType(t)
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) IsTuple() bool {
|
|
return a.t.objectFlags&ObjectFlagsTuple != 0
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) Pattern() *ast.Node {
|
|
if a.checker == nil {
|
|
return nil
|
|
}
|
|
return a.checker.patternForType[a.t]
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) RecursionIdentity() any {
|
|
return getRecursionIdentity(a.t).value
|
|
}
|
|
|
|
func (a *tracedTypeAdapter) Display() string {
|
|
// Compute display text for types where it's valuable for trace analysis.
|
|
// TypeScript only does this for Anonymous|Literal types, but we extend to
|
|
// unions, intersections, and template literals since they often lack
|
|
// firstDeclaration and the display text helps identify them.
|
|
// Incomplete types during tracing can cause panics, which we intentionally
|
|
// suppress (returning ""), matching TypeScript's try/catch around typeToString.
|
|
if a.checker == nil {
|
|
return ""
|
|
}
|
|
if a.t.objectFlags&ObjectFlagsAnonymous != 0 ||
|
|
a.t.flags&(TypeFlagsLiteral|TypeFlagsTemplateLiteral|TypeFlagsUnion|TypeFlagsIntersection) != 0 {
|
|
defer func() {
|
|
_ = recover()
|
|
}()
|
|
return a.checker.TypeToString(a.t)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func wrapType(t *Type) tracing.TracedType {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return &tracedTypeAdapter{t: t, checker: t.checker}
|
|
}
|
|
|
|
func wrapTypes(types []*Type) []tracing.TracedType {
|
|
if len(types) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]tracing.TracedType, len(types))
|
|
for i, t := range types {
|
|
result[i] = wrapType(t)
|
|
}
|
|
return result
|
|
}
|