diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 7a8456f5b9051..6590ce7233722 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -854,7 +854,7 @@ class AnonymousFunctions ' Test "GetDiagnosticsForIdsAsync" does force computation of compilation end diagnostics. ' Verify compilation diagnostics are reported with correct location info when asked for project diagnostics. Dim projectDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId:=Nothing, - diagnosticIds:=Nothing, includeSuppressedDiagnostics:=False, + diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeSuppressedDiagnostics:=False, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, projectDiagnostics.Count()) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index 06d279ce14f80..0b2ab8ccad861 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -61,13 +61,13 @@ public Task> GetCachedDiagnosticsAsync(Workspace public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, Func? addOperationScope, DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) => Task.FromResult(_diagnosticsWithKindFilter.Where(d => diagnosticKind == d.KindFilter).Select(d => d.Diagnostic).ToImmutableArray()); - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 6d8ec684b0b98..ba3bb2ed9680c 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -51,17 +51,17 @@ internal interface IDiagnosticAnalyzerService Task ForceAnalyzeAsync(Solution solution, Action onProjectAnalyzed, ProjectId? projectId, CancellationToken cancellationToken); /// - /// Get diagnostics of the given diagnostic ids from the given solution. all diagnostics returned should be up-to-date with respect to the given solution. - /// Note that for project case, this method returns diagnostics from all project documents as well. Use + /// Get diagnostics of the given diagnostic ids and/or analyzers from the given solution. all diagnostics returned should be up-to-date with respect to the given solution. + /// Note that for project case, this method returns diagnostics from all project documents as well. Use /// if you want to fetch only project diagnostics without source locations. /// - Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// - /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids from the given solution. all diagnostics returned should be up-to-date with respect to the given solution. - /// Note that this method doesn't return any document diagnostics. Use to also fetch those. + /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids and/or analyzers from the given solution. all diagnostics returned should be up-to-date with respect to the given solution. + /// Note that this method doesn't return any document diagnostics. Use to also fetch those. /// - Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Try to return up to date diagnostics for the given span for the document. diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index b06b8fb3783d1..558810f8383d0 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -46,7 +46,7 @@ public FixAllDiagnosticProvider(IDiagnosticAnalyzerService diagnosticService, Im public override async Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken) { var solution = document.Project.Solution; - var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(solution, projectId: null, document.Id, _diagnosticIds, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(solution, projectId: null, document.Id, _diagnosticIds, shouldIncludeAnalyzer: null, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } @@ -64,14 +64,14 @@ public override async Task> GetDocumentSpanDiagnosticsAs public override async Task> GetAllDiagnosticsAsync(Project project, CancellationToken cancellationToken) { // Get all diagnostics for the entire project, including document diagnostics. - var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, _diagnosticIds, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, _diagnosticIds, shouldIncludeAnalyzer: null, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } public override async Task> GetProjectDiagnosticsAsync(Project project, CancellationToken cancellationToken) { // Get all no-location diagnostics for the project, doesn't include document diagnostics. - var diagnostics = await _diagnosticService.GetProjectDiagnosticsForIdsAsync(project.Solution, project.Id, _diagnosticIds, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticService.GetProjectDiagnosticsForIdsAsync(project.Solution, project.Id, _diagnosticIds, shouldIncludeAnalyzer: null, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 7185f7b4aca8a..b8066fafc91b4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -200,22 +200,24 @@ public async Task ForceAnalyzeAsync(Solution solution, Action onProject } public Task> GetDiagnosticsForIdsAsync( - Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(solution.Workspace, out var analyzer)) { - return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); + return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } return SpecializedTasks.EmptyImmutableArray(); } public Task> GetProjectDiagnosticsForIdsAsync( - Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, + Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, + bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(solution.Workspace, out var analyzer)) { - return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); + return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } return SpecializedTasks.EmptyImmutableArray(); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 7df4f9c5eebbe..3bd8835324a9a 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -29,13 +30,13 @@ public Task> GetCachedDiagnosticsAsync(Solution s => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds: null, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds: null, shouldIncludeAnalyzer: null, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds: diagnosticIds, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); + public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); private abstract class DiagnosticGetter { @@ -230,11 +231,16 @@ private static async Task> GetProjectStateDiagnos private sealed class IdeLatestDiagnosticGetter : DiagnosticGetter { private readonly ImmutableHashSet? _diagnosticIds; + private readonly Func? _shouldIncludeAnalyzer; - public IdeLatestDiagnosticGetter(DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics) + public IdeLatestDiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, + DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, + bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics) : base(owner, solution, projectId, documentId, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics) { _diagnosticIds = diagnosticIds; + _shouldIncludeAnalyzer = shouldIncludeAnalyzer; } public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) @@ -296,6 +302,11 @@ private bool ShouldIncludeStateSet(Project project, StateSet stateSet) return false; } + if (_shouldIncludeAnalyzer != null && !_shouldIncludeAnalyzer(stateSet.Analyzer)) + { + return false; + } + if (_diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(stateSet.Analyzer).All(d => !_diagnosticIds.Contains(d.Id))) { return false; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs index 537efcb3ef13e..9ab028f945b1b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; -internal sealed record class ProjectDiagnosticSource(Project Project) : IDiagnosticSource +internal sealed record class ProjectDiagnosticSource(Project Project, Func? ShouldIncludeAnalyzer) : IDiagnosticSource { public ProjectOrDocumentId GetId() => new(Project.Id); public Project GetProject() => Project; @@ -31,7 +31,7 @@ public async Task> GetDiagnosticsAsync( // it will be computed on demand. Because it is always accurate as per this snapshot, all spans are correct // and do not need to be adjusted. var projectDiagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync(Project.Solution, Project.Id, - diagnosticIds: null, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + diagnosticIds: null, ShouldIncludeAnalyzer, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); return projectDiagnostics; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs index faa4874e2cda6..119480bd5dd4f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs @@ -13,9 +13,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal sealed class WorkspaceDocumentDiagnosticSource : AbstractDocumentDiagnosticSource { - public WorkspaceDocumentDiagnosticSource(TextDocument document) + private readonly Func? _shouldIncludeAnalyzer; + + public WorkspaceDocumentDiagnosticSource(TextDocument document, Func? shouldIncludeAnalyzer) : base(document) { + _shouldIncludeAnalyzer = shouldIncludeAnalyzer; } public override async Task> GetDiagnosticsAsync( @@ -36,7 +39,7 @@ public override async Task> GetDiagnosticsAsync( // However we can include them as a part of workspace pull when FSA is on. var documentDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( Document.Project.Solution, Document.Project.Id, Document.Id, - diagnosticIds: null, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + diagnosticIds: null, _shouldIncludeAnalyzer, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); return documentDiagnostics; } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index b108c3cab4464..3ced29d52f60f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -175,7 +176,7 @@ public static async ValueTask> GetDiagnosticSo async Task AddDocumentsAndProject(Project project, CancellationToken cancellationToken) { - var fullSolutionAnalysisEnabled = globalOptions.IsFullSolutionAnalysisEnabled(project.Language); + var fullSolutionAnalysisEnabled = globalOptions.IsFullSolutionAnalysisEnabled(project.Language, out var compilerFullSolutionAnalysisEnabled, out var analyzersFullSolutionAnalysisEnabled); if (!fullSolutionAnalysisEnabled) return; @@ -188,14 +189,24 @@ async Task AddDocumentsAndProject(Project project, CancellationToken cancellatio documents = documents.AddRange(sourceGeneratedDocuments); } + Func? shouldIncludeAnalyzer = !compilerFullSolutionAnalysisEnabled || !analyzersFullSolutionAnalysisEnabled + ? ShouldIncludeAnalyzer : null; foreach (var document in documents) { if (!ShouldSkipDocument(context, document)) - result.Add(new WorkspaceDocumentDiagnosticSource(document)); + result.Add(new WorkspaceDocumentDiagnosticSource(document, shouldIncludeAnalyzer)); } // Finally, add the project source to get project specific diagnostics, not associated with any document. - result.Add(new ProjectDiagnosticSource(project)); + result.Add(new ProjectDiagnosticSource(project, shouldIncludeAnalyzer)); + + bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) + { + if (analyzer.IsCompilerAnalyzer()) + return compilerFullSolutionAnalysisEnabled; + else + return analyzersFullSolutionAnalysisEnabled; + } } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index 32a1e32dd11c7..d848b77dd429c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -296,30 +296,44 @@ static DocumentDiagnosticParams CreateProposedDocumentDiagnosticParams( } } - private protected Task CreateTestWorkspaceWithDiagnosticsAsync(string markup, bool mutatingLspWorkspace, BackgroundAnalysisScope scope, bool useVSDiagnostics, bool pullDiagnostics = true) - => CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(scope, useVSDiagnostics, pullDiagnostics ? DiagnosticMode.LspPull : DiagnosticMode.SolutionCrawlerPush)); + private protected Task CreateTestWorkspaceWithDiagnosticsAsync(string markup, bool mutatingLspWorkspace, BackgroundAnalysisScope analyzerDiagnosticsScope, bool useVSDiagnostics, bool pullDiagnostics = true, CompilerDiagnosticsScope? compilerDiagnosticsScope = null) + => CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(analyzerDiagnosticsScope, compilerDiagnosticsScope, useVSDiagnostics, pullDiagnostics ? DiagnosticMode.LspPull : DiagnosticMode.SolutionCrawlerPush)); - private protected Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, bool mutatingLspWorkspace, BackgroundAnalysisScope scope, bool useVSDiagnostics, bool pullDiagnostics = true) - => CreateTestLspServerAsync(markups, mutatingLspWorkspace, GetInitializationOptions(scope, useVSDiagnostics, pullDiagnostics ? DiagnosticMode.LspPull : DiagnosticMode.SolutionCrawlerPush)); + private protected Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, bool mutatingLspWorkspace, BackgroundAnalysisScope analyzerDiagnosticsScope, bool useVSDiagnostics, bool pullDiagnostics = true, CompilerDiagnosticsScope? compilerDiagnosticsScope = null) + => CreateTestLspServerAsync(markups, mutatingLspWorkspace, GetInitializationOptions(analyzerDiagnosticsScope, compilerDiagnosticsScope, useVSDiagnostics, pullDiagnostics ? DiagnosticMode.LspPull : DiagnosticMode.SolutionCrawlerPush)); - private protected Task CreateTestWorkspaceFromXmlAsync(string xmlMarkup, bool mutatingLspWorkspace, BackgroundAnalysisScope scope, bool useVSDiagnostics, bool pullDiagnostics = true) - => CreateXmlTestLspServerAsync(xmlMarkup, mutatingLspWorkspace, initializationOptions: GetInitializationOptions(scope, useVSDiagnostics, pullDiagnostics ? DiagnosticMode.LspPull : DiagnosticMode.SolutionCrawlerPush)); + private protected Task CreateTestWorkspaceFromXmlAsync(string xmlMarkup, bool mutatingLspWorkspace, BackgroundAnalysisScope analyzerDiagnosticsScope, bool useVSDiagnostics, bool pullDiagnostics = true, CompilerDiagnosticsScope? compilerDiagnosticsScope = null) + => CreateXmlTestLspServerAsync(xmlMarkup, mutatingLspWorkspace, initializationOptions: GetInitializationOptions(analyzerDiagnosticsScope, compilerDiagnosticsScope, useVSDiagnostics, pullDiagnostics ? DiagnosticMode.LspPull : DiagnosticMode.SolutionCrawlerPush)); private protected static InitializationOptions GetInitializationOptions( - BackgroundAnalysisScope scope, + BackgroundAnalysisScope analyzerDiagnosticsScope, + CompilerDiagnosticsScope? compilerDiagnosticsScope, bool useVSDiagnostics, DiagnosticMode mode, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer, string[]? sourceGeneratedMarkups = null) { + // If no explicit compiler diagnostics scope has been provided, match it with the provided analyzer diagnostics scope + compilerDiagnosticsScope ??= analyzerDiagnosticsScope switch + { + BackgroundAnalysisScope.None => CompilerDiagnosticsScope.None, + BackgroundAnalysisScope.ActiveFile => CompilerDiagnosticsScope.VisibleFilesAndFilesWithPreviouslyReportedDiagnostics, + BackgroundAnalysisScope.OpenFiles => CompilerDiagnosticsScope.OpenFiles, + BackgroundAnalysisScope.FullSolution => CompilerDiagnosticsScope.FullSolution, + _ => throw ExceptionUtilities.UnexpectedValue(analyzerDiagnosticsScope), + }; + return new InitializationOptions { ClientCapabilities = useVSDiagnostics ? CapabilitiesWithVSExtensions : new LSP.ClientCapabilities(), OptionUpdater = (globalOptions) => { - globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, scope); - globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.VisualBasic, scope); - globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, InternalLanguageNames.TypeScript, scope); + globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analyzerDiagnosticsScope); + globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.VisualBasic, analyzerDiagnosticsScope); + globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, InternalLanguageNames.TypeScript, analyzerDiagnosticsScope); + globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, compilerDiagnosticsScope.Value); + globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.VisualBasic, compilerDiagnosticsScope.Value); + globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, InternalLanguageNames.TypeScript, compilerDiagnosticsScope.Value); globalOptions.SetGlobalOption(InternalDiagnosticsOptionsStorage.NormalDiagnosticMode, mode); globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.EnableDiagnosticsInSourceGeneratedFiles, true); }, diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index d87841e466794..4554f22b7ae06 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -205,7 +205,7 @@ public async Task TestNoDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOf var markup = @"class A {"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Default)); + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Default)); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -224,7 +224,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b var markup = @"class A {"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Default)); + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Default)); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -618,7 +618,7 @@ public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, // Turn off pull diagnostics by default, but send a request to the razor LSP server which is always pull. await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.SolutionCrawlerPush, WellKnownLspServerKinds.RazorLspServer)); + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, DiagnosticMode.SolutionCrawlerPush, WellKnownLspServerKinds.RazorLspServer)); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -643,7 +643,7 @@ public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnosti // Turn off pull diagnostics by default, but send a request to the razor LSP server which is always pull. await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.SolutionCrawlerPush, WellKnownLspServerKinds.LiveShareLspServer)); + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, DiagnosticMode.SolutionCrawlerPush, WellKnownLspServerKinds.LiveShareLspServer)); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -1132,7 +1132,7 @@ public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiag var markup2 = ""; await using var testLspServer = await CreateTestLspServerAsync( markups: Array.Empty(), mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.FullSolution, useVSDiagnostics, DiagnosticMode.LspPull, sourceGeneratedMarkups: new[] { markup1, markup2 })); + GetInitializationOptions(BackgroundAnalysisScope.FullSolution, CompilerDiagnosticsScope.FullSolution, useVSDiagnostics, DiagnosticMode.LspPull, sourceGeneratedMarkups: new[] { markup1, markup2 })); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 26119b38ebc07..59ca6ed2a3749 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -547,7 +547,7 @@ private async Task>> Ge var uniqueDiagnosticIds = group.SelectMany(kvp => kvp.Value.Select(d => d.Id)).ToImmutableHashSet(); var latestProjectDiagnostics = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, - diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) + diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)).Where(IsDocumentDiagnostic); latestDocumentDiagnosticsMap.Clear(); @@ -637,7 +637,7 @@ private async Task>> Get var uniqueDiagnosticIds = diagnostics.Select(d => d.Id).ToImmutableHashSet(); var latestDiagnosticsFromDiagnosticService = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, - diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) + diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)); latestDiagnosticsToFix.Clear(); diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index ffce4f2eb69b7..9badd52937f44 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -683,11 +683,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function - Public Function GetDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), includeSuppressedDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync + Public Function GetDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeSuppressedDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function - Public Function GetProjectDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, diagnosticIds As ImmutableHashSet(Of String), includeSuppressedDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync + Public Function GetProjectDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeSuppressedDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function