@@ -371,15 +371,23 @@ func (p *Program) BindSourceFiles() {
371371 wg .RunAndWait ()
372372}
373373
374+ func (p * Program ) CheckSourceFiles (ctx context.Context , files []* ast.SourceFile ) {
375+ p .checkerPool .ForEachCheckerParallel (ctx , func (_ int , checker * checker.Checker ) {
376+ for file := range p .checkerPool .Files (checker ) {
377+ if files == nil || slices .Contains (files , file ) {
378+ checker .CheckSourceFile (ctx , file )
379+ }
380+ }
381+ })
382+ }
383+
374384// Return the type checker associated with the program.
375385func (p * Program ) GetTypeChecker (ctx context.Context ) (* checker.Checker , func ()) {
376386 return p .checkerPool .GetChecker (ctx )
377387}
378388
379- func (p * Program ) ForEachCheckerParallel (cb func (idx int , c * checker.Checker )) {
380- if pool , ok := p .checkerPool .(* checkerPool ); ok {
381- pool .ForEachCheckerParallel (cb )
382- }
389+ func (p * Program ) ForEachCheckerParallel (ctx context.Context , cb func (idx int , c * checker.Checker )) {
390+ p .checkerPool .ForEachCheckerParallel (ctx , cb )
383391}
384392
385393// Return a checker for the given file. We may have multiple checkers in concurrent scenarios and this
@@ -417,59 +425,39 @@ func (p *Program) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*mo
417425 return p .resolvedModules
418426}
419427
420- // collectDiagnostics collects diagnostics from a single file or all files.
421- // If sourceFile is non-nil, returns diagnostics for just that file.
422- // If sourceFile is nil, returns diagnostics for all files in the program.
423- func (p * Program ) collectDiagnostics (ctx context.Context , sourceFile * ast.SourceFile , collect func (context.Context , * ast.SourceFile ) []* ast.Diagnostic ) []* ast.Diagnostic {
424- var result []* ast.Diagnostic
425- if sourceFile != nil {
426- result = collect (ctx , sourceFile )
427- } else {
428- for _ , file := range p .files {
429- result = append (result , collect (ctx , file )... )
430- }
431- }
432- return SortAndDeduplicateDiagnostics (result )
433- }
434-
435428func (p * Program ) GetSyntacticDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
436- return p .collectDiagnostics (ctx , sourceFile , func (_ context.Context , file * ast.SourceFile ) []* ast.Diagnostic {
437- return core .Concatenate (file .Diagnostics (), file .JSDiagnostics ())
438- })
429+ return p .getDiagnosticsHelper (ctx , sourceFile , false /*ensureBound*/ , false /*ensureChecked*/ , p .getSyntacticDiagnosticsForFile )
439430}
440431
441432func (p * Program ) GetBindDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
442- if sourceFile != nil {
443- binder .BindSourceFile (sourceFile )
444- } else {
445- p .BindSourceFiles ()
446- }
447- return p .collectDiagnostics (ctx , sourceFile , func (_ context.Context , file * ast.SourceFile ) []* ast.Diagnostic {
448- return file .BindDiagnostics ()
449- })
433+ return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , false /*ensureChecked*/ , p .getBindDiagnosticsForFile )
450434}
451435
452436func (p * Program ) GetSemanticDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
453- return p .collectDiagnostics (ctx , sourceFile , p .getSemanticDiagnosticsForFile )
437+ return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , true /*ensureChecked*/ , p .getSemanticDiagnosticsForFile )
454438}
455439
456- func (p * Program ) GetSemanticDiagnosticsWithoutNoEmitFiltering (ctx context.Context , sourceFiles []* ast.SourceFile ) map [* ast.SourceFile ][]* ast.Diagnostic {
440+ func (p * Program ) GetSemanticDiagnosticsNoFilter (ctx context.Context , sourceFiles []* ast.SourceFile ) map [* ast.SourceFile ][]* ast.Diagnostic {
441+ p .BindSourceFiles ()
442+ p .CheckSourceFiles (ctx , sourceFiles )
443+ if ctx .Err () != nil {
444+ return nil
445+ }
457446 result := make (map [* ast.SourceFile ][]* ast.Diagnostic , len (sourceFiles ))
458447 for _ , file := range sourceFiles {
459- result [file ] = SortAndDeduplicateDiagnostics (p .getBindAndCheckDiagnosticsForFile (ctx , file ))
448+ result [file ] = SortAndDeduplicateDiagnostics (p .getSemanticDiagnosticsForFileNotFilter (ctx , file ))
460449 }
461450 return result
462451}
463452
464453func (p * Program ) GetSuggestionDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
465- return p .collectDiagnostics (ctx , sourceFile , p .getSuggestionDiagnosticsForFile )
454+ return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , true /*ensureChecked*/ , p .getSuggestionDiagnosticsForFile )
466455}
467456
468457func (p * Program ) GetProgramDiagnostics () []* ast.Diagnostic {
469- return SortAndDeduplicateDiagnostics (core . Concatenate (
458+ return SortAndDeduplicateDiagnostics (slices . Concat (
470459 p .programDiagnostics ,
471- p .includeProcessor .getDiagnostics (p ).GetGlobalDiagnostics (),
472- ))
460+ p .includeProcessor .getDiagnostics (p ).GetGlobalDiagnostics ()))
473461}
474462
475463func (p * Program ) GetIncludeProcessorDiagnostics (sourceFile * ast.SourceFile ) []* ast.Diagnostic {
@@ -998,26 +986,40 @@ func (p *Program) GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic {
998986 }
999987
1000988 globalDiagnostics := make ([][]* ast.Diagnostic , p .checkerPool .Count ())
1001- p .ForEachCheckerParallel (func (idx int , checker * checker.Checker ) {
989+ p .checkerPool . ForEachCheckerParallel (ctx , func (idx int , checker * checker.Checker ) {
1002990 globalDiagnostics [idx ] = checker .GetGlobalDiagnostics ()
1003991 })
1004992
1005993 return SortAndDeduplicateDiagnostics (slices .Concat (globalDiagnostics ... ))
1006994}
1007995
1008996func (p * Program ) GetDeclarationDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1009- return p .collectDiagnostics (ctx , sourceFile , p .getDeclarationDiagnosticsForFile )
997+ return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , true /*ensureChecked*/ , p .getDeclarationDiagnosticsForFile )
1010998}
1011999
10121000func (p * Program ) GetOptionsDiagnostics (ctx context.Context ) []* ast.Diagnostic {
1013- return SortAndDeduplicateDiagnostics (core . Concatenate (p .GetGlobalDiagnostics (ctx ), p .getOptionsDiagnosticsOfConfigFile ()))
1001+ return SortAndDeduplicateDiagnostics (append (p .GetGlobalDiagnostics (ctx ), p .getOptionsDiagnosticsOfConfigFile ()... ))
10141002}
10151003
10161004func (p * Program ) getOptionsDiagnosticsOfConfigFile () []* ast.Diagnostic {
1005+ // todo update p.configParsingDiagnostics when updateAndGetProgramDiagnostics is implemented
10171006 if p .Options () == nil || p .Options ().ConfigFilePath == "" {
10181007 return nil
10191008 }
1020- return p .GetConfigFileParsingDiagnostics ()
1009+ return p .GetConfigFileParsingDiagnostics () // TODO: actually call getDiagnosticsHelper on config path
1010+ }
1011+
1012+ func (p * Program ) getSyntacticDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1013+ return core .Concatenate (sourceFile .Diagnostics (), sourceFile .JSDiagnostics ())
1014+ }
1015+
1016+ func (p * Program ) getBindDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1017+ // TODO: restore this; tsgo's main depends on this function binding all files for timing.
1018+ // if checker.SkipTypeChecking(sourceFile, p.compilerOptions) {
1019+ // return nil
1020+ // }
1021+
1022+ return sourceFile .BindDiagnostics ()
10211023}
10221024
10231025func FilterNoEmitSemanticDiagnostics (diagnostics []* ast.Diagnostic , options * core.CompilerOptions ) []* ast.Diagnostic {
@@ -1030,26 +1032,40 @@ func FilterNoEmitSemanticDiagnostics(diagnostics []*ast.Diagnostic, options *cor
10301032}
10311033
10321034func (p * Program ) getSemanticDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1033- return core . Concatenate (
1034- FilterNoEmitSemanticDiagnostics (p .getBindAndCheckDiagnosticsForFile (ctx , sourceFile ), p .Options ()),
1035+ return slices . Concat (
1036+ FilterNoEmitSemanticDiagnostics (p .getSemanticDiagnosticsForFileNotFilter (ctx , sourceFile ), p .Options ()),
10351037 p .GetIncludeProcessorDiagnostics (sourceFile ),
10361038 )
10371039}
10381040
1039- // getBindAndCheckDiagnosticsForFile gets semantic diagnostics for a single file,
1040- // including bind diagnostics, checker diagnostics, and handling of @ts-ignore/@ts-expect-error directives.
1041- func (p * Program ) getBindAndCheckDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1041+ func (p * Program ) getSemanticDiagnosticsForFileNotFilter (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
10421042 compilerOptions := p .Options ()
10431043 if checker .SkipTypeChecking (sourceFile , compilerOptions , p , false ) {
10441044 return nil
10451045 }
10461046
1047- fileChecker , done := p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1048- defer done ()
1049-
1050- // Getting a checker will force a bind, so this will be populated.
1047+ var fileChecker * checker.Checker
1048+ var done func ()
1049+ if sourceFile != nil {
1050+ fileChecker , done = p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1051+ defer done ()
1052+ }
10511053 diags := slices .Clip (sourceFile .BindDiagnostics ())
1052- diags = append (diags , fileChecker .GetDiagnostics (ctx , sourceFile )... )
1054+ // Ask for diags from all checkers; checking one file may add diagnostics to other files.
1055+ // These are deduplicated later.
1056+ checkerDiags := make ([][]* ast.Diagnostic , p .checkerPool .Count ())
1057+ p .checkerPool .ForEachCheckerParallel (ctx , func (idx int , checker * checker.Checker ) {
1058+ if sourceFile == nil || checker == fileChecker {
1059+ checkerDiags [idx ] = checker .GetDiagnostics (ctx , sourceFile )
1060+ }
1061+ })
1062+ if ctx .Err () != nil {
1063+ return nil
1064+ }
1065+
1066+ diags = append (diags , slices .Concat (checkerDiags ... )... )
1067+
1068+ // !!! This should be rewritten to work like getBindAndCheckDiagnosticsForFileNoCache.
10531069
10541070 isPlainJS := ast .IsPlainJSFile (sourceFile , compilerOptions .CheckJs )
10551071 if isPlainJS {
@@ -1131,12 +1147,28 @@ func (p *Program) getSuggestionDiagnosticsForFile(ctx context.Context, sourceFil
11311147 return nil
11321148 }
11331149
1134- fileChecker , done := p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1135- defer done ()
1150+ var fileChecker * checker.Checker
1151+ var done func ()
1152+ if sourceFile != nil {
1153+ fileChecker , done = p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1154+ defer done ()
1155+ }
11361156
1137- // Getting a checker will force a bind, so this will be populated.
11381157 diags := slices .Clip (sourceFile .BindSuggestionDiagnostics )
1139- diags = append (diags , fileChecker .GetSuggestionDiagnostics (ctx , sourceFile )... )
1158+
1159+ checkerDiags := make ([][]* ast.Diagnostic , p .checkerPool .Count ())
1160+ p .checkerPool .ForEachCheckerParallel (ctx , func (idx int , checker * checker.Checker ) {
1161+ if sourceFile == nil || checker == fileChecker {
1162+ checkerDiags [idx ] = checker .GetSuggestionDiagnostics (ctx , sourceFile )
1163+ } else {
1164+ // !!! is there any case where suggestion diagnostics are produced in other checkers?
1165+ }
1166+ })
1167+ if ctx .Err () != nil {
1168+ return nil
1169+ }
1170+
1171+ diags = append (diags , slices .Concat (checkerDiags ... )... )
11401172
11411173 return diags
11421174}
@@ -1189,6 +1221,29 @@ func compactAndMergeRelatedInfos(diagnostics []*ast.Diagnostic) []*ast.Diagnosti
11891221 return diagnostics [:j ]
11901222}
11911223
1224+ func (p * Program ) getDiagnosticsHelper (ctx context.Context , sourceFile * ast.SourceFile , ensureBound bool , ensureChecked bool , getDiagnostics func (context.Context , * ast.SourceFile ) []* ast.Diagnostic ) []* ast.Diagnostic {
1225+ if sourceFile != nil {
1226+ if ensureBound {
1227+ binder .BindSourceFile (sourceFile )
1228+ }
1229+ return SortAndDeduplicateDiagnostics (getDiagnostics (ctx , sourceFile ))
1230+ }
1231+ if ensureBound {
1232+ p .BindSourceFiles ()
1233+ }
1234+ if ensureChecked {
1235+ p .CheckSourceFiles (ctx , nil )
1236+ if ctx .Err () != nil {
1237+ return nil
1238+ }
1239+ }
1240+ var result []* ast.Diagnostic
1241+ for _ , file := range p .files {
1242+ result = append (result , getDiagnostics (ctx , file )... )
1243+ }
1244+ return SortAndDeduplicateDiagnostics (result )
1245+ }
1246+
11921247func (p * Program ) LineCount () int {
11931248 var count int
11941249 for _ , file := range p .files {
@@ -1212,23 +1267,23 @@ func (p *Program) SymbolCount() int {
12121267 }
12131268 var val atomic.Uint32
12141269 val .Store (uint32 (count ))
1215- p .ForEachCheckerParallel (func (_ int , c * checker.Checker ) {
1270+ p .checkerPool . ForEachCheckerParallel (context . Background (), func (idx int , c * checker.Checker ) {
12161271 val .Add (c .SymbolCount )
12171272 })
12181273 return int (val .Load ())
12191274}
12201275
12211276func (p * Program ) TypeCount () int {
12221277 var val atomic.Uint32
1223- p .ForEachCheckerParallel (func (_ int , c * checker.Checker ) {
1278+ p .checkerPool . ForEachCheckerParallel (context . Background (), func (idx int , c * checker.Checker ) {
12241279 val .Add (c .TypeCount )
12251280 })
12261281 return int (val .Load ())
12271282}
12281283
12291284func (p * Program ) InstantiationCount () int {
12301285 var val atomic.Uint32
1231- p .ForEachCheckerParallel (func (_ int , c * checker.Checker ) {
1286+ p .checkerPool . ForEachCheckerParallel (context . Background (), func (idx int , c * checker.Checker ) {
12321287 val .Add (c .TotalInstantiationCount )
12331288 })
12341289 return int (val .Load ())
@@ -1323,6 +1378,8 @@ type SourceMapEmitResult struct {
13231378}
13241379
13251380func (p * Program ) Emit (ctx context.Context , options EmitOptions ) * EmitResult {
1381+ // !!! performance measurement
1382+ p .BindSourceFiles ()
13261383 if options .EmitOnly != EmitOnlyForcedDts {
13271384 result := HandleNoEmitOnError (
13281385 ctx ,
0 commit comments