@@ -799,8 +799,12 @@ func (p *Program) GetSemanticDiagnostics(ctx context.Context, sourceFile *ast.So
799799 return p .collectCheckerDiagnostics (ctx , sourceFile , p .getSemanticDiagnosticsWithChecker )
800800}
801801
802- func (p * Program ) GetSemanticDiagnosticsWithoutNoEmitFiltering (ctx context.Context , sourceFiles []* ast.SourceFile ) map [* ast.SourceFile ][]* ast.Diagnostic {
803- allDiags := p .collectCheckerDiagnosticsFromFiles (ctx , sourceFiles , p .getBindAndCheckDiagnosticsWithChecker )
802+ // GetSemanticDiagnosticsForIncremental includes newly discovered globals in each
803+ // file's cached diagnostics and leaves noEmit filtering to the builder.
804+ func (p * Program ) GetSemanticDiagnosticsForIncremental (ctx context.Context , sourceFiles []* ast.SourceFile ) map [* ast.SourceFile ][]* ast.Diagnostic {
805+ allDiags := p .collectCheckerDiagnosticsFromFiles (ctx , sourceFiles , func (ctx context.Context , c * checker.Checker , file * ast.SourceFile ) []* ast.Diagnostic {
806+ return p .getBindAndCheckDiagnosticsWithChecker (ctx , c , file , true /*includeDeferredGlobals*/ )
807+ })
804808 result := make (map [* ast.SourceFile ][]* ast.Diagnostic , len (sourceFiles ))
805809 for i , diags := range allDiags {
806810 result [sourceFiles [i ]] = filterAndSortDiagnostics (diags )
@@ -1475,24 +1479,42 @@ func FilterNoEmitSemanticDiagnostics(diagnostics []*ast.Diagnostic, options *cor
14751479
14761480func (p * Program ) getSemanticDiagnosticsWithChecker (ctx context.Context , c * checker.Checker , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
14771481 return core .Concatenate (
1478- FilterNoEmitSemanticDiagnostics (p .getBindAndCheckDiagnosticsWithChecker (ctx , c , sourceFile ), p .Options ()),
1482+ FilterNoEmitSemanticDiagnostics (p .getBindAndCheckDiagnosticsWithChecker (ctx , c , sourceFile , false /*includeDeferredGlobals*/ ), p .Options ()),
14791483 p .GetIncludeProcessorDiagnostics (sourceFile ),
14801484 )
14811485}
14821486
14831487// getBindAndCheckDiagnosticsWithChecker gets semantic diagnostics for a single file using a
14841488// caller-provided checker, including bind diagnostics, checker diagnostics, and handling
14851489// of @ts-ignore/@ts-expect-error directives.
1486- func (p * Program ) getBindAndCheckDiagnosticsWithChecker (ctx context.Context , fileChecker * checker.Checker , sourceFile * ast.SourceFile ) []* ast.Diagnostic {
1490+ func (p * Program ) getBindAndCheckDiagnosticsWithChecker (ctx context.Context , fileChecker * checker.Checker , sourceFile * ast.SourceFile , includeDeferredGlobals bool ) []* ast.Diagnostic {
14871491 compilerOptions := p .Options ()
14881492 if p .SkipTypeChecking (sourceFile , false ) {
14891493 return nil
14901494 }
1495+ var previousGlobals []* ast.Diagnostic
1496+ if includeDeferredGlobals {
1497+ previousGlobals = fileChecker .GetGlobalDiagnostics ()
1498+ }
14911499
14921500 // Checker creation forces binding, so bind diagnostics will be populated.
14931501 diags := slices .Clip (sourceFile .BindDiagnostics ())
14941502 diags = append (diags , fileChecker .GetDiagnostics (ctx , sourceFile )... )
14951503
1504+ if includeDeferredGlobals {
1505+ if fileChecker .WasCanceled () {
1506+ return nil
1507+ }
1508+ currentGlobals := fileChecker .GetGlobalDiagnostics ()
1509+ if len (currentGlobals ) > len (previousGlobals ) {
1510+ for _ , diagnostic := range currentGlobals {
1511+ if _ , found := slices .BinarySearchFunc (previousGlobals , diagnostic , ast .CompareDiagnostics ); ! found {
1512+ diags = append (diags , diagnostic )
1513+ }
1514+ }
1515+ }
1516+ }
1517+
14961518 isPlainJS := ast .IsPlainJSFile (sourceFile , compilerOptions .CheckJs )
14971519 if isPlainJS {
14981520 return core .Filter (diags , func (d * ast.Diagnostic ) bool {
@@ -1525,7 +1547,7 @@ func applyContentMapperDiagnosticDirectives(sourceFile *ast.SourceFile, diags []
15251547 }
15261548 used := make ([]bool , len (directives ))
15271549 markUsed := func (diag * ast.Diagnostic ) bool {
1528- if diag .Source () != "" {
1550+ if diag .File () != sourceFile || diag . Source () != "" {
15291551 return false
15301552 }
15311553 for i , directive := range directives {
@@ -1568,6 +1590,10 @@ func (p *Program) getDiagnosticsWithPrecedingDirectives(sourceFile *ast.SourceFi
15681590 filtered := make ([]* ast.Diagnostic , 0 , len (diags ))
15691591 for _ , diagnostic := range diags {
15701592 ignoreDiagnostic := false
1593+ if diagnostic .File () != sourceFile {
1594+ filtered = append (filtered , diagnostic )
1595+ continue
1596+ }
15711597 for line := scanner .ComputeLineOfPosition (lineStarts , diagnostic .Pos ()) - 1 ; line >= 0 ; line -- {
15721598 // If line contains a @ts-ignore or @ts-expect-error directive, ignore this diagnostic and change
15731599 // the directive kind to @ts-ignore to indicate it was used.
@@ -2023,8 +2049,11 @@ func GetDiagnosticsOfAnyProgram(
20232049
20242050 if len (allDiagnostics ) == configFileParsingDiagnosticsLength {
20252051 allDiagnostics = appendDiagnosticsForAllFiles (allDiagnostics , getSemanticDiagnostics )
2026- // Ask for the global diagnostics again (they were empty above); we may have found new during checking, e.g. missing globals.
2027- allDiagnostics = append (allDiagnostics , program .GetGlobalDiagnostics (ctx )... )
2052+ if p , ok := program .(* Program ); ok {
2053+ // Incremental programs cache checking globals with file diagnostics;
2054+ // a late sweep would also collect incidental signature-generation globals.
2055+ allDiagnostics = append (allDiagnostics , p .GetGlobalDiagnostics (ctx )... )
2056+ }
20282057 }
20292058
20302059 if (skipNoEmitCheckForDtsDiagnostics || program .Options ().NoEmit .IsTrue ()) && program .Options ().GetEmitDeclarations () && len (allDiagnostics ) == configFileParsingDiagnosticsLength {
0 commit comments