From 2b9c87777f31d07611b72526310c3b1689d5e883 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 11:45:14 -0700 Subject: [PATCH 1/9] Properly update workspace options when switching from FSA on to off --- .../Api/VSTypeScriptGlobalOptions.cs | 2 + .../AbstractPullDiagnosticHandler.cs | 28 +++++++--- .../Diagnostics/PullDiagnosticTests.cs | 51 +++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptGlobalOptions.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptGlobalOptions.cs index e80b36e4cea71..fe33d18f6e4f4 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptGlobalOptions.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptGlobalOptions.cs @@ -28,6 +28,8 @@ public void SetBackgroundAnalysisScope(bool openFilesOnly) { _globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, InternalLanguageNames.TypeScript, openFilesOnly ? BackgroundAnalysisScope.OpenFiles : BackgroundAnalysisScope.FullSolution); + _globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, InternalLanguageNames.TypeScript, + openFilesOnly ? CompilerDiagnosticsScope.OpenFiles : CompilerDiagnosticsScope.FullSolution); _globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.RemoveDocumentDiagnosticsOnDocumentClose, InternalLanguageNames.TypeScript, openFilesOnly); diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 19556203d19d7..5da03eafbe67b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -160,7 +160,8 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT // Create a mapping from documents to the previous results the client says it has for them. That way as we // process documents we know if we should tell the client it should stay the same, or we can tell it what // the updated diagnostics are. - var documentToPreviousDiagnosticParams = GetIdToPreviousDiagnosticParams(context, previousResults, out var removedResults); + using var _1 = PooledHashSet.GetInstance(out var removedResults); + var documentToPreviousDiagnosticParams = GetIdToPreviousDiagnosticParams(context, previousResults, removedResults); // First, let the client know if any workspace documents have gone away. That way it can remove those for // the user from squiggles or error-list. @@ -173,8 +174,11 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT context.TraceInformation($"Processing {orderedSources.Length} documents"); + using var _2 = PooledHashSet.GetInstance(out var seenDiagnosticSourceIds); + foreach (var diagnosticSource in orderedSources) { + seenDiagnosticSourceIds.Add(diagnosticSource.GetId()); var globalStateVersion = _diagnosticRefresher.GlobalStateVersion; var project = diagnosticSource.GetProject(); @@ -207,6 +211,20 @@ await ComputeAndReportCurrentDiagnosticsAsync( } } + // Now, for any diagnostics reported from a prior source that we do not see this time around, report its + // diagnostics as being removed. This allows for different sets of diagnostic-sources to be computed + // each time around, while still producing accurate diagnostic reports. + // + // Only do this if we haven't already created a removal report for that prior result. + foreach (var (projectOrDocumentId, previousDiagnosticParams) in documentToPreviousDiagnosticParams) + { + if (!seenDiagnosticSourceIds.Contains(projectOrDocumentId) && + !removedResults.Contains(previousDiagnosticParams)) + { + progress.Report(CreateRemovedReport(previousDiagnosticParams.TextDocument)); + } + } + // Clear out the solution context to avoid retaining memory // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1809058 context.ClearSolutionContext(); @@ -225,12 +243,11 @@ await ComputeAndReportCurrentDiagnosticsAsync( } private static Dictionary GetIdToPreviousDiagnosticParams( - RequestContext context, ImmutableArray previousResults, out ImmutableArray removedDocuments) + RequestContext context, ImmutableArray previousResults, HashSet removedDocuments) { Contract.ThrowIfNull(context.Solution); var result = new Dictionary(); - using var _ = ArrayBuilder.GetInstance(out var removedDocumentsBuilder); foreach (var diagnosticParams in previousResults) { if (diagnosticParams.TextDocument != null) @@ -244,12 +261,11 @@ private static Dictionary GetIdToPrevio { // The client previously had a result from us for this document, but we no longer have it in our solution. // Record it so we can report to the client that it has been removed. - removedDocumentsBuilder.Add(diagnosticParams); + removedDocuments.Add(diagnosticParams); } } } - removedDocuments = removedDocumentsBuilder.ToImmutable(); return result; static ProjectOrDocumentId? GetIdForPreviousResult(TextDocumentIdentifier textDocumentIdentifier, Solution solution) @@ -306,7 +322,7 @@ private async Task ComputeAndReportCurrentDiagnosticsAsync( progress.Report(report); } - private void HandleRemovedDocuments(RequestContext context, ImmutableArray removedPreviousResults, BufferedProgress progress) + private void HandleRemovedDocuments(RequestContext context, HashSet removedPreviousResults, BufferedProgress progress) { foreach (var removedResult in removedPreviousResults) { diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 4020f21f0529d..bac36437b333d 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; @@ -1892,6 +1893,56 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSD Assert.NotEmpty(results); } + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = +@"class A {"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + + Assert.Equal(2, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + + var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.OpenFiles); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, CompilerDiagnosticsScope.OpenFiles); + + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + + Assert.Equal(2, results.Length); + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); + } + + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOffToOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = +@"class A {"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + + Assert.Equal(2, results.Length); + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); + + var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, CompilerDiagnosticsScope.FullSolution); + + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + + Assert.Equal(2, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + } + #endregion } } From b0b10f75ee93576626f47db7a71580a33f1ef295 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:22:41 -0700 Subject: [PATCH 2/9] update tests --- .../Diagnostics/PullDiagnosticTests.cs | 2543 ++++++++--------- 1 file changed, 1268 insertions(+), 1275 deletions(-) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index bac36437b333d..080a67c9fcf3e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -23,427 +23,423 @@ using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics -{ - public class PullDiagnosticTests : AbstractPullDiagnosticTestsBase - { - public PullDiagnosticTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - } +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics; - #region Document Diagnostics +public sealed class PullDiagnosticTests(ITestOutputHelper testOutputHelper) : AbstractPullDiagnosticTestsBase(testOutputHelper) +{ + #region Document Diagnostics - [Theory, CombinatorialData] - public async Task TestNoDocumentDiagnosticsForClosedFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestNoDocumentDiagnosticsForClosedFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Empty(results); + Assert.Empty(results); - // Verify document pull diagnostics are unaffected by running code analysis. - await testLspServer.RunCodeAnalysisAsync(document.Project.Id); - results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Empty(results); - } + // Verify document pull diagnostics are unaffected by running code analysis. + await testLspServer.RunCodeAnalysisAsync(document.Project.Id); + results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + Assert.Empty(results); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); - } + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); + } - [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/fsharp/issues/15972")] - public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff_Categories(bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/fsharp/issues/15972")] + public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff_Categories(bool mutatingLspWorkspace) + { + var markup = @"class A : B {"; - var additionalAnalyzers = new DiagnosticAnalyzer[] { new CSharpSyntaxAnalyzer(), new CSharpSemanticAnalyzer() }; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true, additionalAnalyzers: additionalAnalyzers); + var additionalAnalyzers = new DiagnosticAnalyzer[] { new CSharpSyntaxAnalyzer(), new CSharpSemanticAnalyzer() }; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true, additionalAnalyzers: additionalAnalyzers); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var syntaxResults = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentCompilerSyntax); + var syntaxResults = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentCompilerSyntax); - var semanticResults = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentCompilerSemantic); + var semanticResults = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentCompilerSemantic); - Assert.Equal("CS1513", syntaxResults.Single().Diagnostics.Single().Code); - Assert.Equal("CS0246", semanticResults.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", syntaxResults.Single().Diagnostics.Single().Code); + Assert.Equal("CS0246", semanticResults.Single().Diagnostics.Single().Code); - var syntaxResults2 = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: syntaxResults.Single().ResultId, category: PullDiagnosticCategories.DocumentCompilerSyntax); - var semanticResults2 = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: semanticResults.Single().ResultId, category: PullDiagnosticCategories.DocumentCompilerSemantic); + var syntaxResults2 = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: syntaxResults.Single().ResultId, category: PullDiagnosticCategories.DocumentCompilerSyntax); + var semanticResults2 = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: semanticResults.Single().ResultId, category: PullDiagnosticCategories.DocumentCompilerSemantic); - Assert.Equal(syntaxResults.Single().ResultId, syntaxResults2.Single().ResultId); - Assert.Equal(semanticResults.Single().ResultId, semanticResults2.Single().ResultId); + Assert.Equal(syntaxResults.Single().ResultId, syntaxResults2.Single().ResultId); + Assert.Equal(semanticResults.Single().ResultId, semanticResults2.Single().ResultId); - var syntaxAnalyzerResults = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentAnalyzerSyntax); + var syntaxAnalyzerResults = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentAnalyzerSyntax); - var semanticAnalyzerResults = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentAnalyzerSemantic); + var semanticAnalyzerResults = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.DocumentAnalyzerSemantic); - Assert.Equal(CSharpSyntaxAnalyzer.RuleId, syntaxAnalyzerResults.Single().Diagnostics.Single().Code); - Assert.Equal(CSharpSemanticAnalyzer.RuleId, semanticAnalyzerResults.Single().Diagnostics.Single().Code); + Assert.Equal(CSharpSyntaxAnalyzer.RuleId, syntaxAnalyzerResults.Single().Diagnostics.Single().Code); + Assert.Equal(CSharpSemanticAnalyzer.RuleId, semanticAnalyzerResults.Single().Diagnostics.Single().Code); - var syntaxAnalyzerResults2 = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: syntaxAnalyzerResults.Single().ResultId, category: PullDiagnosticCategories.DocumentAnalyzerSyntax); - var semanticAnalyzerResults2 = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: semanticAnalyzerResults.Single().ResultId, category: PullDiagnosticCategories.DocumentAnalyzerSemantic); + var syntaxAnalyzerResults2 = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: syntaxAnalyzerResults.Single().ResultId, category: PullDiagnosticCategories.DocumentAnalyzerSyntax); + var semanticAnalyzerResults2 = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true, previousResultId: semanticAnalyzerResults.Single().ResultId, category: PullDiagnosticCategories.DocumentAnalyzerSemantic); - Assert.Equal(syntaxAnalyzerResults.Single().ResultId, syntaxAnalyzerResults2.Single().ResultId); - Assert.Equal(semanticAnalyzerResults.Single().ResultId, semanticAnalyzerResults2.Single().ResultId); - } + Assert.Equal(syntaxAnalyzerResults.Single().ResultId, syntaxAnalyzerResults2.Single().ResultId); + Assert.Equal(semanticAnalyzerResults.Single().ResultId, semanticAnalyzerResults2.Single().ResultId); + } - private sealed class CSharpSyntaxAnalyzer : DiagnosticAnalyzer - { - public const string RuleId = "SYN0001"; - private readonly DiagnosticDescriptor _descriptor = new(RuleId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private sealed class CSharpSyntaxAnalyzer : DiagnosticAnalyzer + { + public const string RuleId = "SYN0001"; + private readonly DiagnosticDescriptor _descriptor = new(RuleId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_descriptor); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_descriptor); - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxTreeAction(context => - context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Tree.GetRoot(context.CancellationToken).GetLocation()))); - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxTreeAction(context => + context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Tree.GetRoot(context.CancellationToken).GetLocation()))); } + } - private sealed class CSharpSemanticAnalyzer : DiagnosticAnalyzer - { - public const string RuleId = "SEM0001"; - private readonly DiagnosticDescriptor _descriptor = new(RuleId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private sealed class CSharpSemanticAnalyzer : DiagnosticAnalyzer + { + public const string RuleId = "SEM0001"; + private readonly DiagnosticDescriptor _descriptor = new(RuleId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_descriptor); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_descriptor); - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(context => - context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Node.GetLocation())), - CSharp.SyntaxKind.CompilationUnit); - } + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(context => + context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Node.GetLocation())), + CSharp.SyntaxKind.CompilationUnit); } + } - [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65172")] - public async Task TestDocumentDiagnosticsHasVSExpandedMessage(bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65172")] + public async Task TestDocumentDiagnosticsHasVSExpandedMessage(bool mutatingLspWorkspace) + { + var markup = @"internal class Program { static void Main(string[] args) { } }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true); - Assert.Equal("IDE0060", results.Single().Diagnostics.Single().Code); - var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics.Single(); - Assert.Equal(vsDiagnostic.ExpandedMessage, AnalyzersResources.Avoid_unused_parameters_in_your_code_If_the_parameter_cannot_be_removed_then_change_its_name_so_it_starts_with_an_underscore_and_is_optionally_followed_by_an_integer_such_as__comma__1_comma__2_etc_These_are_treated_as_special_discard_symbol_names); - } + Assert.Equal("IDE0060", results.Single().Diagnostics.Single().Code); + var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics.Single(); + Assert.Equal(vsDiagnostic.ExpandedMessage, AnalyzersResources.Avoid_unused_parameters_in_your_code_If_the_parameter_cannot_be_removed_then_change_its_name_so_it_starts_with_an_underscore_and_is_optionally_followed_by_an_integer_such_as__comma__1_comma__2_etc_These_are_treated_as_special_discard_symbol_names); + } - [Theory, CombinatorialData] - public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile_NoCategory(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile_NoCategory(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Empty(results.Single().Diagnostics); - } + Assert.Empty(results.Single().Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile_Category(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile_Category(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics, category: PullDiagnosticCategories.Task); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics, category: PullDiagnosticCategories.Task); - if (useVSDiagnostics) - { - Assert.Equal("TODO", results.Single().Diagnostics.Single().Code); - Assert.Equal("todo: goo", results.Single().Diagnostics.Single().Message); - } - else - { - Assert.Empty(results.Single().Diagnostics); - } + if (useVSDiagnostics) + { + Assert.Equal("TODO", results.Single().Diagnostics.Single().Code); + Assert.Equal("todo: goo", results.Single().Diagnostics.Single().Message); } - - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + else { - var markup = + Assert.Empty(results.Single().Diagnostics); + } + } + + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics)); + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics)); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - } + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var workspace = testLspServer.TestWorkspace; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var workspace = testLspServer.TestWorkspace; - // Calling GetTextBuffer will effectively open the file. - workspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + workspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - // Get the diagnostics for the solution containing the doc. - var solution = document.Project.Solution; + // Get the diagnostics for the solution containing the doc. + var solution = document.Project.Solution; - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics).ConfigureAwait(false); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics).ConfigureAwait(false); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - // Now remove the doc. - workspace.OnDocumentRemoved(workspace.Documents.Single().Id); - await CloseDocumentAsync(testLspServer, document); + // Now remove the doc. + workspace.OnDocumentRemoved(workspace.Documents.Single().Id); + await CloseDocumentAsync(testLspServer, document); - results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, results.Single().ResultId).ConfigureAwait(false); + results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, results.Single().ResultId).ConfigureAwait(false); - Assert.Equal(useVSDiagnostics ? null : [], results.Single().Diagnostics); - Assert.Null(results.Single().ResultId); - } + Assert.Equal(useVSDiagnostics ? null : [], results.Single().Diagnostics); + Assert.Null(results.Single().ResultId); + } - [Theory, CombinatorialData] - public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - var resultId = results.Single().ResultId; - results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: resultId); + var resultId = results.Single().ResultId; + results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: resultId); - Assert.Null(results.Single().Diagnostics); - Assert.Equal(resultId, results.Single().ResultId); - } + Assert.Null(results.Single().Diagnostics); + Assert.Equal(resultId, results.Single().ResultId); + } - [Theory, CombinatorialData] - [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1481208")] - public async Task TestDocumentDiagnosticsWhenGlobalStateChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1481208")] + public async Task TestDocumentDiagnosticsWhenGlobalStateChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - var resultId = results.Single().ResultId; + var resultId = results.Single().ResultId; - // Trigger refresh due to a change to global state that affects diagnostics. - var refresher = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); - refresher.RequestWorkspaceRefresh(); + // Trigger refresh due to a change to global state that affects diagnostics. + var refresher = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + refresher.RequestWorkspaceRefresh(); - results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: resultId); + results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: resultId); - // Result should be different, but diagnostics should be the same - Assert.NotEqual(resultId, results.Single().ResultId); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - } + // Result should be different, but diagnostics should be the same + Assert.NotEqual(resultId, results.Single().ResultId); + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - await InsertTextAsync(testLspServer, document, buffer.CurrentSnapshot.Length, "}"); + await InsertTextAsync(testLspServer, document, buffer.CurrentSnapshot.Length, "}"); - results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, results.Single().ResultId); - Assert.Empty(results[0].Diagnostics); - } + results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, results.Single().ResultId); + Assert.Empty(results[0].Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); + await OpenDocumentAsync(testLspServer, document); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); - buffer.Insert(0, " "); - await InsertTextAsync(testLspServer, document, position: 0, text: " "); + buffer.Insert(0, " "); + await InsertTextAsync(testLspServer, document, position: 0, text: " "); - results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), - useVSDiagnostics, - previousResultId: results[0].ResultId); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 10 }, results[0].Diagnostics.Single().Range.Start); - } + results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), + useVSDiagnostics, + previousResultId: results[0].ResultId); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 10 }, results[0].Diagnostics.Single().Range.Start); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsAreNotMapped(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsAreNotMapped(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"#line 1 ""test.txt"" class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.Equal(1, results.Single().Diagnostics.Single().Range.Start.Line); - } + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal(1, results.Single().Diagnostics.Single().Range.Start.Line); + } - [Theory, CombinatorialData] - public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, useProgress: true); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, useProgress: true); - Assert.Equal("CS1513", results!.Single().Diagnostics.Single().Code); - } + Assert.Equal("CS1513", results!.Single().Diagnostics.Single().Code); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsForOpenFilesUsesActiveContext(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var documentText = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsForOpenFilesUsesActiveContext(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var documentText = @"#if ONE class A { #endif class B {"; - var workspaceXml = + var workspaceXml = @$" {documentText} @@ -453,46 +449,46 @@ class B {"; "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - // Open either of the documents via LSP, we're tracking the URI and text. - await OpenDocumentAsync(testLspServer, csproj1Document); + // Open either of the documents via LSP, we're tracking the URI and text. + await OpenDocumentAsync(testLspServer, csproj1Document); - // This opens all documents in the workspace and ensures buffers are created. - testLspServer.TestWorkspace.GetTestDocument(csproj1Document.Id)!.GetTextBuffer(); + // This opens all documents in the workspace and ensures buffers are created. + testLspServer.TestWorkspace.GetTestDocument(csproj1Document.Id)!.GetTextBuffer(); - // Set CSProj2 as the active context and get diagnostics. - testLspServer.TestWorkspace.SetDocumentContext(csproj2Document.Id); - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj2Document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - if (useVSDiagnostics) - { - // Only VSDiagnostics will have the project. - var vsDiagnostic = (LSP.VSDiagnostic)results.Single().Diagnostics.Single(); - Assert.Equal("CSProj2", vsDiagnostic.Projects.Single().ProjectName); - } - - // Set CSProj1 as the active context and get diagnostics. - testLspServer.TestWorkspace.SetDocumentContext(csproj1Document.Id); - results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); - Assert.Equal(2, results.Single().Diagnostics!.Length); - Assert.All(results.Single().Diagnostics, d => Assert.Equal("CS1513", d.Code)); - - if (useVSDiagnostics) - { - Assert.All(results.Single().Diagnostics, d => Assert.Equal("CSProj1", ((VSDiagnostic)d).Projects.Single().ProjectName)); - } + // Set CSProj2 as the active context and get diagnostics. + testLspServer.TestWorkspace.SetDocumentContext(csproj2Document.Id); + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj2Document.GetURI(), useVSDiagnostics); + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + if (useVSDiagnostics) + { + // Only VSDiagnostics will have the project. + var vsDiagnostic = (LSP.VSDiagnostic)results.Single().Diagnostics.Single(); + Assert.Equal("CSProj2", vsDiagnostic.Projects.Single().ProjectName); } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsHasSameIdentifierForLinkedFile(bool mutatingLspWorkspace) + // Set CSProj1 as the active context and get diagnostics. + testLspServer.TestWorkspace.SetDocumentContext(csproj1Document.Id); + results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); + Assert.Equal(2, results.Single().Diagnostics!.Length); + Assert.All(results.Single().Diagnostics, d => Assert.Equal("CS1513", d.Code)); + + if (useVSDiagnostics) { - var documentText = + Assert.All(results.Single().Diagnostics, d => Assert.Equal("CSProj1", ((VSDiagnostic)d).Projects.Single().ProjectName)); + } + } + + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsHasSameIdentifierForLinkedFile(bool mutatingLspWorkspace) + { + var documentText = @"class A { err }"; - var workspaceXml = + var workspaceXml = @$" {documentText} @@ -502,50 +498,50 @@ public async Task TestDocumentDiagnosticsHasSameIdentifierForLinkedFile(bool mut "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); - var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - // Open either of the documents via LSP, we're tracking the URI and text. - await OpenDocumentAsync(testLspServer, csproj1Document); + // Open either of the documents via LSP, we're tracking the URI and text. + await OpenDocumentAsync(testLspServer, csproj1Document); - var csproj1Results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, GetVsTextDocumentIdentifier(csproj1Document), useVSDiagnostics: true); - var csproj1Diagnostic = (VSDiagnostic)csproj1Results.Single().Diagnostics.Single(); - var csproj2Results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, GetVsTextDocumentIdentifier(csproj2Document), useVSDiagnostics: true); - var csproj2Diagnostic = (VSDiagnostic)csproj2Results.Single().Diagnostics.Single(); - Assert.Equal(csproj1Diagnostic.Identifier, csproj2Diagnostic.Identifier); + var csproj1Results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, GetVsTextDocumentIdentifier(csproj1Document), useVSDiagnostics: true); + var csproj1Diagnostic = (VSDiagnostic)csproj1Results.Single().Diagnostics.Single(); + var csproj2Results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, GetVsTextDocumentIdentifier(csproj2Document), useVSDiagnostics: true); + var csproj2Diagnostic = (VSDiagnostic)csproj2Results.Single().Diagnostics.Single(); + Assert.Equal(csproj1Diagnostic.Identifier, csproj2Diagnostic.Identifier); - static VSTextDocumentIdentifier GetVsTextDocumentIdentifier(Document document) + static VSTextDocumentIdentifier GetVsTextDocumentIdentifier(Document document) + { + var projectContext = new VSProjectContext + { + Id = ProtocolConversions.ProjectIdToProjectContextId(document.Project.Id), + Label = document.Project.Name + }; + return new VSTextDocumentIdentifier { - var projectContext = new VSProjectContext - { - Id = ProtocolConversions.ProjectIdToProjectContextId(document.Project.Id), - Label = document.Project.Name - }; - return new VSTextDocumentIdentifier - { - ProjectContext = projectContext, - Uri = document.GetURI(), - }; - } + ProjectContext = projectContext, + Uri = document.GetURI(), + }; } + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsWithChangeInReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsWithChangeInReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var markup2 = + var markup2 = @"namespace M { public class {|caret:|} { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -556,43 +552,43 @@ public class {|caret:|} { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - await testLspServer.OpenDocumentAsync(csproj1Document.GetURI()); - await testLspServer.OpenDocumentAsync(csproj2Document.GetURI()); - - // Verify we a diagnostic in A.cs since B does not exist. - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); - Assert.Single(results); - Assert.Equal("CS0246", results.Single().Diagnostics.Single().Code); - - // Insert B into B.cs and verify that the error in A.cs is now gone. - var locationToReplace = testLspServer.GetLocations("caret").Single().Range; - await testLspServer.ReplaceTextAsync(csproj2Document.GetURI(), (locationToReplace, "B")); - var originalResultId = results.Single().ResultId; - results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics, originalResultId); - Assert.Single(results); - Assert.Empty(results.Single().Diagnostics); - Assert.NotEqual(originalResultId, results.Single().ResultId); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + await testLspServer.OpenDocumentAsync(csproj1Document.GetURI()); + await testLspServer.OpenDocumentAsync(csproj2Document.GetURI()); + + // Verify we a diagnostic in A.cs since B does not exist. + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); + Assert.Single(results); + Assert.Equal("CS0246", results.Single().Diagnostics.Single().Code); + + // Insert B into B.cs and verify that the error in A.cs is now gone. + var locationToReplace = testLspServer.GetLocations("caret").Single().Range; + await testLspServer.ReplaceTextAsync(csproj2Document.GetURI(), (locationToReplace, "B")); + var originalResultId = results.Single().ResultId; + results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics, originalResultId); + Assert.Single(results); + Assert.Empty(results.Single().Diagnostics); + Assert.NotEqual(originalResultId, results.Single().ResultId); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsWithChangeInNotReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsWithChangeInNotReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var markup2 = + var markup2 = @"namespace M { public class {|caret:|} { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -602,171 +598,171 @@ public class {|caret:|} { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - await testLspServer.OpenDocumentAsync(csproj1Document.GetURI()); - await testLspServer.OpenDocumentAsync(csproj2Document.GetURI()); - - // Verify we get a diagnostic in A since the class B does not exist. - var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); - Assert.Single(results); - Assert.Equal("CS0246", results.Single().Diagnostics.Single().Code); - - // Add B to CSProj2 and verify that we get an unchanged result (still has diagnostic) for A.cs - // since CSProj1 does not reference CSProj2 - var locationToReplace = testLspServer.GetLocations("caret").Single().Range; - await testLspServer.ReplaceTextAsync(csproj2Document.GetURI(), (locationToReplace, "B")); - var originalResultId = results.Single().ResultId; - results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics, originalResultId); - Assert.Single(results); - Assert.Null(results.Single().Diagnostics); - Assert.Equal(originalResultId, results.Single().ResultId); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + await testLspServer.OpenDocumentAsync(csproj1Document.GetURI()); + await testLspServer.OpenDocumentAsync(csproj2Document.GetURI()); + + // Verify we get a diagnostic in A since the class B does not exist. + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); + Assert.Single(results); + Assert.Equal("CS0246", results.Single().Diagnostics.Single().Code); + + // Add B to CSProj2 and verify that we get an unchanged result (still has diagnostic) for A.cs + // since CSProj1 does not reference CSProj2 + var locationToReplace = testLspServer.GetLocations("caret").Single().Range; + await testLspServer.ReplaceTextAsync(csproj2Document.GetURI(), (locationToReplace, "B")); + var originalResultId = results.Single().ResultId; + results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics, originalResultId); + Assert.Single(results); + Assert.Null(results.Single().Diagnostics); + Assert.Equal(originalResultId, results.Single().ResultId); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.RazorLspServer)); + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.RazorLspServer)); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - // Assert that we have diagnostics even though the option is set to push. - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); - } + // Assert that we have diagnostics even though the option is set to push. + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A {"; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.LiveShareLspServer)); + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, + GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.LiveShareLspServer)); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - // Assert that we have diagnostics even though the option is set to push. - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); - } + // Assert that we have diagnostics even though the option is set to push. + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsIncludesSourceGeneratorDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = "// Hello, World"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsIncludesSourceGeneratorDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = "// Hello, World"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - var generator = new DiagnosticProducingGenerator(context => Location.Create(context.Compilation.SyntaxTrees.Single(), new TextSpan(0, 10))); + var generator = new DiagnosticProducingGenerator(context => Location.Create(context.Compilation.SyntaxTrees.Single(), new TextSpan(0, 10))); - testLspServer.TestWorkspace.OnAnalyzerReferenceAdded( - document.Project.Id, - new TestGeneratorReference(generator)); + testLspServer.TestWorkspace.OnAnalyzerReferenceAdded( + document.Project.Id, + new TestGeneratorReference(generator)); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - var diagnostic = Assert.Single(results.Single().Diagnostics); - Assert.Equal(DiagnosticProducingGenerator.Descriptor.Id, diagnostic.Code); - } + var diagnostic = Assert.Single(results.Single().Diagnostics); + Assert.Equal(DiagnosticProducingGenerator.Descriptor.Id, diagnostic.Code); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsWithFadingOptionOn(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsWithFadingOptionOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @" {|first:using System.Linq; using System.Threading;|} class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var firstLocation = testLspServer.GetLocations("first").Single().Range; - testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp, true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var firstLocation = testLspServer.GetLocations("first").Single().Range; + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp, true); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - if (useVSDiagnostics) - { - // We should have an unnecessary diagnostic marking all the usings. - Assert.True(results.Single().Diagnostics![0].Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(firstLocation, results.Single().Diagnostics![1].Range); - - // We should have a regular diagnostic marking all the usings that doesn't fade. - Assert.False(results.Single().Diagnostics![1].Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(firstLocation, results.Single().Diagnostics![1].Range); - } - else - { - // We should have just one diagnostic that fades since the public spec does not support fully hidden diagnostics. - Assert.True(results.Single().Diagnostics![0].Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(firstLocation, results.Single().Diagnostics![0].Range); - } - } + if (useVSDiagnostics) + { + // We should have an unnecessary diagnostic marking all the usings. + Assert.True(results.Single().Diagnostics![0].Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(firstLocation, results.Single().Diagnostics![1].Range); - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsWithFadingOptionOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + // We should have a regular diagnostic marking all the usings that doesn't fade. + Assert.False(results.Single().Diagnostics![1].Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(firstLocation, results.Single().Diagnostics![1].Range); + } + else { - var markup = + // We should have just one diagnostic that fades since the public spec does not support fully hidden diagnostics. + Assert.True(results.Single().Diagnostics![0].Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(firstLocation, results.Single().Diagnostics![0].Range); + } + } + + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsWithFadingOptionOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @" {|first:using System.Linq; using System.Threading;|} class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var firstLocation = testLspServer.GetLocations("first").Single().Range; - testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp, false); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var firstLocation = testLspServer.GetLocations("first").Single().Range; + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp, false); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - Assert.All(results.Single().Diagnostics, d => Assert.False(d.Tags!.Contains(DiagnosticTag.Unnecessary))); - } + Assert.All(results.Single().Diagnostics, d => Assert.False(d.Tags!.Contains(DiagnosticTag.Unnecessary))); + } - [Theory, CombinatorialData] - public async Task TestDocumentDiagnosticsWithNotConfigurableFading(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestDocumentDiagnosticsWithNotConfigurableFading(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @"class A { void M() @@ -775,66 +771,66 @@ void M() 3 + 4{|close:)|}; } }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var openLocation = testLspServer.GetLocations("open").Single().Range; - var closeLocation = testLspServer.GetLocations("close").Single().Range; - var lineLocation = testLspServer.GetLocations("line").Single().Range; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var openLocation = testLspServer.GetLocations("open").Single().Range; + var closeLocation = testLspServer.GetLocations("close").Single().Range; + var lineLocation = testLspServer.GetLocations("line").Single().Range; - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - if (useVSDiagnostics) - { - // The first line should have a diagnostic on it that is not marked as unnecessary. - Assert.False(results.Single().Diagnostics![0].Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(lineLocation, results.Single().Diagnostics![0].Range); - - // The open paren should have an unnecessary diagnostic. - Assert.True(results.Single().Diagnostics![1].Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(openLocation, results.Single().Diagnostics![1].Range); - - // The close paren should have an unnecessary diagnostic. - Assert.True(results.Single().Diagnostics![2].Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(closeLocation, results.Single().Diagnostics![2].Range); - } - else - { - // There should be one unnecessary diagnostic. - Assert.True(results.Single().Diagnostics.Single().Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(lineLocation, results.Single().Diagnostics.Single().Range); + if (useVSDiagnostics) + { + // The first line should have a diagnostic on it that is not marked as unnecessary. + Assert.False(results.Single().Diagnostics![0].Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(lineLocation, results.Single().Diagnostics![0].Range); - // There should be an additional location for the open paren. - Assert.Equal(openLocation, results.Single().Diagnostics.Single().RelatedInformation![0].Location.Range); + // The open paren should have an unnecessary diagnostic. + Assert.True(results.Single().Diagnostics![1].Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(openLocation, results.Single().Diagnostics![1].Range); - // There should be an additional location for the close paren. - Assert.Equal(closeLocation, results.Single().Diagnostics.Single().RelatedInformation![1].Location.Range); - } + // The close paren should have an unnecessary diagnostic. + Assert.True(results.Single().Diagnostics![2].Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(closeLocation, results.Single().Diagnostics![2].Range); } - - [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1806590")] - public async Task TestDocumentDiagnosticsForUnnecessarySuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace) + else { - var markup = "#pragma warning disable IDE0000"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + // There should be one unnecessary diagnostic. + Assert.True(results.Single().Diagnostics.Single().Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(lineLocation, results.Single().Diagnostics.Single().Range); + + // There should be an additional location for the open paren. + Assert.Equal(openLocation, results.Single().Diagnostics.Single().RelatedInformation![0].Location.Range); + + // There should be an additional location for the close paren. + Assert.Equal(closeLocation, results.Single().Diagnostics.Single().RelatedInformation![1].Location.Range); + } + } - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1806590")] + public async Task TestDocumentDiagnosticsForUnnecessarySuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = "#pragma warning disable IDE0000"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - await OpenDocumentAsync(testLspServer, document); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + await OpenDocumentAsync(testLspServer, document); - Assert.Equal(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId, results.Single().Diagnostics.Single().Code); - } + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1824321")] - public async Task TestDocumentDiagnosticsForSourceSuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = @" + Assert.Equal(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId, results.Single().Diagnostics.Single().Code); + } + + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1824321")] + public async Task TestDocumentDiagnosticsForSourceSuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = @" class C { void M() @@ -844,418 +840,418 @@ void M() #pragma warning restore CS0168 // Variable is declared but never used } }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Empty(results.Single().Diagnostics); - } + Assert.Empty(results.Single().Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestInfoDiagnosticsAreReportedAsInformationInVS(bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestInfoDiagnosticsAreReportedAsInformationInVS(bool mutatingLspWorkspace) + { + var markup = @"class A { public A SomeA = new A(); }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: true); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true); - Assert.Equal("IDE0090", results.Single().Diagnostics.Single().Code); - Assert.Equal(LSP.DiagnosticSeverity.Information, results.Single().Diagnostics.Single().Severity); - } + Assert.Equal("IDE0090", results.Single().Diagnostics.Single().Code); + Assert.Equal(LSP.DiagnosticSeverity.Information, results.Single().Diagnostics.Single().Severity); + } - [Theory, CombinatorialData] - public async Task TestInfoDiagnosticsAreReportedAsHintInVSCode(bool mutatingLspWorkspace) - { - var markup = + [Theory, CombinatorialData] + public async Task TestInfoDiagnosticsAreReportedAsHintInVSCode(bool mutatingLspWorkspace) + { + var markup = @"class A { public A SomeA = new A(); }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - await OpenDocumentAsync(testLspServer, document); + await OpenDocumentAsync(testLspServer, document); - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: false); + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: false); - Assert.Equal("IDE0090", results.Single().Diagnostics.Single().Code); - Assert.Equal(LSP.DiagnosticSeverity.Hint, results.Single().Diagnostics.Single().Severity); - } + Assert.Equal("IDE0090", results.Single().Diagnostics.Single().Code); + Assert.Equal(LSP.DiagnosticSeverity.Hint, results.Single().Diagnostics.Single().Severity); + } - #endregion + #endregion - #region Workspace Diagnostics + #region Workspace Diagnostics - [Theory, CombinatorialData] - public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Empty(results); - } + Assert.Empty(results); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); - } + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); + } - [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65967")] - public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace, bool scopeRunCodeAnalysisToProject) - { - var markup1 = + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65967")] + public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace, bool scopeRunCodeAnalysisToProject) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var projectId = scopeRunCodeAnalysisToProject ? testLspServer.GetCurrentSolution().Projects.Single().Id : null; - await testLspServer.RunCodeAnalysisAsync(projectId); + var projectId = scopeRunCodeAnalysisToProject ? testLspServer.GetCurrentSolution().Projects.Single().Id : null; + await testLspServer.RunCodeAnalysisAsync(projectId); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - // this should be considered a build-error, since it was produced by the last code-analysis run. - Assert.Contains(VSDiagnosticTags.BuildError, results[0].Diagnostics.Single().Tags); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + // this should be considered a build-error, since it was produced by the last code-analysis run. + Assert.Contains(VSDiagnosticTags.BuildError, results[0].Diagnostics.Single().Tags); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); - // Now fix the compiler error, but don't re-execute code analysis. - // Verify that we still get the workspace diagnostics from the prior snapshot on which code analysis was executed. - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "}"); + // Now fix the compiler error, but don't re-execute code analysis. + // Verify that we still get the workspace diagnostics from the prior snapshot on which code analysis was executed. + var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); + buffer.Insert(buffer.CurrentSnapshot.Length, "}"); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - Assert.Equal(results.Length, results2.Length); + Assert.Equal(results.Length, results2.Length); - Assert.Equal(results[0].Diagnostics, results2[0].Diagnostics); - // this should be considered a build-error, since it was produced by the last code-analysis run. - Assert.Contains(VSDiagnosticTags.BuildError, results2[0].Diagnostics.Single().Tags); - Assert.Equal(results[1].Diagnostics, results2[1].Diagnostics); - Assert.Equal(results[2].Diagnostics, results2[2].Diagnostics); + Assert.Equal(results[0].Diagnostics, results2[0].Diagnostics); + // this should be considered a build-error, since it was produced by the last code-analysis run. + Assert.Contains(VSDiagnosticTags.BuildError, results2[0].Diagnostics.Single().Tags); + Assert.Equal(results[1].Diagnostics, results2[1].Diagnostics); + Assert.Equal(results[2].Diagnostics, results2[2].Diagnostics); - // Re-run code analysis and verify up-to-date diagnostics are returned now, i.e. there are no compiler errors. - await testLspServer.RunCodeAnalysisAsync(projectId); + // Re-run code analysis and verify up-to-date diagnostics are returned now, i.e. there are no compiler errors. + await testLspServer.RunCodeAnalysisAsync(projectId); - var results3 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results2)); + var results3 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results2)); - Assert.Equal(results.Length, results3.Length); - Assert.Empty(results3[0].Diagnostics); - Assert.Empty(results3[1].Diagnostics); - Assert.Empty(results3[2].Diagnostics); - } + Assert.Equal(results.Length, results3.Length); + Assert.Empty(results3[0].Diagnostics); + Assert.Empty(results3[1].Diagnostics); + Assert.Empty(results3[2].Diagnostics); + } - [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65967")] - public async Task TestWorkspaceDiagnosticsForClosedFilesWithWithRunCodeAnalysisFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace, bool scopeRunCodeAnalysisToProject) - { - var markup1 = + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65967")] + public async Task TestWorkspaceDiagnosticsForClosedFilesWithWithRunCodeAnalysisFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace, bool scopeRunCodeAnalysisToProject) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - // Run code analysis on the initial project snapshot with compiler error. - var projectId = scopeRunCodeAnalysisToProject ? testLspServer.GetCurrentSolution().Projects.Single().Id : null; - await testLspServer.RunCodeAnalysisAsync(projectId); + // Run code analysis on the initial project snapshot with compiler error. + var projectId = scopeRunCodeAnalysisToProject ? testLspServer.GetCurrentSolution().Projects.Single().Id : null; + await testLspServer.RunCodeAnalysisAsync(projectId); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - // this should *not* be considered a build-error, since it was produced by the live workspace results. - Assert.DoesNotContain(VSDiagnosticTags.BuildError, results[0].Diagnostics.Single().Tags); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + // this should *not* be considered a build-error, since it was produced by the live workspace results. + Assert.DoesNotContain(VSDiagnosticTags.BuildError, results[0].Diagnostics.Single().Tags); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); - // Now fix the compiler error, but don't rerun code analysis. - // Verify that we get up-to-date workspace diagnostics, i.e. no compiler errors, from the current snapshot because FSA is enabled. - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "}"); + // Now fix the compiler error, but don't rerun code analysis. + // Verify that we get up-to-date workspace diagnostics, i.e. no compiler errors, from the current snapshot because FSA is enabled. + var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); + buffer.Insert(buffer.CurrentSnapshot.Length, "}"); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - Assert.Equal(results.Length, results2.Length); + Assert.Equal(results.Length, results2.Length); - Assert.Equal(results.Length, results2.Length); - Assert.Empty(results2[0].Diagnostics); - Assert.Empty(results2[1].Diagnostics); - Assert.Empty(results2[2].Diagnostics); + Assert.Equal(results.Length, results2.Length); + Assert.Empty(results2[0].Diagnostics); + Assert.Empty(results2[1].Diagnostics); + Assert.Empty(results2[2].Diagnostics); - // Now rerun code analysis and verify we still get up-to-date workspace diagnostics. - await testLspServer.RunCodeAnalysisAsync(projectId); + // Now rerun code analysis and verify we still get up-to-date workspace diagnostics. + await testLspServer.RunCodeAnalysisAsync(projectId); - var results3 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results2)); + var results3 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results2)); - Assert.Equal(results2.Length, results3.Length); + Assert.Equal(results2.Length, results3.Length); - Assert.Equal(results2[0].Diagnostics, results3[0].Diagnostics); - Assert.Equal(results2[1].Diagnostics, results3[1].Diagnostics); - Assert.Equal(results2[2].Diagnostics, results3[2].Diagnostics); - } + Assert.Equal(results2[0].Diagnostics, results3[0].Diagnostics); + Assert.Equal(results2[1].Diagnostics, results3[1].Diagnostics); + Assert.Equal(results2[2].Diagnostics, results3[2].Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOff(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false, category: PullDiagnosticCategories.Task); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false, category: PullDiagnosticCategories.Task); - Assert.Equal(0, results.Length); - } + Assert.Equal(0, results.Length); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); - if (useVSDiagnostics) - { - Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics.Single().Code); - Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); - Assert.Equal(VSDiagnosticRank.Default, ((VSDiagnostic)results[0].Diagnostics.Single()).DiagnosticRank); - } - else - { - Assert.Empty(results); - } + if (useVSDiagnostics) + { + Assert.Equal(1, results.Length); + Assert.Equal("TODO", results[0].Diagnostics.Single().Code); + Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); + Assert.Equal(VSDiagnosticRank.Default, ((VSDiagnostic)results[0].Diagnostics.Single()).DiagnosticRank); } - - [Theory] - [InlineData("1", (int)VSDiagnosticRank.Low, false)] - [InlineData("1", (int)VSDiagnosticRank.Low, true)] - [InlineData("2", (int)VSDiagnosticRank.Default, false)] - [InlineData("2", (int)VSDiagnosticRank.Default, true)] - [InlineData("3", (int)VSDiagnosticRank.High, false)] - [InlineData("3", (int)VSDiagnosticRank.High, true)] - public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn_Priorities( - string priString, int intRank, bool mutatingLspWorkspace) + else { - var rank = (VSDiagnosticRank)intRank; - var markup1 = + Assert.Empty(results); + } + } + + [Theory] + [InlineData("1", (int)VSDiagnosticRank.Low, false)] + [InlineData("1", (int)VSDiagnosticRank.Low, true)] + [InlineData("2", (int)VSDiagnosticRank.Default, false)] + [InlineData("2", (int)VSDiagnosticRank.Default, true)] + [InlineData("3", (int)VSDiagnosticRank.High, false)] + [InlineData("3", (int)VSDiagnosticRank.High, true)] + public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn_Priorities( + string priString, int intRank, bool mutatingLspWorkspace) + { + var rank = (VSDiagnosticRank)intRank; + var markup1 = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption( - TaskListOptionsStorage.Descriptors, - ImmutableArray.Create("HACK:2", $"TODO:{priString}", "UNDONE:2", "UnresolvedMergeConflict:3")); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption( + TaskListOptionsStorage.Descriptors, + ImmutableArray.Create("HACK:2", $"TODO:{priString}", "UNDONE:2", "UnresolvedMergeConflict:3")); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true, includeTaskListItems: true, category: PullDiagnosticCategories.Task); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true, includeTaskListItems: true, category: PullDiagnosticCategories.Task); - Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics.Single().Code); - Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); - Assert.Equal(rank, ((VSDiagnostic)results[0].Diagnostics.Single()).DiagnosticRank); - } + Assert.Equal(1, results.Length); + Assert.Equal("TODO", results[0].Diagnostics.Single().Code); + Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); + Assert.Equal(rank, ((VSDiagnostic)results[0].Diagnostics.Single()).DiagnosticRank); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOff(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false, category: PullDiagnosticCategories.Task); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false, category: PullDiagnosticCategories.Task); - if (useVSDiagnostics) - { - Assert.Equal(0, results.Length); - } - else - { - Assert.Equal(2, results.Length); - Assert.Empty(results[0].Diagnostics); - Assert.Empty(results[1].Diagnostics); - } + if (useVSDiagnostics) + { + Assert.Equal(0, results.Length); } - - [Theory, CombinatorialData] - public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + else { - var markup1 = + Assert.Equal(2, results.Length); + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); + } + } + + [Theory, CombinatorialData] + public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @" // todo: goo class A { }"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); - if (useVSDiagnostics) - { - Assert.Equal(1, results.Length); + if (useVSDiagnostics) + { + Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics.Single().Code); - Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); - } - else - { - Assert.Equal(2, results.Length); + Assert.Equal("TODO", results[0].Diagnostics.Single().Code); + Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); + } + else + { + Assert.Equal(2, results.Length); - Assert.Empty(results[0].Diagnostics); - Assert.Empty(results[1].Diagnostics); - } + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); } + } - [Theory, CombinatorialData] - public async Task TestWorkspaceTodoAndDiagnosticForClosedFilesWithFSAOnAndTodoOn(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceTodoAndDiagnosticForClosedFilesWithFSAOnAndTodoOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @" // todo: goo class A { "; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); - if (useVSDiagnostics) - { - Assert.Equal(1, results.Length); + if (useVSDiagnostics) + { + Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics![0].Code); - } - else - { - Assert.Equal(2, results.Length); + Assert.Equal("TODO", results[0].Diagnostics![0].Code); + } + else + { + Assert.Equal(2, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics![0].Code); + Assert.Equal("CS1513", results[0].Diagnostics![0].Code); - Assert.Empty(results[1].Diagnostics); - } + Assert.Empty(results[1].Diagnostics); } + } - [Theory, CombinatorialData] - public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOffWithFileInProjectOpen(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOffWithFileInProjectOpen(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var firstDocument = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); - await OpenDocumentAsync(testLspServer, firstDocument); + var firstDocument = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); + await OpenDocumentAsync(testLspServer, firstDocument); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Empty(results); - } + Assert.Empty(results); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsIncludesSourceGeneratorDiagnosticsClosedFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = "// Hello, World"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - markup, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsIncludesSourceGeneratorDiagnosticsClosedFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = "// Hello, World"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + markup, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - var generator = new DiagnosticProducingGenerator(context => Location.Create(context.Compilation.SyntaxTrees.Single(), new TextSpan(0, 10))); + var generator = new DiagnosticProducingGenerator(context => Location.Create(context.Compilation.SyntaxTrees.Single(), new TextSpan(0, 10))); - testLspServer.TestWorkspace.OnAnalyzerReferenceAdded( - document.Project.Id, - new TestGeneratorReference(generator)); + testLspServer.TestWorkspace.OnAnalyzerReferenceAdded( + document.Project.Id, + new TestGeneratorReference(generator)); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(DiagnosticProducingGenerator.Descriptor.Id, results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - } + Assert.Equal(DiagnosticProducingGenerator.Descriptor.Id, results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsDoesNotIncludeSourceGeneratorDiagnosticsClosedFSAOffAndNoFilesOpen(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup = "// Hello, World"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsDoesNotIncludeSourceGeneratorDiagnosticsClosedFSAOffAndNoFilesOpen(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup = "// Hello, World"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var generator = new DiagnosticProducingGenerator( - context => Location.Create( - context.Compilation.SyntaxTrees.Single(), - new TextSpan(0, 10))); + var generator = new DiagnosticProducingGenerator( + context => Location.Create( + context.Compilation.SyntaxTrees.Single(), + new TextSpan(0, 10))); - testLspServer.TestWorkspace.OnAnalyzerReferenceAdded( - testLspServer.GetCurrentSolution().Projects.Single().Id, - new TestGeneratorReference(generator)); + testLspServer.TestWorkspace.OnAnalyzerReferenceAdded( + testLspServer.GetCurrentSolution().Projects.Single().Id, + new TestGeneratorReference(generator)); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Empty(results); - } + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + Assert.Empty(results); + } - [Theory, CombinatorialData] - public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrectLanguage(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var csharpMarkup = + [Theory, CombinatorialData] + public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrectLanguage(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var csharpMarkup = @"class A {"; - var typeScriptMarkup = "???"; + var typeScriptMarkup = "???"; - var workspaceXml = + var workspaceXml = @$" {csharpMarkup} @@ -1265,218 +1261,218 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.False(results.Any(r => r.TextDocument!.Uri.LocalPath.Contains(".ts"))); - } + Assert.False(results.Any(r => r.TextDocument!.Uri.LocalPath.Contains(".ts"))); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestLspServerAsync( - markups: [], mutatingLspWorkspace, - GetInitializationOptions(BackgroundAnalysisScope.FullSolution, CompilerDiagnosticsScope.FullSolution, useVSDiagnostics, sourceGeneratedMarkups: [markup1, markup2])); - - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - - // Project.GetSourceGeneratedDocumentsAsync may not return documents in a deterministic order, so we sort - // the results here to ensure subsequent assertions are not dependent on the order of items provided by the - // project. - results = results.Sort((x, y) => x.Uri.ToString().CompareTo(y.Uri.ToString())); - - Assert.Equal(3, results.Length); - // Since we sorted above by URI the first result is the project. - Assert.Empty(results[0].Diagnostics); - Assert.Equal("CS1513", results[1].Diagnostics.Single().Code); - Assert.Empty(results[2].Diagnostics); - } + var markup2 = ""; + await using var testLspServer = await CreateTestLspServerAsync( + markups: [], mutatingLspWorkspace, + GetInitializationOptions(BackgroundAnalysisScope.FullSolution, CompilerDiagnosticsScope.FullSolution, useVSDiagnostics, sourceGeneratedMarkups: [markup1, markup2])); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + + // Project.GetSourceGeneratedDocumentsAsync may not return documents in a deterministic order, so we sort + // the results here to ensure subsequent assertions are not dependent on the order of items provided by the + // project. + results = results.Sort((x, y) => x.Uri.ToString().CompareTo(y.Uri.ToString())); + + Assert.Equal(3, results.Length); + // Since we sorted above by URI the first result is the project. + Assert.Empty(results[0].Diagnostics); + Assert.Equal("CS1513", results[1].Diagnostics.Single().Code); + Assert.Empty(results[2].Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); - testLspServer.TestWorkspace.OnDocumentRemoved(testLspServer.TestWorkspace.Documents.First().Id); + testLspServer.TestWorkspace.OnDocumentRemoved(testLspServer.TestWorkspace.Documents.First().Id); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - // First doc should show up as removed. - Assert.Equal(3, results2.Length); - Assert.Equal(useVSDiagnostics ? null : [], results2[0].Diagnostics); - Assert.Null(results2[0].ResultId); + // First doc should show up as removed. + Assert.Equal(3, results2.Length); + Assert.Equal(useVSDiagnostics ? null : [], results2[0].Diagnostics); + Assert.Null(results2[0].ResultId); - // Second and third doc should be changed as the project has changed. - Assert.Empty(results2[1].Diagnostics); - Assert.NotEqual(results[1].ResultId, results2[1].ResultId); - Assert.Empty(results2[2].Diagnostics); - Assert.NotEqual(results[2].ResultId, results2[2].ResultId); - } + // Second and third doc should be changed as the project has changed. + Assert.Empty(results2[1].Diagnostics); + Assert.NotEqual(results[1].ResultId, results2[1].ResultId); + Assert.Empty(results2[2].Diagnostics); + Assert.NotEqual(results[2].ResultId, results2[2].ResultId); + } - [Theory, CombinatorialData] - public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - // 'no changes' will be reported as an empty array. - Assert.Empty(results2); - } + // 'no changes' will be reported as an empty array. + Assert.Empty(results2); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "}"); + var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); + buffer.Insert(buffer.CurrentSnapshot.Length, "}"); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - Assert.Equal(3, results2.Length); - Assert.Empty(results2[0].Diagnostics); - // Project has changed, so we re-computed diagnostics as changes in the first file - // may have changed results in the second. - Assert.Empty(results2[1].Diagnostics); - Assert.Empty(results2[2].Diagnostics); + Assert.Equal(3, results2.Length); + Assert.Empty(results2[0].Diagnostics); + // Project has changed, so we re-computed diagnostics as changes in the first file + // may have changed results in the second. + Assert.Empty(results2[1].Diagnostics); + Assert.Empty(results2[2].Diagnostics); - Assert.NotEqual(results[0].ResultId, results2[0].ResultId); - Assert.NotEqual(results[1].ResultId, results2[1].ResultId); - Assert.NotEqual(results[2].ResultId, results2[2].ResultId); - } + Assert.NotEqual(results[0].ResultId, results2[0].ResultId); + Assert.NotEqual(results[1].ResultId, results2[1].ResultId); + Assert.NotEqual(results[2].ResultId, results2[2].ResultId); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(0, " "); + var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); + buffer.Insert(0, " "); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); - var text = await document.GetTextAsync(); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); + var text = await document.GetTextAsync(); - // Hacky, but we need to close the document manually since editing the text-buffer will open it in the - // test-workspace. - testLspServer.TestWorkspace.OnDocumentClosed( - document.Id, TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create()))); + // Hacky, but we need to close the document manually since editing the text-buffer will open it in the + // test-workspace. + testLspServer.TestWorkspace.OnDocumentClosed( + document.Id, TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create()))); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal("CS1513", results2[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 10 }, results2[0].Diagnostics.Single().Range.Start); + Assert.Equal("CS1513", results2[0].Diagnostics.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 10 }, results2[0].Diagnostics.Single().Range.Start); - Assert.Empty(results2[1].Diagnostics); - Assert.NotEqual(results[1].ResultId, results2[1].ResultId); - Assert.Empty(results2[2].Diagnostics); - Assert.NotEqual(results[2].ResultId, results2[2].ResultId); - } + Assert.Empty(results2[1].Diagnostics); + Assert.NotEqual(results[1].ResultId, results2[1].ResultId); + Assert.Empty(results2[2].Diagnostics); + Assert.NotEqual(results[2].ResultId, results2[2].ResultId); + } - [Theory, CombinatorialData] - public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); + Assert.Equal(3, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true); - Assert.Equal("CS1513", results[0].Diagnostics![0].Code); - } + Assert.Equal("CS1513", results[0].Diagnostics![0].Code); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsAreNotMapped(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsAreNotMapped(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"#line 1 ""test.txt"" class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal(ProtocolConversions.CreateAbsoluteUri(@"C:\test1.cs"), results[0].TextDocument!.Uri); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(1, results[0].Diagnostics.Single().Range.Start.Line); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); - } + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + Assert.Equal(3, results.Length); + Assert.Equal(ProtocolConversions.CreateAbsoluteUri(@"C:\test1.cs"), results[0].TextDocument!.Uri); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal(1, results[0].Diagnostics.Single().Range.Start.Line); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWithChangeInReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWithChangeInReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var markup2 = + var markup2 = @"namespace M { public class {|caret:|} { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -1487,56 +1483,56 @@ public class {|caret:|} { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - // Verify we a diagnostic in A.cs since B does not exist - // and a diagnostic in B.cs since it is missing the class name. - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - AssertEx.NotNull(results); - Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); - - // Insert B into B.cs via the workspace. - var caretLocation = testLspServer.GetLocations("caret").First().Range; - var csproj2DocumentText = await csproj2Document.GetTextAsync(); - var newCsProj2Document = csproj2Document.WithText(csproj2DocumentText.WithChanges(new TextChange(ProtocolConversions.RangeToTextSpan(caretLocation, csproj2DocumentText), "B"))); - await testLspServer.TestWorkspace.ChangeDocumentAsync(csproj2Document.Id, newCsProj2Document.Project.Solution); - - // Get updated workspace diagnostics for the change. - var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds); - AssertEx.NotNull(results); - Assert.Equal(4, results.Length); - - // Verify diagnostics for A.cs are updated as the type B now exists. - Assert.Empty(results[0].Diagnostics); - Assert.NotEqual(previousResultIds[0].resultId, results[0].ResultId); - - // Verify diagnostics for B.cs are updated as the class definition is now correct. - Assert.Empty(results[2].Diagnostics); - Assert.NotEqual(previousResultIds[2].resultId, results[2].ResultId); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + // Verify we a diagnostic in A.cs since B does not exist + // and a diagnostic in B.cs since it is missing the class name. + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + AssertEx.NotNull(results); + Assert.Equal(4, results.Length); + Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + + // Insert B into B.cs via the workspace. + var caretLocation = testLspServer.GetLocations("caret").First().Range; + var csproj2DocumentText = await csproj2Document.GetTextAsync(); + var newCsProj2Document = csproj2Document.WithText(csproj2DocumentText.WithChanges(new TextChange(ProtocolConversions.RangeToTextSpan(caretLocation, csproj2DocumentText), "B"))); + await testLspServer.TestWorkspace.ChangeDocumentAsync(csproj2Document.Id, newCsProj2Document.Project.Solution); + + // Get updated workspace diagnostics for the change. + var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds); + AssertEx.NotNull(results); + Assert.Equal(4, results.Length); + + // Verify diagnostics for A.cs are updated as the type B now exists. + Assert.Empty(results[0].Diagnostics); + Assert.NotEqual(previousResultIds[0].resultId, results[0].ResultId); + + // Verify diagnostics for B.cs are updated as the class definition is now correct. + Assert.Empty(results[2].Diagnostics); + Assert.NotEqual(previousResultIds[2].resultId, results[2].ResultId); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWithChangeInRecursiveReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWithChangeInRecursiveReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { public class A { } }"; - var markup2 = + var markup2 = @"namespace M { public class B { } }"; - var markup3 = + var markup3 = @"namespace M { public class {|caret:|} @@ -1544,7 +1540,7 @@ public class {|caret:|} } }"; - var workspaceXml = + var workspaceXml = @$" CSProj2 @@ -1559,65 +1555,65 @@ public class {|caret:|} "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj3Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj3").Single().Documents.First(); - - // Verify we have a diagnostic in C.cs initially. - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - AssertEx.NotNull(results); - Assert.Equal(6, results.Length); - Assert.Empty(results[0].Diagnostics); - Assert.Empty(results[1].Diagnostics); - Assert.Empty(results[2].Diagnostics); - Assert.Empty(results[3].Diagnostics); - Assert.Equal("CS1001", results[4].Diagnostics.Single().Code); - Assert.Empty(results[5].Diagnostics); - - // Insert C into C.cs via the workspace. - var caretLocation = testLspServer.GetLocations("caret").First().Range; - var csproj3DocumentText = await csproj3Document.GetTextAsync().ConfigureAwait(false); - var newCsProj3Document = csproj3Document.WithText(csproj3DocumentText.WithChanges(new TextChange(ProtocolConversions.RangeToTextSpan(caretLocation, csproj3DocumentText), "C"))); - await testLspServer.TestWorkspace.ChangeDocumentAsync(csproj3Document.Id, newCsProj3Document.Project.Solution).ConfigureAwait(false); - - // Get updated workspace diagnostics for the change. - var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds).ConfigureAwait(false); - AssertEx.NotNull(results); - Assert.Equal(6, results.Length); - - // Verify that new diagnostics are returned for all files (even though the diagnostics for the first two files are the same) - // since we re-calculate when transitive project dependencies change. - Assert.Empty(results[0].Diagnostics); - Assert.NotEqual(previousResultIds[0].resultId, results[0].ResultId); - Assert.Empty(results[1].Diagnostics); - Assert.NotEqual(previousResultIds[1].resultId, results[1].ResultId); - - Assert.Empty(results[2].Diagnostics); - Assert.NotEqual(previousResultIds[2].resultId, results[2].ResultId); - Assert.Empty(results[3].Diagnostics); - Assert.NotEqual(previousResultIds[3].resultId, results[3].ResultId); - - Assert.Empty(results[4].Diagnostics); - Assert.NotEqual(previousResultIds[4].resultId, results[4].ResultId); - Assert.Empty(results[5].Diagnostics); - Assert.NotEqual(previousResultIds[5].resultId, results[5].ResultId); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj3Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj3").Single().Documents.First(); + + // Verify we have a diagnostic in C.cs initially. + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + AssertEx.NotNull(results); + Assert.Equal(6, results.Length); + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); + Assert.Empty(results[2].Diagnostics); + Assert.Empty(results[3].Diagnostics); + Assert.Equal("CS1001", results[4].Diagnostics.Single().Code); + Assert.Empty(results[5].Diagnostics); + + // Insert C into C.cs via the workspace. + var caretLocation = testLspServer.GetLocations("caret").First().Range; + var csproj3DocumentText = await csproj3Document.GetTextAsync().ConfigureAwait(false); + var newCsProj3Document = csproj3Document.WithText(csproj3DocumentText.WithChanges(new TextChange(ProtocolConversions.RangeToTextSpan(caretLocation, csproj3DocumentText), "C"))); + await testLspServer.TestWorkspace.ChangeDocumentAsync(csproj3Document.Id, newCsProj3Document.Project.Solution).ConfigureAwait(false); + + // Get updated workspace diagnostics for the change. + var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds).ConfigureAwait(false); + AssertEx.NotNull(results); + Assert.Equal(6, results.Length); + + // Verify that new diagnostics are returned for all files (even though the diagnostics for the first two files are the same) + // since we re-calculate when transitive project dependencies change. + Assert.Empty(results[0].Diagnostics); + Assert.NotEqual(previousResultIds[0].resultId, results[0].ResultId); + Assert.Empty(results[1].Diagnostics); + Assert.NotEqual(previousResultIds[1].resultId, results[1].ResultId); + + Assert.Empty(results[2].Diagnostics); + Assert.NotEqual(previousResultIds[2].resultId, results[2].ResultId); + Assert.Empty(results[3].Diagnostics); + Assert.NotEqual(previousResultIds[3].resultId, results[3].ResultId); + + Assert.Empty(results[4].Diagnostics); + Assert.NotEqual(previousResultIds[4].resultId, results[4].ResultId); + Assert.Empty(results[5].Diagnostics); + Assert.NotEqual(previousResultIds[5].resultId, results[5].ResultId); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWithChangeInNotReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWithChangeInNotReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var markup2 = + var markup2 = @"namespace M { public class {|caret:|} { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -1627,54 +1623,54 @@ public class {|caret:|} { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - // Verify we a diagnostic in A.cs since B does not exist - // and a diagnostic in B.cs since it is missing the class name. - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - AssertEx.NotNull(results); - Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); - Assert.Empty(results[3].Diagnostics); - - // Insert B into B.cs via the workspace. - var caretLocation = testLspServer.GetLocations("caret").First().Range; - var csproj2DocumentText = await csproj2Document.GetTextAsync(); - var newCsProj2Document = csproj2Document.WithText(csproj2DocumentText.WithChanges(new TextChange(ProtocolConversions.RangeToTextSpan(caretLocation, csproj2DocumentText), "B"))); - await testLspServer.TestWorkspace.ChangeDocumentAsync(csproj2Document.Id, newCsProj2Document.Project.Solution); - - // Get updated workspace diagnostics for the change. - var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResultIds); - AssertEx.NotNull(results); - Assert.Equal(2, results.Length); - - // Note: tehre will be no results for A.cs as it is unchanged and does not reference CSProj2. - // Verify that the diagnostics result for B.cs reflects the change we made to it. - Assert.Empty(results[0].Diagnostics); - Assert.NotEqual(previousResultIds[2].resultId, results[0].ResultId); - Assert.Empty(results[1].Diagnostics); - Assert.NotEqual(previousResultIds[3].resultId, results[1].ResultId); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + // Verify we a diagnostic in A.cs since B does not exist + // and a diagnostic in B.cs since it is missing the class name. + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + AssertEx.NotNull(results); + Assert.Equal(4, results.Length); + Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + Assert.Empty(results[3].Diagnostics); + + // Insert B into B.cs via the workspace. + var caretLocation = testLspServer.GetLocations("caret").First().Range; + var csproj2DocumentText = await csproj2Document.GetTextAsync(); + var newCsProj2Document = csproj2Document.WithText(csproj2DocumentText.WithChanges(new TextChange(ProtocolConversions.RangeToTextSpan(caretLocation, csproj2DocumentText), "B"))); + await testLspServer.TestWorkspace.ChangeDocumentAsync(csproj2Document.Id, newCsProj2Document.Project.Solution); + + // Get updated workspace diagnostics for the change. + var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResultIds); + AssertEx.NotNull(results); + Assert.Equal(2, results.Length); + + // Note: tehre will be no results for A.cs as it is unchanged and does not reference CSProj2. + // Verify that the diagnostics result for B.cs reflects the change we made to it. + Assert.Empty(results[0].Diagnostics); + Assert.NotEqual(previousResultIds[2].resultId, results[0].ResultId); + Assert.Empty(results[1].Diagnostics); + Assert.NotEqual(previousResultIds[3].resultId, results[1].ResultId); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWithDependentProjectReloadedAndChanged(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWithDependentProjectReloadedAndChanged(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var markup2 = + var markup2 = @"namespace M { public class {|caret:|} { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -1685,51 +1681,51 @@ public class {|caret:|} { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - // Verify we a diagnostic in A.cs since B does not exist - // and a diagnostic in B.cs since it is missing the class name. - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - AssertEx.NotNull(results); - Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); - - // Change and reload the project via the workspace. - var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); - projectInfo = projectInfo.WithCompilationOptions(projectInfo.CompilationOptions!.WithPlatform(Platform.X64)); - testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); - var operations = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); - await operations.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); - - // Get updated workspace diagnostics for the change. - var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds); - - AssertEx.NotNull(results); - Assert.Equal(4, results.Length); - - // The diagnostics should have been recalculated for both projects as a referenced project changed. - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + // Verify we a diagnostic in A.cs since B does not exist + // and a diagnostic in B.cs since it is missing the class name. + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + AssertEx.NotNull(results); + Assert.Equal(4, results.Length); + Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + + // Change and reload the project via the workspace. + var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); + projectInfo = projectInfo.WithCompilationOptions(projectInfo.CompilationOptions!.WithPlatform(Platform.X64)); + testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); + var operations = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + await operations.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); + + // Get updated workspace diagnostics for the change. + var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds); + + AssertEx.NotNull(results); + Assert.Equal(4, results.Length); + + // The diagnostics should have been recalculated for both projects as a referenced project changed. + Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWithDependentProjectReloadedUnchanged(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWithDependentProjectReloadedUnchanged(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var markup2 = + var markup2 = @"namespace M { public class {|caret:|} { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -1740,44 +1736,44 @@ public class {|caret:|} { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - // Verify we a diagnostic in A.cs since B does not exist - // and a diagnostic in B.cs since it is missing the class name. - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - AssertEx.NotNull(results); - Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); - - // Reload the project via the workspace. - var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); - testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); - var operations = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); - await operations.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); - - // Get updated workspace diagnostics for the change. - var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds); - - // Verify that since no actual changes have been made we report unchanged diagnostics. - // We get an empty array here as this is workspace diagnostics, and we do not report unchanged - // docs there for efficiency. - AssertEx.NotNull(results); - Assert.Empty(results); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + // Verify we a diagnostic in A.cs since B does not exist + // and a diagnostic in B.cs since it is missing the class name. + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + AssertEx.NotNull(results); + Assert.Equal(4, results.Length); + Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + + // Reload the project via the workspace. + var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); + testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); + var operations = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + await operations.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); + + // Get updated workspace diagnostics for the change. + var previousResultIds = CreateDiagnosticParamsFromPreviousReports(results); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResultIds); + + // Verify that since no actual changes have been made we report unchanged diagnostics. + // We get an empty array here as this is workspace diagnostics, and we do not report unchanged + // docs there for efficiency. + AssertEx.NotNull(results); + Assert.Empty(results); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsOrderOfReferencedProjectsReloadedDoesNotMatter(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsOrderOfReferencedProjectsReloadedDoesNotMatter(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"namespace M { class A : B { } }"; - var workspaceXml = + var workspaceXml = @$" {markup1} @@ -1792,40 +1788,40 @@ class A : B { } "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); - - // Verify we a diagnostic in A.cs since B does not exist - // and a diagnostic in B.cs since it is missing the class name. - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - AssertEx.NotNull(results); - Assert.Equal(6, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - - // Reload the project via the workspace. - var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); - testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); - var operations = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); - await operations.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); - - // Get updated workspace diagnostics for the change. - var previousResults = CreateDiagnosticParamsFromPreviousReports(results); - var previousResultIds = previousResults.Select(param => param.resultId).ToImmutableArray(); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResults); - - // Verify that since no actual changes have been made we report unchanged diagnostics. - // We get an empty array here as this is workspace diagnostics, and we do not report unchanged - // docs there for efficiency. - AssertEx.NotNull(results); - Assert.Empty(results); - } + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); + + // Verify we a diagnostic in A.cs since B does not exist + // and a diagnostic in B.cs since it is missing the class name. + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + AssertEx.NotNull(results); + Assert.Equal(6, results.Length); + Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + + // Reload the project via the workspace. + var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); + testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); + var operations = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + await operations.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); + + // Get updated workspace diagnostics for the change. + var previousResults = CreateDiagnosticParamsFromPreviousReports(results); + var previousResultIds = previousResults.Select(param => param.resultId).ToImmutableArray(); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: previousResults); + + // Verify that since no actual changes have been made we report unchanged diagnostics. + // We get an empty array here as this is workspace diagnostics, and we do not report unchanged + // docs there for efficiency. + AssertEx.NotNull(results); + Assert.Empty(results); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsDoesNotThrowIfProjectWithoutFilePathExists(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var csharpMarkup = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsDoesNotThrowIfProjectWithoutFilePathExists(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var csharpMarkup = @"class A {"; - var workspaceXml = + var workspaceXml = @$" {csharpMarkup} @@ -1835,114 +1831,111 @@ public async Task TestWorkspaceDiagnosticsDoesNotThrowIfProjectWithoutFilePathEx "; - await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - Assert.Equal(@"C:/C.cs", results[0].TextDocument.Uri.AbsolutePath); - Assert.Equal(@"C:/CSProj1.csproj", results[1].TextDocument.Uri.AbsolutePath); - Assert.Equal(@"C:/C2.cs", results[2].TextDocument.Uri.AbsolutePath); - } + Assert.Equal(3, results.Length); + Assert.Equal(@"C:/C.cs", results[0].TextDocument.Uri.AbsolutePath); + Assert.Equal(@"C:/CSProj1.csproj", results[1].TextDocument.Uri.AbsolutePath); + Assert.Equal(@"C:/C2.cs", results[2].TextDocument.Uri.AbsolutePath); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWaitsForLspTextChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWaitsForLspTextChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); + var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); - // Assert that the connection isn't closed and task doesn't complete even after some delay. - await Task.Delay(TimeSpan.FromSeconds(5)); - Assert.False(resultTask.IsCompleted); + // Assert that the connection isn't closed and task doesn't complete even after some delay. + await Task.Delay(TimeSpan.FromSeconds(5)); + Assert.False(resultTask.IsCompleted); - // Make an LSP document change that will trigger connection close. - var uri = testLspServer.GetCurrentSolution().Projects.First().Documents.First().GetURI(); - await testLspServer.OpenDocumentAsync(uri); + // Make an LSP document change that will trigger connection close. + var uri = testLspServer.GetCurrentSolution().Projects.First().Documents.First().GetURI(); + await testLspServer.OpenDocumentAsync(uri); - // Assert the task completes after a change occurs - var results = await resultTask; - Assert.NotEmpty(results); - } + // Assert the task completes after a change occurs + var results = await resultTask; + Assert.NotEmpty(results); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - var markup2 = ""; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); + var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); - // Assert that the connection isn't closed and task doesn't complete even after some delay. - await Task.Delay(TimeSpan.FromSeconds(5)); - Assert.False(resultTask.IsCompleted); + // Assert that the connection isn't closed and task doesn't complete even after some delay. + await Task.Delay(TimeSpan.FromSeconds(5)); + Assert.False(resultTask.IsCompleted); - // Make workspace change that will trigger connection close. - var projectInfo = testLspServer.TestWorkspace.Projects.Single().ToProjectInfo(); - testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); + // Make workspace change that will trigger connection close. + var projectInfo = testLspServer.TestWorkspace.Projects.Single().ToProjectInfo(); + testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); - // Assert the task completes after a change occurs - var results = await resultTask; - Assert.NotEmpty(results); - } + // Assert the task completes after a change occurs + var results = await resultTask; + Assert.NotEmpty(results); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(2, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); + Assert.Equal(2, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); - var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); - options.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.OpenFiles); - options.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, CompilerDiagnosticsScope.OpenFiles); + var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.OpenFiles); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, CompilerDiagnosticsScope.OpenFiles); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - Assert.Equal(2, results.Length); - Assert.Empty(results[0].Diagnostics); - Assert.Empty(results[1].Diagnostics); - } + Assert.Equal(2, results.Length); + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); + } - [Theory, CombinatorialData] - public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOffToOn(bool useVSDiagnostics, bool mutatingLspWorkspace) - { - var markup1 = + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOffToOn(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(2, results.Length); - Assert.Empty(results[0].Diagnostics); - Assert.Empty(results[1].Diagnostics); + Assert.Equal(0, results.Length); - var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); - options.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); - options.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, CompilerDiagnosticsScope.FullSolution); + var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); + options.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, CompilerDiagnosticsScope.FullSolution); - results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); - Assert.Equal(2, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Empty(results[1].Diagnostics); - } - - #endregion + Assert.Equal(2, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); } + + #endregion } From 4b284aa4f85f748f435f5a4b9c7b52bee7c66607 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:32:40 -0700 Subject: [PATCH 3/9] Update tests --- .../AbstractPullDiagnosticTestsBase.cs | 6 +----- .../Diagnostics/PullDiagnosticTests.cs | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index fcc57e1a545ce..201f98f4233cf 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -35,12 +35,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics using DocumentDiagnosticPartialReport = SumType; using WorkspaceDiagnosticPartialReport = SumType; - public abstract class AbstractPullDiagnosticTestsBase : AbstractLanguageServerProtocolTests + public abstract class AbstractPullDiagnosticTestsBase(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper) { - protected AbstractPullDiagnosticTestsBase(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - } - private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() { var builder = ImmutableDictionary.CreateBuilder>(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 080a67c9fcf3e..6f319b901cc83 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -1910,8 +1910,22 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(boo results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); Assert.Equal(2, results.Length); - Assert.Empty(results[0].Diagnostics); - Assert.Empty(results[1].Diagnostics); + + Assert.Null(results[0].ResultId); + Assert.Null(results[1].ResultId); + + if (useVSDiagnostics) + { + // In VS we represent removal with a null ResultId and null diagnostics. + Assert.Null(results[0].Diagnostics); + Assert.Null(results[1].Diagnostics); + } + else + { + // In plain LSP we represent removal a null ResultId and with an empty array. + Assert.Empty(results[0].Diagnostics); + Assert.Empty(results[1].Diagnostics); + } } [Theory, CombinatorialData] From 2fe41816be2168cabea54cf4443d9f223bb98100 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:37:03 -0700 Subject: [PATCH 4/9] docs --- .../ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 6f319b901cc83..2441ba3f13099 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -276,6 +276,7 @@ public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostic results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, results.Single().ResultId).ConfigureAwait(false); + // VS represents removal with null diagnostics, VS code represents with an empty diagnostics array. Assert.Equal(useVSDiagnostics ? null : [], results.Single().Diagnostics); Assert.Null(results.Single().ResultId); } @@ -1314,6 +1315,7 @@ public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnosti // First doc should show up as removed. Assert.Equal(3, results2.Length); + // VS represents removal with null diagnostics, VS code represents with an empty diagnostics array. Assert.Equal(useVSDiagnostics ? null : [], results2[0].Diagnostics); Assert.Null(results2[0].ResultId); @@ -1914,15 +1916,14 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(boo Assert.Null(results[0].ResultId); Assert.Null(results[1].ResultId); + // VS represents removal with null diagnostics, VS code represents with an empty diagnostics array. if (useVSDiagnostics) { - // In VS we represent removal with a null ResultId and null diagnostics. Assert.Null(results[0].Diagnostics); Assert.Null(results[1].Diagnostics); } else { - // In plain LSP we represent removal a null ResultId and with an empty array. Assert.Empty(results[0].Diagnostics); Assert.Empty(results[1].Diagnostics); } From 7b5614c34b5661a3e566a6c323e10b24f22c3eb1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:38:20 -0700 Subject: [PATCH 5/9] use collection exprs --- .../Diagnostics/PullDiagnosticTests.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 2441ba3f13099..f7ca6fcf4ecae 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -912,7 +912,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSD @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -926,7 +926,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiag @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -943,7 +943,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFS @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var projectId = scopeRunCodeAnalysisToProject ? testLspServer.GetCurrentSolution().Projects.Single().Id : null; await testLspServer.RunCodeAnalysisAsync(projectId); @@ -990,7 +990,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithWithRunCodeAnalysisF @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); // Run code analysis on the initial project snapshot with compiler error. var projectId = scopeRunCodeAnalysisToProject ? testLspServer.GetCurrentSolution().Projects.Single().Id : null; @@ -1040,7 +1040,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOff(bool useVS class A { }"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false, category: PullDiagnosticCategories.Task); @@ -1056,7 +1056,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn(bool useVSD class A { }"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); @@ -1090,7 +1090,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn_Priorities( class A { }"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption( TaskListOptionsStorage.Descriptors, @@ -1113,7 +1113,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOff(bool useVSD class A { }"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false, category: PullDiagnosticCategories.Task); @@ -1138,7 +1138,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOn(bool useVSDi class A { }"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); @@ -1167,7 +1167,7 @@ public async Task TestWorkspaceTodoAndDiagnosticForClosedFilesWithFSAOnAndTodoOn class A { "; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true, category: PullDiagnosticCategories.Task); @@ -1194,7 +1194,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOffWithFileInPr @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var firstDocument = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); await OpenDocumentAsync(testLspServer, firstDocument); @@ -1300,7 +1300,7 @@ public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnosti @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1333,7 +1333,7 @@ public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagno @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1355,7 +1355,7 @@ public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDia @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1388,7 +1388,7 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1428,7 +1428,7 @@ public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics, bool @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1449,7 +1449,7 @@ public async Task TestWorkspaceDiagnosticsAreNotMapped(bool useVSDiagnostics, bo class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); @@ -1850,7 +1850,7 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspTextChanges(bool useVSDiagn @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); @@ -1874,7 +1874,7 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSD @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); From 9ac239bd6a3439a54521f753d90868b74902af60 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:39:34 -0700 Subject: [PATCH 6/9] Update f# as well --- src/Tools/ExternalAccess/FSharp/FSharpGlobalOptions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Tools/ExternalAccess/FSharp/FSharpGlobalOptions.cs b/src/Tools/ExternalAccess/FSharp/FSharpGlobalOptions.cs index ebb4987c5faca..1735f0020ece9 100644 --- a/src/Tools/ExternalAccess/FSharp/FSharpGlobalOptions.cs +++ b/src/Tools/ExternalAccess/FSharp/FSharpGlobalOptions.cs @@ -36,6 +36,9 @@ public void SetBackgroundAnalysisScope(bool openFilesOnly) _globalOptions.SetGlobalOption( SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.FSharp, openFilesOnly ? BackgroundAnalysisScope.OpenFiles : BackgroundAnalysisScope.FullSolution); + _globalOptions.SetGlobalOption( + SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.FSharp, + openFilesOnly ? CompilerDiagnosticsScope.OpenFiles : CompilerDiagnosticsScope.FullSolution); } } } From f48dac89f612d9253e1142c47e1a87470ebc5815 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:42:34 -0700 Subject: [PATCH 7/9] Cleanup --- .../AbstractPullDiagnosticHandler.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 5da03eafbe67b..aca2e9bd98af7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -160,8 +160,9 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT // Create a mapping from documents to the previous results the client says it has for them. That way as we // process documents we know if we should tell the client it should stay the same, or we can tell it what // the updated diagnostics are. - using var _1 = PooledHashSet.GetInstance(out var removedResults); - var documentToPreviousDiagnosticParams = GetIdToPreviousDiagnosticParams(context, previousResults, removedResults); + using var _1 = PooledDictionary.GetInstance(out var documentToPreviousDiagnosticParams); + using var _2 = PooledHashSet.GetInstance(out var removedResults); + AddIdToPreviousDiagnosticParams(context, previousResults, documentToPreviousDiagnosticParams, removedResults); // First, let the client know if any workspace documents have gone away. That way it can remove those for // the user from squiggles or error-list. @@ -174,7 +175,9 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT context.TraceInformation($"Processing {orderedSources.Length} documents"); - using var _2 = PooledHashSet.GetInstance(out var seenDiagnosticSourceIds); + // Keep track of what diagnostic sources we see this time around. For any we do not see this time + // around, we'll notify the client that the diagnostics for it have been removed. + using var _3 = PooledHashSet.GetInstance(out var seenDiagnosticSourceIds); foreach (var diagnosticSource in orderedSources) { @@ -242,12 +245,13 @@ await ComputeAndReportCurrentDiagnosticsAsync( return CreateReturn(progress); } - private static Dictionary GetIdToPreviousDiagnosticParams( - RequestContext context, ImmutableArray previousResults, HashSet removedDocuments) + private static void AddIdToPreviousDiagnosticParams( + RequestContext context, ImmutableArray previousResults, + Dictionary idToPreviousDiagnosticParams, + HashSet removedDocuments) { Contract.ThrowIfNull(context.Solution); - var result = new Dictionary(); foreach (var diagnosticParams in previousResults) { if (diagnosticParams.TextDocument != null) @@ -255,7 +259,7 @@ private static Dictionary GetIdToPrevio var id = GetIdForPreviousResult(diagnosticParams.TextDocument, context.Solution); if (id != null) { - result[id.Value] = diagnosticParams; + idToPreviousDiagnosticParams[id.Value] = diagnosticParams; } else { @@ -266,7 +270,7 @@ private static Dictionary GetIdToPrevio } } - return result; + return; static ProjectOrDocumentId? GetIdForPreviousResult(TextDocumentIdentifier textDocumentIdentifier, Solution solution) { From 98c5d698a7b16845cc505b40db033e864265169b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:46:31 -0700 Subject: [PATCH 8/9] cleanup --- .../AbstractPullDiagnosticHandler.cs | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index aca2e9bd98af7..7fd19258dc841 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -144,6 +144,8 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT } else { + Contract.ThrowIfNull(context.Solution); + var clientCapabilities = context.GetRequiredClientCapabilities(); var category = GetDiagnosticCategory(diagnosticsParams) ?? ""; var sourceIdentifier = GetDiagnosticSourceIdentifier(diagnosticsParams) ?? ""; @@ -160,13 +162,13 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT // Create a mapping from documents to the previous results the client says it has for them. That way as we // process documents we know if we should tell the client it should stay the same, or we can tell it what // the updated diagnostics are. - using var _1 = PooledDictionary.GetInstance(out var documentToPreviousDiagnosticParams); - using var _2 = PooledHashSet.GetInstance(out var removedResults); - AddIdToPreviousDiagnosticParams(context, previousResults, documentToPreviousDiagnosticParams, removedResults); + using var _1 = PooledDictionary.GetInstance(out var documentIdToPreviousDiagnosticParams); + using var _2 = PooledHashSet.GetInstance(out var removedDocuments); + ProcessPreviousResults(context.Solution, previousResults, documentIdToPreviousDiagnosticParams, removedDocuments); // First, let the client know if any workspace documents have gone away. That way it can remove those for // the user from squiggles or error-list. - HandleRemovedDocuments(context, removedResults, progress); + HandleRemovedDocuments(context, removedDocuments, progress); // Next process each file in priority order. Determine if diagnostics are changed or unchanged since the // last time we notified the client. Report back either to the client so they can update accordingly. @@ -187,7 +189,7 @@ protected virtual Task WaitForChangesAsync(RequestContext context, CancellationT var project = diagnosticSource.GetProject(); var newResultId = await versionedCache.GetNewResultIdAsync( - documentToPreviousDiagnosticParams, + documentIdToPreviousDiagnosticParams, diagnosticSource.GetId(), project, computeCheapVersionAsync: async () => (globalStateVersion, await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false)), @@ -208,7 +210,7 @@ await ComputeAndReportCurrentDiagnosticsAsync( // // Note: if this is a workspace request, we can do nothing, as that will be interpreted by the // client as nothing having been changed for that document. - var previousParams = documentToPreviousDiagnosticParams[diagnosticSource.GetId()]; + var previousParams = documentIdToPreviousDiagnosticParams[diagnosticSource.GetId()]; if (TryCreateUnchangedReport(previousParams.TextDocument, previousParams.PreviousResultId, out var report)) progress.Report(report); } @@ -219,10 +221,10 @@ await ComputeAndReportCurrentDiagnosticsAsync( // each time around, while still producing accurate diagnostic reports. // // Only do this if we haven't already created a removal report for that prior result. - foreach (var (projectOrDocumentId, previousDiagnosticParams) in documentToPreviousDiagnosticParams) + foreach (var (projectOrDocumentId, previousDiagnosticParams) in documentIdToPreviousDiagnosticParams) { if (!seenDiagnosticSourceIds.Contains(projectOrDocumentId) && - !removedResults.Contains(previousDiagnosticParams)) + !removedDocuments.Contains(previousDiagnosticParams)) { progress.Report(CreateRemovedReport(previousDiagnosticParams.TextDocument)); } @@ -243,35 +245,32 @@ await ComputeAndReportCurrentDiagnosticsAsync( } return CreateReturn(progress); - } - private static void AddIdToPreviousDiagnosticParams( - RequestContext context, ImmutableArray previousResults, - Dictionary idToPreviousDiagnosticParams, - HashSet removedDocuments) - { - Contract.ThrowIfNull(context.Solution); - - foreach (var diagnosticParams in previousResults) + static void ProcessPreviousResults( + Solution solution, + ImmutableArray previousResults, + Dictionary idToPreviousDiagnosticParams, + HashSet removedResults) { - if (diagnosticParams.TextDocument != null) + foreach (var diagnosticParams in previousResults) { - var id = GetIdForPreviousResult(diagnosticParams.TextDocument, context.Solution); - if (id != null) - { - idToPreviousDiagnosticParams[id.Value] = diagnosticParams; - } - else + if (diagnosticParams.TextDocument != null) { - // The client previously had a result from us for this document, but we no longer have it in our solution. - // Record it so we can report to the client that it has been removed. - removedDocuments.Add(diagnosticParams); + var id = GetIdForPreviousResult(diagnosticParams.TextDocument, solution); + if (id != null) + { + idToPreviousDiagnosticParams[id.Value] = diagnosticParams; + } + else + { + // The client previously had a result from us for this document, but we no longer have it in our solution. + // Record it so we can report to the client that it has been removed. + removedResults.Add(diagnosticParams); + } } } } - return; - static ProjectOrDocumentId? GetIdForPreviousResult(TextDocumentIdentifier textDocumentIdentifier, Solution solution) { var document = solution.GetDocument(textDocumentIdentifier); From 6ba5c074b24d1858be34b68d79f4db3ba03f514d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Apr 2024 13:48:12 -0700 Subject: [PATCH 9/9] Docs --- .../Handler/Diagnostics/AbstractPullDiagnosticHandler.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 7fd19258dc841..d24329c95636b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -220,7 +220,11 @@ await ComputeAndReportCurrentDiagnosticsAsync( // diagnostics as being removed. This allows for different sets of diagnostic-sources to be computed // each time around, while still producing accurate diagnostic reports. // - // Only do this if we haven't already created a removal report for that prior result. + // Only do this if we haven't already created a removal report for that prior result above. + // + // Note: we are intentionally notifying the client that this is not a remove (vs an empty set of + // results). As far as we and the client are concerned, this document no longer exists at this point + // for the purposes of diagnostics. foreach (var (projectOrDocumentId, previousDiagnosticParams) in documentIdToPreviousDiagnosticParams) { if (!seenDiagnosticSourceIds.Contains(projectOrDocumentId) &&