@@ -363,23 +363,15 @@ func (p *Program) BindSourceFiles() {
363363 wg .RunAndWait ()
364364}
365365
366- func (p * Program ) CheckSourceFiles (ctx context.Context , files []* ast.SourceFile ) {
367- p .checkerPool .ForEachCheckerParallel (ctx , func (_ int , checker * checker.Checker ) {
368- for file := range p .checkerPool .Files (checker ) {
369- if files == nil || slices .Contains (files , file ) {
370- checker .CheckSourceFile (ctx , file )
371- }
372- }
373- })
374- }
375-
376366// Return the type checker associated with the program.
377367func (p * Program ) GetTypeChecker (ctx context.Context ) (* checker.Checker , func ()) {
378368 return p .checkerPool .GetChecker (ctx )
379369}
380370
381- func (p * Program ) ForEachCheckerParallel (ctx context.Context , cb func (idx int , c * checker.Checker )) {
382- p .checkerPool .ForEachCheckerParallel (ctx , cb )
371+ func (p * Program ) ForEachCheckerParallel (cb func (idx int , c * checker.Checker )) {
372+ if pool , ok := p .checkerPool .(* checkerPool ); ok {
373+ pool .ForEachCheckerParallel (cb )
374+ }
383375}
384376
385377// Return a checker for the given file. We may have multiple checkers in concurrent scenarios and this
@@ -417,39 +409,59 @@ func (p *Program) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*mo
417409 return p .resolvedModules
418410}
419411
412+ // collectDiagnostics collects diagnostics from a single file or all files.
413+ // If sourceFile is non-nil, returns diagnostics for just that file.
414+ // If sourceFile is nil, returns diagnostics for all files in the program.
415+ func (p * Program ) collectDiagnostics (ctx context.Context , sourceFile * ast.SourceFile , collect func (context.Context , * ast.SourceFile ) []* ast.Diagnostic ) []* ast.Diagnostic {
416+ var result []* ast.Diagnostic
417+ if sourceFile != nil {
418+ result = collect (ctx , sourceFile )
419+ } else {
420+ for _ , file := range p .files {
421+ result = append (result , collect (ctx , file )... )
422+ }
423+ }
424+ return SortAndDeduplicateDiagnostics (result )
425+ }
426+
420427func (p * Program ) GetSyntacticDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
421- return p .getDiagnosticsHelper (ctx , sourceFile , false /*ensureBound*/ , false /*ensureChecked*/ , p .getSyntacticDiagnosticsForFile )
428+ return p .collectDiagnostics (ctx , sourceFile , func (_ context.Context , file * ast.SourceFile ) []* ast.Diagnostic {
429+ return core .Concatenate (file .Diagnostics (), file .JSDiagnostics ())
430+ })
422431}
423432
424433func (p * Program ) GetBindDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
425- return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , false /*ensureChecked*/ , p .getBindDiagnosticsForFile )
434+ if sourceFile != nil {
435+ binder .BindSourceFile (sourceFile )
436+ } else {
437+ p .BindSourceFiles ()
438+ }
439+ return p .collectDiagnostics (ctx , sourceFile , func (_ context.Context , file * ast.SourceFile ) []* ast.Diagnostic {
440+ return file .BindDiagnostics ()
441+ })
426442}
427443
428444func (p * Program ) GetSemanticDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
429- return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , true /*ensureChecked*/ , p .getSemanticDiagnosticsForFile )
445+ return p .collectDiagnostics (ctx , sourceFile , p .getSemanticDiagnosticsForFile )
430446}
431447
432- func (p * Program ) GetSemanticDiagnosticsNoFilter (ctx context.Context , sourceFiles []* ast.SourceFile ) map [* ast.SourceFile ][]* ast.Diagnostic {
433- p .BindSourceFiles ()
434- p .CheckSourceFiles (ctx , sourceFiles )
435- if ctx .Err () != nil {
436- return nil
437- }
448+ func (p * Program ) GetSemanticDiagnosticsWithoutNoEmitFiltering (ctx context.Context , sourceFiles []* ast.SourceFile ) map [* ast.SourceFile ][]* ast.Diagnostic {
438449 result := make (map [* ast.SourceFile ][]* ast.Diagnostic , len (sourceFiles ))
439450 for _ , file := range sourceFiles {
440- result [file ] = SortAndDeduplicateDiagnostics (p .getSemanticDiagnosticsForFileNotFilter (ctx , file ))
451+ result [file ] = SortAndDeduplicateDiagnostics (p .getBindAndCheckDiagnosticsForFile (ctx , file ))
441452 }
442453 return result
443454}
444455
445456func (p * Program ) GetSuggestionDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
446- return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , true /*ensureChecked*/ , p .getSuggestionDiagnosticsForFile )
457+ return p .collectDiagnostics (ctx , sourceFile , p .getSuggestionDiagnosticsForFile )
447458}
448459
449460func (p * Program ) GetProgramDiagnostics () []* ast.Diagnostic {
450- return SortAndDeduplicateDiagnostics (slices . Concat (
461+ return SortAndDeduplicateDiagnostics (core . Concatenate (
451462 p .programDiagnostics ,
452- p .includeProcessor .getDiagnostics (p ).GetGlobalDiagnostics ()))
463+ p .includeProcessor .getDiagnostics (p ).GetGlobalDiagnostics (),
464+ ))
453465}
454466
455467func (p * Program ) GetIncludeProcessorDiagnostics (sourceFile * ast.SourceFile ) []* ast.Diagnostic {
@@ -978,40 +990,26 @@ func (p *Program) GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic {
978990 }
979991
980992 globalDiagnostics := make ([][]* ast.Diagnostic , p .checkerPool .Count ())
981- p .checkerPool . ForEachCheckerParallel (ctx , func (idx int , checker * checker.Checker ) {
993+ p .ForEachCheckerParallel (func (idx int , checker * checker.Checker ) {
982994 globalDiagnostics [idx ] = checker .GetGlobalDiagnostics ()
983995 })
984996
985997 return SortAndDeduplicateDiagnostics (slices .Concat (globalDiagnostics ... ))
986998}
987999
9881000func (p * Program ) GetDeclarationDiagnostics (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
989- return p .getDiagnosticsHelper (ctx , sourceFile , true /*ensureBound*/ , true /*ensureChecked*/ , p .getDeclarationDiagnosticsForFile )
1001+ return p .collectDiagnostics (ctx , sourceFile , p .getDeclarationDiagnosticsForFile )
9901002}
9911003
9921004func (p * Program ) GetOptionsDiagnostics (ctx context.Context ) []* ast.Diagnostic {
993- return SortAndDeduplicateDiagnostics (append (p .GetGlobalDiagnostics (ctx ), p .getOptionsDiagnosticsOfConfigFile ()... ))
1005+ return SortAndDeduplicateDiagnostics (core . Concatenate (p .GetGlobalDiagnostics (ctx ), p .getOptionsDiagnosticsOfConfigFile ()))
9941006}
9951007
9961008func (p * Program ) getOptionsDiagnosticsOfConfigFile () []* ast.Diagnostic {
997- // todo update p.configParsingDiagnostics when updateAndGetProgramDiagnostics is implemented
9981009 if p .Options () == nil || p .Options ().ConfigFilePath == "" {
9991010 return nil
10001011 }
1001- return p .GetConfigFileParsingDiagnostics () // TODO: actually call getDiagnosticsHelper on config path
1002- }
1003-
1004- func (p * Program ) getSyntacticDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1005- return core .Concatenate (sourceFile .Diagnostics (), sourceFile .JSDiagnostics ())
1006- }
1007-
1008- func (p * Program ) getBindDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1009- // TODO: restore this; tsgo's main depends on this function binding all files for timing.
1010- // if checker.SkipTypeChecking(sourceFile, p.compilerOptions) {
1011- // return nil
1012- // }
1013-
1014- return sourceFile .BindDiagnostics ()
1012+ return p .GetConfigFileParsingDiagnostics ()
10151013}
10161014
10171015func FilterNoEmitSemanticDiagnostics (diagnostics []* ast.Diagnostic , options * core.CompilerOptions ) []* ast.Diagnostic {
@@ -1024,40 +1022,26 @@ func FilterNoEmitSemanticDiagnostics(diagnostics []*ast.Diagnostic, options *cor
10241022}
10251023
10261024func (p * Program ) getSemanticDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1027- return slices . Concat (
1028- FilterNoEmitSemanticDiagnostics (p .getSemanticDiagnosticsForFileNotFilter (ctx , sourceFile ), p .Options ()),
1025+ return core . Concatenate (
1026+ FilterNoEmitSemanticDiagnostics (p .getBindAndCheckDiagnosticsForFile (ctx , sourceFile ), p .Options ()),
10291027 p .GetIncludeProcessorDiagnostics (sourceFile ),
10301028 )
10311029}
10321030
1033- func (p * Program ) getSemanticDiagnosticsForFileNotFilter (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1031+ // getBindAndCheckDiagnosticsForFile gets semantic diagnostics for a single file,
1032+ // including bind diagnostics, checker diagnostics, and handling of @ts-ignore/@ts-expect-error directives.
1033+ func (p * Program ) getBindAndCheckDiagnosticsForFile (ctx context.Context , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
10341034 compilerOptions := p .Options ()
10351035 if checker .SkipTypeChecking (sourceFile , compilerOptions , p , false ) {
10361036 return nil
10371037 }
10381038
1039- var fileChecker * checker.Checker
1040- var done func ()
1041- if sourceFile != nil {
1042- fileChecker , done = p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1043- defer done ()
1044- }
1045- diags := slices .Clip (sourceFile .BindDiagnostics ())
1046- // Ask for diags from all checkers; checking one file may add diagnostics to other files.
1047- // These are deduplicated later.
1048- checkerDiags := make ([][]* ast.Diagnostic , p .checkerPool .Count ())
1049- p .checkerPool .ForEachCheckerParallel (ctx , func (idx int , checker * checker.Checker ) {
1050- if sourceFile == nil || checker == fileChecker {
1051- checkerDiags [idx ] = checker .GetDiagnostics (ctx , sourceFile )
1052- }
1053- })
1054- if ctx .Err () != nil {
1055- return nil
1056- }
1057-
1058- diags = append (diags , slices .Concat (checkerDiags ... )... )
1039+ fileChecker , done := p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1040+ defer done ()
10591041
1060- // !!! This should be rewritten to work like getBindAndCheckDiagnosticsForFileNoCache.
1042+ // Getting a checker will force a bind, so this will be populated.
1043+ diags := slices .Clip (sourceFile .BindDiagnostics ())
1044+ diags = append (diags , fileChecker .GetDiagnostics (ctx , sourceFile )... )
10611045
10621046 isPlainJS := ast .IsPlainJSFile (sourceFile , compilerOptions .CheckJs )
10631047 if isPlainJS {
@@ -1139,28 +1123,12 @@ func (p *Program) getSuggestionDiagnosticsForFile(ctx context.Context, sourceFil
11391123 return nil
11401124 }
11411125
1142- var fileChecker * checker.Checker
1143- var done func ()
1144- if sourceFile != nil {
1145- fileChecker , done = p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1146- defer done ()
1147- }
1126+ fileChecker , done := p .checkerPool .GetCheckerForFile (ctx , sourceFile )
1127+ defer done ()
11481128
1129+ // Getting a checker will force a bind, so this will be populated.
11491130 diags := slices .Clip (sourceFile .BindSuggestionDiagnostics )
1150-
1151- checkerDiags := make ([][]* ast.Diagnostic , p .checkerPool .Count ())
1152- p .checkerPool .ForEachCheckerParallel (ctx , func (idx int , checker * checker.Checker ) {
1153- if sourceFile == nil || checker == fileChecker {
1154- checkerDiags [idx ] = checker .GetSuggestionDiagnostics (ctx , sourceFile )
1155- } else {
1156- // !!! is there any case where suggestion diagnostics are produced in other checkers?
1157- }
1158- })
1159- if ctx .Err () != nil {
1160- return nil
1161- }
1162-
1163- diags = append (diags , slices .Concat (checkerDiags ... )... )
1131+ diags = append (diags , fileChecker .GetSuggestionDiagnostics (ctx , sourceFile )... )
11641132
11651133 return diags
11661134}
@@ -1213,29 +1181,6 @@ func compactAndMergeRelatedInfos(diagnostics []*ast.Diagnostic) []*ast.Diagnosti
12131181 return diagnostics [:j ]
12141182}
12151183
1216- func (p * Program ) getDiagnosticsHelper (ctx context.Context , sourceFile * ast.SourceFile , ensureBound bool , ensureChecked bool , getDiagnostics func (context.Context , * ast.SourceFile ) []* ast.Diagnostic ) []* ast.Diagnostic {
1217- if sourceFile != nil {
1218- if ensureBound {
1219- binder .BindSourceFile (sourceFile )
1220- }
1221- return SortAndDeduplicateDiagnostics (getDiagnostics (ctx , sourceFile ))
1222- }
1223- if ensureBound {
1224- p .BindSourceFiles ()
1225- }
1226- if ensureChecked {
1227- p .CheckSourceFiles (ctx , nil )
1228- if ctx .Err () != nil {
1229- return nil
1230- }
1231- }
1232- var result []* ast.Diagnostic
1233- for _ , file := range p .files {
1234- result = append (result , getDiagnostics (ctx , file )... )
1235- }
1236- return SortAndDeduplicateDiagnostics (result )
1237- }
1238-
12391184func (p * Program ) LineCount () int {
12401185 var count int
12411186 for _ , file := range p .files {
@@ -1259,23 +1204,23 @@ func (p *Program) SymbolCount() int {
12591204 }
12601205 var val atomic.Uint32
12611206 val .Store (uint32 (count ))
1262- p .checkerPool . ForEachCheckerParallel (context . Background (), func (idx int , c * checker.Checker ) {
1207+ p .ForEachCheckerParallel (func (_ int , c * checker.Checker ) {
12631208 val .Add (c .SymbolCount )
12641209 })
12651210 return int (val .Load ())
12661211}
12671212
12681213func (p * Program ) TypeCount () int {
12691214 var val atomic.Uint32
1270- p .checkerPool . ForEachCheckerParallel (context . Background (), func (idx int , c * checker.Checker ) {
1215+ p .ForEachCheckerParallel (func (_ int , c * checker.Checker ) {
12711216 val .Add (c .TypeCount )
12721217 })
12731218 return int (val .Load ())
12741219}
12751220
12761221func (p * Program ) InstantiationCount () int {
12771222 var val atomic.Uint32
1278- p .checkerPool . ForEachCheckerParallel (context . Background (), func (idx int , c * checker.Checker ) {
1223+ p .ForEachCheckerParallel (func (_ int , c * checker.Checker ) {
12791224 val .Add (c .TotalInstantiationCount )
12801225 })
12811226 return int (val .Load ())
@@ -1370,8 +1315,6 @@ type SourceMapEmitResult struct {
13701315}
13711316
13721317func (p * Program ) Emit (ctx context.Context , options EmitOptions ) * EmitResult {
1373- // !!! performance measurement
1374- p .BindSourceFiles ()
13751318 if options .EmitOnly != EmitOnlyForcedDts {
13761319 result := HandleNoEmitOnError (
13771320 ctx ,
0 commit comments