diff --git a/internal/compiler/checkerpool.go b/internal/compiler/checkerpool.go index 9b32992f20..9e0fe4e0cb 100644 --- a/internal/compiler/checkerpool.go +++ b/internal/compiler/checkerpool.go @@ -14,6 +14,7 @@ import ( type CheckerPool interface { GetChecker(ctx context.Context) (*checker.Checker, func()) GetCheckerForFile(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) + GetCheckerForFileExclusive(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) GetAllCheckers(ctx context.Context) ([]*checker.Checker, func()) Files(checker *checker.Checker) iter.Seq[*ast.SourceFile] } @@ -24,6 +25,7 @@ type checkerPool struct { createCheckersOnce sync.Once checkers []*checker.Checker + locks []sync.Mutex fileAssociations map[*ast.SourceFile]*checker.Checker } @@ -34,6 +36,7 @@ func newCheckerPool(checkerCount int, program *Program) *checkerPool { program: program, checkerCount: checkerCount, checkers: make([]*checker.Checker, checkerCount), + locks: make([]sync.Mutex, checkerCount), } return pool @@ -45,6 +48,16 @@ func (p *checkerPool) GetCheckerForFile(ctx context.Context, file *ast.SourceFil return checker, noop } +func (p *checkerPool) GetCheckerForFileExclusive(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) { + c, done := p.GetCheckerForFile(ctx, file) + idx := slices.Index(p.checkers, c) + p.locks[idx].Lock() + return c, sync.OnceFunc(func() { + p.locks[idx].Unlock() + done() + }) +} + func (p *checkerPool) GetChecker(ctx context.Context) (*checker.Checker, func()) { p.createCheckers() checker := p.checkers[0] diff --git a/internal/compiler/program.go b/internal/compiler/program.go index ce89aaa601..9bd9c5db5a 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -383,6 +383,12 @@ func (p *Program) GetTypeCheckerForFile(ctx context.Context, file *ast.SourceFil return p.checkerPool.GetCheckerForFile(ctx, file) } +// Return a checker for the given file, locked to the current thread to prevent data races from multiple threads +// accessing the same checker. The lock will be released when the `done` function is called. +func (p *Program) GetTypeCheckerForFileExclusive(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) { + return p.checkerPool.GetCheckerForFileExclusive(ctx, file) +} + func (p *Program) GetResolvedModule(file ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule { if resolutions, ok := p.resolvedModules[file.Path()]; ok { if resolved, ok := resolutions[module.ModeAwareCacheKey{Name: moduleReference, Mode: mode}]; ok { @@ -1273,6 +1279,10 @@ func (p *Program) InstantiationCount() int { return count } +func (p *Program) Program() *Program { + return p +} + func (p *Program) GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData { return p.sourceFileMetaDatas[path] } @@ -1437,6 +1447,7 @@ func CombineEmitResults(results []*EmitResult) *EmitResult { type ProgramLike interface { Options() *core.CompilerOptions + GetSourceFile(path string) *ast.SourceFile GetSourceFiles() []*ast.SourceFile GetConfigFileParsingDiagnostics() []*ast.Diagnostic GetSyntacticDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic @@ -1446,7 +1457,11 @@ type ProgramLike interface { GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic GetSemanticDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic GetDeclarationDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic + GetSuggestionDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic Emit(ctx context.Context, options EmitOptions) *EmitResult + CommonSourceDirectory() string + IsSourceFileDefaultLibrary(path tspath.Path) bool + Program() *Program } func HandleNoEmitOnError(ctx context.Context, program ProgramLike, file *ast.SourceFile) *EmitResult { diff --git a/internal/execute/incremental/affectedfileshandler.go b/internal/execute/incremental/affectedfileshandler.go index 355b58aec8..ed82d52f5c 100644 --- a/internal/execute/incremental/affectedfileshandler.go +++ b/internal/execute/incremental/affectedfileshandler.go @@ -229,7 +229,7 @@ func (h *affectedFilesHandler) handleDtsMayChangeOfAffectedFile(dtsMayChange dts break } if typeChecker == nil { - typeChecker, done = h.program.program.GetTypeCheckerForFile(h.ctx, affectedFile) + typeChecker, done = h.program.program.GetTypeCheckerForFileExclusive(h.ctx, affectedFile) } aliased := checker.SkipAlias(exported, typeChecker) if aliased == exported { diff --git a/internal/execute/incremental/program.go b/internal/execute/incremental/program.go index ceac9c35d4..dd94d16241 100644 --- a/internal/execute/incremental/program.go +++ b/internal/execute/incremental/program.go @@ -84,12 +84,36 @@ func (p *Program) Options() *core.CompilerOptions { return p.snapshot.options } +// CommonSourceDirectory implements compiler.AnyProgram interface. +func (p *Program) CommonSourceDirectory() string { + p.panicIfNoProgram("CommonSourceDirectory") + return p.program.CommonSourceDirectory() +} + +// Program implements compiler.AnyProgram interface. +func (p *Program) Program() *compiler.Program { + p.panicIfNoProgram("Program") + return p.program +} + +// IsSourceFileDefaultLibrary implements compiler.AnyProgram interface. +func (p *Program) IsSourceFileDefaultLibrary(path tspath.Path) bool { + p.panicIfNoProgram("IsSourceFileDefaultLibrary") + return p.program.IsSourceFileDefaultLibrary(path) +} + // GetSourceFiles implements compiler.AnyProgram interface. func (p *Program) GetSourceFiles() []*ast.SourceFile { p.panicIfNoProgram("GetSourceFiles") return p.program.GetSourceFiles() } +// GetSourceFile implements compiler.AnyProgram interface. +func (p *Program) GetSourceFile(path string) *ast.SourceFile { + p.panicIfNoProgram("GetSourceFile") + return p.program.GetSourceFile(path) +} + // GetConfigFileParsingDiagnostics implements compiler.AnyProgram interface. func (p *Program) GetConfigFileParsingDiagnostics() []*ast.Diagnostic { p.panicIfNoProgram("GetConfigFileParsingDiagnostics") @@ -172,6 +196,12 @@ func (p *Program) GetDeclarationDiagnostics(ctx context.Context, file *ast.Sourc return nil } +// GetSuggestionDiagnostics implements compiler.AnyProgram interface. +func (p *Program) GetSuggestionDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic { + p.panicIfNoProgram("GetSuggestionDiagnostics") + return p.program.GetSuggestionDiagnostics(ctx, file) // TODO: incremental suggestion diagnostics (only relevant in editor incremental builder?) +} + // GetModeForUsageLocation implements compiler.AnyProgram interface. func (p *Program) Emit(ctx context.Context, options compiler.EmitOptions) *compiler.EmitResult { p.panicIfNoProgram("Emit") diff --git a/internal/execute/incremental/programtosnapshot.go b/internal/execute/incremental/programtosnapshot.go index dfaeb25131..56e0415a94 100644 --- a/internal/execute/incremental/programtosnapshot.go +++ b/internal/execute/incremental/programtosnapshot.go @@ -263,7 +263,7 @@ func getReferencedFiles(program *compiler.Program, file *ast.SourceFile) *collec // We need to use a set here since the code can contain the same import twice, // but that will only be one dependency. // To avoid invernal conversion, the key of the referencedFiles map must be of type Path - checker, done := program.GetTypeCheckerForFile(context.TODO(), file) + checker, done := program.GetTypeCheckerForFileExclusive(context.TODO(), file) defer done() for _, importName := range file.Imports() { addReferencedFilesFromImportLiteral(file, &referencedFiles, checker, importName) diff --git a/internal/project/checkerpool.go b/internal/project/checkerpool.go index 017fd2fbee..3618917d96 100644 --- a/internal/project/checkerpool.go +++ b/internal/project/checkerpool.go @@ -20,6 +20,7 @@ type CheckerPool struct { cond *sync.Cond createCheckersOnce sync.Once checkers []*checker.Checker + locks []sync.Mutex inUse map[*checker.Checker]bool fileAssociations map[*ast.SourceFile]int requestAssociations map[string]int @@ -33,6 +34,7 @@ func newCheckerPool(maxCheckers int, program *compiler.Program, log func(msg str program: program, maxCheckers: maxCheckers, checkers: make([]*checker.Checker, maxCheckers), + locks: make([]sync.Mutex, maxCheckers), inUse: make(map[*checker.Checker]bool), requestAssociations: make(map[string]int), log: log, @@ -75,6 +77,12 @@ func (p *CheckerPool) GetCheckerForFile(ctx context.Context, file *ast.SourceFil return checker, p.createRelease(requestID, index, checker) } +// GetCheckerForFileExclusive is the same as GetCheckerForFile but also locks a mutex associated with the checker. +// Call `done` to free the lock. +func (p *CheckerPool) GetCheckerForFileExclusive(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) { + panic("unimplemented") // implement if used by LS +} + func (p *CheckerPool) GetChecker(ctx context.Context) (*checker.Checker, func()) { p.mu.Lock() defer p.mu.Unlock() diff --git a/internal/testrunner/compiler_runner.go b/internal/testrunner/compiler_runner.go index 5b86188001..3a2745d242 100644 --- a/internal/testrunner/compiler_runner.go +++ b/internal/testrunner/compiler_runner.go @@ -528,7 +528,8 @@ func createHarnessTestFile(unit *testUnit, currentDirectory string) *harnessutil func (c *compilerTest) verifyUnionOrdering(t *testing.T) { t.Run("union ordering", func(t *testing.T) { - checkers, done := c.result.Program.GetTypeCheckers(t.Context()) + p := c.result.Program.Program() + checkers, done := p.GetTypeCheckers(t.Context()) defer done() for _, c := range checkers { for union := range c.UnionTypes() { diff --git a/internal/testutil/harnessutil/harnessutil.go b/internal/testutil/harnessutil/harnessutil.go index f88e72048d..ae0d04f040 100644 --- a/internal/testutil/harnessutil/harnessutil.go +++ b/internal/testutil/harnessutil/harnessutil.go @@ -21,6 +21,7 @@ import ( "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/compiler" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/execute/incremental" "github.com/microsoft/typescript-go/internal/outputpaths" "github.com/microsoft/typescript-go/internal/parser" "github.com/microsoft/typescript-go/internal/repo" @@ -687,13 +688,13 @@ func compileFilesWithHost( } emitResult := program.Emit(ctx, compiler.EmitOptions{}) - return newCompilationResult(config.CompilerOptions(), program, emitResult, diagnostics, harnessOptions) + return newCompilationResult(host, config.CompilerOptions(), program, emitResult, diagnostics, harnessOptions) } type CompilationResult struct { Diagnostics []*ast.Diagnostic Result *compiler.EmitResult - Program *compiler.Program + Program compiler.ProgramLike Options *core.CompilerOptions HarnessOptions *HarnessOptions JS collections.OrderedMap[string, *TestFile] @@ -705,6 +706,7 @@ type CompilationResult struct { inputs []*TestFile inputsAndOutputs collections.OrderedMap[string, *CompilationOutput] Trace string + Host compiler.CompilerHost } type CompilationOutput struct { @@ -715,8 +717,9 @@ type CompilationOutput struct { } func newCompilationResult( + host compiler.CompilerHost, options *core.CompilerOptions, - program *compiler.Program, + program compiler.ProgramLike, result *compiler.EmitResult, diagnostics []*ast.Diagnostic, harnessOptions *HarnessOptions, @@ -731,9 +734,10 @@ func newCompilationResult( Program: program, Options: options, HarnessOptions: harnessOptions, + Host: host, } - fs := program.Host().FS().(*OutputRecorderFS) + fs := host.FS().(*OutputRecorderFS) if fs != nil && program != nil { // Corsa, unlike Strada, can use multiple threads for emit. As a result, the order of outputs is non-deterministic. // To make the order deterministic, we sort the outputs by the order of the inputs. @@ -803,7 +807,7 @@ func compareTestFiles(a *TestFile, b *TestFile) int { } func (c *CompilationResult) getOutputPath(path string, ext string) string { - path = tspath.ResolvePath(c.Program.GetCurrentDirectory(), path) + path = tspath.ResolvePath(c.Host.GetCurrentDirectory(), path) var outDir string if ext == ".d.ts" || ext == ".d.mts" || ext == ".d.cts" || (strings.HasSuffix(ext, ".ts") && strings.Contains(ext, ".d.")) { outDir = c.Options.DeclarationDir @@ -817,17 +821,17 @@ func (c *CompilationResult) getOutputPath(path string, ext string) string { common := c.Program.CommonSourceDirectory() if common != "" { path = tspath.GetRelativePathFromDirectory(common, path, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: c.Program.UseCaseSensitiveFileNames(), - CurrentDirectory: c.Program.GetCurrentDirectory(), + UseCaseSensitiveFileNames: c.Host.FS().UseCaseSensitiveFileNames(), + CurrentDirectory: c.Host.GetCurrentDirectory(), }) - path = tspath.CombinePaths(tspath.ResolvePath(c.Program.GetCurrentDirectory(), c.Options.OutDir), path) + path = tspath.CombinePaths(tspath.ResolvePath(c.Host.GetCurrentDirectory(), c.Options.OutDir), path) } } return tspath.ChangeExtension(path, ext) } func (r *CompilationResult) FS() vfs.FS { - return r.Program.Host().FS() + return r.Host.FS() } func (r *CompilationResult) GetNumberOfJSFiles(includeJson bool) int { @@ -852,7 +856,7 @@ func (c *CompilationResult) Outputs() []*TestFile { } func (c *CompilationResult) GetInputsAndOutputsForFile(path string) *CompilationOutput { - return c.inputsAndOutputs.GetOrZero(tspath.ResolvePath(c.Program.GetCurrentDirectory(), path)) + return c.inputsAndOutputs.GetOrZero(tspath.ResolvePath(c.Host.GetCurrentDirectory(), path)) } func (c *CompilationResult) GetInputsForFile(path string) []*TestFile { @@ -915,7 +919,24 @@ func (c *CompilationResult) GetSourceMapRecord() string { return sourceMapRecorder.String() } -func createProgram(host compiler.CompilerHost, config *tsoptions.ParsedCommandLine) *compiler.Program { +type testBuildInfoReader struct { + inner incremental.BuildInfoReader +} + +func (t *testBuildInfoReader) ReadBuildInfo(config *tsoptions.ParsedCommandLine) *incremental.BuildInfo { + r := t.inner.ReadBuildInfo(config) + if r == nil { + return nil + } + r.Version = core.Version() + return r +} + +func getTestBuildInfoReader(host compiler.CompilerHost) *testBuildInfoReader { + return &testBuildInfoReader{inner: incremental.NewBuildInfoReader(host)} +} + +func createProgram(host compiler.CompilerHost, config *tsoptions.ParsedCommandLine) compiler.ProgramLike { var singleThreaded core.Tristate if testutil.TestProgramIsSingleThreaded() { singleThreaded = core.TSTrue @@ -927,6 +948,11 @@ func createProgram(host compiler.CompilerHost, config *tsoptions.ParsedCommandLi SingleThreaded: singleThreaded, } program := compiler.NewProgram(programOptions) + if config.CompilerOptions().Incremental.IsTrue() { + oldProgram := incremental.ReadBuildInfoProgram(config, getTestBuildInfoReader(host), host) + incrementalProgram := incremental.NewProgram(program, oldProgram, incremental.CreateHost(host), false) + return incrementalProgram + } return program } diff --git a/internal/testutil/tsbaseline/js_emit_baseline.go b/internal/testutil/tsbaseline/js_emit_baseline.go index 8b51c19349..fc4bced86a 100644 --- a/internal/testutil/tsbaseline/js_emit_baseline.go +++ b/internal/testutil/tsbaseline/js_emit_baseline.go @@ -201,7 +201,7 @@ func prepareDeclarationCompilationContext( var sourceFileName string if len(options.OutDir) != 0 { - sourceFilePath := tspath.GetNormalizedAbsolutePath(sourceFile.FileName(), result.Program.GetCurrentDirectory()) + sourceFilePath := tspath.GetNormalizedAbsolutePath(sourceFile.FileName(), result.Host.GetCurrentDirectory()) sourceFilePath = strings.Replace(sourceFilePath, result.Program.CommonSourceDirectory(), "", 1) sourceFileName = tspath.CombinePaths(options.OutDir, sourceFilePath) } else { diff --git a/internal/testutil/tsbaseline/type_symbol_baseline.go b/internal/testutil/tsbaseline/type_symbol_baseline.go index f08dbf37b4..b55d91dc10 100644 --- a/internal/testutil/tsbaseline/type_symbol_baseline.go +++ b/internal/testutil/tsbaseline/type_symbol_baseline.go @@ -31,7 +31,7 @@ func DoTypeAndSymbolBaseline( t *testing.T, baselinePath string, header string, - program *compiler.Program, + program compiler.ProgramLike, allFiles []*harnessutil.TestFile, opts baseline.Options, skipTypeBaselines bool, @@ -259,13 +259,13 @@ func iterateBaseline(allFiles []*harnessutil.TestFile, fullWalker *typeWriterWal } type typeWriterWalker struct { - program *compiler.Program + program compiler.ProgramLike hadErrorBaseline bool currentSourceFile *ast.SourceFile declarationTextCache map[*ast.Node]string } -func newTypeWriterWalker(program *compiler.Program, hadErrorBaseline bool) *typeWriterWalker { +func newTypeWriterWalker(program compiler.ProgramLike, hadErrorBaseline bool) *typeWriterWalker { return &typeWriterWalker{ program: program, hadErrorBaseline: hadErrorBaseline, @@ -276,7 +276,7 @@ func newTypeWriterWalker(program *compiler.Program, hadErrorBaseline bool) *type func (walker *typeWriterWalker) getTypeCheckerForCurrentFile() (*checker.Checker, func()) { // If we don't use the right checker for the file, its contents won't be up to date // since the types/symbols baselines appear to depend on files having been checked. - return walker.program.GetTypeCheckerForFile(context.Background(), walker.currentSourceFile) + return walker.program.Program().GetTypeCheckerForFile(context.Background(), walker.currentSourceFile) } type typeWriterResult struct { diff --git a/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.js b/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.js new file mode 100644 index 0000000000..8f3f16b17c --- /dev/null +++ b/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.js @@ -0,0 +1,295 @@ +//// [tests/cases/compiler/incrementalConcurrentSafeAliasFollowing.ts] //// + +//// [a.tsbuildinfo] +{ + "version": "TEST", + "root": [ [ 8, 9 ] ], + "fileNames": [ + "lib.d.ts", + "lib.es5.d.ts", + "lib.dom.d.ts", + "lib.webworker.importscripts.d.ts", + "lib.scripthost.d.ts", + "lib.decorators.d.ts", + "lib.decorators.legacy.d.ts", + "./.src/file2.ts", + "./.src/file1.ts" + ], + "fileInfos": [ + "8aa2344ef67b04dfd9aa23b0f29ffb31", + { + "version": "71cf8049ea8d435bcdf47408dac2525c", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "9cf691967d2e0b0210f5864fdf1ad87a", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "eb49c11101339d745cfc83e213607152", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "a4fa81fccf6300a830a36517b5beb51f", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "45c91c5f844a9ee1df11d1b71c484b0e", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "39e009135c77d60baa790854b51d2195", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + "ed441e10e1833cd4635f7c61645b7ee7", + "553372ff8498789eb6e91ea68f08e47a" + ], + "fileIdsList": [ [ 8 ] ], + "options": { + "newLine": 1, + "noErrorTruncation": true, + "skipDefaultLibCheck": true, + "tsBuildInfoFile": "./a.tsbuildinfo" + }, + "referencedMap": [ [ 9, 1 ] ] +} +//// [file1.ts] +// need an out of date but present buildinfo, chained export aliases, +// and enough files that the same checker is used for multiple files +// to trigger the race from typescript-go/#1470 +import {b} from "./file2.js" + +export type {b as c} +//// [file2.ts] +const a = 1 + +export type {a as b} +//// [file0.ts] +import {c} from "./file1.js" + +export type {c as d} +//// [file3.ts] +import {b} from "./file2.js" + +export type {b as e} +//// [file4.ts] +import {b} from "./file2.js" + +export type {b as f} +//// [file5.ts] +import {b} from "./file2.js" + +export type {b as g} +//// [file6.ts] +import {b} from "./file2.js" + +export type {b as h} +//// [file7.ts] +import {b} from "./file2.js" + +export type {b as i} +//// [file8.ts] +import {b} from "./file2.js" + +export type {b as j} +//// [file9.ts] +import {b} from "./file2.js" + +export type {b as k} +//// [file10.ts] +import {b} from "./file2.js" + +export type {b as l} +//// [file11.ts] +import {b} from "./file2.js" + +export type {b as m} +//// [file12.ts] +import {b} from "./file2.js" + +export type {b as n} +//// [file13.ts] +import {b} from "./file2.js" + +export type {b as o} +//// [file14.ts] +import {b} from "./file2.js" + +export type {b as p} +//// [file15.ts] +import {b} from "./file2.js" + +export type {b as q} +//// [barrel.ts] +export * from "./file0.js" +export * from "./file1.js" +export * from "./file2.js" +export * from "./file3.js" +export * from "./file4.js" +export * from "./file5.js" +export * from "./file6.js" +export * from "./file7.js" +export * from "./file8.js" +export * from "./file9.js" +export * from "./file10.js" +export * from "./file11.js" +export * from "./file12.js" +export * from "./file13.js" +export * from "./file14.js" +export * from "./file15.js" + +//// [file2.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const a = 1; +//// [file1.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file0.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file3.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file4.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file5.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file6.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file7.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file8.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file9.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file10.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file11.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file12.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file13.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file14.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file15.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [barrel.js] +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./file0.js"), exports); +__exportStar(require("./file1.js"), exports); +__exportStar(require("./file2.js"), exports); +__exportStar(require("./file3.js"), exports); +__exportStar(require("./file4.js"), exports); +__exportStar(require("./file5.js"), exports); +__exportStar(require("./file6.js"), exports); +__exportStar(require("./file7.js"), exports); +__exportStar(require("./file8.js"), exports); +__exportStar(require("./file9.js"), exports); +__exportStar(require("./file10.js"), exports); +__exportStar(require("./file11.js"), exports); +__exportStar(require("./file12.js"), exports); +__exportStar(require("./file13.js"), exports); +__exportStar(require("./file14.js"), exports); +__exportStar(require("./file15.js"), exports); + + +//// [file2.d.ts] +declare const a = 1; +export type { a as b }; +//// [file1.d.ts] +import { b } from "./file2.js"; +export type { b as c }; +//// [file0.d.ts] +import { c } from "./file1.js"; +export type { c as d }; +//// [file3.d.ts] +import { b } from "./file2.js"; +export type { b as e }; +//// [file4.d.ts] +import { b } from "./file2.js"; +export type { b as f }; +//// [file5.d.ts] +import { b } from "./file2.js"; +export type { b as g }; +//// [file6.d.ts] +import { b } from "./file2.js"; +export type { b as h }; +//// [file7.d.ts] +import { b } from "./file2.js"; +export type { b as i }; +//// [file8.d.ts] +import { b } from "./file2.js"; +export type { b as j }; +//// [file9.d.ts] +import { b } from "./file2.js"; +export type { b as k }; +//// [file10.d.ts] +import { b } from "./file2.js"; +export type { b as l }; +//// [file11.d.ts] +import { b } from "./file2.js"; +export type { b as m }; +//// [file12.d.ts] +import { b } from "./file2.js"; +export type { b as n }; +//// [file13.d.ts] +import { b } from "./file2.js"; +export type { b as o }; +//// [file14.d.ts] +import { b } from "./file2.js"; +export type { b as p }; +//// [file15.d.ts] +import { b } from "./file2.js"; +export type { b as q }; +//// [barrel.d.ts] +export * from "./file0.js"; +export * from "./file1.js"; +export * from "./file2.js"; +export * from "./file3.js"; +export * from "./file4.js"; +export * from "./file5.js"; +export * from "./file6.js"; +export * from "./file7.js"; +export * from "./file8.js"; +export * from "./file9.js"; +export * from "./file10.js"; +export * from "./file11.js"; +export * from "./file12.js"; +export * from "./file13.js"; +export * from "./file14.js"; +export * from "./file15.js"; diff --git a/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.symbols b/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.symbols new file mode 100644 index 0000000000..b6ca4ff7d0 --- /dev/null +++ b/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.symbols @@ -0,0 +1,151 @@ +//// [tests/cases/compiler/incrementalConcurrentSafeAliasFollowing.ts] //// + +=== file1.ts === +// need an out of date but present buildinfo, chained export aliases, +// and enough files that the same checker is used for multiple files +// to trigger the race from typescript-go/#1470 +import {b} from "./file2.js" +>b : Symbol(b, Decl(file1.ts, 3, 8)) + +export type {b as c} +>b : Symbol(b, Decl(file1.ts, 3, 8)) +>c : Symbol(c, Decl(file1.ts, 5, 13)) + +=== file2.ts === +const a = 1 +>a : Symbol(a, Decl(file2.ts, 0, 5)) + +export type {a as b} +>a : Symbol(a, Decl(file2.ts, 0, 5)) +>b : Symbol(b, Decl(file2.ts, 2, 13)) + +=== file0.ts === +import {c} from "./file1.js" +>c : Symbol(c, Decl(file0.ts, 0, 8)) + +export type {c as d} +>c : Symbol(c, Decl(file0.ts, 0, 8)) +>d : Symbol(d, Decl(file0.ts, 2, 13)) + +=== file3.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file3.ts, 0, 8)) + +export type {b as e} +>b : Symbol(b, Decl(file3.ts, 0, 8)) +>e : Symbol(e, Decl(file3.ts, 2, 13)) + +=== file4.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file4.ts, 0, 8)) + +export type {b as f} +>b : Symbol(b, Decl(file4.ts, 0, 8)) +>f : Symbol(f, Decl(file4.ts, 2, 13)) + +=== file5.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file5.ts, 0, 8)) + +export type {b as g} +>b : Symbol(b, Decl(file5.ts, 0, 8)) +>g : Symbol(g, Decl(file5.ts, 2, 13)) + +=== file6.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file6.ts, 0, 8)) + +export type {b as h} +>b : Symbol(b, Decl(file6.ts, 0, 8)) +>h : Symbol(h, Decl(file6.ts, 2, 13)) + +=== file7.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file7.ts, 0, 8)) + +export type {b as i} +>b : Symbol(b, Decl(file7.ts, 0, 8)) +>i : Symbol(i, Decl(file7.ts, 2, 13)) + +=== file8.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file8.ts, 0, 8)) + +export type {b as j} +>b : Symbol(b, Decl(file8.ts, 0, 8)) +>j : Symbol(j, Decl(file8.ts, 2, 13)) + +=== file9.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file9.ts, 0, 8)) + +export type {b as k} +>b : Symbol(b, Decl(file9.ts, 0, 8)) +>k : Symbol(k, Decl(file9.ts, 2, 13)) + +=== file10.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file10.ts, 0, 8)) + +export type {b as l} +>b : Symbol(b, Decl(file10.ts, 0, 8)) +>l : Symbol(l, Decl(file10.ts, 2, 13)) + +=== file11.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file11.ts, 0, 8)) + +export type {b as m} +>b : Symbol(b, Decl(file11.ts, 0, 8)) +>m : Symbol(m, Decl(file11.ts, 2, 13)) + +=== file12.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file12.ts, 0, 8)) + +export type {b as n} +>b : Symbol(b, Decl(file12.ts, 0, 8)) +>n : Symbol(n, Decl(file12.ts, 2, 13)) + +=== file13.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file13.ts, 0, 8)) + +export type {b as o} +>b : Symbol(b, Decl(file13.ts, 0, 8)) +>o : Symbol(o, Decl(file13.ts, 2, 13)) + +=== file14.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file14.ts, 0, 8)) + +export type {b as p} +>b : Symbol(b, Decl(file14.ts, 0, 8)) +>p : Symbol(p, Decl(file14.ts, 2, 13)) + +=== file15.ts === +import {b} from "./file2.js" +>b : Symbol(b, Decl(file15.ts, 0, 8)) + +export type {b as q} +>b : Symbol(b, Decl(file15.ts, 0, 8)) +>q : Symbol(q, Decl(file15.ts, 2, 13)) + +=== barrel.ts === + +export * from "./file0.js" +export * from "./file1.js" +export * from "./file2.js" +export * from "./file3.js" +export * from "./file4.js" +export * from "./file5.js" +export * from "./file6.js" +export * from "./file7.js" +export * from "./file8.js" +export * from "./file9.js" +export * from "./file10.js" +export * from "./file11.js" +export * from "./file12.js" +export * from "./file13.js" +export * from "./file14.js" +export * from "./file15.js" diff --git a/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.types b/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.types new file mode 100644 index 0000000000..b2b0234037 --- /dev/null +++ b/testdata/baselines/reference/compiler/incrementalConcurrentSafeAliasFollowing.types @@ -0,0 +1,152 @@ +//// [tests/cases/compiler/incrementalConcurrentSafeAliasFollowing.ts] //// + +=== file1.ts === +// need an out of date but present buildinfo, chained export aliases, +// and enough files that the same checker is used for multiple files +// to trigger the race from typescript-go/#1470 +import {b} from "./file2.js" +>b : 1 + +export type {b as c} +>b : 1 +>c : any + +=== file2.ts === +const a = 1 +>a : 1 +>1 : 1 + +export type {a as b} +>a : 1 +>b : any + +=== file0.ts === +import {c} from "./file1.js" +>c : 1 + +export type {c as d} +>c : 1 +>d : any + +=== file3.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as e} +>b : 1 +>e : any + +=== file4.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as f} +>b : 1 +>f : any + +=== file5.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as g} +>b : 1 +>g : any + +=== file6.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as h} +>b : 1 +>h : any + +=== file7.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as i} +>b : 1 +>i : any + +=== file8.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as j} +>b : 1 +>j : any + +=== file9.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as k} +>b : 1 +>k : any + +=== file10.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as l} +>b : 1 +>l : any + +=== file11.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as m} +>b : 1 +>m : any + +=== file12.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as n} +>b : 1 +>n : any + +=== file13.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as o} +>b : 1 +>o : any + +=== file14.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as p} +>b : 1 +>p : any + +=== file15.ts === +import {b} from "./file2.js" +>b : 1 + +export type {b as q} +>b : 1 +>q : any + +=== barrel.ts === + +export * from "./file0.js" +export * from "./file1.js" +export * from "./file2.js" +export * from "./file3.js" +export * from "./file4.js" +export * from "./file5.js" +export * from "./file6.js" +export * from "./file7.js" +export * from "./file8.js" +export * from "./file9.js" +export * from "./file10.js" +export * from "./file11.js" +export * from "./file12.js" +export * from "./file13.js" +export * from "./file14.js" +export * from "./file15.js" diff --git a/testdata/tests/cases/compiler/incrementalConcurrentSafeAliasFollowing.ts b/testdata/tests/cases/compiler/incrementalConcurrentSafeAliasFollowing.ts new file mode 100644 index 0000000000..4d0f8a3e22 --- /dev/null +++ b/testdata/tests/cases/compiler/incrementalConcurrentSafeAliasFollowing.ts @@ -0,0 +1,147 @@ +// @incremental: true +// @outDir: ./res +// @declaration: true +// @tsBuildInfoFile: /a.tsbuildinfo +// @filename: /a.tsbuildinfo +{ + "version": "TEST", + "root": [ [ 8, 9 ] ], + "fileNames": [ + "lib.d.ts", + "lib.es5.d.ts", + "lib.dom.d.ts", + "lib.webworker.importscripts.d.ts", + "lib.scripthost.d.ts", + "lib.decorators.d.ts", + "lib.decorators.legacy.d.ts", + "./.src/file2.ts", + "./.src/file1.ts" + ], + "fileInfos": [ + "8aa2344ef67b04dfd9aa23b0f29ffb31", + { + "version": "71cf8049ea8d435bcdf47408dac2525c", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "9cf691967d2e0b0210f5864fdf1ad87a", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "eb49c11101339d745cfc83e213607152", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "a4fa81fccf6300a830a36517b5beb51f", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "45c91c5f844a9ee1df11d1b71c484b0e", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + { + "version": "39e009135c77d60baa790854b51d2195", + "affectsGlobalScope": true, + "impliedNodeFormat": 1 + }, + "ed441e10e1833cd4635f7c61645b7ee7", + "553372ff8498789eb6e91ea68f08e47a" + ], + "fileIdsList": [ [ 8 ] ], + "options": { + "newLine": 1, + "noErrorTruncation": true, + "skipDefaultLibCheck": true, + "tsBuildInfoFile": "./a.tsbuildinfo" + }, + "referencedMap": [ [ 9, 1 ] ] +} +// @filename: file1.ts +// need an out of date but present buildinfo, chained export aliases, +// and enough files that the same checker is used for multiple files +// to trigger the race from typescript-go/#1470 +import {b} from "./file2.js" + +export type {b as c} +// @filename: file2.ts +const a = 1 + +export type {a as b} +// @filename: file0.ts +import {c} from "./file1.js" + +export type {c as d} +// @filename: file3.ts +import {b} from "./file2.js" + +export type {b as e} +// @filename: file4.ts +import {b} from "./file2.js" + +export type {b as f} +// @filename: file5.ts +import {b} from "./file2.js" + +export type {b as g} +// @filename: file6.ts +import {b} from "./file2.js" + +export type {b as h} +// @filename: file7.ts +import {b} from "./file2.js" + +export type {b as i} +// @filename: file8.ts +import {b} from "./file2.js" + +export type {b as j} +// @filename: file9.ts +import {b} from "./file2.js" + +export type {b as k} +// @filename: file10.ts +import {b} from "./file2.js" + +export type {b as l} +// @filename: file11.ts +import {b} from "./file2.js" + +export type {b as m} +// @filename: file12.ts +import {b} from "./file2.js" + +export type {b as n} +// @filename: file13.ts +import {b} from "./file2.js" + +export type {b as o} +// @filename: file14.ts +import {b} from "./file2.js" + +export type {b as p} +// @filename: file15.ts +import {b} from "./file2.js" + +export type {b as q} +// @filename: barrel.ts +export * from "./file0.js" +export * from "./file1.js" +export * from "./file2.js" +export * from "./file3.js" +export * from "./file4.js" +export * from "./file5.js" +export * from "./file6.js" +export * from "./file7.js" +export * from "./file8.js" +export * from "./file9.js" +export * from "./file10.js" +export * from "./file11.js" +export * from "./file12.js" +export * from "./file13.js" +export * from "./file14.js" +export * from "./file15.js" \ No newline at end of file