Skip to content

Conversation

@dibarbet
Copy link
Member

@dibarbet dibarbet commented Jul 30, 2025

Removes IsLive from the diagnostic source, and relies on diagnostic tags to correctly set the Build diagnostic tag.

This fixes a debug assert crash when using Razor with the latest main changes

2025-07-30 14:46:02.141 [error] [stderr] Process terminated. Assertion failed.
All document sources should be live
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSourceManager.AggregateSourcesIfNeeded(ImmutableArray`1 sources, Boolean isDocument) in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Protocol\Handler\Diagnostics\DiagnosticSourceProviders\DiagnosticSourceManager.cs:line 109
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSourceManager.CreateDiagnosticSourcesAsync(RequestContext context, String providerName, ImmutableDictionary`2 nameToProviderMap, Boolean isDocument, CancellationToken cancellationToken) in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Protocol\Handler\Diagnostics\DiagnosticSourceProviders\DiagnosticSourceManager.cs:line 95
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSourceManager.CreateDiagnosticSourcesAsync(RequestContext context, String providerName, ImmutableDictionary`2 nameToProviderMap, Boolean isDocument, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSourceManager.CreateDocumentDiagnosticSourcesAsync(RequestContext context, String providerName, CancellationToken cancellationToken) in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Protocol\Handler\Diagnostics\DiagnosticSourceProviders\DiagnosticSourceManager.cs:line 55
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.AbstractDocumentPullDiagnosticHandler`3.GetOrderedDiagnosticSourcesAsync(TDiagnosticsParams diagnosticsParams, String requestDiagnosticCategory, RequestContext context, CancellationToken cancellationToken) in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Protocol\Handler\Diagnostics\AbstractDocumentPullDiagnosticHandler.cs:line 50
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.AbstractPullDiagnosticHandler`3.HandleRequestAsync(TDiagnosticsParams diagnosticsParams, RequestContext context, CancellationToken cancellationToken) in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Protocol\Handler\Diagnostics\AbstractPullDiagnosticHandler.cs:line 142
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.AbstractPullDiagnosticHandler`3.HandleRequestAsync(TDiagnosticsParams diagnosticsParams, RequestContext context, CancellationToken cancellationToken)
   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\QueueItem.cs:line 191
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken)
   at Microsoft.CommonLanguageServerProtocol.Framework.RequestExecutionQueue`1.<>c__DisplayClass18_0`2.<ProcessQueueCoreAsync>b__2() in C:\Users\xiaoyuz\source\repo\roslyn\src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\RequestExecutionQueue.cs:line 378
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()

this bug was revealed in #79421 when a non-live doc diagnostic source was added

warningLevel: 0,
customTags: ImmutableArray<string>.Empty,
// Mark these diagnostics as build errors so they can be overridden by diagnostics from an explicit build.
customTags: [WellKnownDiagnosticTags.Build],
Copy link
Member Author

@dibarbet dibarbet Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are hitting a debug assertion in DiagnosticSourceManager (see below) when attempting to aggregate document diagnostics from multiple sources when a client calls into us that does not support multiple sources (these are Razor, super old versions of VS/VSCode, or third party LSPs). We could not aggregate live and non-live diagnostic sources into a single source as the diagnostics returned are modified based on the source.

We recently introduced a new non-live document diagnostic source, which triggered the assert (see below) when Razor calls into us.

After looking at this, it appeared to me as though a diagnostic source being 'live' or 'not live' was not the correct abstraction. At the end of the day, IsLiveSource was only ever read to set a tag on the diagnostic indicating if it was a build diagnostic. Instead of having IsLiveSource be on the IDiagnosticSource, we can just create diagnostics with the correct build tag to begin with!

// If tagged as build, mark this also as a build error. That way an explicitly kicked off build from a source like CPS can
// override it.
if (!isLiveSource)
result.Add(VSDiagnosticTags.BuildError);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above, this was the only place IDiagnosticSource.isLiveSource eventually got read. And we already set the build tag based on our build tag below. So we can just set the build tag on the diagnostics directly instead of this whole method on the source.

if (isDocument)
{
// Group all document sources into a single source.
Debug.Assert(sources.All(s => s.IsLiveSource()), "All document sources should be live");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was the assert that was firing. Now there is no concept of 'live' vs. non-live sources, just live vs. non-live diagnostics

//This source provides the results of the *last* explicitly kicked off "run code analysis" command from the
// user. As such, it is definitely not "live" data, and it should be overridden by any subsequent fresh data
// that has been produced.
diagnostics = [.. diagnostics.Select(d => d.WithCustomTags(d.CustomTags.Add(WellKnownDiagnosticTags.Build)))];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicitly tag the diagnostics as build diagnostics in the impl, instead of using IsLiveSource

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any possibility of the diagnostics already having Build tags?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. might want a helper that only adds if not alreadythere. and call that single helper from all these places.

@dibarbet dibarbet marked this pull request as ready for review July 30, 2025 23:31
@dibarbet dibarbet requested a review from a team as a code owner July 30, 2025 23:31
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me, I'm not the expert here though.

//This source provides the results of the *last* explicitly kicked off "run code analysis" command from the
// user. As such, it is definitely not "live" data, and it should be overridden by any subsequent fresh data
// that has been produced.
diagnostics = [.. diagnostics.Select(d => d.WithCustomTags(d.CustomTags.Add(WellKnownDiagnosticTags.Build)))];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any possibility of the diagnostics already having Build tags?

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall seems good. tiny cleanup maybe.

@dibarbet dibarbet requested a review from a team as a code owner July 31, 2025 18:15
@dibarbet dibarbet merged commit 9a48772 into dotnet:main Jul 31, 2025
28 checks passed
@dibarbet dibarbet deleted the simplify_live_diagnostics branch July 31, 2025 20:34
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jul 31, 2025
@RikkiGibson RikkiGibson modified the milestones: Next, 18.0 P1 Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants