-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Simplify determination of live vs. build diagnostics #79691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,8 @@ public async Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(RequestCon | |
| isEnabledByDefault: true, | ||
| // Warning level 0 is used as a placeholder when the diagnostic has error severity | ||
| 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], | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, |
||
| properties: ImmutableDictionary<string, string?>.Empty, | ||
| projectId: document.Project.Id, | ||
| location: new DiagnosticDataLocation(location, document.Id) | ||
|
|
@@ -85,13 +86,6 @@ public Project GetProject() | |
| return document.Project; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// These diagnostics are from the last time 'dotnet run-api' was invoked, which only occurs when a design time build is performed. | ||
| /// <seealso cref="IDiagnosticSource.IsLiveSource"/>. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| public bool IsLiveSource() => false; | ||
|
|
||
| public string ToDisplayString() => nameof(VirtualProjectXmlDiagnosticSource); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,23 +17,33 @@ namespace Microsoft.CodeAnalysis.LanguageServer; | |
|
|
||
| internal static partial class ProtocolConversions | ||
| { | ||
| internal static ImmutableArray<DiagnosticData> AddBuildTagIfNotPresent(ImmutableArray<DiagnosticData> diagnostics) | ||
| { | ||
| return diagnostics.SelectAsArray(static d => | ||
| { | ||
| if (d.CustomTags.Contains(WellKnownDiagnosticTags.Build)) | ||
| return d; | ||
|
|
||
| return d.WithCustomTags(d.CustomTags.Add(WellKnownDiagnosticTags.Build)); | ||
| }); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Converts from <see cref="DiagnosticData"/> to <see cref="LSP.Diagnostic"/> | ||
| /// </summary> | ||
| /// <param name="diagnosticData">The diagnostic to convert</param> | ||
| /// <param name="supportsVisualStudioExtensions">Whether the client is Visual Studio</param> | ||
| /// <param name="project">The project the diagnostic is relevant to</param> | ||
| /// <param name="isLiveSource">Whether the diagnostic is considered "live" and should supersede others</param> | ||
| /// <param name="potentialDuplicate">Whether the diagnostic is potentially a duplicate to a build diagnostic</param> | ||
| /// <param name="globalOptionService">The global options service</param> | ||
| public static ImmutableArray<LSP.Diagnostic> ConvertDiagnostic(DiagnosticData diagnosticData, bool supportsVisualStudioExtensions, Project project, bool isLiveSource, bool potentialDuplicate, IGlobalOptionService globalOptionService) | ||
| public static ImmutableArray<LSP.Diagnostic> ConvertDiagnostic(DiagnosticData diagnosticData, bool supportsVisualStudioExtensions, Project project, bool potentialDuplicate, IGlobalOptionService globalOptionService) | ||
| { | ||
| if (!ShouldIncludeHiddenDiagnostic(diagnosticData, supportsVisualStudioExtensions)) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| var diagnostic = CreateLspDiagnostic(diagnosticData, project, isLiveSource, potentialDuplicate, supportsVisualStudioExtensions); | ||
| var diagnostic = CreateLspDiagnostic(diagnosticData, project, potentialDuplicate, supportsVisualStudioExtensions); | ||
|
|
||
| // Check if we need to handle the unnecessary tag (fading). | ||
| if (!diagnosticData.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary)) | ||
|
|
@@ -65,7 +75,7 @@ internal static partial class ProtocolConversions | |
| diagnosticsBuilder.Add(diagnostic); | ||
| foreach (var location in unnecessaryLocations) | ||
| { | ||
| var additionalDiagnostic = CreateLspDiagnostic(diagnosticData, project, isLiveSource, potentialDuplicate, supportsVisualStudioExtensions); | ||
| var additionalDiagnostic = CreateLspDiagnostic(diagnosticData, project, potentialDuplicate, supportsVisualStudioExtensions); | ||
| additionalDiagnostic.Severity = LSP.DiagnosticSeverity.Hint; | ||
| additionalDiagnostic.Range = GetRange(location); | ||
| additionalDiagnostic.Tags = [DiagnosticTag.Unnecessary, VSDiagnosticTags.HiddenInEditor, VSDiagnosticTags.HiddenInErrorList, VSDiagnosticTags.SuppressEditorToolTip]; | ||
|
|
@@ -94,7 +104,6 @@ internal static partial class ProtocolConversions | |
| private static LSP.VSDiagnostic CreateLspDiagnostic( | ||
| DiagnosticData diagnosticData, | ||
| Project project, | ||
| bool isLiveSource, | ||
| bool potentialDuplicate, | ||
| bool supportsVisualStudioExtensions) | ||
| { | ||
|
|
@@ -108,7 +117,7 @@ private static LSP.VSDiagnostic CreateLspDiagnostic( | |
| CodeDescription = ProtocolConversions.HelpLinkToCodeDescription(diagnosticData.GetValidHelpLinkUri()), | ||
| Message = diagnosticData.Message, | ||
| Severity = ConvertDiagnosticSeverity(diagnosticData.Severity), | ||
| Tags = ConvertTags(diagnosticData, isLiveSource, potentialDuplicate), | ||
| Tags = ConvertTags(diagnosticData, potentialDuplicate), | ||
| DiagnosticRank = ConvertRank(diagnosticData), | ||
| Range = GetRange(diagnosticData.DataLocation) | ||
| }; | ||
|
|
@@ -223,7 +232,7 @@ private static LSP.DiagnosticSeverity ConvertDiagnosticSeverity(DiagnosticSeveri | |
| /// If you make change in this method, please also update the corresponding file in | ||
| /// src\VisualStudio\Xaml\Impl\Implementation\LanguageServer\Handler\Diagnostics\AbstractPullDiagnosticHandler.cs | ||
| /// </summary> | ||
| private static DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData, bool isLiveSource, bool potentialDuplicate) | ||
| private static DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData, bool potentialDuplicate) | ||
| { | ||
| using var _ = ArrayBuilder<DiagnosticTag>.GetInstance(out var result); | ||
|
|
||
|
|
@@ -246,11 +255,8 @@ private static DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData, bool i | |
| if (potentialDuplicate) | ||
| result.Add(VSDiagnosticTags.PotentialDuplicate); | ||
|
|
||
| // Mark this also as a build error. That way an explicitly kicked off build from a source like CPS can | ||
| // 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); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above, this was the only place |
||
|
|
||
| result.Add(diagnosticData.CustomTags.Contains(WellKnownDiagnosticTags.Build) | ||
| ? VSDiagnosticTags.BuildError | ||
| : VSDiagnosticTags.IntellisenseError); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,6 @@ | |
| using System.Collections.Generic; | ||
| using System.Collections.Immutable; | ||
| using System.Composition; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
@@ -78,7 +77,7 @@ private static async ValueTask<ImmutableArray<IDiagnosticSource>> CreateDiagnost | |
| } | ||
| else | ||
| { | ||
| // VS Code (and legacy VS ?) pass null sourceName when requesting all sources. | ||
| // Some clients (legacy VS/VSCode, Razor) do not support multiple sources - a null source indicates that diagnostics from all sources should be returned. | ||
| using var _ = ArrayBuilder<IDiagnosticSource>.GetInstance(out var sourcesBuilder); | ||
| foreach (var (name, provider) in nameToProviderMap) | ||
| { | ||
|
|
@@ -106,7 +105,6 @@ public static ImmutableArray<IDiagnosticSource> AggregateSourcesIfNeeded(Immutab | |
| if (isDocument) | ||
| { | ||
| // Group all document sources into a single source. | ||
| Debug.Assert(sources.All(s => s.IsLiveSource()), "All document sources should be live"); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| sources = [new AggregatedDocumentDiagnosticSource(sources)]; | ||
| } | ||
| else | ||
|
|
@@ -115,7 +113,7 @@ public static ImmutableArray<IDiagnosticSource> AggregateSourcesIfNeeded(Immutab | |
| // will have same value for GetDocumentIdentifier and GetProject(). Thus can be | ||
| // aggregated in a single source which will return same values. See | ||
| // AggregatedDocumentDiagnosticSource implementation for more details. | ||
| sources = [.. sources.GroupBy(s => (s.GetId(), s.IsLiveSource()), s => s).SelectMany(g => AggregatedDocumentDiagnosticSource.AggregateIfNeeded(g))]; | ||
| sources = [.. sources.GroupBy(s => s.GetId(), s => s).SelectMany(g => AggregatedDocumentDiagnosticSource.AggregateIfNeeded(g))]; | ||
| } | ||
|
|
||
| return sources; | ||
|
|
@@ -141,7 +139,6 @@ public static ImmutableArray<IDiagnosticSource> AggregateIfNeeded(IEnumerable<ID | |
| return result; | ||
| } | ||
|
|
||
| public bool IsLiveSource() => true; | ||
| public Project GetProject() => sources[0].GetProject(); | ||
| public ProjectOrDocumentId GetId() => sources[0].GetId(); | ||
| public TextDocumentIdentifier? GetDocumentIdentifier() => sources[0].GetDocumentIdentifier(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.