Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
41 changes: 30 additions & 11 deletions tsc/internal/compiler/checkerpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type checkerPool struct {
checkers []*checker.Checker
locks []*sync.Mutex
fileAssociations map[*ast.SourceFile]*checker.Checker
associations []int // from ProgramFactories.AssignCheckers, if set
}

var _ CheckerPool = (*checkerPool)(nil)
Expand Down Expand Up @@ -307,20 +308,35 @@ func newCheckerPool(program *Program) *checkerPool {
}

func newCheckerPoolWithTracing(program *Program, tr *tracing.Tracing) *checkerPool {
return newCheckerPoolWithAssignment(program, tr, nil)
}

// newCheckerPoolWithAssignment is newCheckerPoolWithTracing with the checker
// of each file given by assign, if set; see ProgramFactories.AssignCheckers.
func newCheckerPoolWithAssignment(program *Program, tr *tracing.Tracing, assign func([]*ast.SourceFile) []int) *checkerPool {
var associations []int
checkerCount := 4
if program.SingleThreaded() {
if assign != nil {
associations = assign(program.files)
checkerCount = 1
} else if c := program.Options().Checkers; c != nil {
checkerCount = *c
if len(associations) > 0 {
checkerCount = slices.Max(associations) + 1
}
} else {
if program.SingleThreaded() {
checkerCount = 1
} else if c := program.Options().Checkers; c != nil {
checkerCount = *c
}
checkerCount = max(min(checkerCount, len(program.files), 256), 1)
}

checkerCount = max(min(checkerCount, len(program.files), 256), 1)

pool := &checkerPool{
program: program,
checkers: make([]*checker.Checker, checkerCount),
locks: make([]*sync.Mutex, checkerCount),
tracing: tr,
program: program,
checkers: make([]*checker.Checker, checkerCount),
locks: make([]*sync.Mutex, checkerCount),
tracing: tr,
associations: associations,
}

return pool
Expand Down Expand Up @@ -380,8 +396,11 @@ func (p *checkerPool) createCheckers() {

wg.RunAndWait()

associations := make([]int, len(p.program.files))
if checkerCount > 1 {
associations := p.associations
if associations == nil {
associations = make([]int, len(p.program.files))
}
if checkerCount > 1 && p.associations == nil {
baseWeights := make([]int, len(p.program.files))
importCounts := make([]int, len(p.program.files))
isDeclarationFile := make([]bool, len(p.program.files))
Expand Down
11 changes: 7 additions & 4 deletions tsc/internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type ProgramHosts struct {
type ProgramFactories struct {
CreateCheckerPool func(*Program) CheckerPool
CreateModuleResolver func(module.ResolverOptions) module.Resolver
// AssignCheckers, for testing, returns the index of the checker for each
// file of the program, in place of the usual checker count and assignment.
AssignCheckers func(files []*ast.SourceFile) []int
}

func (p *ProgramConfig) canUseProjectReferenceSource() bool {
Expand Down Expand Up @@ -324,7 +327,7 @@ func NewProgram(opts ProgramOptions) *Program {
defer opts.Tracing.Push(tracing.PhaseProgram, "createProgram", map[string]any{"configFilePath": opts.Config.CompilerOptions().ConfigFilePath}, true)()
}
p.processedFiles, p.resolutionData, p.moduleResolutionError = processAllProgramFiles(opts, p.SingleThreaded())
p.initCheckerPool(opts.CreateCheckerPool)
p.initCheckerPool(opts.CreateCheckerPool, opts.AssignCheckers)
p.verifyCompilerOptions()
p.collectContentMapperOptionDiagnostics()
return p
Expand Down Expand Up @@ -445,19 +448,19 @@ func (p *Program) ReuseProgram(
}
}
result.replaceSyntheticImports(newFile, newSupplementalFiles)
result.initCheckerPool(createCheckerPool)
result.initCheckerPool(createCheckerPool, nil)
return result, newFile, true
}

func (p *Program) initCheckerPool(create func(*Program) CheckerPool) {
func (p *Program) initCheckerPool(create func(*Program) CheckerPool, assign func([]*ast.SourceFile) []int) {
if !p.finishedProcessing {
panic("Program must finish processing files before initializing checker pool")
}

if create != nil {
p.checkerPool = create(p)
} else {
pool := newCheckerPoolWithTracing(p, p.hosts.Tracing)
pool := newCheckerPoolWithAssignment(p, p.hosts.Tracing, assign)
p.checkerPool = pool
p.compilerCheckerPool = pool
}
Expand Down
24 changes: 23 additions & 1 deletion tsc/internal/testrunner/compiler_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/microsoft/TypeScript/tsc/internal/ast"
"github.com/microsoft/TypeScript/tsc/internal/checker"
"github.com/microsoft/TypeScript/tsc/internal/collections"
"github.com/microsoft/TypeScript/tsc/internal/core"
"github.com/microsoft/TypeScript/tsc/internal/repo"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
Expand Down Expand Up @@ -166,8 +167,21 @@ func (r *CompilerBaselineRunner) runTest(t *testing.T, filename string) {
}
}

// When a program checks its files on several checkers, these tests can see different results depending on which
// checker answers a query first.
var concurrentSkippedTests = map[string]string{
"checkOrderCircularDefaults.ts": "Circular default results depend on which checker resolves the cycle first.",
"checkOrderSpreadCycle.ts": "Spread cycle results depend on which checker resolves the cycle first.",
"checkOrderStableInstantiationErrors.ts": "Instantiation limit errors depend on which checker instantiates first.",
}

func (r *CompilerBaselineRunner) runSingleConfigTest(t *testing.T, testName string, test *compilerFileBasedTest, config *harnessutil.NamedTestConfiguration) {
t.Parallel()
if !testutil.TestProgramIsSingleThreaded() {
if msg, ok := concurrentSkippedTests[tspath.GetBaseFileName(test.filename)]; ok {
t.Skipf("Skipping in concurrent mode: %s", msg)
}
}
defer testutil.RecoverAndFail(t, "Panic on compiler test "+test.filename)

payload := makeUnitsFromTest(test.content, test.filename)
Expand All @@ -186,6 +200,14 @@ func (r *CompilerBaselineRunner) runSingleConfigTest(t *testing.T, testName stri
compilerTest.verifyParentPointers(t)
}

// compilerTestVaryBy adds harness options that may vary to the compiler options
// that may vary.
var compilerTestVaryBy = func() *collections.Set[string] {
s := compilerVaryBy.Clone()
s.Add("checkorder")
return s
}()

type compilerFileBasedTest struct {
filename string
content string
Expand All @@ -198,7 +220,7 @@ func getCompilerFileBasedTest(t *testing.T, filename string) *compilerFileBasedT
panic("Could not read test file: " + filename)
}
settings := extractCompilerSettings(content)
configurations := harnessutil.GetFileBasedTestConfigurations(t, settings, compilerVaryBy)
configurations := harnessutil.GetFileBasedTestConfigurations(t, settings, compilerTestVaryBy)
return &compilerFileBasedTest{
filename: filename,
content: content,
Expand Down
2 changes: 2 additions & 0 deletions tsc/internal/testutil/baseline/baseline.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ func writeComparison(t *testing.T, actualContent string, local, reference string
if actualContent == NoContent {
if err := os.WriteFile(local+".delete", []byte{}, 0o644); err != nil {
t.Error(fmt.Errorf("failed to write the local baseline file %s: %w", local+".delete", err))
return
}
t.Errorf("the baseline file %s should be deleted. (Run `hereby baseline-accept` if the deletion is correct.)", reference)
return
}
if err := os.WriteFile(local, []byte(actualContent), 0o644); err != nil {
Expand Down
Loading
Loading