diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 9b52a55d92310..9983614324115 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -23,6 +23,7 @@ import ( "github.com/microsoft/TypeScript/tsc/internal/jsnum" "github.com/microsoft/TypeScript/tsc/internal/module" "github.com/microsoft/TypeScript/tsc/internal/modulespecifiers" + "github.com/microsoft/TypeScript/tsc/internal/printer" "github.com/microsoft/TypeScript/tsc/internal/scanner" "github.com/microsoft/TypeScript/tsc/internal/stringutil" "github.com/microsoft/TypeScript/tsc/internal/tracing" @@ -888,8 +889,7 @@ type Checker struct { isStringIndexSignatureOnlyType func(*Type) bool markNodeAssignments func(*ast.Node) bool compareTypesAssignable TypeComparer - emitResolver *EmitResolver - emitResolverOnce sync.Once + emitResolverLinks EmitResolverLinks _jsxNamespace string _jsxFactoryEntity *ast.Node skipDirectInferenceNodes collections.Set[*ast.Node] @@ -32687,12 +32687,8 @@ func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type { return c.getTypeOfNode(ast.GetReparsedNodeForNode(node)) } -func (c *Checker) GetEmitResolver() *EmitResolver { - c.emitResolverOnce.Do(func() { - c.emitResolver = newEmitResolver(c) - }) - - return c.emitResolver +func (c *Checker) NewEmitResolver(emitContext *printer.EmitContext) *EmitResolver { + return newEmitResolver(c, emitContext) } func (c *Checker) GetAliasedSymbol(symbol *ast.Symbol) *ast.Symbol { diff --git a/tsc/internal/checker/emitresolver.go b/tsc/internal/checker/emitresolver.go index 9bc590fd179bf..4953359de14da 100644 --- a/tsc/internal/checker/emitresolver.go +++ b/tsc/internal/checker/emitresolver.go @@ -31,25 +31,44 @@ type DeclarationFileLinks struct { aliasesMarked bool // if file has had alias visibility marked } +type EmitResolverLinks struct { + jsxLinks core.LinkStore[*ast.Node, JSXLinks] + declarationLinks core.LinkStore[*ast.Node, DeclarationLinks] + declarationFileLinks core.LinkStore[*ast.Node, DeclarationFileLinks] +} + type EmitResolver struct { checker *Checker checkerMu *sync.Mutex + emitContext *printer.EmitContext + requestNodeBuilder *NodeBuilder isValueAliasDeclaration func(node *ast.Node) bool aliasMarkingVisitor func(node *ast.Node) bool referenceResolver binder.ReferenceResolver - jsxLinks core.LinkStore[*ast.Node, JSXLinks] - declarationLinks core.LinkStore[*ast.Node, DeclarationLinks] - declarationFileLinks core.LinkStore[*ast.Node, DeclarationFileLinks] } -func newEmitResolver(checker *Checker) *EmitResolver { - e := &EmitResolver{checker: checker} +func newEmitResolver(checker *Checker, emitContext *printer.EmitContext) *EmitResolver { + if emitContext == nil { + panic("EmitResolver requires an EmitContext") + } + e := &EmitResolver{checker: checker, emitContext: emitContext} e.isValueAliasDeclaration = e.isValueAliasDeclarationWorker e.aliasMarkingVisitor = e.aliasMarkingVisitorWorker e.checkerMu = &checker.mu return e } +func (r *EmitResolver) EmitContext() *printer.EmitContext { + return r.emitContext +} + +func (r *EmitResolver) nodeBuilder() *NodeBuilder { + if r.requestNodeBuilder == nil { + r.requestNodeBuilder = NewNodeBuilder(r.checker, r.emitContext) + } + return r.requestNodeBuilder +} + func (r *EmitResolver) GetJsxFactoryEntity(location *ast.Node) *ast.Node { r.checkerMu.Lock() defer r.checkerMu.Unlock() @@ -105,141 +124,16 @@ 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 - } - if node == nil { - return false - } - - links := r.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) 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.KindJSTypeAliasDeclaration, - ast.KindFunctionDeclaration, - ast.KindEnumDeclaration, - ast.KindImportEqualsDeclaration: - if ast.IsVariableDeclaration(node) { - if ast.IsBindingPattern(node.Name()) && - len(node.Name().Elements()) == 0 { - // If the binding pattern is empty, this variable declaration is not visible - return false - } - // falls through - } - // External module augmentation is always visible - // A @typedef at top-level in an external module is always visible - if ast.IsExternalModuleAugmentation(node) || ast.IsImplicitlyExportedJSDocDeclaration(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 - - // An `export {X}` (without a module specifier) is itself a visible re-export of - // the named binding; it contributes to the symbol's external visibility. - case ast.KindExportSpecifier: - exportDecl := node.Parent.Parent - if ast.IsExportDeclaration(exportDecl) && exportDecl.AsExportDeclaration().ModuleSpecifier == nil { - return r.isDeclarationVisible(exportDecl.Parent) - } - return false - - default: - return false - } + return r.checker.isDeclarationVisible(node) } func (r *EmitResolver) PrecalculateDeclarationEmitVisibility(file *ast.SourceFile) { r.checkerMu.Lock() defer r.checkerMu.Unlock() - if r.declarationFileLinks.Get(file.AsNode()).aliasesMarked { + if r.checker.emitResolverLinks.declarationFileLinks.Get(file.AsNode()).aliasesMarked { return } - r.declarationFileLinks.Get(file.AsNode()).aliasesMarked = true + r.checker.emitResolverLinks.declarationFileLinks.Get(file.AsNode()).aliasesMarked = true // TODO: Does this even *have* to be an upfront walk? If it's not possible for a // import a = a.b.c statement to chain into exposing a statement in a sibling scope, // it could at least be pushed into scope entry - then it wouldn't need to be recursive. @@ -293,7 +187,7 @@ func (r *EmitResolver) markLinkedAliases(node *ast.Node) { var nextSymbol *ast.Symbol for _, declaration := range exportSymbol.Declarations { - r.declarationLinks.Get(declaration).isVisible = core.TSTrue + r.checker.emitResolverLinks.declarationLinks.Get(declaration).isVisible = core.TSTrue if ast.IsInternalModuleImportEqualsDeclaration(declaration) { // Add the referenced top container visible @@ -308,160 +202,10 @@ func (r *EmitResolver) markLinkedAliases(node *ast.Node) { } } -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 || - entityName.Parent.Kind == ast.KindBinaryExpression { - // 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.Expression() == entityName) || - (entityName.Parent.Kind == ast.KindElementAccessExpression && entityName.Parent.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) IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) printer.SymbolAccessibilityResult { r.checkerMu.Lock() defer r.checkerMu.Unlock() - return r.isEntityNameVisible(entityName, enclosingDeclaration, true) -} - -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} - } - - meaning := getMeaningOfEntityNameReference(entityName) - firstIdentifier := ast.GetFirstIdentifier(entityName) - - symbol := r.checker.resolveName(enclosingDeclaration, firstIdentifier.Text(), meaning, nil, false, false) - - if symbol != nil && symbol.Flags&ast.SymbolFlagsTypeParameter != 0 && meaning&ast.SymbolFlagsType != 0 { - return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} - } - - if symbol == nil && ast.IsThisIdentifier(firstIdentifier) { - sym := r.checker.getSymbolOfDeclaration(r.checker.getThisContainer(firstIdentifier, false, false)) - 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, - } -} - -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) { - r.declarationLinks.Get(declaration).isVisible = core.TSTrue - if aliasesToMakeVisibleSet == nil { - aliasesToMakeVisibleSet = make(map[ast.NodeId]*ast.Node) - } - aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = aliasingStatement - } - } 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 { - rootDeclaration := ast.WalkUpBindingElementsAndPatterns(declaration) - if ast.IsParameterDeclaration(rootDeclaration) { - return nil - } - variableStatement := rootDeclaration.Parent.Parent - if !ast.IsVariableStatement(variableStatement) { - return nil - } - 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)), - } + return r.checker.isEntityNameVisible(entityName, enclosingDeclaration, true) } func (r *EmitResolver) IsImplementationOfOverload(node *ast.SignatureDeclaration) bool { @@ -571,7 +315,7 @@ func (r *EmitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, sy } r.checkerMu.Lock() defer r.checkerMu.Unlock() - return r.requiresAddingImplicitUndefined(declaration, symbol, enclosingDeclaration) + return r.checker.requiresAddingImplicitUndefined(declaration, symbol, enclosingDeclaration) } func (r *EmitResolver) RequiresAddingImplicitUndefinedUnsafe(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { @@ -579,61 +323,7 @@ func (r *EmitResolver) RequiresAddingImplicitUndefinedUnsafe(declaration *ast.No return false } // NO LOCKING - only should be called in contexts that already have a checker lock - return r.requiresAddingImplicitUndefined(declaration, symbol, enclosingDeclaration) -} - -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: - if symbol == nil { - symbol = r.checker.getSymbolOfDeclaration(declaration) - } - t := 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, t) - case ast.KindParameter, ast.KindJSDocParameterTag: - return r.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration) - default: - panic("Node cannot possibly require adding undefined") - } -} - -func (r *EmitResolver) requiresAddingImplicitUndefinedWorker(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 - } - t := 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(t) || r.checker.containsUndefinedType(t) -} - -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 + return r.checker.requiresAddingImplicitUndefined(declaration, symbol, enclosingDeclaration) } func (r *EmitResolver) isOptionalParameter(node *ast.Node) bool { @@ -869,14 +559,14 @@ func (r *EmitResolver) GetReferencedExportContainer(node *ast.IdentifierNode, pr func (r *EmitResolver) SetReferencedImportDeclaration(node *ast.IdentifierNode, ref *ast.Declaration) { r.checkerMu.Lock() defer r.checkerMu.Unlock() - r.jsxLinks.Get(node).importRef = ref + r.checker.emitResolverLinks.jsxLinks.Get(node).importRef = ref } func (r *EmitResolver) GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration { r.checkerMu.Lock() defer r.checkerMu.Unlock() if !ast.IsParseTreeNode(node) { - return r.jsxLinks.Get(node).importRef + return r.checker.emitResolverLinks.jsxLinks.Get(node).importRef } symbol := r.checker.getReferencedValueOrAliasSymbol(node) @@ -947,46 +637,43 @@ func (r *EmitResolver) GetReferencedMemberValueDeclaration(node *ast.Node) *ast. // 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) +func (r *EmitResolver) CreateReturnTypeOfSignatureDeclaration(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + original := r.emitContext.ParseNode(signatureDeclaration) if original == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + return r.emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) } r.checkerMu.Lock() defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.SerializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) + return r.nodeBuilder().SerializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) } -func (r *EmitResolver) CreateTypeParametersOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { - original := emitContext.ParseNode(signatureDeclaration) +func (r *EmitResolver) CreateTypeParametersOfSignatureDeclaration(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { + original := r.emitContext.ParseNode(signatureDeclaration) if original == nil { return nil } r.checkerMu.Lock() defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.SerializeTypeParametersForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) + return r.nodeBuilder().SerializeTypeParametersForSignature(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) +func (r *EmitResolver) CreateTypeOfDeclaration(declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + original := r.emitContext.ParseNode(declaration) if original == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + return r.emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) } r.checkerMu.Lock() defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(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 r.nodeBuilder().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) +func (r *EmitResolver) CreateLiteralConstValue(node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node { + node = r.emitContext.ParseNode(node) r.checkerMu.Lock() t := r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node)) r.checkerMu.Unlock() @@ -998,14 +685,13 @@ func (r *EmitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, if t.flags&TypeFlagsEnumLike != 0 { r.checkerMu.Lock() defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - enumResult = requestNodeBuilder.SymbolToExpression(t.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker) + enumResult = r.nodeBuilder().SymbolToExpression(t.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 t == r.checker.trueType { - enumResult = emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) + enumResult = r.emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) } else if t == r.checker.falseType { - enumResult = emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) + enumResult = r.emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) } if enumResult != nil { return enumResult @@ -1015,51 +701,50 @@ func (r *EmitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, } switch value := t.AsLiteralType().value.(type) { case string: - return emitContext.Factory.NewStringLiteral(value, ast.TokenFlagsNone) + return r.emitContext.Factory.NewStringLiteral(value, ast.TokenFlagsNone) case jsnum.Number: if value.IsInf() { if value > 0 { - return emitContext.Factory.NewIdentifier("Infinity") + return r.emitContext.Factory.NewIdentifier("Infinity") } - return emitContext.Factory.NewPrefixUnaryExpression(ast.KindMinusToken, emitContext.Factory.NewIdentifier("Infinity")) + return r.emitContext.Factory.NewPrefixUnaryExpression(ast.KindMinusToken, r.emitContext.Factory.NewIdentifier("Infinity")) } if value.IsNaN() { - return emitContext.Factory.NewIdentifier("NaN") + return r.emitContext.Factory.NewIdentifier("NaN") } if value.Abs() != value { // negative - return emitContext.Factory.NewPrefixUnaryExpression( + return r.emitContext.Factory.NewPrefixUnaryExpression( ast.KindMinusToken, - emitContext.Factory.NewNumericLiteral(value.String()[1:], ast.TokenFlagsNone), + r.emitContext.Factory.NewNumericLiteral(value.String()[1:], ast.TokenFlagsNone), ) } - return emitContext.Factory.NewNumericLiteral(value.String(), ast.TokenFlagsNone) + return r.emitContext.Factory.NewNumericLiteral(value.String(), ast.TokenFlagsNone) case jsnum.PseudoBigInt: - return emitContext.Factory.NewBigIntLiteral(pseudoBigIntToString(value)+"n", ast.TokenFlagsNone) + return r.emitContext.Factory.NewBigIntLiteral(pseudoBigIntToString(value)+"n", ast.TokenFlagsNone) case bool: kind := ast.KindFalseKeyword if value { kind = ast.KindTrueKeyword } - return emitContext.Factory.NewKeywordExpression(kind) + return r.emitContext.Factory.NewKeywordExpression(kind) } panic("unhandled literal const value kind") } -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) +func (r *EmitResolver) CreateTypeOfExpression(expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + expression = r.emitContext.ParseNode(expression) if expression == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) + return r.emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) } r.checkerMu.Lock() defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.SerializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) + return r.nodeBuilder().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) +func (r *EmitResolver) CreateLateBoundIndexSignatures(container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { + container = r.emitContext.ParseNode(container) r.checkerMu.Lock() defer r.checkerMu.Unlock() @@ -1072,7 +757,7 @@ func (r *EmitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols) } - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context + requestNodeBuilder := r.nodeBuilder() var result []*ast.Node for i, infoList := range [][]*IndexInfo{staticInfos, instanceInfos} { @@ -1096,7 +781,7 @@ func (r *EmitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC return c.Name() != nil && ast.IsComputedPropertyName(c.Name()) && ast.IsEntityNameExpression(c.Name().Expression()) && - r.isEntityNameVisible(c.Name().Expression(), enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible + r.checker.isEntityNameVisible(c.Name().Expression(), enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible }) if allComponentComputedNamesSerializable { for _, c := range info.components { @@ -1111,13 +796,13 @@ func (r *EmitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue) } - mods := core.IfElse(isStatic, []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, nil) + mods := core.IfElse(isStatic, []*ast.Node{r.emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, nil) if info.isReadonly { - mods = append(mods, emitContext.Factory.NewModifier(ast.KindReadonlyKeyword)) + mods = append(mods, r.emitContext.Factory.NewModifier(ast.KindReadonlyKeyword)) } - decl := emitContext.Factory.NewPropertyDeclaration( - core.IfElse(mods != nil, emitContext.Factory.NewModifierList(mods), nil), + decl := r.emitContext.Factory.NewPropertyDeclaration( + core.IfElse(mods != nil, r.emitContext.Factory.NewModifierList(mods), nil), c.Name(), c.QuestionToken(), requestNodeBuilder.TypeToTypeNode(r.checker.getTypeOfSymbol(c.Symbol()), enclosingDeclaration, flags, internalFlags, tracker), @@ -1130,10 +815,10 @@ func (r *EmitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC } node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker) if node != nil && isStatic { - modNodes := []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)} + modNodes := []*ast.Node{r.emitContext.Factory.NewModifier(ast.KindStaticKeyword)} modNodes = append(modNodes, node.ModifierNodes()...) - mods := emitContext.Factory.NewModifierList(modNodes) - node = emitContext.Factory.UpdateIndexSignatureDeclaration( + mods := r.emitContext.Factory.NewModifierList(modNodes) + node = r.emitContext.Factory.UpdateIndexSignatureDeclaration( node.AsIndexSignatureDeclaration(), mods, node.ParameterList(), @@ -1269,13 +954,12 @@ func (r *EmitResolver) GetPropertiesOfContainerFunction(node *ast.Node) []*ast.S return r.checker.getPropertiesOfType(r.checker.getTypeOfSymbol(s)) } -func (r *EmitResolver) TryJSTypeNodeToTypeNode(emitContext *printer.EmitContext, typeNode *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - typeNode = emitContext.ParseNode(typeNode) +func (r *EmitResolver) TryJSTypeNodeToTypeNode(typeNode *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + typeNode = r.emitContext.ParseNode(typeNode) r.checkerMu.Lock() defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.TryJSTypeNodeToTypeNode(typeNode, enclosingDeclaration, flags, internalFlags, tracker) + return r.nodeBuilder().TryJSTypeNodeToTypeNode(typeNode, enclosingDeclaration, flags, internalFlags, tracker) } // IsThisPropertyAssignmentDeclarationRedundant reports whether a JS `this. = ...` expando diff --git a/tsc/internal/checker/emitsupport.go b/tsc/internal/checker/emitsupport.go new file mode 100644 index 0000000000000..37e08a0052b46 --- /dev/null +++ b/tsc/internal/checker/emitsupport.go @@ -0,0 +1,336 @@ +package checker + +import ( + "maps" + "slices" + + "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/core" + "github.com/microsoft/TypeScript/tsc/internal/printer" +) + +func (c *Checker) isDeclarationVisible(node *ast.Node) bool { + if !ast.IsParseTreeNode(node) { + return false + } + if node == nil { + return false + } + + links := c.emitResolverLinks.declarationLinks.Get(node) + if links.isVisible == core.TSUnknown { + if c.determineIfDeclarationIsVisible(node) { + links.isVisible = core.TSTrue + } else { + links.isVisible = core.TSFalse + } + } + return links.isVisible == core.TSTrue +} + +func (c *Checker) 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 c.isDeclarationVisible(node.Parent.Parent) + case ast.KindVariableDeclaration, + ast.KindModuleDeclaration, + ast.KindClassDeclaration, + ast.KindInterfaceDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindJSTypeAliasDeclaration, + ast.KindFunctionDeclaration, + ast.KindEnumDeclaration, + ast.KindImportEqualsDeclaration: + if ast.IsVariableDeclaration(node) { + if ast.IsBindingPattern(node.Name()) && + len(node.Name().Elements()) == 0 { + // If the binding pattern is empty, this variable declaration is not visible + return false + } + // falls through + } + // External module augmentation is always visible + // A @typedef at top-level in an external module is always visible + if ast.IsExternalModuleAugmentation(node) || ast.IsImplicitlyExportedJSDocDeclaration(node) { + return true + } + parent := ast.GetDeclarationContainer(node) + // If the node is not exported or it is not ambient module element (except import declaration) + if c.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 c.isDeclarationVisible(parent) + + case ast.KindPropertyDeclaration, + ast.KindPropertySignature, + ast.KindGetAccessor, + ast.KindSetAccessor, + ast.KindMethodDeclaration, + ast.KindMethodSignature: + if c.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 c.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 c.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 + + // An `export {X}` (without a module specifier) is itself a visible re-export of + // the named binding; it contributes to the symbol's external visibility. + case ast.KindExportSpecifier: + exportDecl := node.Parent.Parent + if ast.IsExportDeclaration(exportDecl) && exportDecl.AsExportDeclaration().ModuleSpecifier == nil { + return c.isDeclarationVisible(exportDecl.Parent) + } + return false + + default: + return false + } +} + +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 || + entityName.Parent.Kind == ast.KindBinaryExpression { + // 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.Expression() == entityName) || + (entityName.Parent.Kind == ast.KindElementAccessExpression && entityName.Parent.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 (c *Checker) isEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node, shouldComputeAliasToMakeVisible bool) printer.SymbolAccessibilityResult { + if !ast.IsParseTreeNode(entityName) { + return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityNotAccessible} + } + + meaning := getMeaningOfEntityNameReference(entityName) + firstIdentifier := ast.GetFirstIdentifier(entityName) + + symbol := c.resolveName(enclosingDeclaration, firstIdentifier.Text(), meaning, nil, false, false) + + if symbol != nil && symbol.Flags&ast.SymbolFlagsTypeParameter != 0 && meaning&ast.SymbolFlagsType != 0 { + return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} + } + + if symbol == nil && ast.IsThisIdentifier(firstIdentifier) { + sym := c.getSymbolOfDeclaration(c.getThisContainer(firstIdentifier, false, false)) + if c.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 := c.hasVisibleDeclarations(symbol, shouldComputeAliasToMakeVisible) + if visible != nil { + return *visible + } + + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotAccessible, + ErrorSymbolName: firstIdentifier.Text(), + ErrorNode: firstIdentifier, + } +} + +func noopAddVisibleAlias(declaration *ast.Node, aliasingStatement *ast.Node) {} + +func (c *Checker) 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) { + c.emitResolverLinks.declarationLinks.Get(declaration).isVisible = core.TSTrue + if aliasesToMakeVisibleSet == nil { + aliasesToMakeVisibleSet = make(map[ast.NodeId]*ast.Node) + } + aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = aliasingStatement + } + } else { + addVisibleAlias = noopAddVisibleAlias + } + + for _, declaration := range symbol.Declarations { + if ast.IsIdentifier(declaration) { + continue + } + if !c.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 + c.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 + c.isDeclarationVisible(declaration.Parent.Parent.Parent) { + addVisibleAlias(declaration, declaration.Parent.Parent) + continue + } + if ast.IsLateVisibilityPaintedStatement(declaration) && // unexported top-level statement + !ast.HasSyntacticModifier(declaration, ast.ModifierFlagsExport) && + c.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) + c.isDeclarationVisible(declaration.Parent.Parent.Parent.Parent.Parent) { + addVisibleAlias(declaration, declaration.Parent.Parent.Parent.Parent) + continue + } + if symbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { + rootDeclaration := ast.WalkUpBindingElementsAndPatterns(declaration) + if ast.IsParameterDeclaration(rootDeclaration) { + return nil + } + variableStatement := rootDeclaration.Parent.Parent + if !ast.IsVariableStatement(variableStatement) { + return nil + } + if ast.HasSyntacticModifier(variableStatement, ast.ModifierFlagsExport) { + continue // no alias to add, already exported + } + if !c.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)), + } +} + +func (c *Checker) requiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + if !ast.IsParseTreeNode(declaration) { + return false + } + switch declaration.Kind { + case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindJSDocPropertyTag: + if symbol == nil { + symbol = c.getSymbolOfDeclaration(declaration) + } + t := c.getTypeOfSymbol(symbol) + c.mappedSymbolLinks.Has(symbol) + return (symbol.Flags&ast.SymbolFlagsProperty != 0) && (symbol.Flags&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && c.ReverseMappedSymbolLinks.Has(symbol) && c.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(c, t) + case ast.KindParameter, ast.KindJSDocParameterTag: + return c.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration) + default: + panic("Node cannot possibly require adding undefined") + } +} + +func (c *Checker) requiresAddingImplicitUndefinedWorker(parameter *ast.Node, enclosingDeclaration *ast.Node) bool { + return (c.isRequiredInitializedParameter(parameter, enclosingDeclaration) || c.isOptionalUninitializedParameterProperty(parameter)) && !c.declaredParameterTypeContainsUndefined(parameter) +} + +func (c *Checker) declaredParameterTypeContainsUndefined(parameter *ast.Node) bool { + // typeNode := getNonlocalEffectiveTypeAnnotationNode(parameter); // !!! JSDoc Support + typeNode := parameter.Type() + if typeNode == nil { + return false + } + t := c.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 c.isErrorType(t) || c.containsUndefinedType(t) +} + +func (c *Checker) isOptionalUninitializedParameterProperty(parameter *ast.Node) bool { + return c.strictNullChecks && + c.isOptionalParameter(parameter) && + ( /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() == nil) && // !!! TODO: JSDoc support + ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier) +} + +func (c *Checker) isRequiredInitializedParameter(parameter *ast.Node, enclosingDeclaration *ast.Node) bool { + if !c.strictNullChecks || c.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 +} diff --git a/tsc/internal/checker/exports.go b/tsc/internal/checker/exports.go index e38a55be571ff..f77488e783142 100644 --- a/tsc/internal/checker/exports.go +++ b/tsc/internal/checker/exports.go @@ -392,7 +392,7 @@ func (c *Checker) RequiresAddingImplicitUndefined(node *ast.Node) bool { if symbol == nil { return false } - return c.GetEmitResolver().RequiresAddingImplicitUndefined(node, symbol, enclosingDeclaration) + return c.requiresAddingImplicitUndefined(node, symbol, enclosingDeclaration) } func (c *Checker) RemoveMissingOrUndefinedType(t *Type) *Type { diff --git a/tsc/internal/checker/nodebuilderimpl.go b/tsc/internal/checker/nodebuilderimpl.go index fac1f1414c58a..4592b3c656c43 100644 --- a/tsc/internal/checker/nodebuilderimpl.go +++ b/tsc/internal/checker/nodebuilderimpl.go @@ -2153,8 +2153,7 @@ func (b *NodeBuilderImpl) isTriviallySerializableComputedName(e *ast.Node) bool if !shapeGood { return false } - // TODO: going through emit resolver here is weird. Relayer these APIs. - return b.ch.GetEmitResolver().isEntityNameVisible(e.Name().Expression(), b.ctx.enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible + return b.ch.isEntityNameVisible(e.Name().Expression(), b.ctx.enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible } func (b *NodeBuilderImpl) indexInfoToObjectComputedNamesOrSignatureDeclaration(indexInfo *IndexInfo, typeNode *ast.TypeNode) []*ast.Node { @@ -2285,8 +2284,7 @@ func (b *NodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati } } - // !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker - requiresAddingUndefined := declaration != nil && (ast.IsParameterDeclaration(declaration) || ast.IsPropertySignatureDeclaration(declaration) || ast.IsPropertyDeclaration(declaration)) && b.ch.GetEmitResolver().requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) + requiresAddingUndefined := declaration != nil && (ast.IsParameterDeclaration(declaration) || ast.IsPropertySignatureDeclaration(declaration) || ast.IsPropertyDeclaration(declaration)) && b.ch.requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) addUndefinedForParameter := requiresAddingUndefined && (ast.IsParameterDeclaration(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) if addUndefinedForParameter { t = b.ch.getOptionalType(t, false) diff --git a/tsc/internal/checker/services.go b/tsc/internal/checker/services.go index 35fc2cf7e6dd7..73d70a79293c7 100644 --- a/tsc/internal/checker/services.go +++ b/tsc/internal/checker/services.go @@ -10,7 +10,6 @@ import ( "github.com/microsoft/TypeScript/tsc/internal/collections" "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/debug" - "github.com/microsoft/TypeScript/tsc/internal/printer" "github.com/microsoft/TypeScript/tsc/internal/scanner" ) @@ -897,12 +896,11 @@ func (c *Checker) GetConstantValue(node *ast.Node) any { } func (c *Checker) getResolvedSignatureWorker(node *ast.Node, checkMode CheckMode, argumentCount int) (*Signature, []*Signature) { - parsedNode := printer.NewEmitContext().ParseNode(node) c.apparentArgumentCount = &argumentCount candidatesOutArray := &[]*Signature{} var res *Signature - if parsedNode != nil { - res = c.getResolvedSignature(parsedNode, candidatesOutArray, checkMode) + if node != nil && ast.IsParseTreeNode(node) { + res = c.getResolvedSignature(node, candidatesOutArray, checkMode) } c.apparentArgumentCount = nil return res, *candidatesOutArray diff --git a/tsc/internal/checker/symbolaccessibility.go b/tsc/internal/checker/symbolaccessibility.go index a066af7159de5..6115adea5d373 100644 --- a/tsc/internal/checker/symbolaccessibility.go +++ b/tsc/internal/checker/symbolaccessibility.go @@ -36,8 +36,7 @@ func (c *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclarat accessibleSymbolChain := c.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning /*useOnlyExternalAliasing*/, false) if len(accessibleSymbolChain) > 0 { hadAccessibleChain = symbol - // TODO: going through emit resolver here is weird. Relayer these APIs. - hasAccessibleDeclarations := c.GetEmitResolver().hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) + hasAccessibleDeclarations := c.hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) if hasAccessibleDeclarations != nil { return hasAccessibleDeclarations } diff --git a/tsc/internal/compiler/emitHost.go b/tsc/internal/compiler/emitHost.go index 1f199f0c9d7a0..46c160faa67fa 100644 --- a/tsc/internal/compiler/emitHost.go +++ b/tsc/internal/compiler/emitHost.go @@ -4,6 +4,7 @@ import ( "context" "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/checker" "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/module" "github.com/microsoft/TypeScript/tsc/internal/outputpaths" @@ -31,15 +32,15 @@ var _ EmitHost = (*emitHost)(nil) // NOTE: emitHost operations must be thread-safe type emitHost struct { - program *Program - emitResolver printer.EmitResolver + program *Program + newEmitResolver func(*printer.EmitContext) *checker.EmitResolver } func newEmitHost(ctx context.Context, program *Program, file *ast.SourceFile) (*emitHost, func()) { checker, done := program.GetTypeCheckerForFile(ctx, file) return &emitHost{ - program: program, - emitResolver: checker.GetEmitResolver(), + program: program, + newEmitResolver: checker.NewEmitResolver, }, done } @@ -87,10 +88,6 @@ func (host *emitHost) GetRedirectTargets(path tspath.PathKey) []tspath.RootedFil return host.program.GetRedirectTargets(path) } -func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { - return host.GetEmitResolver().GetEffectiveDeclarationFlags(node, flags) -} - func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths { // TODO: cache return outputpaths.GetOutputPathsFor(file, host.Options(), host, outputpaths.ForceEmitPaths{Dts: forceDtsPaths}) @@ -130,8 +127,8 @@ func (host *emitHost) WriteFile(fileName tspath.RootedFilePath, text string) err return host.program.Host().FS().WriteFile(fileName, text) } -func (host *emitHost) GetEmitResolver() printer.EmitResolver { - return host.emitResolver +func (host *emitHost) NewEmitResolver(emitContext *printer.EmitContext) printer.EmitResolver { + return host.newEmitResolver(emitContext) } func (host *emitHost) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool { diff --git a/tsc/internal/compiler/emitter.go b/tsc/internal/compiler/emitter.go index 4e3b697a1b17e..d2a70b1d2a4ae 100644 --- a/tsc/internal/compiler/emitter.go +++ b/tsc/internal/compiler/emitter.go @@ -47,8 +47,10 @@ func (e *emitter) emit() { if e.tr != nil { defer e.tr.Push(tracing.PhaseEmit, "emit", map[string]any{"path": string(e.sourceFile.PathKey())}, true)() } - e.emitJSFile(e.sourceFile, e.paths.JsFilePath(), e.paths.SourceMapFilePath()) - e.emitDeclarationFile(e.sourceFile, e.paths.DeclarationFilePath(), e.paths.DeclarationMapPath()) + emitContext := printer.NewEmitContext() + emitResolver := e.host.NewEmitResolver(emitContext) + e.emitJSFile(emitResolver, e.sourceFile, e.paths.JsFilePath(), e.paths.SourceMapFilePath()) + e.emitDeclarationFile(emitResolver, e.sourceFile, e.paths.DeclarationFilePath(), e.paths.DeclarationMapPath()) e.emitResult.Diagnostics = e.emitterDiagnostics.GetDiagnostics() } @@ -57,30 +59,30 @@ type declarationTransformer interface { GetDiagnostics() []*ast.Diagnostic } -func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath tspath.RootedFilePath) []declarationTransformer { +func (e *emitter) getDeclarationTransformers(emitResolver printer.EmitResolver, sourceFile *ast.SourceFile, declarationFilePath tspath.RootedFilePath) []declarationTransformer { forceDtsEmit := e.emitOnly == EmitOnlyBuilderSignature || e.forceEmit && e.emitOnly == EmitOnlyDts return []declarationTransformer{ - declarations.NewDeclarationTransformer(e.host, emitContext, e.host.Options(), declarationFilePath), + declarations.NewDeclarationTransformer(e.host, emitResolver, e.host.Options(), declarationFilePath), declarations.NewSupplementalReferencesTransformer(e.host, sourceFile, declarationFilePath, forceDtsEmit), } } -func (e *emitter) runScriptTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile) *ast.SourceFile { +func (e *emitter) runScriptTransformers(emitResolver printer.EmitResolver, sourceFile *ast.SourceFile) *ast.SourceFile { if e.tr != nil { defer e.tr.Push(tracing.PhaseEmit, "transformNodes", map[string]any{"path": string(sourceFile.PathKey())}, false)() } - for _, transformer := range getScriptTransformers(emitContext, e.host, sourceFile) { + for _, transformer := range getScriptTransformers(emitResolver, e.host, sourceFile) { sourceFile = transformer.TransformSourceFile(sourceFile) } return sourceFile } -func (e *emitter) runDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath, declarationMapPath tspath.RootedFilePath) (*ast.SourceFile, []*ast.Diagnostic) { +func (e *emitter) runDeclarationTransformers(emitResolver printer.EmitResolver, sourceFile *ast.SourceFile, declarationFilePath, declarationMapPath tspath.RootedFilePath) (*ast.SourceFile, []*ast.Diagnostic) { if e.tr != nil { defer e.tr.Push(tracing.PhaseEmit, "transformNodes", map[string]any{"path": string(sourceFile.PathKey())}, false)() } var diags []*ast.Diagnostic - for _, transformer := range e.getDeclarationTransformers(emitContext, sourceFile, declarationFilePath) { + for _, transformer := range e.getDeclarationTransformers(emitResolver, sourceFile, declarationFilePath) { sourceFile = transformer.TransformSourceFile(sourceFile) diags = append(diags, transformer.GetDiagnostics()...) } @@ -109,7 +111,11 @@ func getModuleTransformer(opts *transformers.TransformOptions) *transformers.Tra } } -func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHost, sourceFile *ast.SourceFile) []*transformers.Transformer { +func getScriptTransformers(emitResolver printer.EmitResolver, host printer.EmitHost, sourceFile *ast.SourceFile) []*transformers.Transformer { + if emitResolver == nil || emitResolver.EmitContext() == nil { + panic("Script transformers require an EmitResolver with an EmitContext") + } + emitContext := emitResolver.EmitContext() var tx []*transformers.Transformer options := host.Options() @@ -117,8 +123,6 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo importElisionEnabled := !options.VerbatimModuleSyntax.IsTrue() && !ast.IsInJSFile(sourceFile.AsNode()) jsxTransformEnabled := options.GetJSXTransformEnabled() && sourceFile.LanguageVariant == core.LanguageVariantJSX - emitResolver := host.GetEmitResolver() - var referenceResolver binder.ReferenceResolver if importElisionEnabled || jsxTransformEnabled || !options.GetIsolatedModules() || options.EmitDecoratorMetadata.IsTrue() { referenceResolver = emitResolver @@ -178,7 +182,8 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo return tx } -func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath tspath.RootedFilePath, sourceMapFilePath tspath.RootedFilePath) { +func (e *emitter) emitJSFile(emitResolver printer.EmitResolver, sourceFile *ast.SourceFile, jsFilePath tspath.RootedFilePath, sourceMapFilePath tspath.RootedFilePath) { + emitContext := emitResolver.EmitContext() options := e.host.Options() if sourceFile == nil || e.emitOnly != EmitAll && e.emitOnly != EmitOnlyJs || jsFilePath == "" { @@ -194,10 +199,7 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath tspath.Roote defer e.tr.Push(tracing.PhaseEmit, "emitJsFileOrBundle", map[string]any{"jsFilePath": jsFilePath}, true)() } - emitContext, putEmitContext := printer.GetEmitContext() - defer putEmitContext() - - sourceFile = e.runScriptTransformers(emitContext, sourceFile) + sourceFile = e.runScriptTransformers(emitResolver, sourceFile) printerOptions := printer.PrinterOptions{ RemoveComments: options.RemoveComments.IsTrue(), @@ -215,10 +217,11 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath tspath.Roote // !!! }, emitContext) - e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer, options, shouldEmitSourceMaps(options, sourceFile)) + e.printSourceFile(emitContext, jsFilePath, sourceMapFilePath, sourceFile, printer, options, shouldEmitSourceMaps(options, sourceFile)) } -func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFilePath tspath.RootedFilePath, declarationMapPath tspath.RootedFilePath) { +func (e *emitter) emitDeclarationFile(emitResolver printer.EmitResolver, sourceFile *ast.SourceFile, declarationFilePath tspath.RootedFilePath, declarationMapPath tspath.RootedFilePath) { + emitContext := emitResolver.EmitContext() options := e.host.Options() if sourceFile == nil || e.emitOnly == EmitOnlyJs || declarationFilePath == "" { @@ -231,9 +234,7 @@ func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFil defer e.tr.Push(tracing.PhaseEmit, "emitDeclarationFileOrBundle", map[string]any{"declarationFilePath": declarationFilePath}, true)() } - emitContext, putEmitContext := printer.GetEmitContext() - defer putEmitContext() - sourceFile, diags := e.runDeclarationTransformers(emitContext, sourceFile, declarationFilePath, declarationMapPath) + sourceFile, diags := e.runDeclarationTransformers(emitResolver, sourceFile, declarationFilePath, declarationMapPath) for _, elem := range diags { // Add declaration transform diagnostics to emit diagnostics @@ -289,7 +290,7 @@ func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFil MapRoot: options.MapRoot, // Explicitly do not pass through either inline option. } - e.printSourceFile(declarationFilePath, declarationMapPath, sourceFile, printer, declarationMapOptions, shouldEmitSourceMaps(declarationMapOptions, sourceFile)) + e.printSourceFile(emitContext, declarationFilePath, declarationMapPath, sourceFile, printer, declarationMapOptions, shouldEmitSourceMaps(declarationMapOptions, sourceFile)) } type declarationMapSource struct { @@ -311,7 +312,7 @@ func (s *declarationMapSource) FileName() tspath.RootedFilePath { return s.fileN func (s *declarationMapSource) Text() string { return s.text } func (s *declarationMapSource) ECMALineMap() []core.TextPos { return s.lineMap } -func (e *emitter) printSourceFile(jsFilePath tspath.RootedFilePath, sourceMapFilePath tspath.RootedFilePath, sourceFile *ast.SourceFile, printer_ *printer.Printer, mapOptions *core.CompilerOptions, shouldEmitSourceMaps bool) { +func (e *emitter) printSourceFile(emitContext *printer.EmitContext, jsFilePath tspath.RootedFilePath, sourceMapFilePath tspath.RootedFilePath, sourceFile *ast.SourceFile, printer_ *printer.Printer, mapOptions *core.CompilerOptions, shouldEmitSourceMaps bool) { // !!! sourceMapGenerator options := e.host.Options() var sourceMapGenerator *sourcemap.Generator @@ -325,6 +326,7 @@ func (e *emitter) printSourceFile(jsFilePath tspath.RootedFilePath, sourceMapFil } printer_.Write(sourceFile.AsNode(), sourceFile, e.writer, sourceMapGenerator) + emitContext.Factory.ReleaseArenas() sourceMapUrlPos := -1 if sourceMapGenerator != nil { @@ -553,7 +555,8 @@ func getDeclarationDiagnostics(host EmitHost, file *ast.SourceFile) []*ast.Diagn return []*ast.Diagnostic{} } options := host.Options() - transform := declarations.NewDeclarationTransformer(host, nil, options, "") + emitResolver := host.NewEmitResolver(printer.NewEmitContext()) + transform := declarations.NewDeclarationTransformer(host, emitResolver, options, "") transform.TransformSourceFile(file) return transform.GetDiagnostics() } diff --git a/tsc/internal/ls/completions.go b/tsc/internal/ls/completions.go index be8e2d0904d28..ff0f180d5b84d 100644 --- a/tsc/internal/ls/completions.go +++ b/tsc/internal/ls/completions.go @@ -6166,6 +6166,7 @@ func getJSDocParameterCompletions( } } paramIndex := -1 + var emitContext *printer.EmitContext return core.MapNonNil(fun.Parameters(), func(param *ast.ParameterDeclarationNode) *CompletionItem { paramIndex++ if paramIndex < paramTagCount { @@ -6176,6 +6177,7 @@ func getJSDocParameterCompletions( tabstopCounter := 1 paramName := param.Name().Text() displayText := getJSDocParamAnnotation( + &emitContext, paramName, param.Initializer(), param.AsParameterDeclaration().DotDotDotToken, @@ -6190,6 +6192,7 @@ func getJSDocParameterCompletions( var snippetText string if isSnippet { snippetText = getJSDocParamAnnotation( + &emitContext, paramName, param.Initializer(), param.AsParameterDeclaration().DotDotDotToken, @@ -6222,6 +6225,7 @@ func getJSDocParameterCompletions( // Destructuring parameter; do it positionally paramPath := fmt.Sprintf("param%d", paramIndex) displayTextResult := generateJSDocParamTagsForDestructuring( + &emitContext, paramPath, param.Name(), param.Initializer(), @@ -6235,6 +6239,7 @@ func getJSDocParameterCompletions( var snippetText string if isSnippet { snippetTextResult := generateJSDocParamTagsForDestructuring( + &emitContext, paramPath, param.Name(), param.Initializer(), @@ -6267,6 +6272,7 @@ func getJSDocParameterCompletions( } func getJSDocParamAnnotation( + emitContext **printer.EmitContext, paramName string, initializer *ast.Expression, dotDotDotToken *ast.TokenNode, @@ -6310,7 +6316,9 @@ func getJSDocParamAnnotation( nil, /*idToSymbol*/ ) if typeNode != nil { - emitContext := printer.NewEmitContext() + if *emitContext == nil { + *emitContext = printer.NewEmitContext() + } // !!! snippet p p := printer.NewPrinter(printer.PrinterOptions{ RemoveComments: true, @@ -6318,8 +6326,8 @@ func getJSDocParamAnnotation( // Module: options.Module, // ModuleResolution: options.ModuleResolution, // Target: options.Target, - }, printer.PrintHandlers{}, emitContext) - emitContext.SetEmitFlags(typeNode, printer.EFSingleLine) + }, printer.PrintHandlers{}, *emitContext) + (*emitContext).SetEmitFlags(typeNode, printer.EFSingleLine) t = p.Emit(typeNode, file) } } @@ -6358,6 +6366,7 @@ func getJSDocParamNameWithInitializer(paramName string, initializer *ast.Express } func generateJSDocParamTagsForDestructuring( + emitContext **printer.EmitContext, path string, pattern *ast.BindingPatternNode, initializer *ast.Expression, @@ -6371,6 +6380,7 @@ func generateJSDocParamTagsForDestructuring( tabstopCounter := 1 if !isJS { return []string{getJSDocParamAnnotation( + emitContext, path, initializer, dotDotDotToken, @@ -6384,6 +6394,7 @@ func generateJSDocParamTagsForDestructuring( )} } return jsDocParamPatternWorker( + emitContext, path, pattern, initializer, @@ -6398,6 +6409,7 @@ func generateJSDocParamTagsForDestructuring( } func jsDocParamPatternWorker( + emitContext **printer.EmitContext, path string, pattern *ast.BindingPatternNode, initializer *ast.Expression, @@ -6412,6 +6424,7 @@ func jsDocParamPatternWorker( if ast.IsObjectBindingPattern(pattern) && dotDotDotToken == nil { childCounter := *counter rootParam := getJSDocParamAnnotation( + emitContext, path, initializer, dotDotDotToken, @@ -6426,6 +6439,7 @@ func jsDocParamPatternWorker( var childTags []string for _, element := range pattern.Elements() { elementTags := jsDocParamElementWorker( + emitContext, path, element, initializer, @@ -6450,6 +6464,7 @@ func jsDocParamPatternWorker( } return []string{ getJSDocParamAnnotation( + emitContext, path, initializer, dotDotDotToken, @@ -6467,6 +6482,7 @@ func jsDocParamPatternWorker( // Assumes binding element is inside object binding pattern. // We can't deeply annotate an array binding pattern. func jsDocParamElementWorker( + emitContext **printer.EmitContext, path string, element *ast.BindingElementNode, initializer *ast.Expression, @@ -6491,6 +6507,7 @@ func jsDocParamElementWorker( paramName := fmt.Sprintf("%s.%s", path, propertyName) return []string{ getJSDocParamAnnotation( + emitContext, paramName, element.Initializer(), element.AsBindingElement().DotDotDotToken, @@ -6509,6 +6526,7 @@ func jsDocParamElementWorker( return nil } return jsDocParamPatternWorker( + emitContext, fmt.Sprintf("%s.%s", path, propertyName), element.Name(), element.Initializer(), diff --git a/tsc/internal/ls/findallreferences.go b/tsc/internal/ls/findallreferences.go index 80b53f8f48e1f..ea287f75d7638 100644 --- a/tsc/internal/ls/findallreferences.go +++ b/tsc/internal/ls/findallreferences.go @@ -509,7 +509,7 @@ func (l *LanguageService) getNonLocalDefinition(ctx context.Context, entry *Symb program := l.GetProgram() checker, done := program.GetTypeChecker(ctx) defer done() - emitResolver := checker.GetEmitResolver() + emitResolver := checker.NewEmitResolver(printer.NewEmitContext()) for _, d := range entry.definition.symbol.Declarations { if isDefinitionVisible(emitResolver, d) { file, startPos := getFileAndStartPosFromDeclaration(d) diff --git a/tsc/internal/ls/hover.go b/tsc/internal/ls/hover.go index c86032ce1f196..249960e8021dd 100644 --- a/tsc/internal/ls/hover.go +++ b/tsc/internal/ls/hover.go @@ -438,6 +438,13 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol // nodeBuilderFlags for classified output (same as signatureHelpNodeBuilderFlags) const classifiedNodeBuilderFlags = nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope | nodebuilder.FlagsWriteTypeParametersInQualifiedName + var displayEmitContext *printer.EmitContext + getEmitContext := func() *printer.EmitContext { + if displayEmitContext == nil { + displayEmitContext = printer.NewEmitContext() + } + return displayEmitContext + } // writeTypeClassified writes a type to dpw with proper classification (punctuation, symbols, keywords). // Falls back to flat text when vsCapability is false or when TypeToTypeNode fails. @@ -447,7 +454,8 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol dpw.Write(c.TypeToStringEx(t, enclosing, flags, vc)) return } - emitContext := printer.NewEmitContext() + emitContext := getEmitContext() + defer emitContext.Factory.ReleaseArenas() idToSymbol := make(map[*ast.IdentifierNode]*ast.Symbol) nb := checker.NewNodeBuilderEx(c, emitContext, idToSymbol) combinedFlags := nodebuilder.Flags(flags&checker.TypeFormatFlagsNodeBuilderFlagsMask) | classifiedNodeBuilderFlags @@ -485,7 +493,8 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol sigOutput = ast.KindCallSignature } } - emitContext := printer.NewEmitContext() + emitContext := getEmitContext() + defer emitContext.Factory.ReleaseArenas() idToSymbol := make(map[*ast.IdentifierNode]*ast.Symbol) nb := checker.NewNodeBuilderEx(c, emitContext, idToSymbol) combinedFlags := nodebuilder.Flags(flags&checker.TypeFormatFlagsNodeBuilderFlagsMask) | classifiedNodeBuilderFlags @@ -519,7 +528,7 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol return } attributes := declaration.AsModuleDeclaration().Attributes - emitContext := printer.NewEmitContext() + emitContext := getEmitContext() emitContext.SetEmitFlags(attributes, printer.EFSingleLine) p := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, emitContext) tempDpw := newDisplayPartsWriter(vsCapability) diff --git a/tsc/internal/ls/signaturehelp.go b/tsc/internal/ls/signaturehelp.go index 78aa78490d4b8..6e1fce510d96c 100644 --- a/tsc/internal/ls/signaturehelp.go +++ b/tsc/internal/ls/signaturehelp.go @@ -209,11 +209,13 @@ func createTypeHelpItems(ctx context.Context, symbol *ast.Symbol, argumentInfo * } func getTypeHelpItem(symbol *ast.Symbol, typeParameter []*checker.Type, enclosingDeclaration *ast.Node, sourceFile *ast.SourceFile, c *checker.Checker) signatureInformation { - printer := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, nil) + emitContext := printer.NewEmitContext() + builder := checker.NewNodeBuilder(c, emitContext) + printer := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, emitContext) parameters := make([]signatureHelpParameter, len(typeParameter)) for i, typeParam := range typeParameter { - parameters[i] = createSignatureHelpParameterForTypeParameter(typeParam, sourceFile, enclosingDeclaration, c, printer) + parameters[i] = createSignatureHelpParameterForTypeParameter(typeParam, sourceFile, enclosingDeclaration, builder, printer) } // Creating display label @@ -447,14 +449,15 @@ func (l *LanguageService) computeActiveParameter(sig signatureInformation, argum } func (l *LanguageService) getSignatureHelpItem(candidate *checker.Signature, isTypeParameterList bool, callTargetSymbol string, callTargetSym *ast.Symbol, enclosingDeclaration *ast.Node, sourceFile *ast.SourceFile, c *checker.Checker, docFormat lsproto.MarkupKind, vsCapability bool) []signatureInformation { + emitContext := printer.NewEmitContext() var infos []*signatureHelpItemInfo if isTypeParameterList { - infos = l.itemInfoForTypeParameters(candidate, c, enclosingDeclaration, sourceFile, docFormat, vsCapability) + infos = l.itemInfoForTypeParameters(candidate, c, enclosingDeclaration, sourceFile, docFormat, vsCapability, emitContext) } else { - infos = l.itemInfoForParameters(candidate, c, enclosingDeclaration, sourceFile, docFormat, vsCapability) + infos = l.itemInfoForParameters(candidate, c, enclosingDeclaration, sourceFile, docFormat, vsCapability, emitContext) } - suffixDpw := returnTypeToDisplayParts(candidate, c, enclosingDeclaration, sourceFile, vsCapability) + suffixDpw := returnTypeToDisplayParts(candidate, c, enclosingDeclaration, sourceFile, vsCapability, emitContext) // Generate documentation from the signature's declaration var documentation *string @@ -485,7 +488,7 @@ func (l *LanguageService) getSignatureHelpItem(candidate *checker.Signature, isT return result } -func returnTypeToDisplayParts(candidateSignature *checker.Signature, c *checker.Checker, enclosingDeclaration *ast.Node, sourceFile *ast.SourceFile, vsCapability bool) *displayPartsWriter { +func returnTypeToDisplayParts(candidateSignature *checker.Signature, c *checker.Checker, enclosingDeclaration *ast.Node, sourceFile *ast.SourceFile, vsCapability bool, emitContext *printer.EmitContext) *displayPartsWriter { dpw := newDisplayPartsWriter(vsCapability) // Add ": " prefix @@ -498,7 +501,7 @@ func returnTypeToDisplayParts(candidateSignature *checker.Signature, c *checker. returnType := c.GetReturnTypeOfSignature(candidateSignature) typeNode := c.TypeToTypeNode(returnType, enclosingDeclaration, signatureHelpNodeBuilderFlags, nil) if typeNode != nil { - p := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, printer.NewEmitContext()) + p := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, emitContext) // Use a temporary writer for p.Write since the printer calls Clear() on its writer tempDpw := newDisplayPartsWriter(vsCapability) p.Write(typeNode, sourceFile, tempDpw, nil) @@ -510,8 +513,8 @@ func returnTypeToDisplayParts(candidateSignature *checker.Signature, c *checker. return dpw } -func (l *LanguageService) itemInfoForTypeParameters(candidateSignature *checker.Signature, c *checker.Checker, enclosingDeclaration *ast.Node, sourceFile *ast.SourceFile, docFormat lsproto.MarkupKind, vsCapability bool) []*signatureHelpItemInfo { - emitContext := printer.NewEmitContext() +func (l *LanguageService) itemInfoForTypeParameters(candidateSignature *checker.Signature, c *checker.Checker, enclosingDeclaration *ast.Node, sourceFile *ast.SourceFile, docFormat lsproto.MarkupKind, vsCapability bool, emitContext *printer.EmitContext) []*signatureHelpItemInfo { + builder := checker.NewNodeBuilder(c, emitContext) p := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, emitContext) var typeParameters []*checker.Type @@ -522,12 +525,12 @@ func (l *LanguageService) itemInfoForTypeParameters(candidateSignature *checker. } signatureHelpTypeParameters := make([]signatureHelpParameter, len(typeParameters)) for i, typeParameter := range typeParameters { - signatureHelpTypeParameters[i] = createSignatureHelpParameterForTypeParameter(typeParameter, sourceFile, enclosingDeclaration, c, p) + signatureHelpTypeParameters[i] = createSignatureHelpParameterForTypeParameter(typeParameter, sourceFile, enclosingDeclaration, builder, p) } thisParameter := []signatureHelpParameter{} if candidateSignature.ThisParameter() != nil { - thisParameter = []signatureHelpParameter{l.createSignatureHelpParameterForParameter(candidateSignature.ThisParameter(), enclosingDeclaration, p, sourceFile, c, docFormat)} + thisParameter = []signatureHelpParameter{l.createSignatureHelpParameterForParameter(candidateSignature.ThisParameter(), enclosingDeclaration, builder, p, sourceFile, c, docFormat)} } // Creating type parameter display label @@ -559,7 +562,7 @@ func (l *LanguageService) itemInfoForTypeParameters(candidateSignature *checker. parameters := thisParameter for j, param := range parameterList { - paramNode := checker.NewNodeBuilder(c, emitContext).SymbolToParameterDeclaration(param, enclosingDeclaration, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil) + paramNode := builder.SymbolToParameterDeclaration(param, enclosingDeclaration, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil) if j > 0 { paramDpw.WritePunctuation(", ") @@ -567,6 +570,7 @@ func (l *LanguageService) itemInfoForTypeParameters(candidateSignature *checker. // Use a temporary writer for p.Write since the printer calls Clear() on its writer tempDpw := newDisplayPartsWriter(vsCapability) p.Write(paramNode, sourceFile, tempDpw, nil) + emitContext.Factory.ReleaseArenas() paramLabel := tempDpw.String() paramDpw.WriteFrom(tempDpw) @@ -585,14 +589,14 @@ func (l *LanguageService) itemInfoForTypeParameters(candidateSignature *checker. return result } -func (l *LanguageService) itemInfoForParameters(candidateSignature *checker.Signature, c *checker.Checker, enclosingDeclaratipn *ast.Node, sourceFile *ast.SourceFile, docFormat lsproto.MarkupKind, vsCapability bool) []*signatureHelpItemInfo { - emitContext := printer.NewEmitContext() +func (l *LanguageService) itemInfoForParameters(candidateSignature *checker.Signature, c *checker.Checker, enclosingDeclaratipn *ast.Node, sourceFile *ast.SourceFile, docFormat lsproto.MarkupKind, vsCapability bool, emitContext *printer.EmitContext) []*signatureHelpItemInfo { + builder := checker.NewNodeBuilder(c, emitContext) p := printer.NewPrinter(printer.PrinterOptions{NewLine: core.NewLineKindLF}, printer.PrintHandlers{}, emitContext) signatureHelpTypeParameters := make([]signatureHelpParameter, len(candidateSignature.TypeParameters())) if len(candidateSignature.TypeParameters()) != 0 { for i, typeParameter := range candidateSignature.TypeParameters() { - signatureHelpTypeParameters[i] = createSignatureHelpParameterForTypeParameter(typeParameter, sourceFile, enclosingDeclaratipn, c, p) + signatureHelpTypeParameters[i] = createSignatureHelpParameterForTypeParameter(typeParameter, sourceFile, enclosingDeclaratipn, builder, p) } } @@ -637,7 +641,7 @@ func (l *LanguageService) itemInfoForParameters(candidateSignature *checker.Sign paramDpw.WriteFrom(dpw) for j, param := range parameterList { - paramNode := checker.NewNodeBuilder(c, emitContext).SymbolToParameterDeclaration(param, enclosingDeclaratipn, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil) + paramNode := builder.SymbolToParameterDeclaration(param, enclosingDeclaratipn, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil) if j > 0 { paramDpw.WritePunctuation(", ") @@ -645,6 +649,7 @@ func (l *LanguageService) itemInfoForParameters(candidateSignature *checker.Sign // Use a temporary writer for p.Write since the printer calls Clear() on its writer tempDpw := newDisplayPartsWriter(vsCapability) p.Write(paramNode, sourceFile, tempDpw, nil) + emitContext.Factory.ReleaseArenas() paramLabel := tempDpw.String() paramDpw.WriteFrom(tempDpw) @@ -691,13 +696,15 @@ func (l *LanguageService) createSignatureHelpParameterFromLabel(parameter *ast.S } } -func (l *LanguageService) createSignatureHelpParameterForParameter(parameter *ast.Symbol, enclosingDeclaratipn *ast.Node, p *printer.Printer, sourceFile *ast.SourceFile, c *checker.Checker, docFormat lsproto.MarkupKind) signatureHelpParameter { - display := p.Emit(checker.NewNodeBuilder(c, printer.NewEmitContext()).SymbolToParameterDeclaration(parameter, enclosingDeclaratipn, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil), sourceFile) +func (l *LanguageService) createSignatureHelpParameterForParameter(parameter *ast.Symbol, enclosingDeclaratipn *ast.Node, builder *checker.NodeBuilder, p *printer.Printer, sourceFile *ast.SourceFile, c *checker.Checker, docFormat lsproto.MarkupKind) signatureHelpParameter { + defer builder.EmitContext().Factory.ReleaseArenas() + display := p.Emit(builder.SymbolToParameterDeclaration(parameter, enclosingDeclaratipn, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil), sourceFile) return l.createSignatureHelpParameterFromLabel(parameter, display, c, docFormat) } -func createSignatureHelpParameterForTypeParameter(t *checker.Type, sourceFile *ast.SourceFile, enclosingDeclaration *ast.Node, c *checker.Checker, p *printer.Printer) signatureHelpParameter { - display := p.Emit(checker.NewNodeBuilder(c, printer.NewEmitContext()).TypeParameterToDeclaration(t, enclosingDeclaration, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil), sourceFile) +func createSignatureHelpParameterForTypeParameter(t *checker.Type, sourceFile *ast.SourceFile, enclosingDeclaration *ast.Node, builder *checker.NodeBuilder, p *printer.Printer) signatureHelpParameter { + defer builder.EmitContext().Factory.ReleaseArenas() + display := p.Emit(builder.TypeParameterToDeclaration(t, enclosingDeclaration, signatureHelpNodeBuilderFlags, nodebuilder.InternalFlagsNone, nil), sourceFile) return signatureHelpParameter{ parameterInfo: &lsproto.ParameterInformation{ Label: lsproto.StringOrTuple{String: &display}, diff --git a/tsc/internal/printer/emitcontext.go b/tsc/internal/printer/emitcontext.go index ee483873f0d58..7953212bd40e7 100644 --- a/tsc/internal/printer/emitcontext.go +++ b/tsc/internal/printer/emitcontext.go @@ -3,7 +3,6 @@ package printer import ( "maps" "slices" - "sync" "sync/atomic" "github.com/microsoft/TypeScript/tsc/internal/ast" @@ -48,26 +47,6 @@ func NewEmitContext() *EmitContext { return c } -var emitContextPool = sync.Pool{ - New: func() any { - return NewEmitContext() - }, -} - -func GetEmitContext() (*EmitContext, func()) { - c := emitContextPool.Get().(*EmitContext) - return c, func() { - c.Reset() - emitContextPool.Put(c) - } -} - -func (c *EmitContext) Reset() { - *c = EmitContext{ - Factory: c.Factory, - } -} - func (c *EmitContext) onCreate(node *ast.Node) { node.Flags |= ast.NodeFlagsSynthesized } diff --git a/tsc/internal/printer/emithost.go b/tsc/internal/printer/emithost.go index 993e02ee12f76..fb08bd83799c5 100644 --- a/tsc/internal/printer/emithost.go +++ b/tsc/internal/printer/emithost.go @@ -16,7 +16,7 @@ type EmitHost interface { IsEmitBlocked(file tspath.RootedFilePath) bool WriteFile(fileName tspath.RootedFilePath, text string) error GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind - GetEmitResolver() EmitResolver + NewEmitResolver(emitContext *EmitContext) EmitResolver GetProjectReferenceFromSource(path tspath.PathKey) *tsoptions.SourceOutputAndProjectReference IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool } diff --git a/tsc/internal/printer/emitresolver.go b/tsc/internal/printer/emitresolver.go index f9b98dba994ce..d86a0ca378ecc 100644 --- a/tsc/internal/printer/emitresolver.go +++ b/tsc/internal/printer/emitresolver.go @@ -75,6 +75,7 @@ const ( type EmitResolver interface { binder.ReferenceResolver + EmitContext() *EmitContext IsReferencedAliasDeclaration(node *ast.Node) bool IsValueAliasDeclaration(node *ast.Node) bool IsTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool @@ -117,11 +118,11 @@ type EmitResolver interface { GetReferencedValueDeclarationUnsafe(node *ast.IdentifierNode) *ast.Declaration // 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 - CreateTypeParametersOfSignatureDeclaration(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 - TryJSTypeNodeToTypeNode(emitContext *EmitContext, typeNode *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateTypeOfDeclaration(declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateReturnTypeOfSignatureDeclaration(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateTypeParametersOfSignatureDeclaration(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + CreateLiteralConstValue(node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node + CreateTypeOfExpression(expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateLateBoundIndexSignatures(container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + TryJSTypeNodeToTypeNode(typeNode *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node } diff --git a/tsc/internal/testutil/tsbaseline/type_symbol_baseline.go b/tsc/internal/testutil/tsbaseline/type_symbol_baseline.go index 6a04466983cc6..343a85a74e973 100644 --- a/tsc/internal/testutil/tsbaseline/type_symbol_baseline.go +++ b/tsc/internal/testutil/tsbaseline/type_symbol_baseline.go @@ -350,8 +350,7 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b fileChecker, done := walker.getTypeCheckerForCurrentFile() defer done() - ctx, putCtx := printer.GetEmitContext() - defer putCtx() + ctx := printer.NewEmitContext() if !isSymbolWalk { // Don't try to get the type of something that's already a type. @@ -390,7 +389,6 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b !isIntrinsicJsxTag(node, walker.currentSourceFile) { typeString = t.AsIntrinsicType().IntrinsicName() } else { - ctx.Reset() builder := checker.NewNodeBuilder(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) diff --git a/tsc/internal/transformers/declarations/transform.go b/tsc/internal/transformers/declarations/transform.go index e8e6c0b22c5f1..0daf681bfb59e 100644 --- a/tsc/internal/transformers/declarations/transform.go +++ b/tsc/internal/transformers/declarations/transform.go @@ -38,8 +38,6 @@ type DeclarationEmitHost interface { GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths SourceFileMayBeEmitted(file *ast.SourceFile, forceDtsEmit bool) bool - GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags - GetEmitResolver() printer.EmitResolver } type thisPropertyAssignmentKey struct { @@ -98,8 +96,11 @@ type DeclarationTransformer struct { } // TODO: Convert to transformers.TransformerFactory signature to allow more automatic composition with other transforms -func NewDeclarationTransformer(host DeclarationEmitHost, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath tspath.RootedFilePath) *DeclarationTransformer { - resolver := host.GetEmitResolver() +func NewDeclarationTransformer(host DeclarationEmitHost, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions, declarationFilePath tspath.RootedFilePath) *DeclarationTransformer { + if resolver == nil || resolver.EmitContext() == nil { + panic("DeclarationTransformer requires an EmitResolver with an EmitContext") + } + context := resolver.EmitContext() state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), stripInternal: compilerOptions.StripInternal.IsTrue(), resolver: resolver} tracker := NewSymbolTracker(host, resolver, state) // TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit) @@ -818,7 +819,7 @@ func (tx *DeclarationTransformer) transformExpressionWithTypeArguments(input *as } func (tx *DeclarationTransformer) transformTypeParameterDeclaration(input *ast.TypeParameterDeclaration) *ast.Node { - if isPrivateMethodTypeParameter(tx.host, input) && (input.DefaultType != nil || input.Constraint != nil) { + if isPrivateMethodTypeParameter(tx.resolver, input) && (input.DefaultType != nil || input.Constraint != nil) { return tx.Factory().UpdateTypeParameterDeclaration( input, input.Modifiers(), @@ -1004,7 +1005,7 @@ func (tx *DeclarationTransformer) transformSetAccessorDeclaration(input *ast.Set tx.ensureModifiers(input.AsNode()), input.Name(), nil, // accessors shouldn't have type params - tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), + tx.updateAccessorParamList(input.AsNode(), tx.resolver.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), nil, nil, nil, @@ -1020,7 +1021,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(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), + tx.updateAccessorParamList(input.AsNode(), tx.resolver.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), tx.ensureType(input.AsNode(), false), nil, nil, @@ -1113,7 +1114,7 @@ func (tx *DeclarationTransformer) omitPrivateMethodType(input *ast.Node) *ast.No } func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast.MethodSignatureDeclaration) *ast.Node { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { + if tx.resolver.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { return tx.omitPrivateMethodType(input.AsNode()) } else if ast.IsPrivateIdentifier(input.Name()) { return nil @@ -1131,7 +1132,7 @@ func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast } func (tx *DeclarationTransformer) transformMethodDeclaration(input *ast.MethodDeclaration) *ast.Node { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { + if tx.resolver.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { return tx.omitPrivateMethodType(input.AsNode()) } else if ast.IsPrivateIdentifier(input.Name()) { return nil @@ -1274,7 +1275,7 @@ func (tx *DeclarationTransformer) transformExportAssignment(input *ast.Node, ass tx.cjsExportAssignmentName = newId var type_, initializer *ast.Node if ast.IsPrimitiveLiteralValue(unwrapParenthesizedExpression(expression), true) { - initializer = tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(assignment), tx.tracker) + initializer = tx.resolver.CreateLiteralConstValue(tx.EmitContext().ParseNode(assignment), tx.tracker) } if initializer == nil { type_ = tx.ensureType(assignment, false) @@ -1490,7 +1491,7 @@ func (tx *DeclarationTransformer) transformCommonJSExportWorker(input *ast.Node, tx.preserveJsDoc(statement, input) tx.removeAllComments(assignment) return tx.Factory().NewSyntaxList([]*ast.Node{statement, assignment}) - } else if tx.host.GetEmitResolver().GetReferencedValueDeclaration(name) == input || tx.host.GetEmitResolver().GetReferencedValueDeclaration(name) == nil { + } else if tx.resolver.GetReferencedValueDeclaration(name) == input || tx.resolver.GetReferencedValueDeclaration(name) == nil { // only inline to a export var if the `name` lookup points at this assignment or nothing - if it points at something else, we must use a temp name // export var name: Type tx.tracker.PushErrorFallbackNode(input) @@ -1635,7 +1636,7 @@ func (tx *DeclarationTransformer) removeAllComments(node *ast.Node) { } func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) *ast.Node { - if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { + if !ignorePrivate && tx.resolver.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 } @@ -1655,7 +1656,7 @@ func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) if tx.inClassExpressionDeclaration { jsFlags &^= nodebuilder.FlagsWriteClassExpressionAsTypeLiteral } - res := tx.resolver.TryJSTypeNodeToTypeNode(tx.EmitContext(), node.Type(), tx.enclosingDeclaration, jsFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + res := tx.resolver.TryJSTypeNodeToTypeNode(node.Type(), tx.enclosingDeclaration, jsFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) if res != nil { return res } @@ -1681,9 +1682,9 @@ func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) flags &^= nodebuilder.FlagsWriteClassExpressionAsTypeLiteral } if ast.HasInferredType(node) { - typeNode = tx.resolver.CreateTypeOfDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, flags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + typeNode = tx.resolver.CreateTypeOfDeclaration(node, tx.enclosingDeclaration, flags, declarationEmitInternalNodeBuilderFlags, tx.tracker) } else if ast.IsFunctionLike(node) { - typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, flags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(node, tx.enclosingDeclaration, flags, declarationEmitInternalNodeBuilderFlags, tx.tracker) } else { debug.AssertNever(node) } @@ -1699,7 +1700,7 @@ func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) } func (tx *DeclarationTransformer) shouldPrintWithInitializer(node *ast.Node) bool { - return canHaveLiteralInitializer(tx.host, node) && node.Initializer() != nil && tx.resolver.IsLiteralConstDeclaration(tx.EmitContext().MostOriginal(node)) + return canHaveLiteralInitializer(tx.resolver, node) && node.Initializer() != nil && tx.resolver.IsLiteralConstDeclaration(tx.EmitContext().MostOriginal(node)) } func (tx *DeclarationTransformer) checkEntityNameVisibility(entityName *ast.Node, enclosingDeclaration *ast.Node) { @@ -1909,7 +1910,7 @@ func (tx *DeclarationTransformer) stripExportModifiers(statement *ast.Node) *ast return nil } parseNode := tx.EmitContext().ParseNode(statement) - if ast.IsImportEqualsDeclaration(statement) || (parseNode != nil && tx.host.GetEffectiveDeclarationFlags(parseNode, ast.ModifierFlagsDefault) != 0) || !ast.CanHaveModifiers(statement) { + if ast.IsImportEqualsDeclaration(statement) || (parseNode != nil && tx.resolver.GetEffectiveDeclarationFlags(parseNode, 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 @@ -1965,7 +1966,6 @@ func (tx *DeclarationTransformer) buildClassMembers(classNode *ast.Node, extraMe } lateIndexes := tx.resolver.CreateLateBoundIndexSignatures( - tx.EmitContext(), classNode, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, @@ -2027,7 +2027,7 @@ func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDecl varDecl := tx.Factory().NewVariableDeclaration( newId, nil, - tx.resolver.CreateTypeOfExpression(tx.EmitContext(), extendsClause.Expression(), input.AsNode(), declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker), + tx.resolver.CreateTypeOfExpression(extendsClause.Expression(), input.AsNode(), declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker), nil, ) var mods *ast.ModifierList @@ -2356,7 +2356,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(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { + if tx.resolver.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { return nil } var typeParameters *ast.TypeParameterList @@ -2374,7 +2374,7 @@ func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.T } if data := node.FunctionLikeData(); data != nil && data.FullSignature != nil { - if nodes := tx.resolver.CreateTypeParametersOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker); nodes != nil { + if nodes := tx.resolver.CreateTypeParametersOfSignatureDeclaration(node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker); nodes != nil { typeParameters = &ast.TypeParameterList{ Loc: node.Loc, Nodes: nodes, @@ -2390,7 +2390,7 @@ func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.T } func (tx *DeclarationTransformer) updateParamList(node *ast.Node, params *ast.ParameterList) *ast.ParameterList { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 { + if tx.resolver.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)) @@ -2432,7 +2432,7 @@ func (tx *DeclarationTransformer) ensureNoInitializer(node *ast.Node) *ast.Node if !ast.IsPrimitiveLiteralValue(unwrappedInitializer, true) { tx.tracker.ReportInferenceFallback(node) } - return tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(node), tx.tracker) + return tx.resolver.CreateLiteralConstValue(tx.EmitContext().ParseNode(node), tx.tracker) } return nil } diff --git a/tsc/internal/transformers/declarations/util.go b/tsc/internal/transformers/declarations/util.go index cfe66b26ba76c..422a62134808f 100644 --- a/tsc/internal/transformers/declarations/util.go +++ b/tsc/internal/transformers/declarations/util.go @@ -10,11 +10,11 @@ func needsScopeMarker(result *ast.Node) bool { return !ast.IsAnyImportOrReExport(result) && !ast.IsExportAssignment(result) && !ast.HasSyntacticModifier(result, ast.ModifierFlagsExport) && !ast.IsAmbientModule(result) } -func canHaveLiteralInitializer(host DeclarationEmitHost, node *ast.Node) bool { +func canHaveLiteralInitializer(resolver printer.EmitResolver, node *ast.Node) bool { switch node.Kind { case ast.KindPropertyDeclaration, ast.KindPropertySignature: - return host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) == 0 + return resolver.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) == 0 case ast.KindParameter, ast.KindVariableDeclaration: return true @@ -146,8 +146,8 @@ func unwrapParenthesizedExpression(o *ast.Node) *ast.Node { return o } -func isPrivateMethodTypeParameter(host DeclarationEmitHost, node *ast.TypeParameterDeclaration) bool { - return node.AsNode().Parent.Kind == ast.KindMethodDeclaration && host.GetEffectiveDeclarationFlags(node.AsNode().Parent, ast.ModifierFlagsPrivate) != 0 +func isPrivateMethodTypeParameter(resolver printer.EmitResolver, node *ast.TypeParameterDeclaration) bool { + return node.AsNode().Parent.Kind == ast.KindMethodDeclaration && resolver.GetEffectiveDeclarationFlags(node.AsNode().Parent, ast.ModifierFlagsPrivate) != 0 } // Returns true if expando properties should be emitted for this function. diff --git a/tsc/internal/transformers/tstransforms/importelision_test.go b/tsc/internal/transformers/tstransforms/importelision_test.go index 259db833bcec4..b6654955a7c5d 100644 --- a/tsc/internal/transformers/tstransforms/importelision_test.go +++ b/tsc/internal/transformers/tstransforms/importelision_test.go @@ -263,9 +263,10 @@ func TestImportElision(t *testing.T) { }, }, nil) - emitResolver := c.GetEmitResolver() + emitContext := printer.NewEmitContext() + emitResolver := c.NewEmitResolver(emitContext) - opts := &transformers.TransformOptions{CompilerOptions: compilerOptions, Context: printer.NewEmitContext(), EmitResolver: emitResolver, Resolver: emitResolver} + opts := &transformers.TransformOptions{CompilerOptions: compilerOptions, Context: emitContext, EmitResolver: emitResolver, Resolver: emitResolver} file = tstransforms.NewTypeEraserTransformer(opts).TransformSourceFile(file) file = tstransforms.NewImportElisionTransformer(opts).TransformSourceFile(file) emittestutil.CheckEmit(t, nil, file, rec.output)