From 6f8b70e970a7bdb1b3c837b21bd42e60b21e66bc Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 11 Feb 2025 01:12:32 -0800 Subject: [PATCH 01/15] Wire up type printing to use node builder, port most type printback logic excluding ID, JSX, and symbol names --- internal/ast/utilities.go | 4 +- internal/checker/checker.go | 9 +- internal/checker/nodebuilder.go | 1927 ++++++++++++++++++++ internal/checker/nodebuilderapi.go | 180 ++ internal/checker/nodebuilderscopes.go | 230 +++ internal/checker/printer.go | 813 ++------- internal/checker/relater.go | 8 +- internal/checker/symbolaccessibility.go | 76 + internal/checker/symboltracker.go | 107 ++ internal/checker/types.go | 33 + internal/printer/singlelinestringwriter.go | 1 + 11 files changed, 2763 insertions(+), 625 deletions(-) create mode 100644 internal/checker/nodebuilder.go create mode 100644 internal/checker/nodebuilderapi.go create mode 100644 internal/checker/nodebuilderscopes.go create mode 100644 internal/checker/symbolaccessibility.go create mode 100644 internal/checker/symboltracker.go diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 30a4d350b2..13173ddfd8 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -1435,10 +1435,10 @@ func IsDynamicName(name *Node) bool { } func IsEntityNameExpression(node *Node) bool { - return node.Kind == KindIdentifier || isPropertyAccessEntityNameExpression(node) + return node.Kind == KindIdentifier || IsPropertyAccessEntityNameExpression(node) } -func isPropertyAccessEntityNameExpression(node *Node) bool { +func IsPropertyAccessEntityNameExpression(node *Node) bool { if node.Kind == KindPropertyAccessExpression { expr := node.AsPropertyAccessExpression() return expr.Name().Kind == KindIdentifier && IsEntityNameExpression(expr.Expression) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 8bbc51ba9e..6b2ea1e290 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -821,6 +821,7 @@ type Checker struct { markNodeAssignments func(*ast.Node) bool emitResolver *emitResolver emitResolverOnce sync.Once + nodeBuilder NodeBuilderInterface _jsxNamespace string _jsxFactoryEntity *ast.Node } @@ -1027,6 +1028,8 @@ func NewChecker(program Program) *Checker { c.getGlobalClassAccessorDecoratorTargetType = c.getGlobalTypeResolver("ClassAccessorDecoratorTarget", 2 /*arity*/, true /*reportErrors*/) c.getGlobalClassAccessorDecoratorResultType = c.getGlobalTypeResolver("ClassAccessorDecoratorResult", 2 /*arity*/, true /*reportErrors*/) c.getGlobalClassFieldDecoratorContextType = c.getGlobalTypeResolver("ClassFieldDecoratorContext", 2 /*arity*/, true /*reportErrors*/) + diagnosticConstructionContext := printer.NewEmitContext() + c.nodeBuilder = NewNodeBuilderAPI(c, diagnosticConstructionContext) c.initializeClosures() c.initializeIterationResolvers() c.initializeChecker() @@ -17923,7 +17926,7 @@ func (c *Checker) areAllOuterTypeParametersApplied(t *Type) bool { } func (c *Checker) reportCircularBaseType(node *ast.Node, t *Type) { - c.error(node, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.typeToStringEx(t, nil, TypeFormatFlagsWriteArrayAsGenericType)) + c.error(node, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.typeToStringEx(t, nil, TypeFormatFlagsWriteArrayAsGenericType, nil)) } // A valid base type is `any`, an object type or intersection of object types. @@ -20196,11 +20199,11 @@ func (c *Checker) elaborateNeverIntersection(chain *ast.Diagnostic, node *ast.No if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsNeverIntersection != 0 { neverProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), c.isDiscriminantWithNeverType) if neverProp != nil { - return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction), c.symbolToString(neverProp)) + return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(neverProp)) } privateProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), isConflictingPrivateProperty) if privateProp != nil { - return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction), c.symbolToString(privateProp)) + return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(privateProp)) } } return chain diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go new file mode 100644 index 0000000000..9afee5843f --- /dev/null +++ b/internal/checker/nodebuilder.go @@ -0,0 +1,1927 @@ +package checker + +import ( + "fmt" + "slices" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" +) + +// NOTE: If modifying this enum, must modify `TypeFormatFlags` too! +// dprint-ignore +type NodeBuilderFlags int32 + +const ( + NodeBuilderFlagsNone NodeBuilderFlags = 0 + // Options + NodeBuilderFlagsNoTruncation NodeBuilderFlags = 1 << 0 + NodeBuilderFlagsWriteArrayAsGenericType NodeBuilderFlags = 1 << 1 + NodeBuilderFlagsGenerateNamesForShadowedTypeParams NodeBuilderFlags = 1 << 2 + NodeBuilderFlagsUseStructuralFallback NodeBuilderFlags = 1 << 3 + NodeBuilderFlagsForbidIndexedAccessSymbolReferences NodeBuilderFlags = 1 << 4 + NodeBuilderFlagsWriteTypeArgumentsOfSignature NodeBuilderFlags = 1 << 5 + NodeBuilderFlagsUseFullyQualifiedType NodeBuilderFlags = 1 << 6 + NodeBuilderFlagsUseOnlyExternalAliasing NodeBuilderFlags = 1 << 7 + NodeBuilderFlagsSuppressAnyReturnType NodeBuilderFlags = 1 << 8 + NodeBuilderFlagsWriteTypeParametersInQualifiedName NodeBuilderFlags = 1 << 9 + NodeBuilderFlagsMultilineObjectLiterals NodeBuilderFlags = 1 << 10 + NodeBuilderFlagsWriteClassExpressionAsTypeLiteral NodeBuilderFlags = 1 << 11 + NodeBuilderFlagsUseTypeOfFunction NodeBuilderFlags = 1 << 12 + NodeBuilderFlagsOmitParameterModifiers NodeBuilderFlags = 1 << 13 + NodeBuilderFlagsUseAliasDefinedOutsideCurrentScope NodeBuilderFlags = 1 << 14 + NodeBuilderFlagsUseSingleQuotesForStringLiteralType NodeBuilderFlags = 1 << 28 + NodeBuilderFlagsNoTypeReduction NodeBuilderFlags = 1 << 29 + NodeBuilderFlagsOmitThisParameter NodeBuilderFlags = 1 << 25 + // Error handling + NodeBuilderFlagsAllowThisInObjectLiteral NodeBuilderFlags = 1 << 15 + NodeBuilderFlagsAllowQualifiedNameInPlaceOfIdentifier NodeBuilderFlags = 1 << 16 + NodeBuilderFlagsAllowAnonymousIdentifier NodeBuilderFlags = 1 << 17 + NodeBuilderFlagsAllowEmptyUnionOrIntersection NodeBuilderFlags = 1 << 18 + NodeBuilderFlagsAllowEmptyTuple NodeBuilderFlags = 1 << 19 + NodeBuilderFlagsAllowUniqueESSymbolType NodeBuilderFlags = 1 << 20 + NodeBuilderFlagsAllowEmptyIndexInfoType NodeBuilderFlags = 1 << 21 + // Errors (cont.) + NodeBuilderFlagsAllowNodeModulesRelativePaths NodeBuilderFlags = 1 << 26 + NodeBuilderFlagsIgnoreErrors NodeBuilderFlags = NodeBuilderFlagsAllowThisInObjectLiteral | NodeBuilderFlagsAllowQualifiedNameInPlaceOfIdentifier | NodeBuilderFlagsAllowAnonymousIdentifier | NodeBuilderFlagsAllowEmptyUnionOrIntersection | NodeBuilderFlagsAllowEmptyTuple | NodeBuilderFlagsAllowEmptyIndexInfoType | NodeBuilderFlagsAllowNodeModulesRelativePaths + // State + NodeBuilderFlagsInObjectTypeLiteral NodeBuilderFlags = 1 << 22 + NodeBuilderFlagsInTypeAlias NodeBuilderFlags = 1 << 23 + NodeBuilderFlagsInInitialEntityName NodeBuilderFlags = 1 << 24 +) + +/** @internal */ +// dprint-ignore + +type InternalNodeBuilderFlags int32 + +const ( + InternalNodeBuilderFlagsNone InternalNodeBuilderFlags = 0 + InternalNodeBuilderFlagsWriteComputedProps InternalNodeBuilderFlags = 1 << 0 + InternalNodeBuilderFlagsNoSyntacticPrinter InternalNodeBuilderFlags = 1 << 1 + InternalNodeBuilderFlagsDoNotIncludeSymbolChain InternalNodeBuilderFlags = 1 << 2 + InternalNodeBuilderFlagsAllowUnresolvedNames InternalNodeBuilderFlags = 1 << 3 +) + +type CompositeSymbolIdentity struct { + isConstructorNode bool + symbolId ast.SymbolId + nodeId ast.NodeId +} + +type TrackedSymbolArgs struct { + symbol *ast.Symbol + enclosingDeclaration *ast.Node + meaning ast.SymbolFlags +} + +type SerializedTypeEntry struct { + node *ast.Node + truncating bool + addedLength int + trackedSymbols []*TrackedSymbolArgs +} + +type CompositeTypeCacheIdentity struct { + typeId TypeId + flags NodeBuilderFlags + internalFlags InternalNodeBuilderFlags +} + +type NodeBuilderLinks struct { + serializedTypes map[CompositeTypeCacheIdentity]*SerializedTypeEntry // Collection of types serialized at this location + fakeScopeForSignatureDeclaration *string // If present, this is a fake scope injected into an enclosing declaration chain. +} + +type NodeBuilderContext struct { + tracker SymbolTracker + approximateLength int + encounteredError bool + truncating bool + reportedDiagnostic bool + flags NodeBuilderFlags + internalFlags InternalNodeBuilderFlags + depth int + enclosingDeclaration *ast.Node + enclosingFile *ast.SourceFile + inferTypeParameters []*Type + visitedTypes map[TypeId]bool + symbolDepth map[CompositeSymbolIdentity]int + trackedSymbols []*TrackedSymbolArgs + mapper *TypeMapper + reverseMappedStack []*ast.Symbol + enclosingSymbolTypes map[ast.SymbolId]*Type + suppressReportInferenceFallback bool + + // per signature scope state + mustCreateTypeParameterSymbolList bool + mustCreateTypeParametersNamesLookups bool + typeParameterNames any + typeParameterNamesByText any + typeParameterNamesByTextNextNameCount any + typeParameterSymbolList any +} + +type NodeBuilder struct { + // host members + f *ast.NodeFactory + ch *Checker + e *printer.EmitContext + + // cache + links core.LinkStore[*ast.Node, NodeBuilderLinks] + + // closures + typeToTypeNodeClosure func(t *Type) *ast.TypeNode + typeReferenceToTypeNodeClosure func(t *Type) *ast.TypeNode + conditionalTypeToTypeNodeClosure func(t *Type) *ast.TypeNode + createTypeNodeFromObjectTypeClosure func(t *Type) *ast.TypeNode + isStringNamedClosure func(d *ast.Declaration) bool + isSingleQuotedStringNamedClosure func(d *ast.Declaration) bool + + // state + ctx *NodeBuilderContext +} + +const defaultMaximumTruncationLength = 160 +const noTruncationMaximumTruncationLength = 1_000_000 + +// Node builder utility functions + +// You probably don't mean to use this - use `NewNodeBuilderAPI` instead +func NewNodeBuilder(ch *Checker, e *printer.EmitContext) NodeBuilder { + result := NodeBuilder{f: e.Factory, ch: ch, e: e, typeToTypeNodeClosure: nil, typeReferenceToTypeNodeClosure: nil, conditionalTypeToTypeNodeClosure: nil, ctx: nil} + result.initializeClosures() + return result +} + +func (b *NodeBuilder) initializeClosures() { + b.typeToTypeNodeClosure = b.typeToTypeNode + b.typeReferenceToTypeNodeClosure = b.typeReferenceToTypeNode + b.conditionalTypeToTypeNodeClosure = b.conditionalTypeToTypeNode + b.createTypeNodeFromObjectTypeClosure = b.createTypeNodeFromObjectType + b.isStringNamedClosure = b.isStringNamed + b.isSingleQuotedStringNamedClosure = b.isSingleQuotedStringNamed +} + +func (b *NodeBuilder) saveRestoreFlags() func() { + flags := b.ctx.flags + internalFlags := b.ctx.internalFlags + depth := b.ctx.depth + + return func() { + b.ctx.flags = flags + b.ctx.internalFlags = internalFlags + b.ctx.depth = depth + } +} + +func (b *NodeBuilder) checkTruncationLength() bool { + if b.ctx.truncating { + return b.ctx.truncating + } + b.ctx.truncating = b.ctx.approximateLength > (core.IfElse((b.ctx.flags&NodeBuilderFlagsNoTruncation != 0), noTruncationMaximumTruncationLength, defaultMaximumTruncationLength)) + return b.ctx.truncating +} + +func (b *NodeBuilder) appendReferenceToType(root *ast.TypeNode, ref *ast.TypeNode) *ast.TypeNode { + if ast.IsImportTypeNode(root) { + // first shift type arguments + + // !!! In the old emitter, an Identifier could have type arguments for use with quickinfo: + // typeArguments := root.TypeArguments + // qualifier := root.AsImportTypeNode().Qualifier + // if qualifier != nil { + // if ast.IsIdentifier(qualifier) { + // if typeArguments != getIdentifierTypeArguments(qualifier) { + // qualifier = setIdentifierTypeArguments(b.f.CloneNode(qualifier), typeArguments) + // } + // } else { + // if typeArguments != getIdentifierTypeArguments(qualifier.Right) { + // qualifier = b.f.UpdateQualifiedName(qualifier, qualifier.Left, setIdentifierTypeArguments(b.f.cloneNode(qualifier.Right), typeArguments)) + // } + // } + // } + // !!! Without the above, nested type args are silently elided + imprt := root.AsImportTypeNode() + // then move qualifiers + ids := getAccessStack(ref) + var qualifier *ast.Node + for _, id := range ids { + if qualifier != nil { + qualifier = b.f.NewQualifiedName(qualifier, id) + } else { + qualifier = id + } + } + return b.f.UpdateImportTypeNode(imprt, imprt.IsTypeOf, imprt.Argument, imprt.Attributes, qualifier, ref.AsTypeReferenceNode().TypeArguments) + } else { + // first shift type arguments + // !!! In the old emitter, an Identifier could have type arguments for use with quickinfo: + // typeArguments := root.TypeArguments + // typeName := root.AsTypeReferenceNode().TypeName + // if ast.IsIdentifier(typeName) { + // if typeArguments != getIdentifierTypeArguments(typeName) { + // typeName = setIdentifierTypeArguments(b.f.cloneNode(typeName), typeArguments) + // } + // } else { + // if typeArguments != getIdentifierTypeArguments(typeName.Right) { + // typeName = b.f.UpdateQualifiedName(typeName, typeName.Left, setIdentifierTypeArguments(b.f.cloneNode(typeName.Right), typeArguments)) + // } + // } + // !!! Without the above, nested type args are silently elided + // then move qualifiers + ids := getAccessStack(ref) + var typeName *ast.Node = root.AsTypeReferenceNode().TypeName + for _, id := range ids { + typeName = b.f.NewQualifiedName(typeName, id) + } + return b.f.UpdateTypeReferenceNode(root.AsTypeReferenceNode(), typeName, ref.AsTypeReferenceNode().TypeArguments) + } +} + +func getAccessStack(ref *ast.Node) []*ast.Node { + var state *ast.Node = ref.AsTypeReferenceNode().TypeName + ids := []*ast.Node{} + for !ast.IsIdentifier(state) { + entity := state.AsQualifiedName() + ids = append([]*ast.Node{entity.Right}, ids...) + state = entity.Left + } + ids = append([]*ast.Node{state}, ids...) + return ids +} + +func isClassInstanceSide(c *Checker, t *Type) bool { + return t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 && (t == c.getDeclaredTypeOfClassOrInterface(t.symbol) || (t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsIsClassInstanceClone != 0)) +} + +func (b *NodeBuilder) createElidedInformationPlaceholder() *ast.TypeNode { + b.ctx.approximateLength += 3 + if b.ctx.flags&NodeBuilderFlagsNoTruncation == 0 { + return b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/) + } + // addSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided") // !!! + return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) +} + +func (b *NodeBuilder) mapToTypeNodes(list []*Type) *ast.NodeList { + if len(list) == 0 { + return nil + } + contents := core.Map(list, b.typeToTypeNodeClosure) + return b.f.NewNodeList(contents) +} + +func (b *NodeBuilder) setCommentRange(node *ast.Node, range_ *ast.Node) { + if range_ != nil && b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(range_) { + // Copy comments to node for declaration emit + b.e.AssignCommentRange(node, range_) + } +} + +func (b *NodeBuilder) tryReuseExistingTypeNodeHelper(existing *ast.TypeNode) *ast.TypeNode { + return nil // !!! +} + +func containsNonMissingUndefinedType(c *Checker, t *Type) bool { + var candidate *Type + if t.flags&TypeFlagsUnion != 0 { + candidate = t.AsUnionType().types[0] + } else { + candidate = t + } + return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType +} + +func (b *NodeBuilder) tryReuseExistingTypeNode(typeNode *ast.TypeNode, t *Type, host *ast.Node, addUndefined bool) *ast.TypeNode { + originalType := t + if addUndefined { + t = b.ch.getOptionalType(t, !ast.IsParameter(host)) + } + clone := b.tryReuseExistingNonParameterTypeNode(typeNode, t, host, nil) + if clone != nil { + // explicitly add `| undefined` if it's missing from the input type nodes and the type contains `undefined` (and not the missing type) + if addUndefined && containsNonMissingUndefinedType(b.ch, t) && !someType(b.getTypeFromTypeNode(typeNode, false), func(t *Type) bool { + return t.flags&TypeFlagsUndefined != 0 + }) { + return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{clone, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)})) + } + return clone + } + if addUndefined && originalType != t { + cloneMissingUndefined := b.tryReuseExistingNonParameterTypeNode(typeNode, originalType, host, nil) + if cloneMissingUndefined != nil { + return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{cloneMissingUndefined, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)})) + } + } + return nil +} + +func (b *NodeBuilder) typeNodeIsEquivalentToType(annotatedDeclaration *ast.Node, t *Type, typeFromTypeNode *Type) bool { + if typeFromTypeNode == t { + return true + } + if annotatedDeclaration == nil { + return false + } + // !!! + // used to be hasEffectiveQuestionToken for JSDoc + if isOptionalDeclaration(annotatedDeclaration) { + return b.ch.getTypeWithFacts(t, TypeFactsNEUndefined) == typeFromTypeNode + } + return false +} + +func (b *NodeBuilder) existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing *ast.TypeNode, t *Type) bool { + // In JS, you can say something like `Foo` and get a `Foo` implicitly - we don't want to preserve that original `Foo` in these cases, though. + if t.objectFlags&ObjectFlagsReference == 0 { + return true + } + if !ast.IsTypeReferenceNode(existing) { + return true + } + // `type` is a reference type, and `existing` is a type reference node, but we still need to make sure they refer to the _same_ target type + // before we go comparing their type argument counts. + b.ch.getTypeFromTypeReference(existing) + // call to ensure symbol is resolved + links := b.ch.symbolNodeLinks.TryGet(existing) + if links == nil { + return true + } + symbol := links.resolvedSymbol + if symbol == nil { + return true + } + existingTarget := b.ch.getDeclaredTypeOfSymbol(symbol) + if existingTarget == nil || existingTarget != t.AsTypeReference().target { + return true + } + return len(existing.TypeArguments()) >= b.ch.getMinTypeArgumentCount(t.AsTypeReference().target.AsInterfaceType().TypeParameters()) +} + +func (b *NodeBuilder) tryReuseExistingNonParameterTypeNode(existing *ast.TypeNode, t *Type, host *ast.Node, annotationType *Type) *ast.TypeNode { + if host == nil { + host = b.ctx.enclosingDeclaration + } + if annotationType == nil { + annotationType = b.getTypeFromTypeNode(existing, true) + } + if annotationType != nil && b.typeNodeIsEquivalentToType(host, t, annotationType) && b.existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, t) { + result := b.tryReuseExistingTypeNodeHelper(existing) + if result != nil { + return result + } + } + return nil +} + +func (b *NodeBuilder) getResolvedTypeWithoutAbstractConstructSignatures(t *StructuredType) *Type { + if len(t.ConstructSignatures()) == 0 { + return t.AsType() + } + if t.objectTypeWithoutAbstractConstructSignatures != nil { + return t.objectTypeWithoutAbstractConstructSignatures + } + constructSignatures := core.Filter(t.ConstructSignatures(), func(signature *Signature) bool { + return signature.flags&SignatureFlagsAbstract == 0 + }) + if len(constructSignatures) == len(t.ConstructSignatures()) { + t.objectTypeWithoutAbstractConstructSignatures = t.AsType() + return t.AsType() + } + typeCopy := b.ch.newAnonymousType(t.symbol, t.members, t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.indexInfos) + t.objectTypeWithoutAbstractConstructSignatures = typeCopy + typeCopy.AsStructuredType().objectTypeWithoutAbstractConstructSignatures = typeCopy + return typeCopy +} + +func (b *NodeBuilder) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, false bool) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolToEntityNameNode(symbol *ast.Symbol) *ast.EntityName { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, typeArguments *ast.NodeList) *ast.TypeNode { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlags) *ast.Expression { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) typeParameterToDeclarationWithConstraint(typeParameter *Type, constraintNode *ast.TypeNode) *ast.TypeParameterDeclarationNode { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) typeParameterToName(typeParameter *Type) *ast.Identifier { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) createMappedTypeNodeFromType(type_ *Type) *ast.TypeNode { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) typePredicateToTypePredicateNode(predicate *TypePredicate) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) typeParameterToDeclaration(parameter *Type) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolToTypeParameterDeclarations(symbol *ast.Symbol) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolToParameterDeclaration(symbol *ast.Symbol, preserveModifierFlags bool) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable) []*ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) serializeTypeForExpression(expr *ast.Node) *ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) serializeInferredReturnTypeForSignature(signature *Signature, returnType *Type) *ast.Node { + oldSuppressReportInferenceFallback := b.ctx.suppressReportInferenceFallback + b.ctx.suppressReportInferenceFallback = true + typePredicate := b.ch.getTypePredicateOfSignature(signature) + var returnTypeNode *ast.Node + if typePredicate != nil { + var predicate *TypePredicate + if b.ctx.mapper != nil { + predicate = b.ch.instantiateTypePredicate(typePredicate, b.ctx.mapper) + } else { + predicate = typePredicate + } + returnTypeNode = b.typePredicateToTypePredicateNodeHelper(predicate) + } else { + returnTypeNode = b.typeToTypeNodeClosure(returnType) + } + b.ctx.suppressReportInferenceFallback = oldSuppressReportInferenceFallback + return returnTypeNode +} + +func (b *NodeBuilder) typePredicateToTypePredicateNodeHelper(typePredicate *TypePredicate) *ast.Node { + var assertsModifier *ast.Node + if typePredicate.kind == TypePredicateKindAssertsThis || typePredicate.kind == TypePredicateKindAssertsIdentifier { + assertsModifier = b.f.NewToken(ast.KindAssertsKeyword) + } else { + assertsModifier = nil + } + var parameterName *ast.Node + if typePredicate.kind == TypePredicateKindIdentifier || typePredicate.kind == TypePredicateKindAssertsIdentifier { + parameterName = b.f.NewIdentifier(typePredicate.parameterName) + b.e.SetEmitFlags(parameterName, printer.EFNoAsciiEscaping) + } else { + parameterName = b.f.NewThisTypeNode() + } + var typeNode *ast.Node + if typePredicate.t != nil { + typeNode = b.typeToTypeNodeClosure(typePredicate.t) + } + return b.f.NewTypePredicateNode(assertsModifier, parameterName, typeNode) +} + +type SignatureToSignatureDeclarationOptions struct { + modifiers []*ast.Node + name *ast.PropertyName + questionToken *ast.Node +} + +func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature, kind ast.Kind, options *SignatureToSignatureDeclarationOptions) *ast.Node { + var typeParameters *[]*ast.Node + var typeArguments *[]*ast.Node + + expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0] + cleanup := b.enterNewScope(signature.declaration, &expandedParams, &signature.typeParameters, &signature.parameters, signature.mapper) + b.ctx.approximateLength += 3 + // Usually a signature contributes a few more characters than this, but 3 is the minimum + + if b.ctx.flags&NodeBuilderFlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && signature.target.typeParameters != nil { + for _, parameter := range signature.target.typeParameters { + node := b.typeToTypeNodeClosure(b.ch.instantiateType(parameter, signature.mapper)) + if typeArguments == nil { + typeArguments = &[]*ast.Node{} + } + args := append(*typeArguments, node) + typeArguments = &args + } + } else if signature.typeParameters != nil { + for _, parameter := range signature.typeParameters { + node := b.typeParameterToDeclaration(parameter) + if typeParameters == nil { + typeParameters = &[]*ast.Node{} + } + args := append(*typeParameters, node) + typeParameters = &args + } + } + + restoreFlags := b.saveRestoreFlags() + b.ctx.flags &^= NodeBuilderFlagsSuppressAnyReturnType + // If the expanded parameter list had a variadic in a non-trailing position, don't expand it + parameters := core.Map(core.IfElse(core.Some(expandedParams, func(p *ast.Symbol) bool { + return p != expandedParams[len(expandedParams)-1] && p.CheckFlags&ast.CheckFlagsRestParameter != 0 + }), signature.parameters, expandedParams), func(parameter *ast.Symbol) *ast.Node { + return b.symbolToParameterDeclaration(parameter, kind == ast.KindConstructor) + }) + var thisParameter *ast.Node + if b.ctx.flags&NodeBuilderFlagsOmitThisParameter != 0 { + thisParameter = nil + } else { + thisParameter = b.tryGetThisParameterDeclaration(signature) + } + if thisParameter != nil { + parameters = append([]*ast.Node{thisParameter}, parameters...) + } + restoreFlags() + + returnTypeNode := b.serializeReturnTypeForSignature(signature) + + var modifiers []*ast.Node + if options != nil { + modifiers = options.modifiers + } + if (kind == ast.KindConstructorType) && signature.flags&SignatureFlagsAbstract != 0 { + flags := ast.ModifiersToFlags(modifiers) + modifiers = createModifiersFromModifierFlags(flags|ast.ModifierFlagsAbstract, b.f.NewModifier) + } + + paramList := b.f.NewNodeList(parameters) + var typeParamList *ast.NodeList + if typeParameters != nil { + typeParamList = b.f.NewNodeList(*typeParameters) + } + var modifierList *ast.ModifierList + if modifiers != nil && len(modifiers) > 0 { + modifierList = b.f.NewModifierList(modifiers) + } + var name *ast.Node + if options != nil { + name = options.name + } + if name == nil { + name = b.f.NewIdentifier("") + } + + var node *ast.Node + switch { + case kind == ast.KindCallSignature: + node = b.f.NewCallSignatureDeclaration(typeParamList, paramList, returnTypeNode) + case kind == ast.KindConstructSignature: + node = b.f.NewConstructSignatureDeclaration(typeParamList, paramList, returnTypeNode) + case kind == ast.KindMethodSignature: + var questionToken *ast.Node + if options != nil { + questionToken = options.questionToken + } + node = b.f.NewMethodSignatureDeclaration(modifierList, name, questionToken, typeParamList, paramList, returnTypeNode) + case kind == ast.KindMethodDeclaration: + node = b.f.NewMethodDeclaration(modifierList, nil /*asteriskToken*/, name, nil /*questionToken*/, typeParamList, paramList, returnTypeNode, nil /*body*/) + case kind == ast.KindConstructor: + node = b.f.NewConstructorDeclaration(modifierList, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*body*/) + case kind == ast.KindGetAccessor: + node = b.f.NewGetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, returnTypeNode, nil /*body*/) + case kind == ast.KindSetAccessor: + node = b.f.NewSetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*body*/) + case kind == ast.KindIndexSignature: + node = b.f.NewIndexSignatureDeclaration(modifierList, paramList, returnTypeNode) + // !!! JSDoc Support + // case kind == ast.KindJSDocFunctionType: + // node = b.f.NewJSDocFunctionType(parameters, returnTypeNode) + case kind == ast.KindFunctionType: + if returnTypeNode == nil { + returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil) + } + node = b.f.NewFunctionTypeNode(typeParamList, paramList, returnTypeNode) + case kind == ast.KindConstructorType: + if returnTypeNode == nil { + returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil) + } + node = b.f.NewConstructorTypeNode(modifierList, typeParamList, paramList, returnTypeNode) + case kind == ast.KindFunctionDeclaration: + // TODO: assert name is Identifier + node = b.f.NewFunctionDeclaration(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*body*/) + case kind == ast.KindFunctionExpression: + // TODO: assert name is Identifier + node = b.f.NewFunctionExpression(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false)) + case kind == ast.KindArrowFunction: + node = b.f.NewArrowFunction(modifierList, typeParamList, paramList, returnTypeNode, nil /*equalsGreaterThanToken*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false)) + default: + panic("Unhandled kind in signatureToSignatureDeclarationHelper") + } + + // !!! TODO: Smuggle type arguments of signatures out for quickinfo + // if typeArguments != nil { + // node.TypeArguments = b.f.NewNodeList(typeArguments) + // } + // !!! TODO: synthetic comment support + // if signature.declaration. /* ? */ kind == ast.KindJSDocSignature && signature.declaration.Parent.Kind == ast.KindJSDocOverloadTag { + // comment := getTextOfNode(signature.declaration.Parent.Parent, true /*includeTrivia*/).slice(2, -2).split(regexp.MustParse(`\r\n|\n|\r`)).map_(func(line string) string { + // return line.replace(regexp.MustParse(`^\s+`), " ") + // }).join("\n") + // addSyntheticLeadingComment(node, ast.KindMultiLineCommentTrivia, comment, true /*hasTrailingNewLine*/) + // } + + cleanup() + return node +} + +func createModifiersFromModifierFlags(flags ast.ModifierFlags, createModifier func(kind ast.Kind) *ast.Node) []*ast.Node { + var result []*ast.Node + if flags&ast.ModifierFlagsExport != 0 { + result = append(result, createModifier(ast.KindExportKeyword)) + } + if flags&ast.ModifierFlagsAmbient != 0 { + result = append(result, createModifier(ast.KindDeclareKeyword)) + } + if flags&ast.ModifierFlagsDefault != 0 { + result = append(result, createModifier(ast.KindDefaultKeyword)) + } + if flags&ast.ModifierFlagsConst != 0 { + result = append(result, createModifier(ast.KindConstKeyword)) + } + if flags&ast.ModifierFlagsPublic != 0 { + result = append(result, createModifier(ast.KindPublicKeyword)) + } + if flags&ast.ModifierFlagsPrivate != 0 { + result = append(result, createModifier(ast.KindPrivateKeyword)) + } + if flags&ast.ModifierFlagsProtected != 0 { + result = append(result, createModifier(ast.KindProtectedKeyword)) + } + if flags&ast.ModifierFlagsAbstract != 0 { + result = append(result, createModifier(ast.KindAbstractKeyword)) + } + if flags&ast.ModifierFlagsStatic != 0 { + result = append(result, createModifier(ast.KindStaticKeyword)) + } + if flags&ast.ModifierFlagsOverride != 0 { + result = append(result, createModifier(ast.KindOverrideKeyword)) + } + if flags&ast.ModifierFlagsReadonly != 0 { + result = append(result, createModifier(ast.KindReadonlyKeyword)) + } + if flags&ast.ModifierFlagsAccessor != 0 { + result = append(result, createModifier(ast.KindAccessorKeyword)) + } + if flags&ast.ModifierFlagsAsync != 0 { + result = append(result, createModifier(ast.KindAsyncKeyword)) + } + if flags&ast.ModifierFlagsIn != 0 { + result = append(result, createModifier(ast.KindInKeyword)) + } + if flags&ast.ModifierFlagsOut != 0 { + result = append(result, createModifier(ast.KindOutKeyword)) + } + return result +} + +func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) [][]*ast.Symbol { + if signatureHasRestParameter(sig) { + restIndex := len(sig.parameters) - 1 + restSymbol := sig.parameters[restIndex] + restType := c.getTypeOfSymbol(restSymbol) + getUniqAssociatedNamesFromTupleType := func(t *Type, restSymbol *ast.Symbol) []string { + names := core.MapIndex(t.AsTupleType().elementInfos, func(info TupleElementInfo, i int) string { + return c.getTupleElementLabel(info, restSymbol, i) + }) + if len(names) > 0 { + duplicates := []int{} + uniqueNames := make(map[string]bool) + for i := 0; i < len(names); i++ { + name := names[i] + _, ok := uniqueNames[name] + if ok { + duplicates = append(duplicates, i) + } else { + uniqueNames[name] = true + } + } + counters := make(map[string]int) + for _, i := range duplicates { + counter, ok := counters[names[i]] + if !ok { + counter = 1 + } + var name string + for true { + name = fmt.Sprintf("{}_{}", names[i], counter) + _, ok := uniqueNames[name] + if ok { + counter++ + continue + } else { + uniqueNames[name] = true + break + } + } + names[i] = name + counters[names[i]] = counter + 1 + } + } + return names + } + expandSignatureParametersWithTupleMembers := func(restType *Type, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol { + elementTypes := c.getTypeArguments(restType) + associatedNames := getUniqAssociatedNamesFromTupleType(restType, restSymbol) + restParams := core.MapIndex(elementTypes, func(t *Type, i int) *ast.Symbol { + // Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name + // TODO: getTupleElementLabel can no longer fail, investigate if this lack of falliability meaningfully changes output + // var name *string + // if associatedNames != nil && associatedNames[i] != nil { + // name = associatedNames[i] + // } else { + // name = c.getParameterNameAtPosition(sig, restIndex+i, restType) + // } + name := associatedNames[i] + flags := restType.AsTupleType().elementInfos[i].flags + var checkFlags ast.CheckFlags + switch { + case flags&ElementFlagsVariable != 0: + checkFlags = ast.CheckFlagsRestParameter + case flags&ElementFlagsOptional != 0: + checkFlags = ast.CheckFlagsOptionalParameter + default: + checkFlags = 0 + } + symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, name, checkFlags) + links := c.valueSymbolLinks.Get(symbol) + if flags&ElementFlagsRest != 0 { + links.resolvedType = c.createArrayType(t) + } else { + links.resolvedType = t + } + return symbol + }) + return core.Concatenate(sig.parameters[0:restIndex], restParams) + } + + if isTupleType(restType) { + return [][]*ast.Symbol{expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)} + } else if !skipUnionExpanding && restType.flags&TypeFlagsUnion != 0 && core.Every(restType.AsUnionType().types, isTupleType) { + return core.Map(restType.AsUnionType().types, func(t *Type) []*ast.Symbol { + return expandSignatureParametersWithTupleMembers(t, restIndex, restSymbol) + }) + } + } + return [][]*ast.Symbol{sig.parameters} + +} + +func (b *NodeBuilder) tryGetThisParameterDeclaration(signature *Signature) *ast.Node { + if signature.thisParameter != nil { + return b.symbolToParameterDeclaration(signature.thisParameter, false) + } + if signature.declaration != nil && ast.IsInJSFile(signature.declaration) { + // !!! JSDoc Support + // thisTag := getJSDocThisTag(signature.declaration) + // if (thisTag && thisTag.typeExpression) { + // return factory.createParameterDeclaration( + // /*modifiers*/ undefined, + // /*dotDotDotToken*/ undefined, + // "this", + // /*questionToken*/ undefined, + // typeToTypeNodeHelper(getTypeFromTypeNode(context, thisTag.typeExpression), context), + // ); + // } + } + return nil +} + +/** +* Serializes the return type of the signature by first trying to use the syntactic printer if possible and falling back to the checker type if not. + */ +func (b *NodeBuilder) serializeReturnTypeForSignature(signature *Signature) *ast.Node { + suppressAny := b.ctx.flags&NodeBuilderFlagsSuppressAnyReturnType != 0 + restoreFlags := b.saveRestoreFlags() + if suppressAny { + b.ctx.flags &= ^NodeBuilderFlagsSuppressAnyReturnType // suppress only toplevel `any`s + } + var returnTypeNode *ast.Node + + returnType := b.ch.getReturnTypeOfSignature(signature) + if !(suppressAny && IsTypeAny(returnType)) { + // !!! IsolatedDeclaration support + // if signature.declaration != nil && !ast.NodeIsSynthesized(signature.declaration) { + // declarationSymbol := b.ch.getSymbolOfDeclaration(signature.declaration) + // restore := addSymbolTypeToContext(declarationSymbol, returnType) + // returnTypeNode = syntacticNodeBuilder.serializeReturnTypeForSignature(signature.declaration, declarationSymbol) + // restore() + // } + if returnTypeNode == nil { + returnTypeNode = b.serializeInferredReturnTypeForSignature(signature, returnType) + } + } + + if returnTypeNode == nil && !suppressAny { + returnTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + restoreFlags() + return returnTypeNode +} + +func (b *NodeBuilder) indexInfoToIndexSignatureDeclarationHelper(indexInfo *IndexInfo, typeNode *ast.TypeNode) *ast.Node { + name := getNameFromIndexInfo(indexInfo) + indexerTypeNode := b.typeToTypeNodeClosure(indexInfo.keyType) + + indexingParameter := b.f.NewParameterDeclaration(nil, nil, b.f.NewIdentifier(name), nil, indexerTypeNode, nil) + if typeNode == nil { + if indexInfo.valueType == nil { + typeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } else { + typeNode = b.typeToTypeNodeClosure(indexInfo.valueType) + } + } + if indexInfo.valueType == nil && b.ctx.flags&NodeBuilderFlagsAllowEmptyIndexInfoType == 0 { + b.ctx.encounteredError = true + } + b.ctx.approximateLength += len(name) + 4 + var modifiers *ast.ModifierList + if indexInfo.isReadonly { + b.ctx.approximateLength += 9 + modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)}) + } + return b.f.NewIndexSignatureDeclaration(modifiers, b.f.NewNodeList([]*ast.Node{indexingParameter}), typeNode) +} + +/** +* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag +* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym` +* @param declaration - The preferred declaration to pull existing type nodes from (the symbol will be used as a fallback to find any annotated declaration) +* @param type - The type to write; an existing annotation must match this type if it's used, otherwise this is the type serialized as a new type node +* @param symbol - The symbol is used both to find an existing annotation if declaration is not provided, and to determine if `unique symbol` should be printed + */ +func (b *NodeBuilder) serializeTypeForDeclaration(declaration *ast.Declaration, t *Type, symbol *ast.Symbol) *ast.Node { + // !!! node reuse logic + restoreFlags := b.saveRestoreFlags() + if t.flags&TypeFlagsUniqueESSymbol != 0 && t.symbol == symbol && (b.ctx.enclosingDeclaration == nil || core.Some(symbol.Declarations, func(d *ast.Declaration) bool { + return ast.GetSourceFileOfNode(d) == b.ctx.enclosingFile + })) { + b.ctx.flags |= NodeBuilderFlagsAllowUniqueESSymbolType + } + result := b.typeToTypeNodeClosure(t) // !!! expressionOrTypeToTypeNode + restoreFlags() + return result +} + +const MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH = 3 + +func (b *NodeBuilder) shouldUsePlaceholderForProperty(propertySymbol *ast.Symbol) bool { + // Use placeholders for reverse mapped types we've either + // (1) already descended into, or + // (2) are nested reverse mappings within a mapping over a non-anonymous type, or + // (3) are deeply nested properties that originate from the same mapped type. + // Condition (2) is a restriction mostly just to + // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`. + // Since anonymous types usually come from expressions, this allows us to preserve the output + // for deep mappings which likely come from expressions, while truncating those parts which + // come from mappings over library functions. + // Condition (3) limits printing of possibly infinitely deep reverse mapped types. + if propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped == 0 { + return false + } + // (1) + for _, elem := range b.ctx.reverseMappedStack { + if elem == propertySymbol { + return true + } + } + // (2) + if len(b.ctx.reverseMappedStack) > 0 { + last := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1] + if b.ch.ReverseMappedSymbolLinks.Has(last) { + links := b.ch.ReverseMappedSymbolLinks.TryGet(last) + propertyType := links.propertyType + if propertyType != nil && propertyType.objectFlags&ObjectFlagsAnonymous == 0 { + return true + } + } + } + // (3) - we only inspect the last MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH elements of the + // stack for approximate matches to catch tight infinite loops + // TODO: Why? Reasoning lost to time. this could probably stand to be improved? + if len(b.ctx.reverseMappedStack) < MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { + return false + } + if !b.ch.ReverseMappedSymbolLinks.Has(propertySymbol) { + return false + } + propertyLinks := b.ch.ReverseMappedSymbolLinks.TryGet(propertySymbol) + propMappedType := propertyLinks.mappedType + if propMappedType == nil || propMappedType.symbol == nil { + return false + } + for i := range b.ctx.reverseMappedStack { + if i > MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { + break + } + prop := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1-i] + if b.ch.ReverseMappedSymbolLinks.Has(prop) { + links := b.ch.ReverseMappedSymbolLinks.TryGet(prop) + mappedType := links.mappedType + if mappedType != nil && mappedType.symbol == propMappedType.symbol { + return true + } + } + } + return false +} + +func (b *NodeBuilder) trackComputedName(accessExpression *ast.Node, enclosingDeclaration *ast.Node) { + // get symbol of the first identifier of the entityName + firstIdentifier := ast.GetFirstIdentifier(accessExpression) + name := b.ch.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false) + if name != nil { + b.ctx.tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue) + } +} + +func (b *NodeBuilder) createPropertyNameNodeForIdentifierOrLiteral(name string, target core.ScriptTarget, _singleQuote bool, stringNamed bool, isMethod bool) *ast.Node { + isMethodNamedNew := isMethod && name == "new" + if !isMethodNamedNew && scanner.IsIdentifierText(name, target) { + return b.f.NewIdentifier(name) + } + if !stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 { + return b.f.NewNumericLiteral(name) + } + result := b.f.NewStringLiteral(name) + // !!! TODO: set singleQuote + return result +} + +func (b *NodeBuilder) isStringNamed(d *ast.Declaration) bool { + name := ast.GetNameOfDeclaration(d) + if name == nil { + return false + } + if ast.IsComputedPropertyName(name) { + t := b.ch.checkExpression(name.AsComputedPropertyName().Expression) + return t.flags&TypeFlagsStringLike != 0 + } + if ast.IsElementAccessExpression(name) { + t := b.ch.checkExpression(name.AsElementAccessExpression().ArgumentExpression) + return t.flags&TypeFlagsStringLike != 0 + } + return ast.IsStringLiteral(name) +} + +func (b *NodeBuilder) isSingleQuotedStringNamed(d *ast.Declaration) bool { + return false // !!! + // TODO: actually support single-quote-style-maintenance + // name := ast.GetNameOfDeclaration(d) + // return name != nil && ast.IsStringLiteral(name) && (name.AsStringLiteral().SingleQuote || !nodeIsSynthesized(name) && startsWith(getTextOfNode(name, false /*includeTrivia*/), "'")) +} + +func (b *NodeBuilder) getPropertyNameNodeForSymbol(symbol *ast.Symbol) *ast.Node { + stringNamed := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isStringNamedClosure) + singleQuote := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isSingleQuotedStringNamedClosure) + isMethod := symbol.Flags&ast.SymbolFlagsMethod != 0 + fromNameType := b.getPropertyNameNodeForSymbolFromNameType(symbol, singleQuote, stringNamed, isMethod) + if fromNameType != nil { + return fromNameType + } + return b.createPropertyNameNodeForIdentifierOrLiteral(symbol.Name, b.ch.compilerOptions.GetEmitScriptTarget(), singleQuote, stringNamed, isMethod) +} + +// See getNameForSymbolFromNameType for a stringy equivalent +func (b *NodeBuilder) getPropertyNameNodeForSymbolFromNameType(symbol *ast.Symbol, singleQuote bool, stringNamed bool, isMethod bool) *ast.Node { + if !b.ch.valueSymbolLinks.Has(symbol) { + return nil + } + nameType := b.ch.valueSymbolLinks.TryGet(symbol).nameType + if nameType == nil { + return nil + } + if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + name := nameType.AsLiteralType().value.(string) + if !scanner.IsIdentifierText(name, b.ch.compilerOptions.GetEmitScriptTarget()) && (stringNamed || !isNumericLiteralName(name)) { + // !!! TODO: set singleQuote + return b.f.NewStringLiteral(name) + } + if isNumericLiteralName(name) && name[0] == '-' { + return b.f.NewComputedPropertyName(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(name[1:]))) + } + return b.createPropertyNameNodeForIdentifierOrLiteral(name, b.ch.compilerOptions.GetEmitScriptTarget(), singleQuote, stringNamed, isMethod) + } + if nameType.flags&TypeFlagsUniqueESSymbol != 0 { + return b.f.NewComputedPropertyName(b.symbolToExpression(nameType.AsUniqueESSymbolType().symbol, ast.SymbolFlagsValue)) + } + return nil +} + +func (b *NodeBuilder) addPropertyToElementList(propertySymbol *ast.Symbol, typeElements []*ast.TypeElement) []*ast.TypeElement { + propertyIsReverseMapped := propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped != 0 + var propertyType *Type + if b.shouldUsePlaceholderForProperty(propertySymbol) { + propertyType = b.ch.anyType + } else { + propertyType = b.ch.getNonMissingTypeOfSymbol(propertySymbol) + } + saveEnclosingDeclaration := b.ctx.enclosingDeclaration + b.ctx.enclosingDeclaration = nil + if isLateBoundName(propertySymbol.Name) { + if len(propertySymbol.Declarations) > 0 { + decl := propertySymbol.Declarations[0] + if b.ch.hasLateBindableName(decl) { + if ast.IsBinaryExpression(decl) { + name := ast.GetNameOfDeclaration(decl) + if name != nil && ast.IsElementAccessExpression(name) && ast.IsPropertyAccessEntityNameExpression(name.AsElementAccessExpression().ArgumentExpression) { + b.trackComputedName(name.AsElementAccessExpression().ArgumentExpression, saveEnclosingDeclaration) + } + } else { + b.trackComputedName(decl.Name().Expression(), saveEnclosingDeclaration) + } + } + } else { + b.ctx.tracker.ReportNonSerializableProperty(b.ch.symbolToString(propertySymbol)) + } + } + if propertySymbol.ValueDeclaration != nil { + b.ctx.enclosingDeclaration = propertySymbol.ValueDeclaration + } else if len(propertySymbol.Declarations) > 0 && propertySymbol.Declarations[0] != nil { + b.ctx.enclosingDeclaration = propertySymbol.Declarations[0] + } else { + b.ctx.enclosingDeclaration = saveEnclosingDeclaration + } + propertyName := b.getPropertyNameNodeForSymbol(propertySymbol) + b.ctx.enclosingDeclaration = saveEnclosingDeclaration + b.ctx.approximateLength += len(ast.SymbolName(propertySymbol)) + 1 + + if propertySymbol.Flags&ast.SymbolFlagsAccessor != 0 { + writeType := b.ch.getWriteTypeOfSymbol(propertySymbol) + if propertyType != writeType && !b.ch.isErrorType(propertyType) && !b.ch.isErrorType(writeType) { + getterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindGetAccessor) + getterSignature := b.ch.getSignatureFromDeclaration(getterDeclaration) + getter := b.signatureToSignatureDeclarationHelper(getterSignature, ast.KindGetAccessor, &SignatureToSignatureDeclarationOptions{ + name: propertyName, + }) + b.setCommentRange(getter, getterDeclaration) + typeElements = append(typeElements, getter) + setterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindSetAccessor) + setterSignature := b.ch.getSignatureFromDeclaration(setterDeclaration) + setter := b.signatureToSignatureDeclarationHelper(setterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{ + name: propertyName, + }) + b.setCommentRange(setter, setterDeclaration) + typeElements = append(typeElements, setter) + return typeElements + } + } + + var optionalToken *ast.Node + if propertySymbol.Flags&ast.SymbolFlagsOptional != 0 { + optionalToken = b.f.NewToken(ast.KindQuestionToken) + } else { + optionalToken = nil + } + if propertySymbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 && len(b.ch.getPropertiesOfObjectType(propertyType)) == 0 && !b.ch.isReadonlySymbol(propertySymbol) { + signatures := b.ch.getSignaturesOfType(b.ch.filterType(propertyType, func(t *Type) bool { + return t.flags&TypeFlagsUndefined == 0 + }), SignatureKindCall) + for _, signature := range signatures { + methodDeclaration := b.signatureToSignatureDeclarationHelper(signature, ast.KindMethodSignature, &SignatureToSignatureDeclarationOptions{ + name: propertyName, + questionToken: optionalToken, + }) + b.setCommentRange(methodDeclaration, propertySymbol.ValueDeclaration) // !!! missing JSDoc support formerly provided by preserveCommentsOn + typeElements = append(typeElements, methodDeclaration) + } + if len(signatures) != 0 || optionalToken == nil { + return typeElements + } + } + var propertyTypeNode *ast.TypeNode + if b.shouldUsePlaceholderForProperty(propertySymbol) { + propertyTypeNode = b.createElidedInformationPlaceholder() + } else { + if propertyIsReverseMapped { + b.ctx.reverseMappedStack = append(b.ctx.reverseMappedStack, propertySymbol) + } + if propertyType != nil { + propertyTypeNode = b.serializeTypeForDeclaration(nil /*declaration*/, propertyType, propertySymbol) + } else { + propertyTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + if propertyIsReverseMapped { + b.ctx.reverseMappedStack = b.ctx.reverseMappedStack[:len(b.ctx.reverseMappedStack)-1] + } + } + + var modifiers *ast.ModifierList + if b.ch.isReadonlySymbol(propertySymbol) { + modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)}) + b.ctx.approximateLength += 9 + } + propertySignature := b.f.NewPropertySignatureDeclaration(modifiers, propertyName, optionalToken, propertyTypeNode, nil) + + b.setCommentRange(propertySignature, propertySymbol.ValueDeclaration) // !!! missing JSDoc support formerly provided by preserveCommentsOn + typeElements = append(typeElements, propertySignature) + + return typeElements +} + +func (b *NodeBuilder) createTypeNodesFromResolvedType(resolvedType *StructuredType) *ast.NodeList { + if b.checkTruncationLength() { + if b.ctx.flags&NodeBuilderFlagsNoTruncation != 0 { + panic("NotEmittedTypeElement not implemented") // !!! + // elem := b.f.NewNotEmittedTypeElement() + // b.e.addSyntheticTrailingComment(elem, ast.KindMultiLineCommentTrivia, "elided") + // return b.f.NewNodeList([]*ast.TypeElement{elem}) + } + return b.f.NewNodeList([]*ast.Node{b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier("..."), nil, nil, nil)}) + } + var typeElements []*ast.TypeElement + for _, signature := range resolvedType.CallSignatures() { + typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindCallSignature, nil)) + } + for _, signature := range resolvedType.ConstructSignatures() { + if signature.flags&SignatureFlagsAbstract != 0 { + continue + } + typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructSignature, nil)) + } + for _, info := range resolvedType.indexInfos { + typeElements = append(typeElements, b.indexInfoToIndexSignatureDeclarationHelper(info, core.IfElse(resolvedType.objectFlags&ObjectFlagsReverseMapped != 0, b.createElidedInformationPlaceholder(), nil))) + } + + properties := resolvedType.properties + if len(properties) == 0 { + return b.f.NewNodeList(typeElements) + } + + i := 0 + for _, propertySymbol := range properties { + i++ + if b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0 { + if propertySymbol.Flags&ast.SymbolFlagsPrototype != 0 { + continue + } + if getDeclarationModifierFlagsFromSymbol(propertySymbol)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { + b.ctx.tracker.ReportPrivateInBaseOfClassExpression(propertySymbol.Name) + } + } + if b.checkTruncationLength() && (i+2 < len(properties)-1) { + if b.ctx.flags&NodeBuilderFlagsNoTruncation != 0 { + // !!! synthetic comment support - missing middle silently elided without + // typeElement := typeElements[len(typeElements) - 1].Clone() + // typeElements = typeElements[0:len(typeElements)-1] + // b.e.addSyntheticTrailingComment(typeElement, ast.KindMultiLineCommentTrivia, __TEMPLATE__("... ", properties.length-i, " more elided ...")) + // typeElements = append(typeElements, typeElement) + } else { + text := fmt.Sprintf("... %d more ...", len(properties)-i) + typeElements = append(typeElements, b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier(text), nil, nil, nil)) + } + typeElements = b.addPropertyToElementList(properties[len(properties)-1], typeElements) + break + } + typeElements = b.addPropertyToElementList(propertySymbol, typeElements) + } + if len(typeElements) != 0 { + return b.f.NewNodeList(typeElements) + } else { + return nil + } +} + +func (b *NodeBuilder) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { + if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError) { + return b.createMappedTypeNodeFromType(t) + } + + resolved := b.ch.resolveStructuredTypeMembers(t) + callSigs := resolved.CallSignatures() + ctorSigs := resolved.ConstructSignatures() + if len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { + if len(callSigs) == 0 && len(ctorSigs) == 0 { + b.ctx.approximateLength += 2 + result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{})) + b.e.SetEmitFlags(result, printer.EFSingleLine) + return result + } + + if len(callSigs) == 1 && len(ctorSigs) == 0 { + signature := callSigs[0] + signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindFunctionType, nil) + return signatureNode + } + + if len(ctorSigs) == 1 && len(callSigs) == 0 { + signature := ctorSigs[0] + signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructorType, nil) + return signatureNode + } + } + + abstractSignatures := core.Filter(ctorSigs, func(signature *Signature) bool { + return signature.flags&SignatureFlagsAbstract != 0 + }) + if len(callSigs) > 0 { + types := core.Map(abstractSignatures, func(s *Signature) *Type { + return b.ch.getOrCreateTypeFromSignature(s, nil) + }) + // count the number of type elements excluding abstract constructors + typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + (core.IfElse(b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { + return p.Flags&ast.SymbolFlagsPrototype == 0 + }), len(resolved.properties))) + // don't include an empty object literal if there were no other static-side + // properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}` + // and not `(abstract new () => {}) & {}` + if typeElementCount != 0 { + // create a copy of the object type without any abstract construct signatures. + types = append(types, b.getResolvedTypeWithoutAbstractConstructSignatures(resolved)) + } + return b.typeToTypeNodeClosure(b.ch.getIntersectionType(types)) + } + + restoreFlags := b.saveRestoreFlags() + b.ctx.flags |= NodeBuilderFlagsInObjectTypeLiteral + members := b.createTypeNodesFromResolvedType(resolved) + restoreFlags() + typeLiteralNode := b.f.NewTypeLiteralNode(members) + b.ctx.approximateLength += 2 + b.e.SetEmitFlags(typeLiteralNode, core.IfElse((b.ctx.flags&NodeBuilderFlagsMultilineObjectLiterals != 0), 0, printer.EFSingleLine)) + return typeLiteralNode +} + +func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol { + if t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 && t.symbol.Declarations != nil { + node := ast.WalkUpParenthesizedTypes(t.symbol.Declarations[0].Parent) + if ast.IsTypeAliasDeclaration(node) { + return c.getSymbolOfDeclaration(node) + } + } + return nil +} + +func (b *NodeBuilder) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) bool { + isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { + return ast.IsStatic(declaration) + }) + isNonLocalFunctionSymbol := false + if symbol.Flags&ast.SymbolFlagsFunction != 0 { + if symbol.Parent != nil { + isNonLocalFunctionSymbol = true + } else { + for _, declaration := range symbol.Declarations { + if declaration.Parent.Kind == ast.KindSourceFile || declaration.Parent.Kind == ast.KindModuleBlock { + isNonLocalFunctionSymbol = true + break + } + } + } + } + if isStaticMethodSymbol || isNonLocalFunctionSymbol { + // typeof is allowed only for static/non local functions + _, visited := b.ctx.visitedTypes[typeId] + return (b.ctx.flags&NodeBuilderFlagsUseTypeOfFunction != 0 || visited) && (b.ctx.flags&NodeBuilderFlagsUseStructuralFallback == 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 + } + return false +} + +func (b *NodeBuilder) createAnonymousTypeNode(t *Type) *ast.TypeNode { + typeId := t.id + symbol := t.symbol + if symbol != nil { + isInstantiationExpressionType := t.objectFlags&ObjectFlagsInstantiationExpressionType != 0 + if isInstantiationExpressionType { + instantiationExpressionType := t.AsInstantiationExpressionType() + existing := instantiationExpressionType.node + if ast.IsTypeQueryNode(existing) { + typeNode := b.tryReuseExistingNonParameterTypeNode(existing, t, nil, nil) + if typeNode != nil { + return typeNode + } + } + if _, ok := b.ctx.visitedTypes[typeId]; ok { + return b.createElidedInformationPlaceholder() + } + return b.visitAndTransformType(t, b.createTypeNodeFromObjectTypeClosure) + } + var isInstanceType ast.SymbolFlags + if isClassInstanceSide(b.ch, t) { + isInstanceType = ast.SymbolFlagsType + } else { + isInstanceType = ast.SymbolFlagsValue + } + + // !!! JS support + // if c.isJSConstructor(symbol.ValueDeclaration) { + // // Instance and static types share the same symbol; only add 'typeof' for the static side. + // return b.symbolToTypeNode(symbol, isInstanceType, nil) + // } else + if symbol.Flags&ast.SymbolFlagsClass != 0 && b.ch.getBaseTypeVariableOfClass(symbol) == nil && !(symbol.ValueDeclaration != nil && ast.IsClassLike(symbol.ValueDeclaration) && b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0 && (!ast.IsClassDeclaration(symbol.ValueDeclaration) || b.ch.IsSymbolAccessible(symbol, b.ctx.enclosingDeclaration, isInstanceType, false /*shouldComputeAliasesToMakeVisible*/).accessibility != SymbolAccessibilityAccessible)) || symbol.Flags&(ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 || b.shouldWriteTypeOfFunctionSymbol(symbol, typeId) { + return b.symbolToTypeNode(symbol, isInstanceType, nil) + } else if _, ok := b.ctx.visitedTypes[typeId]; ok { + // If type is an anonymous type literal in a type alias declaration, use type alias name + typeAlias := getTypeAliasForTypeLiteral(b.ch, t) + if typeAlias != nil { + // The specified symbol flags need to be reinterpreted as type flags + return b.symbolToTypeNode(typeAlias, ast.SymbolFlagsType, nil) + } else { + return b.createElidedInformationPlaceholder() + } + } else { + return b.visitAndTransformType(t, b.createTypeNodeFromObjectTypeClosure) + } + } else { + // Anonymous types without a symbol are never circular. + return b.createTypeNodeFromObjectTypeClosure(t) + } + +} + +func (b *NodeBuilder) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes bool) *Type { + // !!! noMappedTypes optional param support + t := b.ch.getTypeFromTypeNode(node) + if b.ctx.mapper == nil { + return t + } + + instantiated := b.ch.instantiateType(t, b.ctx.mapper) + if noMappedTypes && instantiated != t { + return nil + } + return instantiated +} + +func (b *NodeBuilder) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode { + if t.flags&TypeFlagsUnion != 0 { + if _, ok := b.ctx.visitedTypes[t.id]; ok { + if b.ctx.flags&NodeBuilderFlagsAllowAnonymousIdentifier == 0 { + b.ctx.encounteredError = true + b.ctx.tracker.ReportCyclicStructureError() + } + return b.createElidedInformationPlaceholder() + } + return b.visitAndTransformType(t, b.typeToTypeNodeClosure) + } + return b.typeToTypeNodeClosure(t) +} + +func (b *NodeBuilder) conditionalTypeToTypeNode(_t *Type) *ast.TypeNode { + t := _t.AsConditionalType() + checkTypeNode := b.typeToTypeNodeClosure(t.checkType) + b.ctx.approximateLength += 15 + if b.ctx.flags&NodeBuilderFlagsGenerateNamesForShadowedTypeParams != 0 && t.root.isDistributive && t.checkType.flags&TypeFlagsTypeParameter == 0 { + newParam := b.ch.newTypeParameter(b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T" /* as __String */)) + name := b.typeParameterToName(newParam) + newTypeVariable := b.f.NewTypeReferenceNode(name.AsNode(), nil) + b.ctx.approximateLength += 37 + // 15 each for two added conditionals, 7 for an added infer type + newMapper := prependTypeMapping(t.root.checkType, newParam, t.mapper) + saveInferTypeParameters := b.ctx.inferTypeParameters + b.ctx.inferTypeParameters = t.root.inferTypeParameters + extendsTypeNode := b.typeToTypeNodeClosure(b.ch.instantiateType(t.root.extendsType, newMapper)) + b.ctx.inferTypeParameters = saveInferTypeParameters + trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.TrueType, false), newMapper)) + falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.FalseType, false), newMapper)) + + // outermost conditional makes `T` a type parameter, allowing the inner conditionals to be distributive + // second conditional makes `T` have `T & checkType` substitution, so it is correctly usable as the checkType + // inner conditional runs the check the user provided on the check type (distributively) and returns the result + // checkType extends infer T ? T extends checkType ? T extends extendsType ? trueType : falseType : never : never; + // this is potentially simplifiable to + // checkType extends infer T ? T extends checkType & extendsType ? trueType : falseType : never; + // but that may confuse users who read the output more. + // On the other hand, + // checkType extends infer T extends checkType ? T extends extendsType ? trueType : falseType : never; + // may also work with `infer ... extends ...` in, but would produce declarations only compatible with the latest TS. + newId := newTypeVariable.AsTypeReferenceNode().TypeName.AsIdentifier().Clone(b.f) + syntheticExtendsNode := b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newId, nil, nil)) + innerCheckConditionalNode := b.f.NewConditionalTypeNode(newTypeVariable, extendsTypeNode, trueTypeNode, falseTypeNode) + syntheticTrueNode := b.f.NewConditionalTypeNode(b.f.NewTypeReferenceNode(name.Clone(b.f), nil), b.f.DeepCloneNode(checkTypeNode), innerCheckConditionalNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword)) + return b.f.NewConditionalTypeNode(checkTypeNode, syntheticExtendsNode, syntheticTrueNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword)) + } + saveInferTypeParameters := b.ctx.inferTypeParameters + b.ctx.inferTypeParameters = t.root.inferTypeParameters + extendsTypeNode := b.typeToTypeNodeClosure(t.extendsType) + b.ctx.inferTypeParameters = saveInferTypeParameters + trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getTrueTypeFromConditionalType(_t)) + falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getFalseTypeFromConditionalType(_t)) + return b.f.NewConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode) +} + +func (b *NodeBuilder) getParentSymbolOfTypeParameter(typeParameter *TypeParameter) *ast.Symbol { + tp := ast.GetDeclarationOfKind(typeParameter.symbol, ast.KindTypeParameter) + var host *ast.Node + // !!! JSDoc support + // if ast.IsJSDocTemplateTag(tp.Parent) { + // host = getEffectiveContainerForJSDocTemplateTag(tp.Parent) + // } else { + host = tp.Parent + // } + if host == nil { + return nil + } + return b.ch.getSymbolOfNode(host) +} + +func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { + var typeArguments []*Type = b.ch.getTypeArguments(t) + if t.Target() == b.ch.globalArrayType || t.Target() == b.ch.globalReadonlyArrayType { + if b.ctx.flags&NodeBuilderFlagsWriteArrayAsGenericType != 0 { + typeArgumentNode := b.typeToTypeNodeClosure(typeArguments[0]) + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(core.IfElse(t.Target() == b.ch.globalArrayType, "Array", "ReadonlyArray")), b.f.NewNodeList([]*ast.TypeNode{typeArgumentNode})) + } + elementType := b.typeToTypeNodeClosure(typeArguments[0]) + arrayType := b.f.NewArrayTypeNode(elementType) + if t.Target() == b.ch.globalArrayType { + return arrayType + } else { + return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, arrayType) + } + } else if t.Target().objectFlags&ObjectFlagsTuple != 0 { + typeArguments = core.SameMapIndex(typeArguments, func(t *Type, i int) *Type { + return b.ch.removeMissingType(t, t.Target().AsTupleType().elementInfos[i].flags&ElementFlagsOptional != 0) + }) + if len(typeArguments) > 0 { + arity := b.ch.getTypeReferenceArity(t) + tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity]) + if tupleConstituentNodes != nil { + for i := 0; i < len(tupleConstituentNodes.Nodes); i++ { + flags := t.Target().AsTupleType().elementInfos[i].flags + labeledElementDeclaration := t.Target().AsTupleType().elementInfos[i].labeledDeclaration + + if labeledElementDeclaration != nil { + tupleConstituentNodes.Nodes[i] = b.f.NewNamedTupleMember(core.IfElse(flags&ElementFlagsVariable != 0, b.f.NewToken(ast.KindDotDotDotToken), nil), b.f.NewIdentifier(b.ch.getTupleElementLabel(t.Target().AsTupleType().elementInfos[i], nil, i)), core.IfElse(flags&ElementFlagsOptional != 0, b.f.NewToken(ast.KindQuestionToken), nil), core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i])) + } else { + switch { + case flags&ElementFlagsVariable != 0: + tupleConstituentNodes.Nodes[i] = b.f.NewRestTypeNode(core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i])) + case flags&ElementFlagsOptional != 0: + tupleConstituentNodes.Nodes[i] = b.f.NewOptionalTypeNode(tupleConstituentNodes.Nodes[i]) + } + } + } + tupleTypeNode := b.f.NewTupleTypeNode(tupleConstituentNodes) + b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) + if t.Target().AsTupleType().readonly { + return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode) + } else { + return tupleTypeNode + } + } + } + if b.ctx.encounteredError || (b.ctx.flags&NodeBuilderFlagsAllowEmptyTuple != 0) { + tupleTypeNode := b.f.NewTupleTypeNode(b.f.NewNodeList([]*ast.TypeNode{})) + b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) + if t.Target().AsTupleType().readonly { + return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode) + } else { + return tupleTypeNode + } + } + b.ctx.encounteredError = true + return nil + // TODO: GH#18217 + } else if b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0 && t.symbol.ValueDeclaration != nil && ast.IsClassLike(t.symbol.ValueDeclaration) && !b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { + return b.createAnonymousTypeNode(t) + } else { + outerTypeParameters := t.Target().AsInterfaceType().OuterTypeParameters() + i := 0 + var resultType *ast.TypeNode + if outerTypeParameters != nil { + length := len(outerTypeParameters) + for i < length { + // Find group of type arguments for type parameters with the same declaring container. + start := i + parent := b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) + for ok := true; ok; ok = i < length && b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) == parent { // do-while loop + i++ + } + // When type parameters are their own type arguments for the whole group (i.e. we have + // the default outer type arguments), we don't show the group. + + if !slices.Equal(outerTypeParameters[start:i], typeArguments[start:i]) { + typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i]) + restoreFlags := b.saveRestoreFlags() + b.ctx.flags |= NodeBuilderFlagsForbidIndexedAccessSymbolReferences + ref := b.symbolToTypeNode(parent, ast.SymbolFlagsType, typeArgumentSlice) + restoreFlags() + if resultType == nil { + resultType = ref + } else { + resultType = b.appendReferenceToType(resultType, ref) + } + } + } + } + var typeArgumentNodes *ast.NodeList + if len(typeArguments) > 0 { + typeParameterCount := 0 + typeParams := t.Target().AsInterfaceType().TypeParameters() + if typeParams != nil { + typeParameterCount = min(len(typeParams), len(typeArguments)) + + // Maybe we should do this for more types, but for now we only elide type arguments that are + // identical to their associated type parameters' defaults for `Iterable`, `IterableIterator`, + // `AsyncIterable`, and `AsyncIterableIterator` to provide backwards-compatible .d.ts emit due + // to each now having three type parameters instead of only one. + if b.ch.isReferenceToType(t, b.ch.getGlobalIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalIterableIteratorType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableIteratorType()) { + if t.AsInterfaceType().node == nil || !ast.IsTypeReferenceNode(t.AsInterfaceType().node) || t.AsInterfaceType().node.TypeArguments() == nil || len(t.AsInterfaceType().node.TypeArguments()) < typeParameterCount { + for typeParameterCount > 0 { + typeArgument := typeArguments[typeParameterCount-1] + typeParameter := t.Target().AsInterfaceType().TypeParameters()[typeParameterCount-1] + defaultType := b.ch.getDefaultFromTypeParameter(typeParameter) + if defaultType == nil || !b.ch.isTypeIdenticalTo(typeArgument, defaultType) { + break + } + typeParameterCount-- + } + } + } + } + + typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount]) + } + restoreFlags := b.saveRestoreFlags() + b.ctx.flags |= NodeBuilderFlagsForbidIndexedAccessSymbolReferences + finalRef := b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, typeArgumentNodes) + restoreFlags() + if resultType == nil { + return finalRef + } else { + return b.appendReferenceToType(resultType, finalRef) + } + } +} + +func (b *NodeBuilder) visitAndTransformType(t *Type, transform func(t *Type) *ast.TypeNode) *ast.TypeNode { + typeId := t.id + isConstructorObject := t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 + var id *CompositeSymbolIdentity + switch { + case t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node != nil: + id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsTypeReference().node)} + case t.flags&TypeFlagsConditional != 0: + id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsConditionalType().root.node.AsNode())} + case t.symbol != nil: + id = &CompositeSymbolIdentity{isConstructorObject, ast.GetSymbolId(t.symbol), 0} + default: + id = nil + } + // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead + // of types allows us to catch circular references to instantiations of the same anonymous type + + key := CompositeTypeCacheIdentity{typeId, b.ctx.flags, b.ctx.internalFlags} + if b.links.Has(b.ctx.enclosingDeclaration) { + links := b.links.Get(b.ctx.enclosingDeclaration) + cachedResult, ok := links.serializedTypes[key] + if ok { + // TODO:: check if we instead store late painted statements associated with this? + for _, arg := range cachedResult.trackedSymbols { + b.ctx.tracker.TrackSymbol(arg.symbol, arg.enclosingDeclaration, arg.meaning) + } + if cachedResult.truncating { + b.ctx.truncating = true + } + b.ctx.approximateLength += cachedResult.addedLength + return b.f.DeepCloneNode(cachedResult.node) + } + } + + var depth int + if id != nil { + depth = b.ctx.symbolDepth[*id] + if depth > 10 { + return b.createElidedInformationPlaceholder() + } + b.ctx.symbolDepth[*id] = depth + 1 + } + b.ctx.visitedTypes[typeId] = true + prevTrackedSymbols := b.ctx.trackedSymbols + b.ctx.trackedSymbols = nil + startLength := b.ctx.approximateLength + result := transform(t) + addedLength := b.ctx.approximateLength - startLength + if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError { + links := b.links.Get(b.ctx.enclosingDeclaration) + links.serializedTypes[key] = &SerializedTypeEntry{ + node: result, + truncating: b.ctx.truncating, + addedLength: addedLength, + trackedSymbols: b.ctx.trackedSymbols, + } + } + delete(b.ctx.visitedTypes, typeId) + if id != nil { + b.ctx.symbolDepth[*id] = depth + } + b.ctx.trackedSymbols = prevTrackedSymbols + return result + + // !!! TODO: Attempt node reuse or parse nodes to minimize copying once text range setting is set up + // deepCloneOrReuseNode := func(node T) T { + // if !nodeIsSynthesized(node) && getParseTreeNode(node) == node { + // return node + // } + // return setTextRange(b.ctx, b.f.cloneNode(visitEachChildWorker(node, deepCloneOrReuseNode, nil /*b.ctx*/, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node) + // } + + // deepCloneOrReuseNodes := func(nodes *NodeArray[*ast.Node], visitor Visitor, test func(node *ast.Node) bool, start number, count number) *NodeArray[*ast.Node] { + // if nodes != nil && nodes.length == 0 { + // // Ensure we explicitly make a copy of an empty array; visitNodes will not do this unless the array has elements, + // // which can lead to us reusing the same empty NodeArray more than once within the same AST during type noding. + // return setTextRangeWorker(b.f.NewNodeArray(nil, nodes.hasTrailingComma), nodes) + // } + // return visitNodes(nodes, visitor, test, start, count) + // } +} + +func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { + + inTypeAlias := b.ctx.flags & NodeBuilderFlagsInTypeAlias + b.ctx.flags &^= NodeBuilderFlagsInTypeAlias + + if t == nil { + if b.ctx.flags&NodeBuilderFlagsAllowEmptyUnionOrIntersection == 0 { + b.ctx.encounteredError = true + return nil + // TODO: GH#18217 + } + b.ctx.approximateLength += 3 + return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + + if b.ctx.flags&NodeBuilderFlagsNoTypeReduction == 0 { + t = b.ch.getReducedType(t) + } + + if t.flags&TypeFlagsAny != 0 { + if t.alias != nil { + return t.alias.ToTypeReferenceNode(b) + } + // !!! TODO: add comment once synthetic comment additions to nodes are supported + // if t == b.ch.unresolvedType { + // return e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "unresolved") + // } + b.ctx.approximateLength += 3 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(core.IfElse(t == b.ch.intrinsicMarkerType, ast.KindIntrinsicKeyword, ast.KindAnyKeyword))) + } + if t.flags&TypeFlagsUnknown != 0 { + return b.f.NewKeywordTypeNode(ast.KindUnknownKeyword) + } + if t.flags&TypeFlagsString != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindStringKeyword) + } + if t.flags&TypeFlagsNumber != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindNumberKeyword) + } + if t.flags&TypeFlagsBigInt != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindBigIntKeyword) + } + if t.flags&TypeFlagsBoolean != 0 && t.alias == nil { + b.ctx.approximateLength += 7 + return b.f.NewKeywordTypeNode(ast.KindBooleanKeyword) + } + if t.flags&TypeFlagsEnumLike != 0 { + if t.symbol.Flags&ast.SymbolFlagsEnumMember != 0 { + parentSymbol := b.ch.getParentOfSymbol(t.symbol) + parentName := b.symbolToTypeNode(parentSymbol, ast.SymbolFlagsType, nil) + if b.ch.getDeclaredTypeOfSymbol(parentSymbol) == t { + return parentName + } + memberName := ast.SymbolName(t.symbol) + if scanner.IsIdentifierText(memberName, core.ScriptTargetES5) { + return b.appendReferenceToType(parentName /* as TypeReferenceNode | ImportTypeNode */, b.f.NewTypeReferenceNode(b.f.NewIdentifier(memberName), nil /*typeArguments*/)) + } + if ast.IsImportTypeNode(parentName) { + parentName.AsImportTypeNode().IsTypeOf = true + // mutably update, node is freshly manufactured anyhow + return b.f.NewIndexedAccessTypeNode(parentName, b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName))) + } else if ast.IsTypeReferenceNode(parentName) { + return b.f.NewIndexedAccessTypeNode(b.f.NewTypeQueryNode(parentName.AsTypeReferenceNode().TypeName, nil), b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName))) + } else { + panic("Unhandled type node kind returned from `symbolToTypeNode`.") + } + } + return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil) + } + if t.flags&TypeFlagsStringLiteral != 0 { + b.ctx.approximateLength += len(t.AsLiteralType().value.(string)) + 2 + return b.f.NewLiteralTypeNode(b.f.NewStringLiteral(t.AsLiteralType().value.(string) /*, b.flags&NodeBuilderFlagsUseSingleQuotesForStringLiteralType != 0*/)) + } + if t.flags&TypeFlagsNumberLiteral != 0 { + value := t.AsLiteralType().value.(jsnum.Number) + b.ctx.approximateLength += len(value.String()) + return b.f.NewLiteralTypeNode(core.IfElse(value < 0, b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral("-"+value.String())), b.f.NewNumericLiteral(value.String()))) + } + if t.flags&TypeFlagsBigIntLiteral != 0 { + b.ctx.approximateLength += len(pseudoBigIntToString(getBigIntLiteralValue(t))) + 1 + return b.f.NewLiteralTypeNode(b.f.NewBigIntLiteral(pseudoBigIntToString(getBigIntLiteralValue(t)))) + } + if t.flags&TypeFlagsBooleanLiteral != 0 { + b.ctx.approximateLength += len(t.AsIntrinsicType().intrinsicName) + return b.f.NewLiteralTypeNode(core.IfElse(t.AsIntrinsicType().intrinsicName == "true", b.f.NewKeywordExpression(ast.KindTrueKeyword), b.f.NewKeywordExpression(ast.KindFalseKeyword))) + } + if t.flags&TypeFlagsUniqueESSymbol != 0 { + if b.ctx.flags&NodeBuilderFlagsAllowUniqueESSymbolType == 0 { + if b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { + b.ctx.approximateLength += 6 + return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsValue, nil) + } + b.ctx.tracker.ReportInaccessibleUniqueSymbolError() + } + b.ctx.approximateLength += 13 + return b.f.NewTypeOperatorNode(ast.KindUniqueKeyword, b.f.NewKeywordTypeNode(ast.KindSymbolKeyword)) + } + if t.flags&TypeFlagsVoid != 0 { + b.ctx.approximateLength += 4 + return b.f.NewKeywordTypeNode(ast.KindVoidKeyword) + } + if t.flags&TypeFlagsUndefined != 0 { + b.ctx.approximateLength += 9 + return b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword) + } + if t.flags&TypeFlagsNull != 0 { + b.ctx.approximateLength += 4 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindNullKeyword)) + } + if t.flags&TypeFlagsNever != 0 { + b.ctx.approximateLength += 5 + return b.f.NewKeywordTypeNode(ast.KindNeverKeyword) + } + if t.flags&TypeFlagsESSymbol != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindSymbolKeyword) + } + if t.flags&TypeFlagsNonPrimitive != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindObjectKeyword) + } + if isThisTypeParameter(t) { + if b.ctx.flags&NodeBuilderFlagsInObjectTypeLiteral != 0 { + if !b.ctx.encounteredError && b.ctx.flags&NodeBuilderFlagsAllowThisInObjectLiteral == 0 { + b.ctx.encounteredError = true + } + b.ctx.tracker.ReportInaccessibleThisError() + } + b.ctx.approximateLength += 4 + return b.f.NewThisTypeNode() + } + + if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&NodeBuilderFlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) { + sym := t.alias.Symbol() + typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments()) + if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 { + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), typeArgumentNodes) + } + if typeArgumentNodes != nil && len(typeArgumentNodes.Nodes) == 1 && sym == b.ch.globalArrayType.symbol { + return b.f.NewArrayTypeNode(typeArgumentNodes.Nodes[0]) + } + return b.symbolToTypeNode(sym, ast.SymbolFlagsType, typeArgumentNodes) + } + + objectFlags := t.objectFlags + + if objectFlags&ObjectFlagsReference != 0 { + // Debug.assert(t.flags&TypeFlagsObject != 0) // !!! + if t.AsTypeReference().node != nil { + return b.visitAndTransformType(t, b.typeReferenceToTypeNodeClosure) + } else { + return b.typeReferenceToTypeNodeClosure(t) + } + } + if t.flags&TypeFlagsTypeParameter != 0 || objectFlags&ObjectFlagsClassOrInterface != 0 { + if t.flags&TypeFlagsTypeParameter != 0 && slices.Contains(b.ctx.inferTypeParameters, t) { + b.ctx.approximateLength += len(ast.SymbolName(t.symbol)) + 6 + var constraintNode *ast.TypeNode + constraint := b.ch.getConstraintOfTypeParameter(t) + if constraint != nil { + // If the infer type has a constraint that is not the same as the constraint + // we would have normally inferred based on b, we emit the constraint + // using `infer T extends ?`. We omit inferred constraints from type references + // as they may be elided. + inferredConstraint := b.ch.getInferredTypeParameterConstraint(t, true /*omitTypeReferences*/) + if !(inferredConstraint != nil && b.ch.isTypeIdenticalTo(constraint, inferredConstraint)) { + b.ctx.approximateLength += 9 + constraintNode = b.typeToTypeNodeClosure(constraint) + } + } + return b.f.NewInferTypeNode(b.typeParameterToDeclarationWithConstraint(t, constraintNode)) + } + if b.ctx.flags&NodeBuilderFlagsGenerateNamesForShadowedTypeParams != 0 && t.flags&TypeFlagsTypeParameter != 0 { + name := b.typeParameterToName(t) + b.ctx.approximateLength += len(name.Text) + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name.Text), nil /*typeArguments*/) + } + // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. + if t.symbol != nil { + return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil) + } + var name string + if (t == b.ch.markerSuperTypeForCheck || t == b.ch.markerSubTypeForCheck) && b.ch.varianceTypeParameter != nil && b.ch.varianceTypeParameter.symbol != nil { + name = (core.IfElse(t == b.ch.markerSubTypeForCheck, "sub-", "super-")) + ast.SymbolName(b.ch.varianceTypeParameter.symbol) + } else { + name = "?" + } + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name), nil /*typeArguments*/) + } + if t.flags&TypeFlagsUnion != 0 && t.AsUnionType().origin != nil { + t = t.AsUnionType().origin + } + if t.flags&(TypeFlagsUnion|TypeFlagsIntersection) != 0 { + var types []*Type + if t.flags&TypeFlagsUnion != 0 { + types = b.ch.formatUnionTypes(t.AsUnionType().types) + } else { + types = t.AsIntersectionType().types + } + if len(types) == 1 { + return b.typeToTypeNodeClosure(types[0]) + } + typeNodes := b.mapToTypeNodes(types) + if typeNodes != nil && len(typeNodes.Nodes) > 0 { + if t.flags&TypeFlagsUnion != 0 { + return b.f.NewUnionTypeNode(typeNodes) + } else { + return b.f.NewIntersectionTypeNode(typeNodes) + } + } else { + if !b.ctx.encounteredError && b.ctx.flags&NodeBuilderFlagsAllowEmptyUnionOrIntersection == 0 { + b.ctx.encounteredError = true + } + return nil + // TODO: GH#18217 + } + } + if objectFlags&(ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 { + // Debug.assert(t.flags&TypeFlagsObject != 0) // !!! + // The type is an object literal type. + return b.createAnonymousTypeNode(t) + } + if t.flags&TypeFlagsIndex != 0 { + indexedType := t.Target() + b.ctx.approximateLength += 6 + indexTypeNode := b.typeToTypeNodeClosure(indexedType) + return b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTypeNode) + } + if t.flags&TypeFlagsTemplateLiteral != 0 { + texts := t.AsTemplateLiteralType().texts + types := t.AsTemplateLiteralType().types + templateHead := b.f.NewTemplateHead(texts[0], texts[0], ast.TokenFlagsNone) + templateSpans := b.f.NewNodeList(core.MapIndex(types, func(t *Type, i int) *ast.Node { + var res *ast.TemplateMiddleOrTail + if i < len(types)-1 { + res = b.f.NewTemplateMiddle(texts[i+1], texts[i+1], ast.TokenFlagsNone) + } else { + res = b.f.NewTemplateTail(texts[i+1], texts[i+1], ast.TokenFlagsNone) + } + return b.f.NewTemplateLiteralTypeSpan(b.typeToTypeNodeClosure(t), res) + })) + b.ctx.approximateLength += 2 + return b.f.NewTemplateLiteralTypeNode(templateHead, templateSpans) + } + if t.flags&TypeFlagsStringMapping != 0 { + typeNode := b.typeToTypeNodeClosure(t.Target()) + return b.symbolToTypeNode(t.AsStringMappingType().symbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode})) + } + if t.flags&TypeFlagsIndexedAccess != 0 { + objectTypeNode := b.typeToTypeNodeClosure(t.AsIndexedAccessType().objectType) + indexTypeNode := b.typeToTypeNodeClosure(t.AsIndexedAccessType().indexType) + b.ctx.approximateLength += 2 + return b.f.NewIndexedAccessTypeNode(objectTypeNode, indexTypeNode) + } + if t.flags&TypeFlagsConditional != 0 { + return b.visitAndTransformType(t, b.conditionalTypeToTypeNodeClosure) + } + if t.flags&TypeFlagsSubstitution != 0 { + typeNode := b.typeToTypeNodeClosure(t.AsSubstitutionType().baseType) + if !b.ch.isNoInferType(t) { + return typeNode + } + noInferSymbol := b.ch.getGlobalTypeAliasSymbol("NoInfer", 1, false) + if noInferSymbol != nil { + return b.symbolToTypeNode(noInferSymbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode})) + } else { + return typeNode + } + } + + panic("Should be unreachable.") +} + +// Direct serialization core functions for types, type aliases, and symbols + +func (t *TypeAlias) ToTypeReferenceNode(b *NodeBuilder) *ast.Node { + return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments())) +} diff --git a/internal/checker/nodebuilderapi.go b/internal/checker/nodebuilderapi.go new file mode 100644 index 0000000000..7cdaaccffd --- /dev/null +++ b/internal/checker/nodebuilderapi.go @@ -0,0 +1,180 @@ +package checker + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/printer" +) + +type NodeBuilderInterface interface { + typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) []*ast.Node + symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node +} + +type NodeBuilderAPI struct { + ctxStack []*NodeBuilderContext + impl *NodeBuilder +} + +func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) { + b.impl.ctx = &NodeBuilderContext{ + tracker: tracker, + approximateLength: 0, + encounteredError: false, + truncating: false, + reportedDiagnostic: false, + flags: flags, + internalFlags: internalFlags, + depth: 0, + enclosingDeclaration: enclosingDeclaration, + enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration), + inferTypeParameters: make([]*Type, 0), + visitedTypes: make(map[TypeId]bool), + symbolDepth: make(map[CompositeSymbolIdentity]int), + trackedSymbols: make([]*TrackedSymbolArgs, 0), + mapper: nil, + reverseMappedStack: make([]*ast.Symbol, 0), + } + b.impl.initializeClosures() // recapture ctx + b.ctxStack = append(b.ctxStack, b.impl.ctx) +} + +func (b *NodeBuilderAPI) popContext() { + b.impl.ctx = nil + if len(b.ctxStack) > 1 { + b.impl.ctx = b.ctxStack[len(b.ctxStack)-1] + } + b.ctxStack = b.ctxStack[0 : len(b.ctxStack)-1] +} + +func (b *NodeBuilderAPI) exitContext(result *ast.Node) *ast.Node { + b.exitContextCheck() + defer b.popContext() + if b.impl.ctx.encounteredError { + return nil + } + return result +} + +func (b *NodeBuilderAPI) exitContextSlice(result []*ast.Node) []*ast.Node { + b.exitContextCheck() + defer b.popContext() + if b.impl.ctx.encounteredError { + return nil + } + return result +} + +func (b *NodeBuilderAPI) exitContextCheck() { + if b.impl.ctx.truncating && b.impl.ctx.flags&NodeBuilderFlagsNoTruncation != 0 { + b.impl.ctx.tracker.ReportTruncationError() + } +} + +// indexInfoToIndexSignatureDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.indexInfoToIndexSignatureDeclarationHelper(info, nil)) +} + +// serializeReturnTypeForSignature implements NodeBuilderInterface. +func (b *NodeBuilderAPI) serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + signature := b.impl.ch.getSignatureFromDeclaration(signatureDeclaration) + symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration) + returnType, ok := b.impl.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] + if !ok || returnType == nil { + returnType = b.impl.ch.instantiateType(b.impl.ch.getReturnTypeOfSignature(signature), b.impl.ctx.mapper) + } + return b.exitContext(b.impl.serializeInferredReturnTypeForSignature(signature, returnType)) +} + +// serializeTypeForDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.serializeTypeForDeclaration(declaration, nil, symbol)) +} + +// serializeTypeForExpression implements NodeBuilderInterface. +func (b *NodeBuilderAPI) serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.serializeTypeForExpression(expr)) +} + +// signatureToSignatureDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.signatureToSignatureDeclarationHelper(signature, kind, nil)) +} + +// symbolTableToDeclarationStatements implements NodeBuilderInterface. +func (b *NodeBuilderAPI) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) []*ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContextSlice(b.impl.symbolTableToDeclarationStatements(symbolTable)) +} + +// symbolToEntityName implements NodeBuilderInterface. +func (b *NodeBuilderAPI) symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToName(symbol, meaning, false)) +} + +// symbolToExpression implements NodeBuilderInterface. +func (b *NodeBuilderAPI) symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToExpression(symbol, meaning)) +} + +// symbolToNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToNode(symbol, meaning)) +} + +// symbolToParameterDeclaration implements NodeBuilderInterface. +func (b NodeBuilderAPI) symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToParameterDeclaration(symbol, false)) +} + +// symbolToTypeParameterDeclarations implements NodeBuilderInterface. +func (b *NodeBuilderAPI) symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToTypeParameterDeclarations(symbol)) +} + +// typeParameterToDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.typeParameterToDeclaration(parameter)) +} + +// typePredicateToTypePredicateNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.typePredicateToTypePredicateNode(predicate)) +} + +// typeToTypeNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.typeToTypeNode(typ)) +} + +// var _ NodeBuilderInterface = NewNodeBuilderAPI(nil, nil) + +func NewNodeBuilderAPI(ch *Checker, e *printer.EmitContext) *NodeBuilderAPI { + impl := NewNodeBuilder(ch, e) + return &NodeBuilderAPI{impl: &impl, ctxStack: make([]*NodeBuilderContext, 0, 1)} +} diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go new file mode 100644 index 0000000000..9143ca86c7 --- /dev/null +++ b/internal/checker/nodebuilderscopes.go @@ -0,0 +1,230 @@ +package checker + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" +) + +func cloneNodeBuilderContext(context *NodeBuilderContext) func() { + // Make type parameters created within this context not consume the name outside this context + // The symbol serializer ends up creating many sibling scopes that all need "separate" contexts when + // it comes to naming things - within a normal `typeToTypeNode` call, the node builder only ever descends + // through the type tree, so the only cases where we could have used distinct sibling scopes was when there + // were multiple generic overloads with similar generated type parameter names + // The effect: + // When we write out + // export const x: (x: T) => T + // export const y: (x: T) => T + // we write it out like that, rather than as + // export const x: (x: T) => T + // export const y: (x: T_1) => T_1 + oldMustCreateTypeParameterSymbolList := context.mustCreateTypeParameterSymbolList + oldMustCreateTypeParametersNamesLookups := context.mustCreateTypeParametersNamesLookups + context.mustCreateTypeParameterSymbolList = true + context.mustCreateTypeParametersNamesLookups = true + oldTypeParameterNames := context.typeParameterNames + oldTypeParameterNamesByText := context.typeParameterNamesByText + oldTypeParameterNamesByTextNextNameCount := context.typeParameterNamesByTextNextNameCount + oldTypeParameterSymbolList := context.typeParameterSymbolList + return func() { + context.typeParameterNames = oldTypeParameterNames + context.typeParameterNamesByText = oldTypeParameterNamesByText + context.typeParameterNamesByTextNextNameCount = oldTypeParameterNamesByTextNextNameCount + context.typeParameterSymbolList = oldTypeParameterSymbolList + context.mustCreateTypeParameterSymbolList = oldMustCreateTypeParameterSymbolList + context.mustCreateTypeParametersNamesLookups = oldMustCreateTypeParametersNamesLookups + } +} + +type localsRecord struct { + name string + oldSymbol *ast.Symbol +} + +func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*ast.Symbol, typeParameters *[]*Type, originalParameters *[]*ast.Symbol, mapper *TypeMapper) func() { + cleanupContext := cloneNodeBuilderContext(b.ctx) + // For regular function/method declarations, the enclosing declaration will already be signature.declaration, + // so this is a no-op, but for arrow functions and function expressions, the enclosing declaration will be + // the declaration that the arrow function / function expression is assigned to. + // + // If the parameters or return type include "typeof globalThis.paramName", using the wrong scope will lead + // us to believe that we can emit "typeof paramName" instead, even though that would refer to the parameter, + // not the global. Make sure we are in the right scope by changing the enclosingDeclaration to the function. + // + // We can't use the declaration directly; it may be in another file and so we may lose access to symbols + // accessible to the current enclosing declaration, or gain access to symbols not accessible to the current + // enclosing declaration. To keep this chain accurate, insert a fake scope into the chain which makes the + // function's parameters visible. + var cleanupParams *func() + var cleanupTypeParams *func() + oldEnclosingDecl := b.ctx.enclosingDeclaration + oldMapper := b.ctx.mapper + if mapper != nil { + b.ctx.mapper = mapper + } + if b.ctx.enclosingDeclaration != nil && declaration != nil { + // As a performance optimization, reuse the same fake scope within this chain. + // This is especially needed when we are working on an excessively deep type; + // if we don't do this, then we spend all of our time adding more and more + // scopes that need to be searched in isSymbolAccessible later. Since all we + // really want to do is to mark certain names as unavailable, we can just keep + // all of the names we're introducing in one large table and push/pop from it as + // needed; isSymbolAccessible will walk upward and find the closest "fake" scope, + // which will conveniently report on any and all faked scopes in the chain. + // + // It'd likely be better to store this somewhere else for isSymbolAccessible, but + // since that API _only_ uses the enclosing declaration (and its parents), this is + // seems like the best way to inject names into that search process. + // + // Note that we only check the most immediate enclosingDeclaration; the only place we + // could potentially add another fake scope into the chain is right here, so we don't + // traverse all ancestors. + pushFakeScope := func(kind string, addAll func(addSymbol func(name string, symbol *ast.Symbol))) *func() { + // We only ever need to look two declarations upward. + // Debug.assert(context.enclosingDeclaration) // !!! + var existingFakeScope *ast.Node + if b.links.Has(b.ctx.enclosingDeclaration) { + links := b.links.Get(b.ctx.enclosingDeclaration) + if links.fakeScopeForSignatureDeclaration != nil && *links.fakeScopeForSignatureDeclaration == kind { + existingFakeScope = b.ctx.enclosingDeclaration + } + } + if existingFakeScope == nil && b.ctx.enclosingDeclaration.Parent != nil { + if b.links.Has(b.ctx.enclosingDeclaration.Parent) { + links := b.links.Get(b.ctx.enclosingDeclaration.Parent) + if links.fakeScopeForSignatureDeclaration != nil && *links.fakeScopeForSignatureDeclaration == kind { + existingFakeScope = b.ctx.enclosingDeclaration.Parent + } + } + } + // Debug.assertOptionalNode(existingFakeScope, isBlock) // !!! + + var locals ast.SymbolTable + if existingFakeScope != nil { + locals = existingFakeScope.Locals() + } else { + locals = createSymbolTable([]*ast.Symbol{}) + } + newLocals := []string{} + oldLocals := []localsRecord{} + addAll(func(name string, symbol *ast.Symbol) { + // Add cleanup information only if we don't own the fake scope + if existingFakeScope != nil { + oldSymbol, ok := locals[name] + if !ok || oldSymbol == nil { + newLocals = append(newLocals, name) + } else { + oldLocals = append(oldLocals, localsRecord{name, oldSymbol}) + } + } + locals[name] = symbol + }) + + if existingFakeScope == nil { + // Use a Block for this; the type of the node doesn't matter so long as it + // has locals, and this is cheaper/easier than using a function-ish Node. + fakeScope := b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false) + b.links.Get(fakeScope).fakeScopeForSignatureDeclaration = &kind + data := fakeScope.LocalsContainerData() + data.Locals = locals + fakeScope.Parent = b.ctx.enclosingDeclaration + b.ctx.enclosingDeclaration = fakeScope + return nil + } else { + // We did not create the current scope, so we have to clean it up + undo := func() { + for _, s := range newLocals { + delete(locals, s) + } + for _, s := range oldLocals { + locals[s.name] = s.oldSymbol + } + } + return &undo + } + } + + if expandedParams == nil || !core.Some(*expandedParams, func(p *ast.Symbol) bool { return p != nil }) { + cleanupParams = nil + } else { + cleanupParams = pushFakeScope("params", func(add func(name string, symbol *ast.Symbol)) { + if expandedParams == nil { + return + } + for pIndex := 0; pIndex < len(*expandedParams); pIndex++ { + param := (*expandedParams)[pIndex] + originalParam := (*originalParameters)[pIndex] + if originalParameters != nil && originalParam != param { + // Can't reference parameters that come from an expansion + add(param.Name, b.ch.unknownSymbol) + // Can't reference the original expanded parameter either + if originalParam != nil { + add(originalParam.Name, b.ch.unknownSymbol) + } + } else if !core.Some(param.Declarations, func(d *ast.Node) bool { + var bindElement *(func(e *ast.BindingElement)) + var bindPattern *(func(e *ast.BindingPattern)) + + bindPatternWorker := func(p *ast.BindingPattern) { + for _, e := range p.Elements.Nodes { + switch e.Kind { + case ast.KindOmittedExpression: + return + case ast.KindBindingElement: + (*bindElement)(e.AsBindingElement()) + return + default: + panic("Unhandled binding element kind") + } + } + } + + bindElementWorker := func(e *ast.BindingElement) { + if ast.IsBindingPattern(e.Name()) { + (*bindPattern)(e.Name().AsBindingPattern()) + return + } + symbol := b.ch.getSymbolOfDeclaration(e.AsNode()) + add(symbol.Name, symbol) + } + bindElement = &bindElementWorker + bindPattern = &bindPatternWorker + + if ast.IsParameter(d) && ast.IsBindingPattern(d.Name()) { + (*bindPattern)(d.Name().AsBindingPattern()) + return true + } + return false + + }) { + add(param.Name, param) + } + } + }) + } + + if b.ctx.flags&NodeBuilderFlagsGenerateNamesForShadowedTypeParams != 0 && typeParameters != nil && core.Some(*typeParameters, func(p *Type) bool { return p != nil }) { + cleanupTypeParams = pushFakeScope("typeParams", func(add func(name string, symbol *ast.Symbol)) { + if typeParameters == nil { + return + } + for _, typeParam := range *typeParameters { + if typeParam == nil { + continue + } + typeParamName := b.typeParameterToName(typeParam).Text + add(typeParamName, typeParam.symbol) + } + }) + } + + } + + return func() { + (*cleanupParams)() + (*cleanupTypeParams)() + cleanupContext() + b.ctx.enclosingDeclaration = oldEnclosingDecl + b.ctx.mapper = oldMapper + } +} diff --git a/internal/checker/printer.go b/internal/checker/printer.go index 454b8680c5..4e14b1d75e 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -10,638 +10,257 @@ import ( "github.com/microsoft/typescript-go/internal/scanner" ) -func (c *Checker) getTypePrecedence(t *Type) ast.TypePrecedence { - if t.alias == nil { - switch { - case t.flags&TypeFlagsConditional != 0: - return ast.TypePrecedenceConditional - case t.flags&TypeFlagsIntersection != 0: - return ast.TypePrecedenceIntersection - case t.flags&TypeFlagsUnion != 0 && t.flags&TypeFlagsBoolean == 0: - return ast.TypePrecedenceUnion - case t.flags&TypeFlagsIndex != 0 || isInferTypeParameter(t): - return ast.TypePrecedenceTypeOperator - case c.isArrayType(t): - return ast.TypePrecedencePostfix - case t.objectFlags&ObjectFlagsClassOrInterface == 0 && c.getSingleCallOrConstructSignature(t) != nil: - return ast.TypePrecedenceFunction - } - } - return ast.TypePrecedenceNonArray -} - -func (c *Checker) SymbolToString(s *ast.Symbol) string { - return c.symbolToString(s) -} - -func (c *Checker) symbolToString(s *ast.Symbol) string { - if scanner.IsValidIdentifier(s.Name, c.languageVersion) { - return s.Name - } - if s.ValueDeclaration != nil { - if ast.IsJsxAttribute(s.ValueDeclaration) { - return "\"" + s.Name + "\"" - } - name := ast.GetNameOfDeclaration(s.ValueDeclaration) - if name != nil { - return scanner.GetTextOfNode(name) - } - } - if len(s.Name) == 0 || s.Name[0] != '\xFE' { - return s.Name // !!! Implement escaping - } - switch s.Name { - case ast.InternalSymbolNameClass: - return "(Anonymous class)" - case ast.InternalSymbolNameFunction: - return "(Anonymous function)" - case ast.InternalSymbolNameType, ast.InternalSymbolNameObject: - return "(Anonymous type)" - case ast.InternalSymbolNameComputed: - return "(Computed name)" - } - if len(s.Name) >= 2 && s.Name[1] == '@' { - return "(Unique symbol)" - } - return "(Missing)" -} - -func (c *Checker) TypeToString(t *Type) string { - return c.typeToStringEx(t, nil, TypeFormatFlagsNone) -} - -func (c *Checker) typeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - p := c.newPrinter(flags) - if flags&TypeFormatFlagsNoTypeReduction == 0 { - t = c.getReducedType(t) - } - p.printType(t) - return p.string() -} - -func (c *Checker) SourceFileWithTypes(sourceFile *ast.SourceFile) string { - p := c.newPrinter(TypeFormatFlagsInTypeAlias) - p.printSourceFileWithTypes(sourceFile) - return p.string() -} - -func (c *Checker) signatureToString(s *Signature) string { - p := c.newPrinter(TypeFormatFlagsNone) - if s.flags&SignatureFlagsConstruct != 0 { - p.print("new ") - } - p.printSignature(s, ": ") - return p.string() -} - -func (c *Checker) typePredicateToString(t *TypePredicate) string { - p := c.newPrinter(TypeFormatFlagsNone) - p.printTypePredicate(t) - return p.string() +// TODO: Memoize once per checker to retain threadsafety +func createPrinterWithDefaults() *printer.Printer { + return printer.NewPrinter(printer.PrinterOptions{}, printer.PrintHandlers{}, nil) } -func (c *Checker) valueToString(value any) string { - p := c.newPrinter(TypeFormatFlagsNone) - p.printValue(value) - return p.string() +func createPrinterWithRemoveComments() *printer.Printer { + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil) } -type Printer struct { - c *Checker - flags TypeFormatFlags - sb strings.Builder - printing core.Set[*Type] - depth int32 - extendsTypeDepth int32 +func createPrinterWithRemoveCommentsOmitTrailingSemicolon() *printer.Printer { + // TODO: OmitTrailingSemicolon support + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil) } -func (c *Checker) newPrinter(flags TypeFormatFlags) *Printer { - return &Printer{c: c, flags: flags} +func createPrinterWithRemoveCommentsNeverAsciiEscape() *printer.Printer { + // TODO: NeverAsciiEscape support + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil) } -func (p *Printer) string() string { - return p.sb.String() +func getTrailingSemicolonDeferringWriter(writer printer.EmitTextWriter) printer.EmitTextWriter { + // TODO: wrap arbitrary writer with writer that only commits semicolon writes on following write operations (is OmitTrailingSemicolon printer option redundant?) + return writer } -func (p *Printer) print(s string) { - p.sb.WriteString(s) +func (c *Checker) TypeToString(type_ *Type) string { + return c.typeToStringEx(type_, nil, TypeFormatFlagsNone, nil) } -func (p *Printer) printName(symbol *ast.Symbol) { - p.print(p.c.symbolToString(symbol)) +func toNodeBuilderFlags(flags TypeFormatFlags) NodeBuilderFlags { + return NodeBuilderFlags(flags & TypeFormatFlagsNodeBuilderFlagsMask) } -func (p *Printer) printQualifiedName(symbol *ast.Symbol) { - if p.flags&TypeFormatFlagsUseFullyQualifiedType != 0 && symbol.Parent != nil { - p.printQualifiedName(symbol.Parent) - p.print(".") - } - if symbol.Flags&ast.SymbolFlagsModule != 0 && strings.HasPrefix(symbol.Name, "\"") { - p.print("import(") - p.print(symbol.Name) - p.print(")") - return - } - p.printName(symbol) -} - -func (p *Printer) printTypeEx(t *Type, precedence ast.TypePrecedence) { - if p.c.getTypePrecedence(t) < precedence { - p.print("(") - p.printType(t) - p.print(")") - } else { - p.printType(t) +func (c *Checker) typeToStringEx(type_ *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags, writer printer.EmitTextWriter) string { + if writer == nil { + writer = printer.NewTextWriter("") } -} - -func (p *Printer) printType(t *Type) { - if p.sb.Len() > 1_000_000 { - p.print("...") - return + noTruncation := (c.compilerOptions.NoErrorTruncation == core.TSTrue) || (flags&TypeFormatFlagsNoTruncation != 0) + combinedFlags := toNodeBuilderFlags(flags) | NodeBuilderFlagsIgnoreErrors + if noTruncation { + combinedFlags = combinedFlags | NodeBuilderFlagsNoTruncation } - - if t.alias != nil && (p.flags&TypeFormatFlagsInTypeAlias == 0 || p.depth > 0) { - p.printQualifiedName(t.alias.symbol) - p.printTypeArguments(t.alias.typeArguments) - } else { - p.printTypeNoAlias(t) + typeNode := c.nodeBuilder.typeToTypeNode(type_, enclosingDeclaration, combinedFlags, InternalNodeBuilderFlagsNone, nil) + if typeNode == nil { + panic("should always get typenode") } -} - -func (p *Printer) printTypeNoAlias(t *Type) { - p.depth++ - switch { - case t.flags&TypeFlagsIntrinsic != 0: - p.print(t.AsIntrinsicType().intrinsicName) - case t.flags&(TypeFlagsLiteral|TypeFlagsEnum) != 0: - p.printLiteralType(t) - case t.flags&TypeFlagsUniqueESSymbol != 0: - p.printUniqueESSymbolType(t) - case t.flags&TypeFlagsUnion != 0: - p.printUnionType(t) - case t.flags&TypeFlagsIntersection != 0: - p.printIntersectionType(t) - case t.flags&TypeFlagsTypeParameter != 0: - p.printTypeParameter(t) - case t.flags&TypeFlagsObject != 0: - p.printRecursive(t, (*Printer).printObjectType) - case t.flags&TypeFlagsIndex != 0: - p.printRecursive(t, (*Printer).printIndexType) - case t.flags&TypeFlagsIndexedAccess != 0: - p.printRecursive(t, (*Printer).printIndexedAccessType) - case t.flags&TypeFlagsConditional != 0: - p.printRecursive(t, (*Printer).printConditionalType) - case t.flags&TypeFlagsTemplateLiteral != 0: - p.printTemplateLiteralType(t) - case t.flags&TypeFlagsStringMapping != 0: - p.printStringMappingType(t) - case t.flags&TypeFlagsSubstitution != 0: - if p.c.isNoInferType(t) { - if noInferSymbol := p.c.getGlobalNoInferSymbolOrNil(); noInferSymbol != nil { - p.printQualifiedName(noInferSymbol) - p.printTypeArguments([]*Type{t.AsSubstitutionType().baseType}) - break - } - } - 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) - f(p, t) - p.printing.Delete(t) + // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. + // Otherwise, we always strip comments out. + var printer *printer.Printer + if type_ == c.unresolvedType { + printer = createPrinterWithDefaults() } else { - p.print("???") + printer = createPrinterWithRemoveComments() } -} - -func (p *Printer) printLiteralType(t *Type) { - if t.flags&(TypeFlagsEnumLiteral|TypeFlagsEnum) != 0 { - p.printEnumLiteral(t) - } else { - p.printValue(t.AsLiteralType().value) + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } -} + printer.Write(typeNode /*sourceFile*/, sourceFile, writer, nil) + result := writer.String() -func (p *Printer) printValue(value any) { - switch value := value.(type) { - case string: - p.printStringLiteral(value) - case jsnum.Number: - p.printNumberLiteral(value) - case bool: - p.printBooleanLiteral(value) - case jsnum.PseudoBigInt: - p.printBigIntLiteral(value) + maxLength := defaultMaximumTruncationLength * 2 + if noTruncation { + maxLength = noTruncationMaximumTruncationLength * 2 } + if maxLength > 0 && result != "" && len(result) >= maxLength { + return result[0:maxLength-len("...")] + "..." + } + return result } -func (p *Printer) printStringLiteral(s string) { - p.print("\"") - p.print(printer.EscapeString(s, '"')) - p.print("\"") -} - -func (p *Printer) printNumberLiteral(f jsnum.Number) { - p.print(f.String()) -} - -func (p *Printer) printBooleanLiteral(b bool) { - p.print(core.IfElse(b, "true", "false")) -} - -func (p *Printer) printBigIntLiteral(b jsnum.PseudoBigInt) { - p.print(b.String() + "n") -} - -func (p *Printer) printUniqueESSymbolType(t *Type) { - p.print("unique symbol") -} - -func (p *Printer) printTemplateLiteralType(t *Type) { - texts := t.AsTemplateLiteralType().texts - types := t.AsTemplateLiteralType().types - p.print("`") - p.print(texts[0]) - for i, t := range types { - p.print("${") - p.printType(t) - p.print("}") - p.print(texts[i+1]) - } - p.print("`") +func (c *Checker) SymbolToString(s *ast.Symbol) string { + return c.symbolToString(s) } -func (p *Printer) printStringMappingType(t *Type) { - p.printName(t.symbol) - p.print("<") - p.printType(t.AsStringMappingType().target) - p.print(">") +func (c *Checker) symbolToString(symbol *ast.Symbol) string { + return "" } -func (p *Printer) printEnumLiteral(t *Type) { - if parent := p.c.getParentOfSymbol(t.symbol); parent != nil { - p.printQualifiedName(parent) - if p.c.getDeclaredTypeOfSymbol(parent) != t { - p.print(".") - p.printName(t.symbol) - } - return +// TODO: port SymbolFormatFlags +func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags, kind *SignatureKind, writer printer.EmitTextWriter) string { + if writer == nil { + writer = printer.SingleLineStringWriter } - p.printQualifiedName(t.symbol) -} -func (p *Printer) printObjectType(t *Type) { - switch { - case t.objectFlags&ObjectFlagsReference != 0: - p.printParameterizedType(t) - case t.objectFlags&ObjectFlagsClassOrInterface != 0: - p.printQualifiedName(t.symbol) - case p.c.isGenericMappedType(t) || t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError: - p.printMappedType(t) - default: - p.printAnonymousType(t) + nodeFlags := NodeBuilderFlagsIgnoreErrors + internalNodeFlags := InternalNodeBuilderFlagsNone + if flags&SymbolFormatFlagsUseOnlyExternalAliasing != 0 { + nodeFlags |= NodeBuilderFlagsUseOnlyExternalAliasing } -} - -func (p *Printer) printParameterizedType(t *Type) { - switch { - case p.c.isArrayType(t) && p.flags&TypeFormatFlagsWriteArrayAsGenericType == 0: - p.printArrayType(t) - case isTupleType(t): - p.printTupleType(t) - default: - p.printTypeReference(t) + if flags&SymbolFormatFlagsWriteTypeParametersOrArguments != 0 { + nodeFlags |= NodeBuilderFlagsWriteTypeParametersInQualifiedName + } + if flags&SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope != 0 { + nodeFlags |= NodeBuilderFlagsUseAliasDefinedOutsideCurrentScope + } + if flags&SymbolFormatFlagsDoNotIncludeSymbolChain != 0 { + internalNodeFlags |= InternalNodeBuilderFlagsDoNotIncludeSymbolChain + } + if flags&SymbolFormatFlagsWriteComputedProps != 0 { + internalNodeFlags |= InternalNodeBuilderFlagsWriteComputedProps } -} - -func (p *Printer) printTypeReference(t *Type) { - p.printQualifiedName(t.symbol) - p.printTypeArguments(p.c.getTypeArguments(t)[:p.c.getTypeReferenceArity(t)]) -} -func (p *Printer) printTypeArguments(typeArguments []*Type) { - if len(typeArguments) != 0 { - p.print("<") - var tail bool - for _, t := range typeArguments { - if tail { - p.print(", ") - } - p.printType(t) - tail = true + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) + } + if writer == printer.SingleLineStringWriter { + // handle uses of the single-line writer during an ongoing write + existing := writer.String() + defer writer.Clear() + if existing != "" { + defer writer.WriteKeyword(existing) } - p.print(">") } -} + var printer_ *printer.Printer + if enclosingDeclaration != nil && enclosingDeclaration.Kind == ast.KindSourceFile { + printer_ = createPrinterWithRemoveCommentsNeverAsciiEscape() + } else { + printer_ = createPrinterWithRemoveComments() + } -func (p *Printer) printArrayType(t *Type) { - d := t.AsTypeReference() - if d.target != p.c.globalArrayType { - p.print("readonly ") + var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + if flags&SymbolFormatFlagsAllowAnyNodeKind != 0 { + builder = c.nodeBuilder.symbolToNode + } else { + builder = c.nodeBuilder.symbolToEntityName } - p.printTypeEx(p.c.getTypeArguments(t)[0], ast.TypePrecedencePostfix) - p.print("[]") + entity := builder(symbol, meaning, enclosingDeclaration, nodeFlags, internalNodeFlags, nil) // TODO: GH#18217 + printer_.Write(entity /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 + return writer.String() } -func (p *Printer) printTupleType(t *Type) { - if t.TargetTupleType().readonly { - p.print("readonly ") - } - p.print("[") - elementInfos := t.TargetTupleType().elementInfos - typeArguments := p.c.getTypeArguments(t) - var tail bool - for i, info := range elementInfos { - t := typeArguments[i] - if tail { - p.print(", ") - } - if info.flags&ElementFlagsVariable != 0 { - p.print("...") - } - if info.labeledDeclaration != nil { - p.print(info.labeledDeclaration.Name().Text()) - if info.flags&ElementFlagsOptional != 0 { - p.print("?: ") - p.printType(p.c.removeMissingType(t, true)) - } else { - p.print(": ") - if info.flags&ElementFlagsRest != 0 { - p.printTypeEx(t, ast.TypePrecedencePostfix) - p.print("[]") - } else { - p.printType(t) - } - } - } else { - if info.flags&ElementFlagsOptional != 0 { - p.printTypeEx(p.c.removeMissingType(t, true), ast.TypePrecedencePostfix) - p.print("?") - } else if info.flags&ElementFlagsRest != 0 { - p.printTypeEx(t, ast.TypePrecedencePostfix) - p.print("[]") - } else { - p.printType(t) - } - } - tail = true - } - p.print("]") +func (c *Checker) signatureToString(signature *Signature) string { + return c.signatureToStringEx(signature, nil, TypeFormatFlagsNone, nil, nil) } -func (p *Printer) printAnonymousType(t *Type) { - if t.symbol != nil && len(t.symbol.Name) != 0 { - if t.symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 { - if t == p.c.getTypeOfSymbol(t.symbol) { - p.print("typeof ") - p.printQualifiedName(t.symbol) - return - } - } - } - props := p.c.getPropertiesOfObjectType(t) - callSignatures := p.c.getSignaturesOfType(t, SignatureKindCall) - constructSignatures := p.c.getSignaturesOfType(t, SignatureKindConstruct) - if len(props) == 0 { - if len(callSignatures) == 1 && len(constructSignatures) == 0 { - p.printSignature(callSignatures[0], " => ") - return +func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration *ast.Node, flags TypeFormatFlags, kind *SignatureKind, writer printer.EmitTextWriter) string { + var sigOutput ast.Kind + if flags&TypeFormatFlagsWriteArrowStyleSignature != 0 { + if kind != nil && *kind == SignatureKindConstruct { + sigOutput = ast.KindConstructorType + } else { + sigOutput = ast.KindFunctionType } - if len(callSignatures) == 0 && len(constructSignatures) == 1 { - p.print("new ") - p.printSignature(constructSignatures[0], " => ") - return + } else { + if kind != nil && *kind == SignatureKindConstruct { + sigOutput = ast.KindConstructSignature + } else { + sigOutput = ast.KindCallSignature } } - p.print("{") - hasMembers := false - for _, sig := range callSignatures { - p.print(" ") - p.printSignature(sig, ": ") - p.print(";") - hasMembers = true - } - for _, sig := range constructSignatures { - p.print(" new ") - p.printSignature(sig, ": ") - p.print(";") - hasMembers = true - } - for _, info := range p.c.getIndexInfosOfType(t) { - if info.isReadonly { - p.print(" readonly") - } - p.print(" [") - p.print(getNameFromIndexInfo(info)) - p.print(": ") - p.printType(info.keyType) - p.print("]: ") - p.printType(info.valueType) - p.print(";") - hasMembers = true - } - for _, prop := range props { - if p.c.isReadonlySymbol(prop) { - p.print(" readonly") - } - p.print(" ") - p.printName(prop) - if prop.Flags&ast.SymbolFlagsOptional != 0 { - p.print("?") + if writer == nil { + writer = printer.SingleLineStringWriter + } + combinedFlags := toNodeBuilderFlags(flags) | NodeBuilderFlagsIgnoreErrors | NodeBuilderFlagsWriteTypeParametersInQualifiedName + sig := c.nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, InternalNodeBuilderFlagsNone, nil) + printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon() + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) + } + if writer == printer.SingleLineStringWriter { + // handle uses of the single-line writer during an ongoing write + existing := writer.String() + defer writer.Clear() + if existing != "" { + defer writer.WriteKeyword(existing) } - p.print(": ") - p.printType(p.c.getNonMissingTypeOfSymbol(prop)) - p.print(";") - hasMembers = true } - if hasMembers { - p.print(" ") - } - p.print("}") + printer_.Write(sig /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 + return writer.String() } -func (p *Printer) printSignature(sig *Signature, returnSeparator string) { - if len(sig.typeParameters) != 0 { - p.print("<") - var tail bool - for _, tp := range sig.typeParameters { - if tail { - p.print(", ") - } - p.printTypeParameterAndConstraint(tp) - tail = true - } - p.print(">") - } - p.print("(") - var tail bool - if sig.thisParameter != nil { - p.print("this: ") - p.printType(p.c.getTypeOfSymbol(sig.thisParameter)) - tail = true - } - expandedParameters := p.c.GetExpandedParameters(sig) - // If the expanded parameter list had a variadic in a non-trailing position, don't expand it - parameters := core.IfElse(core.Some(expandedParameters, func(s *ast.Symbol) bool { - return s != expandedParameters[len(expandedParameters)-1] && s.CheckFlags&ast.CheckFlagsRestParameter != 0 - }), sig.parameters, expandedParameters) - for i, param := range parameters { - if tail { - p.print(", ") - } - if param.ValueDeclaration != nil && isRestParameter(param.ValueDeclaration) || param.CheckFlags&ast.CheckFlagsRestParameter != 0 { - p.print("...") - p.printName(param) - } else { - p.printName(param) - if i >= p.c.getMinArgumentCountEx(sig, MinArgumentCountFlagsVoidIsNonOptional) { - p.print("?") - } - } - p.print(": ") - p.printType(p.c.getTypeOfSymbol(param)) - tail = true - } - p.print(")") - p.print(returnSeparator) - if pred := p.c.getTypePredicateOfSignature(sig); pred != nil { - p.printTypePredicate(pred) - } else { - p.printType(p.c.getReturnTypeOfSignature(sig)) - } +func (c *Checker) typePredicateToString(typePredicate *TypePredicate) string { + return c.typePredicateToStringEx(typePredicate, nil, TypeFormatFlagsUseAliasDefinedOutsideCurrentScope, nil) } -func (p *Printer) printTypePredicate(pred *TypePredicate) { - if pred.kind == TypePredicateKindAssertsThis || pred.kind == TypePredicateKindAssertsIdentifier { - p.print("asserts ") - } - if pred.kind == TypePredicateKindThis || pred.kind == TypePredicateKindAssertsThis { - p.print("this") - } else { - p.print(pred.parameterName) +func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosingDeclaration *ast.Node, flags TypeFormatFlags, writer printer.EmitTextWriter) string { + if writer == nil { + writer = printer.SingleLineStringWriter } - if pred.t != nil { - p.print(" is ") - p.printType(pred.t) + combinedFlags := toNodeBuilderFlags(flags) | NodeBuilderFlagsIgnoreErrors | NodeBuilderFlagsWriteTypeParametersInQualifiedName + predicate := c.nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, InternalNodeBuilderFlagsNone, nil) // TODO: GH#18217 + printer_ := createPrinterWithRemoveComments() + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } -} - -func (p *Printer) printTypeParameter(t *Type) { - switch { - case t.AsTypeParameter().isThisType: - p.print("this") - case p.extendsTypeDepth > 0 && isInferTypeParameter(t): - p.print("infer ") - p.printTypeParameterAndConstraint(t) - case t.symbol != nil: - p.printName(t.symbol) - default: - p.print("???") + if writer == printer.SingleLineStringWriter { + // handle uses of the single-line writer during an ongoing write + existing := writer.String() + defer writer.Clear() + if existing != "" { + defer writer.WriteKeyword(existing) + } } + printer_.Write(predicate /*sourceFile*/, sourceFile, writer, nil) + return writer.String() } -func (p *Printer) printTypeParameterAndConstraint(t *Type) { - p.printName(t.symbol) - if constraint := p.c.getConstraintOfTypeParameter(t); constraint != nil { - p.print(" extends ") - p.printType(constraint) +func (c *Checker) valueToString(value any) string { + switch value := value.(type) { + case string: + return "\"" + printer.EscapeString(value, '"') + "\"" + case jsnum.Number: + return value.String() + case bool: + return core.IfElse(value, "true", "false") + case jsnum.PseudoBigInt: + return value.String() + "n" } + panic("unhandled value type in valueToString") } -func (p *Printer) printUnionType(t *Type) { - switch { - case t.flags&TypeFlagsBoolean != 0: - p.print("boolean") - case t.flags&TypeFlagsEnumLiteral != 0: - p.printQualifiedName(t.symbol) - default: - u := t.AsUnionType() - if u.origin != nil { - p.printType(u.origin) - } else { - var tail bool - for _, t := range p.c.formatUnionTypes(u.types) { - if tail { - p.print(" | ") +func (c *Checker) formatUnionTypes(types []*Type) []*Type { + var result []*Type + var flags TypeFlags + for i := 0; i < len(types); i++ { + t := types[i] + flags |= t.flags + if t.flags&TypeFlagsNullable == 0 { + if t.flags&(TypeFlagsBooleanLiteral|TypeFlagsEnumLike) != 0 { + var baseType *Type + if t.flags&TypeFlagsBooleanLiteral != 0 { + baseType = c.booleanType + } else { + baseType = c.getBaseTypeOfEnumLikeType(t) + } + if baseType.flags&TypeFlagsUnion != 0 { + count := len(baseType.AsUnionType().types) + if i+count <= len(types) && c.getRegularTypeOfLiteralType(types[i+count-1]) == c.getRegularTypeOfLiteralType(baseType.AsUnionType().types[count-1]) { + result = append(result, baseType) + i += count - 1 + continue + } } - p.printTypeEx(t, ast.TypePrecedenceUnion) - tail = true } + result = append(result, t) } } -} - -func (p *Printer) printIntersectionType(t *Type) { - var tail bool - for _, t := range t.AsIntersectionType().types { - if tail { - p.print(" & ") - } - p.printTypeEx(t, ast.TypePrecedenceIntersection) - tail = true + if flags&TypeFlagsNull != 0 { + result = append(result, c.nullType) } -} - -func (p *Printer) printIndexType(t *Type) { - p.print("keyof ") - p.printTypeEx(t.AsIndexType().target, ast.TypePrecedenceTypeOperator) -} - -func (p *Printer) printIndexedAccessType(t *Type) { - p.printType(t.AsIndexedAccessType().objectType) - p.print("[") - p.printType(t.AsIndexedAccessType().indexType) - p.print("]") -} - -func (p *Printer) printConditionalType(t *Type) { - p.printType(t.AsConditionalType().checkType) - p.print(" extends ") - p.extendsTypeDepth++ - p.printType(t.AsConditionalType().extendsType) - p.extendsTypeDepth-- - p.print(" ? ") - p.printType(p.c.getTrueTypeFromConditionalType(t)) - p.print(" : ") - p.printType(p.c.getFalseTypeFromConditionalType(t)) -} - -func (p *Printer) printMappedType(t *Type) { - d := t.AsMappedType().declaration - p.print("{ ") - if d.ReadonlyToken != nil { - if d.ReadonlyToken.Kind != ast.KindReadonlyKeyword { - p.print(scanner.TokenToString(d.ReadonlyToken.Kind)) - } - p.print("readonly ") - } - p.print("[") - p.printName(p.c.getTypeParameterFromMappedType(t).symbol) - p.print(" in ") - p.printType(p.c.getConstraintTypeFromMappedType(t)) - nameType := p.c.getNameTypeFromMappedType(t) - if nameType != nil { - p.print(" as ") - p.printType(nameType) - } - p.print("]") - if d.QuestionToken != nil { - if d.QuestionToken.Kind != ast.KindQuestionToken { - p.print(scanner.TokenToString(d.QuestionToken.Kind)) - } - p.print("?") + if flags&TypeFlagsUndefined != 0 { + result = append(result, c.undefinedType) } - p.print(": ") - p.printType(p.c.getTemplateTypeFromMappedType(t)) - p.print("; }") + return result } -func (p *Printer) printSourceFileWithTypes(sourceFile *ast.SourceFile) { +func (c *Checker) SourceFileWithTypes(sourceFile *ast.SourceFile) string { + writer := printer.NewTextWriter("\n") var pos int var visit func(*ast.Node) bool var typesPrinted bool @@ -656,32 +275,33 @@ func (p *Printer) printSourceFileWithTypes(sourceFile *ast.SourceFile) { } if pos < nextLineStart { if typesPrinted { - p.print("\n") + writer.WriteLine() } - p.print(sourceFile.Text()[pos:nextLineStart]) + writer.Write(sourceFile.Text()[pos:nextLineStart]) pos = nextLineStart typesPrinted = false } } visit = func(node *ast.Node) bool { - text, t, isDeclaration := p.c.getTextAndTypeOfNode(node) + text, t, isDeclaration := c.getTextAndTypeOfNode(node) if text != "" && !strings.Contains(text, "\n") { printLinesBefore(node) - p.print(">") - p.print(text) - p.print(" : ") - p.printType(t) + writer.Write(">") + writer.Write(text) + writer.Write(" : ") + c.typeToStringEx(t, nil, TypeFormatFlagsNone, writer) if isDeclaration && t.flags&TypeFlagsEnumLiteral != 0 && t.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 { - p.print(" = ") - p.printValue(t.AsLiteralType().value) + writer.Write(" = ") + writer.Write(c.valueToString(t.AsLiteralType().value)) } - p.print("\n") + writer.WriteLine() typesPrinted = true } return node.ForEachChild(visit) } visit(sourceFile.AsNode()) - p.print(sourceFile.Text()[pos:sourceFile.End()]) + writer.Write(sourceFile.Text()[pos:sourceFile.End()]) + return writer.String() } func (c *Checker) getTextAndTypeOfNode(node *ast.Node) (string, *Type, bool) { @@ -701,42 +321,3 @@ func (c *Checker) getTextAndTypeOfNode(node *ast.Node) (string, *Type, bool) { } return "", nil, false } - -func (c *Checker) formatUnionTypes(types []*Type) []*Type { - var result []*Type - var flags TypeFlags - for i := 0; i < len(types); i++ { - t := types[i] - flags |= t.flags - if t.flags&TypeFlagsNullable == 0 { - if t.flags&(TypeFlagsBooleanLiteral|TypeFlagsEnumLike) != 0 { - var baseType *Type - if t.flags&TypeFlagsBooleanLiteral != 0 { - baseType = c.booleanType - } else { - baseType = c.getBaseTypeOfEnumLikeType(t) - } - if baseType.flags&TypeFlagsUnion != 0 { - count := len(baseType.AsUnionType().types) - if i+count <= len(types) && c.getRegularTypeOfLiteralType(types[i+count-1]) == c.getRegularTypeOfLiteralType(baseType.AsUnionType().types[count-1]) { - result = append(result, baseType) - i += count - 1 - continue - } - } - } - result = append(result, t) - } - } - if flags&TypeFlagsNull != 0 { - result = append(result, c.nullType) - } - if flags&TypeFlagsUndefined != 0 { - result = append(result, c.undefinedType) - } - return result -} - -func isInferTypeParameter(t *Type) bool { - return t.flags&TypeFlagsTypeParameter != 0 && t.symbol != nil && core.Some(t.symbol.Declarations, func(d *ast.Node) bool { return ast.IsInferTypeNode(d.Parent) }) -} diff --git a/internal/checker/relater.go b/internal/checker/relater.go index 4cca82aa2c..c4ca45a5ea 100644 --- a/internal/checker/relater.go +++ b/internal/checker/relater.go @@ -1267,13 +1267,13 @@ func isNonPrimitiveType(t *Type) bool { func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, string) { var leftStr string if c.symbolValueDeclarationIsContextSensitive(left.symbol) { - leftStr = c.typeToStringEx(left, left.symbol.ValueDeclaration, TypeFormatFlagsNone) + leftStr = c.typeToStringEx(left, left.symbol.ValueDeclaration, TypeFormatFlagsNone, nil) } else { leftStr = c.TypeToString(left) } var rightStr string if c.symbolValueDeclarationIsContextSensitive(right.symbol) { - rightStr = c.typeToStringEx(right, right.symbol.ValueDeclaration, TypeFormatFlagsNone) + rightStr = c.typeToStringEx(right, right.symbol.ValueDeclaration, TypeFormatFlagsNone, nil) } else { rightStr = c.TypeToString(right) } @@ -1285,7 +1285,7 @@ func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, } func (c *Checker) getTypeNameForErrorDisplay(t *Type) string { - return c.typeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsUseFullyQualifiedType) + return c.typeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsUseFullyQualifiedType, nil) } func (c *Checker) symbolValueDeclarationIsContextSensitive(symbol *ast.Symbol) bool { @@ -4645,7 +4645,7 @@ func (r *Relater) reportErrorResults(originalSource *Type, originalTarget *Type, prop = core.Find(r.c.getPropertiesOfUnionOrIntersectionType(originalTarget), isConflictingPrivateProperty) } if prop != nil { - r.reportError(message, r.c.typeToStringEx(originalTarget, nil /*enclosingDeclaration*/, TypeFormatFlagsNoTypeReduction), r.c.symbolToString(prop)) + r.reportError(message, r.c.typeToStringEx(originalTarget, nil /*enclosingDeclaration*/, TypeFormatFlagsNoTypeReduction, nil), r.c.symbolToString(prop)) } } r.reportRelationError(headMessage, source, target) diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go new file mode 100644 index 0000000000..3e4fb8dbd6 --- /dev/null +++ b/internal/checker/symbolaccessibility.go @@ -0,0 +1,76 @@ +package checker + +import "github.com/microsoft/typescript-go/internal/ast" + +type SymbolAccessibility int32 + +const ( + SymbolAccessibilityAccessible SymbolAccessibility = iota + SymbolAccessibilityNotAccessible + SymbolAccessibilityCannotBeNamed + SymbolAccessibilityNotResolved +) + +type SymbolAccessibilityResult struct { + accessibility SymbolAccessibility + aliasesToMakeVisible []*ast.Node // aliases that need to have this symbol visible + errorSymbolName string // Optional symbol name that results in error + errorNode *ast.Node // optional node that results in error +} + +func (ch *Checker) IsTypeSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + return false // !!! +} + +func (ch *Checker) IsValueSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + return false // !!! +} + +/** + * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested + * + * @param symbol a Symbol to check if accessible + * @param enclosingDeclaration a Node containing reference to the symbol + * @param meaning a SymbolFlags to check if such meaning of the symbol is accessible + * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible + */ + +func (c *Checker) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool) SymbolAccessibilityResult { + return c.isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, true /*allowModules*/) +} + +func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) SymbolAccessibilityResult { + // if symbol != nil && enclosingDeclaration != nil { + // result := c.isAnySymbolAccessible([]*ast.Symbol{symbol}, enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules) + // if result != nil { + // return result + // } + + // // This could be a symbol that is not exported in the external module + // // or it could be a symbol from different external module that is not aliased and hence cannot be named + // symbolExternalModule := forEach(symbol.Declarations, c.getExternalModuleContainer) + // if symbolExternalModule != nil { + // enclosingExternalModule := c.getExternalModuleContainer(enclosingDeclaration) + // if symbolExternalModule != enclosingExternalModule { + // // name from different external module that is not visible + // return SymbolAccessibilityResult{ + // accessibility: SymbolAccessibilityCannotBeNamed, + // errorSymbolName: c.symbolToString(symbol, enclosingDeclaration, meaning), + // errorModuleName: c.symbolToString(symbolExternalModule), + // errorNode: ifElse(isInJSFile(enclosingDeclaration), enclosingDeclaration, nil), + // } + // } + // } + + // // Just a local name that is not accessible + // return SymbolAccessibilityResult{ + // accessibility: SymbolAccessibilityNotAccessible, + // errorSymbolName: c.symbolToString(symbol, enclosingDeclaration, meaning), + // } + // } + + // return SymbolAccessibilityResult{ + // accessibility: SymbolAccessibilityAccessible, + // } + return SymbolAccessibilityResult{} // !!! +} diff --git a/internal/checker/symboltracker.go b/internal/checker/symboltracker.go new file mode 100644 index 0000000000..10da2486fb --- /dev/null +++ b/internal/checker/symboltracker.go @@ -0,0 +1,107 @@ +package checker + +import "github.com/microsoft/typescript-go/internal/ast" + +// TODO: previously all symboltracker methods were optional, but now they're required. +type SymbolTracker interface { + GetModuleSpecifierGenerationHost() any // !!! + + TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool + ReportInaccessibleThisError() + ReportPrivateInBaseOfClassExpression(propertyName string) + ReportInaccessibleUniqueSymbolError() + ReportCyclicStructureError() + ReportLikelyUnsafeImportRequiredError(specifier string) + ReportTruncationError() + ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) + ReportNonSerializableProperty(propertyName string) + + ReportInferenceFallback(node *ast.Node) + PushErrorFallbackNode(node *ast.Node) + PopErrorFallbackNode() +} + +type SymbolTrackerImpl struct { + context NodeBuilderContext + inner SymbolTracker + DisableTrackSymbol bool +} + +func NewSymbolTrackerImpl(context NodeBuilderContext, tracker SymbolTracker) *SymbolTrackerImpl { + // TODO: unwrap `tracker` before setting `inner` + return &SymbolTrackerImpl{context, tracker, false} +} + +func (this *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { + return this.inner.GetModuleSpecifierGenerationHost() +} + +func (this *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { + if !this.DisableTrackSymbol { + if this.inner.TrackSymbol(symbol, enclosingDeclaration, meaning) { + this.onDiagnosticReported() + return true + } + // Skip recording type parameters as they dont contribute to late painted statements + if symbol.Flags&ast.SymbolFlagsTypeParameter == 0 { + this.context.trackedSymbols = append(this.context.trackedSymbols, &TrackedSymbolArgs{symbol, enclosingDeclaration, meaning}) + } + } + return false +} + +func (this *SymbolTrackerImpl) ReportInaccessibleThisError() { + this.onDiagnosticReported() + this.inner.ReportInaccessibleThisError() +} + +func (this *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) { + this.onDiagnosticReported() + this.inner.ReportPrivateInBaseOfClassExpression(propertyName) +} + +func (this *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() { + this.onDiagnosticReported() + this.inner.ReportInaccessibleUniqueSymbolError() +} + +func (this *SymbolTrackerImpl) ReportCyclicStructureError() { + this.onDiagnosticReported() + this.inner.ReportCyclicStructureError() +} + +func (this *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) { + this.onDiagnosticReported() + this.inner.ReportLikelyUnsafeImportRequiredError(specifier) +} + +func (this *SymbolTrackerImpl) ReportTruncationError() { + this.onDiagnosticReported() + this.inner.ReportTruncationError() +} + +func (this *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) { + this.onDiagnosticReported() + this.inner.ReportNonlocalAugmentation(containingFile, parentSymbol, augmentingSymbol) +} + +func (this *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) { + this.onDiagnosticReported() + this.inner.ReportNonSerializableProperty(propertyName) +} + +func (this *SymbolTrackerImpl) onDiagnosticReported() { + this.context.reportedDiagnostic = true +} + +func (this *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) { + this.inner.ReportInferenceFallback(node) +} + +func (this *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) { + this.inner.PushErrorFallbackNode(node) +} + +func (this *SymbolTrackerImpl) PopErrorFallbackNode() { + this.inner.PopErrorFallbackNode() +} diff --git a/internal/checker/types.go b/internal/checker/types.go index fa77c35cce..061e6ec734 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -75,6 +75,37 @@ const ( TypeFormatFlagsInTypeAlias TypeFormatFlags = 1 << 23 // Writing type in type alias declaration ) +const TypeFormatFlagsNodeBuilderFlagsMask = TypeFormatFlagsNoTruncation | TypeFormatFlagsWriteArrayAsGenericType | TypeFormatFlagsGenerateNamesForShadowedTypeParams | TypeFormatFlagsUseStructuralFallback | TypeFormatFlagsWriteTypeArgumentsOfSignature | + TypeFormatFlagsUseFullyQualifiedType | TypeFormatFlagsSuppressAnyReturnType | TypeFormatFlagsMultilineObjectLiterals | TypeFormatFlagsWriteClassExpressionAsTypeLiteral | + TypeFormatFlagsUseTypeOfFunction | TypeFormatFlagsOmitParameterModifiers | TypeFormatFlagsUseAliasDefinedOutsideCurrentScope | TypeFormatFlagsAllowUniqueESSymbolType | TypeFormatFlagsInTypeAlias | + TypeFormatFlagsUseSingleQuotesForStringLiteralType | TypeFormatFlagsNoTypeReduction | TypeFormatFlagsOmitThisParameter + +// dprint-ignore +type SymbolFormatFlags int32 + +const ( + SymbolFormatFlagsNone SymbolFormatFlags = 0 + // Write symbols's type argument if it is instantiated symbol + // eg. class C { p: T } <-- Show p as C.p here + // var a: C; + // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p + SymbolFormatFlagsWriteTypeParametersOrArguments SymbolFormatFlags = 1 << 0 + // Use only external alias information to get the symbol name in the given context + // eg. module m { export class c { } } import x = m.c; + // When this flag is specified m.c will be used to refer to the class instead of alias symbol x + SymbolFormatFlagsUseOnlyExternalAliasing SymbolFormatFlags = 1 << 1 + // Build symbol name using any nodes needed, instead of just components of an entity name + SymbolFormatFlagsAllowAnyNodeKind SymbolFormatFlags = 1 << 2 + // Prefer aliases which are not directly visible + SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope SymbolFormatFlags = 1 << 3 + // { [E.A]: 1 } + /** @internal */ + SymbolFormatFlagsWriteComputedProps SymbolFormatFlags = 1 << 4 + // Skip building an accessible symbol chain + /** @internal */ + SymbolFormatFlagsDoNotIncludeSymbolChain SymbolFormatFlags = 1 << 5 +) + // Ids type TypeId uint32 @@ -697,6 +728,8 @@ type StructuredType struct { signatures []*Signature // Signatures (call + construct) callSignatureCount int // Count of call signatures indexInfos []*IndexInfo + + objectTypeWithoutAbstractConstructSignatures *Type } func (t *StructuredType) AsStructuredType() *StructuredType { return t } diff --git a/internal/printer/singlelinestringwriter.go b/internal/printer/singlelinestringwriter.go index 9f80c68a3f..4642b47097 100644 --- a/internal/printer/singlelinestringwriter.go +++ b/internal/printer/singlelinestringwriter.go @@ -8,6 +8,7 @@ import ( "github.com/microsoft/typescript-go/internal/stringutil" ) +// TODO: Definitely not threadsafe - make one per checker instead of one global one (threadlocal storage would be neat) var SingleLineStringWriter EmitTextWriter = &singleLineStringWriter{} type singleLineStringWriter struct { From 5190004fbdd4ea7fbe8ab75c0051823074518e06 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 16 Apr 2025 01:40:46 -0700 Subject: [PATCH 02/15] Pull in a chunk of declaration emit integrations from older work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This needs more pulled in from stashed old work to fill it out, (part of the way through the declaration transform itself and resolver) and it's useless without the nodebuilder itself done, but has the full declaration emit pipeline hooked in and tests enabled - another thing showing just how not complete it is! If the PR was already massive, this won't help~ For real, this is all going to have to be un-integrated to be at all mergable in the end, but all up it will be able to signal completion with green CI in the end, so long as nobody blind baseline-accepts... ¯\_(ツ)_/¯ --- internal/ast/utilities.go | 55 ++ internal/checker/checker.go | 24 +- internal/checker/emitresolver.go | 151 ++++ internal/checker/nodebuilder.go | 200 +---- internal/checker/nodebuilderapi.go | 61 +- internal/checker/nodebuilderscopes.go | 3 +- internal/checker/printer.go | 35 +- internal/checker/symbolaccessibility.go | 25 +- internal/checker/symboltracker.go | 28 +- internal/checker/utilities.go | 15 +- internal/compiler/emitHost.go | 21 + internal/compiler/emitter.go | 129 ++- internal/compiler/program.go | 56 ++ internal/core/compileroptions.go | 3 +- internal/declarations/diagnostics.go | 466 +++++++++++ internal/declarations/tracker.go | 217 +++++ internal/declarations/transform.go | 743 ++++++++++++++++++ internal/declarations/util.go | 181 +++++ internal/execute/tsc.go | 6 +- internal/nodebuilder/types.go | 78 ++ internal/printer/emitresolver.go | 34 + .../testutil/tsbaseline/js_emit_baseline.go | 56 +- internal/transformers/commonjsmodule.go | 2 +- internal/transformers/esmodule.go | 2 +- internal/transformers/impliedmodule.go | 2 +- internal/transformers/importelision.go | 2 +- internal/transformers/modifiervisitor.go | 2 +- internal/transformers/runtimesyntax.go | 2 +- internal/transformers/transformer.go | 14 +- internal/transformers/typeeraser.go | 2 +- 30 files changed, 2297 insertions(+), 318 deletions(-) create mode 100644 internal/declarations/diagnostics.go create mode 100644 internal/declarations/tracker.go create mode 100644 internal/declarations/transform.go create mode 100644 internal/declarations/util.go create mode 100644 internal/nodebuilder/types.go diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 13173ddfd8..aca5d23872 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -2639,3 +2639,58 @@ func getPragmaArgument(pragma *Pragma, name string) string { } return "" } + +func GetExternalModuleImportEqualsDeclarationExpression(node *Node) *Node { + // Debug.assert(isExternalModuleImportEqualsDeclaration(node)) + return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().Expression +} + +func CreateModifiersFromModifierFlags(flags ModifierFlags, createModifier func(kind Kind) *Node) []*Node { + var result []*Node + if flags&ModifierFlagsExport != 0 { + result = append(result, createModifier(KindExportKeyword)) + } + if flags&ModifierFlagsAmbient != 0 { + result = append(result, createModifier(KindDeclareKeyword)) + } + if flags&ModifierFlagsDefault != 0 { + result = append(result, createModifier(KindDefaultKeyword)) + } + if flags&ModifierFlagsConst != 0 { + result = append(result, createModifier(KindConstKeyword)) + } + if flags&ModifierFlagsPublic != 0 { + result = append(result, createModifier(KindPublicKeyword)) + } + if flags&ModifierFlagsPrivate != 0 { + result = append(result, createModifier(KindPrivateKeyword)) + } + if flags&ModifierFlagsProtected != 0 { + result = append(result, createModifier(KindProtectedKeyword)) + } + if flags&ModifierFlagsAbstract != 0 { + result = append(result, createModifier(KindAbstractKeyword)) + } + if flags&ModifierFlagsStatic != 0 { + result = append(result, createModifier(KindStaticKeyword)) + } + if flags&ModifierFlagsOverride != 0 { + result = append(result, createModifier(KindOverrideKeyword)) + } + if flags&ModifierFlagsReadonly != 0 { + result = append(result, createModifier(KindReadonlyKeyword)) + } + if flags&ModifierFlagsAccessor != 0 { + result = append(result, createModifier(KindAccessorKeyword)) + } + if flags&ModifierFlagsAsync != 0 { + result = append(result, createModifier(KindAsyncKeyword)) + } + if flags&ModifierFlagsIn != 0 { + result = append(result, createModifier(KindInKeyword)) + } + if flags&ModifierFlagsOut != 0 { + result = append(result, createModifier(KindOutKeyword)) + } + return result +} diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 6b2ea1e290..5e4879fe27 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -3023,12 +3023,12 @@ func (c *Checker) checkTemplateLiteralType(node *ast.Node) { func (c *Checker) checkImportType(node *ast.Node) { c.checkSourceElement(node.AsImportTypeNode().Argument) if attributes := node.AsImportTypeNode().Attributes; attributes != nil { - c.getResolutionModeOverride(attributes.AsImportAttributes(), true /*reportErrors*/) + c.GetResolutionModeOverride(attributes.AsImportAttributes(), true /*reportErrors*/) } c.checkTypeReferenceOrImport(node) } -func (c *Checker) getResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode { +func (c *Checker) GetResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode { if len(node.Attributes.Nodes) != 1 { if reportErrors { c.grammarErrorOnNode(node.AsNode(), core.IfElse(node.Token == ast.KindWithKeyword, @@ -3194,17 +3194,17 @@ func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { // deviations, we XOR someOverloadFlags with allOverloadFlags someButNotAllOverloadFlags := someOverloadFlags ^ allOverloadFlags if someButNotAllOverloadFlags != 0 { - canonicalFlags := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck) + canonicalFlags := c.GetEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck) groups := make(map[*ast.SourceFile][]*ast.Node) for _, overload := range overloads { sourceFile := ast.GetSourceFileOfNode(overload) groups[sourceFile] = append(groups[sourceFile], overload) } for _, overloadsInFile := range groups { - canonicalFlagsForFile := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloadsInFile, implementation), flagsToCheck) + canonicalFlagsForFile := c.GetEffectiveDeclarationFlags(getCanonicalOverload(overloadsInFile, implementation), flagsToCheck) for _, overload := range overloadsInFile { - deviation := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlags - deviationInFile := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlagsForFile + deviation := c.GetEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlags + deviationInFile := c.GetEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlagsForFile switch { case deviationInFile&ast.ModifierFlagsExport != 0: // Overloads in different files need not all have export modifiers. This is ok: @@ -3314,7 +3314,7 @@ func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { } if ast.IsFunctionDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsConstructorDeclaration(node) { functionDeclarations = append(functionDeclarations, node) - currentNodeFlags := c.getEffectiveDeclarationFlags(node, flagsToCheck) + currentNodeFlags := c.GetEffectiveDeclarationFlags(node, flagsToCheck) someNodeFlags |= currentNodeFlags allNodeFlags &= currentNodeFlags someHaveQuestionToken = someHaveQuestionToken || isOptionalDeclaration(node) @@ -3393,7 +3393,7 @@ func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { } } -func (c *Checker) getEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags { +func (c *Checker) GetEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags { flags := c.getCombinedModifierFlagsCached(n) // children of classes (even ambient classes) should not be marked as ambient or export // because those flags have no useful semantics there. @@ -5005,7 +5005,7 @@ func (c *Checker) checkImportAttributes(declaration *ast.Node) { c.checkTypeAssignableTo(c.getTypeFromImportAttributes(node), c.getNullableType(importAttributesType, TypeFlagsUndefined), node, nil) } isTypeOnly := isTypeOnlyImportOrExportDeclaration(declaration) - override := c.getResolutionModeOverride(node.AsImportAttributes(), isTypeOnly) + override := c.GetResolutionModeOverride(node.AsImportAttributes(), isTypeOnly) isImportAttributes := node.AsImportAttributes().Token == ast.KindWithKeyword if isTypeOnly && override != core.ResolutionModeNone { return // Other grammar checks do not apply to type-only imports with resolution mode assertions @@ -6385,7 +6385,7 @@ func (c *Checker) checkExportsOnMergedDeclarations(node *ast.Node) { defaultExportedDeclarationSpaces := DeclarationSpacesNone for _, d := range symbol.Declarations { declarationSpaces := c.getDeclarationSpaces(d) - effectiveDeclarationFlags := c.getEffectiveDeclarationFlags(d, ast.ModifierFlagsExport|ast.ModifierFlagsDefault) + effectiveDeclarationFlags := c.GetEffectiveDeclarationFlags(d, ast.ModifierFlagsExport|ast.ModifierFlagsDefault) if effectiveDeclarationFlags&ast.ModifierFlagsExport != 0 { if effectiveDeclarationFlags&ast.ModifierFlagsDefault != 0 { defaultExportedDeclarationSpaces |= declarationSpaces @@ -13562,7 +13562,7 @@ func (c *Checker) getTargetOfImportEqualsDeclaration(node *ast.Node, dontResolve if ast.IsVariableDeclaration(node) || node.AsImportEqualsDeclaration().ModuleReference.Kind == ast.KindExternalModuleReference { moduleReference := getExternalModuleRequireArgument(node) if moduleReference == nil { - moduleReference = getExternalModuleImportEqualsDeclarationExpression(node) + moduleReference = ast.GetExternalModuleImportEqualsDeclarationExpression(node) } immediate := c.resolveExternalModuleName(node, moduleReference, false /*ignoreErrors*/) resolved := c.resolveExternalModuleSymbol(immediate, false /*dontResolveAlias*/) @@ -29359,7 +29359,7 @@ func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Sy // 1). import x = require("./mo/*gotToDefinitionHere*/d") // 2). External module name in an import declaration // 4). type A = import("./f/*gotToDefinitionHere*/oo") - if (ast.IsExternalModuleImportEqualsDeclaration(grandParent) && getExternalModuleImportEqualsDeclarationExpression(grandParent) == node) || + if (ast.IsExternalModuleImportEqualsDeclaration(grandParent) && ast.GetExternalModuleImportEqualsDeclarationExpression(grandParent) == node) || ((parent.Kind == ast.KindImportDeclaration || parent.Kind == ast.KindExportDeclaration) && parent.AsImportDeclaration().ModuleSpecifier == node) || (ast.IsLiteralTypeNode(parent) && ast.IsLiteralImportTypeNode(grandParent) && grandParent.AsImportTypeNode().Argument == parent) { return c.resolveExternalModuleName(node, node, ignoreErrors) diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 1b2bca67c0..ed08ea346f 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -6,6 +6,8 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/binder" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/evaluator" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" ) @@ -18,6 +20,155 @@ type emitResolver struct { referenceResolver binder.ReferenceResolver } +func (r *emitResolver) GetEnumMemberValue(node *ast.Node) evaluator.Result { + panic("unimplemented") // !!! +} + +func (r *emitResolver) IsDeclarationVisible(node *ast.Node) bool { + panic("unimplemented") // !!! +} + +func (r *emitResolver) IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) printer.SymbolAccessibilityResult { + panic("unimplemented") // !!! +} + +func (r *emitResolver) IsImplementationOfOverload(node *ast.SignatureDeclaration) bool { + panic("unimplemented") // !!! +} + +func (r *emitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool { + panic("unimplemented") // !!! +} + +// TODO: the emit resolver being respoinsible for some amount of node construction is a very leaky abstraction, +// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API +// and likely reduces performance. There's probably some refactoring that could be done here to simplify this. + +func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + original := emitContext.ParseNode(signatureDeclaration) + if original == nil { + return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + } + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + return requestNodeBuilder.serializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) +} + +func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + original := emitContext.ParseNode(declaration) + if original == nil { + return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + } + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + // // Get type of the symbol if this is the valid symbol otherwise get type at location + symbol := r.checker.getSymbolOfDeclaration(declaration) + return requestNodeBuilder.serializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) +} + +func (r *emitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + switch declaration.Kind { + case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindJSDocPropertyTag: + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + if symbol == nil { + symbol = r.checker.getSymbolOfDeclaration(declaration) + } + type_ := r.checker.getTypeOfSymbol(symbol) + r.checker.mappedSymbolLinks.Has(symbol) + return !!((symbol.Flags&ast.SymbolFlagsProperty != 0) && (symbol.Flags&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && r.checker.ReverseMappedSymbolLinks.Has(symbol) && r.checker.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(r.checker, type_)) + case ast.KindParameter, ast.KindJSDocParameterTag: + return r.requiresAddingImplicitUndefined(declaration, enclosingDeclaration) + default: + panic("Node cannot possibly require adding undefined") + } +} + +func (r *emitResolver) requiresAddingImplicitUndefined(parameter *ast.Node, enclosingDeclaration *ast.Node) bool { + return (r.isRequiredInitializedParameter(parameter, enclosingDeclaration) || r.isOptionalUninitializedParameterProperty(parameter)) && !r.declaredParameterTypeContainsUndefined(parameter) +} + +func (r *emitResolver) declaredParameterTypeContainsUndefined(parameter *ast.Node) bool { + // typeNode := getNonlocalEffectiveTypeAnnotationNode(parameter); // !!! JSDoc Support + typeNode := parameter.Type() + if typeNode == nil { + return false + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + type_ := r.checker.getTypeFromTypeNode(typeNode) + // allow error type here to avoid confusing errors that the annotation has to contain undefined when it does in cases like this: + // + // export function fn(x?: Unresolved | undefined): void {} + return r.checker.isErrorType(type_) || r.checker.containsUndefinedType(type_) +} + +func (r *emitResolver) isOptionalUninitializedParameterProperty(parameter *ast.Node) bool { + return r.checker.strictNullChecks && + r.isOptionalParameter(parameter) && + ( /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() != nil) && // !!! TODO: JSDoc support + ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier) +} + +func (r *emitResolver) isRequiredInitializedParameter(parameter *ast.Node, enclosingDeclaration *ast.Node) bool { + if r.checker.strictNullChecks || r.isOptionalParameter(parameter) || /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() == nil { // !!! TODO: JSDoc Support + return false + } + if ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier) { + return enclosingDeclaration != nil && ast.IsFunctionLikeDeclaration(enclosingDeclaration) + } + return true +} + +func (r *emitResolver) isOptionalParameter(node *ast.Node) bool { + // !!! TODO: JSDoc support + // if (hasEffectiveQuestionToken(node)) { + // return true; + // } + if ast.IsParameter(node) && node.AsParameterDeclaration().QuestionToken != nil { + return true + } + if !ast.IsParameter(node) { + return false + } + if node.Initializer() != nil { + signature := r.checker.getSignatureFromDeclaration(node.Parent) + parameterIndex := core.FindIndex(node.Parent.Parameters(), func(p *ast.ParameterDeclarationNode) bool { return p == node }) + // Debug.assert(parameterIndex >= 0); // !!! + // Only consider syntactic or instantiated parameters as optional, not `void` parameters as this function is used + // in grammar checks and checking for `void` too early results in parameter types widening too early + // and causes some noImplicitAny errors to be lost. + return parameterIndex >= r.checker.getMinArgumentCountEx(signature, MinArgumentCountFlagsStrongArityForUntypedJS|MinArgumentCountFlagsVoidIsNonOptional) + } + iife := ast.GetImmediatelyInvokedFunctionExpression(node.Parent) + if iife != nil { + parameterIndex := core.FindIndex(node.Parent.Parameters(), func(p *ast.ParameterDeclarationNode) bool { return p == node }) + return node.Type() == nil && + node.AsParameterDeclaration().DotDotDotToken == nil && + parameterIndex >= len(r.checker.getEffectiveCallArguments(iife)) + } + + return false +} + +func (r *emitResolver) IsLiteralConstDeclaration(node *ast.Node) bool { + if isDeclarationReadonly(node) || ast.IsVariableDeclaration(node) && ast.IsVarConst(node) { + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + return isFreshLiteralType(r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node))) + } + return false +} + +func (r *emitResolver) IsExpandoFunctionDeclaration(node *ast.Node) bool { + // !!! TODO: expando function support + return false +} + +func (r *emitResolver) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) printer.SymbolAccessibilityResult { + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + return r.checker.IsSymbolAccessible(symbol, enclosingDeclaration, meaning, shouldComputeAliasToMarkVisible) +} + func isConstEnumOrConstEnumOnlyModule(s *ast.Symbol) bool { return isConstEnumSymbol(s) || s.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 } diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 9afee5843f..bc22282381 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -7,65 +7,11 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" ) -// NOTE: If modifying this enum, must modify `TypeFormatFlags` too! -// dprint-ignore -type NodeBuilderFlags int32 - -const ( - NodeBuilderFlagsNone NodeBuilderFlags = 0 - // Options - NodeBuilderFlagsNoTruncation NodeBuilderFlags = 1 << 0 - NodeBuilderFlagsWriteArrayAsGenericType NodeBuilderFlags = 1 << 1 - NodeBuilderFlagsGenerateNamesForShadowedTypeParams NodeBuilderFlags = 1 << 2 - NodeBuilderFlagsUseStructuralFallback NodeBuilderFlags = 1 << 3 - NodeBuilderFlagsForbidIndexedAccessSymbolReferences NodeBuilderFlags = 1 << 4 - NodeBuilderFlagsWriteTypeArgumentsOfSignature NodeBuilderFlags = 1 << 5 - NodeBuilderFlagsUseFullyQualifiedType NodeBuilderFlags = 1 << 6 - NodeBuilderFlagsUseOnlyExternalAliasing NodeBuilderFlags = 1 << 7 - NodeBuilderFlagsSuppressAnyReturnType NodeBuilderFlags = 1 << 8 - NodeBuilderFlagsWriteTypeParametersInQualifiedName NodeBuilderFlags = 1 << 9 - NodeBuilderFlagsMultilineObjectLiterals NodeBuilderFlags = 1 << 10 - NodeBuilderFlagsWriteClassExpressionAsTypeLiteral NodeBuilderFlags = 1 << 11 - NodeBuilderFlagsUseTypeOfFunction NodeBuilderFlags = 1 << 12 - NodeBuilderFlagsOmitParameterModifiers NodeBuilderFlags = 1 << 13 - NodeBuilderFlagsUseAliasDefinedOutsideCurrentScope NodeBuilderFlags = 1 << 14 - NodeBuilderFlagsUseSingleQuotesForStringLiteralType NodeBuilderFlags = 1 << 28 - NodeBuilderFlagsNoTypeReduction NodeBuilderFlags = 1 << 29 - NodeBuilderFlagsOmitThisParameter NodeBuilderFlags = 1 << 25 - // Error handling - NodeBuilderFlagsAllowThisInObjectLiteral NodeBuilderFlags = 1 << 15 - NodeBuilderFlagsAllowQualifiedNameInPlaceOfIdentifier NodeBuilderFlags = 1 << 16 - NodeBuilderFlagsAllowAnonymousIdentifier NodeBuilderFlags = 1 << 17 - NodeBuilderFlagsAllowEmptyUnionOrIntersection NodeBuilderFlags = 1 << 18 - NodeBuilderFlagsAllowEmptyTuple NodeBuilderFlags = 1 << 19 - NodeBuilderFlagsAllowUniqueESSymbolType NodeBuilderFlags = 1 << 20 - NodeBuilderFlagsAllowEmptyIndexInfoType NodeBuilderFlags = 1 << 21 - // Errors (cont.) - NodeBuilderFlagsAllowNodeModulesRelativePaths NodeBuilderFlags = 1 << 26 - NodeBuilderFlagsIgnoreErrors NodeBuilderFlags = NodeBuilderFlagsAllowThisInObjectLiteral | NodeBuilderFlagsAllowQualifiedNameInPlaceOfIdentifier | NodeBuilderFlagsAllowAnonymousIdentifier | NodeBuilderFlagsAllowEmptyUnionOrIntersection | NodeBuilderFlagsAllowEmptyTuple | NodeBuilderFlagsAllowEmptyIndexInfoType | NodeBuilderFlagsAllowNodeModulesRelativePaths - // State - NodeBuilderFlagsInObjectTypeLiteral NodeBuilderFlags = 1 << 22 - NodeBuilderFlagsInTypeAlias NodeBuilderFlags = 1 << 23 - NodeBuilderFlagsInInitialEntityName NodeBuilderFlags = 1 << 24 -) - -/** @internal */ -// dprint-ignore - -type InternalNodeBuilderFlags int32 - -const ( - InternalNodeBuilderFlagsNone InternalNodeBuilderFlags = 0 - InternalNodeBuilderFlagsWriteComputedProps InternalNodeBuilderFlags = 1 << 0 - InternalNodeBuilderFlagsNoSyntacticPrinter InternalNodeBuilderFlags = 1 << 1 - InternalNodeBuilderFlagsDoNotIncludeSymbolChain InternalNodeBuilderFlags = 1 << 2 - InternalNodeBuilderFlagsAllowUnresolvedNames InternalNodeBuilderFlags = 1 << 3 -) - type CompositeSymbolIdentity struct { isConstructorNode bool symbolId ast.SymbolId @@ -87,8 +33,8 @@ type SerializedTypeEntry struct { type CompositeTypeCacheIdentity struct { typeId TypeId - flags NodeBuilderFlags - internalFlags InternalNodeBuilderFlags + flags nodebuilder.Flags + internalFlags nodebuilder.InternalFlags } type NodeBuilderLinks struct { @@ -97,13 +43,13 @@ type NodeBuilderLinks struct { } type NodeBuilderContext struct { - tracker SymbolTracker + tracker nodebuilder.SymbolTracker approximateLength int encounteredError bool truncating bool reportedDiagnostic bool - flags NodeBuilderFlags - internalFlags InternalNodeBuilderFlags + flags nodebuilder.Flags + internalFlags nodebuilder.InternalFlags depth int enclosingDeclaration *ast.Node enclosingFile *ast.SourceFile @@ -183,7 +129,7 @@ func (b *NodeBuilder) checkTruncationLength() bool { if b.ctx.truncating { return b.ctx.truncating } - b.ctx.truncating = b.ctx.approximateLength > (core.IfElse((b.ctx.flags&NodeBuilderFlagsNoTruncation != 0), noTruncationMaximumTruncationLength, defaultMaximumTruncationLength)) + b.ctx.truncating = b.ctx.approximateLength > (core.IfElse((b.ctx.flags&nodebuilder.FlagsNoTruncation != 0), noTruncationMaximumTruncationLength, defaultMaximumTruncationLength)) return b.ctx.truncating } @@ -261,7 +207,7 @@ func isClassInstanceSide(c *Checker, t *Type) bool { func (b *NodeBuilder) createElidedInformationPlaceholder() *ast.TypeNode { b.ctx.approximateLength += 3 - if b.ctx.flags&NodeBuilderFlagsNoTruncation == 0 { + if b.ctx.flags&nodebuilder.FlagsNoTruncation == 0 { return b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/) } // addSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided") // !!! @@ -287,16 +233,6 @@ func (b *NodeBuilder) tryReuseExistingTypeNodeHelper(existing *ast.TypeNode) *as return nil // !!! } -func containsNonMissingUndefinedType(c *Checker, t *Type) bool { - var candidate *Type - if t.flags&TypeFlagsUnion != 0 { - candidate = t.AsUnionType().types[0] - } else { - candidate = t - } - return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType -} - func (b *NodeBuilder) tryReuseExistingTypeNode(typeNode *ast.TypeNode, t *Type, host *ast.Node, addUndefined bool) *ast.TypeNode { originalType := t if addUndefined { @@ -511,7 +447,7 @@ func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature b.ctx.approximateLength += 3 // Usually a signature contributes a few more characters than this, but 3 is the minimum - if b.ctx.flags&NodeBuilderFlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && signature.target.typeParameters != nil { + if b.ctx.flags&nodebuilder.FlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && signature.target.typeParameters != nil { for _, parameter := range signature.target.typeParameters { node := b.typeToTypeNodeClosure(b.ch.instantiateType(parameter, signature.mapper)) if typeArguments == nil { @@ -532,7 +468,7 @@ func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature } restoreFlags := b.saveRestoreFlags() - b.ctx.flags &^= NodeBuilderFlagsSuppressAnyReturnType + b.ctx.flags &^= nodebuilder.FlagsSuppressAnyReturnType // If the expanded parameter list had a variadic in a non-trailing position, don't expand it parameters := core.Map(core.IfElse(core.Some(expandedParams, func(p *ast.Symbol) bool { return p != expandedParams[len(expandedParams)-1] && p.CheckFlags&ast.CheckFlagsRestParameter != 0 @@ -540,7 +476,7 @@ func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature return b.symbolToParameterDeclaration(parameter, kind == ast.KindConstructor) }) var thisParameter *ast.Node - if b.ctx.flags&NodeBuilderFlagsOmitThisParameter != 0 { + if b.ctx.flags&nodebuilder.FlagsOmitThisParameter != 0 { thisParameter = nil } else { thisParameter = b.tryGetThisParameterDeclaration(signature) @@ -558,7 +494,7 @@ func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature } if (kind == ast.KindConstructorType) && signature.flags&SignatureFlagsAbstract != 0 { flags := ast.ModifiersToFlags(modifiers) - modifiers = createModifiersFromModifierFlags(flags|ast.ModifierFlagsAbstract, b.f.NewModifier) + modifiers = ast.CreateModifiersFromModifierFlags(flags|ast.ModifierFlagsAbstract, b.f.NewModifier) } paramList := b.f.NewNodeList(parameters) @@ -641,56 +577,6 @@ func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature return node } -func createModifiersFromModifierFlags(flags ast.ModifierFlags, createModifier func(kind ast.Kind) *ast.Node) []*ast.Node { - var result []*ast.Node - if flags&ast.ModifierFlagsExport != 0 { - result = append(result, createModifier(ast.KindExportKeyword)) - } - if flags&ast.ModifierFlagsAmbient != 0 { - result = append(result, createModifier(ast.KindDeclareKeyword)) - } - if flags&ast.ModifierFlagsDefault != 0 { - result = append(result, createModifier(ast.KindDefaultKeyword)) - } - if flags&ast.ModifierFlagsConst != 0 { - result = append(result, createModifier(ast.KindConstKeyword)) - } - if flags&ast.ModifierFlagsPublic != 0 { - result = append(result, createModifier(ast.KindPublicKeyword)) - } - if flags&ast.ModifierFlagsPrivate != 0 { - result = append(result, createModifier(ast.KindPrivateKeyword)) - } - if flags&ast.ModifierFlagsProtected != 0 { - result = append(result, createModifier(ast.KindProtectedKeyword)) - } - if flags&ast.ModifierFlagsAbstract != 0 { - result = append(result, createModifier(ast.KindAbstractKeyword)) - } - if flags&ast.ModifierFlagsStatic != 0 { - result = append(result, createModifier(ast.KindStaticKeyword)) - } - if flags&ast.ModifierFlagsOverride != 0 { - result = append(result, createModifier(ast.KindOverrideKeyword)) - } - if flags&ast.ModifierFlagsReadonly != 0 { - result = append(result, createModifier(ast.KindReadonlyKeyword)) - } - if flags&ast.ModifierFlagsAccessor != 0 { - result = append(result, createModifier(ast.KindAccessorKeyword)) - } - if flags&ast.ModifierFlagsAsync != 0 { - result = append(result, createModifier(ast.KindAsyncKeyword)) - } - if flags&ast.ModifierFlagsIn != 0 { - result = append(result, createModifier(ast.KindInKeyword)) - } - if flags&ast.ModifierFlagsOut != 0 { - result = append(result, createModifier(ast.KindOutKeyword)) - } - return result -} - func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) [][]*ast.Symbol { if signatureHasRestParameter(sig) { restIndex := len(sig.parameters) - 1 @@ -720,7 +606,7 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) } var name string for true { - name = fmt.Sprintf("{}_{}", names[i], counter) + name = fmt.Sprintf("%s_%d", names[i], counter) _, ok := uniqueNames[name] if ok { counter++ @@ -807,10 +693,10 @@ func (b *NodeBuilder) tryGetThisParameterDeclaration(signature *Signature) *ast. * Serializes the return type of the signature by first trying to use the syntactic printer if possible and falling back to the checker type if not. */ func (b *NodeBuilder) serializeReturnTypeForSignature(signature *Signature) *ast.Node { - suppressAny := b.ctx.flags&NodeBuilderFlagsSuppressAnyReturnType != 0 + suppressAny := b.ctx.flags&nodebuilder.FlagsSuppressAnyReturnType != 0 restoreFlags := b.saveRestoreFlags() if suppressAny { - b.ctx.flags &= ^NodeBuilderFlagsSuppressAnyReturnType // suppress only toplevel `any`s + b.ctx.flags &= ^nodebuilder.FlagsSuppressAnyReturnType // suppress only toplevel `any`s } var returnTypeNode *ast.Node @@ -847,7 +733,7 @@ func (b *NodeBuilder) indexInfoToIndexSignatureDeclarationHelper(indexInfo *Inde typeNode = b.typeToTypeNodeClosure(indexInfo.valueType) } } - if indexInfo.valueType == nil && b.ctx.flags&NodeBuilderFlagsAllowEmptyIndexInfoType == 0 { + if indexInfo.valueType == nil && b.ctx.flags&nodebuilder.FlagsAllowEmptyIndexInfoType == 0 { b.ctx.encounteredError = true } b.ctx.approximateLength += len(name) + 4 @@ -872,7 +758,7 @@ func (b *NodeBuilder) serializeTypeForDeclaration(declaration *ast.Declaration, if t.flags&TypeFlagsUniqueESSymbol != 0 && t.symbol == symbol && (b.ctx.enclosingDeclaration == nil || core.Some(symbol.Declarations, func(d *ast.Declaration) bool { return ast.GetSourceFileOfNode(d) == b.ctx.enclosingFile })) { - b.ctx.flags |= NodeBuilderFlagsAllowUniqueESSymbolType + b.ctx.flags |= nodebuilder.FlagsAllowUniqueESSymbolType } result := b.typeToTypeNodeClosure(t) // !!! expressionOrTypeToTypeNode restoreFlags() @@ -1137,7 +1023,7 @@ func (b *NodeBuilder) addPropertyToElementList(propertySymbol *ast.Symbol, typeE func (b *NodeBuilder) createTypeNodesFromResolvedType(resolvedType *StructuredType) *ast.NodeList { if b.checkTruncationLength() { - if b.ctx.flags&NodeBuilderFlagsNoTruncation != 0 { + if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { panic("NotEmittedTypeElement not implemented") // !!! // elem := b.f.NewNotEmittedTypeElement() // b.e.addSyntheticTrailingComment(elem, ast.KindMultiLineCommentTrivia, "elided") @@ -1167,7 +1053,7 @@ func (b *NodeBuilder) createTypeNodesFromResolvedType(resolvedType *StructuredTy i := 0 for _, propertySymbol := range properties { i++ - if b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0 { + if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 { if propertySymbol.Flags&ast.SymbolFlagsPrototype != 0 { continue } @@ -1176,7 +1062,7 @@ func (b *NodeBuilder) createTypeNodesFromResolvedType(resolvedType *StructuredTy } } if b.checkTruncationLength() && (i+2 < len(properties)-1) { - if b.ctx.flags&NodeBuilderFlagsNoTruncation != 0 { + if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { // !!! synthetic comment support - missing middle silently elided without // typeElement := typeElements[len(typeElements) - 1].Clone() // typeElements = typeElements[0:len(typeElements)-1] @@ -1235,7 +1121,7 @@ func (b *NodeBuilder) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { return b.ch.getOrCreateTypeFromSignature(s, nil) }) // count the number of type elements excluding abstract constructors - typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + (core.IfElse(b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { + typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + (core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { return p.Flags&ast.SymbolFlagsPrototype == 0 }), len(resolved.properties))) // don't include an empty object literal if there were no other static-side @@ -1249,12 +1135,12 @@ func (b *NodeBuilder) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { } restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= NodeBuilderFlagsInObjectTypeLiteral + b.ctx.flags |= nodebuilder.FlagsInObjectTypeLiteral members := b.createTypeNodesFromResolvedType(resolved) restoreFlags() typeLiteralNode := b.f.NewTypeLiteralNode(members) b.ctx.approximateLength += 2 - b.e.SetEmitFlags(typeLiteralNode, core.IfElse((b.ctx.flags&NodeBuilderFlagsMultilineObjectLiterals != 0), 0, printer.EFSingleLine)) + b.e.SetEmitFlags(typeLiteralNode, core.IfElse((b.ctx.flags&nodebuilder.FlagsMultilineObjectLiterals != 0), 0, printer.EFSingleLine)) return typeLiteralNode } @@ -1288,7 +1174,7 @@ func (b *NodeBuilder) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId if isStaticMethodSymbol || isNonLocalFunctionSymbol { // typeof is allowed only for static/non local functions _, visited := b.ctx.visitedTypes[typeId] - return (b.ctx.flags&NodeBuilderFlagsUseTypeOfFunction != 0 || visited) && (b.ctx.flags&NodeBuilderFlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration)) + return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || visited) && (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 } return false @@ -1325,7 +1211,7 @@ func (b *NodeBuilder) createAnonymousTypeNode(t *Type) *ast.TypeNode { // // Instance and static types share the same symbol; only add 'typeof' for the static side. // return b.symbolToTypeNode(symbol, isInstanceType, nil) // } else - if symbol.Flags&ast.SymbolFlagsClass != 0 && b.ch.getBaseTypeVariableOfClass(symbol) == nil && !(symbol.ValueDeclaration != nil && ast.IsClassLike(symbol.ValueDeclaration) && b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0 && (!ast.IsClassDeclaration(symbol.ValueDeclaration) || b.ch.IsSymbolAccessible(symbol, b.ctx.enclosingDeclaration, isInstanceType, false /*shouldComputeAliasesToMakeVisible*/).accessibility != SymbolAccessibilityAccessible)) || symbol.Flags&(ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 || b.shouldWriteTypeOfFunctionSymbol(symbol, typeId) { + 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) { return b.symbolToTypeNode(symbol, isInstanceType, nil) } else if _, ok := b.ctx.visitedTypes[typeId]; ok { // If type is an anonymous type literal in a type alias declaration, use type alias name @@ -1363,7 +1249,7 @@ func (b *NodeBuilder) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes bool func (b *NodeBuilder) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode { if t.flags&TypeFlagsUnion != 0 { if _, ok := b.ctx.visitedTypes[t.id]; ok { - if b.ctx.flags&NodeBuilderFlagsAllowAnonymousIdentifier == 0 { + if b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 { b.ctx.encounteredError = true b.ctx.tracker.ReportCyclicStructureError() } @@ -1378,7 +1264,7 @@ func (b *NodeBuilder) conditionalTypeToTypeNode(_t *Type) *ast.TypeNode { t := _t.AsConditionalType() checkTypeNode := b.typeToTypeNodeClosure(t.checkType) b.ctx.approximateLength += 15 - if b.ctx.flags&NodeBuilderFlagsGenerateNamesForShadowedTypeParams != 0 && t.root.isDistributive && t.checkType.flags&TypeFlagsTypeParameter == 0 { + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.root.isDistributive && t.checkType.flags&TypeFlagsTypeParameter == 0 { newParam := b.ch.newTypeParameter(b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T" /* as __String */)) name := b.typeParameterToName(newParam) newTypeVariable := b.f.NewTypeReferenceNode(name.AsNode(), nil) @@ -1435,7 +1321,7 @@ func (b *NodeBuilder) getParentSymbolOfTypeParameter(typeParameter *TypeParamete func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { var typeArguments []*Type = b.ch.getTypeArguments(t) if t.Target() == b.ch.globalArrayType || t.Target() == b.ch.globalReadonlyArrayType { - if b.ctx.flags&NodeBuilderFlagsWriteArrayAsGenericType != 0 { + if b.ctx.flags&nodebuilder.FlagsWriteArrayAsGenericType != 0 { typeArgumentNode := b.typeToTypeNodeClosure(typeArguments[0]) return b.f.NewTypeReferenceNode(b.f.NewIdentifier(core.IfElse(t.Target() == b.ch.globalArrayType, "Array", "ReadonlyArray")), b.f.NewNodeList([]*ast.TypeNode{typeArgumentNode})) } @@ -1478,7 +1364,7 @@ func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { } } } - if b.ctx.encounteredError || (b.ctx.flags&NodeBuilderFlagsAllowEmptyTuple != 0) { + if b.ctx.encounteredError || (b.ctx.flags&nodebuilder.FlagsAllowEmptyTuple != 0) { tupleTypeNode := b.f.NewTupleTypeNode(b.f.NewNodeList([]*ast.TypeNode{})) b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) if t.Target().AsTupleType().readonly { @@ -1490,7 +1376,7 @@ func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { b.ctx.encounteredError = true return nil // TODO: GH#18217 - } else if b.ctx.flags&NodeBuilderFlagsWriteClassExpressionAsTypeLiteral != 0 && t.symbol.ValueDeclaration != nil && ast.IsClassLike(t.symbol.ValueDeclaration) && !b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { + } else if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && t.symbol.ValueDeclaration != nil && ast.IsClassLike(t.symbol.ValueDeclaration) && !b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { return b.createAnonymousTypeNode(t) } else { outerTypeParameters := t.Target().AsInterfaceType().OuterTypeParameters() @@ -1511,7 +1397,7 @@ func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { if !slices.Equal(outerTypeParameters[start:i], typeArguments[start:i]) { typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i]) restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= NodeBuilderFlagsForbidIndexedAccessSymbolReferences + b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences ref := b.symbolToTypeNode(parent, ast.SymbolFlagsType, typeArgumentSlice) restoreFlags() if resultType == nil { @@ -1551,7 +1437,7 @@ func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount]) } restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= NodeBuilderFlagsForbidIndexedAccessSymbolReferences + b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences finalRef := b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, typeArgumentNodes) restoreFlags() if resultType == nil { @@ -1646,11 +1532,11 @@ func (b *NodeBuilder) visitAndTransformType(t *Type, transform func(t *Type) *as func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { - inTypeAlias := b.ctx.flags & NodeBuilderFlagsInTypeAlias - b.ctx.flags &^= NodeBuilderFlagsInTypeAlias + inTypeAlias := b.ctx.flags & nodebuilder.FlagsInTypeAlias + b.ctx.flags &^= nodebuilder.FlagsInTypeAlias if t == nil { - if b.ctx.flags&NodeBuilderFlagsAllowEmptyUnionOrIntersection == 0 { + if b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 { b.ctx.encounteredError = true return nil // TODO: GH#18217 @@ -1659,7 +1545,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) } - if b.ctx.flags&NodeBuilderFlagsNoTypeReduction == 0 { + if b.ctx.flags&nodebuilder.FlagsNoTypeReduction == 0 { t = b.ch.getReducedType(t) } @@ -1718,7 +1604,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { } if t.flags&TypeFlagsStringLiteral != 0 { b.ctx.approximateLength += len(t.AsLiteralType().value.(string)) + 2 - return b.f.NewLiteralTypeNode(b.f.NewStringLiteral(t.AsLiteralType().value.(string) /*, b.flags&NodeBuilderFlagsUseSingleQuotesForStringLiteralType != 0*/)) + return b.f.NewLiteralTypeNode(b.f.NewStringLiteral(t.AsLiteralType().value.(string) /*, b.flags&nodebuilder.FlagsUseSingleQuotesForStringLiteralType != 0*/)) } if t.flags&TypeFlagsNumberLiteral != 0 { value := t.AsLiteralType().value.(jsnum.Number) @@ -1734,7 +1620,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { return b.f.NewLiteralTypeNode(core.IfElse(t.AsIntrinsicType().intrinsicName == "true", b.f.NewKeywordExpression(ast.KindTrueKeyword), b.f.NewKeywordExpression(ast.KindFalseKeyword))) } if t.flags&TypeFlagsUniqueESSymbol != 0 { - if b.ctx.flags&NodeBuilderFlagsAllowUniqueESSymbolType == 0 { + if b.ctx.flags&nodebuilder.FlagsAllowUniqueESSymbolType == 0 { if b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { b.ctx.approximateLength += 6 return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsValue, nil) @@ -1769,8 +1655,8 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { return b.f.NewKeywordTypeNode(ast.KindObjectKeyword) } if isThisTypeParameter(t) { - if b.ctx.flags&NodeBuilderFlagsInObjectTypeLiteral != 0 { - if !b.ctx.encounteredError && b.ctx.flags&NodeBuilderFlagsAllowThisInObjectLiteral == 0 { + if b.ctx.flags&nodebuilder.FlagsInObjectTypeLiteral != 0 { + if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowThisInObjectLiteral == 0 { b.ctx.encounteredError = true } b.ctx.tracker.ReportInaccessibleThisError() @@ -1779,7 +1665,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { return b.f.NewThisTypeNode() } - if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&NodeBuilderFlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) { + if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) { sym := t.alias.Symbol() typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments()) if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 { @@ -1819,7 +1705,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { } return b.f.NewInferTypeNode(b.typeParameterToDeclarationWithConstraint(t, constraintNode)) } - if b.ctx.flags&NodeBuilderFlagsGenerateNamesForShadowedTypeParams != 0 && t.flags&TypeFlagsTypeParameter != 0 { + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.flags&TypeFlagsTypeParameter != 0 { name := b.typeParameterToName(t) b.ctx.approximateLength += len(name.Text) return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name.Text), nil /*typeArguments*/) @@ -1857,7 +1743,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { return b.f.NewIntersectionTypeNode(typeNodes) } } else { - if !b.ctx.encounteredError && b.ctx.flags&NodeBuilderFlagsAllowEmptyUnionOrIntersection == 0 { + if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 { b.ctx.encounteredError = true } return nil diff --git a/internal/checker/nodebuilderapi.go b/internal/checker/nodebuilderapi.go index 7cdaaccffd..62384e8359 100644 --- a/internal/checker/nodebuilderapi.go +++ b/internal/checker/nodebuilderapi.go @@ -2,24 +2,25 @@ package checker import ( "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" ) type NodeBuilderInterface interface { - typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node - symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) []*ast.Node - symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node } type NodeBuilderAPI struct { @@ -27,7 +28,7 @@ type NodeBuilderAPI struct { impl *NodeBuilder } -func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) { +func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) { b.impl.ctx = &NodeBuilderContext{ tracker: tracker, approximateLength: 0, @@ -77,19 +78,19 @@ func (b *NodeBuilderAPI) exitContextSlice(result []*ast.Node) []*ast.Node { } func (b *NodeBuilderAPI) exitContextCheck() { - if b.impl.ctx.truncating && b.impl.ctx.flags&NodeBuilderFlagsNoTruncation != 0 { + if b.impl.ctx.truncating && b.impl.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { b.impl.ctx.tracker.ReportTruncationError() } } // indexInfoToIndexSignatureDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.indexInfoToIndexSignatureDeclarationHelper(info, nil)) } // serializeReturnTypeForSignature implements NodeBuilderInterface. -func (b *NodeBuilderAPI) serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) signature := b.impl.ch.getSignatureFromDeclaration(signatureDeclaration) symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration) @@ -101,73 +102,73 @@ func (b *NodeBuilderAPI) serializeReturnTypeForSignature(signatureDeclaration *a } // serializeTypeForDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.serializeTypeForDeclaration(declaration, nil, symbol)) } // serializeTypeForExpression implements NodeBuilderInterface. -func (b *NodeBuilderAPI) serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.serializeTypeForExpression(expr)) } // signatureToSignatureDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.signatureToSignatureDeclarationHelper(signature, kind, nil)) } // symbolTableToDeclarationStatements implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) []*ast.Node { +func (b *NodeBuilderAPI) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContextSlice(b.impl.symbolTableToDeclarationStatements(symbolTable)) } // symbolToEntityName implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToName(symbol, meaning, false)) } // symbolToExpression implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToExpression(symbol, meaning)) } // symbolToNode implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToNode(symbol, meaning)) } // symbolToParameterDeclaration implements NodeBuilderInterface. -func (b NodeBuilderAPI) symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b NodeBuilderAPI) symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToParameterDeclaration(symbol, false)) } // symbolToTypeParameterDeclarations implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToTypeParameterDeclarations(symbol)) } // typeParameterToDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.typeParameterToDeclaration(parameter)) } // typePredicateToTypePredicateNode implements NodeBuilderInterface. -func (b *NodeBuilderAPI) typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.typePredicateToTypePredicateNode(predicate)) } // typeToTypeNode implements NodeBuilderInterface. -func (b *NodeBuilderAPI) typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.typeToTypeNode(typ)) } diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go index 9143ca86c7..8085bc2ab2 100644 --- a/internal/checker/nodebuilderscopes.go +++ b/internal/checker/nodebuilderscopes.go @@ -3,6 +3,7 @@ package checker import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/nodebuilder" ) func cloneNodeBuilderContext(context *NodeBuilderContext) func() { @@ -203,7 +204,7 @@ func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*as }) } - if b.ctx.flags&NodeBuilderFlagsGenerateNamesForShadowedTypeParams != 0 && typeParameters != nil && core.Some(*typeParameters, func(p *Type) bool { return p != nil }) { + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && typeParameters != nil && core.Some(*typeParameters, func(p *Type) bool { return p != nil }) { cleanupTypeParams = pushFakeScope("typeParams", func(add func(name string, symbol *ast.Symbol)) { if typeParameters == nil { return diff --git a/internal/checker/printer.go b/internal/checker/printer.go index 4e14b1d75e..f2b66945a3 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -6,6 +6,7 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" ) @@ -38,8 +39,8 @@ func (c *Checker) TypeToString(type_ *Type) string { return c.typeToStringEx(type_, nil, TypeFormatFlagsNone, nil) } -func toNodeBuilderFlags(flags TypeFormatFlags) NodeBuilderFlags { - return NodeBuilderFlags(flags & TypeFormatFlagsNodeBuilderFlagsMask) +func toNodeBuilderFlags(flags TypeFormatFlags) nodebuilder.Flags { + return nodebuilder.Flags(flags & TypeFormatFlagsNodeBuilderFlagsMask) } func (c *Checker) typeToStringEx(type_ *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags, writer printer.EmitTextWriter) string { @@ -47,11 +48,11 @@ func (c *Checker) typeToStringEx(type_ *Type, enclosingDeclaration *ast.Node, fl writer = printer.NewTextWriter("") } noTruncation := (c.compilerOptions.NoErrorTruncation == core.TSTrue) || (flags&TypeFormatFlagsNoTruncation != 0) - combinedFlags := toNodeBuilderFlags(flags) | NodeBuilderFlagsIgnoreErrors + combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors if noTruncation { - combinedFlags = combinedFlags | NodeBuilderFlagsNoTruncation + combinedFlags = combinedFlags | nodebuilder.FlagsNoTruncation } - typeNode := c.nodeBuilder.typeToTypeNode(type_, enclosingDeclaration, combinedFlags, InternalNodeBuilderFlagsNone, nil) + typeNode := c.nodeBuilder.typeToTypeNode(type_, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) if typeNode == nil { panic("should always get typenode") } @@ -94,22 +95,22 @@ func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast writer = printer.SingleLineStringWriter } - nodeFlags := NodeBuilderFlagsIgnoreErrors - internalNodeFlags := InternalNodeBuilderFlagsNone + nodeFlags := nodebuilder.FlagsIgnoreErrors + internalNodeFlags := nodebuilder.InternalFlagsNone if flags&SymbolFormatFlagsUseOnlyExternalAliasing != 0 { - nodeFlags |= NodeBuilderFlagsUseOnlyExternalAliasing + nodeFlags |= nodebuilder.FlagsUseOnlyExternalAliasing } if flags&SymbolFormatFlagsWriteTypeParametersOrArguments != 0 { - nodeFlags |= NodeBuilderFlagsWriteTypeParametersInQualifiedName + nodeFlags |= nodebuilder.FlagsWriteTypeParametersInQualifiedName } if flags&SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope != 0 { - nodeFlags |= NodeBuilderFlagsUseAliasDefinedOutsideCurrentScope + nodeFlags |= nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope } if flags&SymbolFormatFlagsDoNotIncludeSymbolChain != 0 { - internalNodeFlags |= InternalNodeBuilderFlagsDoNotIncludeSymbolChain + internalNodeFlags |= nodebuilder.InternalFlagsDoNotIncludeSymbolChain } if flags&SymbolFormatFlagsWriteComputedProps != 0 { - internalNodeFlags |= InternalNodeBuilderFlagsWriteComputedProps + internalNodeFlags |= nodebuilder.InternalFlagsWriteComputedProps } var sourceFile *ast.SourceFile @@ -131,7 +132,7 @@ func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast printer_ = createPrinterWithRemoveComments() } - var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags NodeBuilderFlags, internalFlags InternalNodeBuilderFlags, tracker SymbolTracker) *ast.Node + var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node if flags&SymbolFormatFlagsAllowAnyNodeKind != 0 { builder = c.nodeBuilder.symbolToNode } else { @@ -164,8 +165,8 @@ func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration if writer == nil { writer = printer.SingleLineStringWriter } - combinedFlags := toNodeBuilderFlags(flags) | NodeBuilderFlagsIgnoreErrors | NodeBuilderFlagsWriteTypeParametersInQualifiedName - sig := c.nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, InternalNodeBuilderFlagsNone, nil) + combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName + sig := c.nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon() var sourceFile *ast.SourceFile if enclosingDeclaration != nil { @@ -191,8 +192,8 @@ func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosin if writer == nil { writer = printer.SingleLineStringWriter } - combinedFlags := toNodeBuilderFlags(flags) | NodeBuilderFlagsIgnoreErrors | NodeBuilderFlagsWriteTypeParametersInQualifiedName - predicate := c.nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, InternalNodeBuilderFlagsNone, nil) // TODO: GH#18217 + combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName + predicate := c.nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 printer_ := createPrinterWithRemoveComments() var sourceFile *ast.SourceFile if enclosingDeclaration != nil { diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index 3e4fb8dbd6..a79cb6c43d 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -1,23 +1,10 @@ package checker -import "github.com/microsoft/typescript-go/internal/ast" - -type SymbolAccessibility int32 - -const ( - SymbolAccessibilityAccessible SymbolAccessibility = iota - SymbolAccessibilityNotAccessible - SymbolAccessibilityCannotBeNamed - SymbolAccessibilityNotResolved +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/printer" ) -type SymbolAccessibilityResult struct { - accessibility SymbolAccessibility - aliasesToMakeVisible []*ast.Node // aliases that need to have this symbol visible - errorSymbolName string // Optional symbol name that results in error - errorNode *ast.Node // optional node that results in error -} - func (ch *Checker) IsTypeSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { return false // !!! } @@ -35,11 +22,11 @@ func (ch *Checker) IsValueSymbolAccessible(symbol *ast.Symbol, enclosingDeclarat * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible */ -func (c *Checker) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool) SymbolAccessibilityResult { +func (c *Checker) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool) printer.SymbolAccessibilityResult { return c.isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, true /*allowModules*/) } -func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) SymbolAccessibilityResult { +func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) printer.SymbolAccessibilityResult { // if symbol != nil && enclosingDeclaration != nil { // result := c.isAnySymbolAccessible([]*ast.Symbol{symbol}, enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules) // if result != nil { @@ -72,5 +59,5 @@ func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclarat // return SymbolAccessibilityResult{ // accessibility: SymbolAccessibilityAccessible, // } - return SymbolAccessibilityResult{} // !!! + return printer.SymbolAccessibilityResult{} // !!! } diff --git a/internal/checker/symboltracker.go b/internal/checker/symboltracker.go index 10da2486fb..4945b6036d 100644 --- a/internal/checker/symboltracker.go +++ b/internal/checker/symboltracker.go @@ -1,33 +1,17 @@ package checker -import "github.com/microsoft/typescript-go/internal/ast" - -// TODO: previously all symboltracker methods were optional, but now they're required. -type SymbolTracker interface { - GetModuleSpecifierGenerationHost() any // !!! - - TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool - ReportInaccessibleThisError() - ReportPrivateInBaseOfClassExpression(propertyName string) - ReportInaccessibleUniqueSymbolError() - ReportCyclicStructureError() - ReportLikelyUnsafeImportRequiredError(specifier string) - ReportTruncationError() - ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) - ReportNonSerializableProperty(propertyName string) - - ReportInferenceFallback(node *ast.Node) - PushErrorFallbackNode(node *ast.Node) - PopErrorFallbackNode() -} +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/nodebuilder" +) type SymbolTrackerImpl struct { context NodeBuilderContext - inner SymbolTracker + inner nodebuilder.SymbolTracker DisableTrackSymbol bool } -func NewSymbolTrackerImpl(context NodeBuilderContext, tracker SymbolTracker) *SymbolTrackerImpl { +func NewSymbolTrackerImpl(context NodeBuilderContext, tracker nodebuilder.SymbolTracker) *SymbolTrackerImpl { // TODO: unwrap `tracker` before setting `inner` return &SymbolTrackerImpl{context, tracker, false} } diff --git a/internal/checker/utilities.go b/internal/checker/utilities.go index e2231126f5..46c183c904 100644 --- a/internal/checker/utilities.go +++ b/internal/checker/utilities.go @@ -465,11 +465,6 @@ func getExternalModuleRequireArgument(node *ast.Node) *ast.Node { return nil } -func getExternalModuleImportEqualsDeclarationExpression(node *ast.Node) *ast.Node { - // Debug.assert(isExternalModuleImportEqualsDeclaration(node)) - return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().Expression -} - func isRightSideOfQualifiedNameOrPropertyAccess(node *ast.Node) bool { parent := node.Parent switch parent.Kind { @@ -2157,3 +2152,13 @@ func allDeclarationsInSameSourceFile(symbol *ast.Symbol) bool { } return true } + +func containsNonMissingUndefinedType(c *Checker, t *Type) bool { + var candidate *Type + if t.flags&TypeFlagsUnion != 0 { + candidate = t.AsUnionType().types[0] + } else { + candidate = t + } + return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType +} diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index 01445eeb10..286a4dd7bb 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -3,6 +3,7 @@ package compiler import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/declarations" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/tspath" ) @@ -17,6 +18,7 @@ type WriteFileData struct { // NOTE: EmitHost operations must be thread-safe type EmitHost interface { + declarations.DeclarationEmitHost Options() *core.CompilerOptions SourceFiles() []*ast.SourceFile UseCaseSensitiveFileNames() bool @@ -35,6 +37,25 @@ type emitHost struct { program *Program } +func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { + ch := host.program.GetTypeCheckerForFile(ast.GetSourceFileOfNode(node)) + return ch.GetEffectiveDeclarationFlags(node, flags) +} + +func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths { + // TODO: cache + return getOutputPathsFor(file, host, forceDtsPaths) +} + +func (host *emitHost) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode { + ch := host.program.GetTypeCheckerForFile(ast.GetSourceFileOfNode(node)) + return ch.GetResolutionModeOverride(node.AsImportAttributes(), false) +} + +func (host *emitHost) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { + return host.program.GetSourceFileFromReference(origin, ref) +} + func (host *emitHost) Options() *core.CompilerOptions { return host.program.Options() } func (host *emitHost) SourceFiles() []*ast.SourceFile { return host.program.SourceFiles() } func (host *emitHost) GetCurrentDirectory() string { return host.program.host.GetCurrentDirectory() } diff --git a/internal/compiler/emitter.go b/internal/compiler/emitter.go index 2970a179bf..f305ac63ca 100644 --- a/internal/compiler/emitter.go +++ b/internal/compiler/emitter.go @@ -8,6 +8,7 @@ import ( "github.com/microsoft/typescript-go/internal/binder" "github.com/microsoft/typescript-go/internal/compiler/diagnostics" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/declarations" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/sourcemap" "github.com/microsoft/typescript-go/internal/stringutil" @@ -98,6 +99,12 @@ func (e *emitter) getScriptTransformers(emitContext *printer.EmitContext, source return tx } +func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer { + emitResolver := e.host.GetEmitResolver(sourceFile, false /*skipDiagnostics*/) // !!! conditionally skip diagnostics + transform := declarations.NewDeclarationTransformer(e.host, emitResolver, emitContext, e.host.Options(), declarationFilePath, declarationMapPath) + return []*declarations.DeclarationTransformer{transform} +} + func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sourceMapFilePath string) { options := e.host.Options() @@ -129,7 +136,7 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sour // !!! }, emitContext) - e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer) + e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer, shouldEmitSourceMaps(options, sourceFile)) if e.emittedFilesList != nil { e.emittedFilesList = append(e.emittedFilesList, jsFilePath) @@ -140,22 +147,59 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sour } func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) { - // !!! + options := e.host.Options() + + if sourceFile == nil || e.emitOnly != emitAll && e.emitOnly != emitOnlyDts || len(declarationFilePath) == 0 { + return + } + + if options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(declarationFilePath) { + return + } + + var diags []*ast.Diagnostic + emitContext := printer.NewEmitContext() + for _, transformer := range e.getDeclarationTransformers(emitContext, sourceFile, declarationFilePath, declarationMapPath) { + sourceFile = transformer.TransformSourceFile(sourceFile) + diags = append(diags, transformer.GetDiagnostics()...) + } + + printerOptions := printer.PrinterOptions{ + RemoveComments: options.RemoveComments.IsTrue(), + NewLine: options.NewLine, + NoEmitHelpers: options.NoEmitHelpers.IsTrue(), + SourceMap: options.DeclarationMap.IsTrue(), + InlineSourceMap: options.InlineSourceMap.IsTrue(), + InlineSources: options.InlineSources.IsTrue(), + // !!! + } + + // create a printer to print the nodes + printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{ + // !!! + }, emitContext) + + e.printSourceFile(declarationFilePath, declarationMapPath, sourceFile, printer, shouldEmitDeclarationSourceMaps(options, sourceFile)) + + for _, elem := range diags { + // Add declaration transform diagnostics to emit diagnostics + e.emitterDiagnostics.Add(elem) + } } func (e *emitter) emitBuildInfo(buildInfoPath string) { // !!! } -func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer *printer.Printer) bool { +func (e *emitter) printSourceFile(outFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer *printer.Printer, shouldEmitSourceMaps bool) bool { // !!! sourceMapGenerator options := e.host.Options() var sourceMapGenerator *sourcemap.Generator - if shouldEmitSourceMaps(options, sourceFile) { + if shouldEmitSourceMaps { sourceMapGenerator = sourcemap.NewGenerator( - tspath.GetBaseFileName(tspath.NormalizeSlashes(jsFilePath)), + tspath.GetBaseFileName(tspath.NormalizeSlashes(outFilePath)), getSourceRoot(options), - e.getSourceMapDirectory(options, jsFilePath, sourceFile), + e.getSourceMapDirectory(options, outFilePath, sourceFile), tspath.ComparePathsOptions{ UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(), CurrentDirectory: e.host.GetCurrentDirectory(), @@ -174,14 +218,14 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s e.sourceMapDataList = append(e.sourceMapDataList, &SourceMapEmitResult{ InputSourceFileNames: sourceMapGenerator.Sources(), SourceMap: sourceMapGenerator.RawSourceMap(), - GeneratedFile: jsFilePath, + GeneratedFile: outFilePath, }) } sourceMappingURL := e.getSourceMappingURL( options, sourceMapGenerator, - jsFilePath, + outFilePath, sourceMapFilePath, sourceFile, ) @@ -199,7 +243,7 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s sourceMap := sourceMapGenerator.String() err := e.host.WriteFile(sourceMapFilePath, sourceMap, false /*writeByteOrderMark*/, sourceFiles, nil /*data*/) if err != nil { - e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) + e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, outFilePath, err.Error())) } } } else { @@ -209,9 +253,9 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s // Write the output file text := e.writer.String() data := &WriteFileData{SourceMapUrlPos: sourceMapUrlPos} // !!! transform diagnostics - err := e.host.WriteFile(jsFilePath, text, e.host.Options().EmitBOM.IsTrue(), sourceFiles, data) + err := e.host.WriteFile(outFilePath, text, e.host.Options().EmitBOM.IsTrue(), sourceFiles, data) if err != nil { - e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) + e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, outFilePath, err.Error())) } // Reset state @@ -262,6 +306,11 @@ func shouldEmitSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.Sour !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson) } +func shouldEmitDeclarationSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool { + return mapOptions.DeclarationMap.IsTrue() && + !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson) +} + func getSourceRoot(mapOptions *core.CompilerOptions) string { // Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the // relative paths of the sources list in the sourcemap @@ -342,8 +391,33 @@ func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMa } func getDeclarationEmitOutputFilePath(file string, host EmitHost) string { - // !!! - return "" + options := host.Options() + var outputDir *string + if len(options.DeclarationDir) > 0 { + outputDir = &options.DeclarationDir + } else if len(options.OutDir) > 0 { + outputDir = &options.OutDir + } + + var path string + if outputDir != nil { + path = getSourceFilePathInNewDirWorker(file, *outputDir, host.GetCurrentDirectory(), host.CommonSourceDirectory(), host.UseCaseSensitiveFileNames()) + } else { + path = file + } + declarationExtension := tspath.GetDeclarationEmitExtensionForPath(path) + return tspath.RemoveFileExtension(path) + declarationExtension +} + +func getSourceFilePathInNewDirWorker(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, useCaseSensitiveFileNames bool) string { + sourceFilePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) + commonDir := tspath.GetCanonicalFileName(commonSourceDirectory, useCaseSensitiveFileNames) + canonFile := tspath.GetCanonicalFileName(sourceFilePath, useCaseSensitiveFileNames) + isSourceFileInCommonSourceDirectory := strings.HasPrefix(canonFile, commonDir) + if isSourceFileInCommonSourceDirectory { + sourceFilePath = sourceFilePath[len(commonSourceDirectory):] + } + return tspath.CombinePaths(newDirPath, sourceFilePath) } type outputPaths struct { @@ -354,6 +428,16 @@ type outputPaths struct { buildInfoPath string } +// DeclarationFilePath implements declarations.OutputPaths. +func (o *outputPaths) DeclarationFilePath() string { + return o.declarationFilePath +} + +// JsFilePath implements declarations.OutputPaths. +func (o *outputPaths) JsFilePath() string { + return o.jsFilePath +} + func getOutputPathsFor(sourceFile *ast.SourceFile, host EmitHost, forceDtsEmit bool) *outputPaths { options := host.Options() // !!! bundle not implemented, may be deprecated @@ -434,3 +518,22 @@ func getSourceFilesToEmit(host EmitHost, targetSourceFile *ast.SourceFile, force return sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) }) } + +func isJsonSourceFile(file *ast.SourceFile) bool { + return file.ScriptKind == core.ScriptKindJSON +} + +func isSourceFileNotJson(file *ast.SourceFile) bool { + return !isJsonSourceFile(file) +} + +func getDeclarationDiagnostics(host EmitHost, resolver printer.EmitResolver, file *ast.SourceFile) []*ast.Diagnostic { + fullFiles := core.Filter(getSourceFilesToEmit(host, file, false), isSourceFileNotJson) + if !core.Some(fullFiles, func(f *ast.SourceFile) bool { return f == file }) { + return []*ast.Diagnostic{} + } + options := host.Options() + transform := declarations.NewDeclarationTransformer(host, resolver, nil, options, "", "") + transform.TransformSourceFile(file) + return transform.GetDiagnostics() +} diff --git a/internal/compiler/program.go b/internal/compiler/program.go index f5fdb1b523..1419afae8a 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -71,6 +71,50 @@ type Program struct { unsupportedExtensions []string } +/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ +func (p *Program) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { + // TODO: The module loader in corsa is fairly different than strada, it should probably be able to expose this functionality at some point, + // rather than redoing the logic approximately here, since most of the related logic now lives in module.Resolver + // Still, without the failed lookup reporting that only the loader does, this isn't terribly complicated + + fileName := tspath.ResolvePath(tspath.GetDirectoryPath(origin.FileName()), ref.FileName) + supportedExtensionsBase := tsoptions.GetSupportedExtensions(p.Options(), nil /*extraFileExtensions*/) + supportedExtensions := tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(p.Options(), supportedExtensionsBase) + allowNonTsExtensions := p.Options().AllowNonTsExtensions.IsTrue() + if tspath.HasExtension(fileName) { + if !allowNonTsExtensions { + canonicalFileName := tspath.GetCanonicalFileName(fileName, p.host.FS().UseCaseSensitiveFileNames()) + supported := false + for _, group := range supportedExtensions { + if tspath.FileExtensionIsOneOf(canonicalFileName, group) { + supported = true + break + } + } + if !supported { + return nil // unsupported extensions are forced to fail + } + } + + return p.GetSourceFile(fileName) + } + if allowNonTsExtensions { + extensionless := p.GetSourceFile(fileName) + if extensionless != nil { + return extensionless + } + } + + // Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts) + for _, ext := range supportedExtensions[0] { + result := p.GetSourceFile(fileName + ext) + if result != nil { + return result + } + } + return nil +} + func NewProgram(options ProgramOptions) *Program { p := &Program{} p.programOptions = options @@ -294,6 +338,10 @@ func (p *Program) GetGlobalDiagnostics() []*ast.Diagnostic { return SortAndDeduplicateDiagnostics(globalDiagnostics) } +func (p *Program) GetDeclarationDiagnostics(sourceFile *ast.SourceFile) []*ast.Diagnostic { + return p.getDiagnosticsHelper(sourceFile, true /*ensureBound*/, true /*ensureChecked*/, p.getDeclarationDiagnosticsForFile) +} + func (p *Program) GetOptionsDiagnostics() []*ast.Diagnostic { return SortAndDeduplicateDiagnostics(append(p.GetGlobalDiagnostics(), p.getOptionsDiagnosticsOfConfigFile()...)) } @@ -380,6 +428,14 @@ func (p *Program) getSemanticDiagnosticsForFile(sourceFile *ast.SourceFile) []*a return filtered } +func (p *Program) getDeclarationDiagnosticsForFile(sourceFile *ast.SourceFile) []*ast.Diagnostic { + if sourceFile.IsDeclarationFile { + return []*ast.Diagnostic{} + } + host := &emitHost{program: p} + return getDeclarationDiagnostics(host, host.GetEmitResolver(sourceFile, true), sourceFile) +} + func isCommentOrBlankLine(text string, pos int) bool { for pos < len(text) && (text[pos] == ' ' || text[pos] == '\t') { pos++ diff --git a/internal/core/compileroptions.go b/internal/core/compileroptions.go index b35bf50f09..ec606be70d 100644 --- a/internal/core/compileroptions.go +++ b/internal/core/compileroptions.go @@ -252,8 +252,7 @@ func (options *CompilerOptions) GetEmitStandardClassFields() bool { } func (options *CompilerOptions) GetEmitDeclarations() bool { - // !!! - return false + return options.Declaration.IsTrue() || options.Composite.IsTrue() } func (options *CompilerOptions) GetAreDeclarationMapsEnabled() bool { diff --git a/internal/declarations/diagnostics.go b/internal/declarations/diagnostics.go new file mode 100644 index 0000000000..61028b366b --- /dev/null +++ b/internal/declarations/diagnostics.go @@ -0,0 +1,466 @@ +package declarations + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/printer" +) + +type GetSymbolAccessibilityDiagnostic = func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic + +type SymbolAccessibilityDiagnostic struct { + errorNode *ast.Node + diagnosticMessage *diagnostics.Message + typeName *ast.Node +} + +func wrapSimpleDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic { + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selector(node, symbolAccessibilityResult) + if diagnosticMessage == nil { + return nil + } + return &SymbolAccessibilityDiagnostic{ + errorNode: node, + diagnosticMessage: diagnosticMessage, + typeName: ast.GetNameOfDeclaration(node), + } + } +} + +func wrapNamedDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic { + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selector(node, symbolAccessibilityResult) + if diagnosticMessage == nil { + return nil + } + name := ast.GetNameOfDeclaration(node) + return &SymbolAccessibilityDiagnostic{ + errorNode: name, + diagnosticMessage: diagnosticMessage, + typeName: name, + } + } +} + +func wrapFallbackErrorDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic { + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selector(node, symbolAccessibilityResult) + if diagnosticMessage == nil { + return nil + } + errorNode := ast.GetNameOfDeclaration(node) + if errorNode == nil { + errorNode = node + } + return &SymbolAccessibilityDiagnostic{ + errorNode: errorNode, + diagnosticMessage: diagnosticMessage, + } + } +} + +func selectDiagnosticBasedOnModuleName(symbolAccessibilityResult printer.SymbolAccessibilityResult, moduleNotNameable *diagnostics.Message, privateModule *diagnostics.Message, nonModule *diagnostics.Message) *diagnostics.Message { + if len(symbolAccessibilityResult.ErrorModuleName) > 0 { + if symbolAccessibilityResult.Accessibility == printer.SymbolAccessibilityCannotBeNamed { + return moduleNotNameable + } + return privateModule + } + return nonModule +} + +func selectDiagnosticBasedOnModuleNameNoNameCheck(symbolAccessibilityResult printer.SymbolAccessibilityResult, privateModule *diagnostics.Message, nonModule *diagnostics.Message) *diagnostics.Message { + if len(symbolAccessibilityResult.ErrorModuleName) > 0 { + return privateModule + } + return nonModule +} + +func createGetSymbolAccessibilityDiagnosticForNodeName(node *ast.Node) GetSymbolAccessibilityDiagnostic { + if ast.IsSetAccessorDeclaration(node) || ast.IsGetAccessorDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getAccessorNameVisibilityDiagnosticMessage) + } else if ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getMethodNameVisibilityDiagnosticMessage) + } else { + return createGetSymbolAccessibilityDiagnosticForNode(node) + } +} + +func getAccessorNameVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1, + ) + } +} + +func getMethodNameVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1, + ) + } +} + +func createGetSymbolAccessibilityDiagnosticForNode(node *ast.Node) GetSymbolAccessibilityDiagnostic { + if ast.IsVariableDeclaration(node) || ast.IsPropertyDeclaration(node) || ast.IsPropertySignatureDeclaration(node) || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node) || ast.IsBinaryExpression(node) || ast.IsBindingElement(node) || ast.IsConstructorDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getVariableDeclarationTypeVisibilityDiagnosticMessage) + } else if ast.IsSetAccessorDeclaration(node) || ast.IsGetAccessorDeclaration(node) { + return wrapNamedDiagnosticSelector(node, getAccessorDeclarationTypeVisibilityDiagnosticMessage) + } else if ast.IsConstructSignatureDeclaration(node) || ast.IsCallSignatureDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsFunctionDeclaration(node) || ast.IsIndexSignatureDeclaration(node) { + return wrapFallbackErrorDiagnosticSelector(node, getReturnTypeVisibilityDiagnosticMessage) + } else if ast.IsParameter(node) { + if ast.IsParameterPropertyDeclaration(node, node.Parent) && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsPrivate) { + return wrapSimpleDiagnosticSelector(node, getVariableDeclarationTypeVisibilityDiagnosticMessage) + } + return wrapSimpleDiagnosticSelector(node, getParameterDeclarationTypeVisibilityDiagnosticMessage) + } else if ast.IsTypeParameterDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getTypeParameterConstraintVisibilityDiagnosticMessage) + } else if ast.IsExpressionWithTypeArguments(node) { + // unique node selection behavior, inline closure + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + var diagnosticMessage *diagnostics.Message + // Heritage clause is written by user so it can always be named + if ast.IsClassDeclaration(node.Parent.Parent) { + // Class or Interface implemented/extended is inaccessible + if ast.IsHeritageClause(node.Parent) && node.Parent.AsHeritageClause().Token == ast.KindImplementsKeyword { + diagnosticMessage = diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 + } else { + if node.Parent.Parent.Name() != nil { + diagnosticMessage = diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1 + } else { + diagnosticMessage = diagnostics.X_extends_clause_of_exported_class_has_or_is_using_private_name_0 + } + } + } else { + // interface is inaccessible + diagnosticMessage = diagnostics.X_extends_clause_of_exported_interface_0_has_or_is_using_private_name_1 + } + + return &SymbolAccessibilityDiagnostic{ + diagnosticMessage: diagnosticMessage, + errorNode: node, + typeName: ast.GetNameOfDeclaration(node.Parent.Parent), + } + } + } else if ast.IsImportEqualsDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, func(_ *ast.Node, _ printer.SymbolAccessibilityResult) *diagnostics.Message { + return diagnostics.Import_declaration_0_is_using_private_name_1 + }) + } else if ast.IsTypeAliasDeclaration(node) /*|| ast.IsJSDocTypeAlias(node)*/ { // !!! JSDoc support + // unique node selection behavior, inline closure + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1_from_module_2, + diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1, + ) + var errorNode *ast.Node + // !!! JSDoc Support + ////if ast.IsJSDocTypeAlias(node) { + //// errorNode = node.AsJSDocTypedefTag().TypeExpression + ////} else { + errorNode = node.AsTypeAliasDeclaration().Type + ////} + var typeName *ast.Node + // !!! JSDoc Support + ////if ast.IsJSDocTypeAlias(node) { + //// errorNode = getNameOfDeclaration(node) + ////} else { + typeName = node.Name() + ////} + return &SymbolAccessibilityDiagnostic{ + errorNode: errorNode, + diagnosticMessage: diagnosticMessage, + typeName: typeName, + } + } + } else { + panic(fmt.Sprintf("Attempted to set a declaration diagnostic context for unhandled node kind: {}", node.Kind)) + } +} + +func getVariableDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if node.Kind == ast.KindVariableDeclaration || node.Kind == ast.KindBindingElement { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2, + diagnostics.Exported_variable_0_has_or_is_using_private_name_1, + ) + + // This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit + // The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all. + } else if node.Kind == ast.KindPropertyDeclaration || node.Kind == ast.KindPropertyAccessExpression || node.Kind == ast.KindElementAccessExpression || node.Kind == ast.KindBinaryExpression || node.Kind == ast.KindPropertySignature || + (node.Kind == ast.KindParameter && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsPrivate)) { + // TODO(jfreeman): Deal with computed properties in error reporting. + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration || node.Kind == ast.KindParameter { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else { + // Interfaces cannot have types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1, + ) + } + } + return nil // TODO: Audit behavior - should this panic? potentially silent error state in strada +} + +func getAccessorDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if node.Kind == ast.KindSetAccessor { + // Getters can infer the return type from the returned expression, but setters cannot, so the + // "_from_external_module_1_but_cannot_be_named" case cannot occur. + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1, + ) + } else { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1, + ) + } + } else { + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1, + ) + + } else { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1, + ) + } + } +} + +func getReturnTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + switch node.Kind { + case ast.KindConstructSignature: + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0, + ) + case ast.KindCallSignature: + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0, + ) + case ast.KindIndexSignature: + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0, + ) + + case ast.KindMethodDeclaration, ast.KindMethodSignature: + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named, + diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named, + diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0, + ) + } else { + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0, + ) + } + case ast.KindFunctionDeclaration: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named, + diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0, + ) + default: + panic(fmt.Sprintf("This is unknown kind for signature: {}", node.Kind)) + } +} + +func getParameterDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + switch node.Parent.Kind { + case ast.KindConstructor: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1, + ) + + case ast.KindConstructSignature, ast.KindConstructorType: + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1, + ) + + case ast.KindCallSignature: + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1, + ) + + case ast.KindIndexSignature: + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1, + ) + + case ast.KindMethodDeclaration, ast.KindMethodSignature: + if ast.IsStatic(node.Parent) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1, + ) + } else { + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1, + ) + } + + case ast.KindFunctionDeclaration, ast.KindFunctionType: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1, + ) + case ast.KindSetAccessor, ast.KindGetAccessor: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_accessor_has_or_is_using_private_name_1, + ) + default: + panic(fmt.Sprintf("Unknown parent for parameter: {}", node.Parent.Kind)) + } +} + +func getTypeParameterConstraintVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + // Type parameter constraints are named by user so we should always be able to name it + switch node.Parent.Kind { + case ast.KindClassDeclaration: + return diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1 + case ast.KindInterfaceDeclaration: + return diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1 + case ast.KindMappedType: + return diagnostics.Type_parameter_0_of_exported_mapped_object_type_is_using_private_name_1 + case ast.KindConstructorType, ast.KindConstructSignature: + return diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1 + case ast.KindCallSignature: + return diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1 + case ast.KindMethodDeclaration, ast.KindMethodSignature: + if ast.IsStatic(node.Parent) { + return diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1 + } else if node.Parent.Parent.Kind == ast.KindClassDeclaration { + return diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1 + } else { + return diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1 + } + case ast.KindFunctionType, ast.KindFunctionDeclaration: + return diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1 + + case ast.KindInferType: + return diagnostics.Extends_clause_for_inferred_type_0_has_or_is_using_private_name_1 + + case ast.KindTypeAliasDeclaration: + return diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1 + + default: + panic(fmt.Sprintf("This is unknown parent for type parameter: {}", node.Parent.Kind)) + } +} + +// !!! TODO isolatedDeclarations createGetIsolatedDeclarationErrors diff --git a/internal/declarations/tracker.go b/internal/declarations/tracker.go new file mode 100644 index 0000000000..e50b50ff31 --- /dev/null +++ b/internal/declarations/tracker.go @@ -0,0 +1,217 @@ +package declarations + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" +) + +type SymbolTrackerImpl struct { + resolver printer.EmitResolver + state *SymbolTrackerSharedState + fallbackStack []*ast.Node +} + +// GetModuleSpecifierGenerationHost implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { + panic("unimplemented") +} + +// PopErrorFallbackNode implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) PopErrorFallbackNode() { + s.fallbackStack = s.fallbackStack[:len(s.fallbackStack)-1] +} + +// PushErrorFallbackNode implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) { + s.fallbackStack = append(s.fallbackStack, node) +} + +// ReportCyclicStructureError implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportCyclicStructureError() { + location := s.errorLocation() + if location != nil { + s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_a_type_with_a_cyclic_structure_which_cannot_be_trivially_serialized_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback())) + } +} + +// ReportInaccessibleThisError implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportInaccessibleThisError() { + location := s.errorLocation() + if location != nil { + s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), "this")) + } +} + +// ReportInaccessibleUniqueSymbolError implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() { + location := s.errorLocation() + if location != nil { + s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), "unique symbol")) + } +} + +// ReportInferenceFallback implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) { + if s.state.isolatedDeclarations || ast.IsSourceFileJS(s.state.currentSourceFile) { + return + } + if ast.GetSourceFileOfNode(node) != s.state.currentSourceFile { + return // Nested error on a declaration in another file - ignore, will be reemitted if file is in the output file set + } + if ast.IsVariableDeclaration(node) && s.state.resolver.IsExpandoFunctionDeclaration(node) { + s.state.reportExpandoFunctionErrors(node) + } else { + // !!! isolatedDeclaration support + // s.state.addDiagnostic(getIsolatedDeclarationError(node)) + } +} + +// ReportLikelyUnsafeImportRequiredError implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) { + location := s.errorLocation() + if location != nil { + s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_cannot_be_named_without_a_reference_to_1_This_is_likely_not_portable_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), specifier)) + } +} + +// ReportNonSerializableProperty implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) { + location := s.errorLocation() + if location != nil { + s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_type_of_this_node_cannot_be_serialized_because_its_property_0_cannot_be_serialized, propertyName)) + } +} + +// ReportNonlocalAugmentation implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) { + primaryDeclaration := core.Find(parentSymbol.Declarations, func(d *ast.Node) bool { return ast.GetSourceFileOfNode(d) == containingFile }) + augmentingDeclarations := core.Filter(augmentingSymbol.Declarations, func(d *ast.Node) bool { return ast.GetSourceFileOfNode(d) != containingFile }) + if primaryDeclaration != nil && len(augmentingDeclarations) > 0 { + for _, augmentations := range augmentingDeclarations { + diag := createDiagnosticForNode(augmentations, diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized) + related := createDiagnosticForNode(primaryDeclaration, diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file) + diag.AddRelatedInfo(related) + s.state.addDiagnostic(diag) + } + } +} + +// ReportPrivateInBaseOfClassExpression implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) { + location := s.errorLocation() + if location != nil { + diag := createDiagnosticForNode(location, diagnostics.Property_0_of_exported_anonymous_class_type_may_not_be_private_or_protected, propertyName) + if ast.IsVariableDeclaration(location.Parent) { + related := createDiagnosticForNode(location, diagnostics.Add_a_type_annotation_to_the_variable_0, s.errorDeclarationNameWithFallback()) + diag.AddRelatedInfo(related) + } + s.state.addDiagnostic(diag) + } +} + +// ReportTruncationError implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) ReportTruncationError() { + location := s.errorLocation() + if location != nil { + s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_this_node_exceeds_the_maximum_length_the_compiler_will_serialize_An_explicit_type_annotation_is_needed)) + } +} + +func (s *SymbolTrackerImpl) errorFallbackNode() *ast.Node { + if len(s.fallbackStack) >= 1 { + return s.fallbackStack[len(s.fallbackStack)-1] + } + return nil +} + +func (s *SymbolTrackerImpl) errorLocation() *ast.Node { + location := s.state.errorNameNode + if location == nil { + location = s.errorFallbackNode() + } + return location +} + +func (s *SymbolTrackerImpl) errorDeclarationNameWithFallback() string { + if s.state.errorNameNode != nil { + return scanner.DeclarationNameToString(s.state.errorNameNode) + } + if s.errorFallbackNode() != nil && ast.GetNameOfDeclaration(s.errorFallbackNode()) != nil { + return scanner.DeclarationNameToString(ast.GetNameOfDeclaration(s.errorFallbackNode())) + } + if s.errorFallbackNode() != nil && ast.IsExportAssignment(s.errorFallbackNode()) { + if s.errorFallbackNode().AsExportAssignment().IsExportEquals { + return "export=" + } + return "default" + } + return "(Missing)" // same fallback declarationNameToString uses when node is zero-width (ie, nameless) +} + +// TrackSymbol implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { + if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { + return false + } + issuedDiagnostic := s.handleSymbolAccessibilityError(s.resolver.IsSymbolAccessible(symbol, enclosingDeclaration, meaning /*shouldComputeAliasToMarkVisible*/, true)) + return issuedDiagnostic +} + +func (s *SymbolTrackerImpl) handleSymbolAccessibilityError(symbolAccessibilityResult printer.SymbolAccessibilityResult) bool { + if symbolAccessibilityResult.Accessibility == printer.SymbolAccessibilityAccessible { + // Add aliases back onto the possible imports list if they're not there so we can try them again with updated visibility info + if len(symbolAccessibilityResult.AliasesToMakeVisible) > 0 { + for _, ref := range symbolAccessibilityResult.AliasesToMakeVisible { + s.state.lateMarkedStatements = core.AppendIfUnique(s.state.lateMarkedStatements, ref) + } + } + // TODO: Do all these accessibility checks inside/after the first pass in the checker when declarations are enabled, if possible + + // The checker should issue errors on unresolvable names, skip the declaration emit error for using a private/unreachable name for those + } else if symbolAccessibilityResult.Accessibility != printer.SymbolAccessibilityNotResolved { + // Report error + errorInfo := s.state.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult) + if errorInfo != nil { + info := *errorInfo + diagNode := symbolAccessibilityResult.ErrorNode + if diagNode == nil { + diagNode = errorInfo.errorNode + } + if info.typeName != nil { + s.state.addDiagnostic(createDiagnosticForNode(diagNode, info.diagnosticMessage, scanner.GetTextOfNode(info.typeName), symbolAccessibilityResult.ErrorSymbolName, symbolAccessibilityResult.ErrorModuleName)) + } else { + s.state.addDiagnostic(createDiagnosticForNode(diagNode, info.diagnosticMessage, symbolAccessibilityResult.ErrorSymbolName, symbolAccessibilityResult.ErrorModuleName)) + } + return true + } + } + return false +} + +func createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { + return checker.NewDiagnosticForNode(node, message, args...) +} + +type SymbolTrackerSharedState struct { + lateMarkedStatements []*ast.Node + diagnostics []*ast.Diagnostic + getSymbolAccessibilityDiagnostic GetSymbolAccessibilityDiagnostic + errorNameNode *ast.Node + isolatedDeclarations bool + currentSourceFile *ast.SourceFile + resolver printer.EmitResolver + reportExpandoFunctionErrors func(node *ast.Node) +} + +func (s *SymbolTrackerSharedState) addDiagnostic(diag *ast.Diagnostic) { + s.diagnostics = append(s.diagnostics, diag) +} + +func NewSymbolTracker(resolver printer.EmitResolver, state *SymbolTrackerSharedState) *SymbolTrackerImpl { + tracker := &SymbolTrackerImpl{resolver: resolver, state: state} + return tracker +} diff --git a/internal/declarations/transform.go b/internal/declarations/transform.go new file mode 100644 index 0000000000..d282ed77c9 --- /dev/null +++ b/internal/declarations/transform.go @@ -0,0 +1,743 @@ +package declarations + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/transformers" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type ReferencedFilePair struct { + file *ast.SourceFile + ref *ast.FileReference +} + +type OutputPaths interface { + DeclarationFilePath() string + JsFilePath() string +} + +// Used to be passed in the TransformationContext, which is now just an EmitContext +type DeclarationEmitHost interface { + GetCurrentDirectory() string + UseCaseSensitiveFileNames() bool + GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile + + GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths + GetResolutionModeOverride(node *ast.Node) core.ResolutionMode + GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags +} + +type DeclarationTransformer struct { + transformers.Transformer + host DeclarationEmitHost + compilerOptions *core.CompilerOptions + diagnostics []*ast.Diagnostic + tracker *SymbolTrackerImpl + state *SymbolTrackerSharedState + resolver printer.EmitResolver + + declarationFilePath string + declarationMapPath string + + isBundledEmit bool + needsDeclare bool + needsScopeFixMarker bool + resultHasScopeMarker bool + enclosingDeclaration *ast.Node + getSymbolAccessibilityDiagnostic GetSymbolAccessibilityDiagnostic + resultHasExternalModuleIndicator bool + suppressNewDiagnosticContexts bool + lateStatementReplacementMap map[ast.NodeId]*ast.Node + rawReferencedFiles []ReferencedFilePair + rawTypeReferenceDirectives []*ast.FileReference + rawLibReferenceDirectives []*ast.FileReference +} + +func NewDeclarationTransformer(host DeclarationEmitHost, resolver printer.EmitResolver, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer { + shared := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver} + tracker := NewSymbolTracker(resolver, shared) + // TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit) + tx := &DeclarationTransformer{compilerOptions: compilerOptions, tracker: tracker, state: shared, declarationFilePath: declarationFilePath, declarationMapPath: declarationMapPath, host: host} + tx.NewTransformer(tx.visit, context) + return tx +} + +func (tx *DeclarationTransformer) GetDiagnostics() []*ast.Diagnostic { + return tx.diagnostics +} + +const declarationEmitNodeBuilderFlags = nodebuilder.FlagsMultilineObjectLiterals | + nodebuilder.FlagsWriteClassExpressionAsTypeLiteral | + nodebuilder.FlagsUseTypeOfFunction | + nodebuilder.FlagsUseStructuralFallback | + nodebuilder.FlagsAllowEmptyTuple | + nodebuilder.FlagsGenerateNamesForShadowedTypeParams | + nodebuilder.FlagsNoTruncation + +const declarationEmitInternalNodeBuilderFlags = nodebuilder.InternalFlagsAllowUnresolvedNames + +// functions as both `visitDeclarationStatements` and `transformRoot`, utilitzing SyntaxList nodes +func (tx *DeclarationTransformer) visit(node *ast.Node) *ast.Node { + // !!! TODO: Bundle support? + switch node.Kind { + case ast.KindSourceFile: + return tx.visitSourceFile(node.AsSourceFile()) + default: + return tx.visitDeclarationStatements(node) + } +} + +func throwDiagnostic(result printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + panic("Diagnostic emitted without context") +} + +func (tx *DeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node { + if node.IsDeclarationFile { + return node.AsNode() + } + + tx.isBundledEmit = false + tx.needsDeclare = true + tx.needsScopeFixMarker = false + tx.resultHasScopeMarker = false + tx.enclosingDeclaration = node.AsNode() + tx.getSymbolAccessibilityDiagnostic = throwDiagnostic + tx.resultHasExternalModuleIndicator = false + tx.suppressNewDiagnosticContexts = false + tx.state.lateMarkedStatements = make([]*ast.Node, 0) + tx.lateStatementReplacementMap = make(map[ast.NodeId]*ast.Node) + tx.rawReferencedFiles = make([]ReferencedFilePair, 0) + tx.rawTypeReferenceDirectives = make([]*ast.FileReference, 0) + tx.rawLibReferenceDirectives = make([]*ast.FileReference, 0) + tx.state.currentSourceFile = node + tx.collectFileReferences(node) + updated := tx.transformSourceFile(node) + tx.state.currentSourceFile = nil + return updated +} + +func (tx *DeclarationTransformer) collectFileReferences(sourceFile *ast.SourceFile) { + tx.rawReferencedFiles = append(tx.rawReferencedFiles, core.Map(sourceFile.ReferencedFiles, func(ref *ast.FileReference) ReferencedFilePair { return ReferencedFilePair{file: sourceFile, ref: ref} })...) + tx.rawTypeReferenceDirectives = append(tx.rawTypeReferenceDirectives, sourceFile.TypeReferenceDirectives...) + tx.rawLibReferenceDirectives = append(tx.rawLibReferenceDirectives, sourceFile.LibReferenceDirectives...) +} + +func (tx *DeclarationTransformer) transformSourceFile(node *ast.SourceFile) *ast.Node { + var combinedStatements *ast.StatementList + if ast.IsSourceFileJS(node) { + // !!! TODO: JS declaration emit support + combinedStatements = tx.Factory().NewNodeList([]*ast.Node{}) + } else { + statements := tx.Visitor().VisitNodes(node.Statements) + combinedStatements = tx.transformAndReplaceLatePaintedStatements(statements) + combinedStatements.Loc = statements.Loc // setTextRange + if ast.IsExternalModule(node) && (!tx.resultHasExternalModuleIndicator || (tx.needsScopeFixMarker && !tx.resultHasScopeMarker)) { + marker := createEmptyExports(tx.Factory()) + newList := append(combinedStatements.Nodes, marker) + withMarker := tx.Factory().NewNodeList(newList) + withMarker.Loc = combinedStatements.Loc + combinedStatements = withMarker + } + } + outputFilePath := tspath.GetDirectoryPath(tspath.NormalizeSlashes(tx.declarationFilePath)) + result := tx.Factory().UpdateSourceFile(node, combinedStatements) + result.AsSourceFile().LibReferenceDirectives = tx.getLibReferences() + result.AsSourceFile().TypeReferenceDirectives = tx.getTypeReferences() + result.AsSourceFile().HasNoDefaultLib = node.HasNoDefaultLib + result.AsSourceFile().IsDeclarationFile = true + result.AsSourceFile().ReferencedFiles = tx.getReferencedFiles(outputFilePath) + return result.AsNode() +} + +func createEmptyExports(factory *ast.NodeFactory) *ast.Node { + return factory.NewExportDeclaration(nil /*isTypeOnly*/, false, factory.NewNamedExports(factory.NewNodeList([]*ast.Node{})), nil, nil) +} + +func (tx *DeclarationTransformer) transformAndReplaceLatePaintedStatements(statements *ast.StatementList) *ast.StatementList { + // This is a `while` loop because `handleSymbolAccessibilityError` can see additional import aliases marked as visible during + // error handling which must now be included in the output and themselves checked for errors. + // For example: + // ``` + // module A { + // export module Q {} + // import B = Q; + // import C = B; + // export import D = C; + // } + // ``` + // In such a scenario, only Q and D are initially visible, but we don't consider imports as private names - instead we say they if they are referenced they must + // be recorded. So while checking D's visibility we mark C as visible, then we must check C which in turn marks B, completing the chain of + // dependent imports and allowing a valid declaration file output. Today, this dependent alias marking only happens for internal import aliases. + for true { + if len(tx.state.lateMarkedStatements) == 0 { + break + } + + next := tx.state.lateMarkedStatements[0] + tx.state.lateMarkedStatements = tx.state.lateMarkedStatements[1:] + + priorNeedsDeclare := tx.needsDeclare + tx.needsDeclare = next.Parent != nil && ast.IsSourceFile(next.Parent) && !(ast.IsExternalModule(next.Parent.AsSourceFile()) && tx.isBundledEmit) + + result := tx.transformTopLevelDeclaration(next) + + tx.needsDeclare = priorNeedsDeclare + original := tx.EmitContext().MostOriginal(next) + id := ast.GetNodeId(original) + tx.lateStatementReplacementMap[id] = result + } + + // And lastly, we need to get the final form of all those indetermine import declarations from before and add them to the output list + // (and remove them from the set to examine for outter declarations) + results := make([]*ast.Node, 0, len(statements.Nodes)) + for _, statement := range statements.Nodes { + if !isLateVisibilityPaintedStatement(statement) { + results = append(results, statement) + continue + } + original := tx.EmitContext().MostOriginal(statement) + id := ast.GetNodeId(original) + replacement, ok := tx.lateStatementReplacementMap[id] + if !ok || replacement == nil { + continue // not replaced, elide + } + if replacement.Kind == ast.KindSyntaxList { + if !tx.needsScopeFixMarker || !tx.resultHasExternalModuleIndicator { + for _, elem := range replacement.AsSyntaxList().Children { + if needsScopeMarker(elem) { + tx.needsScopeFixMarker = true + } + if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) { + tx.resultHasExternalModuleIndicator = true + } + } + } + results = append(results, replacement.AsSyntaxList().Children...) + } else { + if needsScopeMarker(replacement) { + tx.needsScopeFixMarker = true + } + if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) { + tx.resultHasExternalModuleIndicator = true + } + results = append(results, replacement) + } + } + + return tx.Factory().NewNodeList(results) +} + +func (tx *DeclarationTransformer) getReferencedFiles(outputFilePath string) (results []*ast.FileReference) { + // Handle path rewrites for triple slash ref comments + for _, pair := range tx.rawReferencedFiles { + sourceFile := pair.file + ref := pair.ref + + if !ref.Preserve { + continue + } + + file := tx.host.GetSourceFileFromReference(sourceFile, ref) + if file == nil { + continue + } + + var declFileName string + if file.IsDeclarationFile { + declFileName = file.FileName() + } else { + // !!! bundled emit support, omit bundled refs + // if (tx.isBundledEmit && contains((node as Bundle).sourceFiles, file)) continue + paths := tx.host.GetOutputPathsFor(file, true) + // Try to use output path for referenced file, or output js path if that doesn't exist, or the input path if all else fails + declFileName = paths.DeclarationFilePath() + if len(declFileName) == 0 { + declFileName = paths.JsFilePath() + } + if len(declFileName) == 0 { + declFileName = file.FileName() + } + } + // Should only be missing if the source file is missing a fileName (at which point we can't name a reference to it anyway) + // TODO: Shouldn't this be a crash or assert instead of a silent continue? + if len(declFileName) == 0 { + continue + } + + fileName := tspath.GetRelativePathToDirectoryOrUrl( + outputFilePath, + declFileName, + false, // TODO: Probably unsafe to assume this isn't a URL, but that's what strada does + tspath.ComparePathsOptions{ + CurrentDirectory: tx.host.GetCurrentDirectory(), + UseCaseSensitiveFileNames: tx.host.UseCaseSensitiveFileNames(), + }, + ) + + results = append(results, &ast.FileReference{ + TextRange: core.NewTextRange(-1, -1), + FileName: fileName, + ResolutionMode: ref.ResolutionMode, + Preserve: ref.Preserve, + }) + } + return results +} + +func (tx *DeclarationTransformer) getLibReferences() (result []*ast.FileReference) { + // clone retained references + for _, ref := range tx.rawLibReferenceDirectives { + if !ref.Preserve { + continue + } + result = append(result, &ast.FileReference{ + TextRange: core.NewTextRange(-1, -1), + FileName: ref.FileName, + ResolutionMode: ref.ResolutionMode, + Preserve: ref.Preserve, + }) + } + return result +} + +func (tx *DeclarationTransformer) getTypeReferences() (result []*ast.FileReference) { + // clone retained references + for _, ref := range tx.rawTypeReferenceDirectives { + if !ref.Preserve { + continue + } + result = append(result, &ast.FileReference{ + TextRange: core.NewTextRange(-1, -1), + FileName: ref.FileName, + ResolutionMode: ref.ResolutionMode, + Preserve: ref.Preserve, + }) + } + return result +} + +func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *ast.Node { + if !isPreservedDeclarationStatement(input) { + // return nil for unmatched kinds to omit them from the tree + return nil + } + // !!! TODO: stripInternal support? + // if (shouldStripInternal(input)) return nil + switch input.Kind { + case ast.KindExportDeclaration: + if ast.IsSourceFile(input.Parent) { + tx.resultHasExternalModuleIndicator = true + } + tx.resultHasScopeMarker = true + // Rewrite external module names if necessary + return tx.Factory().UpdateExportDeclaration( + input.AsExportDeclaration(), + input.Modifiers(), + input.IsTypeOnly(), + input.AsExportDeclaration().ExportClause, + tx.rewriteModuleSpecifier(input, input.AsExportDeclaration().ModuleSpecifier), + tx.tryGetResolutionModeOverride(input.AsExportDeclaration().Attributes), + ) + case ast.KindExportAssignment: + if ast.IsSourceFile(input.Parent) { + tx.resultHasExternalModuleIndicator = true + } + tx.resultHasScopeMarker = true + if input.AsExportAssignment().Expression.Kind == ast.KindIdentifier { + return input + } + // expression is non-identifier, create _default typed variable to reference + newId := tx.EmitContext().NewUniqueName("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) + tx.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + return &SymbolAccessibilityDiagnostic{ + diagnosticMessage: diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, + errorNode: input, + } + } + tx.tracker.PushErrorFallbackNode(input) + type_ := tx.ensureType(input, false) + varDecl := tx.Factory().NewVariableDeclaration(newId, nil, type_, nil) + tx.tracker.PopErrorFallbackNode() + var modList *ast.ModifierList + if tx.needsDeclare { + modList = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)}) + } else { + modList = tx.Factory().NewModifierList([]*ast.Node{}) + } + statement := tx.Factory().NewVariableStatement(modList, tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl}))) + + assignment := tx.Factory().UpdateExportAssignment(input.AsExportAssignment(), input.Modifiers(), newId) + // Remove coments from the export declaration and copy them onto the synthetic _default declaration + tx.preserveJsDoc(statement, input) + tx.removeAllComments(assignment) + tx.Factory().NewSyntaxList([]*ast.Node{statement, assignment}) + return nil + default: + result := tx.transformTopLevelDeclaration(input) + // Don't actually transform yet; just leave as original node - will be elided/swapped by late pass + original := tx.EmitContext().MostOriginal(input) + id := ast.GetNodeId(original) + tx.lateStatementReplacementMap[id] = result + return input + } +} + +func (tx *DeclarationTransformer) rewriteModuleSpecifier(parent *ast.Node, input *ast.Node) *ast.Node { + if input == nil { + return nil + } + tx.resultHasExternalModuleIndicator = tx.resultHasExternalModuleIndicator || (parent.Kind != ast.KindModuleDeclaration && parent.Kind != ast.KindImportType) + if ast.IsStringLiteralLike(input) { + if tx.isBundledEmit { + // !!! TODO: support bundled emit specifier rewriting + } + } + return input +} + +func (tx *DeclarationTransformer) tryGetResolutionModeOverride(node *ast.Node) *ast.Node { + if node == nil { + return node + } + mode := tx.host.GetResolutionModeOverride(node) + if mode != core.ResolutionModeNone { + return node + } + return nil +} + +func (tx *DeclarationTransformer) preserveJsDoc(updated *ast.Node, original *ast.Node) { + // !!! TODO: JSDoc comment support + // if (hasJSDocNodes(updated) && hasJSDocNodes(original)) { + // updated.jsDoc = original.jsDoc; + // } + // return setCommentRange(updated, getCommentRange(original)); +} + +func (tx *DeclarationTransformer) removeAllComments(node *ast.Node) { + tx.EmitContext().AddEmitFlags(node, printer.EFNoComments) + // !!! TODO: Also remove synthetic trailing/leading comments added by transforms + // emitNode.leadingComments = undefined; + // emitNode.trailingComments = undefined; +} + +func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) *ast.Node { + if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 { + // Private nodes emit no types (except private parameter properties, whose parameter types are actually visible) + return nil + } + + if tx.shouldPrintWithInitializer(node) { + // Literal const declarations will have an initializer ensured rather than a type + return nil + } + + // Should be removed createTypeOfDeclaration will actually now reuse the existing annotation so there is no real need to duplicate type walking + // Left in for now to minimize diff during syntactic type node builder refactor + if !ast.IsExportAssignment(node) && !ast.IsBindingElement(node) && node.Type() != nil && (!ast.IsParameter(node) || !tx.resolver.RequiresAddingImplicitUndefined(node, nil, tx.enclosingDeclaration)) { + // return visitNode(node.type, visitDeclarationSubtree, isTypeNode); // !!! TODO: visitDeclarationSubtree for ensuring syntactic completeness and vis errors + return node + } + + oldErrorNameNode := tx.state.errorNameNode + tx.state.errorNameNode = node.Name() + var oldDiag GetSymbolAccessibilityDiagnostic + if !tx.suppressNewDiagnosticContexts { + oldDiag = tx.getSymbolAccessibilityDiagnostic + if canProduceDiagnostics(node) { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node) + } + } + var typeNode *ast.Node + + if hasInferredType(node) { + typeNode = tx.resolver.CreateTypeOfDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + } else if ast.IsFunctionLike(node) { + typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + } else { + // Debug.assertNever(node); // !!! + } + + tx.state.errorNameNode = oldErrorNameNode + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + } + if typeNode == nil { + return tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) + } + return typeNode +} + +func (tx *DeclarationTransformer) shouldPrintWithInitializer(node *ast.Node) bool { + return canHaveLiteralInitializer(tx.host, node) && node.Initializer() != nil && tx.resolver.IsLiteralConstDeclaration(tx.EmitContext().MostOriginal(node)) +} + +func (tx *DeclarationTransformer) checkEntityNameVisibility(entityName *ast.Node, enclosingDeclaration *ast.Node) { + visibilityResult := tx.resolver.IsEntityNameVisible(entityName, enclosingDeclaration) + tx.tracker.handleSymbolAccessibilityError(visibilityResult) +} + +// Transforms the direct child of a source file into zero or more replacement statements +func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) *ast.Node { + if len(tx.state.lateMarkedStatements) > 0 { + // Remove duplicates of the current statement from the deferred work queue (this was done via orderedRemoveItem in strada - why? to ensure the same backing array? microop?) + tx.state.lateMarkedStatements = core.Filter(tx.state.lateMarkedStatements, func(node *ast.Node) bool { return node != input }) + } + // !!! TODO: stripInternal support? + // if (shouldStripInternal(input)) return; + if input.Kind == ast.KindImportEqualsDeclaration { + return tx.transformImportEqualsDeclaration(input.AsImportEqualsDeclaration()) + } + if input.Kind == ast.KindImportDeclaration { + return tx.transformImportDeclaration(input.AsImportDeclaration()) + } + if ast.IsDeclaration(input) && isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) { + return nil + } + + // !!! TODO: JSDoc support + // if (isJSDocImportTag(input)) return; + + // Elide implementation signatures from overload sets + if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) { + return nil + } + previousEnclosingDeclaration := tx.enclosingDeclaration + if isEnclosingDeclaration(input) { + tx.enclosingDeclaration = input + } + + canProdiceDiagnostic := canProduceDiagnostics(input) + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if canProdiceDiagnostic { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) + } + previousNeedsDeclare := tx.needsDeclare + + var result *ast.Node + switch input.Kind { + // !!! + case ast.KindVariableStatement: + result = tx.transformVariableStatement(input.AsVariableStatement()) + case ast.KindEnumDeclaration: + result = tx.Factory().UpdateEnumDeclaration( + input.AsEnumDeclaration(), + tx.Factory().NewModifierList(tx.ensureModifiers(input)), + input.Name(), + tx.Factory().NewNodeList(core.MapNonNil(input.AsEnumDeclaration().Members.Nodes, func(m *ast.Node) *ast.Node { + // !!! TODO: stripInternal support? + // if (shouldStripInternal(m)) return; + + // !!! TODO: isolatedDeclarations support + // if ( + // isolatedDeclarations && m.initializer && enumValue?.hasExternalReferences && + // // This will be its own compiler error instead, so don't report. + // !isComputedPropertyName(m.name) + // ) { + // context.addDiagnostic(createDiagnosticForNode(m, Diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations)); + // } + + // Rewrite enum values to their constants, if available + enumValue := tx.resolver.GetEnumMemberValue(m) + var newInitializer *ast.Node + switch value := enumValue.Value.(type) { + case jsnum.Number: + if value >= 0 { + newInitializer = tx.Factory().NewNumericLiteral(value.String()) + } else { + newInitializer = tx.Factory().NewPrefixUnaryExpression( + ast.KindMinusToken, + tx.Factory().NewNumericLiteral((-value).String()), + ) + } + case string: + newInitializer = tx.Factory().NewStringLiteral(value) + default: + // nil + newInitializer = nil + } + result := tx.Factory().UpdateEnumMember(m.AsEnumMember(), m.Name(), newInitializer) + tx.preserveJsDoc(result, m) + return result + })), + ) + default: + // Anything left unhandled is an error, so this should be unreachable + panic(fmt.Sprintf("Unhandled top-level node in declaration emit: %q", input.Kind)) + } + + if isEnclosingDeclaration(input) { + tx.enclosingDeclaration = previousEnclosingDeclaration + } + if canProdiceDiagnostic { + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + } + if input.Kind == ast.KindModuleDeclaration { + tx.needsDeclare = previousNeedsDeclare + } + if result == input { + return input + } + tx.state.errorNameNode = nil + return result +} + +func (tx *DeclarationTransformer) transformVariableStatement(node *ast.VariableStatement) *ast.Node { + return nil // !!! +} + +func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) []*ast.Node { + currentFlags := tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsAll) + newFlags := tx.ensureModifierFlags(node) + if currentFlags == newFlags { + // Elide decorators + return core.Filter(node.Modifiers().Nodes, ast.IsModifier) + } + return ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) +} + +func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.ModifierFlags { + mask := ast.ModifierFlagsAll ^ (ast.ModifierFlagsPublic | ast.ModifierFlagsAsync | ast.ModifierFlagsOverride) // No async and override modifiers in declaration files + additions := ast.ModifierFlagsNone + if tx.needsDeclare && !isAlwaysType(node) { + additions = ast.ModifierFlagsAmbient + } + parentIsFile := node.Parent.Kind == ast.KindSourceFile + if !parentIsFile || (tx.isBundledEmit && parentIsFile && ast.IsExternalModule(node.Parent.AsSourceFile())) { + mask ^= ast.ModifierFlagsAmbient + additions = ast.ModifierFlagsNone + } + return maskModifierFlagsEx(tx.host, node, mask, additions) +} + +func (tx *DeclarationTransformer) transformImportEqualsDeclaration(decl *ast.ImportEqualsDeclaration) *ast.Node { + if !tx.resolver.IsDeclarationVisible(decl.AsNode()) { + return nil + } + if decl.ModuleReference.Kind == ast.KindExternalModuleReference { + // Rewrite external module names if necessary + specifier := ast.GetExternalModuleImportEqualsDeclarationExpression(decl.AsNode()) + return tx.Factory().UpdateImportEqualsDeclaration( + decl, + decl.Modifiers(), + decl.IsTypeOnly, + decl.Name(), + tx.Factory().UpdateExternalModuleReference(decl.ModuleReference.AsExternalModuleReference(), tx.rewriteModuleSpecifier(decl.AsNode(), specifier)), + ) + } else { + oldDiag := tx.getSymbolAccessibilityDiagnostic + tx.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(decl.AsNode()) + tx.checkEntityNameVisibility(decl.ModuleReference, tx.enclosingDeclaration) + tx.getSymbolAccessibilityDiagnostic = oldDiag + return decl.AsNode() + } +} + +func (tx *DeclarationTransformer) transformImportDeclaration(decl *ast.ImportDeclaration) *ast.Node { + if decl.ImportClause == nil { + // import "mod" - possibly needed for side effects? (global interface patches, module augmentations, etc) + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + decl.ImportClause, + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // The `importClause` visibility corresponds to the default's visibility. + var visibleDefaultBinding *ast.Node + if decl.ImportClause != nil && decl.ImportClause.Name() != nil && tx.resolver.IsDeclarationVisible(decl.ImportClause) { + visibleDefaultBinding = decl.ImportClause.Name() + } + if decl.ImportClause.AsImportClause().NamedBindings == nil { + // No named bindings (either namespace or list), meaning the import is just default or should be elided + if visibleDefaultBinding == nil { + return nil + } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + tx.Factory().UpdateImportClause( + decl.ImportClause.AsImportClause(), + decl.ImportClause.AsImportClause().IsTypeOnly, + visibleDefaultBinding, + /*namedBindings*/ nil, + ), + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + if decl.ImportClause.AsImportClause().NamedBindings.Kind == ast.KindNamespaceImport { + // Namespace import (optionally with visible default) + var namedBindings *ast.Node + if tx.resolver.IsDeclarationVisible(decl.ImportClause.AsImportClause().NamedBindings) { + namedBindings = decl.ImportClause.AsImportClause().NamedBindings + } + if visibleDefaultBinding == nil && namedBindings == nil { + return nil + } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + tx.Factory().UpdateImportClause( + decl.ImportClause.AsImportClause(), + decl.ImportClause.AsImportClause().IsTypeOnly, + visibleDefaultBinding, + namedBindings, + ), + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // Named imports (optionally with visible default) + bindingList := core.Filter( + decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports().Elements.Nodes, + func(b *ast.Node) bool { + return tx.resolver.IsDeclarationVisible(b) + }, + ) + if len(bindingList) > 0 || visibleDefaultBinding != nil { + var namedImports *ast.Node + if len(bindingList) > 0 { + namedImports = tx.Factory().UpdateNamedImports( + decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports(), + tx.Factory().NewNodeList(bindingList), + ) + } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + tx.Factory().UpdateImportClause( + decl.ImportClause.AsImportClause(), + decl.ImportClause.AsImportClause().IsTypeOnly, + visibleDefaultBinding, + namedImports, + ), + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // Augmentation of export depends on import + if tx.resolver.IsImportRequiredByAugmentation(decl) { + // IsolatedDeclarations support + // if (isolatedDeclarations) { + // context.addDiagnostic(createDiagnosticForNode(decl, Diagnostics.Declaration_emit_for_this_file_requires_preserving_this_import_for_augmentations_This_is_not_supported_with_isolatedDeclarations)); + // } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + /*importClause*/ nil, + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // Nothing visible + return nil +} diff --git a/internal/declarations/util.go b/internal/declarations/util.go new file mode 100644 index 0000000000..abc315310c --- /dev/null +++ b/internal/declarations/util.go @@ -0,0 +1,181 @@ +package declarations + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/printer" +) + +func isPreservedDeclarationStatement(node *ast.Node) bool { + switch node.Kind { + case ast.KindFunctionDeclaration, + ast.KindModuleDeclaration, + ast.KindImportEqualsDeclaration, + ast.KindInterfaceDeclaration, + ast.KindClassDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindEnumDeclaration, + ast.KindVariableStatement, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindExportAssignment: + return true + } + return false +} + +func needsScopeMarker(result *ast.Node) bool { + return !ast.IsAnyImportOrReExport(result) && !ast.IsExportAssignment(result) && !ast.HasSyntacticModifier(result, ast.ModifierFlagsExport) && !ast.IsAmbientModule(result) +} + +func isLateVisibilityPaintedStatement(node *ast.Node) bool { + switch node.Kind { + case ast.KindImportDeclaration, + ast.KindImportEqualsDeclaration, + ast.KindVariableStatement, + ast.KindClassDeclaration, + ast.KindFunctionDeclaration, + ast.KindModuleDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindInterfaceDeclaration, + ast.KindEnumDeclaration: + return true + default: + return false + } +} + +func canHaveLiteralInitializer(host DeclarationEmitHost, node *ast.Node) bool { + switch node.Kind { + case ast.KindPropertyDeclaration, + ast.KindPropertySignature: + return host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 + case ast.KindParameter, + ast.KindVariableDeclaration: + return true + } + return false +} + +func canProduceDiagnostics(node *ast.Node) bool { + return ast.IsVariableDeclaration(node) || + ast.IsPropertyDeclaration(node) || + ast.IsPropertySignatureDeclaration(node) || + ast.IsBindingElement(node) || + ast.IsSetAccessorDeclaration(node) || + ast.IsGetAccessorDeclaration(node) || + ast.IsConstructSignatureDeclaration(node) || + ast.IsCallSignatureDeclaration(node) || + ast.IsMethodDeclaration(node) || + ast.IsMethodSignatureDeclaration(node) || + ast.IsFunctionDeclaration(node) || + ast.IsParameter(node) || + ast.IsTypeParameterDeclaration(node) || + ast.IsExpressionWithTypeArguments(node) || + ast.IsImportEqualsDeclaration(node) || + ast.IsTypeAliasDeclaration(node) || + ast.IsConstructorDeclaration(node) || + ast.IsIndexSignatureDeclaration(node) || + ast.IsPropertyAccessExpression(node) || + ast.IsElementAccessExpression(node) || + ast.IsBinaryExpression(node) // || // !!! TODO: JSDoc support + /* ast.IsJSDocTypeAlias(node); */ +} + +func hasInferredType(node *ast.Node) bool { + // Debug.type(node); // !!! + switch node.Kind { + case ast.KindParameter, + ast.KindPropertySignature, + ast.KindPropertyDeclaration, + ast.KindBindingElement, + ast.KindPropertyAccessExpression, + ast.KindElementAccessExpression, + ast.KindBinaryExpression, + ast.KindVariableDeclaration, + ast.KindExportAssignment, + ast.KindPropertyAssignment, + ast.KindShorthandPropertyAssignment, + ast.KindJSDocParameterTag, + ast.KindJSDocPropertyTag: + return true + default: + // assertType(node); // !!! + return false + } +} + +func isDeclarationAndNotVisible(emitContext *printer.EmitContext, resolver printer.EmitResolver, node *ast.Node) bool { + node = emitContext.ParseNode(node) + switch node.Kind { + case ast.KindFunctionDeclaration, + ast.KindModuleDeclaration, + ast.KindInterfaceDeclaration, + ast.KindClassDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindEnumDeclaration: + return !resolver.IsDeclarationVisible(node) + // The following should be doing their own visibility checks based on filtering their members + case ast.KindVariableDeclaration: + return !getBindingNameVisible(resolver, node) + case ast.KindImportEqualsDeclaration: + case ast.KindImportDeclaration: + case ast.KindExportDeclaration: + case ast.KindExportAssignment: + return false + case ast.KindClassStaticBlockDeclaration: + return true + } + return false +} + +func getBindingNameVisible(resolver printer.EmitResolver, elem *ast.Node) bool { + if ast.IsOmittedExpression(elem) { + return false + } + if ast.IsBindingPattern(elem.Name()) { + // If any child binding pattern element has been marked visible (usually by collect linked aliases), then this is visible + for _, elem := range elem.Name().AsBindingPattern().Elements.Nodes { + if getBindingNameVisible(resolver, elem) { + return true + } + } + return false + } else { + return resolver.IsDeclarationVisible(elem) + } +} + +func isEnclosingDeclaration(node *ast.Node) bool { + return ast.IsSourceFile(node) || + ast.IsTypeAliasDeclaration(node) || + ast.IsModuleDeclaration(node) || + ast.IsClassDeclaration(node) || + ast.IsInterfaceDeclaration(node) || + ast.IsFunctionLike(node) || + ast.IsIndexSignatureDeclaration(node) || + ast.IsMappedTypeNode(node) +} + +func isAlwaysType(node *ast.Node) bool { + if node.Kind == ast.KindInterfaceDeclaration { + return true + } + return false +} + +func maskModifierFlags(host DeclarationEmitHost, node *ast.Node) ast.ModifierFlags { + return maskModifierFlagsEx(host, node, ast.ModifierFlagsAll^ast.ModifierFlagsPublic, ast.ModifierFlagsNone) +} + +func maskModifierFlagsEx(host DeclarationEmitHost, node *ast.Node, modifierMask ast.ModifierFlags, modifierAdditions ast.ModifierFlags) ast.ModifierFlags { + flags := host.GetEffectiveDeclarationFlags(node, modifierMask) | modifierAdditions + if flags&ast.ModifierFlagsDefault != 0 && (flags&ast.ModifierFlagsExport == 0) { + // A non-exported default is a nonsequitor - we usually try to remove all export modifiers + // from statements in ambient declarations; but a default export must retain its export modifier to be syntactically valid + flags ^= ast.ModifierFlagsExport + } + if flags&ast.ModifierFlagsDefault != 0 && flags&ast.ModifierFlagsAmbient != 0 { + flags ^= ast.ModifierFlagsAmbient // `declare` is never required alongside `default` (and would be an error if printed) + } + return flags +} diff --git a/internal/execute/tsc.go b/internal/execute/tsc.go index be7d06f48d..33e4fa8f82 100644 --- a/internal/execute/tsc.go +++ b/internal/execute/tsc.go @@ -216,10 +216,8 @@ func compileAndEmit(sys System, program *compiler.Program, reportDiagnostic diag if len(diagnostics) == 0 { diagnostics = append(diagnostics, program.GetSemanticDiagnostics(nil)...) } - // TODO: declaration diagnostics - if len(diagnostics) == 0 && options.NoEmit == core.TSTrue && (options.Declaration.IsTrue() && options.Composite.IsTrue()) { - return nil, nil, ExitStatusNotImplemented - // addRange(allDiagnostics, program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken)); + if len(diagnostics) == 0 && options.GetEmitDeclarations() { + diagnostics = append(diagnostics, program.GetDeclarationDiagnostics(nil)...) } emitResult := &compiler.EmitResult{EmitSkipped: true, Diagnostics: []*ast.Diagnostic{}} diff --git a/internal/nodebuilder/types.go b/internal/nodebuilder/types.go new file mode 100644 index 0000000000..107d3e7de4 --- /dev/null +++ b/internal/nodebuilder/types.go @@ -0,0 +1,78 @@ +// Exports interfaces and types defining the node builder - concrete implmentations are on top of the checker, but these types and interfaces are used by the emit resolver in the printer +package nodebuilder + +import "github.com/microsoft/typescript-go/internal/ast" + +// TODO: previously all symboltracker methods were optional, but now they're required. +type SymbolTracker interface { + GetModuleSpecifierGenerationHost() any // !!! + + TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool + ReportInaccessibleThisError() + ReportPrivateInBaseOfClassExpression(propertyName string) + ReportInaccessibleUniqueSymbolError() + ReportCyclicStructureError() + ReportLikelyUnsafeImportRequiredError(specifier string) + ReportTruncationError() + ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) + ReportNonSerializableProperty(propertyName string) + + ReportInferenceFallback(node *ast.Node) + PushErrorFallbackNode(node *ast.Node) + PopErrorFallbackNode() +} + +// NOTE: If modifying this enum, must modify `TypeFormatFlags` too! +// dprint-ignore +type Flags int32 + +const ( + FlagsNone Flags = 0 + // Options + FlagsNoTruncation Flags = 1 << 0 + FlagsWriteArrayAsGenericType Flags = 1 << 1 + FlagsGenerateNamesForShadowedTypeParams Flags = 1 << 2 + FlagsUseStructuralFallback Flags = 1 << 3 + FlagsForbidIndexedAccessSymbolReferences Flags = 1 << 4 + FlagsWriteTypeArgumentsOfSignature Flags = 1 << 5 + FlagsUseFullyQualifiedType Flags = 1 << 6 + FlagsUseOnlyExternalAliasing Flags = 1 << 7 + FlagsSuppressAnyReturnType Flags = 1 << 8 + FlagsWriteTypeParametersInQualifiedName Flags = 1 << 9 + FlagsMultilineObjectLiterals Flags = 1 << 10 + FlagsWriteClassExpressionAsTypeLiteral Flags = 1 << 11 + FlagsUseTypeOfFunction Flags = 1 << 12 + FlagsOmitParameterModifiers Flags = 1 << 13 + FlagsUseAliasDefinedOutsideCurrentScope Flags = 1 << 14 + FlagsUseSingleQuotesForStringLiteralType Flags = 1 << 28 + FlagsNoTypeReduction Flags = 1 << 29 + FlagsOmitThisParameter Flags = 1 << 25 + // Error handling + FlagsAllowThisInObjectLiteral Flags = 1 << 15 + FlagsAllowQualifiedNameInPlaceOfIdentifier Flags = 1 << 16 + FlagsAllowAnonymousIdentifier Flags = 1 << 17 + FlagsAllowEmptyUnionOrIntersection Flags = 1 << 18 + FlagsAllowEmptyTuple Flags = 1 << 19 + FlagsAllowUniqueESSymbolType Flags = 1 << 20 + FlagsAllowEmptyIndexInfoType Flags = 1 << 21 + // Errors (cont.) + FlagsAllowNodeModulesRelativePaths Flags = 1 << 26 + FlagsIgnoreErrors Flags = FlagsAllowThisInObjectLiteral | FlagsAllowQualifiedNameInPlaceOfIdentifier | FlagsAllowAnonymousIdentifier | FlagsAllowEmptyUnionOrIntersection | FlagsAllowEmptyTuple | FlagsAllowEmptyIndexInfoType | FlagsAllowNodeModulesRelativePaths + // State + FlagsInObjectTypeLiteral Flags = 1 << 22 + FlagsInTypeAlias Flags = 1 << 23 + FlagsInInitialEntityName Flags = 1 << 24 +) + +/** @internal */ +// dprint-ignore + +type InternalFlags int32 + +const ( + InternalFlagsNone InternalFlags = 0 + InternalFlagsWriteComputedProps InternalFlags = 1 << 0 + InternalFlagsNoSyntacticPrinter InternalFlags = 1 << 1 + InternalFlagsDoNotIncludeSymbolChain InternalFlags = 1 << 2 + InternalFlagsAllowUnresolvedNames InternalFlags = 1 << 3 +) diff --git a/internal/printer/emitresolver.go b/internal/printer/emitresolver.go index b41172d895..0450a8192c 100644 --- a/internal/printer/emitresolver.go +++ b/internal/printer/emitresolver.go @@ -3,8 +3,27 @@ package printer import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/binder" + "github.com/microsoft/typescript-go/internal/evaluator" + "github.com/microsoft/typescript-go/internal/nodebuilder" ) +type SymbolAccessibility int32 + +const ( + SymbolAccessibilityAccessible SymbolAccessibility = iota + SymbolAccessibilityNotAccessible + SymbolAccessibilityCannotBeNamed + SymbolAccessibilityNotResolved +) + +type SymbolAccessibilityResult struct { + Accessibility SymbolAccessibility + AliasesToMakeVisible []*ast.Node // aliases that need to have this symbol visible + ErrorSymbolName string // Optional - symbol name that results in error + ErrorNode *ast.Node // Optional - node that results in error + ErrorModuleName string // Optional - If the symbol is not visible from module, module's name +} + type EmitResolver interface { binder.ReferenceResolver IsReferencedAliasDeclaration(node *ast.Node) bool @@ -12,4 +31,19 @@ type EmitResolver interface { IsTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool MarkLinkedReferencesRecursively(file *ast.SourceFile) GetExternalModuleFileFromDeclaration(node *ast.Node) *ast.SourceFile + + // declaration emit checker functionality projections + IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) SymbolAccessibilityResult + IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) SymbolAccessibilityResult // previously SymbolVisibilityResult in strada - ErrorModuleName never set + IsExpandoFunctionDeclaration(node *ast.Node) bool + IsLiteralConstDeclaration(node *ast.Node) bool + RequiresAddingImplicitUndefined(node *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool + IsDeclarationVisible(node *ast.Node) bool + IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool + IsImplementationOfOverload(node *ast.SignatureDeclaration) bool + GetEnumMemberValue(node *ast.Node) evaluator.Result + + // Node construction for declaration emit + CreateTypeOfDeclaration(emitContext *EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateReturnTypeOfSignatureDeclaration(emitContext *EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node } diff --git a/internal/testutil/tsbaseline/js_emit_baseline.go b/internal/testutil/tsbaseline/js_emit_baseline.go index ca048b8deb..b377233b72 100644 --- a/internal/testutil/tsbaseline/js_emit_baseline.go +++ b/internal/testutil/tsbaseline/js_emit_baseline.go @@ -1,6 +1,7 @@ package tsbaseline import ( + "slices" "strings" "testing" @@ -74,34 +75,33 @@ func DoJSEmitBaseline( jsCode.WriteString(fileOutput(file, harnessSettings)) } - // !!! Enable the following once .d.ts emit is implemented - ////if result.Dts.Size() > 0 { - //// jsCode += "\r\n\r\n" - //// for declFile := range result.Dts.Values() { - //// jsCode += fileOutput(declFile, harnessSettings) - //// } - ////} - //// - ////declFileContext := prepareDeclarationCompilationContext( - //// toBeCompiled, - //// otherFiles, - //// result, - //// harnessSettings, - //// options, - //// "", /*currentDirectory*/ - ////) - ////declFileCompilationResult := compileDeclarationFiles(t, declFileContext, result.Symlinks) - //// - ////if declFileCompilationResult != nil && len(declFileCompilationResult.declResult.Diagnostics) > 0 { - //// jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n" - //// jsCode += "\r\n\r\n" - //// jsCode += getErrorBaseline( - //// t, - //// slices.Concat(tsConfigFiles, declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), - //// declFileCompilationResult.declResult.Diagnostics, - //// false, /*pretty*/ - //// ) - ////} else + if result.DTS.Size() > 0 { + jsCode.WriteString("\r\n\r\n") + for declFile := range result.DTS.Values() { + jsCode.WriteString(fileOutput(declFile, harnessSettings)) + } + } + + declFileContext := prepareDeclarationCompilationContext( + toBeCompiled, + otherFiles, + result, + harnessSettings, + options, + "", /*currentDirectory*/ + ) + declFileCompilationResult := compileDeclarationFiles(t, declFileContext, result.Symlinks) + + if declFileCompilationResult != nil && len(declFileCompilationResult.declResult.Diagnostics) > 0 { + jsCode.WriteString("\r\n\r\n//// [DtsFileErrors]\r\n") + jsCode.WriteString("\r\n\r\n") + jsCode.WriteString(getErrorBaseline( + t, + slices.Concat(tsConfigFiles, declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), + declFileCompilationResult.declResult.Diagnostics, + false, /*pretty*/ + )) + } // !!! Enable the following once noCheck is more comprehensive ////if !options.NoCheck.IsTrue() && !options.NoEmit.IsTrue() { diff --git a/internal/transformers/commonjsmodule.go b/internal/transformers/commonjsmodule.go index fcfa1773b2..16b0460e92 100644 --- a/internal/transformers/commonjsmodule.go +++ b/internal/transformers/commonjsmodule.go @@ -38,7 +38,7 @@ func NewCommonJSModuleTransformer(emitContext *printer.EmitContext, compilerOpti tx.assignmentPatternVisitor = emitContext.NewNodeVisitor(tx.visitAssignmentPattern) tx.languageVersion = compilerOptions.GetEmitScriptTarget() tx.moduleKind = compilerOptions.GetEmitModuleKind() - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`. diff --git a/internal/transformers/esmodule.go b/internal/transformers/esmodule.go index bbbc0bbce7..0fbd88fbf7 100644 --- a/internal/transformers/esmodule.go +++ b/internal/transformers/esmodule.go @@ -29,7 +29,7 @@ func NewESModuleTransformer(emitContext *printer.EmitContext, compilerOptions *c resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) } tx := &ESModuleTransformer{compilerOptions: compilerOptions, resolver: resolver, sourceFileMetaDataProvider: sourceFileMetaDataProvider} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Visits source elements that are not top-level or top-level nested statements. diff --git a/internal/transformers/impliedmodule.go b/internal/transformers/impliedmodule.go index b23018b8bd..011239bdb1 100644 --- a/internal/transformers/impliedmodule.go +++ b/internal/transformers/impliedmodule.go @@ -21,7 +21,7 @@ func NewImpliedModuleTransformer(emitContext *printer.EmitContext, compilerOptio resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) } tx := &ImpliedModuleTransformer{compilerOptions: compilerOptions, resolver: resolver, sourceFileMetaDataProvider: sourceFileMetaDataProvider} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } func (tx *ImpliedModuleTransformer) visit(node *ast.Node) *ast.Node { diff --git a/internal/transformers/importelision.go b/internal/transformers/importelision.go index 2ce400bdd3..57678a09d2 100644 --- a/internal/transformers/importelision.go +++ b/internal/transformers/importelision.go @@ -18,7 +18,7 @@ func NewImportElisionTransformer(emitContext *printer.EmitContext, compilerOptio panic("ImportElisionTransformer should not be used with VerbatimModuleSyntax") } tx := &ImportElisionTransformer{compilerOptions: compilerOptions, emitResolver: resolver} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } func (tx *ImportElisionTransformer) visit(node *ast.Node) *ast.Node { diff --git a/internal/transformers/modifiervisitor.go b/internal/transformers/modifiervisitor.go index 996b149722..78b5fa80ff 100644 --- a/internal/transformers/modifiervisitor.go +++ b/internal/transformers/modifiervisitor.go @@ -23,6 +23,6 @@ func extractModifiers(emitContext *printer.EmitContext, modifiers *ast.ModifierL return nil } tx := modifierVisitor{AllowedModifiers: allowed} - tx.newTransformer(tx.visit, emitContext) + tx.NewTransformer(tx.visit, emitContext) return tx.visitor.VisitModifiers(modifiers) } diff --git a/internal/transformers/runtimesyntax.go b/internal/transformers/runtimesyntax.go index 9a3703329c..3464d27d15 100644 --- a/internal/transformers/runtimesyntax.go +++ b/internal/transformers/runtimesyntax.go @@ -33,7 +33,7 @@ type RuntimeSyntaxTransformer struct { func NewRuntimeSyntaxTransformer(emitContext *printer.EmitContext, compilerOptions *core.CompilerOptions, resolver binder.ReferenceResolver) *Transformer { tx := &RuntimeSyntaxTransformer{compilerOptions: compilerOptions, resolver: resolver} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`. diff --git a/internal/transformers/transformer.go b/internal/transformers/transformer.go index f321b779e5..5c3d636f9e 100644 --- a/internal/transformers/transformer.go +++ b/internal/transformers/transformer.go @@ -11,7 +11,7 @@ type Transformer struct { visitor *ast.NodeVisitor } -func (tx *Transformer) newTransformer(visit func(node *ast.Node) *ast.Node, emitContext *printer.EmitContext) *Transformer { +func (tx *Transformer) NewTransformer(visit func(node *ast.Node) *ast.Node, emitContext *printer.EmitContext) *Transformer { if tx.emitContext != nil { panic("Transformer already initialized") } @@ -24,6 +24,18 @@ func (tx *Transformer) newTransformer(visit func(node *ast.Node) *ast.Node, emit return tx } +func (tx *Transformer) EmitContext() *printer.EmitContext { + return tx.emitContext +} + +func (tx *Transformer) Visitor() *ast.NodeVisitor { + return tx.visitor +} + +func (tx *Transformer) Factory() *ast.NodeFactory { + return tx.factory +} + func (tx *Transformer) TransformSourceFile(file *ast.SourceFile) *ast.SourceFile { return tx.visitor.VisitSourceFile(file) } diff --git a/internal/transformers/typeeraser.go b/internal/transformers/typeeraser.go index aa5cfcf117..6594145c8e 100644 --- a/internal/transformers/typeeraser.go +++ b/internal/transformers/typeeraser.go @@ -15,7 +15,7 @@ type TypeEraserTransformer struct { func NewTypeEraserTransformer(emitContext *printer.EmitContext, compilerOptions *core.CompilerOptions) *Transformer { tx := &TypeEraserTransformer{compilerOptions: compilerOptions} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`. From 3dac2d2d4c6ced3ad041b5add5c3c5d0d609efca Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 17 Apr 2025 20:33:24 -0700 Subject: [PATCH 03/15] Last half of the declaration transform and emit resolver, bar !!!d features --- internal/ast/utilities.go | 260 ++++++- internal/checker/checker.go | 21 +- internal/checker/emitresolver.go | 565 +++++++++++++- internal/checker/nodebuilder.go | 5 +- internal/checker/nodebuilderscopes.go | 3 +- internal/checker/types.go | 6 + internal/checker/utilities.go | 28 +- internal/declarations/diagnostics.go | 8 +- internal/declarations/transform.go | 1016 +++++++++++++++++++++++-- internal/declarations/util.go | 95 ++- internal/printer/emitresolver.go | 5 + 11 files changed, 1858 insertions(+), 154 deletions(-) diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index aca5d23872..089f948c5c 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -1,6 +1,7 @@ package ast import ( + "fmt" "slices" "strings" "sync" @@ -1530,14 +1531,14 @@ func GetImplementsHeritageClauseElements(node *Node) []*ExpressionWithTypeArgume } func getHeritageElements(node *Node, kind Kind) []*Node { - clause := getHeritageClause(node, kind) + clause := GetHeritageClause(node, kind) if clause != nil { return clause.AsHeritageClause().Types.Nodes } return nil } -func getHeritageClause(node *Node, kind Kind) *Node { +func GetHeritageClause(node *Node, kind Kind) *Node { clauses := getHeritageClauses(node) if clauses != nil { for _, clause := range clauses.Nodes { @@ -2694,3 +2695,258 @@ func CreateModifiersFromModifierFlags(flags ModifierFlags, createModifier func(k } return result } + +func GetThisParameter(signature *Node) *Node { + // callback tags do not currently support this parameters + if len(signature.Parameters()) != 0 { + thisParameter := signature.Parameters()[0] + if IsThisParameter(thisParameter) { + return thisParameter + } + } + return nil +} + +func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierList) *Node { + switch node.Kind { + case KindTypeParameter: + return factory.UpdateTypeParameterDeclaration( + node.AsTypeParameter(), + modifierArray, + node.Name(), + node.AsTypeParameter().Constraint, + node.AsTypeParameter().DefaultType, + ) + case KindParameter: + return factory.UpdateParameterDeclaration( + node.AsParameterDeclaration(), + modifierArray, + node.AsParameterDeclaration().DotDotDotToken, + node.Name(), + node.AsParameterDeclaration().QuestionToken, + node.Type(), + node.Initializer(), + ) + case KindConstructorType: + return factory.UpdateConstructorTypeNode( + node.AsConstructorTypeNode(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + ) + case KindPropertySignature: + return factory.UpdatePropertySignatureDeclaration( + node.AsPropertySignatureDeclaration(), + modifierArray, + node.Name(), + node.AsPropertySignatureDeclaration().PostfixToken, + node.Type(), + node.Initializer(), + ) + case KindPropertyDeclaration: + return factory.UpdatePropertyDeclaration( + node.AsPropertyDeclaration(), + modifierArray, + node.Name(), + node.AsPropertyDeclaration().PostfixToken, + node.Type(), + node.Initializer(), + ) + case KindMethodSignature: + return factory.UpdateMethodSignatureDeclaration( + node.AsMethodSignatureDeclaration(), + modifierArray, + node.Name(), + node.AsMethodSignatureDeclaration().PostfixToken, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + ) + case KindMethodDeclaration: + return factory.UpdateMethodDeclaration( + node.AsMethodDeclaration(), + modifierArray, + node.AsMethodDeclaration().AsteriskToken, + node.Name(), + node.AsMethodDeclaration().PostfixToken, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindConstructor: + return factory.UpdateConstructorDeclaration( + node.AsConstructorDeclaration(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindGetAccessor: + return factory.UpdateGetAccessorDeclaration( + node.AsGetAccessorDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindSetAccessor: + return factory.UpdateSetAccessorDeclaration( + node.AsSetAccessorDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindIndexSignature: + return factory.UpdateIndexSignatureDeclaration( + node.AsIndexSignatureDeclaration(), + modifierArray, + node.ParameterList(), + node.Type(), + ) + case KindFunctionExpression: + return factory.UpdateFunctionExpression( + node.AsFunctionExpression(), + modifierArray, + node.AsFunctionExpression().AsteriskToken, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindArrowFunction: + return factory.UpdateArrowFunction( + node.AsArrowFunction(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsArrowFunction().EqualsGreaterThanToken, + node.Body(), + ) + case KindClassExpression: + return factory.UpdateClassExpression( + node.AsClassExpression(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.AsClassExpression().HeritageClauses, + node.MemberList(), + ) + case KindVariableStatement: + return factory.UpdateVariableStatement( + node.AsVariableStatement(), + modifierArray, + node.AsVariableStatement().DeclarationList, + ) + case KindFunctionDeclaration: + return factory.UpdateFunctionDeclaration( + node.AsFunctionDeclaration(), + modifierArray, + node.AsFunctionDeclaration().AsteriskToken, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindClassDeclaration: + return factory.UpdateClassDeclaration( + node.AsClassDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.AsClassDeclaration().HeritageClauses, + node.MemberList(), + ) + case KindInterfaceDeclaration: + return factory.UpdateInterfaceDeclaration( + node.AsInterfaceDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.AsInterfaceDeclaration().HeritageClauses, + node.MemberList(), + ) + case KindTypeAliasDeclaration: + return factory.UpdateTypeAliasDeclaration( + node.AsTypeAliasDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.Type(), + ) + case KindEnumDeclaration: + return factory.UpdateEnumDeclaration( + node.AsEnumDeclaration(), + modifierArray, + node.Name(), + node.MemberList(), + ) + case KindModuleDeclaration: + return factory.UpdateModuleDeclaration( + node.AsModuleDeclaration(), + modifierArray, + node.AsModuleDeclaration().Keyword, + node.Name(), + node.Body(), + ) + case KindImportEqualsDeclaration: + return factory.UpdateImportEqualsDeclaration( + node.AsImportEqualsDeclaration(), + modifierArray, + node.IsTypeOnly(), + node.Name(), + node.AsImportEqualsDeclaration().ModuleReference, + ) + case KindImportDeclaration: + return factory.UpdateImportDeclaration( + node.AsImportDeclaration(), + modifierArray, + node.AsImportDeclaration().ImportClause, + node.AsImportDeclaration().ModuleSpecifier, + node.AsImportDeclaration().Attributes, + ) + case KindExportAssignment: + return factory.UpdateExportAssignment( + node.AsExportAssignment(), + modifierArray, + node.Expression(), + ) + case KindExportDeclaration: + return factory.UpdateExportDeclaration( + node.AsExportDeclaration(), + modifierArray, + node.IsTypeOnly(), + node.AsExportDeclaration().ExportClause, + node.AsExportDeclaration().ModuleSpecifier, + node.AsExportDeclaration().Attributes, + ) + } + panic(fmt.Sprintf("Node that does not have modifiers tried to have modifier replaced: %d", node.Kind)) +} + +func IsLateVisibilityPaintedStatement(node *Node) bool { + switch node.Kind { + case KindImportDeclaration, + KindImportEqualsDeclaration, + KindVariableStatement, + KindClassDeclaration, + KindFunctionDeclaration, + KindModuleDeclaration, + KindTypeAliasDeclaration, + KindInterfaceDeclaration, + KindEnumDeclaration: + return true + default: + return false + } +} diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 5e4879fe27..c7a3e83d1f 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -619,6 +619,7 @@ type Checker struct { typeNodeLinks core.LinkStore[*ast.Node, TypeNodeLinks] enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] assertionLinks core.LinkStore[*ast.Node, AssertionLinks] + declarationLinks core.LinkStore[*ast.Node, DeclarationLinks] // TODO: Move into EmitResolver if `collectLinkedAliases` also can move arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks] switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] @@ -705,6 +706,7 @@ type Checker struct { resolvingSignature *Signature silentNeverSignature *Signature enumNumberIndexInfo *IndexInfo + anyBaseTypeIndexInfo *IndexInfo patternAmbientModules []*ast.PatternAmbientModule patternAmbientModuleAugmentations ast.SymbolTable globalObjectType *Type @@ -967,6 +969,7 @@ func NewChecker(program Program) *Checker { c.resolvingSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0) c.silentNeverSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.silentNeverType, nil, 0) c.enumNumberIndexInfo = &IndexInfo{keyType: c.numberType, valueType: c.stringType, isReadonly: true} + c.anyBaseTypeIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType, isReadonly: false} c.emptyStringType = c.getStringLiteralType("") c.zeroType = c.getNumberLiteralType(0) c.zeroBigIntType = c.getBigIntLiteralType(jsnum.PseudoBigInt{}) @@ -11297,7 +11300,7 @@ func (c *Checker) getEnclosingClassFromThisParameter(node *ast.Node) *Type { func getThisParameterFromNodeContext(node *ast.Node) *ast.Node { thisContainer := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) if thisContainer != nil && ast.IsFunctionLike(thisContainer) { - return getThisParameter(thisContainer) + return ast.GetThisParameter(thisContainer) } return nil } @@ -11411,7 +11414,7 @@ func (c *Checker) tryGetThisTypeAt(node *ast.Node) *Type { } func (c *Checker) tryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type { - if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container) != nil) { + if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) { thisType := c.getThisTypeOfDeclaration(container) // Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated. // If this is a function in a JS file, it might be a class method. @@ -12970,7 +12973,7 @@ func (c *Checker) getNarrowedTypeOfSymbol(symbol *ast.Symbol, location *ast.Node restType := c.getReducedApparentType(c.instantiateType(c.getTypeOfSymbol(contextualSignature.parameters[0]), mapper)) if restType.flags&TypeFlagsUnion != 0 && everyType(restType, isTupleType) && !core.Some(fn.Parameters(), c.isSomeSymbolAssigned) { narrowedType := c.getFlowTypeOfReferenceEx(fn, restType, restType, nil /*flowContainer*/, getFlowNodeOfNode(location)) - index := slices.Index(fn.Parameters(), declaration) - (core.IfElse(getThisParameter(fn) != nil, 1, 0)) + index := slices.Index(fn.Parameters(), declaration) - (core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0)) return c.getIndexedAccessType(narrowedType, c.getNumberLiteralType(jsnum.Number(index))) } } @@ -17539,7 +17542,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters if instantiatedBaseType != c.anyType { inheritedIndexInfos = c.getIndexInfosOfType(instantiatedBaseType) } else { - inheritedIndexInfos = []*IndexInfo{{keyType: c.stringType, valueType: c.anyType}} + inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} } indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { return findIndexInfo(indexInfos, info.keyType) == nil @@ -18298,7 +18301,7 @@ func (c *Checker) getAnnotatedAccessorThisParameter(accessor *ast.Node) *ast.Sym func (c *Checker) getAccessorThisParameter(accessor *ast.Node) *ast.Node { if len(accessor.Parameters()) == core.IfElse(ast.IsGetAccessorDeclaration(accessor), 1, 2) { - return getThisParameter(accessor) + return ast.GetThisParameter(accessor) } return nil } @@ -19031,7 +19034,7 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { c.addInheritedMembers(members, c.getPropertiesOfType(baseConstructorType)) c.setStructuredTypeMembers(t, members, nil, nil, nil) } else if baseConstructorType == c.anyType { - baseConstructorIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType} + baseConstructorIndexInfo = c.anyBaseTypeIndexInfo } } indexSymbol := members[ast.InternalSymbolNameIndex] @@ -27233,7 +27236,7 @@ func (c *Checker) getContextuallyTypedParameterType(parameter *ast.Node) *Type { } contextualSignature := c.getContextualSignature(fn) if contextualSignature != nil { - index := slices.Index(fn.Parameters(), parameter) - core.IfElse(getThisParameter(fn) != nil, 1, 0) + index := slices.Index(fn.Parameters(), parameter) - core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0) if hasDotDotDotToken(parameter) && core.LastOrNil(fn.Parameters()) == parameter { return c.getRestTypeAtPosition(contextualSignature, index, false) } @@ -27868,10 +27871,10 @@ func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signatur if !ast.IsConstructorDeclaration(node.Parent) && !(ast.IsMethodDeclaration(node.Parent) || ast.IsSetAccessorDeclaration(node.Parent) && ast.IsClassLike(node.Parent.Parent)) { break } - if getThisParameter(node.Parent) == node { + if ast.GetThisParameter(node.Parent) == node { break } - index := slices.Index(node.Parent.Parameters(), node) - core.IfElse(getThisParameter(node.Parent) != nil, 1, 0) + index := slices.Index(node.Parent.Parameters(), node) - core.IfElse(ast.GetThisParameter(node.Parent) != nil, 1, 0) // Debug.assert(index >= 0) // A parameter declaration decorator will have three arguments (see `ParameterDecorator` in // core.d.ts). diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index ed08ea346f..c3ec5db7bc 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -1,12 +1,15 @@ package checker import ( + "maps" + "slices" "sync" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/binder" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/evaluator" + "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" ) @@ -20,51 +23,382 @@ type emitResolver struct { referenceResolver binder.ReferenceResolver } +func (r *emitResolver) IsOptionalParameter(node *ast.Node) bool { + return r.isOptionalParameter(node) +} + +func (r *emitResolver) IsLateBound(node *ast.Node) bool { + // TODO: Require an emitContext to construct an EmitResolver, remove all emitContext arguments + // node = r.emitContext.ParseNode(node) + if node == nil { + return false + } + if !ast.IsParseTreeNode(node) { + return false + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + symbol := r.checker.getSymbolOfDeclaration(node) + if symbol == nil { + return false + } + return symbol.CheckFlags&ast.CheckFlagsLate != 0 +} + func (r *emitResolver) GetEnumMemberValue(node *ast.Node) evaluator.Result { - panic("unimplemented") // !!! + // node = r.emitContext.ParseNode(node) + if !ast.IsParseTreeNode(node) { + return evaluator.NewResult(nil, false, false, false) + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + + r.checker.computeEnumMemberValues(node.Parent) + if !r.checker.enumMemberLinks.Has(node) { + return evaluator.NewResult(nil, false, false, false) + } + return r.checker.enumMemberLinks.Get(node).value } func (r *emitResolver) IsDeclarationVisible(node *ast.Node) bool { - panic("unimplemented") // !!! + // node = r.emitContext.ParseNode(node) + if !ast.IsParseTreeNode(node) { + return false + } + if node == nil { + return false + } + + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + + links := r.checker.declarationLinks.Get(node) + if links.isVisible == core.TSUnknown { + if r.determineIfDeclarationIsVisible(node) { + links.isVisible = core.TSTrue + } else { + links.isVisible = core.TSFalse + } + } + return links.isVisible == core.TSTrue } -func (r *emitResolver) IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) printer.SymbolAccessibilityResult { - panic("unimplemented") // !!! +func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool { + switch node.Kind { + case ast.KindJSDocCallbackTag, + // ast.KindJSDocEnumTag, // !!! TODO: JSDoc @enum support? + ast.KindJSDocTypedefTag: + // Top-level jsdoc type aliases are considered exported + // First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file + return node.Parent != nil && node.Parent.Parent != nil && node.Parent.Parent.Parent != nil && ast.IsSourceFile(node.Parent.Parent.Parent) + case ast.KindBindingElement: + return r.IsDeclarationVisible(node.Parent.Parent) + case ast.KindVariableDeclaration, + ast.KindModuleDeclaration, + ast.KindClassDeclaration, + ast.KindInterfaceDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindFunctionDeclaration, + ast.KindEnumDeclaration, + ast.KindImportEqualsDeclaration: + if ast.IsVariableDeclaration(node) { + if ast.IsBindingPattern(node.Name()) && + len(node.Name().AsBindingPattern().Elements.Nodes) > 0 { + // If the binding pattern is empty, this variable declaration is not visible + return false + } + // falls through + } + // external module augmentation is always visible + if isExternalModuleAugmentation(node) { + return true + } + parent := ast.GetDeclarationContainer(node) + // If the node is not exported or it is not ambient module element (except import declaration) + if r.checker.getCombinedModifierFlagsCached(node)&ast.ModifierFlagsExport == 0 && + !(node.Kind != ast.KindImportEqualsDeclaration && parent.Kind != ast.KindSourceFile && parent.Flags&ast.NodeFlagsAmbient != 0) { + return ast.IsGlobalSourceFile(parent) + } + // Exported members/ambient module elements (exception import declaration) are visible if parent is visible + return r.IsDeclarationVisible(parent) + + case ast.KindPropertyDeclaration, + ast.KindPropertySignature, + ast.KindGetAccessor, + ast.KindSetAccessor, + ast.KindMethodDeclaration, + ast.KindMethodSignature: + if r.checker.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { + // Private/protected properties/methods are not visible + return false + } + // Public properties/methods are visible if its parents are visible, so: + return r.IsDeclarationVisible(node.Parent) + + case ast.KindConstructor, + ast.KindConstructSignature, + ast.KindCallSignature, + ast.KindIndexSignature, + ast.KindParameter, + ast.KindModuleBlock, + ast.KindFunctionType, + ast.KindConstructorType, + ast.KindTypeLiteral, + ast.KindTypeReference, + ast.KindArrayType, + ast.KindTupleType, + ast.KindUnionType, + ast.KindIntersectionType, + ast.KindParenthesizedType, + ast.KindNamedTupleMember: + return r.IsDeclarationVisible(node.Parent) + + // Default binding, import specifier and namespace import is visible + // only on demand so by default it is not visible + case ast.KindImportClause, + ast.KindNamespaceImport, + ast.KindImportSpecifier: + return false + + // Type parameters are always visible + case ast.KindTypeParameter: + return true + // Source file and namespace export are always visible + case ast.KindSourceFile, + ast.KindNamespaceExportDeclaration: + return true + + // Export assignments do not create name bindings outside the module + case ast.KindExportAssignment: + return false + + default: + return false + } } -func (r *emitResolver) IsImplementationOfOverload(node *ast.SignatureDeclaration) bool { - panic("unimplemented") // !!! +func getMeaningOfEntityNameReference(entityName *ast.Node) ast.SymbolFlags { + // get symbol of the first identifier of the entityName + if entityName.Parent.Kind == ast.KindTypeQuery || + entityName.Parent.Kind == ast.KindExpressionWithTypeArguments && !ast.IsPartOfTypeNode(entityName.Parent) || + entityName.Parent.Kind == ast.KindComputedPropertyName || + entityName.Parent.Kind == ast.KindTypePredicate && entityName.Parent.AsTypePredicateNode().ParameterName == entityName { + // Typeof value + return ast.SymbolFlagsValue | ast.SymbolFlagsExportValue + } + if entityName.Kind == ast.KindQualifiedName || entityName.Kind == ast.KindPropertyAccessExpression || + entityName.Parent.Kind == ast.KindImportEqualsDeclaration || + (entityName.Parent.Kind == ast.KindQualifiedName && entityName.Parent.AsQualifiedName().Left == entityName) || + (entityName.Parent.Kind == ast.KindPropertyAccessExpression && (entityName.Parent.AsPropertyAccessExpression()).Expression == entityName) || + (entityName.Parent.Kind == ast.KindElementAccessExpression && (entityName.Parent.AsElementAccessExpression()).Expression == entityName) { + // Left identifier from type reference or TypeAlias + // Entity name of the import declaration + return ast.SymbolFlagsNamespace + } + // Type Reference or TypeAlias entity = Identifier + return ast.SymbolFlagsType } -func (r *emitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool { - panic("unimplemented") // !!! +func (r *emitResolver) IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) printer.SymbolAccessibilityResult { + return r.isEntityNameVisible(entityName, enclosingDeclaration, true) } -// TODO: the emit resolver being respoinsible for some amount of node construction is a very leaky abstraction, -// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API -// and likely reduces performance. There's probably some refactoring that could be done here to simplify this. +func (r *emitResolver) isEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node, shouldComputeAliasToMakeVisible bool) printer.SymbolAccessibilityResult { + // node = r.emitContext.ParseNode(entityName) + if !ast.IsParseTreeNode(entityName) { + return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityNotAccessible} + } -func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - original := emitContext.ParseNode(signatureDeclaration) - if original == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + meaning := getMeaningOfEntityNameReference(entityName) + firstIdentifier := ast.GetFirstIdentifier(entityName) + + r.checkerMu.Lock() + symbol := r.checker.resolveName(enclosingDeclaration, firstIdentifier.Text(), meaning, nil, false, false) + r.checkerMu.Unlock() + + if symbol != nil && symbol.Flags&ast.SymbolFlagsTypeParameter != 0 && meaning&ast.SymbolFlagsType != 0 { + return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} + } + + if symbol == nil && ast.IsThisIdentifier(firstIdentifier) { + r.checkerMu.Lock() + sym := r.checker.getSymbolOfDeclaration(r.checker.getThisContainer(firstIdentifier, false, false)) + r.checkerMu.Unlock() + if r.IsSymbolAccessible(sym, enclosingDeclaration, meaning, false).Accessibility == printer.SymbolAccessibilityAccessible { + return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} + } + } + + if symbol == nil { + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotResolved, + ErrorSymbolName: firstIdentifier.Text(), + ErrorNode: firstIdentifier, + } + } + + visible := r.hasVisibleDeclarations(symbol, shouldComputeAliasToMakeVisible) + if visible != nil { + return *visible + } + + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotAccessible, + ErrorSymbolName: firstIdentifier.Text(), + ErrorNode: firstIdentifier, } - requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.serializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) } -func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - original := emitContext.ParseNode(declaration) - if original == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) +func noopAddVisibleAlias(declaration *ast.Node, aliasingStatement *ast.Node) {} + +func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeAliasToMakeVisible bool) *printer.SymbolAccessibilityResult { + var aliasesToMakeVisibleSet map[ast.NodeId]*ast.Node + + var addVisibleAlias func(declaration *ast.Node, aliasingStatement *ast.Node) + if shouldComputeAliasToMakeVisible { + addVisibleAlias = func(declaration *ast.Node, aliasingStatement *ast.Node) { + // Only lock as we edit links, so the IsDeclarationVisible calls don't trip over the lock + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + r.checker.declarationLinks.Get(declaration).isVisible = core.TSTrue + aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = declaration + } + } else { + addVisibleAlias = noopAddVisibleAlias + } + + for _, declaration := range symbol.Declarations { + if ast.IsIdentifier(declaration) { + continue + } + + if !r.IsDeclarationVisible(declaration) { + // Mark the unexported alias as visible if its parent is visible + // because these kind of aliases can be used to name types in declaration file + anyImportSyntax := getAnyImportSyntax(declaration) + if anyImportSyntax != nil && + !ast.HasSyntacticModifier(anyImportSyntax, ast.ModifierFlagsExport) && // import clause without export + r.IsDeclarationVisible(anyImportSyntax.Parent) { + addVisibleAlias(declaration, anyImportSyntax) + continue + } + if ast.IsVariableDeclaration(declaration) && ast.IsVariableStatement(declaration.Parent.Parent) && + !ast.HasSyntacticModifier(declaration.Parent.Parent, ast.ModifierFlagsExport) && // unexported variable statement + r.IsDeclarationVisible(declaration.Parent.Parent.Parent) { + addVisibleAlias(declaration, declaration.Parent.Parent) + continue + } + if ast.IsLateVisibilityPaintedStatement(declaration) && // unexported top-level statement + !ast.HasSyntacticModifier(declaration, ast.ModifierFlagsExport) && + r.IsDeclarationVisible(declaration.Parent) { + addVisibleAlias(declaration, declaration) + continue + } + if ast.IsBindingElement(declaration) { + if symbol.Flags&ast.SymbolFlagsAlias != 0 && ast.IsInJSFile(declaration) && declaration.Parent != nil && declaration.Parent.Parent != nil && // exported import-like top-level JS require statement + ast.IsVariableDeclaration(declaration.Parent.Parent) && + declaration.Parent.Parent.Parent.Parent != nil && ast.IsVariableStatement(declaration.Parent.Parent.Parent.Parent) && + !ast.HasSyntacticModifier(declaration.Parent.Parent.Parent.Parent, ast.ModifierFlagsExport) && + declaration.Parent.Parent.Parent.Parent.Parent != nil && // check if the thing containing the variable statement is visible (ie, the file) + r.IsDeclarationVisible(declaration.Parent.Parent.Parent.Parent.Parent) { + addVisibleAlias(declaration, declaration.Parent.Parent.Parent.Parent) + continue + } + if symbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { + variableStatement := ast.FindAncestor(declaration, ast.IsVariableStatement) + if ast.HasSyntacticModifier(variableStatement, ast.ModifierFlagsExport) { + continue // no alias to add, already exported + } + if !r.IsDeclarationVisible(variableStatement.Parent) { + return nil // not visible + } + addVisibleAlias(declaration, variableStatement) + continue + } + } + + // Declaration is not visible + return nil + } + } + + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + AliasesToMakeVisible: slices.Collect(maps.Values(aliasesToMakeVisibleSet)), } - requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context - // // Get type of the symbol if this is the valid symbol otherwise get type at location - symbol := r.checker.getSymbolOfDeclaration(declaration) - return requestNodeBuilder.serializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) +} + +func (r *emitResolver) IsImplementationOfOverload(node *ast.SignatureDeclaration) bool { + // node = r.emitContext.ParseNode(node) + if !ast.IsParseTreeNode(node) { + return false + } + if ast.NodeIsPresent(node.Body()) { + if ast.IsGetAccessorDeclaration(node) || ast.IsSetAccessorDeclaration(node) { + return false // Get or set accessors can never be overload implementations, but can have up to 2 signatures + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + symbol := r.checker.getSymbolOfDeclaration(node) + signaturesOfSymbol := r.checker.getSignaturesOfSymbol(symbol) + // If this function body corresponds to function with multiple signature, it is implementation of overload + // e.g.: function foo(a: string): string; + // function foo(a: number): number; + // function foo(a: any) { // This is implementation of the overloads + // return a; + // } + return len(signaturesOfSymbol) > 1 || + // If there is single signature for the symbol, it is overload if that signature isn't coming from the node + // e.g.: function foo(a: string): string; + // function foo(a: any) { // This is implementation of the overloads + // return a; + // } + (len(signaturesOfSymbol) == 1 && signaturesOfSymbol[0].declaration != node) + } + return false +} + +func (r *emitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool { + // node = r.emitContext.ParseNode(node) + if !ast.IsParseTreeNode(decl.AsNode()) { + return false + } + file := ast.GetSourceFileOfNode(decl.AsNode()) + if file.Symbol == nil { + // script file + return false + } + importTarget := r.GetExternalModuleFileFromDeclaration(decl.AsNode()) + if importTarget == nil { + return false + } + if importTarget == file { + return false + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + exports := r.checker.getExportsOfModule(file.Symbol) + for s := range maps.Values(exports) { + merged := r.checker.getMergedSymbol(s) + if merged != s { + if len(merged.Declarations) > 0 { + for _, d := range merged.Declarations { + declFile := ast.GetSourceFileOfNode(d) + if declFile == importTarget { + return true + } + } + } + } + } + return false } func (r *emitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + // node = r.emitContext.ParseNode(node) + if !ast.IsParseTreeNode(declaration) { + return false + } switch declaration.Kind { case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindJSDocPropertyTag: r.checkerMu.Lock() @@ -150,6 +484,10 @@ func (r *emitResolver) isOptionalParameter(node *ast.Node) bool { } func (r *emitResolver) IsLiteralConstDeclaration(node *ast.Node) bool { + // node = r.emitContext.ParseNode(node) + if !ast.IsParseTreeNode(node) { + return false + } if isDeclarationReadonly(node) || ast.IsVariableDeclaration(node) && ast.IsVarConst(node) { r.checkerMu.Lock() defer r.checkerMu.Unlock() @@ -159,6 +497,7 @@ func (r *emitResolver) IsLiteralConstDeclaration(node *ast.Node) bool { } func (r *emitResolver) IsExpandoFunctionDeclaration(node *ast.Node) bool { + // node = r.emitContext.ParseNode(node) // !!! TODO: expando function support return false } @@ -304,15 +643,30 @@ func (r *emitResolver) MarkLinkedReferencesRecursively(file *ast.SourceFile) { } } -func (r *emitResolver) GetExternalModuleFileFromDeclaration(node *ast.Node) *ast.SourceFile { +func (r *emitResolver) GetExternalModuleFileFromDeclaration(declaration *ast.Node) *ast.SourceFile { + if !ast.IsParseTreeNode(declaration) { + return nil + } + + var specifier *ast.Node + if declaration.Kind == ast.KindModuleDeclaration { + if ast.IsStringLiteral(declaration.Name()) { + specifier = declaration.Name() + } + } else { + specifier = ast.GetExternalModuleName(declaration) + } r.checkerMu.Lock() defer r.checkerMu.Unlock() - - if ast.IsParseTreeNode(node) { - // !!! - // return r.checker.getExternalModuleFileFromDeclaration(node) + moduleSymbol := r.checker.resolveExternalModuleNameWorker(specifier, specifier /*moduleNotFoundError*/, nil, false, false) // TODO: GH#18217 + if moduleSymbol == nil { + return nil + } + decl := ast.GetDeclarationOfKind(moduleSymbol, ast.KindSourceFile) + if decl == nil { + return nil } - return nil + return decl.AsSourceFile() } func (r *emitResolver) getReferenceResolver() binder.ReferenceResolver { @@ -373,3 +727,152 @@ func (r *emitResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) return r.getReferenceResolver().GetReferencedValueDeclarations(node) } + +// TODO: the emit resolver being respoinsible for some amount of node construction is a very leaky abstraction, +// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API +// and likely reduces performance. There's probably some refactoring that could be done here to simplify this. + +func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + original := emitContext.ParseNode(signatureDeclaration) + if original == nil { + return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + } + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + return requestNodeBuilder.serializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) +} + +func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + original := emitContext.ParseNode(declaration) + if original == nil { + return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + } + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + // // Get type of the symbol if this is the valid symbol otherwise get type at location + symbol := r.checker.getSymbolOfDeclaration(declaration) + return requestNodeBuilder.serializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) +} + +func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node { + node = emitContext.ParseNode(node) + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + type_ := r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node)) + if type_ == nil { + return nil // TODO: How!? Maybe this should be a panic. All symbols should have a type. + } + + var enumResult *ast.Node + if type_.flags&TypeFlagsEnum != 0 { + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + enumResult = requestNodeBuilder.symbolToExpression(type_.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker) + // What about regularTrueType/regularFalseType - since those aren't fresh, we never make initializers from them + // TODO: handle those if this function is ever used for more than initializers in declaration emit + } else if type_ == r.checker.trueType { + enumResult = emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) + } else if type_ == r.checker.falseType { + enumResult = emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) + } + if enumResult != nil { + return enumResult + } + if type_.flags&TypeFlagsLiteral == 0 { + return nil // non-literal type + } + literalValue := type_.AsLiteralType().value + switch literalValue.(type) { + case string: + return emitContext.Factory.NewStringLiteral(literalValue.(string)) + case jsnum.Number: + value := literalValue.(jsnum.Number) + if value.Abs() != value { + // negative + return emitContext.Factory.NewPrefixUnaryExpression( + ast.KindMinusToken, + emitContext.Factory.NewNumericLiteral(value.String()[1:]), + ) + } + return emitContext.Factory.NewNumericLiteral(value.String()) + case jsnum.PseudoBigInt: + value := literalValue.(jsnum.PseudoBigInt) + if value.Negative { + // negative + return emitContext.Factory.NewPrefixUnaryExpression( + ast.KindMinusToken, + emitContext.Factory.NewBigIntLiteral(value.Base10Value), + ) + } + return emitContext.Factory.NewNumericLiteral(value.Base10Value) + case bool: + if literalValue.(bool) { + return emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) + } + return emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) + } + return nil /// !!! TODO: panic? impossible? +} + +func (r *emitResolver) CreateTypeOfExpression(emitContext *printer.EmitContext, expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + expression = emitContext.ParseNode(expression) + if expression == nil { + return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + } + + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + return requestNodeBuilder.serializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) +} + +func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { + container = emitContext.ParseNode(container) + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + + sym := container.Symbol() + staticInfos := r.checker.getIndexInfosOfType(r.checker.getTypeOfSymbol(sym)) + instanceIndexSymbol := r.checker.getIndexSymbol(sym) + var instanceInfos []*IndexInfo + if instanceIndexSymbol != nil { + siblingSymbols := slices.Collect(maps.Values(r.checker.getMembersOfSymbol(sym))) + instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols) + } + + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + + var result []*ast.Node + for i, infoList := range [][]*IndexInfo{staticInfos, instanceInfos} { + isStatic := true + if i > 0 { + isStatic = false + } + if len(infoList) == 0 { + continue + } + for _, info := range infoList { + if info.declaration != nil { + continue + } + if info == r.checker.anyBaseTypeIndexInfo { + continue // inherited, but looks like a late-bound signature because it has no declarations + } + // if info.components { + // !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures + // } + node := requestNodeBuilder.indexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker) + if node != nil && isStatic { + mods := node.Modifiers() + mods = emitContext.Factory.NewModifierList(append([]*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, mods.Nodes...)) + node = emitContext.Factory.UpdateIndexSignatureDeclaration( + node.AsIndexSignatureDeclaration(), + mods, + node.ParameterList(), + node.Type(), + ) + } + if node != nil { + result = append(result, node) + } + } + } + return result +} diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index bc22282381..6d93df9659 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -339,7 +339,7 @@ func (b *NodeBuilder) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) panic("unimplemented") // !!! } -func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, false bool) *ast.Node { +func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, expectsIdentifier bool) *ast.Node { panic("unimplemented") // !!! } @@ -589,8 +589,7 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) if len(names) > 0 { duplicates := []int{} uniqueNames := make(map[string]bool) - for i := 0; i < len(names); i++ { - name := names[i] + for i, name := range names { _, ok := uniqueNames[name] if ok { duplicates = append(duplicates, i) diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go index 8085bc2ab2..32b30009ee 100644 --- a/internal/checker/nodebuilderscopes.go +++ b/internal/checker/nodebuilderscopes.go @@ -152,8 +152,7 @@ func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*as if expandedParams == nil { return } - for pIndex := 0; pIndex < len(*expandedParams); pIndex++ { - param := (*expandedParams)[pIndex] + for pIndex, param := range *expandedParams { originalParam := (*originalParameters)[pIndex] if originalParameters != nil && originalParam != param { // Can't reference parameters that come from an expansion diff --git a/internal/checker/types.go b/internal/checker/types.go index 061e6ec734..4a1424048b 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -372,6 +372,12 @@ type AssertionLinks struct { exprType *Type // Assertion expression type } +// Links for declarations + +type DeclarationLinks struct { + isVisible core.Tristate // if declaration is depended upon by exported declarations +} + // SourceFile links type SourceFileLinks struct { diff --git a/internal/checker/utilities.go b/internal/checker/utilities.go index 46c183c904..f52f197e89 100644 --- a/internal/checker/utilities.go +++ b/internal/checker/utilities.go @@ -349,7 +349,7 @@ func nodeCanBeDecorated(useLegacyDecorators bool, node *ast.Node, parent *ast.No return false } // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target. - return parent != nil && parent.BodyData() != nil && (parent.BodyData()).Body != nil && (parent.Kind == ast.KindConstructor || parent.Kind == ast.KindMethodDeclaration || parent.Kind == ast.KindSetAccessor) && getThisParameter(parent) != node && grandparent != nil && grandparent.Kind == ast.KindClassDeclaration + return parent != nil && parent.BodyData() != nil && (parent.BodyData()).Body != nil && (parent.Kind == ast.KindConstructor || parent.Kind == ast.KindMethodDeclaration || parent.Kind == ast.KindSetAccessor) && ast.GetThisParameter(parent) != node && grandparent != nil && grandparent.Kind == ast.KindClassDeclaration } return false @@ -1251,17 +1251,6 @@ func isLateBoundName(name string) bool { return len(name) >= 2 && name[0] == '\xfe' && name[1] == '@' } -func getThisParameter(signature *ast.Node) *ast.Node { - // callback tags do not currently support this parameters - if len(signature.Parameters()) != 0 { - thisParameter := signature.Parameters()[0] - if ast.IsThisParameter(thisParameter) { - return thisParameter - } - } - return nil -} - func isObjectOrArrayLiteralType(t *Type) bool { return t.objectFlags&(ObjectFlagsObjectLiteral|ObjectFlagsArrayLiteral) != 0 } @@ -2162,3 +2151,18 @@ func containsNonMissingUndefinedType(c *Checker, t *Type) bool { } return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType } + +func getAnyImportSyntax(node *ast.Node) *ast.Node { + switch node.Kind { + case ast.KindImportEqualsDeclaration: + return node + case ast.KindImportClause: + return node.Parent + case ast.KindNamespaceImport: + return node.Parent.Parent + case ast.KindImportSpecifier: + return node.Parent.Parent.Parent + default: + return nil + } +} diff --git a/internal/declarations/diagnostics.go b/internal/declarations/diagnostics.go index 61028b366b..0e3bad19e9 100644 --- a/internal/declarations/diagnostics.go +++ b/internal/declarations/diagnostics.go @@ -211,7 +211,7 @@ func createGetSymbolAccessibilityDiagnosticForNode(node *ast.Node) GetSymbolAcce } } } else { - panic(fmt.Sprintf("Attempted to set a declaration diagnostic context for unhandled node kind: {}", node.Kind)) + panic(fmt.Sprintf("Attempted to set a declaration diagnostic context for unhandled node kind: %d", node.Kind)) } } @@ -347,7 +347,7 @@ func getReturnTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilit diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0, ) default: - panic(fmt.Sprintf("This is unknown kind for signature: {}", node.Kind)) + panic(fmt.Sprintf("This is unknown kind for signature: %d", node.Kind)) } } @@ -424,7 +424,7 @@ func getParameterDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symb diagnostics.Parameter_0_of_accessor_has_or_is_using_private_name_1, ) default: - panic(fmt.Sprintf("Unknown parent for parameter: {}", node.Parent.Kind)) + panic(fmt.Sprintf("Unknown parent for parameter: %d", node.Parent.Kind)) } } @@ -459,7 +459,7 @@ func getTypeParameterConstraintVisibilityDiagnosticMessage(node *ast.Node, symbo return diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1 default: - panic(fmt.Sprintf("This is unknown parent for type parameter: {}", node.Parent.Kind)) + panic(fmt.Sprintf("This is unknown parent for type parameter: %d", node.Parent.Kind)) } } diff --git a/internal/declarations/transform.go b/internal/declarations/transform.go index d282ed77c9..cae3d5a7d2 100644 --- a/internal/declarations/transform.go +++ b/internal/declarations/transform.go @@ -9,6 +9,7 @@ import ( "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" "github.com/microsoft/typescript-go/internal/transformers" "github.com/microsoft/typescript-go/internal/tspath" ) @@ -51,7 +52,6 @@ type DeclarationTransformer struct { needsScopeFixMarker bool resultHasScopeMarker bool enclosingDeclaration *ast.Node - getSymbolAccessibilityDiagnostic GetSymbolAccessibilityDiagnostic resultHasExternalModuleIndicator bool suppressNewDiagnosticContexts bool lateStatementReplacementMap map[ast.NodeId]*ast.Node @@ -89,8 +89,45 @@ func (tx *DeclarationTransformer) visit(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindSourceFile: return tx.visitSourceFile(node.AsSourceFile()) - default: + // statements we keep but do something to + case ast.KindFunctionDeclaration, + ast.KindModuleDeclaration, + ast.KindImportEqualsDeclaration, + ast.KindInterfaceDeclaration, + ast.KindClassDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindEnumDeclaration, + ast.KindVariableStatement, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindExportAssignment: return tx.visitDeclarationStatements(node) + // statements we elide + case ast.KindBreakStatement, + ast.KindContinueStatement, + ast.KindDebuggerStatement, + ast.KindDoStatement, + ast.KindExpressionStatement, + ast.KindEmptyStatement, + ast.KindForInStatement, + ast.KindForOfStatement, + ast.KindForStatement, + ast.KindIfStatement, + ast.KindLabeledStatement, + ast.KindReturnStatement, + ast.KindSwitchStatement, + ast.KindThrowStatement, + ast.KindTryStatement, + ast.KindWhileStatement, + ast.KindWithStatement, + ast.KindNotEmittedStatement, + ast.KindBlock, + ast.KindMissingDeclaration, + ast.KindJSTypeAliasDeclaration: // !!! TODO: Jsdoc support + return nil + // parts of things, things we just visit children of + default: + return tx.visitDeclarationSubtree(node) } } @@ -108,7 +145,7 @@ func (tx *DeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Nod tx.needsScopeFixMarker = false tx.resultHasScopeMarker = false tx.enclosingDeclaration = node.AsNode() - tx.getSymbolAccessibilityDiagnostic = throwDiagnostic + tx.state.getSymbolAccessibilityDiagnostic = throwDiagnostic tx.resultHasExternalModuleIndicator = false tx.suppressNewDiagnosticContexts = false tx.state.lateMarkedStatements = make([]*ast.Node, 0) @@ -198,7 +235,7 @@ func (tx *DeclarationTransformer) transformAndReplaceLatePaintedStatements(state // (and remove them from the set to examine for outter declarations) results := make([]*ast.Node, 0, len(statements.Nodes)) for _, statement := range statements.Nodes { - if !isLateVisibilityPaintedStatement(statement) { + if !ast.IsLateVisibilityPaintedStatement(statement) { results = append(results, statement) continue } @@ -323,11 +360,459 @@ func (tx *DeclarationTransformer) getTypeReferences() (result []*ast.FileReferen return result } -func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *ast.Node { - if !isPreservedDeclarationStatement(input) { - // return nil for unmatched kinds to omit them from the tree +func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast.Node { + // !!! TODO: stripInternal support? + // if (shouldStripInternal(input)) return nil + if ast.IsDeclaration(input) { + if isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) { + return nil + } + if ast.HasDynamicName(input) { + if tx.state.isolatedDeclarations { + // !!! isolatedDeclarations support + return nil + } else if tx.resolver.IsLateBound(tx.EmitContext().ParseNode(input)) || !ast.IsEntityNameExpression(input.Name().AsComputedPropertyName().Expression) { + return nil + } + } + } + + // Elide implementation signatures from overload sets + if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) { + return nil + } + + if input.Kind == ast.KindSemicolonClassElement { + return nil + } + + previousEnclosingDeclaration := tx.enclosingDeclaration + if isEnclosingDeclaration(input) { + tx.enclosingDeclaration = input + } + + canProdiceDiagnostic := canProduceDiagnostics(input) + oldWithinObjectLiteralType := tx.suppressNewDiagnosticContexts + shouldEnterSuppressNewDiagnosticsContextContext := (input.Kind == ast.KindTypeLiteral || input.Kind == ast.KindMappedType) && input.Parent.Kind != ast.KindTypeAliasDeclaration + + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if canProdiceDiagnostic && !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) + } + oldName := tx.state.errorNameNode + + if shouldEnterSuppressNewDiagnosticsContextContext { + tx.suppressNewDiagnosticContexts = true + } + + var result *ast.Node + + switch input.Kind { + case ast.KindHeritageClause: + result = tx.transformHeritageClause(input.AsHeritageClause()) + case ast.KindMethodSignature: + result = tx.transformMethodSignatureDeclaration(input.AsMethodSignatureDeclaration()) + case ast.KindMethodDeclaration: + result = tx.transformMethodDeclaration(input.AsMethodDeclaration()) + case ast.KindConstructSignature: + result = tx.transformConstructSignatureDeclaration(input.AsConstructSignatureDeclaration()) + case ast.KindConstructor: + result = tx.transformConstructorDeclaration(input.AsConstructorDeclaration()) + case ast.KindGetAccessor: + result = tx.transformGetAccesorDeclaration(input.AsGetAccessorDeclaration()) + case ast.KindSetAccessor: + result = tx.transformSetAccessorDeclaration(input.AsSetAccessorDeclaration()) + case ast.KindPropertyDeclaration: + result = tx.transformPropertyDeclaration(input.AsPropertyDeclaration()) + case ast.KindPropertySignature: + result = tx.transformPropertySignatureDeclaration(input.AsPropertySignatureDeclaration()) + case ast.KindCallSignature: + result = tx.transformCallSignatureDeclaration(input.AsCallSignatureDeclaration()) + case ast.KindIndexSignature: + result = tx.transformIndexSignatureDeclaration(input.AsIndexSignatureDeclaration()) + case ast.KindVariableDeclaration: + result = tx.transformVariableDeclaration(input.AsVariableDeclaration()) + case ast.KindTypeParameter: + result = tx.transformTypeParameterDeclaration(input.AsTypeParameter()) + case ast.KindExpressionWithTypeArguments: + result = tx.transformExpressionWithTypeArguments(input.AsExpressionWithTypeArguments()) + case ast.KindTypeReference: + result = tx.transformTypeReference(input.AsTypeReference()) + case ast.KindConditionalType: + result = tx.transformConditionalTypeNode(input.AsConditionalTypeNode()) + case ast.KindFunctionType: + result = tx.transformFunctionTypeNode(input.AsFunctionTypeNode()) + case ast.KindConstructorType: + result = tx.transformConstructorTypeNode(input.AsConstructorTypeNode()) + case ast.KindImportType: + result = tx.transformImportTypeNode(input.AsImportTypeNode()) + case ast.KindTypeQuery: + tx.checkEntityNameVisibility(input.AsTypeQueryNode().ExprName, tx.enclosingDeclaration) + result = tx.Visitor().VisitEachChild(input) + case ast.KindTupleType: + result = tx.Visitor().VisitEachChild(input) + if result != nil { + startLine, _ := scanner.GetLineAndCharacterOfPosition(tx.state.currentSourceFile, input.Loc.Pos()) + endLine, _ := scanner.GetLineAndCharacterOfPosition(tx.state.currentSourceFile, input.Loc.End()) + if startLine == endLine { + tx.EmitContext().AddEmitFlags(result, printer.EFSingleLine) + } + } + default: + result = tx.Visitor().VisitEachChild(input) + } + + tx.enclosingDeclaration = previousEnclosingDeclaration + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.errorNameNode = oldName + tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType + return result +} + +func (tx *DeclarationTransformer) transformHeritageClause(clause *ast.HeritageClause) *ast.Node { + retainedClauses := core.Filter(clause.Types.Nodes, func(t *ast.Node) bool { + return ast.IsEntityNameExpression(t.AsExpressionWithTypeArguments().Expression) || + (clause.Token == ast.KindExtendsKeyword && t.Expression().Kind == ast.KindNullKeyword) + }) + if len(retainedClauses) == len(clause.Types.Nodes) { + return tx.Visitor().VisitEachChild(clause.AsNode()) + } + return tx.Factory().UpdateHeritageClause( + clause, + tx.Visitor().VisitNodes(tx.Factory().NewNodeList(retainedClauses)), + ) +} + +func (tx *DeclarationTransformer) transformImportTypeNode(input *ast.ImportTypeNode) *ast.Node { + if !ast.IsLiteralImportTypeNode(input.AsNode()) { + return input.AsNode() + } + return tx.Factory().UpdateImportTypeNode( + input, + input.IsTypeOf, + tx.Factory().UpdateLiteralTypeNode( + input.Argument.AsLiteralTypeNode(), + tx.rewriteModuleSpecifier(input.AsNode(), input.Argument.AsLiteralTypeNode().Literal), + ), + input.Attributes, + input.Qualifier, + tx.Visitor().VisitNodes(input.TypeArguments), + ) +} + +func (tx *DeclarationTransformer) transformConstructorTypeNode(input *ast.ConstructorTypeNode) *ast.Node { + return tx.Factory().UpdateConstructorTypeNode( + input, + tx.ensureModifiers(input.AsNode()), + tx.Visitor().VisitNodes(input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.Visitor().Visit(input.Type), + ) +} + +func (tx *DeclarationTransformer) transformFunctionTypeNode(input *ast.FunctionTypeNode) *ast.Node { + return tx.Factory().UpdateFunctionTypeNode( + input, + tx.Visitor().VisitNodes(input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.Visitor().Visit(input.Type), + ) +} + +func (tx *DeclarationTransformer) transformConditionalTypeNode(input *ast.ConditionalTypeNode) *ast.Node { + checkType := tx.Visitor().Visit(input.CheckType) + extendsType := tx.Visitor().Visit(input.ExtendsType) + oldEnclosingDecl := tx.enclosingDeclaration + tx.enclosingDeclaration = input.TrueType + trueType := tx.Visitor().Visit(input.TrueType) + tx.enclosingDeclaration = oldEnclosingDecl + falseType := tx.Visitor().Visit(input.FalseType) + + return tx.Factory().UpdateConditionalTypeNode( + input, + checkType, + extendsType, + trueType, + falseType, + ) +} + +func (tx *DeclarationTransformer) transformTypeReference(input *ast.TypeReferenceNode) *ast.Node { + tx.checkEntityNameVisibility(input.TypeName, tx.enclosingDeclaration) + return tx.Visitor().VisitEachChild(input.AsNode()) +} + +func (tx *DeclarationTransformer) transformExpressionWithTypeArguments(input *ast.ExpressionWithTypeArguments) *ast.Node { + if ast.IsEntityName(input.Expression) || ast.IsEntityNameExpression(input.Expression) { + tx.checkEntityNameVisibility(input.Expression, tx.enclosingDeclaration) + } + return tx.Visitor().VisitEachChild(input.AsNode()) +} + +func (tx *DeclarationTransformer) transformTypeParameterDeclaration(input *ast.TypeParameterDeclaration) *ast.Node { + if isPrivateMethodTypeParameter(tx.host, input) && (input.DefaultType != nil || input.Constraint != nil) { + return tx.Factory().UpdateTypeParameterDeclaration( + input, + input.Modifiers(), + input.Name(), + nil, + nil, + ) + } + return tx.Visitor().VisitEachChild(input.AsNode()) +} + +func (tx *DeclarationTransformer) transformVariableDeclaration(input *ast.VariableDeclaration) *ast.Node { + if ast.IsBindingPattern(input.Name()) { + return tx.recreateBindingPattern(input.Name().AsBindingPattern()) + } + // Variable declaration types also suppress new diagnostic contexts, provided the contexts wouldn't be made for binding pattern types + tx.suppressNewDiagnosticContexts = true + return tx.Factory().UpdateVariableDeclaration( + input, + input.Name(), + nil, + tx.ensureType(input.AsNode(), false), + tx.ensureNoInitializer(input.AsNode()), + ) +} + +func (tx *DeclarationTransformer) recreateBindingPattern(input *ast.BindingPattern) *ast.Node { + var results []*ast.Node + for _, elem := range input.Elements.Nodes { + result := tx.recreateBindingElement(elem.AsBindingElement()) + if result == nil { + continue + } + if result.Kind == ast.KindSyntaxList { + results = append(results, result.AsSyntaxList().Children...) + } else { + results = append(results, result) + } + } + if len(results) == 0 { + return nil + } + if len(results) == 1 { + return results[1] + } + return tx.Factory().NewSyntaxList(results) +} + +func (tx *DeclarationTransformer) recreateBindingElement(e *ast.BindingElement) *ast.Node { + if e.Kind == ast.KindBindingElement { + return nil + } + if e.Name() == nil { + return nil + } + if !getBindingNameVisible(tx.resolver, e.AsNode()) { + return nil + } + if ast.IsBindingPattern(e.Name()) { + return tx.recreateBindingPattern(e.Name().AsBindingPattern()) + } + return tx.Factory().NewVariableDeclaration( + e.Name(), + nil, + tx.ensureType(e.AsNode(), false), + nil, // TODO: possible strada bug - not emitting const initialized binding pattern elements? + ) +} + +func (tx *DeclarationTransformer) transformIndexSignatureDeclaration(input *ast.IndexSignatureDeclaration) *ast.Node { + type_ := tx.Visitor().Visit(input.Type) + if type_ == nil { + type_ = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) + } + return tx.Factory().UpdateIndexSignatureDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + tx.updateParamList(input.AsNode(), input.Parameters), + type_, + ) +} + +func (tx *DeclarationTransformer) transformCallSignatureDeclaration(input *ast.CallSignatureDeclaration) *ast.Node { + return tx.Factory().UpdateCallSignatureDeclaration( + input, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + ) +} + +func (tx *DeclarationTransformer) transformPropertySignatureDeclaration(input *ast.PropertySignatureDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdatePropertySignatureDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + input.PostfixToken, + tx.ensureType(input.AsNode(), false), + tx.ensureNoInitializer(input.AsNode()), // TODO: possible strada bug (fixed here) - const property signatures never initialized + ) +} + +func (tx *DeclarationTransformer) transformPropertyDeclaration(input *ast.PropertyDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdatePropertyDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + input.PostfixToken, + tx.ensureType(input.AsNode(), false), + tx.ensureNoInitializer(input.AsNode()), + ) +} + +func (tx *DeclarationTransformer) transformSetAccessorDeclaration(input *ast.SetAccessorDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdateSetAccessorDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + nil, // accessors shouldn't have type params + tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0), + nil, + nil, + ) +} + +func (tx *DeclarationTransformer) transformGetAccesorDeclaration(input *ast.GetAccessorDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdateGetAccessorDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + nil, // accessors shouldn't have type params + tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0), + tx.ensureType(input.AsNode(), false), + nil, + ) +} + +const defaultModifierFlagsMask = ast.ModifierFlagsAll ^ ast.ModifierFlagsPublic + +func (tx *DeclarationTransformer) updateAccessorParamList(input *ast.Node, isPrivate bool) *ast.ParameterList { + var newParams []*ast.Node + if !isPrivate { + thisParam := ast.GetThisParameter(input) + if thisParam != nil { + newParams = append(newParams, tx.ensureParameter(thisParam.AsParameterDeclaration(), defaultModifierFlagsMask)) + } + } + if ast.IsSetAccessorDeclaration(input) { + var valueParam *ast.Node + if !isPrivate { + if len(newParams) == 1 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 2 { + valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[1].AsParameterDeclaration(), defaultModifierFlagsMask) + } else if len(newParams) == 0 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 1 { + valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[0].AsParameterDeclaration(), defaultModifierFlagsMask) + } + } + if valueParam == nil { + // TODO: strada bug - no type printed on set accessor missing arg as though private + var type_ *ast.Node + if !isPrivate { + type_ = tx.Factory().NewKeywordExpression(ast.KindAnyKeyword) + } + valueParam = tx.Factory().NewParameterDeclaration( + nil, + nil, + tx.Factory().NewIdentifier("value"), + nil, + type_, + nil, + ) + } + newParams = append(newParams, valueParam) + } + return tx.Factory().NewNodeList(newParams) +} + +func (tx *DeclarationTransformer) transformConstructorDeclaration(input *ast.ConstructorDeclaration) *ast.Node { + // A constructor declaration may not have a type annotation + return tx.Factory().UpdateConstructorDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + nil, // no type params + tx.updateParamListEx(input.AsNode(), input.Parameters, ast.ModifierFlagsNone), + nil, // no return type + nil, + ) +} + +func (tx *DeclarationTransformer) transformConstructSignatureDeclaration(input *ast.ConstructSignatureDeclaration) *ast.Node { + return tx.Factory().UpdateConstructSignatureDeclaration( + input, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamListEx(input.AsNode(), input.Parameters, ast.ModifierFlagsNone), + nil, // no return type + ) +} + +func (tx *DeclarationTransformer) omitPrivateMethodType(input *ast.Node) *ast.Node { + if input.Symbol() != nil && len(input.Symbol().Declarations) > 0 && input.Symbol().Declarations[0] != input { + return nil + } else { + return tx.Factory().NewPropertyDeclaration( + tx.ensureModifiers(input), + input.Name(), + nil, + nil, + nil, + ) + } +} + +func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast.MethodSignatureDeclaration) *ast.Node { + if tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0 { + return tx.omitPrivateMethodType(input.AsNode()) + } else if ast.IsPrivateIdentifier(input.Name()) { return nil + } else { + return tx.Factory().UpdateMethodSignatureDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + input.PostfixToken, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + ) } +} + +func (tx *DeclarationTransformer) transformMethodDeclaration(input *ast.MethodDeclaration) *ast.Node { + if tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0 { + return tx.omitPrivateMethodType(input.AsNode()) + } else if ast.IsPrivateIdentifier(input.Name()) { + return nil + } else { + return tx.Factory().UpdateMethodDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + nil, + input.Name(), + input.PostfixToken, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + nil, + ) + } +} + +func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *ast.Node { // !!! TODO: stripInternal support? // if (shouldStripInternal(input)) return nil switch input.Kind { @@ -355,7 +840,7 @@ func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *a } // expression is non-identifier, create _default typed variable to reference newId := tx.EmitContext().NewUniqueName("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) - tx.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { return &SymbolAccessibilityDiagnostic{ diagnosticMessage: diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, errorNode: input, @@ -442,15 +927,14 @@ func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) // Should be removed createTypeOfDeclaration will actually now reuse the existing annotation so there is no real need to duplicate type walking // Left in for now to minimize diff during syntactic type node builder refactor if !ast.IsExportAssignment(node) && !ast.IsBindingElement(node) && node.Type() != nil && (!ast.IsParameter(node) || !tx.resolver.RequiresAddingImplicitUndefined(node, nil, tx.enclosingDeclaration)) { - // return visitNode(node.type, visitDeclarationSubtree, isTypeNode); // !!! TODO: visitDeclarationSubtree for ensuring syntactic completeness and vis errors - return node + return tx.Visitor().Visit(node.Type()) } oldErrorNameNode := tx.state.errorNameNode tx.state.errorNameNode = node.Name() var oldDiag GetSymbolAccessibilityDiagnostic if !tx.suppressNewDiagnosticContexts { - oldDiag = tx.getSymbolAccessibilityDiagnostic + oldDiag = tx.state.getSymbolAccessibilityDiagnostic if canProduceDiagnostics(node) { tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node) } @@ -516,6 +1000,7 @@ func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) canProdiceDiagnostic := canProduceDiagnostics(input) oldDiag := tx.state.getSymbolAccessibilityDiagnostic + oldName := tx.state.errorNameNode if canProdiceDiagnostic { tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) } @@ -523,84 +1008,382 @@ func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) var result *ast.Node switch input.Kind { - // !!! + case ast.KindTypeAliasDeclaration: + result = tx.transformTypeAliasDeclaration(input.AsTypeAliasDeclaration()) + case ast.KindInterfaceDeclaration: + result = tx.transformInterfaceDeclaration(input.AsInterfaceDeclaration()) + case ast.KindFunctionDeclaration: + result = tx.transformFunctionDeclaration(input.AsFunctionDeclaration()) + case ast.KindModuleDeclaration: + result = tx.transformModuleDeclaration(input.AsModuleDeclaration()) + case ast.KindClassDeclaration: + result = tx.transformClassDeclaration(input.AsClassDeclaration()) case ast.KindVariableStatement: result = tx.transformVariableStatement(input.AsVariableStatement()) case ast.KindEnumDeclaration: - result = tx.Factory().UpdateEnumDeclaration( - input.AsEnumDeclaration(), - tx.Factory().NewModifierList(tx.ensureModifiers(input)), - input.Name(), - tx.Factory().NewNodeList(core.MapNonNil(input.AsEnumDeclaration().Members.Nodes, func(m *ast.Node) *ast.Node { - // !!! TODO: stripInternal support? - // if (shouldStripInternal(m)) return; - - // !!! TODO: isolatedDeclarations support - // if ( - // isolatedDeclarations && m.initializer && enumValue?.hasExternalReferences && - // // This will be its own compiler error instead, so don't report. - // !isComputedPropertyName(m.name) - // ) { - // context.addDiagnostic(createDiagnosticForNode(m, Diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations)); - // } - - // Rewrite enum values to their constants, if available - enumValue := tx.resolver.GetEnumMemberValue(m) - var newInitializer *ast.Node - switch value := enumValue.Value.(type) { - case jsnum.Number: - if value >= 0 { - newInitializer = tx.Factory().NewNumericLiteral(value.String()) - } else { - newInitializer = tx.Factory().NewPrefixUnaryExpression( - ast.KindMinusToken, - tx.Factory().NewNumericLiteral((-value).String()), - ) - } - case string: - newInitializer = tx.Factory().NewStringLiteral(value) - default: - // nil - newInitializer = nil - } - result := tx.Factory().UpdateEnumMember(m.AsEnumMember(), m.Name(), newInitializer) - tx.preserveJsDoc(result, m) - return result - })), - ) + result = tx.transformEnumDeclaration(input.AsEnumDeclaration()) default: // Anything left unhandled is an error, so this should be unreachable panic(fmt.Sprintf("Unhandled top-level node in declaration emit: %q", input.Kind)) } - if isEnclosingDeclaration(input) { - tx.enclosingDeclaration = previousEnclosingDeclaration + tx.enclosingDeclaration = previousEnclosingDeclaration + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.needsDeclare = previousNeedsDeclare + tx.state.errorNameNode = oldName + return result +} + +func (tx *DeclarationTransformer) transformTypeAliasDeclaration(input *ast.TypeAliasDeclaration) *ast.Node { + tx.needsDeclare = false + return tx.Factory().UpdateTypeAliasDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + tx.Visitor().VisitNodes(input.TypeParameters), + tx.Visitor().Visit(input.Type), + ) +} + +func (tx *DeclarationTransformer) transformInterfaceDeclaration(input *ast.InterfaceDeclaration) *ast.Node { + return tx.Factory().UpdateInterfaceDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + tx.Visitor().VisitNodes(input.TypeParameters), + tx.Visitor().VisitNodes(input.HeritageClauses), + tx.Visitor().VisitNodes(input.Members), + ) +} + +func (tx *DeclarationTransformer) transformFunctionDeclaration(input *ast.FunctionDeclaration) *ast.Node { + updated := tx.Factory().UpdateFunctionDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + nil, + input.Name(), + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + nil, + ) + if updated == nil || !tx.resolver.IsExpandoFunctionDeclaration(input.AsNode()) || !shouldEmitFunctionProperties(input) { + return updated } - if canProdiceDiagnostic { + // Add expando function properties to result + + // !!! TODO: expando function support + // props := tx.resolver.GetPropertiesOfContainerFunction(input) + // if tx.state.isolatedDeclarations { + // tx.state.reportExpandoFunctionErrors(input.AsNode()) + // } + return updated // !!! +} + +func (tx *DeclarationTransformer) transformModuleDeclaration(input *ast.ModuleDeclaration) *ast.Node { + // !!! TODO: module declarations are now parsed into nested module objects with export modifiers + // It'd be good to collapse those back in the declaration output, but the AST can't represent the + // `namespace a.b.c` shape for the printer (without using invalid identifier names). + mods := tx.ensureModifiers(input.AsNode()) + oldNeedsDeclare := tx.needsDeclare + tx.needsDeclare = false + inner := input.Body + if inner != nil && inner.Kind == ast.KindModuleBlock { + oldNeedsScopeFix := tx.needsScopeFixMarker + oldHasScopeFix := tx.resultHasScopeMarker + tx.resultHasScopeMarker = false + tx.needsScopeFixMarker = false + statements := tx.Visitor().VisitNodes(inner.AsModuleBlock().Statements) + lateStatements := tx.transformAndReplaceLatePaintedStatements(statements) + if input.Flags&ast.NodeFlagsAmbient != 0 { + tx.needsScopeFixMarker = false // If it was `declare`'d everything is implicitly exported already, ignore late printed "privates" + } + // With the final list of statements, there are 3 possibilities: + // 1. There's an export assignment or export declaration in the namespace - do nothing + // 2. Everything is exported and there are no export assignments or export declarations - strip all export modifiers + // 3. Some things are exported, some are not, and there's no marker - add an empty marker + if !ast.IsGlobalScopeAugmentation(input.AsNode()) && !tx.resultHasScopeMarker && !hasScopeMarker(lateStatements) { + if tx.needsScopeFixMarker { + lateStatements = tx.Factory().NewNodeList(append(lateStatements.Nodes, createEmptyExports(tx.Factory()))) + } else { + lateStatements = tx.EmitContext().NewNodeVisitor(tx.stripExportModifiers).VisitNodes(lateStatements) + } + } + + body := tx.Factory().UpdateModuleBlock(inner.AsModuleBlock(), lateStatements) + tx.needsDeclare = oldNeedsDeclare + tx.needsScopeFixMarker = oldNeedsScopeFix + tx.resultHasScopeMarker = oldHasScopeFix + + return tx.Factory().UpdateModuleDeclaration( + input, + mods, + input.Keyword, + input.Name(), + body, + ) + } + // trigger visit. ignore result (is deferred, so is just inner unless elided) + tx.Visitor().Visit(inner) + // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) + original := tx.EmitContext().MostOriginal(inner) + id := ast.GetNodeId(original) + body, _ := tx.lateStatementReplacementMap[id] + delete(tx.lateStatementReplacementMap, id) + return tx.Factory().UpdateModuleDeclaration( + input, + mods, + input.Keyword, + input.Name(), + body, + ) +} + +func (tx *DeclarationTransformer) stripExportModifiers(statement *ast.Node) *ast.Node { + if ast.IsImportEqualsDeclaration(statement) || tx.host.GetEffectiveDeclarationFlags(statement, ast.ModifierFlagsDefault) != 0 || !ast.CanHaveModifiers(statement) { + // `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace + // Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too + return statement + } + + oldFlags := ast.GetCombinedModifierFlags(statement) + if oldFlags&ast.ModifierFlagsExport == 0 { + return statement + } + newFlags := oldFlags & (ast.ModifierFlagsAll ^ ast.ModifierFlagsExport) + modifiers := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) + return ast.ReplaceModifiers(tx.Factory(), statement, tx.Factory().NewModifierList(modifiers)) +} + +func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDeclaration) *ast.Node { + tx.state.errorNameNode = input.Name() + tx.tracker.PushErrorFallbackNode(input.AsNode()) + defer tx.tracker.PopErrorFallbackNode() + + modifiers := tx.ensureModifiers(input.AsNode()) + typeParameters := tx.ensureTypeParams(input.AsNode(), input.TypeParameters) + ctor := getFirstConstructorWithBody(input.AsNode()) + var parameterProperties []*ast.Node + if ctor != nil { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + for _, param := range ctor.AsConstructorDeclaration().Parameters.Nodes { + if !ast.HasSyntacticModifier(param, ast.ModifierFlagsParameterPropertyModifier) { + continue + } + // !!! TODO: stripInternal support? + // if (shouldStripInternal(param)) { continue } + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param) + if param.Name().Kind == ast.KindIdentifier { + updated := tx.Factory().NewPropertyDeclaration( + tx.ensureModifiers(param), + param.Name(), + param.AsParameterDeclaration().QuestionToken, + tx.ensureType(param, false), + tx.ensureNoInitializer(param), + ) + tx.preserveJsDoc(updated, param) + parameterProperties = append(parameterProperties, updated) + } else { + // Pattern - this is currently an error, but we emit declarations for it somewhat correctly + // !!! is this worth reimplementing? We never made it not-an-error + } + } tx.state.getSymbolAccessibilityDiagnostic = oldDiag } - if input.Kind == ast.KindModuleDeclaration { - tx.needsDeclare = previousNeedsDeclare + + // When the class has at least one private identifier, create a unique constant identifier to retain the nominal typing behavior + // Prevents other classes with the same public members from being used in place of the current class + var privateIdentifier *ast.Node + if core.Some(input.Members.Nodes, func(member *ast.Node) bool { return member.Name() != nil && ast.IsPrivateIdentifier(member.Name()) }) { + privateIdentifier = tx.Factory().NewPropertyDeclaration( + nil, + tx.Factory().NewPrivateIdentifier("#private"), + nil, + nil, + nil, + ) } - if result == input { - return input + + lateIndexes := tx.resolver.CreateLateBoundIndexSignatures( + tx.EmitContext(), + input.AsNode(), + tx.enclosingDeclaration, + declarationEmitNodeBuilderFlags, + declarationEmitInternalNodeBuilderFlags, + tx.tracker, + ) + + memberNodes := make([]*ast.Node, 0, len(input.Members.Nodes)) + if privateIdentifier != nil { + memberNodes = append(memberNodes, privateIdentifier) } - tx.state.errorNameNode = nil - return result + memberNodes = append(memberNodes, lateIndexes...) + memberNodes = append(memberNodes, parameterProperties...) + visitResult := tx.Visitor().VisitNodes(input.Members) + if visitResult != nil && len(visitResult.Nodes) > 0 { + memberNodes = append(memberNodes, visitResult.Nodes...) + } + members := tx.Factory().NewNodeList(memberNodes) + + extendsClause := getEffectiveBaseTypeNode(input.AsNode()) + + if extendsClause != nil && !ast.IsEntityNameExpression(extendsClause.AsExpressionWithTypeArguments().Expression) && extendsClause.AsExpressionWithTypeArguments().Expression.Kind != ast.KindNullKeyword { + oldId := "default" + if ast.NodeIsPresent(input.Name()) && ast.IsIdentifier(input.Name()) && len(input.Name().AsIdentifier().Text) > 0 { + oldId = input.Name().AsIdentifier().Text + } + newId := tx.EmitContext().NewUniqueName(oldId+"_base", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) + tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + return &SymbolAccessibilityDiagnostic{ + diagnosticMessage: diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1, + errorNode: extendsClause, + typeName: input.Name(), + } + } + + varDecl := tx.Factory().NewVariableDeclaration( + newId, + nil, + tx.resolver.CreateTypeOfExpression(tx.EmitContext(), extendsClause.Expression(), input.AsNode(), declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker), + nil, + ) + var mods *ast.ModifierList + if tx.needsDeclare { + mods = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)}) + } + statement := tx.Factory().NewVariableStatement( + mods, + tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl})), + ) + newHeritageClause := tx.Factory().UpdateHeritageClause( + extendsClause.AsHeritageClause(), + tx.Factory().NewNodeList([]*ast.Node{ + tx.Factory().UpdateExpressionWithTypeArguments( + extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments(), + newId, + tx.Visitor().VisitNodes(extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments().TypeArguments), + ), + }), + ) + retainedHeritageClauses := tx.Visitor().VisitNodes(input.HeritageClauses) // should just be `implements` + heritageList := []*ast.Node{ + newHeritageClause, + } + if retainedHeritageClauses != nil && len(retainedHeritageClauses.Nodes) > 0 { + heritageList = append(heritageList, retainedHeritageClauses.Nodes...) + } + heritageClauses := tx.Factory().NewNodeList(heritageList) + + return tx.Factory().NewSyntaxList([]*ast.Node{ + statement, + tx.Factory().UpdateClassDeclaration( + input, + modifiers, + input.Name(), + typeParameters, + heritageClauses, + members, + ), + }) + } + + return tx.Factory().UpdateClassDeclaration( + input, + modifiers, + input.Name(), + typeParameters, + tx.Visitor().VisitNodes(input.HeritageClauses), + members, + ) +} + +func (tx *DeclarationTransformer) transformVariableStatement(input *ast.VariableStatement) *ast.Node { + visible := false + for _, decl := range input.DeclarationList.AsVariableDeclarationList().Declarations.Nodes { + visible = getBindingNameVisible(tx.resolver, decl) + if visible { + break + } + } + if !visible { + return nil + } + + nodes := tx.Visitor().VisitNodes(input.DeclarationList.AsVariableDeclarationList().Declarations) + if nodes != nil && len(nodes.Nodes) == 0 { + return nil + } + + modifiers := tx.ensureModifiers(input.AsNode()) + + var declList *ast.Node + if ast.IsVarUsing(input.DeclarationList) || ast.IsVarAwaitUsing(input.DeclarationList) { + declList = tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, nodes) + tx.EmitContext().SetOriginal(declList, input.DeclarationList) + tx.EmitContext().SetCommentRange(declList, input.DeclarationList.Loc) + declList.Loc = input.DeclarationList.Loc + } else { + declList = tx.Factory().UpdateVariableDeclarationList(input.DeclarationList.AsVariableDeclarationList(), nodes) + } + return tx.Factory().UpdateVariableStatement(input, modifiers, declList) } -func (tx *DeclarationTransformer) transformVariableStatement(node *ast.VariableStatement) *ast.Node { - return nil // !!! +func (tx *DeclarationTransformer) transformEnumDeclaration(input *ast.EnumDeclaration) *ast.Node { + return tx.Factory().UpdateEnumDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + tx.Factory().NewNodeList(core.MapNonNil(input.Members.Nodes, func(m *ast.Node) *ast.Node { + // !!! TODO: stripInternal support? + // if (shouldStripInternal(m)) return; + + // !!! TODO: isolatedDeclarations support + // if ( + // isolatedDeclarations && m.initializer && enumValue?.hasExternalReferences && + // // This will be its own compiler error instead, so don't report. + // !isComputedPropertyName(m.name) + // ) { + // context.addDiagnostic(createDiagnosticForNode(m, Diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations)); + // } + + // Rewrite enum values to their constants, if available + enumValue := tx.resolver.GetEnumMemberValue(m) + var newInitializer *ast.Node + switch value := enumValue.Value.(type) { + case jsnum.Number: + if value >= 0 { + newInitializer = tx.Factory().NewNumericLiteral(value.String()) + } else { + newInitializer = tx.Factory().NewPrefixUnaryExpression( + ast.KindMinusToken, + tx.Factory().NewNumericLiteral((-value).String()), + ) + } + case string: + newInitializer = tx.Factory().NewStringLiteral(value) + default: + // nil + newInitializer = nil + } + result := tx.Factory().UpdateEnumMember(m.AsEnumMember(), m.Name(), newInitializer) + tx.preserveJsDoc(result, m) + return result + })), + ) } -func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) []*ast.Node { +func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) *ast.ModifierList { currentFlags := tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsAll) newFlags := tx.ensureModifierFlags(node) if currentFlags == newFlags { // Elide decorators - return core.Filter(node.Modifiers().Nodes, ast.IsModifier) + return tx.Factory().NewModifierList(core.Filter(node.Modifiers().Nodes, ast.IsModifier)) } - return ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) + result := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) + if len(result) == 0 { + return nil + } + return tx.Factory().NewModifierList(result) } func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.ModifierFlags { @@ -617,6 +1400,99 @@ func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.Modifi return maskModifierFlagsEx(tx.host, node, mask, additions) } +func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.TypeParameterList) *ast.TypeParameterList { + if tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 { + return nil + } + return tx.Visitor().VisitNodes(params) +} + +func (tx *DeclarationTransformer) updateParamList(node *ast.Node, params *ast.ParameterList) *ast.ParameterList { + return tx.updateParamListEx(node, params, ast.ModifierFlagsAll^ast.ModifierFlagsPublic) +} + +func (tx *DeclarationTransformer) updateParamListEx(node *ast.Node, params *ast.ParameterList, modifierMask ast.ModifierFlags) *ast.ParameterList { + if tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 { + return tx.Factory().NewNodeList([]*ast.Node{}) + } + results := make([]*ast.Node, len(params.Nodes)) + for i, p := range params.Nodes { + results[i] = tx.ensureParameter(p.AsParameterDeclaration(), modifierMask) + } + return tx.Factory().NewNodeList(results) +} + +// Elide "public" modifier, as it is the default +func (tx *DeclarationTransformer) maskModifiers(node *ast.Node, mask ast.ModifierFlags, additions ast.ModifierFlags) *ast.ModifierList { + list := ast.CreateModifiersFromModifierFlags(maskModifierFlagsEx(tx.host, node, mask, additions), tx.Factory().NewModifier) + return tx.Factory().NewModifierList(list) +} + +func (tx *DeclarationTransformer) ensureParameter(p *ast.ParameterDeclaration, modifierMask ast.ModifierFlags) *ast.Node { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.AsNode()) + } + var questionToken *ast.TokenNode + if tx.resolver.IsOptionalParameter(p.AsNode()) { + if p.QuestionToken != nil { + questionToken = p.QuestionToken + } else { + questionToken = tx.Factory().NewToken(ast.KindQuestionToken) + } + } + result := tx.Factory().UpdateParameterDeclaration( + p, + tx.maskModifiers(p.AsNode(), modifierMask, ast.ModifierFlagsNone), + p.DotDotDotToken, + tx.filterBindingPatternInitializers(p.Name()), + questionToken, + tx.ensureType(p.AsNode(), true), + tx.ensureNoInitializer(p.AsNode()), + ) + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + return result +} + +func (tx *DeclarationTransformer) ensureNoInitializer(node *ast.Node) *ast.Node { + if tx.shouldPrintWithInitializer(node) { + unwrappedInitializer := unwrapParenthesizedExpression(node.Initializer()) + if !isPrimitiveLiteralValue(unwrappedInitializer, true) { + tx.tracker.ReportInferenceFallback(node) + } + return tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(node), tx.tracker) + } + return nil +} + +func (tx *DeclarationTransformer) filterBindingPatternInitializers(node *ast.Node) *ast.Node { + if node.Kind == ast.KindIdentifier { + return node + } else { + // TODO: visitor to avoid always making new nodes? + elements := make([]*ast.Node, 0, len(node.AsBindingPattern().Elements.Nodes)) + for _, elem := range node.AsBindingPattern().Elements.Nodes { + if elem.Kind == ast.KindOmittedExpression { + elements = append(elements, elem) + continue + } + if elem.PropertyName() != nil && ast.IsComputedPropertyName(elem.PropertyName()) && ast.IsEntityNameExpression(elem.PropertyName().Expression()) { + tx.checkEntityNameVisibility(elem.PropertyName().Expression(), tx.enclosingDeclaration) + } + + elements = append(elements, tx.Factory().UpdateBindingElement( + elem.AsBindingElement(), + elem.AsBindingElement().DotDotDotToken, + elem.PropertyName(), + tx.filterBindingPatternInitializers(elem.Name()), + nil, + )) + } + elemList := tx.Factory().NewNodeList(elements) + return tx.Factory().UpdateBindingPattern(node.AsBindingPattern(), elemList) + } +} + func (tx *DeclarationTransformer) transformImportEqualsDeclaration(decl *ast.ImportEqualsDeclaration) *ast.Node { if !tx.resolver.IsDeclarationVisible(decl.AsNode()) { return nil @@ -632,10 +1508,10 @@ func (tx *DeclarationTransformer) transformImportEqualsDeclaration(decl *ast.Imp tx.Factory().UpdateExternalModuleReference(decl.ModuleReference.AsExternalModuleReference(), tx.rewriteModuleSpecifier(decl.AsNode(), specifier)), ) } else { - oldDiag := tx.getSymbolAccessibilityDiagnostic - tx.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(decl.AsNode()) + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(decl.AsNode()) tx.checkEntityNameVisibility(decl.ModuleReference, tx.enclosingDeclaration) - tx.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.getSymbolAccessibilityDiagnostic = oldDiag return decl.AsNode() } } diff --git a/internal/declarations/util.go b/internal/declarations/util.go index abc315310c..5968321c54 100644 --- a/internal/declarations/util.go +++ b/internal/declarations/util.go @@ -2,6 +2,7 @@ package declarations import ( "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/printer" ) @@ -27,23 +28,6 @@ func needsScopeMarker(result *ast.Node) bool { return !ast.IsAnyImportOrReExport(result) && !ast.IsExportAssignment(result) && !ast.HasSyntacticModifier(result, ast.ModifierFlagsExport) && !ast.IsAmbientModule(result) } -func isLateVisibilityPaintedStatement(node *ast.Node) bool { - switch node.Kind { - case ast.KindImportDeclaration, - ast.KindImportEqualsDeclaration, - ast.KindVariableStatement, - ast.KindClassDeclaration, - ast.KindFunctionDeclaration, - ast.KindModuleDeclaration, - ast.KindTypeAliasDeclaration, - ast.KindInterfaceDeclaration, - ast.KindEnumDeclaration: - return true - default: - return false - } -} - func canHaveLiteralInitializer(host DeclarationEmitHost, node *ast.Node) bool { switch node.Kind { case ast.KindPropertyDeclaration, @@ -117,10 +101,10 @@ func isDeclarationAndNotVisible(emitContext *printer.EmitContext, resolver print // The following should be doing their own visibility checks based on filtering their members case ast.KindVariableDeclaration: return !getBindingNameVisible(resolver, node) - case ast.KindImportEqualsDeclaration: - case ast.KindImportDeclaration: - case ast.KindExportDeclaration: - case ast.KindExportAssignment: + case ast.KindImportEqualsDeclaration, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindExportAssignment: return false case ast.KindClassStaticBlockDeclaration: return true @@ -179,3 +163,72 @@ func maskModifierFlagsEx(host DeclarationEmitHost, node *ast.Node, modifierMask } return flags } + +func unwrapParenthesizedExpression(o *ast.Node) *ast.Node { + for o.Kind == ast.KindParenthesizedExpression { + o = o.Expression() + } + return o +} + +func isPrimitiveLiteralValue(node *ast.Node, includeBigInt bool) bool { + return false // !!! +} + +func isPrivateMethodTypeParameter(host DeclarationEmitHost, node *ast.TypeParameterDeclaration) bool { + return node.AsNode().Parent.Kind == ast.KindMethodDeclaration && host.GetEffectiveDeclarationFlags(node.AsNode().Parent, ast.ModifierFlagsPrivate) != 0 +} + +// If the ExpandoFunctionDeclaration have multiple overloads, then we only need to emit properties for the last one. +func shouldEmitFunctionProperties(input *ast.FunctionDeclaration) bool { + if input.Body != nil { // if it has an implementation, it must be the last one + return true + } + + overloadSignatures := core.Filter(input.Symbol.Declarations, func(decl *ast.Node) bool { + return ast.IsFunctionDeclaration(decl) + }) + + return len(overloadSignatures) == 0 || overloadSignatures[len(overloadSignatures)-1] == input.AsNode() +} + +func getFirstConstructorWithBody(node *ast.Node) *ast.Node { + for _, member := range node.Members() { + if ast.IsConstructorDeclaration(member) && ast.NodeIsPresent(member.Body()) { + return member + } + } + return nil +} + +func getEffectiveBaseTypeNode(node *ast.Node) *ast.Node { + baseType := getClassExtendsHeritageElement(node) + // !!! TODO: JSDoc support + // if (baseType && isInJSFile(node)) { + // // Prefer an @augments tag because it may have type parameters. + // const tag = getJSDocAugmentsTag(node); + // if (tag) { + // return tag.class; + // } + // } + return baseType +} + +func getClassExtendsHeritageElement(node *ast.Node) *ast.Node { + heritageClause := ast.GetHeritageClause(node, ast.KindExtendsKeyword) + if heritageClause != nil && len(heritageClause.AsHeritageClause().Types.Nodes) > 0 { + return heritageClause.AsHeritageClause().Types.Nodes[0] + } + return nil +} + +func isScopeMarker(node *ast.Node) bool { + return ast.IsExportAssignment(node) || ast.IsExportDeclaration(node) +} + +func hasScopeMarker(statements *ast.StatementList) bool { + if statements == nil { + return false + } + return core.Some(statements.Nodes, isScopeMarker) +} diff --git a/internal/printer/emitresolver.go b/internal/printer/emitresolver.go index 0450a8192c..52a4992608 100644 --- a/internal/printer/emitresolver.go +++ b/internal/printer/emitresolver.go @@ -42,8 +42,13 @@ type EmitResolver interface { IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool IsImplementationOfOverload(node *ast.SignatureDeclaration) bool GetEnumMemberValue(node *ast.Node) evaluator.Result + IsLateBound(node *ast.Node) bool + IsOptionalParameter(node *ast.Node) bool // Node construction for declaration emit CreateTypeOfDeclaration(emitContext *EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node CreateReturnTypeOfSignatureDeclaration(emitContext *EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateLiteralConstValue(emitContext *EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node + CreateTypeOfExpression(emitContext *EmitContext, expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateLateBoundIndexSignatures(emitContext *EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node } From b1c7164836ec7c6ec3f986269cd15948195e3af1 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 17 Apr 2025 21:04:34 -0700 Subject: [PATCH 04/15] Add missing name check, fix some NPEs, swap symbolToString from stub to builder-backed since thats now --- internal/checker/printer.go | 3 +- internal/declarations/transform.go | 57 ++++++++++++++++++++++-------- internal/declarations/util.go | 47 ++++++++++++------------ 3 files changed, 67 insertions(+), 40 deletions(-) diff --git a/internal/checker/printer.go b/internal/checker/printer.go index f2b66945a3..92f68b283d 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -86,10 +86,9 @@ func (c *Checker) SymbolToString(s *ast.Symbol) string { } func (c *Checker) symbolToString(symbol *ast.Symbol) string { - return "" + return c.symbolToStringEx(symbol, nil, ast.SymbolFlagsAll, SymbolFormatFlagsNone, nil, nil) } -// TODO: port SymbolFormatFlags func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags, kind *SignatureKind, writer printer.EmitTextWriter) string { if writer == nil { writer = printer.SingleLineStringWriter diff --git a/internal/declarations/transform.go b/internal/declarations/transform.go index cae3d5a7d2..e6958e9ec9 100644 --- a/internal/declarations/transform.go +++ b/internal/declarations/transform.go @@ -37,13 +37,11 @@ type DeclarationEmitHost interface { type DeclarationTransformer struct { transformers.Transformer - host DeclarationEmitHost - compilerOptions *core.CompilerOptions - diagnostics []*ast.Diagnostic - tracker *SymbolTrackerImpl - state *SymbolTrackerSharedState - resolver printer.EmitResolver - + host DeclarationEmitHost + compilerOptions *core.CompilerOptions + tracker *SymbolTrackerImpl + state *SymbolTrackerSharedState + resolver printer.EmitResolver declarationFilePath string declarationMapPath string @@ -61,16 +59,24 @@ type DeclarationTransformer struct { } func NewDeclarationTransformer(host DeclarationEmitHost, resolver printer.EmitResolver, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer { - shared := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver} - tracker := NewSymbolTracker(resolver, shared) + state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver} + tracker := NewSymbolTracker(resolver, state) // TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit) - tx := &DeclarationTransformer{compilerOptions: compilerOptions, tracker: tracker, state: shared, declarationFilePath: declarationFilePath, declarationMapPath: declarationMapPath, host: host} + tx := &DeclarationTransformer{ + host: host, + compilerOptions: compilerOptions, + tracker: tracker, + state: state, + resolver: resolver, + declarationFilePath: declarationFilePath, + declarationMapPath: declarationMapPath, + } tx.NewTransformer(tx.visit, context) return tx } func (tx *DeclarationTransformer) GetDiagnostics() []*ast.Diagnostic { - return tx.diagnostics + return tx.state.diagnostics } const declarationEmitNodeBuilderFlags = nodebuilder.FlagsMultilineObjectLiterals | @@ -462,6 +468,10 @@ func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast. result = tx.Visitor().VisitEachChild(input) } + if result != nil && canProdiceDiagnostic && ast.HasDynamicName(input) { + tx.checkName(input) + } + tx.enclosingDeclaration = previousEnclosingDeclaration tx.state.getSymbolAccessibilityDiagnostic = oldDiag tx.state.errorNameNode = oldName @@ -469,6 +479,21 @@ func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast. return result } +func (tx *DeclarationTransformer) checkName(node *ast.Node) { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNodeName(node) + } + tx.state.errorNameNode = node.Name() + // !!! Debug.assert(hasDynamicName(node as NamedDeclaration)); // Should only be called with dynamic names + entityName := node.Name().AsComputedPropertyName().Expression + tx.checkEntityNameVisibility(entityName, tx.enclosingDeclaration) + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + } + tx.state.errorNameNode = nil +} + func (tx *DeclarationTransformer) transformHeritageClause(clause *ast.HeritageClause) *ast.Node { retainedClauses := core.Filter(clause.Types.Nodes, func(t *ast.Node) bool { return ast.IsEntityNameExpression(t.AsExpressionWithTypeArguments().Expression) || @@ -1377,7 +1402,11 @@ func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) *ast.ModifierL newFlags := tx.ensureModifierFlags(node) if currentFlags == newFlags { // Elide decorators - return tx.Factory().NewModifierList(core.Filter(node.Modifiers().Nodes, ast.IsModifier)) + mods := node.Modifiers() + if mods == nil { + return mods + } + return tx.Factory().NewModifierList(core.Filter(mods.Nodes, ast.IsModifier)) } result := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) if len(result) == 0 { @@ -1397,7 +1426,7 @@ func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.Modifi mask ^= ast.ModifierFlagsAmbient additions = ast.ModifierFlagsNone } - return maskModifierFlagsEx(tx.host, node, mask, additions) + return maskModifierFlags(tx.host, node, mask, additions) } func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.TypeParameterList) *ast.TypeParameterList { @@ -1424,7 +1453,7 @@ func (tx *DeclarationTransformer) updateParamListEx(node *ast.Node, params *ast. // Elide "public" modifier, as it is the default func (tx *DeclarationTransformer) maskModifiers(node *ast.Node, mask ast.ModifierFlags, additions ast.ModifierFlags) *ast.ModifierList { - list := ast.CreateModifiersFromModifierFlags(maskModifierFlagsEx(tx.host, node, mask, additions), tx.Factory().NewModifier) + list := ast.CreateModifiersFromModifierFlags(maskModifierFlags(tx.host, node, mask, additions), tx.Factory().NewModifier) return tx.Factory().NewModifierList(list) } diff --git a/internal/declarations/util.go b/internal/declarations/util.go index 5968321c54..dfd5bb97a6 100644 --- a/internal/declarations/util.go +++ b/internal/declarations/util.go @@ -6,24 +6,6 @@ import ( "github.com/microsoft/typescript-go/internal/printer" ) -func isPreservedDeclarationStatement(node *ast.Node) bool { - switch node.Kind { - case ast.KindFunctionDeclaration, - ast.KindModuleDeclaration, - ast.KindImportEqualsDeclaration, - ast.KindInterfaceDeclaration, - ast.KindClassDeclaration, - ast.KindTypeAliasDeclaration, - ast.KindEnumDeclaration, - ast.KindVariableStatement, - ast.KindImportDeclaration, - ast.KindExportDeclaration, - ast.KindExportAssignment: - return true - } - return false -} - func needsScopeMarker(result *ast.Node) bool { return !ast.IsAnyImportOrReExport(result) && !ast.IsExportAssignment(result) && !ast.HasSyntacticModifier(result, ast.ModifierFlagsExport) && !ast.IsAmbientModule(result) } @@ -147,11 +129,7 @@ func isAlwaysType(node *ast.Node) bool { return false } -func maskModifierFlags(host DeclarationEmitHost, node *ast.Node) ast.ModifierFlags { - return maskModifierFlagsEx(host, node, ast.ModifierFlagsAll^ast.ModifierFlagsPublic, ast.ModifierFlagsNone) -} - -func maskModifierFlagsEx(host DeclarationEmitHost, node *ast.Node, modifierMask ast.ModifierFlags, modifierAdditions ast.ModifierFlags) ast.ModifierFlags { +func maskModifierFlags(host DeclarationEmitHost, node *ast.Node, modifierMask ast.ModifierFlags, modifierAdditions ast.ModifierFlags) ast.ModifierFlags { flags := host.GetEffectiveDeclarationFlags(node, modifierMask) | modifierAdditions if flags&ast.ModifierFlagsDefault != 0 && (flags&ast.ModifierFlagsExport == 0) { // A non-exported default is a nonsequitor - we usually try to remove all export modifiers @@ -172,7 +150,28 @@ func unwrapParenthesizedExpression(o *ast.Node) *ast.Node { } func isPrimitiveLiteralValue(node *ast.Node, includeBigInt bool) bool { - return false // !!! + // !!! Debug.type(node); + switch node.Kind { + case ast.KindTrueKeyword, + ast.KindFalseKeyword, + ast.KindNumericLiteral, + ast.KindStringLiteral, + ast.KindNoSubstitutionTemplateLiteral: + return true + case ast.KindBigIntLiteral: + return includeBigInt + case ast.KindPrefixUnaryExpression: + if node.AsPrefixUnaryExpression().Operator == ast.KindMinusToken { + return ast.IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) || (includeBigInt && ast.IsBigIntLiteral(node.AsPrefixUnaryExpression().Operand)) + } + if node.AsPrefixUnaryExpression().Operator == ast.KindPlusToken { + return ast.IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) + } + return false + default: + // !!! assertType(node); + return false + } } func isPrivateMethodTypeParameter(host DeclarationEmitHost, node *ast.TypeParameterDeclaration) bool { From 10500fd25e0978b0b76044e4b21ecb4a05bcf4b5 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 18 Apr 2025 01:25:54 -0700 Subject: [PATCH 05/15] Port old, inefficient symbol accesibility checking methods --- internal/checker/checker.go | 3 +- internal/checker/nodebuilder.go | 21 +- internal/checker/printer.go | 4 +- internal/checker/symbolaccessibility.go | 702 ++++++++++++++++++++++-- internal/checker/types.go | 12 + 5 files changed, 702 insertions(+), 40 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index c7a3e83d1f..26962ea338 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -639,6 +639,7 @@ type Checker struct { indexSymbolLinks core.LinkStore[*ast.Symbol, IndexSymbolLinks] ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks] markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks] + symbolContainerLinks core.LinkStore[*ast.Symbol, ContainingSymbolLinks] sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] patternForType map[*Type]*ast.Node contextFreeTypes map[*ast.Node]*Type @@ -29765,7 +29766,7 @@ func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type { return c.getTypeOfNode(node) } -func (c *Checker) GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool) printer.EmitResolver { +func (c *Checker) GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool) *emitResolver { c.emitResolverOnce.Do(func() { c.emitResolver = &emitResolver{checker: c} }) diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 6d93df9659..974ae8584f 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -336,7 +336,25 @@ func (b *NodeBuilder) getResolvedTypeWithoutAbstractConstructSignatures(t *Struc } func (b *NodeBuilder) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node { - panic("unimplemented") // !!! + if b.ctx.internalFlags&nodebuilder.InternalFlagsWriteComputedProps != 0 { + if symbol.ValueDeclaration != nil { + name := ast.GetNameOfDeclaration(symbol.ValueDeclaration) + if name != nil && ast.IsComputedPropertyName(name) { + return name + } + if b.ch.valueSymbolLinks.Has(symbol) { + nameType := b.ch.valueSymbolLinks.Get(symbol).nameType + if nameType != nil && nameType.flags&(TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0 { + oldEnclosing := b.ctx.enclosingDeclaration + b.ctx.enclosingDeclaration = nameType.symbol.ValueDeclaration + result := b.f.NewComputedPropertyName(b.symbolToExpression(nameType.symbol, meaning)) + b.ctx.enclosingDeclaration = oldEnclosing + return result + } + } + } + } + return b.symbolToExpression(symbol, meaning) } func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, expectsIdentifier bool) *ast.Node { @@ -352,6 +370,7 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, } func (b *NodeBuilder) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlags) *ast.Expression { + // chain := b.lookupSymbolChain(symbol, meaning) panic("unimplemented") // !!! } diff --git a/internal/checker/printer.go b/internal/checker/printer.go index 92f68b283d..82b98ef04f 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -86,10 +86,10 @@ func (c *Checker) SymbolToString(s *ast.Symbol) string { } func (c *Checker) symbolToString(symbol *ast.Symbol) string { - return c.symbolToStringEx(symbol, nil, ast.SymbolFlagsAll, SymbolFormatFlagsNone, nil, nil) + return c.symbolToStringEx(symbol, nil, ast.SymbolFlagsAll, SymbolFormatFlagsAllowAnyNodeKind, nil) } -func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags, kind *SignatureKind, writer printer.EmitTextWriter) string { +func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags, writer printer.EmitTextWriter) string { if writer == nil { writer = printer.SingleLineStringWriter } diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index a79cb6c43d..b92a04ba13 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -1,16 +1,647 @@ package checker import ( + "reflect" + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/printer" ) -func (ch *Checker) IsTypeSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { - return false // !!! +func (ch *Checker) IsTypeSymbolAccessible(typeSymbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + access := ch.isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, ast.SymbolFlagsType /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, true) + return access.Accessibility == printer.SymbolAccessibilityAccessible } func (ch *Checker) IsValueSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { - return false // !!! + access := ch.isSymbolAccessibleWorker(symbol, enclosingDeclaration, ast.SymbolFlagsValue /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, true) + return access.Accessibility == printer.SymbolAccessibilityAccessible +} + +func (ch *Checker) IsSymbolAccessibleByFlags(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags ast.SymbolFlags) bool { + access := ch.isSymbolAccessibleWorker(symbol, enclosingDeclaration, flags /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, false) // TODO: Strada bug? Why is this allowModules: false? + return access.Accessibility == printer.SymbolAccessibilityAccessible +} + +func (ch *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclaration *ast.Node, initialSymbol *ast.Symbol, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) *printer.SymbolAccessibilityResult { + if len(symbols) == 0 { + return nil + } + + var hadAccessibleChain *ast.Symbol + earlyModuleBail := false + for _, symbol := range symbols { + // Symbol is accessible if it by itself is accessible + accessibleSymbolChain := ch.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning /*useOnlyExternalAliasing*/, false) + if len(accessibleSymbolChain) > 0 { + hadAccessibleChain = symbol + // TODO: going through emit resolver here is weird. Relayer these APIs. + hasAccessibleDeclarations := ch.GetEmitResolver(nil, true).hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) + if hasAccessibleDeclarations != nil { + return hasAccessibleDeclarations + } + } + if allowModules { + if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + if shouldComputeAliasesToMakeVisible { + earlyModuleBail = true + // Generally speaking, we want to use the aliases that already exist to refer to a module, if present + // In order to do so, we need to find those aliases in order to retain them in declaration emit; so + // if we are in declaration emit, we cannot use the fast path for module visibility until we've exhausted + // all other visibility options (in order to capture the possible aliases used to reference the module) + continue + } + // Any meaning of a module symbol is always accessible via an `import` type + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + } + } + } + + // If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible. + // It could be a qualified symbol and hence verify the path + // e.g.: + // module m { + // export class c { + // } + // } + // const x: typeof m.c + // In the above example when we start with checking if typeof m.c symbol is accessible, + // we are going to see if c can be accessed in scope directly. + // But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible + // It is accessible if the parent m is accessible because then m.c can be accessed through qualification + + containers := ch.getContainersOfSymbol(symbol, enclosingDeclaration, meaning) + nextMeaning := meaning + if initialSymbol == symbol { + nextMeaning = getQualifiedLeftMeaning(meaning) + } + parentResult := ch.IsAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, nextMeaning, shouldComputeAliasesToMakeVisible, allowModules) + if parentResult != nil { + return parentResult + } + } + + if earlyModuleBail { + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + } + } + + if hadAccessibleChain != nil { + var moduleName string + if hadAccessibleChain != initialSymbol { + moduleName = ch.symbolToStringEx(hadAccessibleChain, enclosingDeclaration, ast.SymbolFlagsNamespace, SymbolFormatFlagsAllowAnyNodeKind, nil) + } + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotAccessible, + ErrorSymbolName: ch.symbolToStringEx(initialSymbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind, nil), + ErrorModuleName: moduleName, + } + } + return nil +} + +func hasNonGlobalAugmentationExternalModuleSymbol(declaration *ast.Node) bool { + return isModuleWithStringLiteralName(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile())) +} + +func isModuleWithStringLiteralName(node *ast.Node) bool { + return ast.IsModuleDeclaration(node) && node.Name().Kind == ast.KindStringLiteral +} + +func getQualifiedLeftMeaning(rightMeaning ast.SymbolFlags) ast.SymbolFlags { + // If we are looking in value space, the parent meaning is value, other wise it is namespace + if rightMeaning == ast.SymbolFlagsValue { + return ast.SymbolFlagsValue + } + return ast.SymbolFlagsNamespace +} + +func (ch *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { + additionalContainers := core.MapNonNil(container.Declarations, func(d *ast.Node) *ast.Symbol { + return ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, container) + }) + var reexportContainers []*ast.Symbol + if enclosingDeclaration != nil { + reexportContainers = ch.getAlternativeContainingModules(symbol, enclosingDeclaration) + } + objectLiteralContainer := ch.getVariableDeclarationOfObjectLiteral(container, meaning) + leftMeaning := getQualifiedLeftMeaning(meaning) + if enclosingDeclaration != nil && + container.Flags&leftMeaning != 0 && + len(ch.getAccessibleSymbolChain(container, enclosingDeclaration, ast.SymbolFlagsNamespace /*useOnlyExternalAliasing*/, false)) > 0 { + // This order expresses a preference for the real container if it is in scope + return append(append(append([]*ast.Symbol{container}, additionalContainers...), reexportContainers...), objectLiteralContainer) + } + // we potentially have a symbol which is a member of the instance side of something - look for a variable in scope with the container's type + // which may be acting like a namespace (eg, `Symbol` acts like a namespace when looking up `Symbol.toStringTag`) + var firstVariableMatch *ast.Symbol + if (meaning == ast.SymbolFlagsValue && + container.Flags&leftMeaning == 0) && + container.Flags&ast.SymbolFlagsType != 0 && + ch.getDeclaredTypeOfSymbol(container).flags&TypeFlagsObject != 0 { + ch.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { + for _, s := range t { + if s.Flags&leftMeaning != 0 && ch.getTypeOfSymbol(s) == ch.getDeclaredTypeOfSymbol(container) { + firstVariableMatch = s + return true + } + } + return false + }) + } + + var res []*ast.Symbol + if firstVariableMatch != nil { + res = append(res, firstVariableMatch) + } + res = append(res, additionalContainers...) + res = append(res, container) + if objectLiteralContainer != nil { + res = append(res, objectLiteralContainer) + } + res = append(res, reexportContainers...) + return res +} + +func (ch *Checker) getAlternativeContainingModules(symbol *ast.Symbol, enclosingDeclaration *ast.Node) []*ast.Symbol { + if enclosingDeclaration == nil { + return nil + } + containingFile := ast.GetSourceFileOfNode(enclosingDeclaration) + id := ast.GetNodeId(containingFile.AsNode()) + links := ch.symbolContainerLinks.Get(symbol) + existing, ok := links.extendedContainersByFile[id] + if ok && existing != nil { + return existing + } + var results []*ast.Symbol + if len(containingFile.Imports) > 0 { + // Try to make an import using an import already in the enclosing file, if possible + for _, importRef := range containingFile.Imports { + if ast.NodeIsSynthesized(importRef) { + // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error + continue + } + resolvedModule := ch.resolveExternalModuleName(enclosingDeclaration, importRef /*ignoreErrors*/, true) + if resolvedModule == nil { + continue + } + ref := ch.getAliasForSymbolInContainer(resolvedModule, symbol) + if ref == nil { + continue + } + results = append(results, resolvedModule) + } + if len(results) > 0 { + links.extendedContainersByFile[id] = results + return results + } + } + + if links.extendedContainers != nil { + return *links.extendedContainers + } + // No results from files already being imported by this file - expand search (expensive, but not location-specific, so cached) + otherFiles := ch.program.SourceFiles() + for _, file := range otherFiles { + if !ast.IsExternalModule(file) { + continue + } + sym := ch.getSymbolOfDeclaration(file.AsNode()) + ref := ch.getAliasForSymbolInContainer(sym, symbol) + if ref == nil { + continue + } + results = append(results, sym) + } + links.extendedContainers = &results + return results +} + +func (ch *Checker) getVariableDeclarationOfObjectLiteral(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Symbol { + // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct + // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, + // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. + if meaning&ast.SymbolFlagsValue == 0 { + return nil + } + if len(symbol.Declarations) == 0 { + return nil + } + firstDecl := symbol.Declarations[0] + if !ast.IsVariableDeclaration(firstDecl.Parent) { + return nil + } + if ast.IsObjectLiteralExpression(firstDecl) && firstDecl == firstDecl.Parent.Initializer() || ast.IsTypeLiteralNode(firstDecl) && firstDecl == firstDecl.Parent.Type() { + return ch.getSymbolOfDeclaration(firstDecl.Parent) + } + return nil +} + +func hasExternalModuleSymbol(declaration *ast.Node) bool { + return ast.IsAmbientModule(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile())) +} + +func (ch *Checker) getExternalModuleContainer(declaration *ast.Node) *ast.Symbol { + node := ast.FindAncestor(declaration, hasExternalModuleSymbol) + if node == nil { + return nil + } + return ch.getSymbolOfDeclaration(node) +} + +func (ch *Checker) getFileSymbolIfFileSymbolExportEqualsContainer(d *ast.Node, container *ast.Symbol) *ast.Symbol { + fileSymbol := ch.getExternalModuleContainer(d) + if fileSymbol == nil || fileSymbol.Exports == nil { + return nil + } + exported, ok := fileSymbol.Exports[ast.InternalSymbolNameExportEquals] + if !ok || exported == nil { + return nil + } + if ch.getSymbolIfSameReference(exported, container) != nil { + return fileSymbol + } + return nil +} + +/** +* Attempts to find the symbol corresponding to the container a symbol is in - usually this +* is just its' `.parent`, but for locals, this value is `undefined` + */ +func (ch *Checker) getContainersOfSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { + container := ch.getParentOfSymbol(symbol) + // Type parameters end up in the `members` lists but are not externally visible + if container != nil && (symbol.Flags&ast.SymbolFlagsTypeParameter == 0) { + return ch.getWithAlternativeContainers(container, symbol, enclosingDeclaration, meaning) + } + var candidates []*ast.Symbol + for _, d := range symbol.Declarations { + if !ast.IsAmbientModule(d) && d.Parent != nil { + // direct children of a module + if hasNonGlobalAugmentationExternalModuleSymbol(d.Parent) { + candidates = append(candidates, ch.getSymbolOfDeclaration(d.Parent)) + continue + } + // export ='d member of an ambient module + if ast.IsModuleBlock(d.Parent) && d.Parent.Parent != nil && ch.resolveExternalModuleSymbol(ch.getSymbolOfDeclaration(d.Parent.Parent), false) == symbol { + candidates = append(candidates, ch.getSymbolOfDeclaration(d.Parent.Parent)) + continue + } + } + if ast.IsClassExpression(d) && ast.IsBinaryExpression(d.Parent) && d.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && ast.IsAccessExpression(d.Parent.AsBinaryExpression().Left) && ast.IsEntityNameExpression(d.Parent.AsBinaryExpression().Left.Expression()) { + if isModuleExportsAccessExpression(d.Parent.AsBinaryExpression().Left) || ast.IsExportsIdentifier(d.Parent.AsBinaryExpression().Left.Expression()) { + candidates = append(candidates, ch.getSymbolOfDeclaration(ast.GetSourceFileOfNode(d).AsNode())) + continue + } + ch.checkExpressionCached(d.Parent.AsBinaryExpression().Left.Expression()) + sym := ch.symbolNodeLinks.Get(d.Parent.AsBinaryExpression().Left.Expression()).resolvedSymbol + candidates = append(candidates, sym) + continue + } + } + if len(candidates) == 0 { + return nil + } + + var bestContainers []*ast.Symbol + var alternativeContainers []*ast.Symbol + for _, container := range candidates { + if ch.getAliasForSymbolInContainer(container, symbol) == nil { + continue + } + allAlts := ch.getWithAlternativeContainers(container, symbol, enclosingDeclaration, meaning) + if len(allAlts) == 0 { + continue + } + bestContainers = append(bestContainers, allAlts[0]) + alternativeContainers = append(alternativeContainers, allAlts[1:]...) + } + return append(bestContainers, alternativeContainers...) +} + +func (ch *Checker) getAliasForSymbolInContainer(container *ast.Symbol, symbol *ast.Symbol) *ast.Symbol { + if container == ch.getParentOfSymbol(symbol) { + // fast path, `symbol` is either already the alias or isn't aliased + return symbol + } + // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return + // the container itself as the alias for the symbol + if container.Exports != nil { + exportEquals, ok := container.Exports[ast.InternalSymbolNameExportEquals] + if ok && exportEquals != nil && ch.getSymbolIfSameReference(exportEquals, symbol) != nil { + return container + } + } + exports := ch.getExportsOfSymbol(container) + quick, ok := exports[symbol.Name] + if ok && quick != nil && ch.getSymbolIfSameReference(quick, symbol) != nil { + return quick + } + for _, exported := range exports { + if ch.getSymbolIfSameReference(exported, symbol) != nil { + return exported + } + } + return nil +} + +func (ch *Checker) getAccessibleSymbolChain( + symbol *ast.Symbol, + enclosingDeclaration *ast.Node, + meaning ast.SymbolFlags, + useOnlyExternalAliasing bool) []*ast.Symbol { + return ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{symbol, enclosingDeclaration, meaning, useOnlyExternalAliasing, make(map[ast.SymbolId]map[uintptr]struct{})}) +} + +type accessibleSymbolChainContext struct { + symbol *ast.Symbol + enclosingDeclaration *ast.Node + meaning ast.SymbolFlags + useOnlyExternalAliasing bool + visitedSymbolTablesMap map[ast.SymbolId]map[uintptr]struct{} +} + +func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) []*ast.Symbol { + if ctx.symbol == nil { + return nil + } + if isPropertyOrMethodDeclarationSymbol(ctx.symbol) { + return nil + } + // Go from enclosingDeclaration to the first scope we check, so the cache is keyed off the scope and thus shared more + var firstRelevantLocation *ast.Node + ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(_ ast.SymbolTable, _ bool, _ bool, node *ast.Node) bool { + firstRelevantLocation = node + return true + }) + links := ch.symbolContainerLinks.Get(ctx.symbol) + linkKey := accessibleChainCacheKey{ctx.useOnlyExternalAliasing, firstRelevantLocation, ctx.meaning} + existing, ok := links.accessibleChainCache[linkKey] + if ok { + return existing + } + + var result []*ast.Symbol + + ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, _ *ast.Node) bool { + res := ch.getAccessibleSymbolChainFromSymbolTable(ctx, t, ignoreQualification, isLocalNameLookup) + if len(res) > 0 { + result = res + return true + } + return false + }) + links.accessibleChainCache[linkKey] = result + return result +} + +/** +* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) + */ +func (ch *Checker) getAccessibleSymbolChainFromSymbolTable(ctx accessibleSymbolChainContext, t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool) []*ast.Symbol { + symId := ast.GetSymbolId(ctx.symbol) + visitedSymbolTables, ok := ctx.visitedSymbolTablesMap[symId] + if !ok { + visitedSymbolTables = make(map[uintptr]struct{}) + ctx.visitedSymbolTablesMap[symId] = visitedSymbolTables + } + + id := reflect.ValueOf(t).Pointer() // TODO: Is this seriously the only way to check reference equality of maps? + _, present := visitedSymbolTables[id] + if present { + return nil + } + visitedSymbolTables[id] = struct{}{} + + res := ch.trySymbolTable(ctx, t, ignoreQualification, isLocalNameLookup) + + delete(visitedSymbolTables, id) + return res +} + +func (ch *Checker) trySymbolTable( + ctx accessibleSymbolChainContext, + symbols ast.SymbolTable, + ignoreQualification bool, + isLocalNameLookup bool, +) []*ast.Symbol { + // If symbol is directly available by its name in the symbol table + res, ok := symbols[ctx.symbol.Name] + if ok && res != nil && ch.isAccessible(ctx, res /*resolvedAliasSymbol*/, nil, ignoreQualification) { + return []*ast.Symbol{ctx.symbol} + } + + for _, symbolFromSymbolTable := range symbols { + if symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && + symbolFromSymbolTable.Name != ast.InternalSymbolNameExportEquals && + symbolFromSymbolTable.Name != ast.InternalSymbolNameDefault && + !(isUMDExportSymbol(symbolFromSymbolTable) && ctx.enclosingDeclaration != nil && ast.IsExternalModule(ast.GetSourceFileOfNode(ctx.enclosingDeclaration))) && + // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name + (!ctx.useOnlyExternalAliasing || core.Some(symbolFromSymbolTable.Declarations, ast.IsExternalModuleImportEqualsDeclaration)) && + // If we're looking up a local name to reference directly, omit namespace reexports, otherwise when we're trawling through an export list to make a dotted name, we can keep it + (!isLocalNameLookup || core.Some(symbolFromSymbolTable.Declarations, isNamespaceReexportDeclaration)) && + // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ + // See similar comment in `resolveName` for details + (ignoreQualification || getDeclarationsOfKind(symbolFromSymbolTable, ast.KindExportSpecifier) == nil) { + resolvedImportedSymbol := ch.resolveAlias(symbolFromSymbolTable) + candidate := ch.getCandidateListForSymbol(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) + if len(candidate) > 0 { + return candidate + } + } + if symbolFromSymbolTable.Name == ctx.symbol.Name && symbolFromSymbolTable.ExportSymbol != nil { + if ch.isAccessible(ctx, ch.getMergedSymbol(symbolFromSymbolTable.ExportSymbol) /*resolvedAliasSymbol*/, nil, ignoreQualification) { + return []*ast.Symbol{ctx.symbol} + } + } + } + + // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that + if reflect.ValueOf(ch.globals).Pointer() == reflect.ValueOf(symbols).Pointer() { + return ch.getCandidateListForSymbol(ctx, ch.globalThisSymbol, ch.globalThisSymbol, ignoreQualification) + } + return nil +} + +func isUMDExportSymbol(symbol *ast.Symbol) bool { + return symbol != nil && len(symbol.Declarations) > 0 && symbol.Declarations[0] != nil && ast.IsNamespaceExportDeclaration(symbol.Declarations[0]) +} + +func isNamespaceReexportDeclaration(node *ast.Node) bool { + return ast.IsNamespaceExport(node) && node.Parent.AsExportDeclaration().ModuleSpecifier != nil +} + +func (ch *Checker) getCandidateListForSymbol( + ctx accessibleSymbolChainContext, + symbolFromSymbolTable *ast.Symbol, + resolvedImportedSymbol *ast.Symbol, + ignoreQualification bool, +) []*ast.Symbol { + if ch.isAccessible(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) { + return []*ast.Symbol{symbolFromSymbolTable} + } + + // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain + // but only if the symbolFromSymbolTable can be qualified + candidateTable := ch.getExportsOfSymbol(resolvedImportedSymbol) + if candidateTable == nil { + return nil + } + accessibleSymbolsFromExports := ch.getAccessibleSymbolChainFromSymbolTable(ctx, candidateTable /*ignoreQualification*/, true, false) + if len(accessibleSymbolsFromExports) == 0 { + return nil + } + if !ch.canQualifySymbol(ctx, symbolFromSymbolTable, getQualifiedLeftMeaning(ctx.meaning)) { + return nil + } + return append([]*ast.Symbol{symbolFromSymbolTable}, accessibleSymbolsFromExports...) +} + +func (ch *Checker) isAccessible( + ctx accessibleSymbolChainContext, + symbolFromSymbolTable *ast.Symbol, + resolvedAliasSymbol *ast.Symbol, + ignoreQualification bool, +) bool { + likeSymbols := false + if ctx.symbol == resolvedAliasSymbol { + likeSymbols = true + } + if ctx.symbol == symbolFromSymbolTable { + likeSymbols = true + } + symbol := ch.getMergedSymbol(ctx.symbol) + if symbol == ch.getMergedSymbol(resolvedAliasSymbol) { + likeSymbols = true + } + if symbol == ch.getMergedSymbol(symbolFromSymbolTable) { + likeSymbols = true + } + if !likeSymbols { + return false + } + // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) + // and if symbolFromSymbolTable or alias resolution matches the symbol, + // check the symbol can be qualified, it is only then this symbol is accessible + return !core.Some(symbolFromSymbolTable.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) && + (ignoreQualification || ch.canQualifySymbol(ctx, ch.getMergedSymbol(symbolFromSymbolTable), ctx.meaning)) +} + +func (ch *Checker) canQualifySymbol( + ctx accessibleSymbolChainContext, + symbolFromSymbolTable *ast.Symbol, + meaning ast.SymbolFlags, +) bool { + // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible + return !ch.needsQualification(symbolFromSymbolTable, ctx.enclosingDeclaration, meaning) || + // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too + len(ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{ctx.symbol, ctx.enclosingDeclaration, getQualifiedLeftMeaning(meaning), ctx.useOnlyExternalAliasing, ctx.visitedSymbolTablesMap})) > 0 +} + +func (ch *Checker) needsQualification(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { + qualify := false + ch.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { + // If symbol of this name is not available in the symbol table we are ok + res, ok := symbolTable[symbol.Name] + if !ok || res == nil { + return false + } + symbolFromSymbolTable := ch.getMergedSymbol(res) + if symbolFromSymbolTable == nil { + // Continue to the next symbol table + return false + } + // If the symbol with this name is present it should refer to the symbol + if symbolFromSymbolTable == symbol { + // No need to qualify + return true + } + + // Qualify if the symbol from symbol table has same meaning as expected + shouldResolveAlias := symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && ast.GetDeclarationOfKind(symbolFromSymbolTable, ast.KindExportSpecifier) == nil + if shouldResolveAlias { + symbolFromSymbolTable = ch.resolveAlias(symbolFromSymbolTable) + } + flags := symbolFromSymbolTable.Flags + if shouldResolveAlias { + flags = ch.getSymbolFlags(symbolFromSymbolTable) + } + if flags&meaning != 0 { + qualify = true + return true + } + + // Continue to the next symbol table + return false + }) + + return qualify +} + +func isPropertyOrMethodDeclarationSymbol(symbol *ast.Symbol) bool { + if len(symbol.Declarations) > 0 { + for _, declaration := range symbol.Declarations { + switch declaration.Kind { + case ast.KindPropertyDeclaration, + ast.KindMethodDeclaration, + ast.KindGetAccessor, + ast.KindSetAccessor: + continue + default: + return false + } + } + return true + } + return false +} + +func (ch *Checker) someSymbolTableInScope( + enclosingDeclaration *ast.Node, + callback func(symbolTable ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, scopeNode *ast.Node) bool, +) bool { + for location := enclosingDeclaration; location != nil; location = location.Parent { + // Locals of a source file are not in scope (because they get merged into the global symbol table) + if canHaveLocals(location) && location.Locals() != nil && !ast.IsGlobalSourceFile(location) { + if callback(location.Locals(), false, true, location) { + return true + } + } + switch location.Kind { + case ast.KindSourceFile, ast.KindModuleDeclaration: + if ast.IsSourceFile(location) && !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) { + break + } + sym := ch.getSymbolOfDeclaration(location) + if callback(sym.Exports, false, true, location) { + return true + } + case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration: + // Type parameters are bound into `members` lists so they can merge across declarations + // This is troublesome, since in all other respects, they behave like locals :cries: + // TODO: the below is shared with similar code in `resolveName` - in fact, rephrasing all this symbol + // lookup logic in terms of `resolveName` would be nice + // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals + // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would + // trigger resolving late-bound names, which we may already be in the process of doing while we're here! + var table ast.SymbolTable + sym := ch.getSymbolOfDeclaration(location) + // TODO: Should this filtered table be cached in some way? + for key, memberSymbol := range sym.Members { + if memberSymbol.Flags&(ast.SymbolFlagsType & ^ast.SymbolFlagsAssignment) != 0 { + table[key] = memberSymbol + } + } + if table != nil && callback(table, false, false, location) { + return true + } + } + } + + return callback(ch.globals, false, true, nil) } /** @@ -27,37 +658,36 @@ func (c *Checker) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *a } func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) printer.SymbolAccessibilityResult { - // if symbol != nil && enclosingDeclaration != nil { - // result := c.isAnySymbolAccessible([]*ast.Symbol{symbol}, enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules) - // if result != nil { - // return result - // } - - // // This could be a symbol that is not exported in the external module - // // or it could be a symbol from different external module that is not aliased and hence cannot be named - // symbolExternalModule := forEach(symbol.Declarations, c.getExternalModuleContainer) - // if symbolExternalModule != nil { - // enclosingExternalModule := c.getExternalModuleContainer(enclosingDeclaration) - // if symbolExternalModule != enclosingExternalModule { - // // name from different external module that is not visible - // return SymbolAccessibilityResult{ - // accessibility: SymbolAccessibilityCannotBeNamed, - // errorSymbolName: c.symbolToString(symbol, enclosingDeclaration, meaning), - // errorModuleName: c.symbolToString(symbolExternalModule), - // errorNode: ifElse(isInJSFile(enclosingDeclaration), enclosingDeclaration, nil), - // } - // } - // } - - // // Just a local name that is not accessible - // return SymbolAccessibilityResult{ - // accessibility: SymbolAccessibilityNotAccessible, - // errorSymbolName: c.symbolToString(symbol, enclosingDeclaration, meaning), - // } - // } - - // return SymbolAccessibilityResult{ - // accessibility: SymbolAccessibilityAccessible, - // } - return printer.SymbolAccessibilityResult{} // !!! + if symbol != nil && enclosingDeclaration != nil { + result := c.IsAnySymbolAccessible([]*ast.Symbol{symbol}, enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules) + if result != nil { + return *result + } + + // This could be a symbol that is not exported in the external module + // or it could be a symbol from different external module that is not aliased and hence cannot be named + symbolExternalModule := core.FirstNonNil(symbol.Declarations, c.getExternalModuleContainer) + if symbolExternalModule != nil { + enclosingExternalModule := c.getExternalModuleContainer(enclosingDeclaration) + if symbolExternalModule != enclosingExternalModule { + // name from different external module that is not visible + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityCannotBeNamed, + ErrorSymbolName: c.symbolToStringEx(symbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind, nil), + ErrorModuleName: c.symbolToString(symbolExternalModule), + ErrorNode: core.IfElse(ast.IsInJSFile(enclosingDeclaration), enclosingDeclaration, nil), + } + } + } + + // Just a local name that is not accessible + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotAccessible, + ErrorSymbolName: c.symbolToStringEx(symbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind, nil), + } + } + + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + } } diff --git a/internal/checker/types.go b/internal/checker/types.go index 4a1424048b..82c22fb1c9 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -268,6 +268,18 @@ type MarkedAssignmentSymbolLinks struct { hasDefiniteAssignment bool // Symbol is definitely assigned somewhere } +type accessibleChainCacheKey struct { + useOnlyExternalAliasing bool + location *ast.Node + meaning ast.SymbolFlags +} + +type ContainingSymbolLinks struct { + extendedContainersByFile map[ast.NodeId][]*ast.Symbol // Symbols of nodes which which logically contain this one, cached by file the request is made within + extendedContainers *[]*ast.Symbol // Containers (other than the parent) which this symbol is aliased in + accessibleChainCache map[accessibleChainCacheKey][]*ast.Symbol +} + type AccessFlags uint32 const ( From 76528fa29373ed8600ae93df761b76addd2bce1d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 21 Apr 2025 13:54:01 -0700 Subject: [PATCH 06/15] Nodebuilder symbol chain lookup logic & symbol manufacturing logic, sans type parameter smuggling and module specifier generation --- internal/checker/nodebuilder.go | 464 ++++++++++++++++++++++++++- internal/modulespecifiers/compare.go | 11 + 2 files changed, 470 insertions(+), 5 deletions(-) create mode 100644 internal/modulespecifiers/compare.go diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 974ae8584f..5e029bcc8d 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -3,13 +3,16 @@ package checker import ( "fmt" "slices" + "strings" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/modulespecifiers" "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/tspath" ) type CompositeSymbolIdentity struct { @@ -358,20 +361,471 @@ func (b *NodeBuilder) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) } func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, expectsIdentifier bool) *ast.Node { - panic("unimplemented") // !!! + chain := b.lookupSymbolChain(symbol, meaning, false) + if expectsIdentifier && len(chain) != 1 && !b.ctx.encounteredError && (b.ctx.flags&nodebuilder.FlagsAllowQualifiedNameInPlaceOfIdentifier != 0) { + b.ctx.encounteredError = true + } + return b.createEntityNameFromSymbolChain(chain, len(chain)-1) +} + +func (b *NodeBuilder) createEntityNameFromSymbolChain(chain []*ast.Symbol, index int) *ast.Node { + // !!! TODO: smuggle type arguments out + // typeParameterNodes := lookupTypeParameterNodes(chain, index, context); + symbol := chain[index] + + if index == 0 { + b.ctx.flags |= nodebuilder.FlagsInInitialEntityName + } + symbolName := b.getNameOfSymbolAsWritten(symbol) + if index == 0 { + b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName + } + + identifier := b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + // expression = identifier; + if index > 0 { + return b.f.NewQualifiedName( + b.createEntityNameFromSymbolChain(chain, index-1), + identifier, + ) + } + return identifier } +// TODO: Audit usages of symbolToEntityNameNode - they should probably all be symbolToName func (b *NodeBuilder) symbolToEntityNameNode(symbol *ast.Symbol) *ast.EntityName { - panic("unimplemented") // !!! + identifier := b.f.NewIdentifier(symbol.Name) + if symbol.Parent != nil { + return b.f.NewQualifiedName(b.symbolToEntityNameNode(symbol.Parent), identifier) + } + return identifier } func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, typeArguments *ast.NodeList) *ast.TypeNode { - panic("unimplemented") // !!! + chain := b.lookupSymbolChain(symbol, mask, (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0)) // If we're using aliases outside the current scope, dont bother with the module + if len(chain) == 0 { + return nil // TODO: shouldn't be possible, `lookupSymbolChain` should always at least return the input symbol and issue an error + } + isTypeOf := mask == ast.SymbolFlagsType + if core.Some(chain[0].Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + // module is root, must use `ImportTypeNode` + var nonRootParts *ast.Node + if len(chain) > 1 { + nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1) + // !!! + } + typeParameterNodes := typeArguments /*|| lookupTypeParameterNodes(chain, 0, context);*/ // !!! TODO: type argument smuggling + contextFile := ast.GetSourceFileOfNode(b.e.MostOriginal(b.ctx.enclosingDeclaration)) // TODO: Just use b.ctx.enclosingFile ? Or is the delayed lookup important for context moves? + targetFile := getSourceFileOfModule(chain[0]) + var specifier string + var attributes *ast.Node + if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { + // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion + if targetFile != nil && b.ch.program.GetEmitModuleFormatOfFile(targetFile) == core.ModuleKindESNext && b.ch.program.GetEmitModuleFormatOfFile(targetFile) != b.ch.program.GetEmitModuleFormatOfFile(contextFile) { + specifier = b.getSpecifierForModuleSymbol(chain[0], core.ModuleKindESNext) + attributes = b.f.NewImportAttributes( + ast.KindWithKeyword, + b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral("import"))}), + false, + ) + } + } + if len(specifier) == 0 { + specifier = b.getSpecifierForModuleSymbol(chain[0], core.ResolutionModeNone) + } + if (b.ctx.flags&nodebuilder.FlagsAllowNodeModulesRelativePaths == 0) /* && b.ch.compilerOptions.GetModuleResolutionKind() != core.ModuleResolutionKindClassic */ && strings.Contains(specifier, "/node_modules/") { + oldSpecifier := specifier + + if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { + // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set + swappedMode := core.ModuleKindESNext + if b.ch.program.GetEmitModuleFormatOfFile(contextFile) == core.ModuleKindESNext { + swappedMode = core.ModuleKindCommonJS + } + specifier = b.getSpecifierForModuleSymbol(chain[0], swappedMode) + + if strings.Contains(specifier, "/node_modules/") { + // Still unreachable :( + specifier = oldSpecifier + } else { + modeStr := "require" + if swappedMode == core.ModuleKindESNext { + modeStr = "import" + } + attributes = b.f.NewImportAttributes( + ast.KindWithKeyword, + b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral(modeStr))}), + false, + ) + } + } + + if attributes == nil { + // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error + // since declaration files with these kinds of references are liable to fail when published :( + b.ctx.encounteredError = true + b.ctx.tracker.ReportLikelyUnsafeImportRequiredError(oldSpecifier) + } + } + + lit := b.f.NewLiteralTypeNode(b.f.NewStringLiteral(specifier)) + b.ctx.approximateLength += len(specifier) + 10 // specifier + import("") + if nonRootParts == nil || ast.IsEntityName(nonRootParts) { + if nonRootParts != nil { + // !!! TODO: smuggle type arguments out + // const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right; + // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); + } + return b.f.NewImportTypeNode(isTypeOf, lit, attributes, nonRootParts, typeParameterNodes) + } + + splitNode := getTopmostIndexedAccessType(nonRootParts.AsIndexedAccessTypeNode()) + qualifier := splitNode.ObjectType.AsTypeReference().TypeName + return b.f.NewIndexedAccessTypeNode( + b.f.NewImportTypeNode(isTypeOf, lit, attributes, qualifier, typeParameterNodes), + splitNode.IndexType, + ) + + } + + entityName := b.createAccessFromSymbolChain(chain, len(chain)-1, 0) + if ast.IsIndexedAccessTypeNode(entityName) { + return entityName // Indexed accesses can never be `typeof` + } + if isTypeOf { + return b.f.NewTypeQueryNode(entityName, nil) + } + // !!! TODO: smuggle type arguments out + // const lastId = isIdentifier(entityName) ? entityName : entityName.right; + // const lastTypeArgs = getIdentifierTypeArguments(lastId); + // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); + return b.f.NewTypeReferenceNode(entityName, nil) +} + +func getTopmostIndexedAccessType(node *ast.IndexedAccessTypeNode) *ast.IndexedAccessTypeNode { + if ast.IsIndexedAccessTypeNode(node.ObjectType) { + return getTopmostIndexedAccessType(node.ObjectType.AsIndexedAccessTypeNode()) + } + return node +} + +func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int, stopper int) *ast.Node { + // !!! TODO: smuggle type arguments out + // const typeParameterNodes = index === (chain.length - 1) ? overrideTypeArguments : lookupTypeParameterNodes(chain, index, context); + symbol := chain[index] + var parent *ast.Symbol + if index > 0 { + parent = chain[index-1] + } + + var symbolName string + if index == 0 { + b.ctx.flags |= nodebuilder.FlagsInInitialEntityName + symbolName = b.getNameOfSymbolAsWritten(symbol) + b.ctx.approximateLength += len(symbolName) + 1 + b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName + } else { + // lookup a ref to symbol within parent to handle export aliases + if parent != nil { + exports := b.ch.getExportsOfSymbol(parent) + if exports != nil { + for name, ex := range exports { + if b.ch.getSymbolIfSameReference(ex, symbol) != nil && !isLateBoundName(name) && name != ast.InternalSymbolNameExportEquals { + symbolName = name + break + } + } + } + } + } + + if len(symbolName) == 0 { + var name *ast.Node + for _, d := range symbol.Declarations { + name = ast.GetNameOfDeclaration(d) + if name != nil { + break + } + } + if name != nil && ast.IsComputedPropertyName(name) && ast.IsEntityName(name.AsComputedPropertyName().Expression) { + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper) + if ast.IsEntityName(lhs) { + return b.f.NewIndexedAccessTypeNode( + b.f.NewParenthesizedTypeNode(b.f.NewTypeQueryNode(lhs, nil)), + b.f.NewTypeQueryNode(name.Expression(), nil), + ) + } + return lhs + } + } + b.ctx.approximateLength += len(symbolName) + 1 + + if (b.ctx.flags&nodebuilder.FlagsForbidIndexedAccessSymbolReferences == 0) && parent != nil && + b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent)[symbol.Name] != nil && + b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent)[symbol.Name], symbol) != nil { + // Should use an indexed access + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper) + if ast.IsIndexedAccessTypeNode(lhs) { + return b.f.NewIndexedAccessTypeNode( + lhs, + b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), + ) + } + return b.f.NewIndexedAccessTypeNode( + b.f.NewTypeReferenceNode(lhs /*!!! todo: type args*/, nil), + b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), + ) + } + + identifier := b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + + if index > stopper { + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper) + if !ast.IsEntityName(lhs) { + panic("Impossible construct - an export of an indexed access cannot be reachable") + } + return b.f.NewQualifiedName(lhs, identifier) + } + + return identifier } func (b *NodeBuilder) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlags) *ast.Expression { - // chain := b.lookupSymbolChain(symbol, meaning) - panic("unimplemented") // !!! + chain := b.lookupSymbolChain(symbol, mask, false) + return b.createExpressionFromSymbolChain(chain, len(chain)-1) +} + +func (b *NodeBuilder) createExpressionFromSymbolChain(chain []*ast.Symbol, index int) *ast.Expression { + // !!! TODO: smuggle type arguments out + // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) + symbol := chain[index] + + if index == 0 { + b.ctx.flags |= nodebuilder.FlagsInInitialEntityName + } + symbolName := b.getNameOfSymbolAsWritten(symbol) + if index == 0 { + b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName + } + + if startsWithSingleOrDoubleQuote(symbolName) && core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + return b.f.NewStringLiteral(b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)) + } + + if index == 0 || canUsePropertyAccess(symbolName, b.ch.languageVersion) { + identifier := b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + if index > 0 { + b.f.NewPropertyAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, identifier, ast.NodeFlagsNone) + } + return identifier + } + + if startsWithSquareBracket(symbolName) { + symbolName = symbolName[1 : len(symbolName)-1] + } + + var expression *ast.Expression + if startsWithSingleOrDoubleQuote(symbolName) && symbol.Flags&ast.SymbolFlagsEnumMember == 0 { + expression = b.f.NewStringLiteral(unquoteString(symbolName)) + } else if jsnum.FromString(symbolName).String() == symbolName { + // TODO: the follwing in strada would assert if the number is negative, but no such assertion exists here + // Moreover, what's even guaranteeing the name *isn't* -1 here anyway? Needs double-checking. + expression = b.f.NewNumericLiteral(symbolName) + } + if expression == nil { + expression = b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(expression, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + // expression = identifier; + } + return b.f.NewElementAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, expression, ast.NodeFlagsNone) +} + +func canUsePropertyAccess(name string, languageVersion core.ScriptTarget) bool { + if len(name) == 0 { + return false + } + // TODO: in strada, this only used `isIdentifierStart` on the first character, while this checks the whole string for validity + // - possible strada bug? + if strings.HasPrefix(name, "#") { + return len(name) > 1 && scanner.IsIdentifierText(name[1:], languageVersion) + } + return scanner.IsIdentifierText(name, languageVersion) +} + +func unquoteString(str string) string { + // strconv.Unquote is insufficient as that only handles a single character inside single quotes, as those are character literals in go + inner := stripQuotes(str) + // In strada we do str.replace(/\\./g, s => s.substring(1)) - which is to say, replace all backslash-something with just something + // That's replicated here faithfully, but it seems wrong! This should probably be an actual unquote operation? + return strings.ReplaceAll(inner, "\\", "") +} + +/** + * Strip off existed surrounding single quotes, double quotes, or backticks from a given string + * + * @return non-quoted string + * + * @internal + */ +func stripQuotes(name string) string { + length := len(name) + if length >= 2 && startsWithSingleOrDoubleQuote(name) && name[0] == name[length-1] { // TODO: in TS this also handles backtick quoted things + return name[1 : len(name)-1] + } + return name +} + +func startsWithSingleOrDoubleQuote(str string) bool { + return strings.HasPrefix(str, "'") || strings.HasPrefix(str, "\"") +} + +func startsWithSquareBracket(str string) bool { + return strings.HasPrefix(str, "[") +} + +/** +* Gets a human-readable name for a symbol. +* Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. +* +* Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. +* It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. + */ +func (b *NodeBuilder) getNameOfSymbolAsWritten(symbol *ast.Symbol) string { + return "" // !!! +} + +func (b *NodeBuilder) lookupTypeParameterNodes(chain []*ast.Symbol, index int) *ast.TypeParameterList { + return nil // !!! TODO: nested reference type parameter synthesis +} + +// TODO: move `lookupSymbolChain` and co to `symbolaccessibility.go` (but getSpecifierForModuleSymbol uses much context which makes that hard?) +func (b *NodeBuilder) lookupSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol { + b.ctx.tracker.TrackSymbol(symbol, b.ctx.enclosingDeclaration, meaning) + return b.lookupSymbolChainWorker(symbol, meaning, yieldModuleSymbol) +} + +func (b *NodeBuilder) lookupSymbolChainWorker(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol { + // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. + var chain []*ast.Symbol + isTypeParameter := symbol.Flags&ast.SymbolFlagsTypeParameter != 0 + if !isTypeParameter && (b.ctx.enclosingDeclaration != nil || b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType != 0) && (b.ctx.internalFlags&nodebuilder.InternalFlagsDoNotIncludeSymbolChain == 0) { + res := b.getSymbolChain(symbol, meaning /*endOfChain*/, true, yieldModuleSymbol) + chain = res + // Debug.checkDefined(chain) // !!! + // Debug.assert(chain && chain.length > 0); // !!! + } else { + chain = append(chain, symbol) + } + return chain +} + +type sortedSymbolNamePair struct { + sym *ast.Symbol + name string +} + +/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ +func (b *NodeBuilder) getSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, endOfChain bool, yieldModuleSymbol bool) []*ast.Symbol { + accessibleSymbolChain := b.ch.getAccessibleSymbolChain(symbol, b.ctx.enclosingDeclaration, meaning, b.ctx.flags&nodebuilder.FlagsUseOnlyExternalAliasing != 0) + qualifierMeaning := meaning + if len(accessibleSymbolChain) > 0 { + qualifierMeaning = getQualifiedLeftMeaning(meaning) + } + if len(accessibleSymbolChain) == 0 || + b.ch.needsQualification(accessibleSymbolChain[0], b.ctx.enclosingDeclaration, qualifierMeaning) { + // Go up and add our parent. + root := symbol + if len(accessibleSymbolChain) > 0 { + root = accessibleSymbolChain[0] + } + parents := b.ch.getContainersOfSymbol(root, b.ctx.enclosingDeclaration, meaning) + if len(parents) > 0 { + parentSpecifiers := core.Map(parents, func(symbol *ast.Symbol) sortedSymbolNamePair { + if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + return sortedSymbolNamePair{symbol, b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)} + } + return sortedSymbolNamePair{symbol, ""} + }) + slices.SortStableFunc(parentSpecifiers, sortByBestName) + for _, pair := range parentSpecifiers { + parent := pair.sym + parentChain := b.getSymbolChain(parent, getQualifiedLeftMeaning(meaning), false, false) + if len(parentChain) > 0 { + if parent.Exports != nil { + exported, ok := parent.Exports[ast.InternalSymbolNameExportEquals] + if ok && b.ch.getSymbolIfSameReference(exported, symbol) != nil { + // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent + // No need to lookup an alias for the symbol in itself + accessibleSymbolChain = parentChain + } + } + nextSyms := accessibleSymbolChain + if len(nextSyms) == 0 { + fallback := b.ch.getAliasForSymbolInContainer(parent, symbol) + if fallback == nil { + fallback = symbol + } + nextSyms = append(nextSyms, fallback) + } + accessibleSymbolChain = append(parentChain, nextSyms...) + break + } + } + } + } + if len(accessibleSymbolChain) > 0 { + return accessibleSymbolChain + } + if + // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. + endOfChain || + // If a parent symbol is an anonymous type, don't write it. + (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) == 0) { + // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) + if !endOfChain && !yieldModuleSymbol && !!core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + return nil + } + return []*ast.Symbol{symbol} + } + return nil +} + +func sortByBestName(a sortedSymbolNamePair, b sortedSymbolNamePair) int { + specifierA := a.name + specifierB := b.name + if len(specifierA) > 0 && len(specifierB) > 0 { + isBRelative := tspath.PathIsRelative(specifierB) + if tspath.PathIsRelative(specifierA) == isBRelative { + // Both relative or both non-relative, sort by number of parts + return modulespecifiers.CountPathComponents(specifierA) - modulespecifiers.CountPathComponents(specifierB) + } + if isBRelative { + // A is non-relative, B is relative: prefer A + return -1 + } + // A is relative, B is non-relative: prefer B + return 1 + } + return 0 +} + +func (b *NodeBuilder) getSpecifierForModuleSymbol(symbol *ast.Symbol, overrideImportMode core.ResolutionMode) string { + return "" // !!! !TODO!: specifier generation } func (b *NodeBuilder) typeParameterToDeclarationWithConstraint(typeParameter *Type, constraintNode *ast.TypeNode) *ast.TypeParameterDeclarationNode { diff --git a/internal/modulespecifiers/compare.go b/internal/modulespecifiers/compare.go new file mode 100644 index 0000000000..c4ecfb7951 --- /dev/null +++ b/internal/modulespecifiers/compare.go @@ -0,0 +1,11 @@ +package modulespecifiers + +import "strings" + +func CountPathComponents(path string) int { + initial := 0 + if strings.HasPrefix(path, "./") { + initial = 2 + } + return strings.Count(path[initial:], "/") +} From b60f514e3a2290d2813ecdcfdb4724aedc49d7cc Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 22 Apr 2025 14:07:51 -0700 Subject: [PATCH 07/15] Last of the core norebuilder functionality --- internal/checker/nodebuilder.go | 623 +++++++++++++++++++++++- internal/checker/nodebuilderapi.go | 10 +- internal/checker/nodebuilderscopes.go | 12 +- internal/checker/symboltracker.go | 57 ++- internal/declarations/tracker.go | 5 + internal/modulespecifiers/compare.go | 4 +- internal/modulespecifiers/specifiers.go | 18 + internal/modulespecifiers/types.go | 7 + internal/nodebuilder/types.go | 1 + 9 files changed, 704 insertions(+), 33 deletions(-) create mode 100644 internal/modulespecifiers/specifiers.go create mode 100644 internal/modulespecifiers/types.go diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 5e029bcc8d..2000e77951 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler/module" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/modulespecifiers" @@ -45,6 +46,9 @@ type NodeBuilderLinks struct { fakeScopeForSignatureDeclaration *string // If present, this is a fake scope injected into an enclosing declaration chain. } +type NodeBuilderSymbolLinks struct { + specifierCache module.ModeAwareCache[string] +} type NodeBuilderContext struct { tracker nodebuilder.SymbolTracker approximateLength int @@ -64,14 +68,15 @@ type NodeBuilderContext struct { reverseMappedStack []*ast.Symbol enclosingSymbolTypes map[ast.SymbolId]*Type suppressReportInferenceFallback bool + remappedSymbolReferences map[ast.SymbolId]*ast.Symbol // per signature scope state - mustCreateTypeParameterSymbolList bool - mustCreateTypeParametersNamesLookups bool - typeParameterNames any - typeParameterNamesByText any - typeParameterNamesByTextNextNameCount any - typeParameterSymbolList any + hasCreatedTypeParameterSymbolList bool + hasCreatedTypeParametersNamesLookups bool + typeParameterNames map[TypeId]*ast.Identifier + typeParameterNamesByText map[string]struct{} + typeParameterNamesByTextNextNameCount map[string]int + typeParameterSymbolList map[int]struct{} } type NodeBuilder struct { @@ -81,7 +86,8 @@ type NodeBuilder struct { e *printer.EmitContext // cache - links core.LinkStore[*ast.Node, NodeBuilderLinks] + links core.LinkStore[*ast.Node, NodeBuilderLinks] + symbolLinks core.LinkStore[*ast.Symbol, NodeBuilderSymbolLinks] // closures typeToTypeNodeClosure func(t *Type) *ast.TypeNode @@ -698,6 +704,40 @@ func startsWithSquareBracket(str string) bool { return strings.HasPrefix(str, "[") } +func isDefaultBindingContext(location *ast.Node) bool { + return location.Kind == ast.KindSourceFile || ast.IsAmbientModule(location) +} + +func (b *NodeBuilder) getNameOfSymbolFromNameType(symbol *ast.Symbol) string { + if b.ch.valueSymbolLinks.Has(symbol) { + nameType := b.ch.valueSymbolLinks.Get(symbol).nameType + if nameType == nil { + return "" + } + if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + var name string + switch v := nameType.AsLiteralType().value.(type) { + case string: + name = v + case jsnum.Number: + name = v.String() + } + if !scanner.IsIdentifierText(name, b.ch.compilerOptions.GetEmitScriptTarget()) && !isNumericLiteralName(name) { + return b.ch.valueToString(nameType.AsLiteralType().value) + } + if isNumericLiteralName(name) && strings.HasPrefix(name, "-") { + return fmt.Sprintf("[%s]", name) + } + return name + } + if nameType.flags&TypeFlagsUniqueESSymbol != 0 { + text := b.getNameOfSymbolAsWritten(nameType.AsUniqueESSymbolType().symbol) + return fmt.Sprintf("[%s]", text) + } + } + return "" +} + /** * Gets a human-readable name for a symbol. * Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. @@ -706,7 +746,60 @@ func startsWithSquareBracket(str string) bool { * It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. */ func (b *NodeBuilder) getNameOfSymbolAsWritten(symbol *ast.Symbol) string { - return "" // !!! + result, ok := b.ctx.remappedSymbolReferences[ast.GetSymbolId(symbol)] + if ok { + symbol = result + } + if symbol.Name == ast.InternalSymbolNameDefault && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0) && + // If it's not the first part of an entity name, it must print as `default` + ((b.ctx.flags&nodebuilder.FlagsInInitialEntityName == 0) || + // if the symbol is synthesized, it will only be referenced externally it must print as `default` + len(symbol.Declarations) == 0 || + // if not in the same binding context (source file, module declaration), it must print as `default` + (b.ctx.enclosingDeclaration != nil && ast.FindAncestor(symbol.Declarations[0], isDefaultBindingContext) != ast.FindAncestor(b.ctx.enclosingDeclaration, isDefaultBindingContext))) { + return "default" + } + if len(symbol.Declarations) > 0 { + declaration := core.FirstNonNil(symbol.Declarations, ast.GetNameOfDeclaration) // Try using a declaration with a name, first + if declaration != nil { + name := ast.GetNameOfDeclaration(declaration) + if name != nil { + // !!! TODO: JS Object.defineProperty declarations + // if ast.IsCallExpression(declaration) && ast.IsBindableObjectDefinePropertyCall(declaration) { + // return symbol.Name + // } + if ast.IsComputedPropertyName(name) && symbol.CheckFlags&ast.CheckFlagsLate == 0 { + if b.ch.valueSymbolLinks.Has(symbol) && b.ch.valueSymbolLinks.Get(symbol).nameType != nil && b.ch.valueSymbolLinks.Get(symbol).nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + result := b.getNameOfSymbolFromNameType(symbol) + if len(result) > 0 { + return result + } + } + } + return scanner.DeclarationNameToString(name) + } + } + declaration = symbol.Declarations[0] // Declaration may be nameless, but we'll try anyway + if declaration.Parent != nil && declaration.Parent.Kind == ast.KindVariableDeclaration { + return scanner.DeclarationNameToString(declaration.Parent.AsVariableDeclaration().Name()) + } + if ast.IsClassExpression(declaration) || ast.IsFunctionExpression(declaration) || ast.IsArrowFunction(declaration) { + if b.ctx != nil && !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 { + b.ctx.encounteredError = true + } + switch declaration.Kind { + case ast.KindClassExpression: + return "(Anonymous class)" + case ast.KindFunctionExpression, ast.KindArrowFunction: + return "(Anonymous function)" + } + } + } + name := b.getNameOfSymbolFromNameType(symbol) + if len(name) > 0 { + return name + } + return symbol.Name } func (b *NodeBuilder) lookupTypeParameterNodes(chain []*ast.Symbol, index int) *ast.TypeParameterList { @@ -824,36 +917,530 @@ func sortByBestName(a sortedSymbolNamePair, b sortedSymbolNamePair) int { return 0 } +func isAmbientModuleSymbolName(s string) bool { + return strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") +} + +func canHaveModuleSpecifier(node *ast.Node) bool { + if node == nil { + return false + } + switch node.Kind { + case ast.KindVariableDeclaration, + ast.KindBindingElement, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindImportEqualsDeclaration, + ast.KindImportClause, + ast.KindNamespaceExport, + ast.KindNamespaceImport, + ast.KindExportSpecifier, + ast.KindImportSpecifier, + ast.KindImportType: + return true + } + return false +} + +func tryGetModuleSpecifierFromDeclaration(node *ast.Node) *ast.Node { + res := tryGetModuleSpecifierFromDeclarationWorker(node) + if !ast.IsStringLiteral(res) { + return nil + } + return res +} + +func tryGetModuleSpecifierFromDeclarationWorker(node *ast.Node) *ast.Node { + switch node.Kind { + case ast.KindVariableDeclaration, ast.KindBindingElement: + requireCall := ast.FindAncestor(node.Initializer(), func(node *ast.Node) bool { + return ast.IsRequireCall(node /*requireStringLiteralLikeArgument*/, true) + }) + if requireCall == nil { + return nil + } + return requireCall.AsCallExpression().Arguments.Nodes[0] + case ast.KindImportDeclaration: + return node.AsImportDeclaration().ModuleSpecifier + case ast.KindExportDeclaration: + return node.AsExportDeclaration().ModuleSpecifier + case ast.KindJSDocImportTag: + return node.AsJSDocImportTag().ModuleSpecifier + case ast.KindImportEqualsDeclaration: + ref := node.AsImportEqualsDeclaration().ModuleReference + if ref.Kind != ast.KindExternalModuleReference { + return nil + } + return ref.AsExternalModuleReference().Expression + case ast.KindImportClause: + if ast.IsImportDeclaration(node.Parent.Parent) { + return node.Parent.AsImportDeclaration().ModuleSpecifier + } + return node.Parent.AsJSDocImportTag().ModuleSpecifier + case ast.KindNamespaceExport: + return node.Parent.AsExportDeclaration().ModuleSpecifier + case ast.KindNamespaceImport: + if ast.IsImportDeclaration(node.Parent.Parent) { + return node.Parent.Parent.AsImportDeclaration().ModuleSpecifier + } + return node.Parent.Parent.AsJSDocImportTag().ModuleSpecifier + case ast.KindExportSpecifier: + return node.Parent.Parent.AsExportDeclaration().ModuleSpecifier + case ast.KindImportSpecifier: + if ast.IsImportDeclaration(node.Parent.Parent.Parent) { + return node.Parent.Parent.Parent.AsImportDeclaration().ModuleSpecifier + } + return node.Parent.Parent.Parent.AsJSDocImportTag().ModuleSpecifier + case ast.KindImportType: + if ast.IsLiteralImportTypeNode(node) { + return node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal + } + return nil + default: + // Debug.assertNever(node); // !!! + return nil + } +} + func (b *NodeBuilder) getSpecifierForModuleSymbol(symbol *ast.Symbol, overrideImportMode core.ResolutionMode) string { - return "" // !!! !TODO!: specifier generation + file := ast.GetDeclarationOfKind(symbol, ast.KindSourceFile) + if file == nil { + equivalentSymbol := core.FirstNonNil(symbol.Declarations, func(d *ast.Node) *ast.Symbol { + return b.ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol) + }) + if equivalentSymbol != nil { + file = ast.GetDeclarationOfKind(equivalentSymbol, ast.KindSourceFile) + } + } + + if file == nil { + if isAmbientModuleSymbolName(symbol.Name) { + return stripQuotes(symbol.Name) + } + } + if b.ctx.enclosingFile == nil || b.ctx.tracker.GetModuleSpecifierGenerationHost() == nil { + if isAmbientModuleSymbolName(symbol.Name) { + return stripQuotes(symbol.Name) + } + return ast.GetSourceFileOfNode(getNonAugmentationDeclaration(symbol)).FileName() + } + + enclosingDeclaration := b.e.MostOriginal(b.ctx.enclosingDeclaration) + var originalModuleSpecifier *ast.Node + if canHaveModuleSpecifier(enclosingDeclaration) { + originalModuleSpecifier = tryGetModuleSpecifierFromDeclaration(enclosingDeclaration) + } + contextFile := b.ctx.enclosingFile + resolutionMode := core.ResolutionModeNone + if overrideImportMode != core.ResolutionModeNone || originalModuleSpecifier != nil { + // !!! import resolution mode support + //resolutionMode = b.ch.host.GetModeForUsageLocation(contextFile, originalModuleSpecifier) + } else if contextFile != nil { + // !!! import resolution mode support + //resolutionMode = b.ch.host.GetDefaultResolutionModeForFile(contextFile) + } + cacheKey := module.ModeAwareCacheKey{Name: string(contextFile.Path()), Mode: resolutionMode} + links := b.symbolLinks.Get(symbol) + if links.specifierCache == nil { + links.specifierCache = make(module.ModeAwareCache[string]) + } + result, ok := links.specifierCache[cacheKey] + if ok { + return result + } + isBundle := len(b.ch.compilerOptions.OutFile) > 0 + // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports, + // just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this + // using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative + // specifier preference + host := b.ctx.tracker.GetModuleSpecifierGenerationHost() + specifierCompilerOptions := b.ch.compilerOptions + if isBundle { + // !!! relies on option cloning and specifier host implementation + // specifierCompilerOptions = &core.CompilerOptions{BaseUrl: host.GetCommonSourceDirectory()} + // TODO: merge with b.ch.compilerOptions + } + allSpecifiers := modulespecifiers.GetModuleSpecifiers( + symbol, + b.ch, + specifierCompilerOptions, + contextFile, + host, + nil, + nil, + // !!! pass options when unstubbed + // { + // importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative", + // importModuleSpecifierEnding: isBundle ? "minimal" + // : resolutionMode === ModuleKind.ESNext ? "js" + // : undefined, + // }, + // { overrideImportMode }, + ) + specifier := allSpecifiers[0] + links.specifierCache[cacheKey] = specifier + return specifier } func (b *NodeBuilder) typeParameterToDeclarationWithConstraint(typeParameter *Type, constraintNode *ast.TypeNode) *ast.TypeParameterDeclarationNode { - panic("unimplemented") // !!! + restoreFlags := b.saveRestoreFlags() + b.ctx.flags &= ^nodebuilder.FlagsWriteTypeParametersInQualifiedName // Avoids potential infinite loop when building for a claimspace with a generic + modifiers := ast.CreateModifiersFromModifierFlags(b.ch.getTypeParameterModifiers(typeParameter), b.f.NewModifier) + var modifiersList *ast.ModifierList + if len(modifiers) > 0 { + modifiersList = b.f.NewModifierList(modifiers) + } + name := b.typeParameterToName(typeParameter) + defaultParameter := b.ch.getDefaultFromTypeParameter(typeParameter) + var defaultParameterNode *ast.Node + if defaultParameter != nil { + defaultParameterNode = b.typeToTypeNodeClosure(defaultParameter) + } + restoreFlags() + return b.f.NewTypeParameterDeclaration( + modifiersList, + name.AsNode(), + constraintNode, + defaultParameterNode, + ) +} + +/** +* Unlike the utilities `setTextRange`, this checks if the `location` we're trying to set on `range` is within the +* same file as the active context. If not, the range is not applied. This prevents us from copying ranges across files, +* which will confuse the node printer (as it assumes all node ranges are within the current file). +* Additionally, if `range` _isn't synthetic_, or isn't in the current file, it will _copy_ it to _remove_ its' position +* information. +* +* It also calls `setOriginalNode` to setup a `.original` pointer, since you basically *always* want these in the node builder. + */ +func (b *NodeBuilder) setTextRange(range_ *ast.Node, location *ast.Node) *ast.Node { + if range_ == nil { + return range_ + } + if !ast.NodeIsSynthesized(range_) || (range_.Flags&ast.NodeFlagsSynthesized == 0) || b.ctx.enclosingFile == nil || b.ctx.enclosingFile != ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) { + range_ = range_.Clone(b.f) // if `range` is synthesized or originates in another file, copy it so it definitely has synthetic positions + } + if range_ == location || location == nil { + return range_ + } + // Don't overwrite the original node if `range` has an `original` node that points either directly or indirectly to `location` + original := b.e.Original(range_) + for original != nil && original != location { + original = b.e.Original(original) + } + if original == nil { + b.e.SetOriginal(range_, location) + } + + // only set positions if range comes from the same file since copying text across files isn't supported by the emitter + if b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) { + range_.Loc = location.Loc + return range_ + } + return range_ +} + +func (b *NodeBuilder) typeParameterShadowsOtherTypeParameterInScope(name string, typeParameter *Type) bool { + result := b.ch.resolveName(b.ctx.enclosingDeclaration, name, ast.SymbolFlagsType, nil, false, false) + if result != nil && result.Flags&ast.SymbolFlagsTypeParameter != 0 { + return result != typeParameter.symbol + } + return false } func (b *NodeBuilder) typeParameterToName(typeParameter *Type) *ast.Identifier { - panic("unimplemented") // !!! + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.ctx.typeParameterNames != nil { + cached, ok := b.ctx.typeParameterNames[typeParameter.id] + if ok { + return cached + } + } + result := b.symbolToName(typeParameter.symbol, ast.SymbolFlagsType /*expectsIdentifier*/, true) + if !ast.IsIdentifier(result) { + return b.f.NewIdentifier("(Missing type parameter)").AsIdentifier() + } + if typeParameter.symbol != nil && len(typeParameter.symbol.Declarations) > 0 { + decl := typeParameter.symbol.Declarations[0] + if decl != nil && ast.IsTypeParameterDeclaration(decl) { + result = b.setTextRange(result, decl.Name()) + } + } + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 { + if !b.ctx.hasCreatedTypeParametersNamesLookups { + b.ctx.hasCreatedTypeParametersNamesLookups = true + b.ctx.typeParameterNames = make(map[TypeId]*ast.Identifier) + b.ctx.typeParameterNamesByText = make(map[string]struct{}) + b.ctx.typeParameterNamesByTextNextNameCount = make(map[string]int) + } + + rawText := result.AsIdentifier().Text + i := 0 + cached, ok := b.ctx.typeParameterNamesByTextNextNameCount[rawText] + if ok { + i = cached + } + text := rawText + + for true { + _, present := b.ctx.typeParameterNamesByText[text] + if !present && !b.typeParameterShadowsOtherTypeParameterInScope(text, typeParameter) { + break + } + i++ + text = fmt.Sprintf("%s_%d", rawText, i) + } + + if text != rawText { + // !!! TODO: smuggle type arguments out + // const typeArguments = getIdentifierTypeArguments(result); + result = b.f.NewIdentifier(text) + // setIdentifierTypeArguments(result, typeArguments); + } + + // avoiding iterations of the above loop turns out to be worth it when `i` starts to get large, so we cache the max + // `i` we've used thus far, to save work later + b.ctx.typeParameterNamesByTextNextNameCount[rawText] = i + b.ctx.typeParameterNames[typeParameter.id] = result.AsIdentifier() + b.ctx.typeParameterNamesByText[text] = struct{}{} + } + + return result.AsIdentifier() +} + +func (b *NodeBuilder) isMappedTypeHomomorphic(mapped *Type) bool { + return b.ch.getHomomorphicTypeVariable(mapped) != nil +} + +func (b *NodeBuilder) isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped *MappedType) bool { + return mapped.target != nil && !b.isMappedTypeHomomorphic(mapped.AsType()) && b.isMappedTypeHomomorphic(mapped.target) } func (b *NodeBuilder) createMappedTypeNodeFromType(type_ *Type) *ast.TypeNode { - panic("unimplemented") // !!! + // Debug.assert(!!(type.flags & TypeFlags.Object)); // !!! + mapped := type_.AsMappedType() + var readonlyToken *ast.Node + if mapped.declaration.ReadonlyToken != nil { + readonlyToken = b.f.NewToken(ast.KindReadonlyKeyword) + } + var questionToken *ast.Node + if mapped.declaration.QuestionToken != nil { + readonlyToken = b.f.NewToken(ast.KindQuestionToken) + } + var appropriateConstraintTypeNode *ast.Node + var newTypeVariable *ast.Node + + // If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do + needsModifierPreservingWrapper := !b.ch.isMappedTypeWithKeyofConstraintDeclaration(type_) && + b.ch.getModifiersTypeFromMappedType(type_).flags&TypeFlagsUnknown == 0 && + b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && + !(b.ch.getConstraintTypeFromMappedType(type_).flags&TypeFlagsTypeParameter != 0 && b.ch.getConstraintOfTypeParameter(b.ch.getConstraintTypeFromMappedType(type_)).flags&TypeFlagsIndex != 0) + + if b.ch.isMappedTypeWithKeyofConstraintDeclaration(type_) { + // We have a { [P in keyof T]: X } + // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType` + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) { + newParam := b.ch.newTypeParameter( + b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"), + ) + name := b.typeParameterToName(newParam) + newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil) + } + indexTarget := newTypeVariable + if indexTarget == nil { + indexTarget = b.typeToTypeNodeClosure(b.ch.getModifiersTypeFromMappedType(type_)) + } + appropriateConstraintTypeNode = b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTarget) + } else if needsModifierPreservingWrapper { + // So, step 1: new type variable + newParam := b.ch.newTypeParameter( + b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"), + ) + name := b.typeParameterToName(newParam) + newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil) + // step 2: make that new type variable itself the constraint node, making the mapped type `{[K in T_1]: Template}` + appropriateConstraintTypeNode = newTypeVariable + } else { + appropriateConstraintTypeNode = b.typeToTypeNodeClosure(b.ch.getConstraintTypeFromMappedType(type_)) + } + + typeParameterNode := b.typeParameterToDeclarationWithConstraint(b.ch.getTypeParameterFromMappedType(type_), appropriateConstraintTypeNode) + var nameTypeNode *ast.Node + if mapped.declaration.NameType != nil { + nameTypeNode = b.typeToTypeNodeClosure(b.ch.getNameTypeFromMappedType(type_)) + } + templateTypeNode := b.typeToTypeNodeClosure(b.ch.removeMissingType( + b.ch.getTemplateTypeFromMappedType(type_), + getMappedTypeModifiers(type_)&MappedTypeModifiersIncludeOptional != 0, + )) + result := b.f.NewMappedTypeNode( + readonlyToken, + typeParameterNode, + nameTypeNode, + questionToken, + templateTypeNode, + nil, + ) + b.e.AddEmitFlags(result, printer.EFSingleLine) + + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) { + // homomorphic mapped type with a non-homomorphic naive inlining + // wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting + // type stays homomorphic + + rawConstraintTypeFromDeclaration := b.getTypeFromTypeNode(mapped.declaration.TypeParameter.AsTypeParameter().Constraint.AsTypeOperatorNode().Type, false) + if rawConstraintTypeFromDeclaration != nil { + rawConstraintTypeFromDeclaration = b.ch.getConstraintOfTypeParameter(rawConstraintTypeFromDeclaration) + } + if rawConstraintTypeFromDeclaration == nil { + rawConstraintTypeFromDeclaration = b.ch.unknownType + } + originalConstraint := b.ch.instantiateType(rawConstraintTypeFromDeclaration, mapped.mapper) + + var originalConstraintNode *ast.Node + if originalConstraint.flags&TypeFlagsUnknown != 0 { + originalConstraintNode = b.typeToTypeNodeClosure(originalConstraint) + } + + return b.f.NewConditionalTypeNode( + b.typeToTypeNodeClosure(b.ch.getModifiersTypeFromMappedType(type_)), + b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), originalConstraintNode, nil)), + result, + b.f.NewKeywordTypeNode(ast.KindNeverKeyword), + ) + } else if needsModifierPreservingWrapper { + // and step 3: once the mapped type is reconstructed, create a `ConstraintType extends infer T_1 extends keyof ModifiersType ? {[K in T_1]: Template} : never` + // subtly different from the `keyof` constraint case, by including the `keyof` constraint on the `infer` type parameter, it doesn't rely on the constraint type being itself + // constrained to a `keyof` type to preserve its modifier-preserving behavior. This is all basically because we preserve modifiers for a wider set of mapped types than + // just homomorphic ones. + return b.f.NewConditionalTypeNode( + b.typeToTypeNodeClosure(b.ch.getConstraintTypeFromMappedType(type_)), + b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, b.typeToTypeNodeClosure(b.ch.getModifiersTypeFromMappedType(type_))), nil)), + result, + b.f.NewKeywordTypeNode(ast.KindNeverKeyword), + ) + } + + return result } func (b *NodeBuilder) typePredicateToTypePredicateNode(predicate *TypePredicate) *ast.Node { - panic("unimplemented") // !!! + var assertsModifier *ast.Node + if predicate.kind == TypePredicateKindAssertsIdentifier || predicate.kind == TypePredicateKindAssertsThis { + assertsModifier = b.f.NewToken(ast.KindAssertsKeyword) + } + var parameterName *ast.Node + if predicate.kind == TypePredicateKindIdentifier || predicate.kind == TypePredicateKindAssertsIdentifier { + parameterName = b.f.NewIdentifier(predicate.parameterName) + b.e.AddEmitFlags(parameterName, printer.EFNoAsciiEscaping) + } else { + parameterName = b.f.NewKeywordExpression(ast.KindThisKeyword) + } + var typeNode *ast.Node + if predicate.t != nil { + typeNode = b.typeToTypeNodeClosure(predicate.t) + } + return b.f.NewTypePredicateNode( + assertsModifier, + parameterName, + typeNode, + ) +} + +func (b *NodeBuilder) typeToTypeNodeHelperWithPossibleReusableTypeNode(type_ *Type, typeNode *ast.TypeNode) *ast.TypeNode { + if type_ == nil { + return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + if typeNode != nil && b.getTypeFromTypeNode(typeNode, false) == type_ { + reused := b.tryReuseExistingTypeNodeHelper(typeNode) + if reused != nil { + return reused + } + } + return b.typeToTypeNodeClosure(type_) } func (b *NodeBuilder) typeParameterToDeclaration(parameter *Type) *ast.Node { - panic("unimplemented") // !!! + constraint := b.ch.getConstraintOfTypeParameter(parameter) + var constraintNode *ast.Node + if constraint != nil { + constraintNode = b.typeToTypeNodeHelperWithPossibleReusableTypeNode(constraint, b.ch.getConstraintDeclaration(parameter)) + } + return b.typeParameterToDeclarationWithConstraint(parameter, constraintNode) } -func (b *NodeBuilder) symbolToTypeParameterDeclarations(symbol *ast.Symbol) *ast.Node { - panic("unimplemented") // !!! +func (b *NodeBuilder) symbolToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node { + return b.typeParametersToTypeParameterDeclarations(symbol) } -func (b *NodeBuilder) symbolToParameterDeclaration(symbol *ast.Symbol, preserveModifierFlags bool) *ast.Node { - panic("unimplemented") // !!! +func (b *NodeBuilder) typeParametersToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node { + targetSymbol := b.ch.getTargetSymbol(symbol) + if targetSymbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsAlias) != 0 { + var results []*ast.Node + params := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) + for _, param := range params { + results = append(results, b.typeParameterToDeclaration(param)) + } + return results + } + return nil +} + +func getEffectiveParameterDeclaration(symbol *ast.Symbol) *ast.Node { + parameterDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindParameter) + if parameterDeclaration != nil { + return parameterDeclaration + } + if symbol.Flags&ast.SymbolFlagsTransient == 0 { + return ast.GetDeclarationOfKind(symbol, ast.KindJSDocParameterTag) + } + return nil +} + +func (b *NodeBuilder) symbolToParameterDeclaration(parameterSymbol *ast.Symbol, preserveModifierFlags bool) *ast.Node { + parameterDeclaration := getEffectiveParameterDeclaration(parameterSymbol) + + parameterType := b.ch.getTypeOfSymbol(parameterSymbol) + parameterTypeNode := b.serializeTypeForDeclaration(parameterDeclaration, parameterType, parameterSymbol) + var modifiers *ast.ModifierList + if b.ctx.flags&nodebuilder.FlagsOmitParameterModifiers == 0 && preserveModifierFlags && parameterDeclaration != nil && ast.CanHaveModifiers(parameterDeclaration) { + originals := core.Filter(parameterDeclaration.Modifiers().Nodes, ast.IsModifier) + clones := core.Map(originals, func(node *ast.Node) *ast.Node { return node.Clone(b.f) }) + if len(clones) > 0 { + modifiers = b.f.NewModifierList(clones) + } + } + isRest := parameterDeclaration != nil && isRestParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsRestParameter != 0 + var dotDotDotToken *ast.Node + if isRest { + dotDotDotToken = b.f.NewToken(ast.KindDotDotDotToken) + } + name := b.parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration) + // TODO: isOptionalParameter on emit resolver here is silly - hoist to checker and reexpose on emit resolver? + isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver(nil, true).isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0 + var questionToken *ast.Node + if isOptional { + questionToken = b.f.NewToken(ast.KindQuestionToken) + } + + parameterNode := b.f.NewParameterDeclaration( + modifiers, + dotDotDotToken, + name, + questionToken, + parameterTypeNode, + /*initializer*/ nil, + ) + b.ctx.approximateLength += len(parameterSymbol.Name) + 3 + return parameterNode +} + +func (b *NodeBuilder) parameterToParameterDeclarationName(parameterSymbol *ast.Symbol, parameterDeclaration *ast.Node) *ast.Node { + if parameterDeclaration == nil || parameterDeclaration.Name() == nil { + return b.f.NewIdentifier(parameterSymbol.Name) + } + // !!! TODO - symbol tracking of computed names in cloned binding patterns, set singleline emit flags + return b.f.DeepCloneNode(parameterDeclaration.Name()) } func (b *NodeBuilder) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable) []*ast.Node { diff --git a/internal/checker/nodebuilderapi.go b/internal/checker/nodebuilderapi.go index 62384e8359..26c6815cbf 100644 --- a/internal/checker/nodebuilderapi.go +++ b/internal/checker/nodebuilderapi.go @@ -16,7 +16,7 @@ type NodeBuilderInterface interface { signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node @@ -47,6 +47,10 @@ func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags node mapper: nil, reverseMappedStack: make([]*ast.Symbol, 0), } + if tracker == nil { + tracker = NewSymbolTrackerImpl(b.impl.ctx, nil) + b.impl.ctx.tracker = tracker + } b.impl.initializeClosures() // recapture ctx b.ctxStack = append(b.ctxStack, b.impl.ctx) } @@ -150,9 +154,9 @@ func (b NodeBuilderAPI) symbolToParameterDeclaration(symbol *ast.Symbol, enclosi } // symbolToTypeParameterDeclarations implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +func (b *NodeBuilderAPI) symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.symbolToTypeParameterDeclarations(symbol)) + return b.exitContextSlice(b.impl.symbolToTypeParameterDeclarations(symbol)) } // typeParameterToDeclaration implements NodeBuilderInterface. diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go index 32b30009ee..3d018b12c6 100644 --- a/internal/checker/nodebuilderscopes.go +++ b/internal/checker/nodebuilderscopes.go @@ -19,10 +19,10 @@ func cloneNodeBuilderContext(context *NodeBuilderContext) func() { // we write it out like that, rather than as // export const x: (x: T) => T // export const y: (x: T_1) => T_1 - oldMustCreateTypeParameterSymbolList := context.mustCreateTypeParameterSymbolList - oldMustCreateTypeParametersNamesLookups := context.mustCreateTypeParametersNamesLookups - context.mustCreateTypeParameterSymbolList = true - context.mustCreateTypeParametersNamesLookups = true + oldMustCreateTypeParameterSymbolList := context.hasCreatedTypeParameterSymbolList + oldMustCreateTypeParametersNamesLookups := context.hasCreatedTypeParametersNamesLookups + context.hasCreatedTypeParameterSymbolList = false + context.hasCreatedTypeParametersNamesLookups = false oldTypeParameterNames := context.typeParameterNames oldTypeParameterNamesByText := context.typeParameterNamesByText oldTypeParameterNamesByTextNextNameCount := context.typeParameterNamesByTextNextNameCount @@ -32,8 +32,8 @@ func cloneNodeBuilderContext(context *NodeBuilderContext) func() { context.typeParameterNamesByText = oldTypeParameterNamesByText context.typeParameterNamesByTextNextNameCount = oldTypeParameterNamesByTextNextNameCount context.typeParameterSymbolList = oldTypeParameterSymbolList - context.mustCreateTypeParameterSymbolList = oldMustCreateTypeParameterSymbolList - context.mustCreateTypeParametersNamesLookups = oldMustCreateTypeParametersNamesLookups + context.hasCreatedTypeParameterSymbolList = oldMustCreateTypeParameterSymbolList + context.hasCreatedTypeParametersNamesLookups = oldMustCreateTypeParametersNamesLookups } } diff --git a/internal/checker/symboltracker.go b/internal/checker/symboltracker.go index 4945b6036d..7abd4a3625 100644 --- a/internal/checker/symboltracker.go +++ b/internal/checker/symboltracker.go @@ -6,23 +6,37 @@ import ( ) type SymbolTrackerImpl struct { - context NodeBuilderContext + context *NodeBuilderContext inner nodebuilder.SymbolTracker DisableTrackSymbol bool } -func NewSymbolTrackerImpl(context NodeBuilderContext, tracker nodebuilder.SymbolTracker) *SymbolTrackerImpl { - // TODO: unwrap `tracker` before setting `inner` - return &SymbolTrackerImpl{context, tracker, false} +func NewSymbolTrackerImpl(context *NodeBuilderContext, tracker nodebuilder.SymbolTracker) *SymbolTrackerImpl { + var inner nodebuilder.SymbolTracker + if tracker != nil { + inner = tracker.GetInnerSymbolTracker() + if inner == nil { + inner = tracker + } + } + + return &SymbolTrackerImpl{context, inner, false} } func (this *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { + if this.inner == nil { + return nil + } return this.inner.GetModuleSpecifierGenerationHost() } +func (this *SymbolTrackerImpl) GetInnerSymbolTracker() nodebuilder.SymbolTracker { + return this.inner +} + func (this *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { if !this.DisableTrackSymbol { - if this.inner.TrackSymbol(symbol, enclosingDeclaration, meaning) { + if this.inner != nil && this.inner.TrackSymbol(symbol, enclosingDeclaration, meaning) { this.onDiagnosticReported() return true } @@ -36,41 +50,65 @@ func (this *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclarat func (this *SymbolTrackerImpl) ReportInaccessibleThisError() { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportInaccessibleThisError() } func (this *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportPrivateInBaseOfClassExpression(propertyName) } func (this *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportInaccessibleUniqueSymbolError() } func (this *SymbolTrackerImpl) ReportCyclicStructureError() { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportCyclicStructureError() } func (this *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportLikelyUnsafeImportRequiredError(specifier) } func (this *SymbolTrackerImpl) ReportTruncationError() { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportTruncationError() } func (this *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportNonlocalAugmentation(containingFile, parentSymbol, augmentingSymbol) } func (this *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) { this.onDiagnosticReported() + if this.inner == nil { + return + } this.inner.ReportNonSerializableProperty(propertyName) } @@ -79,13 +117,22 @@ func (this *SymbolTrackerImpl) onDiagnosticReported() { } func (this *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) { + if this.inner == nil { + return + } this.inner.ReportInferenceFallback(node) } func (this *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) { + if this.inner == nil { + return + } this.inner.PushErrorFallbackNode(node) } func (this *SymbolTrackerImpl) PopErrorFallbackNode() { + if this.inner == nil { + return + } this.inner.PopErrorFallbackNode() } diff --git a/internal/declarations/tracker.go b/internal/declarations/tracker.go index e50b50ff31..f6c0615d42 100644 --- a/internal/declarations/tracker.go +++ b/internal/declarations/tracker.go @@ -5,6 +5,7 @@ import ( "github.com/microsoft/typescript-go/internal/checker" "github.com/microsoft/typescript-go/internal/compiler/diagnostics" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" ) @@ -20,6 +21,10 @@ func (s *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { panic("unimplemented") } +func (s *SymbolTrackerImpl) GetInnerSymbolTracker() nodebuilder.SymbolTracker { + return nil +} + // PopErrorFallbackNode implements checker.SymbolTracker. func (s *SymbolTrackerImpl) PopErrorFallbackNode() { s.fallbackStack = s.fallbackStack[:len(s.fallbackStack)-1] diff --git a/internal/modulespecifiers/compare.go b/internal/modulespecifiers/compare.go index c4ecfb7951..bcb6a27141 100644 --- a/internal/modulespecifiers/compare.go +++ b/internal/modulespecifiers/compare.go @@ -1,6 +1,8 @@ package modulespecifiers -import "strings" +import ( + "strings" +) func CountPathComponents(path string) int { initial := 0 diff --git a/internal/modulespecifiers/specifiers.go b/internal/modulespecifiers/specifiers.go new file mode 100644 index 0000000000..4ae905f93d --- /dev/null +++ b/internal/modulespecifiers/specifiers.go @@ -0,0 +1,18 @@ +package modulespecifiers + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" +) + +func GetModuleSpecifiers( + moduleSymbol *ast.Symbol, + checker CheckerShape, + compilerOptions *core.CompilerOptions, + importingSourceFile *ast.SourceFile, + host any, + userPreferences *UserPreferences, + options *ModuleSpecifierOptions, +) []string { + return nil // !!! +} diff --git a/internal/modulespecifiers/types.go b/internal/modulespecifiers/types.go new file mode 100644 index 0000000000..88818469d3 --- /dev/null +++ b/internal/modulespecifiers/types.go @@ -0,0 +1,7 @@ +package modulespecifiers + +type CheckerShape interface{} + +type UserPreferences struct{} + +type ModuleSpecifierOptions struct{} diff --git a/internal/nodebuilder/types.go b/internal/nodebuilder/types.go index 107d3e7de4..5b92bc869f 100644 --- a/internal/nodebuilder/types.go +++ b/internal/nodebuilder/types.go @@ -6,6 +6,7 @@ import "github.com/microsoft/typescript-go/internal/ast" // TODO: previously all symboltracker methods were optional, but now they're required. type SymbolTracker interface { GetModuleSpecifierGenerationHost() any // !!! + GetInnerSymbolTracker() SymbolTracker TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool ReportInaccessibleThisError() From c92cf8e40455a86fd10efe1608ead66f8ec9917d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 22 Apr 2025 14:20:46 -0700 Subject: [PATCH 08/15] Fix inverted typeof usage condition --- internal/checker/nodebuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 2000e77951..1dab8c5332 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -416,7 +416,7 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, if len(chain) == 0 { return nil // TODO: shouldn't be possible, `lookupSymbolChain` should always at least return the input symbol and issue an error } - isTypeOf := mask == ast.SymbolFlagsType + isTypeOf := mask == ast.SymbolFlagsValue if core.Some(chain[0].Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { // module is root, must use `ImportTypeNode` var nonRootParts *ast.Node From bcf20a793202ae1e70d5fe9551127d6d30a07847 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 22 Apr 2025 14:25:14 -0700 Subject: [PATCH 09/15] Add some missing map initialization checks --- internal/checker/nodebuilder.go | 3 +++ internal/checker/nodebuilderapi.go | 34 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 1dab8c5332..e5d2572360 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -2557,6 +2557,9 @@ func (b *NodeBuilder) visitAndTransformType(t *Type, transform func(t *Type) *as addedLength := b.ctx.approximateLength - startLength if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError { links := b.links.Get(b.ctx.enclosingDeclaration) + if links.serializedTypes == nil { + links.serializedTypes = make(map[CompositeTypeCacheIdentity]*SerializedTypeEntry) + } links.serializedTypes[key] = &SerializedTypeEntry{ node: result, truncating: b.ctx.truncating, diff --git a/internal/checker/nodebuilderapi.go b/internal/checker/nodebuilderapi.go index 26c6815cbf..e8fbd85716 100644 --- a/internal/checker/nodebuilderapi.go +++ b/internal/checker/nodebuilderapi.go @@ -30,22 +30,24 @@ type NodeBuilderAPI struct { func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) { b.impl.ctx = &NodeBuilderContext{ - tracker: tracker, - approximateLength: 0, - encounteredError: false, - truncating: false, - reportedDiagnostic: false, - flags: flags, - internalFlags: internalFlags, - depth: 0, - enclosingDeclaration: enclosingDeclaration, - enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration), - inferTypeParameters: make([]*Type, 0), - visitedTypes: make(map[TypeId]bool), - symbolDepth: make(map[CompositeSymbolIdentity]int), - trackedSymbols: make([]*TrackedSymbolArgs, 0), - mapper: nil, - reverseMappedStack: make([]*ast.Symbol, 0), + tracker: tracker, + approximateLength: 0, + encounteredError: false, + truncating: false, + reportedDiagnostic: false, + flags: flags, + internalFlags: internalFlags, + depth: 0, + enclosingDeclaration: enclosingDeclaration, + enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration), + inferTypeParameters: make([]*Type, 0), + visitedTypes: make(map[TypeId]bool), + symbolDepth: make(map[CompositeSymbolIdentity]int), + trackedSymbols: make([]*TrackedSymbolArgs, 0), + mapper: nil, + reverseMappedStack: make([]*ast.Symbol, 0), + enclosingSymbolTypes: make(map[ast.SymbolId]*Type), + remappedSymbolReferences: make(map[ast.SymbolId]*ast.Symbol), } if tracker == nil { tracker = NewSymbolTrackerImpl(b.impl.ctx, nil) From 99c5b27a8f3df48658b77ec23c97b5355ffb4d3a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 22 Apr 2025 15:49:47 -0700 Subject: [PATCH 10/15] Bugfixes aplenty --- internal/checker/emitresolver.go | 23 +++++++++++++++-------- internal/checker/nodebuilder.go | 25 +++++++++++++++++++++++-- internal/checker/nodebuilderscopes.go | 8 ++++++-- internal/checker/symbolaccessibility.go | 9 +++++++++ internal/declarations/tracker.go | 2 +- internal/printer/emitcontext.go | 8 +++++++- 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index c3ec5db7bc..0f4ea6087a 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -61,6 +61,13 @@ func (r *emitResolver) GetEnumMemberValue(node *ast.Node) evaluator.Result { } func (r *emitResolver) IsDeclarationVisible(node *ast.Node) bool { + // Only lock on external API func to prevent deadlocks + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + return r.isDeclarationVisible(node) +} + +func (r *emitResolver) isDeclarationVisible(node *ast.Node) bool { // node = r.emitContext.ParseNode(node) if !ast.IsParseTreeNode(node) { return false @@ -69,9 +76,6 @@ func (r *emitResolver) IsDeclarationVisible(node *ast.Node) bool { return false } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - links := r.checker.declarationLinks.Get(node) if links.isVisible == core.TSUnknown { if r.determineIfDeclarationIsVisible(node) { @@ -92,7 +96,7 @@ func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool { // First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file return node.Parent != nil && node.Parent.Parent != nil && node.Parent.Parent.Parent != nil && ast.IsSourceFile(node.Parent.Parent.Parent) case ast.KindBindingElement: - return r.IsDeclarationVisible(node.Parent.Parent) + return r.isDeclarationVisible(node.Parent.Parent) case ast.KindVariableDeclaration, ast.KindModuleDeclaration, ast.KindClassDeclaration, @@ -120,7 +124,7 @@ func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool { return ast.IsGlobalSourceFile(parent) } // Exported members/ambient module elements (exception import declaration) are visible if parent is visible - return r.IsDeclarationVisible(parent) + return r.isDeclarationVisible(parent) case ast.KindPropertyDeclaration, ast.KindPropertySignature, @@ -133,7 +137,7 @@ func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool { return false } // Public properties/methods are visible if its parents are visible, so: - return r.IsDeclarationVisible(node.Parent) + return r.isDeclarationVisible(node.Parent) case ast.KindConstructor, ast.KindConstructSignature, @@ -151,7 +155,7 @@ func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool { ast.KindIntersectionType, ast.KindParenthesizedType, ast.KindNamedTupleMember: - return r.IsDeclarationVisible(node.Parent) + return r.isDeclarationVisible(node.Parent) // Default binding, import specifier and namespace import is visible // only on demand so by default it is not visible @@ -261,7 +265,10 @@ func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeA r.checkerMu.Lock() defer r.checkerMu.Unlock() r.checker.declarationLinks.Get(declaration).isVisible = core.TSTrue - aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = declaration + if aliasesToMakeVisibleSet == nil { + aliasesToMakeVisibleSet = make(map[ast.NodeId]*ast.Node) + } + aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = aliasingStatement } } else { addVisibleAlias = noopAddVisibleAlias diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index e5d2572360..7939dbd294 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -1130,7 +1130,7 @@ func (b *NodeBuilder) setTextRange(range_ *ast.Node, location *ast.Node) *ast.No original = b.e.Original(original) } if original == nil { - b.e.SetOriginal(range_, location) + b.e.SetOriginalEx(range_, location, true) } // only set positions if range comes from the same file since copying text across files isn't supported by the emitter @@ -1813,6 +1813,27 @@ func (b *NodeBuilder) indexInfoToIndexSignatureDeclarationHelper(indexInfo *Inde */ func (b *NodeBuilder) serializeTypeForDeclaration(declaration *ast.Declaration, t *Type, symbol *ast.Symbol) *ast.Node { // !!! node reuse logic + if symbol == nil { + symbol = b.ch.getSymbolOfDeclaration(declaration) + } + if t == nil { + t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] + if t == nil { + if symbol.Flags&ast.SymbolFlagsAccessor != 0 && declaration.Kind == ast.KindSetAccessor { + t = b.ch.instantiateType(b.ch.getWriteTypeOfSymbol(symbol), b.ctx.mapper) + } else if symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { + t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) + } else { + t = b.ch.errorType + } + } + // !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker + addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver(nil, true).RequiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) + if addUndefinedForParameter { + t = b.ch.getOptionalType(t, false) + } + } + restoreFlags := b.saveRestoreFlags() if t.flags&TypeFlagsUniqueESSymbol != 0 && t.symbol == symbol && (b.ctx.enclosingDeclaration == nil || core.Some(symbol.Declarations, func(d *ast.Declaration) bool { return ast.GetSourceFileOfNode(d) == b.ctx.enclosingFile @@ -2620,7 +2641,7 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { // return e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "unresolved") // } b.ctx.approximateLength += 3 - return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(core.IfElse(t == b.ch.intrinsicMarkerType, ast.KindIntrinsicKeyword, ast.KindAnyKeyword))) + return b.f.NewKeywordTypeNode(core.IfElse(t == b.ch.intrinsicMarkerType, ast.KindIntrinsicKeyword, ast.KindAnyKeyword)) } if t.flags&TypeFlagsUnknown != 0 { return b.f.NewKeywordTypeNode(ast.KindUnknownKeyword) diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go index 3d018b12c6..44df74f2da 100644 --- a/internal/checker/nodebuilderscopes.go +++ b/internal/checker/nodebuilderscopes.go @@ -221,8 +221,12 @@ func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*as } return func() { - (*cleanupParams)() - (*cleanupTypeParams)() + if cleanupParams != nil { + (*cleanupParams)() + } + if cleanupTypeParams != nil { + (*cleanupTypeParams)() + } cleanupContext() b.ctx.enclosingDeclaration = oldEnclosingDecl b.ctx.mapper = oldMapper diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index b92a04ba13..1cbd35df94 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -172,6 +172,9 @@ func (ch *Checker) getAlternativeContainingModules(symbol *ast.Symbol, enclosing containingFile := ast.GetSourceFileOfNode(enclosingDeclaration) id := ast.GetNodeId(containingFile.AsNode()) links := ch.symbolContainerLinks.Get(symbol) + if links.extendedContainersByFile == nil { + links.extendedContainersByFile = make(map[ast.NodeId][]*ast.Symbol) + } existing, ok := links.extendedContainersByFile[id] if ok && existing != nil { return existing @@ -231,6 +234,9 @@ func (ch *Checker) getVariableDeclarationOfObjectLiteral(symbol *ast.Symbol, mea return nil } firstDecl := symbol.Declarations[0] + if firstDecl.Parent == nil { + return nil + } if !ast.IsVariableDeclaration(firstDecl.Parent) { return nil } @@ -379,6 +385,9 @@ func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) }) links := ch.symbolContainerLinks.Get(ctx.symbol) linkKey := accessibleChainCacheKey{ctx.useOnlyExternalAliasing, firstRelevantLocation, ctx.meaning} + if links.accessibleChainCache == nil { + links.accessibleChainCache = make(map[accessibleChainCacheKey][]*ast.Symbol) + } existing, ok := links.accessibleChainCache[linkKey] if ok { return existing diff --git a/internal/declarations/tracker.go b/internal/declarations/tracker.go index f6c0615d42..1777980ce4 100644 --- a/internal/declarations/tracker.go +++ b/internal/declarations/tracker.go @@ -18,7 +18,7 @@ type SymbolTrackerImpl struct { // GetModuleSpecifierGenerationHost implements checker.SymbolTracker. func (s *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { - panic("unimplemented") + return nil // !!! } func (s *SymbolTrackerImpl) GetInnerSymbolTracker() nodebuilder.SymbolTracker { diff --git a/internal/printer/emitcontext.go b/internal/printer/emitcontext.go index a395b94e97..6d9cc7f8ae 100644 --- a/internal/printer/emitcontext.go +++ b/internal/printer/emitcontext.go @@ -633,6 +633,10 @@ func (c *EmitContext) NewRewriteRelativeImportExtensionsHelper(firstArgument *as // // NOTE: This is the equivalent to `setOriginalNode` in Strada. func (c *EmitContext) SetOriginal(node *ast.Node, original *ast.Node) { + c.SetOriginalEx(node, original, false) +} + +func (c *EmitContext) SetOriginalEx(node *ast.Node, original *ast.Node, allowOverwrite bool) { if original == nil { panic("Original cannot be nil.") } @@ -647,8 +651,10 @@ func (c *EmitContext) SetOriginal(node *ast.Node, original *ast.Node) { if emitNode := c.emitNodes.TryGet(original); emitNode != nil { c.emitNodes.Get(node).copyFrom(emitNode) } - } else if existing != original { + } else if !allowOverwrite && existing != original { panic("Original node already set.") + } else if allowOverwrite { + c.original[node] = original } } From 2050781d5d57798c73c0517030ddc5cb9bb31aee Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 22 Apr 2025 18:25:05 -0700 Subject: [PATCH 11/15] Fix all the panics and deadlocks exposed by the test suite, implement/stub some missing functionality --- internal/ast/ast.go | 26 +++++++ internal/ast/kind.go | 1 + internal/ast/kind_stringer_generated.go | 7 +- internal/ast/utilities.go | 4 +- internal/checker/emitresolver.go | 21 +++--- internal/checker/nodebuilder.go | 46 ++++++++---- internal/checker/nodebuilderscopes.go | 10 ++- internal/checker/printer.go | 12 ++-- internal/checker/symbolaccessibility.go | 28 ++++++-- internal/checker/types.go | 2 + internal/compiler/emitHost.go | 4 ++ internal/declarations/transform.go | 81 ++++++++++++++++------ internal/declarations/util.go | 5 ++ internal/printer/printer.go | 9 ++- internal/printer/singlelinestringwriter.go | 10 ++- 15 files changed, 197 insertions(+), 69 deletions(-) diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 7d77dbff0b..1cb960a3a8 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -1338,6 +1338,10 @@ func (n *Node) AsNotEmittedStatement() *NotEmittedStatement { return n.data.(*NotEmittedStatement) } +func (n *Node) AsNotEmittedTypeElement() *NotEmittedTypeElement { + return n.data.(*NotEmittedTypeElement) +} + func (n *Node) AsJSDoc() *JSDoc { return n.data.(*JSDoc) } @@ -3915,6 +3919,28 @@ func IsNotEmittedStatement(node *Node) bool { return node.Kind == KindNotEmittedStatement } +// NotEmittedStatement + +// Represents a type element that is elided as part of a transformation to emit comments on a +// not-emitted node. +type NotEmittedTypeElement struct { + NodeBase + TypeElementBase +} + +func (f *NodeFactory) NewNotEmittedTypeElement() *Node { + data := &NotEmittedTypeElement{} + return newNode(KindNotEmittedTypeElement, data, f.hooks) +} + +func (node *NotEmittedTypeElement) Clone(f *NodeFactory) *Node { + return cloneNode(f.NewNotEmittedTypeElement(), node.AsNode(), f.hooks) +} + +func IsNotEmittedTypeElement(node *Node) bool { + return node.Kind == KindNotEmittedTypeElement +} + // ImportEqualsDeclaration type ImportEqualsDeclaration struct { diff --git a/internal/ast/kind.go b/internal/ast/kind.go index 228c440c58..f377ffcde0 100644 --- a/internal/ast/kind.go +++ b/internal/ast/kind.go @@ -384,6 +384,7 @@ const ( KindPartiallyEmittedExpression KindCommaListExpression KindSyntheticReferenceExpression + KindNotEmittedTypeElement // Enum value count KindCount // Markers diff --git a/internal/ast/kind_stringer_generated.go b/internal/ast/kind_stringer_generated.go index 62972bad69..d081993851 100644 --- a/internal/ast/kind_stringer_generated.go +++ b/internal/ast/kind_stringer_generated.go @@ -358,12 +358,13 @@ func _() { _ = x[KindPartiallyEmittedExpression-347] _ = x[KindCommaListExpression-348] _ = x[KindSyntheticReferenceExpression-349] - _ = x[KindCount-350] + _ = x[KindNotEmittedTypeElement-350] + _ = x[KindCount-351] } -const _Kind_name = "KindUnknownKindEndOfFileKindSingleLineCommentTriviaKindMultiLineCommentTriviaKindNewLineTriviaKindWhitespaceTriviaKindConflictMarkerTriviaKindNonTextFileMarkerTriviaKindNumericLiteralKindBigIntLiteralKindStringLiteralKindJsxTextKindJsxTextAllWhiteSpacesKindRegularExpressionLiteralKindNoSubstitutionTemplateLiteralKindTemplateHeadKindTemplateMiddleKindTemplateTailKindOpenBraceTokenKindCloseBraceTokenKindOpenParenTokenKindCloseParenTokenKindOpenBracketTokenKindCloseBracketTokenKindDotTokenKindDotDotDotTokenKindSemicolonTokenKindCommaTokenKindQuestionDotTokenKindLessThanTokenKindLessThanSlashTokenKindGreaterThanTokenKindLessThanEqualsTokenKindGreaterThanEqualsTokenKindEqualsEqualsTokenKindExclamationEqualsTokenKindEqualsEqualsEqualsTokenKindExclamationEqualsEqualsTokenKindEqualsGreaterThanTokenKindPlusTokenKindMinusTokenKindAsteriskTokenKindAsteriskAsteriskTokenKindSlashTokenKindPercentTokenKindPlusPlusTokenKindMinusMinusTokenKindLessThanLessThanTokenKindGreaterThanGreaterThanTokenKindGreaterThanGreaterThanGreaterThanTokenKindAmpersandTokenKindBarTokenKindCaretTokenKindExclamationTokenKindTildeTokenKindAmpersandAmpersandTokenKindBarBarTokenKindQuestionTokenKindColonTokenKindAtTokenKindQuestionQuestionTokenKindBacktickTokenKindHashTokenKindEqualsTokenKindPlusEqualsTokenKindMinusEqualsTokenKindAsteriskEqualsTokenKindAsteriskAsteriskEqualsTokenKindSlashEqualsTokenKindPercentEqualsTokenKindLessThanLessThanEqualsTokenKindGreaterThanGreaterThanEqualsTokenKindGreaterThanGreaterThanGreaterThanEqualsTokenKindAmpersandEqualsTokenKindBarEqualsTokenKindBarBarEqualsTokenKindAmpersandAmpersandEqualsTokenKindQuestionQuestionEqualsTokenKindCaretEqualsTokenKindIdentifierKindPrivateIdentifierKindJSDocCommentTextTokenKindBreakKeywordKindCaseKeywordKindCatchKeywordKindClassKeywordKindConstKeywordKindContinueKeywordKindDebuggerKeywordKindDefaultKeywordKindDeleteKeywordKindDoKeywordKindElseKeywordKindEnumKeywordKindExportKeywordKindExtendsKeywordKindFalseKeywordKindFinallyKeywordKindForKeywordKindFunctionKeywordKindIfKeywordKindImportKeywordKindInKeywordKindInstanceOfKeywordKindNewKeywordKindNullKeywordKindReturnKeywordKindSuperKeywordKindSwitchKeywordKindThisKeywordKindThrowKeywordKindTrueKeywordKindTryKeywordKindTypeOfKeywordKindVarKeywordKindVoidKeywordKindWhileKeywordKindWithKeywordKindImplementsKeywordKindInterfaceKeywordKindLetKeywordKindPackageKeywordKindPrivateKeywordKindProtectedKeywordKindPublicKeywordKindStaticKeywordKindYieldKeywordKindAbstractKeywordKindAccessorKeywordKindAsKeywordKindAssertsKeywordKindAssertKeywordKindAnyKeywordKindAsyncKeywordKindAwaitKeywordKindBooleanKeywordKindConstructorKeywordKindDeclareKeywordKindGetKeywordKindImmediateKeywordKindInferKeywordKindIntrinsicKeywordKindIsKeywordKindKeyOfKeywordKindModuleKeywordKindNamespaceKeywordKindNeverKeywordKindOutKeywordKindReadonlyKeywordKindRequireKeywordKindNumberKeywordKindObjectKeywordKindSatisfiesKeywordKindSetKeywordKindStringKeywordKindSymbolKeywordKindTypeKeywordKindUndefinedKeywordKindUniqueKeywordKindUnknownKeywordKindUsingKeywordKindFromKeywordKindGlobalKeywordKindBigIntKeywordKindOverrideKeywordKindOfKeywordKindQualifiedNameKindComputedPropertyNameKindTypeParameterKindParameterKindDecoratorKindPropertySignatureKindPropertyDeclarationKindMethodSignatureKindMethodDeclarationKindClassStaticBlockDeclarationKindConstructorKindGetAccessorKindSetAccessorKindCallSignatureKindConstructSignatureKindIndexSignatureKindTypePredicateKindTypeReferenceKindFunctionTypeKindConstructorTypeKindTypeQueryKindTypeLiteralKindArrayTypeKindTupleTypeKindOptionalTypeKindRestTypeKindUnionTypeKindIntersectionTypeKindConditionalTypeKindInferTypeKindParenthesizedTypeKindThisTypeKindTypeOperatorKindIndexedAccessTypeKindMappedTypeKindLiteralTypeKindNamedTupleMemberKindTemplateLiteralTypeKindTemplateLiteralTypeSpanKindImportTypeKindObjectBindingPatternKindArrayBindingPatternKindBindingElementKindArrayLiteralExpressionKindObjectLiteralExpressionKindPropertyAccessExpressionKindElementAccessExpressionKindCallExpressionKindNewExpressionKindTaggedTemplateExpressionKindTypeAssertionExpressionKindParenthesizedExpressionKindFunctionExpressionKindArrowFunctionKindDeleteExpressionKindTypeOfExpressionKindVoidExpressionKindAwaitExpressionKindPrefixUnaryExpressionKindPostfixUnaryExpressionKindBinaryExpressionKindConditionalExpressionKindTemplateExpressionKindYieldExpressionKindSpreadElementKindClassExpressionKindOmittedExpressionKindExpressionWithTypeArgumentsKindAsExpressionKindNonNullExpressionKindMetaPropertyKindSyntheticExpressionKindSatisfiesExpressionKindTemplateSpanKindSemicolonClassElementKindBlockKindEmptyStatementKindVariableStatementKindExpressionStatementKindIfStatementKindDoStatementKindWhileStatementKindForStatementKindForInStatementKindForOfStatementKindContinueStatementKindBreakStatementKindReturnStatementKindWithStatementKindSwitchStatementKindLabeledStatementKindThrowStatementKindTryStatementKindDebuggerStatementKindVariableDeclarationKindVariableDeclarationListKindFunctionDeclarationKindClassDeclarationKindInterfaceDeclarationKindTypeAliasDeclarationKindEnumDeclarationKindModuleDeclarationKindModuleBlockKindCaseBlockKindNamespaceExportDeclarationKindImportEqualsDeclarationKindImportDeclarationKindImportClauseKindNamespaceImportKindNamedImportsKindImportSpecifierKindExportAssignmentKindExportDeclarationKindNamedExportsKindNamespaceExportKindExportSpecifierKindMissingDeclarationKindExternalModuleReferenceKindJsxElementKindJsxSelfClosingElementKindJsxOpeningElementKindJsxClosingElementKindJsxFragmentKindJsxOpeningFragmentKindJsxClosingFragmentKindJsxAttributeKindJsxAttributesKindJsxSpreadAttributeKindJsxExpressionKindJsxNamespacedNameKindCaseClauseKindDefaultClauseKindHeritageClauseKindCatchClauseKindImportAttributesKindImportAttributeKindPropertyAssignmentKindShorthandPropertyAssignmentKindSpreadAssignmentKindEnumMemberKindSourceFileKindBundleKindJSDocTypeExpressionKindJSDocNameReferenceKindJSDocMemberNameKindJSDocAllTypeKindJSDocNullableTypeKindJSDocNonNullableTypeKindJSDocOptionalTypeKindJSDocVariadicTypeKindJSDocKindJSDocTextKindJSDocTypeLiteralKindJSDocSignatureKindJSDocLinkKindJSDocLinkCodeKindJSDocLinkPlainKindJSDocTagKindJSDocAugmentsTagKindJSDocImplementsTagKindJSDocDeprecatedTagKindJSDocPublicTagKindJSDocPrivateTagKindJSDocProtectedTagKindJSDocReadonlyTagKindJSDocOverrideTagKindJSDocCallbackTagKindJSDocOverloadTagKindJSDocParameterTagKindJSDocReturnTagKindJSDocThisTagKindJSDocTypeTagKindJSDocTemplateTagKindJSDocTypedefTagKindJSDocSeeTagKindJSDocPropertyTagKindJSDocSatisfiesTagKindJSDocImportTagKindSyntaxListKindJSTypeAliasDeclarationKindNotEmittedStatementKindPartiallyEmittedExpressionKindCommaListExpressionKindSyntheticReferenceExpressionKindCount" +const _Kind_name = "KindUnknownKindEndOfFileKindSingleLineCommentTriviaKindMultiLineCommentTriviaKindNewLineTriviaKindWhitespaceTriviaKindConflictMarkerTriviaKindNonTextFileMarkerTriviaKindNumericLiteralKindBigIntLiteralKindStringLiteralKindJsxTextKindJsxTextAllWhiteSpacesKindRegularExpressionLiteralKindNoSubstitutionTemplateLiteralKindTemplateHeadKindTemplateMiddleKindTemplateTailKindOpenBraceTokenKindCloseBraceTokenKindOpenParenTokenKindCloseParenTokenKindOpenBracketTokenKindCloseBracketTokenKindDotTokenKindDotDotDotTokenKindSemicolonTokenKindCommaTokenKindQuestionDotTokenKindLessThanTokenKindLessThanSlashTokenKindGreaterThanTokenKindLessThanEqualsTokenKindGreaterThanEqualsTokenKindEqualsEqualsTokenKindExclamationEqualsTokenKindEqualsEqualsEqualsTokenKindExclamationEqualsEqualsTokenKindEqualsGreaterThanTokenKindPlusTokenKindMinusTokenKindAsteriskTokenKindAsteriskAsteriskTokenKindSlashTokenKindPercentTokenKindPlusPlusTokenKindMinusMinusTokenKindLessThanLessThanTokenKindGreaterThanGreaterThanTokenKindGreaterThanGreaterThanGreaterThanTokenKindAmpersandTokenKindBarTokenKindCaretTokenKindExclamationTokenKindTildeTokenKindAmpersandAmpersandTokenKindBarBarTokenKindQuestionTokenKindColonTokenKindAtTokenKindQuestionQuestionTokenKindBacktickTokenKindHashTokenKindEqualsTokenKindPlusEqualsTokenKindMinusEqualsTokenKindAsteriskEqualsTokenKindAsteriskAsteriskEqualsTokenKindSlashEqualsTokenKindPercentEqualsTokenKindLessThanLessThanEqualsTokenKindGreaterThanGreaterThanEqualsTokenKindGreaterThanGreaterThanGreaterThanEqualsTokenKindAmpersandEqualsTokenKindBarEqualsTokenKindBarBarEqualsTokenKindAmpersandAmpersandEqualsTokenKindQuestionQuestionEqualsTokenKindCaretEqualsTokenKindIdentifierKindPrivateIdentifierKindJSDocCommentTextTokenKindBreakKeywordKindCaseKeywordKindCatchKeywordKindClassKeywordKindConstKeywordKindContinueKeywordKindDebuggerKeywordKindDefaultKeywordKindDeleteKeywordKindDoKeywordKindElseKeywordKindEnumKeywordKindExportKeywordKindExtendsKeywordKindFalseKeywordKindFinallyKeywordKindForKeywordKindFunctionKeywordKindIfKeywordKindImportKeywordKindInKeywordKindInstanceOfKeywordKindNewKeywordKindNullKeywordKindReturnKeywordKindSuperKeywordKindSwitchKeywordKindThisKeywordKindThrowKeywordKindTrueKeywordKindTryKeywordKindTypeOfKeywordKindVarKeywordKindVoidKeywordKindWhileKeywordKindWithKeywordKindImplementsKeywordKindInterfaceKeywordKindLetKeywordKindPackageKeywordKindPrivateKeywordKindProtectedKeywordKindPublicKeywordKindStaticKeywordKindYieldKeywordKindAbstractKeywordKindAccessorKeywordKindAsKeywordKindAssertsKeywordKindAssertKeywordKindAnyKeywordKindAsyncKeywordKindAwaitKeywordKindBooleanKeywordKindConstructorKeywordKindDeclareKeywordKindGetKeywordKindImmediateKeywordKindInferKeywordKindIntrinsicKeywordKindIsKeywordKindKeyOfKeywordKindModuleKeywordKindNamespaceKeywordKindNeverKeywordKindOutKeywordKindReadonlyKeywordKindRequireKeywordKindNumberKeywordKindObjectKeywordKindSatisfiesKeywordKindSetKeywordKindStringKeywordKindSymbolKeywordKindTypeKeywordKindUndefinedKeywordKindUniqueKeywordKindUnknownKeywordKindUsingKeywordKindFromKeywordKindGlobalKeywordKindBigIntKeywordKindOverrideKeywordKindOfKeywordKindQualifiedNameKindComputedPropertyNameKindTypeParameterKindParameterKindDecoratorKindPropertySignatureKindPropertyDeclarationKindMethodSignatureKindMethodDeclarationKindClassStaticBlockDeclarationKindConstructorKindGetAccessorKindSetAccessorKindCallSignatureKindConstructSignatureKindIndexSignatureKindTypePredicateKindTypeReferenceKindFunctionTypeKindConstructorTypeKindTypeQueryKindTypeLiteralKindArrayTypeKindTupleTypeKindOptionalTypeKindRestTypeKindUnionTypeKindIntersectionTypeKindConditionalTypeKindInferTypeKindParenthesizedTypeKindThisTypeKindTypeOperatorKindIndexedAccessTypeKindMappedTypeKindLiteralTypeKindNamedTupleMemberKindTemplateLiteralTypeKindTemplateLiteralTypeSpanKindImportTypeKindObjectBindingPatternKindArrayBindingPatternKindBindingElementKindArrayLiteralExpressionKindObjectLiteralExpressionKindPropertyAccessExpressionKindElementAccessExpressionKindCallExpressionKindNewExpressionKindTaggedTemplateExpressionKindTypeAssertionExpressionKindParenthesizedExpressionKindFunctionExpressionKindArrowFunctionKindDeleteExpressionKindTypeOfExpressionKindVoidExpressionKindAwaitExpressionKindPrefixUnaryExpressionKindPostfixUnaryExpressionKindBinaryExpressionKindConditionalExpressionKindTemplateExpressionKindYieldExpressionKindSpreadElementKindClassExpressionKindOmittedExpressionKindExpressionWithTypeArgumentsKindAsExpressionKindNonNullExpressionKindMetaPropertyKindSyntheticExpressionKindSatisfiesExpressionKindTemplateSpanKindSemicolonClassElementKindBlockKindEmptyStatementKindVariableStatementKindExpressionStatementKindIfStatementKindDoStatementKindWhileStatementKindForStatementKindForInStatementKindForOfStatementKindContinueStatementKindBreakStatementKindReturnStatementKindWithStatementKindSwitchStatementKindLabeledStatementKindThrowStatementKindTryStatementKindDebuggerStatementKindVariableDeclarationKindVariableDeclarationListKindFunctionDeclarationKindClassDeclarationKindInterfaceDeclarationKindTypeAliasDeclarationKindEnumDeclarationKindModuleDeclarationKindModuleBlockKindCaseBlockKindNamespaceExportDeclarationKindImportEqualsDeclarationKindImportDeclarationKindImportClauseKindNamespaceImportKindNamedImportsKindImportSpecifierKindExportAssignmentKindExportDeclarationKindNamedExportsKindNamespaceExportKindExportSpecifierKindMissingDeclarationKindExternalModuleReferenceKindJsxElementKindJsxSelfClosingElementKindJsxOpeningElementKindJsxClosingElementKindJsxFragmentKindJsxOpeningFragmentKindJsxClosingFragmentKindJsxAttributeKindJsxAttributesKindJsxSpreadAttributeKindJsxExpressionKindJsxNamespacedNameKindCaseClauseKindDefaultClauseKindHeritageClauseKindCatchClauseKindImportAttributesKindImportAttributeKindPropertyAssignmentKindShorthandPropertyAssignmentKindSpreadAssignmentKindEnumMemberKindSourceFileKindBundleKindJSDocTypeExpressionKindJSDocNameReferenceKindJSDocMemberNameKindJSDocAllTypeKindJSDocNullableTypeKindJSDocNonNullableTypeKindJSDocOptionalTypeKindJSDocVariadicTypeKindJSDocKindJSDocTextKindJSDocTypeLiteralKindJSDocSignatureKindJSDocLinkKindJSDocLinkCodeKindJSDocLinkPlainKindJSDocTagKindJSDocAugmentsTagKindJSDocImplementsTagKindJSDocDeprecatedTagKindJSDocPublicTagKindJSDocPrivateTagKindJSDocProtectedTagKindJSDocReadonlyTagKindJSDocOverrideTagKindJSDocCallbackTagKindJSDocOverloadTagKindJSDocParameterTagKindJSDocReturnTagKindJSDocThisTagKindJSDocTypeTagKindJSDocTemplateTagKindJSDocTypedefTagKindJSDocSeeTagKindJSDocPropertyTagKindJSDocSatisfiesTagKindJSDocImportTagKindSyntaxListKindJSTypeAliasDeclarationKindNotEmittedStatementKindPartiallyEmittedExpressionKindCommaListExpressionKindSyntheticReferenceExpressionKindNotEmittedTypeElementKindCount" -var _Kind_index = [...]uint16{0, 11, 24, 51, 77, 94, 114, 138, 165, 183, 200, 217, 228, 253, 281, 314, 330, 348, 364, 382, 401, 419, 438, 458, 479, 491, 509, 527, 541, 561, 578, 600, 620, 643, 669, 690, 716, 743, 775, 801, 814, 828, 845, 870, 884, 900, 917, 936, 961, 992, 1034, 1052, 1064, 1078, 1098, 1112, 1139, 1154, 1171, 1185, 1196, 1221, 1238, 1251, 1266, 1285, 1305, 1328, 1359, 1379, 1401, 1432, 1469, 1517, 1541, 1559, 1580, 1613, 1644, 1664, 1678, 1699, 1724, 1740, 1755, 1771, 1787, 1803, 1822, 1841, 1859, 1876, 1889, 1904, 1919, 1936, 1954, 1970, 1988, 2002, 2021, 2034, 2051, 2064, 2085, 2099, 2114, 2131, 2147, 2164, 2179, 2195, 2210, 2224, 2241, 2255, 2270, 2286, 2301, 2322, 2342, 2356, 2374, 2392, 2412, 2429, 2446, 2462, 2481, 2500, 2513, 2531, 2548, 2562, 2578, 2594, 2612, 2634, 2652, 2666, 2686, 2702, 2722, 2735, 2751, 2768, 2788, 2804, 2818, 2837, 2855, 2872, 2889, 2909, 2923, 2940, 2957, 2972, 2992, 3009, 3027, 3043, 3058, 3075, 3092, 3111, 3124, 3141, 3165, 3182, 3195, 3208, 3229, 3252, 3271, 3292, 3323, 3338, 3353, 3368, 3385, 3407, 3425, 3442, 3459, 3475, 3494, 3507, 3522, 3535, 3548, 3564, 3576, 3589, 3609, 3628, 3641, 3662, 3674, 3690, 3711, 3725, 3740, 3760, 3783, 3810, 3824, 3848, 3871, 3889, 3915, 3942, 3970, 3997, 4015, 4032, 4060, 4087, 4114, 4136, 4153, 4173, 4193, 4211, 4230, 4255, 4281, 4301, 4326, 4348, 4367, 4384, 4403, 4424, 4455, 4471, 4492, 4508, 4531, 4554, 4570, 4595, 4604, 4622, 4643, 4666, 4681, 4696, 4714, 4730, 4748, 4766, 4787, 4805, 4824, 4841, 4860, 4880, 4898, 4914, 4935, 4958, 4985, 5008, 5028, 5052, 5076, 5095, 5116, 5131, 5144, 5174, 5201, 5222, 5238, 5257, 5273, 5292, 5312, 5333, 5349, 5368, 5387, 5409, 5436, 5450, 5475, 5496, 5517, 5532, 5554, 5576, 5592, 5609, 5631, 5648, 5669, 5683, 5700, 5718, 5733, 5753, 5772, 5794, 5825, 5845, 5859, 5873, 5883, 5906, 5928, 5947, 5963, 5984, 6008, 6029, 6050, 6059, 6072, 6092, 6110, 6123, 6140, 6158, 6170, 6190, 6212, 6234, 6252, 6271, 6292, 6312, 6332, 6352, 6372, 6393, 6411, 6427, 6443, 6463, 6482, 6497, 6517, 6538, 6556, 6570, 6596, 6619, 6649, 6672, 6704, 6713} +var _Kind_index = [...]uint16{0, 11, 24, 51, 77, 94, 114, 138, 165, 183, 200, 217, 228, 253, 281, 314, 330, 348, 364, 382, 401, 419, 438, 458, 479, 491, 509, 527, 541, 561, 578, 600, 620, 643, 669, 690, 716, 743, 775, 801, 814, 828, 845, 870, 884, 900, 917, 936, 961, 992, 1034, 1052, 1064, 1078, 1098, 1112, 1139, 1154, 1171, 1185, 1196, 1221, 1238, 1251, 1266, 1285, 1305, 1328, 1359, 1379, 1401, 1432, 1469, 1517, 1541, 1559, 1580, 1613, 1644, 1664, 1678, 1699, 1724, 1740, 1755, 1771, 1787, 1803, 1822, 1841, 1859, 1876, 1889, 1904, 1919, 1936, 1954, 1970, 1988, 2002, 2021, 2034, 2051, 2064, 2085, 2099, 2114, 2131, 2147, 2164, 2179, 2195, 2210, 2224, 2241, 2255, 2270, 2286, 2301, 2322, 2342, 2356, 2374, 2392, 2412, 2429, 2446, 2462, 2481, 2500, 2513, 2531, 2548, 2562, 2578, 2594, 2612, 2634, 2652, 2666, 2686, 2702, 2722, 2735, 2751, 2768, 2788, 2804, 2818, 2837, 2855, 2872, 2889, 2909, 2923, 2940, 2957, 2972, 2992, 3009, 3027, 3043, 3058, 3075, 3092, 3111, 3124, 3141, 3165, 3182, 3195, 3208, 3229, 3252, 3271, 3292, 3323, 3338, 3353, 3368, 3385, 3407, 3425, 3442, 3459, 3475, 3494, 3507, 3522, 3535, 3548, 3564, 3576, 3589, 3609, 3628, 3641, 3662, 3674, 3690, 3711, 3725, 3740, 3760, 3783, 3810, 3824, 3848, 3871, 3889, 3915, 3942, 3970, 3997, 4015, 4032, 4060, 4087, 4114, 4136, 4153, 4173, 4193, 4211, 4230, 4255, 4281, 4301, 4326, 4348, 4367, 4384, 4403, 4424, 4455, 4471, 4492, 4508, 4531, 4554, 4570, 4595, 4604, 4622, 4643, 4666, 4681, 4696, 4714, 4730, 4748, 4766, 4787, 4805, 4824, 4841, 4860, 4880, 4898, 4914, 4935, 4958, 4985, 5008, 5028, 5052, 5076, 5095, 5116, 5131, 5144, 5174, 5201, 5222, 5238, 5257, 5273, 5292, 5312, 5333, 5349, 5368, 5387, 5409, 5436, 5450, 5475, 5496, 5517, 5532, 5554, 5576, 5592, 5609, 5631, 5648, 5669, 5683, 5700, 5718, 5733, 5753, 5772, 5794, 5825, 5845, 5859, 5873, 5883, 5906, 5928, 5947, 5963, 5984, 6008, 6029, 6050, 6059, 6072, 6092, 6110, 6123, 6140, 6158, 6170, 6190, 6212, 6234, 6252, 6271, 6292, 6312, 6332, 6352, 6372, 6393, 6411, 6427, 6443, 6463, 6482, 6497, 6517, 6538, 6556, 6570, 6596, 6619, 6649, 6672, 6704, 6729, 6738} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 089f948c5c..76e0281370 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -584,8 +584,8 @@ func IsTypeElement(node *Node) bool { KindMethodSignature, KindIndexSignature, KindGetAccessor, - KindSetAccessor: - // !!! KindNotEmittedTypeElement + KindSetAccessor, + KindNotEmittedTypeElement: return true } return false diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 0f4ea6087a..7946418b16 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -262,8 +262,7 @@ func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeA if shouldComputeAliasToMakeVisible { addVisibleAlias = func(declaration *ast.Node, aliasingStatement *ast.Node) { // Only lock as we edit links, so the IsDeclarationVisible calls don't trip over the lock - r.checkerMu.Lock() - defer r.checkerMu.Unlock() + // TODO: does this need to lock? but multiple already-locking resolver entrypoints reach here... r.checker.declarationLinks.Get(declaration).isVisible = core.TSTrue if aliasesToMakeVisibleSet == nil { aliasesToMakeVisibleSet = make(map[ast.NodeId]*ast.Node) @@ -279,25 +278,25 @@ func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeA continue } - if !r.IsDeclarationVisible(declaration) { + if !r.isDeclarationVisible(declaration) { // Mark the unexported alias as visible if its parent is visible // because these kind of aliases can be used to name types in declaration file anyImportSyntax := getAnyImportSyntax(declaration) if anyImportSyntax != nil && !ast.HasSyntacticModifier(anyImportSyntax, ast.ModifierFlagsExport) && // import clause without export - r.IsDeclarationVisible(anyImportSyntax.Parent) { + r.isDeclarationVisible(anyImportSyntax.Parent) { addVisibleAlias(declaration, anyImportSyntax) continue } if ast.IsVariableDeclaration(declaration) && ast.IsVariableStatement(declaration.Parent.Parent) && !ast.HasSyntacticModifier(declaration.Parent.Parent, ast.ModifierFlagsExport) && // unexported variable statement - r.IsDeclarationVisible(declaration.Parent.Parent.Parent) { + r.isDeclarationVisible(declaration.Parent.Parent.Parent) { addVisibleAlias(declaration, declaration.Parent.Parent) continue } if ast.IsLateVisibilityPaintedStatement(declaration) && // unexported top-level statement !ast.HasSyntacticModifier(declaration, ast.ModifierFlagsExport) && - r.IsDeclarationVisible(declaration.Parent) { + r.isDeclarationVisible(declaration.Parent) { addVisibleAlias(declaration, declaration) continue } @@ -307,7 +306,7 @@ func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeA declaration.Parent.Parent.Parent.Parent != nil && ast.IsVariableStatement(declaration.Parent.Parent.Parent.Parent) && !ast.HasSyntacticModifier(declaration.Parent.Parent.Parent.Parent, ast.ModifierFlagsExport) && declaration.Parent.Parent.Parent.Parent.Parent != nil && // check if the thing containing the variable statement is visible (ie, the file) - r.IsDeclarationVisible(declaration.Parent.Parent.Parent.Parent.Parent) { + r.isDeclarationVisible(declaration.Parent.Parent.Parent.Parent.Parent) { addVisibleAlias(declaration, declaration.Parent.Parent.Parent.Parent) continue } @@ -316,7 +315,7 @@ func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeA if ast.HasSyntacticModifier(variableStatement, ast.ModifierFlagsExport) { continue // no alias to add, already exported } - if !r.IsDeclarationVisible(variableStatement.Parent) { + if !r.isDeclarationVisible(variableStatement.Parent) { return nil // not visible } addVisibleAlias(declaration, variableStatement) @@ -762,8 +761,8 @@ func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node { node = emitContext.ParseNode(node) r.checkerMu.Lock() - defer r.checkerMu.Unlock() type_ := r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node)) + r.checkerMu.Unlock() if type_ == nil { return nil // TODO: How!? Maybe this should be a panic. All symbols should have a type. } @@ -824,8 +823,6 @@ func (r *emitResolver) CreateTypeOfExpression(emitContext *printer.EmitContext, return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context return requestNodeBuilder.serializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) } @@ -833,7 +830,6 @@ func (r *emitResolver) CreateTypeOfExpression(emitContext *printer.EmitContext, func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { container = emitContext.ParseNode(container) r.checkerMu.Lock() - defer r.checkerMu.Unlock() sym := container.Symbol() staticInfos := r.checker.getIndexInfosOfType(r.checker.getTypeOfSymbol(sym)) @@ -843,6 +839,7 @@ func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC siblingSymbols := slices.Collect(maps.Values(r.checker.getMembersOfSymbol(sym))) instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols) } + r.checkerMu.Unlock() requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 7939dbd294..482628aa2c 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -422,7 +422,6 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, var nonRootParts *ast.Node if len(chain) > 1 { nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1) - // !!! } typeParameterNodes := typeArguments /*|| lookupTypeParameterNodes(chain, 0, context);*/ // !!! TODO: type argument smuggling contextFile := ast.GetSourceFileOfNode(b.e.MostOriginal(b.ctx.enclosingDeclaration)) // TODO: Just use b.ctx.enclosingFile ? Or is the delayed lookup important for context moves? @@ -431,7 +430,7 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, var attributes *ast.Node if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion - if targetFile != nil && b.ch.program.GetEmitModuleFormatOfFile(targetFile) == core.ModuleKindESNext && b.ch.program.GetEmitModuleFormatOfFile(targetFile) != b.ch.program.GetEmitModuleFormatOfFile(contextFile) { + if targetFile != nil && contextFile != nil && b.ch.program.GetEmitModuleFormatOfFile(targetFile) == core.ModuleKindESNext && b.ch.program.GetEmitModuleFormatOfFile(targetFile) != b.ch.program.GetEmitModuleFormatOfFile(contextFile) { specifier = b.getSpecifierForModuleSymbol(chain[0], core.ModuleKindESNext) attributes = b.f.NewImportAttributes( ast.KindWithKeyword, @@ -1334,7 +1333,7 @@ func (b *NodeBuilder) typePredicateToTypePredicateNode(predicate *TypePredicate) parameterName = b.f.NewIdentifier(predicate.parameterName) b.e.AddEmitFlags(parameterName, printer.EFNoAsciiEscaping) } else { - parameterName = b.f.NewKeywordExpression(ast.KindThisKeyword) + parameterName = b.f.NewThisTypeNode() } var typeNode *ast.Node if predicate.t != nil { @@ -1448,7 +1447,9 @@ func (b *NodeBuilder) symbolTableToDeclarationStatements(symbolTable *ast.Symbol } func (b *NodeBuilder) serializeTypeForExpression(expr *ast.Node) *ast.Node { - panic("unimplemented") // !!! + // !!! TODO: shim, add node reuse + type_ := b.ch.instantiateType(b.ch.getWidenedType(b.ch.getRegularTypeOfExpression(expr)), b.ctx.mapper) + return b.typeToTypeNodeClosure(type_) } func (b *NodeBuilder) serializeInferredReturnTypeForSignature(signature *Signature, returnType *Type) *ast.Node { @@ -1643,7 +1644,7 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) restSymbol := sig.parameters[restIndex] restType := c.getTypeOfSymbol(restSymbol) getUniqAssociatedNamesFromTupleType := func(t *Type, restSymbol *ast.Symbol) []string { - names := core.MapIndex(t.AsTupleType().elementInfos, func(info TupleElementInfo, i int) string { + names := core.MapIndex(t.Target().AsTupleType().elementInfos, func(info TupleElementInfo, i int) string { return c.getTupleElementLabel(info, restSymbol, i) }) if len(names) > 0 { @@ -1694,7 +1695,7 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) // name = c.getParameterNameAtPosition(sig, restIndex+i, restType) // } name := associatedNames[i] - flags := restType.AsTupleType().elementInfos[i].flags + flags := restType.Target().AsTupleType().elementInfos[i].flags var checkFlags ast.CheckFlags switch { case flags&ElementFlagsVariable != 0: @@ -1974,7 +1975,13 @@ func (b *NodeBuilder) getPropertyNameNodeForSymbolFromNameType(symbol *ast.Symbo return nil } if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { - name := nameType.AsLiteralType().value.(string) + var name string + switch nameType.AsLiteralType().value.(type) { + case jsnum.Number: + name = nameType.AsLiteralType().value.(jsnum.Number).String() + case string: + name = nameType.AsLiteralType().value.(string) + } if !scanner.IsIdentifierText(name, b.ch.compilerOptions.GetEmitScriptTarget()) && (stringNamed || !isNumericLiteralName(name)) { // !!! TODO: set singleQuote return b.f.NewStringLiteral(name) @@ -2104,10 +2111,10 @@ func (b *NodeBuilder) addPropertyToElementList(propertySymbol *ast.Symbol, typeE func (b *NodeBuilder) createTypeNodesFromResolvedType(resolvedType *StructuredType) *ast.NodeList { if b.checkTruncationLength() { if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - panic("NotEmittedTypeElement not implemented") // !!! - // elem := b.f.NewNotEmittedTypeElement() + elem := b.f.NewNotEmittedTypeElement() + // TODO: attach synthetic comment // b.e.addSyntheticTrailingComment(elem, ast.KindMultiLineCommentTrivia, "elided") - // return b.f.NewNodeList([]*ast.TypeElement{elem}) + return b.f.NewNodeList([]*ast.TypeElement{elem}) } return b.f.NewNodeList([]*ast.Node{b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier("..."), nil, nil, nil)}) } @@ -2413,8 +2420,12 @@ func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, arrayType) } } else if t.Target().objectFlags&ObjectFlagsTuple != 0 { - typeArguments = core.SameMapIndex(typeArguments, func(t *Type, i int) *Type { - return b.ch.removeMissingType(t, t.Target().AsTupleType().elementInfos[i].flags&ElementFlagsOptional != 0) + typeArguments = core.SameMapIndex(typeArguments, func(arg *Type, i int) *Type { + isOptional := false + if i < len(t.Target().AsTupleType().elementInfos) { + isOptional = t.Target().AsTupleType().elementInfos[i].flags&ElementFlagsOptional != 0 + } + return b.ch.removeMissingType(arg, isOptional) }) if len(typeArguments) > 0 { arity := b.ch.getTypeReferenceArity(t) @@ -2500,7 +2511,7 @@ func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { // `AsyncIterable`, and `AsyncIterableIterator` to provide backwards-compatible .d.ts emit due // to each now having three type parameters instead of only one. if b.ch.isReferenceToType(t, b.ch.getGlobalIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalIterableIteratorType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableIteratorType()) { - if t.AsInterfaceType().node == nil || !ast.IsTypeReferenceNode(t.AsInterfaceType().node) || t.AsInterfaceType().node.TypeArguments() == nil || len(t.AsInterfaceType().node.TypeArguments()) < typeParameterCount { + if t.AsTypeReference().node == nil || !ast.IsTypeReferenceNode(t.AsTypeReference().node) || t.AsTypeReference().node.TypeArguments() == nil || len(t.AsTypeReference().node.TypeArguments()) < typeParameterCount { for typeParameterCount > 0 { typeArgument := typeArguments[typeParameterCount-1] typeParameter := t.Target().AsInterfaceType().TypeParameters()[typeParameterCount-1] @@ -2699,8 +2710,13 @@ func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { return b.f.NewLiteralTypeNode(b.f.NewBigIntLiteral(pseudoBigIntToString(getBigIntLiteralValue(t)))) } if t.flags&TypeFlagsBooleanLiteral != 0 { - b.ctx.approximateLength += len(t.AsIntrinsicType().intrinsicName) - return b.f.NewLiteralTypeNode(core.IfElse(t.AsIntrinsicType().intrinsicName == "true", b.f.NewKeywordExpression(ast.KindTrueKeyword), b.f.NewKeywordExpression(ast.KindFalseKeyword))) + if t.AsLiteralType().value.(bool) { + b.ctx.approximateLength += 4 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindTrueKeyword)) + } else { + b.ctx.approximateLength += 5 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindFalseKeyword)) + } } if t.flags&TypeFlagsUniqueESSymbol != 0 { if b.ctx.flags&nodebuilder.FlagsAllowUniqueESSymbolType == 0 { diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go index 44df74f2da..7403c4c5ff 100644 --- a/internal/checker/nodebuilderscopes.go +++ b/internal/checker/nodebuilderscopes.go @@ -103,8 +103,9 @@ func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*as var locals ast.SymbolTable if existingFakeScope != nil { locals = existingFakeScope.Locals() - } else { - locals = createSymbolTable([]*ast.Symbol{}) + } + if locals == nil { + locals = make(ast.SymbolTable) } newLocals := []string{} oldLocals := []localsRecord{} @@ -153,7 +154,10 @@ func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*as return } for pIndex, param := range *expandedParams { - originalParam := (*originalParameters)[pIndex] + var originalParam *ast.Symbol + if pIndex < len(*originalParameters) { + originalParam = (*originalParameters)[pIndex] + } if originalParameters != nil && originalParam != param { // Can't reference parameters that come from an expansion add(param.Name, b.ch.unknownSymbol) diff --git a/internal/checker/printer.go b/internal/checker/printer.go index 82b98ef04f..fe6b78a90e 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -91,7 +91,7 @@ func (c *Checker) symbolToString(symbol *ast.Symbol) string { func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags, writer printer.EmitTextWriter) string { if writer == nil { - writer = printer.SingleLineStringWriter + writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } nodeFlags := nodebuilder.FlagsIgnoreErrors @@ -116,7 +116,7 @@ func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast if enclosingDeclaration != nil { sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } - if writer == printer.SingleLineStringWriter { + if writer == printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) { // handle uses of the single-line writer during an ongoing write existing := writer.String() defer writer.Clear() @@ -162,7 +162,7 @@ func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration } } if writer == nil { - writer = printer.SingleLineStringWriter + writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName sig := c.nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) @@ -171,7 +171,7 @@ func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration if enclosingDeclaration != nil { sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } - if writer == printer.SingleLineStringWriter { + if writer == printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) { // handle uses of the single-line writer during an ongoing write existing := writer.String() defer writer.Clear() @@ -189,7 +189,7 @@ func (c *Checker) typePredicateToString(typePredicate *TypePredicate) string { func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosingDeclaration *ast.Node, flags TypeFormatFlags, writer printer.EmitTextWriter) string { if writer == nil { - writer = printer.SingleLineStringWriter + writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName predicate := c.nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 @@ -198,7 +198,7 @@ func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosin if enclosingDeclaration != nil { sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } - if writer == printer.SingleLineStringWriter { + if writer == printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) { // handle uses of the single-line writer during an ongoing write existing := writer.String() defer writer.Clear() diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index 1cbd35df94..d480f9b903 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -132,7 +132,11 @@ func (ch *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *a container.Flags&leftMeaning != 0 && len(ch.getAccessibleSymbolChain(container, enclosingDeclaration, ast.SymbolFlagsNamespace /*useOnlyExternalAliasing*/, false)) > 0 { // This order expresses a preference for the real container if it is in scope - return append(append(append([]*ast.Symbol{container}, additionalContainers...), reexportContainers...), objectLiteralContainer) + res := append(append([]*ast.Symbol{container}, additionalContainers...), reexportContainers...) + if objectLiteralContainer != nil { + res = append(res, objectLiteralContainer) + } + return res } // we potentially have a symbol which is a member of the instance side of something - look for a variable in scope with the container's type // which may be acting like a namespace (eg, `Symbol` acts like a namespace when looking up `Symbol.toStringTag`) @@ -288,23 +292,34 @@ func (ch *Checker) getContainersOfSymbol(symbol *ast.Symbol, enclosingDeclaratio if !ast.IsAmbientModule(d) && d.Parent != nil { // direct children of a module if hasNonGlobalAugmentationExternalModuleSymbol(d.Parent) { - candidates = append(candidates, ch.getSymbolOfDeclaration(d.Parent)) + sym := ch.getSymbolOfDeclaration(d.Parent) + if sym != nil { + candidates = append(candidates, sym) + } continue } // export ='d member of an ambient module if ast.IsModuleBlock(d.Parent) && d.Parent.Parent != nil && ch.resolveExternalModuleSymbol(ch.getSymbolOfDeclaration(d.Parent.Parent), false) == symbol { - candidates = append(candidates, ch.getSymbolOfDeclaration(d.Parent.Parent)) + sym := ch.getSymbolOfDeclaration(d.Parent.Parent) + if sym != nil { + candidates = append(candidates, sym) + } continue } } if ast.IsClassExpression(d) && ast.IsBinaryExpression(d.Parent) && d.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && ast.IsAccessExpression(d.Parent.AsBinaryExpression().Left) && ast.IsEntityNameExpression(d.Parent.AsBinaryExpression().Left.Expression()) { if isModuleExportsAccessExpression(d.Parent.AsBinaryExpression().Left) || ast.IsExportsIdentifier(d.Parent.AsBinaryExpression().Left.Expression()) { - candidates = append(candidates, ch.getSymbolOfDeclaration(ast.GetSourceFileOfNode(d).AsNode())) + sym := ch.getSymbolOfDeclaration(ast.GetSourceFileOfNode(d).AsNode()) + if sym != nil { + candidates = append(candidates, sym) + } continue } ch.checkExpressionCached(d.Parent.AsBinaryExpression().Left.Expression()) sym := ch.symbolNodeLinks.Get(d.Parent.AsBinaryExpression().Left.Expression()).resolvedSymbol - candidates = append(candidates, sym) + if sym != nil { + candidates = append(candidates, sym) + } continue } } @@ -641,6 +656,9 @@ func (ch *Checker) someSymbolTableInScope( // TODO: Should this filtered table be cached in some way? for key, memberSymbol := range sym.Members { if memberSymbol.Flags&(ast.SymbolFlagsType & ^ast.SymbolFlagsAssignment) != 0 { + if table == nil { + table = make(ast.SymbolTable) + } table[key] = memberSymbol } } diff --git a/internal/checker/types.go b/internal/checker/types.go index 82c22fb1c9..4afd5e2b22 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -643,6 +643,8 @@ func (t *Type) Target() *Type { return t.AsIndexType().target case t.flags&TypeFlagsStringMapping != 0: return t.AsStringMappingType().target + case t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMapped != 0: + return t.AsMappedType().target } panic("Unhandled case in Type.Target") } diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index 286a4dd7bb..592039ca38 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -38,7 +38,11 @@ type emitHost struct { } func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { + // TODO: EmitContext().ParseNode(node) - can only find a checker for parse nodes! ch := host.program.GetTypeCheckerForFile(ast.GetSourceFileOfNode(node)) + if ch == nil { + return ast.ModifierFlagsNone // TODO: Should this be a panic? + } return ch.GetEffectiveDeclarationFlags(node, flags) } diff --git a/internal/declarations/transform.go b/internal/declarations/transform.go index e6958e9ec9..f3e82aade6 100644 --- a/internal/declarations/transform.go +++ b/internal/declarations/transform.go @@ -91,6 +91,9 @@ const declarationEmitInternalNodeBuilderFlags = nodebuilder.InternalFlagsAllowUn // functions as both `visitDeclarationStatements` and `transformRoot`, utilitzing SyntaxList nodes func (tx *DeclarationTransformer) visit(node *ast.Node) *ast.Node { + if node == nil { + return nil + } // !!! TODO: Bundle support? switch node.Kind { case ast.KindSourceFile: @@ -414,6 +417,8 @@ func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast. var result *ast.Node switch input.Kind { + case ast.KindMappedType: + result = tx.transformMappedTypeNode(input.AsMappedTypeNode()) case ast.KindHeritageClause: result = tx.transformHeritageClause(input.AsHeritageClause()) case ast.KindMethodSignature: @@ -494,6 +499,25 @@ func (tx *DeclarationTransformer) checkName(node *ast.Node) { tx.state.errorNameNode = nil } +func (tx *DeclarationTransformer) transformMappedTypeNode(input *ast.MappedTypeNode) *ast.Node { + // handle missing template type nodes, since the printer does not + var typeNode *ast.Node + if input.Type == nil { + typeNode = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) + } else { + typeNode = tx.Visitor().Visit(input.Type) + } + return tx.Factory().UpdateMappedTypeNode( + input, + input.ReadonlyToken, + tx.Visitor().Visit(input.TypeParameter), + tx.Visitor().Visit(input.NameType), + input.QuestionToken, + typeNode, + nil, + ) +} + func (tx *DeclarationTransformer) transformHeritageClause(clause *ast.HeritageClause) *ast.Node { retainedClauses := core.Filter(clause.Types.Nodes, func(t *ast.Node) bool { return ast.IsEntityNameExpression(t.AsExpressionWithTypeArguments().Expression) || @@ -699,12 +723,13 @@ func (tx *DeclarationTransformer) transformSetAccessorDeclaration(input *ast.Set if ast.IsPrivateIdentifier(input.Name()) { return nil } + return tx.Factory().UpdateSetAccessorDeclaration( input, tx.ensureModifiers(input.AsNode()), input.Name(), nil, // accessors shouldn't have type params - tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0), + tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), nil, nil, ) @@ -719,7 +744,7 @@ func (tx *DeclarationTransformer) transformGetAccesorDeclaration(input *ast.GetA tx.ensureModifiers(input.AsNode()), input.Name(), nil, // accessors shouldn't have type params - tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0), + tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), tx.ensureType(input.AsNode(), false), nil, ) @@ -800,7 +825,7 @@ func (tx *DeclarationTransformer) omitPrivateMethodType(input *ast.Node) *ast.No } func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast.MethodSignatureDeclaration) *ast.Node { - if tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0 { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { return tx.omitPrivateMethodType(input.AsNode()) } else if ast.IsPrivateIdentifier(input.Name()) { return nil @@ -818,7 +843,7 @@ func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast } func (tx *DeclarationTransformer) transformMethodDeclaration(input *ast.MethodDeclaration) *ast.Node { - if tx.host.GetEffectiveDeclarationFlags(input.AsNode(), ast.ModifierFlagsPrivate) != 0 { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { return tx.omitPrivateMethodType(input.AsNode()) } else if ast.IsPrivateIdentifier(input.Name()) { return nil @@ -939,7 +964,7 @@ func (tx *DeclarationTransformer) removeAllComments(node *ast.Node) { } func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) *ast.Node { - if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 { + if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { // Private nodes emit no types (except private parameter properties, whose parameter types are actually visible) return nil } @@ -1148,24 +1173,36 @@ func (tx *DeclarationTransformer) transformModuleDeclaration(input *ast.ModuleDe body, ) } - // trigger visit. ignore result (is deferred, so is just inner unless elided) - tx.Visitor().Visit(inner) - // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) - original := tx.EmitContext().MostOriginal(inner) - id := ast.GetNodeId(original) - body, _ := tx.lateStatementReplacementMap[id] - delete(tx.lateStatementReplacementMap, id) + if inner != nil { + // trigger visit. ignore result (is deferred, so is just inner unless elided) + tx.Visitor().Visit(inner) + // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) + original := tx.EmitContext().MostOriginal(inner) + id := ast.GetNodeId(original) + body, _ := tx.lateStatementReplacementMap[id] + delete(tx.lateStatementReplacementMap, id) + return tx.Factory().UpdateModuleDeclaration( + input, + mods, + input.Keyword, + input.Name(), + body, + ) + } return tx.Factory().UpdateModuleDeclaration( input, mods, input.Keyword, input.Name(), - body, + nil, ) } func (tx *DeclarationTransformer) stripExportModifiers(statement *ast.Node) *ast.Node { - if ast.IsImportEqualsDeclaration(statement) || tx.host.GetEffectiveDeclarationFlags(statement, ast.ModifierFlagsDefault) != 0 || !ast.CanHaveModifiers(statement) { + if statement == nil { + return nil + } + if ast.IsImportEqualsDeclaration(statement) || tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(statement), ast.ModifierFlagsDefault) != 0 || !ast.CanHaveModifiers(statement) { // `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace // Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too return statement @@ -1281,12 +1318,12 @@ func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDecl tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl})), ) newHeritageClause := tx.Factory().UpdateHeritageClause( - extendsClause.AsHeritageClause(), + extendsClause.Parent.AsHeritageClause(), tx.Factory().NewNodeList([]*ast.Node{ tx.Factory().UpdateExpressionWithTypeArguments( - extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments(), + extendsClause.AsExpressionWithTypeArguments(), newId, - tx.Visitor().VisitNodes(extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments().TypeArguments), + tx.Visitor().VisitNodes(extendsClause.AsExpressionWithTypeArguments().TypeArguments), ), }), ) @@ -1398,7 +1435,7 @@ func (tx *DeclarationTransformer) transformEnumDeclaration(input *ast.EnumDeclar } func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) *ast.ModifierList { - currentFlags := tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsAll) + currentFlags := tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsAll) newFlags := tx.ensureModifierFlags(node) if currentFlags == newFlags { // Elide decorators @@ -1430,7 +1467,7 @@ func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.Modifi } func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.TypeParameterList) *ast.TypeParameterList { - if tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { return nil } return tx.Visitor().VisitNodes(params) @@ -1441,7 +1478,7 @@ func (tx *DeclarationTransformer) updateParamList(node *ast.Node, params *ast.Pa } func (tx *DeclarationTransformer) updateParamListEx(node *ast.Node, params *ast.ParameterList, modifierMask ast.ModifierFlags) *ast.ParameterList { - if tx.host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 { return tx.Factory().NewNodeList([]*ast.Node{}) } results := make([]*ast.Node, len(params.Nodes)) @@ -1508,6 +1545,10 @@ func (tx *DeclarationTransformer) filterBindingPatternInitializers(node *ast.Nod if elem.PropertyName() != nil && ast.IsComputedPropertyName(elem.PropertyName()) && ast.IsEntityNameExpression(elem.PropertyName().Expression()) { tx.checkEntityNameVisibility(elem.PropertyName().Expression(), tx.enclosingDeclaration) } + if elem.Name() == nil { + elements = append(elements, elem) + continue + } elements = append(elements, tx.Factory().UpdateBindingElement( elem.AsBindingElement(), diff --git a/internal/declarations/util.go b/internal/declarations/util.go index dfd5bb97a6..df3d7e48e6 100644 --- a/internal/declarations/util.go +++ b/internal/declarations/util.go @@ -98,6 +98,11 @@ func getBindingNameVisible(resolver printer.EmitResolver, elem *ast.Node) bool { if ast.IsOmittedExpression(elem) { return false } + // TODO: parseArrayBindingElement _never_ parses out an OmittedExpression anymore, instead producing a nameless binding element + // Audit if OmittedExpression should be removed + if elem.Name() == nil { + return false + } if ast.IsBindingPattern(elem.Name()) { // If any child binding pattern element has been marked visible (usually by collect linked aliases), then this is visible for _, elem := range elem.Name().AsBindingPattern().Elements.Nodes { diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 7b65f042ac..a65e27ff16 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -1762,6 +1762,8 @@ func (p *Printer) emitTypeElement(node *ast.TypeElement) { p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration()) case ast.KindIndexSignature: p.emitIndexSignature(node.AsIndexSignatureDeclaration()) + case ast.KindNotEmittedTypeElement: + p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement()) default: panic(fmt.Sprintf("unexpected TypeElement: %v", node.Kind)) } @@ -3420,6 +3422,10 @@ func (p *Printer) emitNotEmittedStatement(node *ast.NotEmittedStatement) { p.exitNode(node.AsNode(), p.enterNode(node.AsNode())) } +func (p *Printer) emitNotEmittedTypeElement(node *ast.NotEmittedTypeElement) { + p.exitNode(node.AsNode(), p.enterNode(node.AsNode())) +} + // // Declarations // @@ -4920,7 +4926,8 @@ func (p *Printer) Write(node *ast.Node, sourceFile *ast.SourceFile, writer EmitT panic("not implemented") // Transformation nodes - // case ast.KindNotEmittedTypeElement: + case ast.KindNotEmittedTypeElement: + p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement()) default: switch { diff --git a/internal/printer/singlelinestringwriter.go b/internal/printer/singlelinestringwriter.go index 4642b47097..60ed86ef53 100644 --- a/internal/printer/singlelinestringwriter.go +++ b/internal/printer/singlelinestringwriter.go @@ -2,14 +2,20 @@ package printer import ( "strings" + "sync" "unicode/utf8" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/stringutil" ) -// TODO: Definitely not threadsafe - make one per checker instead of one global one (threadlocal storage would be neat) -var SingleLineStringWriter EmitTextWriter = &singleLineStringWriter{} +var SingleLineStringWriterPool sync.Pool = sync.Pool{ + New: func() any { + return &singleLineStringWriter{} + }, +} + +var _ EmitTextWriter = &singleLineStringWriter{} type singleLineStringWriter struct { builder strings.Builder From 1d83436a24122f8ff89083e876f5c764e0970474 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Apr 2025 14:52:41 -0700 Subject: [PATCH 12/15] Fix whitespace differentials, type arg list printback in diags, overload printback --- internal/checker/checker.go | 5 +- internal/checker/nodebuilder.go | 83 ++++++++++++++++++++++++++------- internal/checker/printer.go | 28 +++++------ 3 files changed, 84 insertions(+), 32 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 26962ea338..cf8c556151 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -824,6 +824,7 @@ type Checker struct { markNodeAssignments func(*ast.Node) bool emitResolver *emitResolver emitResolverOnce sync.Once + diagnosticConstructionContext *printer.EmitContext nodeBuilder NodeBuilderInterface _jsxNamespace string _jsxFactoryEntity *ast.Node @@ -1032,8 +1033,8 @@ func NewChecker(program Program) *Checker { c.getGlobalClassAccessorDecoratorTargetType = c.getGlobalTypeResolver("ClassAccessorDecoratorTarget", 2 /*arity*/, true /*reportErrors*/) c.getGlobalClassAccessorDecoratorResultType = c.getGlobalTypeResolver("ClassAccessorDecoratorResult", 2 /*arity*/, true /*reportErrors*/) c.getGlobalClassFieldDecoratorContextType = c.getGlobalTypeResolver("ClassFieldDecoratorContext", 2 /*arity*/, true /*reportErrors*/) - diagnosticConstructionContext := printer.NewEmitContext() - c.nodeBuilder = NewNodeBuilderAPI(c, diagnosticConstructionContext) + c.diagnosticConstructionContext = printer.NewEmitContext() + c.nodeBuilder = NewNodeBuilderAPI(c, c.diagnosticConstructionContext) c.initializeClosures() c.initializeIterationResolvers() c.initializeChecker() diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 482628aa2c..8e4d9d4630 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -76,7 +76,7 @@ type NodeBuilderContext struct { typeParameterNames map[TypeId]*ast.Identifier typeParameterNamesByText map[string]struct{} typeParameterNamesByTextNextNameCount map[string]int - typeParameterSymbolList map[int]struct{} + typeParameterSymbolList map[ast.SymbolId]struct{} } type NodeBuilder struct { @@ -375,8 +375,7 @@ func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, } func (b *NodeBuilder) createEntityNameFromSymbolChain(chain []*ast.Symbol, index int) *ast.Node { - // !!! TODO: smuggle type arguments out - // typeParameterNodes := lookupTypeParameterNodes(chain, index, context); + // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) symbol := chain[index] if index == 0 { @@ -421,9 +420,12 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, // module is root, must use `ImportTypeNode` var nonRootParts *ast.Node if len(chain) > 1 { - nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1) + nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1, typeArguments) + } + typeParameterNodes := typeArguments + if typeParameterNodes == nil { + typeParameterNodes = b.lookupTypeParameterNodes(chain, 0) } - typeParameterNodes := typeArguments /*|| lookupTypeParameterNodes(chain, 0, context);*/ // !!! TODO: type argument smuggling contextFile := ast.GetSourceFileOfNode(b.e.MostOriginal(b.ctx.enclosingDeclaration)) // TODO: Just use b.ctx.enclosingFile ? Or is the delayed lookup important for context moves? targetFile := getSourceFileOfModule(chain[0]) var specifier string @@ -497,7 +499,7 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, } - entityName := b.createAccessFromSymbolChain(chain, len(chain)-1, 0) + entityName := b.createAccessFromSymbolChain(chain, len(chain)-1, 0, typeArguments) if ast.IsIndexedAccessTypeNode(entityName) { return entityName // Indexed accesses can never be `typeof` } @@ -505,10 +507,11 @@ func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, return b.f.NewTypeQueryNode(entityName, nil) } // !!! TODO: smuggle type arguments out + // Move type arguments from last identifier on chain to type reference // const lastId = isIdentifier(entityName) ? entityName : entityName.right; // const lastTypeArgs = getIdentifierTypeArguments(lastId); // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); - return b.f.NewTypeReferenceNode(entityName, nil) + return b.f.NewTypeReferenceNode(entityName, typeArguments) } func getTopmostIndexedAccessType(node *ast.IndexedAccessTypeNode) *ast.IndexedAccessTypeNode { @@ -518,9 +521,12 @@ func getTopmostIndexedAccessType(node *ast.IndexedAccessTypeNode) *ast.IndexedAc return node } -func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int, stopper int) *ast.Node { +func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int, stopper int, overrideTypeArguments *ast.NodeList) *ast.Node { // !!! TODO: smuggle type arguments out - // const typeParameterNodes = index === (chain.length - 1) ? overrideTypeArguments : lookupTypeParameterNodes(chain, index, context); + typeParameterNodes := overrideTypeArguments + if index != (len(chain) - 1) { + typeParameterNodes = b.lookupTypeParameterNodes(chain, index) + } symbol := chain[index] var parent *ast.Symbol if index > 0 { @@ -557,7 +563,7 @@ func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int } } if name != nil && ast.IsComputedPropertyName(name) && ast.IsEntityName(name.AsComputedPropertyName().Expression) { - lhs := b.createAccessFromSymbolChain(chain, index-1, stopper) + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) if ast.IsEntityName(lhs) { return b.f.NewIndexedAccessTypeNode( b.f.NewParenthesizedTypeNode(b.f.NewTypeQueryNode(lhs, nil)), @@ -573,7 +579,7 @@ func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent)[symbol.Name] != nil && b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent)[symbol.Name], symbol) != nil { // Should use an indexed access - lhs := b.createAccessFromSymbolChain(chain, index-1, stopper) + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) if ast.IsIndexedAccessTypeNode(lhs) { return b.f.NewIndexedAccessTypeNode( lhs, @@ -581,7 +587,7 @@ func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int ) } return b.f.NewIndexedAccessTypeNode( - b.f.NewTypeReferenceNode(lhs /*!!! todo: type args*/, nil), + b.f.NewTypeReferenceNode(lhs, typeParameterNodes), b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), ) } @@ -593,7 +599,7 @@ func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int // identifier.symbol = symbol; if index > stopper { - lhs := b.createAccessFromSymbolChain(chain, index-1, stopper) + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) if !ast.IsEntityName(lhs) { panic("Impossible construct - an export of an indexed access cannot be reachable") } @@ -609,7 +615,6 @@ func (b *NodeBuilder) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlag } func (b *NodeBuilder) createExpressionFromSymbolChain(chain []*ast.Symbol, index int) *ast.Expression { - // !!! TODO: smuggle type arguments out // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) symbol := chain[index] @@ -801,8 +806,54 @@ func (b *NodeBuilder) getNameOfSymbolAsWritten(symbol *ast.Symbol) string { return symbol.Name } +// The full set of type parameters for a generic class or interface type consists of its outer type parameters plus +// its locally declared type parameters. +func (b *NodeBuilder) getTypeParametersOfClassOrInterface(symbol *ast.Symbol) []*Type { + result := make([]*Type, 0) + result = append(result, b.ch.getOuterTypeParametersOfClassOrInterface(symbol)...) + result = append(result, b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)...) + return result +} + func (b *NodeBuilder) lookupTypeParameterNodes(chain []*ast.Symbol, index int) *ast.TypeParameterList { - return nil // !!! TODO: nested reference type parameter synthesis + // Debug.assert(chain && 0 <= index && index < chain.length); // !!! + symbol := chain[index] + symbolId := ast.GetSymbolId(symbol) + if !b.ctx.hasCreatedTypeParameterSymbolList { + b.ctx.hasCreatedTypeParameterSymbolList = true + b.ctx.typeParameterSymbolList = make(map[ast.SymbolId]struct{}) + } + _, ok := b.ctx.typeParameterSymbolList[symbolId] + if ok { + return nil + } + b.ctx.typeParameterSymbolList[symbolId] = struct{}{} + + if b.ctx.flags&nodebuilder.FlagsWriteTypeParametersInQualifiedName != 0 && index < (len(chain)-1) { + parentSymbol := symbol + nextSymbol := chain[index+1] + + if nextSymbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { + targetSymbol := parentSymbol + if parentSymbol.Flags&ast.SymbolFlagsAlias != 0 { + targetSymbol = b.ch.resolveAlias(parentSymbol) + } + params := b.getTypeParametersOfClassOrInterface(targetSymbol) + targetMapper := b.ch.valueSymbolLinks.Get(nextSymbol).mapper + if targetMapper != nil { + params = core.Map(params, targetMapper.Map) + } + return b.mapToTypeNodes(params) + } else { + typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol) + if len(typeParameterNodes) > 0 { + return b.f.NewNodeList(typeParameterNodes) + } + return nil + } + } + + return nil } // TODO: move `lookupSymbolChain` and co to `symbolaccessibility.go` (but getSpecifierForModuleSymbol uses much context which makes that hard?) @@ -2203,7 +2254,7 @@ func (b *NodeBuilder) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { abstractSignatures := core.Filter(ctorSigs, func(signature *Signature) bool { return signature.flags&SignatureFlagsAbstract != 0 }) - if len(callSigs) > 0 { + if len(abstractSignatures) > 0 { types := core.Map(abstractSignatures, func(s *Signature) *Type { return b.ch.getOrCreateTypeFromSignature(s, nil) }) diff --git a/internal/checker/printer.go b/internal/checker/printer.go index fe6b78a90e..7e14203bd7 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -12,22 +12,22 @@ import ( ) // TODO: Memoize once per checker to retain threadsafety -func createPrinterWithDefaults() *printer.Printer { - return printer.NewPrinter(printer.PrinterOptions{}, printer.PrintHandlers{}, nil) +func createPrinterWithDefaults(emitContext *printer.EmitContext) *printer.Printer { + return printer.NewPrinter(printer.PrinterOptions{}, printer.PrintHandlers{}, emitContext) } -func createPrinterWithRemoveComments() *printer.Printer { - return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil) +func createPrinterWithRemoveComments(emitContext *printer.EmitContext) *printer.Printer { + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) } -func createPrinterWithRemoveCommentsOmitTrailingSemicolon() *printer.Printer { +func createPrinterWithRemoveCommentsOmitTrailingSemicolon(emitContext *printer.EmitContext) *printer.Printer { // TODO: OmitTrailingSemicolon support - return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil) + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) } -func createPrinterWithRemoveCommentsNeverAsciiEscape() *printer.Printer { +func createPrinterWithRemoveCommentsNeverAsciiEscape(emitContext *printer.EmitContext) *printer.Printer { // TODO: NeverAsciiEscape support - return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, nil) + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) } func getTrailingSemicolonDeferringWriter(writer printer.EmitTextWriter) printer.EmitTextWriter { @@ -60,9 +60,9 @@ func (c *Checker) typeToStringEx(type_ *Type, enclosingDeclaration *ast.Node, fl // Otherwise, we always strip comments out. var printer *printer.Printer if type_ == c.unresolvedType { - printer = createPrinterWithDefaults() + printer = createPrinterWithDefaults(c.diagnosticConstructionContext) } else { - printer = createPrinterWithRemoveComments() + printer = createPrinterWithRemoveComments(c.diagnosticConstructionContext) } var sourceFile *ast.SourceFile if enclosingDeclaration != nil { @@ -126,9 +126,9 @@ func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast } var printer_ *printer.Printer if enclosingDeclaration != nil && enclosingDeclaration.Kind == ast.KindSourceFile { - printer_ = createPrinterWithRemoveCommentsNeverAsciiEscape() + printer_ = createPrinterWithRemoveCommentsNeverAsciiEscape(c.diagnosticConstructionContext) } else { - printer_ = createPrinterWithRemoveComments() + printer_ = createPrinterWithRemoveComments(c.diagnosticConstructionContext) } var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node @@ -166,7 +166,7 @@ func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration } combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName sig := c.nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) - printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon() + printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon(c.diagnosticConstructionContext) var sourceFile *ast.SourceFile if enclosingDeclaration != nil { sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) @@ -193,7 +193,7 @@ func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosin } combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName predicate := c.nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 - printer_ := createPrinterWithRemoveComments() + printer_ := createPrinterWithRemoveComments(c.diagnosticConstructionContext) var sourceFile *ast.SourceFile if enclosingDeclaration != nil { sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) From 1e5b5a5afc56d1c7785de263d4e7d38124780076 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Apr 2025 15:23:28 -0700 Subject: [PATCH 13/15] Fix declaration emit for const refs to enums --- internal/checker/emitresolver.go | 2 +- internal/checker/nodebuilder.go | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 7946418b16..7ed32ed4c0 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -768,7 +768,7 @@ func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, } var enumResult *ast.Node - if type_.flags&TypeFlagsEnum != 0 { + if type_.flags&TypeFlagsEnumLike != 0 { requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context enumResult = requestNodeBuilder.symbolToExpression(type_.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker) // What about regularTrueType/regularFalseType - since those aren't fresh, we never make initializers from them diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 8e4d9d4630..077f4bf4c9 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -637,7 +637,7 @@ func (b *NodeBuilder) createExpressionFromSymbolChain(chain []*ast.Symbol, index // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); // identifier.symbol = symbol; if index > 0 { - b.f.NewPropertyAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, identifier, ast.NodeFlagsNone) + return b.f.NewPropertyAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, identifier, ast.NodeFlagsNone) } return identifier } @@ -764,26 +764,23 @@ func (b *NodeBuilder) getNameOfSymbolAsWritten(symbol *ast.Symbol) string { return "default" } if len(symbol.Declarations) > 0 { - declaration := core.FirstNonNil(symbol.Declarations, ast.GetNameOfDeclaration) // Try using a declaration with a name, first - if declaration != nil { - name := ast.GetNameOfDeclaration(declaration) - if name != nil { - // !!! TODO: JS Object.defineProperty declarations - // if ast.IsCallExpression(declaration) && ast.IsBindableObjectDefinePropertyCall(declaration) { - // return symbol.Name - // } - if ast.IsComputedPropertyName(name) && symbol.CheckFlags&ast.CheckFlagsLate == 0 { - if b.ch.valueSymbolLinks.Has(symbol) && b.ch.valueSymbolLinks.Get(symbol).nameType != nil && b.ch.valueSymbolLinks.Get(symbol).nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { - result := b.getNameOfSymbolFromNameType(symbol) - if len(result) > 0 { - return result - } + name := core.FirstNonNil(symbol.Declarations, ast.GetNameOfDeclaration) // Try using a declaration with a name, first + if name != nil { + // !!! TODO: JS Object.defineProperty declarations + // if ast.IsCallExpression(declaration) && ast.IsBindableObjectDefinePropertyCall(declaration) { + // return symbol.Name + // } + if ast.IsComputedPropertyName(name) && symbol.CheckFlags&ast.CheckFlagsLate == 0 { + if b.ch.valueSymbolLinks.Has(symbol) && b.ch.valueSymbolLinks.Get(symbol).nameType != nil && b.ch.valueSymbolLinks.Get(symbol).nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + result := b.getNameOfSymbolFromNameType(symbol) + if len(result) > 0 { + return result } } - return scanner.DeclarationNameToString(name) } + return scanner.DeclarationNameToString(name) } - declaration = symbol.Declarations[0] // Declaration may be nameless, but we'll try anyway + declaration := symbol.Declarations[0] // Declaration may be nameless, but we'll try anyway if declaration.Parent != nil && declaration.Parent.Kind == ast.KindVariableDeclaration { return scanner.DeclarationNameToString(declaration.Parent.AsVariableDeclaration().Name()) } From 6fc53790f9d6c4b2e87055056d515e7d54e681ab Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Apr 2025 15:55:18 -0700 Subject: [PATCH 14/15] Use more strada-accurate baseline type printing to correct unique symbol printback behavior --- internal/checker/emitresolver.go | 10 +- internal/checker/nodebuilderapi.go | 95 +++++++++++-------- internal/checker/printer.go | 10 +- .../tsbaseline/type_symbol_baseline.go | 32 ++++--- 4 files changed, 80 insertions(+), 67 deletions(-) diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 7ed32ed4c0..6bb4cab8f4 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -744,7 +744,7 @@ func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *print return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) } requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.serializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) + return requestNodeBuilder.SerializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) } func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { @@ -755,7 +755,7 @@ func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context // // Get type of the symbol if this is the valid symbol otherwise get type at location symbol := r.checker.getSymbolOfDeclaration(declaration) - return requestNodeBuilder.serializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) + return requestNodeBuilder.SerializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) } func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node { @@ -770,7 +770,7 @@ func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, var enumResult *ast.Node if type_.flags&TypeFlagsEnumLike != 0 { requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context - enumResult = requestNodeBuilder.symbolToExpression(type_.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker) + enumResult = requestNodeBuilder.SymbolToExpression(type_.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker) // What about regularTrueType/regularFalseType - since those aren't fresh, we never make initializers from them // TODO: handle those if this function is ever used for more than initializers in declaration emit } else if type_ == r.checker.trueType { @@ -824,7 +824,7 @@ func (r *emitResolver) CreateTypeOfExpression(emitContext *printer.EmitContext, } requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.serializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) + return requestNodeBuilder.SerializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) } func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { @@ -862,7 +862,7 @@ func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC // if info.components { // !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures // } - node := requestNodeBuilder.indexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker) + node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker) if node != nil && isStatic { mods := node.Modifiers() mods = emitContext.Factory.NewModifierList(append([]*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, mods.Nodes...)) diff --git a/internal/checker/nodebuilderapi.go b/internal/checker/nodebuilderapi.go index e8fbd85716..b8c0913ab3 100644 --- a/internal/checker/nodebuilderapi.go +++ b/internal/checker/nodebuilderapi.go @@ -7,20 +7,22 @@ import ( ) type NodeBuilderInterface interface { - typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node - symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node - symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + EmitContext() *printer.EmitContext + + TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + TypePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SerializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SerializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + IndexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SignatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + SymbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + TypeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + SymbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node } type NodeBuilderAPI struct { @@ -28,6 +30,11 @@ type NodeBuilderAPI struct { impl *NodeBuilder } +// EmitContext implements NodeBuilderInterface. +func (b *NodeBuilderAPI) EmitContext() *printer.EmitContext { + return b.impl.e +} + func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) { b.impl.ctx = &NodeBuilderContext{ tracker: tracker, @@ -89,14 +96,14 @@ func (b *NodeBuilderAPI) exitContextCheck() { } } -// indexInfoToIndexSignatureDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) indexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// IndexInfoToIndexSignatureDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) IndexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.indexInfoToIndexSignatureDeclarationHelper(info, nil)) } -// serializeReturnTypeForSignature implements NodeBuilderInterface. -func (b *NodeBuilderAPI) serializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SerializeReturnTypeForSignature implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SerializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) signature := b.impl.ch.getSignatureFromDeclaration(signatureDeclaration) symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration) @@ -107,74 +114,74 @@ func (b *NodeBuilderAPI) serializeReturnTypeForSignature(signatureDeclaration *a return b.exitContext(b.impl.serializeInferredReturnTypeForSignature(signature, returnType)) } -// serializeTypeForDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) serializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SerializeTypeForDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.serializeTypeForDeclaration(declaration, nil, symbol)) } -// serializeTypeForExpression implements NodeBuilderInterface. -func (b *NodeBuilderAPI) serializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SerializeTypeForExpression implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SerializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.serializeTypeForExpression(expr)) } -// signatureToSignatureDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) signatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SignatureToSignatureDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SignatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.signatureToSignatureDeclarationHelper(signature, kind, nil)) } -// symbolTableToDeclarationStatements implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { +// SymbolTableToDeclarationStatements implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContextSlice(b.impl.symbolTableToDeclarationStatements(symbolTable)) } -// symbolToEntityName implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SymbolToEntityName implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToName(symbol, meaning, false)) } -// symbolToExpression implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SymbolToExpression implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToExpression(symbol, meaning)) } -// symbolToNode implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SymbolToNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToNode(symbol, meaning)) } -// symbolToParameterDeclaration implements NodeBuilderInterface. -func (b NodeBuilderAPI) symbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// SymbolToParameterDeclaration implements NodeBuilderInterface. +func (b NodeBuilderAPI) SymbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.symbolToParameterDeclaration(symbol, false)) } -// symbolToTypeParameterDeclarations implements NodeBuilderInterface. -func (b *NodeBuilderAPI) symbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { +// SymbolToTypeParameterDeclarations implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContextSlice(b.impl.symbolToTypeParameterDeclarations(symbol)) } -// typeParameterToDeclaration implements NodeBuilderInterface. -func (b *NodeBuilderAPI) typeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// TypeParameterToDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) TypeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.typeParameterToDeclaration(parameter)) } -// typePredicateToTypePredicateNode implements NodeBuilderInterface. -func (b *NodeBuilderAPI) typePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// TypePredicateToTypePredicateNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) TypePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.typePredicateToTypePredicateNode(predicate)) } -// typeToTypeNode implements NodeBuilderInterface. -func (b *NodeBuilderAPI) typeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { +// TypeToTypeNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) return b.exitContext(b.impl.typeToTypeNode(typ)) } @@ -185,3 +192,7 @@ func NewNodeBuilderAPI(ch *Checker, e *printer.EmitContext) *NodeBuilderAPI { impl := NewNodeBuilder(ch, e) return &NodeBuilderAPI{impl: &impl, ctxStack: make([]*NodeBuilderContext, 0, 1)} } + +func (c *Checker) GetDiagnosticNodeBuilder() NodeBuilderInterface { + return c.nodeBuilder +} diff --git a/internal/checker/printer.go b/internal/checker/printer.go index 7e14203bd7..7bbde96c13 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -52,7 +52,7 @@ func (c *Checker) typeToStringEx(type_ *Type, enclosingDeclaration *ast.Node, fl if noTruncation { combinedFlags = combinedFlags | nodebuilder.FlagsNoTruncation } - typeNode := c.nodeBuilder.typeToTypeNode(type_, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) + typeNode := c.nodeBuilder.TypeToTypeNode(type_, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) if typeNode == nil { panic("should always get typenode") } @@ -133,9 +133,9 @@ func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node if flags&SymbolFormatFlagsAllowAnyNodeKind != 0 { - builder = c.nodeBuilder.symbolToNode + builder = c.nodeBuilder.SymbolToNode } else { - builder = c.nodeBuilder.symbolToEntityName + builder = c.nodeBuilder.SymbolToEntityName } entity := builder(symbol, meaning, enclosingDeclaration, nodeFlags, internalNodeFlags, nil) // TODO: GH#18217 printer_.Write(entity /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 @@ -165,7 +165,7 @@ func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName - sig := c.nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) + sig := c.nodeBuilder.SignatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon(c.diagnosticConstructionContext) var sourceFile *ast.SourceFile if enclosingDeclaration != nil { @@ -192,7 +192,7 @@ func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosin writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName - predicate := c.nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 + predicate := c.nodeBuilder.TypePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 printer_ := createPrinterWithRemoveComments(c.diagnosticConstructionContext) var sourceFile *ast.SourceFile if enclosingDeclaration != nil { diff --git a/internal/testutil/tsbaseline/type_symbol_baseline.go b/internal/testutil/tsbaseline/type_symbol_baseline.go index ef4336490d..0e87e8c6d8 100644 --- a/internal/testutil/tsbaseline/type_symbol_baseline.go +++ b/internal/testutil/tsbaseline/type_symbol_baseline.go @@ -11,6 +11,8 @@ import ( "github.com/microsoft/typescript-go/internal/checker" "github.com/microsoft/typescript-go/internal/compiler" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" "github.com/microsoft/typescript-go/internal/testutil" "github.com/microsoft/typescript-go/internal/testutil/baseline" @@ -370,21 +372,21 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b !isIntrinsicJsxTag(node, walker.currentSourceFile) { typeString = t.AsIntrinsicType().IntrinsicName() } else { - // !!! TODO: full type printing and underline when we have node builder - // const typeFormatFlags = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.AllowUniqueESSymbolType | ts.TypeFormatFlags.GenerateNamesForShadowedTypeParams; - // let typeNode = this.checker.typeToTypeNode(type, node.parent, (typeFormatFlags & ts.TypeFormatFlags.NodeBuilderFlagsMask) | ts.NodeBuilderFlags.IgnoreErrors, ts.InternalNodeBuilderFlags.AllowUnresolvedNames)!; - // if (ts.isIdentifier(node) && ts.isTypeAliasDeclaration(node.parent) && node.parent.name === node && ts.isIdentifier(typeNode) && ts.idText(typeNode) === ts.idText(node)) { - // // for a complex type alias `type T = ...`, showing "T : T" isn't very helpful for type tests. When the type produced is the same as - // // the name of the type alias, recreate the type string without reusing the alias name - // typeNode = this.checker.typeToTypeNode(type, node.parent, ((typeFormatFlags | ts.TypeFormatFlags.InTypeAlias) & ts.TypeFormatFlags.NodeBuilderFlagsMask) | ts.NodeBuilderFlags.IgnoreErrors)!; - // } - - // const { printer, writer, underliner, reset } = createSyntheticNodeUnderliningPrinter(); - // printer.writeNode(ts.EmitHint.Unspecified, typeNode, this.currentSourceFile, writer); - // typeString = writer.getText(); - // underline = underliner.getText(); - // reset(); - typeString = fileChecker.TypeToString(t) + ctx := printer.NewEmitContext() + builder := checker.NewNodeBuilderAPI(fileChecker, ctx) + typeFormatFlags := checker.TypeFormatFlagsNoTruncation | checker.TypeFormatFlagsAllowUniqueESSymbolType | checker.TypeFormatFlagsGenerateNamesForShadowedTypeParams + typeNode := builder.TypeToTypeNode(t, node.Parent, nodebuilder.Flags(typeFormatFlags&checker.TypeFormatFlagsNodeBuilderFlagsMask)|nodebuilder.FlagsIgnoreErrors, nodebuilder.InternalFlagsAllowUnresolvedNames, nil) + if ast.IsIdentifier(node) && ast.IsTypeAliasDeclaration(node.Parent) && node.Parent.Name() == node && ast.IsIdentifier(typeNode) && typeNode.AsIdentifier().Text == node.AsIdentifier().Text { + // for a complex type alias `type T = ...`, showing "T : T" isn't very helpful for type tests. When the type produced is the same as + // the name of the type alias, recreate the type string without reusing the alias name + typeNode = builder.TypeToTypeNode(t, node.Parent, nodebuilder.Flags((typeFormatFlags|checker.TypeFormatFlagsInTypeAlias)&checker.TypeFormatFlagsNodeBuilderFlagsMask)|nodebuilder.FlagsIgnoreErrors, nodebuilder.InternalFlagsAllowUnresolvedNames, nil) + } + + // !!! TODO: port underline printer, memoize + writer := printer.NewTextWriter("") + printer := printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, ctx) + printer.Write(typeNode, walker.currentSourceFile, writer, nil) + typeString = writer.String() } return &typeWriterResult{ line: line, From 867d83042e49ee83bd93ffe0e5a935dd09affc87 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Apr 2025 16:29:53 -0700 Subject: [PATCH 15/15] Fix some symbol chain printback issues --- internal/checker/nodebuilder.go | 1 + internal/checker/symbolaccessibility.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go index 077f4bf4c9..fd6adecaaf 100644 --- a/internal/checker/nodebuilder.go +++ b/internal/checker/nodebuilder.go @@ -572,6 +572,7 @@ func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int } return lhs } + symbolName = b.getNameOfSymbolAsWritten(symbol) } b.ctx.approximateLength += len(symbolName) + 1 diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index d480f9b903..3d45673c76 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -466,10 +466,10 @@ func (ch *Checker) trySymbolTable( // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name (!ctx.useOnlyExternalAliasing || core.Some(symbolFromSymbolTable.Declarations, ast.IsExternalModuleImportEqualsDeclaration)) && // If we're looking up a local name to reference directly, omit namespace reexports, otherwise when we're trawling through an export list to make a dotted name, we can keep it - (!isLocalNameLookup || core.Some(symbolFromSymbolTable.Declarations, isNamespaceReexportDeclaration)) && + (isLocalNameLookup && !core.Some(symbolFromSymbolTable.Declarations, isNamespaceReexportDeclaration) || !isLocalNameLookup) && // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ // See similar comment in `resolveName` for details - (ignoreQualification || getDeclarationsOfKind(symbolFromSymbolTable, ast.KindExportSpecifier) == nil) { + (ignoreQualification || len(getDeclarationsOfKind(symbolFromSymbolTable, ast.KindExportSpecifier)) == 0) { resolvedImportedSymbol := ch.resolveAlias(symbolFromSymbolTable) candidate := ch.getCandidateListForSymbol(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) if len(candidate) > 0 {