Skip to content

Ensure we use the right checker in declaration diagnostics, fix lifetime #1309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -30572,7 +30572,7 @@ func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type {
return c.getTypeOfNode(node)
}

func (c *Checker) GetEmitResolver(file *ast.SourceFile) *emitResolver {
func (c *Checker) GetEmitResolver() *emitResolver {
c.emitResolverOnce.Do(func() {
c.emitResolver = &emitResolver{checker: c}
})
Expand Down
4 changes: 2 additions & 2 deletions internal/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ func (b *nodeBuilderImpl) symbolToParameterDeclaration(parameterSymbol *ast.Symb
}
name := b.parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration)
// TODO: isOptionalParameter on emit resolver here is silly - hoist to checker and reexpose on emit resolver?
isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver(nil).isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0
isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver().isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0
var questionToken *ast.Node
if isOptional {
questionToken = b.f.NewToken(ast.KindQuestionToken)
Expand Down Expand Up @@ -1887,7 +1887,7 @@ func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati
}
}
// !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker
addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver(nil).requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration)
addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver().requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration)
if addUndefinedForParameter {
t = b.ch.getOptionalType(t, false)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/symbolaccessibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (ch *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclara
if len(accessibleSymbolChain) > 0 {
hadAccessibleChain = symbol
// TODO: going through emit resolver here is weird. Relayer these APIs.
hasAccessibleDeclarations := ch.GetEmitResolver(nil).hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible)
hasAccessibleDeclarations := ch.GetEmitResolver().hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible)
if hasAccessibleDeclarations != nil {
return hasAccessibleDeclarations
}
Expand Down
25 changes: 14 additions & 11 deletions internal/compiler/emitHost.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,22 @@ type EmitHost interface {
GetCurrentDirectory() string
CommonSourceDirectory() string
IsEmitBlocked(file string) bool
GetEmitResolver(file *ast.SourceFile) printer.EmitResolver
}

var _ EmitHost = (*emitHost)(nil)

// NOTE: emitHost operations must be thread-safe
type emitHost struct {
program *Program
program *Program
emitResolver printer.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(),
}, done
}

func (host *emitHost) GetModeForUsageLocation(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) core.ResolutionMode {
Expand Down Expand Up @@ -83,7 +91,7 @@ func (host *emitHost) GetRedirectTargets(path tspath.Path) []string {
}

func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags {
return host.GetEmitResolver(ast.GetSourceFileOfNode(node)).GetEffectiveDeclarationFlags(node, flags)
return host.GetEmitResolver().GetEffectiveDeclarationFlags(node, flags)
}

func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths {
Expand All @@ -92,7 +100,7 @@ func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool
}

func (host *emitHost) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode {
return host.GetEmitResolver(ast.GetSourceFileOfNode(node)).GetResolutionModeOverride(node)
return host.GetEmitResolver().GetResolutionModeOverride(node)
}

func (host *emitHost) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile {
Expand All @@ -116,13 +124,8 @@ func (host *emitHost) WriteFile(fileName string, text string, writeByteOrderMark
return host.program.Host().FS().WriteFile(fileName, text, writeByteOrderMark)
}

func (host *emitHost) GetEmitResolver(file *ast.SourceFile) printer.EmitResolver {
// The context and done function don't matter in tsc, currently the only caller of this function.
// But if this ever gets used by LSP code, we'll need to thread the context properly and pass the
// done function to the caller to ensure resources are cleaned up at the end of the request.
checker, done := host.program.GetTypeCheckerForFile(context.TODO(), file)
defer done()
return checker.GetEmitResolver(file)
func (host *emitHost) GetEmitResolver() printer.EmitResolver {
return host.emitResolver
}

func (host *emitHost) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool {
Expand Down
16 changes: 8 additions & 8 deletions internal/compiler/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ func (e *emitter) emit() {
e.emitBuildInfo(e.paths.BuildInfoPath())
}

func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer {
emitResolver := e.host.GetEmitResolver(sourceFile)
transform := declarations.NewDeclarationTransformer(e.host, emitResolver, emitContext, e.host.Options(), declarationFilePath, declarationMapPath)
func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer {
transform := declarations.NewDeclarationTransformer(e.host, emitContext, e.host.Options(), declarationFilePath, declarationMapPath)
return []*declarations.DeclarationTransformer{transform}
}

Expand Down Expand Up @@ -86,7 +85,7 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo
var emitResolver printer.EmitResolver
var referenceResolver binder.ReferenceResolver
if importElisionEnabled || options.GetJSXTransformEnabled() {
emitResolver = host.GetEmitResolver(sourceFile)
emitResolver = host.GetEmitResolver()
emitResolver.MarkLinkedReferencesRecursively(sourceFile)
referenceResolver = emitResolver
} else {
Expand Down Expand Up @@ -179,7 +178,7 @@ func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFil
var diags []*ast.Diagnostic
emitContext, putEmitContext := printer.GetEmitContext()
defer putEmitContext()
for _, transformer := range e.getDeclarationTransformers(emitContext, sourceFile, declarationFilePath, declarationMapPath) {
for _, transformer := range e.getDeclarationTransformers(emitContext, declarationFilePath, declarationMapPath) {
sourceFile = transformer.TransformSourceFile(sourceFile)
diags = append(diags, transformer.GetDiagnostics()...)
}
Expand Down Expand Up @@ -378,6 +377,7 @@ type SourceFileMayBeEmittedHost interface {
IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool
GetCurrentDirectory() string
UseCaseSensitiveFileNames() bool
SourceFiles() []*ast.SourceFile
}

func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host SourceFileMayBeEmittedHost, forceDtsEmit bool) bool {
Expand Down Expand Up @@ -435,7 +435,7 @@ func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host SourceFileMayBeEmit
return true
}

func getSourceFilesToEmit(host printer.EmitHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile {
func getSourceFilesToEmit(host SourceFileMayBeEmittedHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile {
// !!! outFile not yet implemented, may be deprecated
var sourceFiles []*ast.SourceFile
if targetSourceFile != nil {
Expand All @@ -452,13 +452,13 @@ func isSourceFileNotJson(file *ast.SourceFile) bool {
return !ast.IsJsonSourceFile(file)
}

func getDeclarationDiagnostics(host EmitHost, resolver printer.EmitResolver, file *ast.SourceFile) []*ast.Diagnostic {
func getDeclarationDiagnostics(host EmitHost, file *ast.SourceFile) []*ast.Diagnostic {
fullFiles := core.Filter(getSourceFilesToEmit(host, file, false), isSourceFileNotJson)
if !core.Some(fullFiles, func(f *ast.SourceFile) bool { return f == file }) {
return []*ast.Diagnostic{}
}
options := host.Options()
transform := declarations.NewDeclarationTransformer(host, resolver, nil, options, "", "")
transform := declarations.NewDeclarationTransformer(host, nil, options, "", "")
transform.TransformSourceFile(file)
return transform.GetDiagnostics()
}
21 changes: 11 additions & 10 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ func (p *Program) getSemanticDiagnosticsForFile(ctx context.Context, sourceFile
return filtered
}

func (p *Program) getDeclarationDiagnosticsForFile(_ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
func (p *Program) getDeclarationDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
if sourceFile.IsDeclarationFile {
return []*ast.Diagnostic{}
}
Expand All @@ -458,8 +458,9 @@ func (p *Program) getDeclarationDiagnosticsForFile(_ctx context.Context, sourceF
return cached
}

host := &emitHost{program: p}
diagnostics := getDeclarationDiagnostics(host, host.GetEmitResolver(sourceFile), sourceFile)
host, done := newEmitHost(ctx, p, sourceFile)
defer done()
diagnostics := getDeclarationDiagnostics(host, sourceFile)
diagnostics, _ = p.declarationDiagnosticCache.LoadOrStore(sourceFile, diagnostics)
return diagnostics
}
Expand Down Expand Up @@ -651,9 +652,8 @@ func (p *Program) CommonSourceDirectory() string {
p.Options(),
func() []string {
var files []string
host := &emitHost{program: p}
for _, file := range p.files {
if sourceFileMayBeEmitted(file, host, false /*forceDtsEmit*/) {
if sourceFileMayBeEmitted(file, p, false /*forceDtsEmit*/) {
files = append(files, file.FileName())
}
}
Expand Down Expand Up @@ -688,27 +688,28 @@ func (p *Program) Emit(options EmitOptions) *EmitResult {
// !!! performance measurement
p.BindSourceFiles()

host := &emitHost{program: p}

writerPool := &sync.Pool{
New: func() any {
return printer.NewTextWriter(host.Options().NewLine.GetNewLineCharacter())
return printer.NewTextWriter(p.Options().NewLine.GetNewLineCharacter())
},
}
wg := core.NewWorkGroup(p.singleThreaded())
var emitters []*emitter
sourceFiles := getSourceFilesToEmit(host, options.TargetSourceFile, options.forceDtsEmit)
sourceFiles := getSourceFilesToEmit(p, options.TargetSourceFile, options.forceDtsEmit)

for _, sourceFile := range sourceFiles {
emitter := &emitter{
host: host,
emittedFilesList: nil,
sourceMapDataList: nil,
writer: nil,
sourceFile: sourceFile,
}
emitters = append(emitters, emitter)
wg.Queue(func() {
host, done := newEmitHost(context.TODO(), p, sourceFile)
defer done()
emitter.host = host

// take an unused writer
writer := writerPool.Get().(printer.EmitTextWriter)
writer.Clear()
Expand Down
2 changes: 1 addition & 1 deletion internal/printer/emithost.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type EmitHost interface {
IsEmitBlocked(file string) bool
WriteFile(fileName string, text string, writeByteOrderMark bool, relatedSourceFiles []*ast.SourceFile, data *WriteFileData) error
GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind
GetEmitResolver(file *ast.SourceFile) EmitResolver
GetEmitResolver() EmitResolver
GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference
IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool
}
4 changes: 3 additions & 1 deletion internal/transformers/declarations/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type DeclarationEmitHost interface {
GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths
GetResolutionModeOverride(node *ast.Node) core.ResolutionMode
GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags
GetEmitResolver() printer.EmitResolver
}

type DeclarationTransformer struct {
Expand All @@ -60,7 +61,8 @@ type DeclarationTransformer struct {
rawLibReferenceDirectives []*ast.FileReference
}

func NewDeclarationTransformer(host DeclarationEmitHost, resolver printer.EmitResolver, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer {
func NewDeclarationTransformer(host DeclarationEmitHost, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer {
resolver := host.GetEmitResolver()
state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.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)
Expand Down
2 changes: 1 addition & 1 deletion internal/transformers/tstransforms/importelision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func TestImportElision(t *testing.T) {
},
})

emitResolver := c.GetEmitResolver(file)
emitResolver := c.GetEmitResolver()
emitResolver.MarkLinkedReferencesRecursively(file)

emitContext := printer.NewEmitContext()
Expand Down