From 8a03b6338262b3c28fec89ac404b369f0a57d7f6 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 19 Mar 2021 13:34:23 -0700 Subject: [PATCH 1/4] Hot Reload watch service --- .../EditAndContinueWorkspaceServiceTests.cs | 349 ++++++++++++------ .../EditSessionActiveStatementsTests.cs | 2 +- .../RemoteEditAndContinueServiceTests.cs | 4 +- .../MockEditAndContinueWorkspaceService.cs | 9 +- .../EditAndContinue/CommittedSolution.cs | 164 +++++--- .../EditAndContinue/DebuggingSession.cs | 5 +- .../EditAndContinueWorkspaceService.cs | 20 +- .../IEditAndContinueWorkspaceService.cs | 3 +- .../Remote/IRemoteEditAndContinueService.cs | 2 +- .../RemoteEditAndContinueServiceProxy.cs | 6 +- .../Microsoft.CodeAnalysis.Features.csproj | 1 + .../ManagedEditAndContinueLanguageService.cs | 2 +- .../Portable/Workspace/Solution/Document.cs | 2 +- .../RemoteEditAndContinueService.cs | 4 +- 14 files changed, 394 insertions(+), 179 deletions(-) diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index d0da28021ee1c..ce58521132d2b 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -128,10 +128,9 @@ private static Project AddDefaultTestProject( return workspace.CurrentSolution.GetProject(document.Project.Id); } - private EditAndContinueWorkspaceService CreateEditAndContinueService(Workspace workspace) + private EditAndContinueWorkspaceService CreateEditAndContinueService() { return new EditAndContinueWorkspaceService( - workspace, _mockCompilationOutputsProvider, testReportTelemetry: data => EditAndContinueWorkspaceService.LogDebuggingSessionTelemetry(data, (id, message) => _telemetryLog.Add($"{id}: {message.GetMessage()}"), () => ++_telemetryId)); } @@ -163,11 +162,10 @@ private static void EndEditSession(EditAndContinueWorkspaceService service, Immu AssertEx.Equal(documentsWithRudeEdits.NullToEmpty(), documentsToReanalyze); } - private static DebuggingSession StartDebuggingSession(EditAndContinueWorkspaceService service, CommittedSolution.DocumentState initialState = CommittedSolution.DocumentState.MatchesBuildOutput) + private static async Task StartDebuggingSessionAsync(EditAndContinueWorkspaceService service, Solution solution, CommittedSolution.DocumentState initialState = CommittedSolution.DocumentState.MatchesBuildOutput) { - var solution = service.Test_GetWorkspace().CurrentSolution; + await service.StartDebuggingSessionAsync(solution, captureMatchingDocuments: false, CancellationToken.None).ConfigureAwait(false); - service.StartDebuggingSession(solution); var session = service.Test_GetDebuggingSession(); if (initialState != CommittedSolution.DocumentState.None) { @@ -264,6 +262,11 @@ private Guid EmitLibrary( compilation = outputCompilation; } + return EmitLibrary(compilation, pdbFormat); + } + + private Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFormat = DebugInformationFormat.PortablePdb) + { var (peImage, pdbImage) = compilation.EmitToArrays(new EmitOptions(debugInformationFormat: pdbFormat)); var symReader = SymReaderTestHelpers.OpenDummySymReader(pdbImage); @@ -319,6 +322,149 @@ private static void VerifyReadersDisposed(IEnumerable readers) } } + private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, string name = "design-time-only.cs", string path = "design-time-only.cs") + => DocumentInfo.Create( + DocumentId.CreateNewId(projectId, name), + name: name, + folders: Array.Empty(), + sourceCodeKind: SourceCodeKind.Regular, + loader: TextLoader.From(TextAndVersion.Create(SourceText.From("class DTO {}"), VersionStamp.Create(), path)), + filePath: path, + isGenerated: false, + designTimeOnly: true, + documentServiceProvider: null); + + internal sealed class FailingTextLoader : TextLoader + { + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + { + Assert.True(false, $"Content of document {documentId} should never be loaded"); + throw ExceptionUtilities.Unreachable; + } + } + + [Fact] + public async Task StartDebuggingSession_CapturingDocuments() + { + var encodingA = Encoding.BigEndianUnicode; + var encodingB = Encoding.Unicode; + var encodingC = Encoding.GetEncoding("SJIS"); + var encodingE = Encoding.UTF8; + + var sourceA1 = "class A {}"; + var sourceB1 = "class B { int F() => 1; }"; + var sourceB2 = "class B { int F() => 2; }"; + var sourceB3 = "class B { int F() => 3; }"; + var sourceC1 = "class C { const char L = 'ワ'; }"; + var sourceD1 = "dummy code"; + var sourceE1 = "class E { }"; + var sourceBytesA1 = encodingA.GetBytes(sourceA1); + var sourceBytesB1 = encodingB.GetBytes(sourceB1); + var sourceBytesC1 = encodingC.GetBytes(sourceC1); + var sourceBytesE1 = encodingE.GetBytes(sourceE1); + + var dir = Temp.CreateDirectory(); + var sourceFileA = dir.CreateFile("A.cs").WriteAllBytes(sourceBytesA1); + var sourceFileB = dir.CreateFile("B.cs").WriteAllBytes(sourceBytesB1); + var sourceFileC = dir.CreateFile("C.cs").WriteAllBytes(sourceBytesC1); + var sourceFileD = dir.CreateFile("dummy").WriteAllText(sourceD1); + var sourceFileE = dir.CreateFile("E.cs").WriteAllBytes(sourceBytesE1); + var sourceTreeA1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesA1, sourceBytesA1.Length, encodingA, SourceHashAlgorithm.Sha256), TestOptions.Regular, sourceFileA.Path); + var sourceTreeB1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesB1, sourceBytesB1.Length, encodingB, SourceHashAlgorithm.Sha256), TestOptions.Regular, sourceFileB.Path); + var sourceTreeC1 = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceBytesC1, sourceBytesC1.Length, encodingC, SourceHashAlgorithm.Sha1), TestOptions.Regular, sourceFileC.Path); + + // E is not included in the compilation: + var compilation = CSharpTestBase.CreateCompilation(new[] { sourceTreeA1, sourceTreeB1, sourceTreeC1 }, options: TestOptions.DebugDll, targetFramework: DefaultTargetFramework, assemblyName: "P"); + EmitLibrary(compilation); + + // change content of B on disk: + sourceFileB.WriteAllText(sourceB2, encodingB); + + // prepare workspace as if it was loaded from project files: + using var workspace = CreateWorkspace(new[] { typeof(DummyLanguageService) }); + + var solution = workspace.CurrentSolution; + + var projectP = solution.AddProject("P", "P", LanguageNames.CSharp); + solution = projectP.Solution; + + var documentIdA = DocumentId.CreateNewId(projectP.Id, debugName: "A"); + solution = solution.AddDocument(DocumentInfo.Create( + id: documentIdA, + name: "A", + loader: new FileTextLoader(sourceFileA.Path, encodingA), + filePath: sourceFileA.Path)); + + var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); + solution = solution.AddDocument(DocumentInfo.Create( + id: documentIdB, + name: "B", + loader: new FileTextLoader(sourceFileB.Path, encodingB), + filePath: sourceFileB.Path)); + + var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); + solution = solution.AddDocument(DocumentInfo.Create( + id: documentIdC, + name: "C", + loader: new FileTextLoader(sourceFileC.Path, encodingC), + filePath: sourceFileC.Path)); + + var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); + solution = solution.AddDocument(DocumentInfo.Create( + id: documentIdE, + name: "E", + loader: new FileTextLoader(sourceFileE.Path, encodingE), + filePath: sourceFileE.Path)); + + // check that are testing documents whose hash algorithm does not match the PDB (but the hash itself does): + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdA).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdB).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdC).GetTextSynchronously(default).ChecksumAlgorithm); + Assert.Equal(SourceHashAlgorithm.Sha1, solution.GetDocument(documentIdE).GetTextSynchronously(default).ChecksumAlgorithm); + + // design-time-only document with and without absolute path: + solution = solution. + AddDocument(CreateDesignTimeOnlyDocument(projectP.Id, name: "dt1.cs", path: Path.Combine(dir.Path, "dt1.cs"))). + AddDocument(CreateDesignTimeOnlyDocument(projectP.Id, name: "dt2.cs", path: "dt2.cs")); + + // project that does not support EnC - the contents of documents in this project shouldn't be loaded: + var projectQ = solution.AddProject("Q", "Q", DummyLanguageService.LanguageName); + solution = projectQ.Solution; + + solution = solution.AddDocument(DocumentInfo.Create( + id: DocumentId.CreateNewId(projectQ.Id, debugName: "D"), + name: "D", + loader: new FailingTextLoader(), + filePath: sourceFileD.Path)); + + var service = CreateEditAndContinueService(); + + await service.StartDebuggingSessionAsync(solution, captureMatchingDocuments: true, CancellationToken.None).ConfigureAwait(false); + + var debuggingSession = service.Test_GetDebuggingSession(); + + var matchingDocuments = debuggingSession.LastCommittedSolution.Test_GetDocumentStates(); + AssertEx.Equal(new[] + { + "(A, MatchesBuildOutput)", + "(C, MatchesBuildOutput)" + }, matchingDocuments.Select(e => (solution.GetDocument(e.id).Name, e.state)).OrderBy(e => e.Name).Select(e => e.ToString())); + + // change content of B on disk again: + sourceFileB.WriteAllText(sourceB3, encodingB); + solution = solution.WithDocumentTextLoader(documentIdB, new FileTextLoader(sourceFileB.Path, encodingB), PreservationMode.PreserveValue); + + StartEditSession(service); + + var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(solution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); + Assert.Empty(updates.Updates); + AssertEx.Equal(new[] { $"{projectP.Id} Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFileB.Path)}" }, InspectDiagnostics(emitDiagnostics)); + + EndEditSession(service); + EndDebuggingSession(service); + } + [Fact] public async Task RunMode_ProjectThatDoesNotSupportEnC() { @@ -328,9 +474,9 @@ public async Task RunMode_ProjectThatDoesNotSupportEnC() var document = project.AddDocument("test", SourceText.From("dummy1")); workspace.ChangeSolution(document.Project.Solution); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); @@ -353,23 +499,14 @@ public async Task RunMode_DesignTimeOnlyDocument() using var workspace = CreateWorkspace(); var project = AddDefaultTestProject(workspace, "class C1 { void M() { System.Console.WriteLine(1); } }"); - var documentInfo = DocumentInfo.Create( - DocumentId.CreateNewId(project.Id), - name: "design-time-only.cs", - folders: Array.Empty(), - sourceCodeKind: SourceCodeKind.Regular, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From("class C2 {}"), VersionStamp.Create(), "design-time-only.cs")), - filePath: "design-time-only.cs", - isGenerated: false, - designTimeOnly: true, - documentServiceProvider: null); + var documentInfo = CreateDesignTimeOnlyDocument(project.Id); workspace.ChangeSolution(project.Solution.WithProjectOutputFilePath(project.Id, moduleFile.Path).AddDocument(documentInfo)); _mockCompilationOutputsProvider = _ => new CompilationOutputFiles(moduleFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // update a design-time-only source file: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(d => d.Id == documentInfo.Id); @@ -400,9 +537,9 @@ public async Task RunMode_ProjectNotBuilt() _mockCompilationOutputsProvider = _ => new MockCompilationOutputs(Guid.Empty); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); @@ -430,9 +567,9 @@ public async Task RunMode_ErrorReadingModuleFile() _mockCompilationOutputsProvider = _ => new CompilationOutputFiles(moduleFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); @@ -465,14 +602,14 @@ public async Task RunMode_DocumentOutOfSync() using var workspace = CreateWorkspace(); var project = AddDefaultTestProject(workspace, "class C1 { void M() { System.Console.WriteLine(1); } }"); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); workspace.ChangeSolution(project.Solution.WithProjectOutputFilePath(project.Id, moduleFile.Path)); _mockCompilationOutputsProvider = _ => new CompilationOutputFiles(moduleFile.Path); var document1 = project.Documents.Single(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); debuggingSession.LastCommittedSolution.Test_SetDocumentState(document1.Id, CommittedSolution.DocumentState.OutOfSync); // no changes: @@ -523,9 +660,9 @@ public async Task RunMode_FileAdded() EmitAndLoadLibraryToDebuggee(sourceA, sourceFilePath: sourceFileA.Path); var project = documentA.Project; - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // add a source file: var documentB = project.AddDocument("file2.cs", SourceText.From(sourceB), filePath: sourceFileB.Path); @@ -561,11 +698,11 @@ public async Task RunMode_Diagnostics() _mockCompilationOutputsProvider = _ => new CompilationOutputFiles(moduleFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); @@ -610,9 +747,9 @@ public async Task RunMode_DifferentDocumentWithSameContent() workspace.ChangeSolution(project.Solution.WithProjectOutputFilePath(project.Id, moduleFile.Path)); _mockCompilationOutputsProvider = _ => new CompilationOutputFiles(moduleFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // update the document var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); @@ -646,9 +783,9 @@ public async Task BreakMode_ProjectThatDoesNotSupportEnC() var document = project.AddDocument("test", SourceText.From("dummy1")); workspace.ChangeSolution(document.Project.Solution); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); // change the source: @@ -687,9 +824,9 @@ public async Task BreakMode_DesignTimeOnlyDocument_Dynamic() var solution = workspace.CurrentSolution.AddDocument(documentInfo); workspace.ChangeSolution(solution); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); // change the source: @@ -741,9 +878,9 @@ public async Task BreakMode_DesignTimeOnlyDocument_Wpf(bool delayLoad) LoadLibraryToDebuggee(moduleId); } - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + await service.StartDebuggingSessionAsync(workspace.CurrentSolution, captureMatchingDocuments: false, CancellationToken.None).ConfigureAwait(false); StartEditSession(service); @@ -804,9 +941,9 @@ public async Task BreakMode_ErrorReadingModuleFile() _mockCompilationOutputsProvider = _ => new CompilationOutputFiles(moduleFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); // change the source: @@ -865,8 +1002,8 @@ public async Task BreakMode_ErrorReadingPdbFile() } }; - var service = CreateEditAndContinueService(workspace); - StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source: @@ -914,8 +1051,8 @@ public async Task BreakMode_ErrorReadingSourceFile() var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); - var service = CreateEditAndContinueService(workspace); - StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source: @@ -978,9 +1115,9 @@ public async Task BreakMode_FileAdded() var project = documentA.Project; - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); // add a source file: @@ -1039,9 +1176,9 @@ void M() LoadLibraryToDebuggee(moduleId, new ManagedEditAndContinueAvailability(ManagedEditAndContinueAvailabilityStatus.NotAllowedForRuntime, "*message*")); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1101,8 +1238,8 @@ public async Task BreakMode_Encodings() var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path, encoding: encoding); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1128,9 +1265,9 @@ public async Task BreakMode_RudeEdits() var project = AddDefaultTestProject(workspace, "class C1 { void M() { System.Console.WriteLine(1); } }"); _mockCompilationOutputsProvider = _ => new MockCompilationOutputs(moduleId); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); @@ -1183,9 +1320,9 @@ class C { int Y => 2; } using var workspace = CreateWorkspace(); var project = AddDefaultTestProject(workspace, sourceV1, generator: generator); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); // change the source: @@ -1234,8 +1371,8 @@ public async Task BreakMode_RudeEdits_DocumentOutOfSync() var document1 = project.AddDocument("a.cs", SourceText.From(source1, Encoding.UTF8), filePath: sourceFile.Path); workspace.ChangeSolution(document1.Project.Solution); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1307,10 +1444,10 @@ public async Task BreakMode_RudeEdits_DocumentWithoutSequencePoints() var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); // do not initialize the document state - we will detect the state based on the PDB content. - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1356,10 +1493,10 @@ public async Task BreakMode_RudeEdits_DelayLoadedModule() var moduleId = EmitLibrary(source1, sourceFilePath: sourceFile.Path); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); // do not initialize the document state - we will detect the state based on the PDB content. - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service); @@ -1409,9 +1546,9 @@ public async Task BreakMode_SyntaxError() var project = AddDefaultTestProject(workspace, "class C1 { void M() { System.Console.WriteLine(1); } }"); _mockCompilationOutputsProvider = _ => new MockCompilationOutputs(moduleId); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); @@ -1454,9 +1591,9 @@ public async Task BreakMode_SemanticError() var moduleId = EmitAndLoadLibraryToDebuggee(sourceV1); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1509,9 +1646,9 @@ public async Task BreakMode_FileStatus_CompilationError() AddDocument("Common.cs", "class Common {}", filePath: "Common.cs").Project. AddDocument("C.cs", "class C {}", filePath: "C.cs").Project.Solution); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); StartEditSession(service); // change C.cs to have a compilation error: @@ -1542,9 +1679,9 @@ public async Task BreakMode_ValidSignificantChange_EmitError() var project = AddDefaultTestProject(workspace, sourceV1); EmitAndLoadLibraryToDebuggee(sourceV1); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1629,8 +1766,8 @@ public async Task BreakMode_ValidSignificantChange_ApplyBeforeFileWatcherEvent(b var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1712,8 +1849,8 @@ public async Task BreakMode_ValidSignificantChange_FileUpdateNotObservedBeforeDe var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1781,8 +1918,8 @@ public async Task BreakMode_ValidSignificantChange_AddedFileNotObservedBeforeDeb var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); // An active statement may be present in the added file since the file exists in the PDB: var activeInstruction1 = new ManagedInstructionId(new ManagedMethodId(moduleId, token: 0x06000001, version: 1), ilOffset: 1); @@ -1859,9 +1996,9 @@ public async Task BreakMode_ValidSignificantChange_DocumentOutOfSync(bool delayL LoadLibraryToDebuggee(moduleId); } - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service, initialState: CommittedSolution.DocumentState.None); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); StartEditSession(service); @@ -1905,8 +2042,8 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful(bool commitUpd var moduleId = EmitAndLoadLibraryToDebuggee(sourceV1); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2026,9 +2163,9 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful_UpdateDeferred sourceSpan: new SourceSpan(0, 15, 0, 16), ActiveStatementFlags.IsLeafFrame)); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // module is not loaded: var editSession = StartEditSession(service, activeStatements, loadedModules: _loadedModulesProvider); @@ -2157,8 +2294,8 @@ partial class C { int Y = 2; } LoadLibraryToDebuggee(EmitLibrary(new[] { (sourceA1, "test1.cs"), (sourceB1, "test2.cs") })); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2264,8 +2401,8 @@ class C { int Y => 2; } var moduleId = EmitLibrary(sourceV1, generator: generator); LoadLibraryToDebuggee(moduleId); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2324,8 +2461,8 @@ int M() var moduleId = EmitLibrary(sourceV1, generator: generator); LoadLibraryToDebuggee(moduleId); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2372,8 +2509,8 @@ partial class C { int X = 1; } var moduleId = EmitLibrary(sourceV1, generator: generator); LoadLibraryToDebuggee(moduleId); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2419,8 +2556,8 @@ class C { int Y => 1; } var moduleId = EmitLibrary(source, generator: generator, additionalFileText: additionalSourceV1); LoadLibraryToDebuggee(moduleId); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the additional source (valid edit): @@ -2462,8 +2599,8 @@ class C { int Y => 1; } var moduleId = EmitLibrary(source, generator: generator, analyzerOptions: configV1); LoadLibraryToDebuggee(moduleId); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the additional source (valid edit): @@ -2503,8 +2640,8 @@ public async Task BreakMode_ValidSignificantChange_SourceGenerators_DocumentRemo var moduleId = EmitLibrary(source1, generator: generator); LoadLibraryToDebuggee(moduleId); - var service = CreateEditAndContinueService(workspace); - var debuggingSession = StartDebuggingSession(service); + var service = CreateEditAndContinueService(); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // remove the source document (valid edit): @@ -2569,9 +2706,9 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() // only module A is loaded LoadLibraryToDebuggee(moduleIdA); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2761,9 +2898,9 @@ public async Task BreakMode_ValidSignificantChange_BaselineCreationFailed_NoStre OpenAssemblyStreamImpl = () => null, }; - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // module not loaded StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2798,9 +2935,9 @@ public async Task BreakMode_ValidSignificantChange_BaselineCreationFailed_Assemb OpenAssemblyStreamImpl = () => throw new IOException("*message*"), }; - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - StartDebuggingSession(service); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // module not loaded StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2855,12 +2992,12 @@ public async Task ActiveStatements() var adjustedActiveLineSpan1 = sourceTextV2.Lines.GetLinePositionSpan(adjustedActiveSpan1); var adjustedActiveLineSpan2 = sourceTextV2.Lines.GetLinePositionSpan(adjustedActiveSpan2); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); // default if called outside of edit session Assert.True((await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false)).IsDefault); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); // default if called outside of edit session Assert.True((await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false)).IsDefault); @@ -2959,9 +3096,11 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy var activeLineSpan11 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan11); var activeLineSpan12 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan12); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service, + var debuggingSession = await StartDebuggingSessionAsync( + service, + workspace.CurrentSolution, isOutOfSync ? CommittedSolution.DocumentState.OutOfSync : CommittedSolution.DocumentState.MatchesBuildOutput); var moduleId = Guid.NewGuid(); @@ -3020,9 +3159,9 @@ public async Task ActiveStatements_ForeignDocument() var document = project.AddDocument("test", SourceText.From("dummy1")); workspace.ChangeSolution(document.Project.Solution); - var service = CreateEditAndContinueService(workspace); + var service = CreateEditAndContinueService(); - var debuggingSession = StartDebuggingSession(service); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); var activeStatements = ImmutableArray.Create( new ManagedActiveStatementDebugInfo( diff --git a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs index 79419d91135b2..225fca9c5c961 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs @@ -113,7 +113,7 @@ public Validator( var mockCompilationOutputsProvider = new Func(_ => new MockCompilationOutputs(Guid.NewGuid())); - var debuggingSession = new DebuggingSession(solution, mockCompilationOutputsProvider); + var debuggingSession = new DebuggingSession(solution, mockCompilationOutputsProvider, SpecializedCollections.EmptyEnumerable>()); if (initialState != CommittedSolution.DocumentState.None) { diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 8ea8ba78fbf56..e55f2865bae18 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -132,13 +132,13 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) // StartDebuggingSession var called = false; - mockEncService.StartDebuggingSessionImpl = solution => + mockEncService.StartDebuggingSessionImpl = (solution, captureMatchingDocuments) => { Assert.Equal("proj", solution.Projects.Single().Name); called = true; }; - await proxy.StartDebuggingSessionAsync(localWorkspace.CurrentSolution, CancellationToken.None).ConfigureAwait(false); + await proxy.StartDebuggingSessionAsync(localWorkspace.CurrentSolution, captureMatchingDocuments: false, CancellationToken.None).ConfigureAwait(false); Assert.True(called); // StartEditSession diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs index 63e7fd1295175..a252a4c6ffb0b 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs @@ -21,7 +21,7 @@ internal class MockEditAndContinueWorkspaceService : IEditAndContinueWorkspaceSe public Func? GetCurrentActiveStatementPositionImpl; public Func>? GetAdjustedActiveStatementSpansImpl; - public Action? StartDebuggingSessionImpl; + public Action? StartDebuggingSessionImpl; public StartEditSession? StartEditSessionImpl; public EndSession? EndDebuggingSessionImpl; public EndSession? EndEditSessionImpl; @@ -76,8 +76,11 @@ public ValueTask HasChangesAsync(Solution solution, SolutionActiveStatemen public void OnSourceFileUpdated(Document document) => OnSourceFileUpdatedImpl?.Invoke(document); - public void StartDebuggingSession(Solution solution) - => StartDebuggingSessionImpl?.Invoke(solution); + public ValueTask StartDebuggingSessionAsync(Solution solution, bool captureMatchingDocuments, CancellationToken cancellationToken) + { + StartDebuggingSessionImpl?.Invoke(solution, captureMatchingDocuments); + return default; + } public void StartEditSession(IManagedEditAndContinueDebuggerService debuggerService, out ImmutableArray documentsToReanalyze) { diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index 293f11fbbc221..3d807b88e655a 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -14,6 +14,8 @@ using Roslyn.Utilities; using System.IO; using System.Text; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Emit; namespace Microsoft.CodeAnalysis.EditAndContinue { @@ -88,11 +90,12 @@ internal enum DocumentState private readonly object _guard = new(); - public CommittedSolution(DebuggingSession debuggingSession, Solution solution) + public CommittedSolution(DebuggingSession debuggingSession, Solution solution, IEnumerable> initialDocumentStates) { _solution = solution; _debuggingSession = debuggingSession; _documentState = new Dictionary(); + _documentState.AddRange(initialDocumentStates); } // test only @@ -104,6 +107,15 @@ internal void Test_SetDocumentState(DocumentId documentId, DocumentState state) } } + // test only + internal ImmutableArray<(DocumentId id, DocumentState state)> Test_GetDocumentStates() + { + lock (_guard) + { + return _documentState.SelectAsArray(e => (e.Key, e.Value)); + } + } + public bool HasNoChanges(Solution solution) => _solution == solution; @@ -200,7 +212,7 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel return (null, DocumentState.None); } - if (!PathUtilities.IsAbsolute(document.FilePath)) + if (!EditAndContinueWorkspaceService.SupportsEditAndContinue(document.DocumentState)) { return (null, DocumentState.DesignTimeOnly); } @@ -208,9 +220,21 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var sourceTextVersion = (committedDocument == null) ? await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false) : default; - var (matchingSourceText, pdbHasDocument) = await Task.Run( - () => TryGetPdbMatchingSourceText(document.FilePath, sourceText.Encoding, document.Project), - cancellationToken).ConfigureAwait(false); + // run file IO on a background thread: + var (matchingSourceText, pdbHasDocument) = await Task.Run(() => + { + var compilationOutputs = _debuggingSession.GetCompilationOutputs(document.Project); + using var debugInfoReaderProvider = GetMethodDebugInfoReader(compilationOutputs, document.Project.Name); + if (debugInfoReaderProvider == null) + { + return (null, null); + } + + var debugInfoReader = debugInfoReaderProvider.CreateEditAndContinueMethodDebugInfoReader(); + + Contract.ThrowIfNull(document.FilePath); + return TryGetPdbMatchingSourceText(debugInfoReader, document.FilePath, sourceText.Encoding); + }, cancellationToken).ConfigureAwait(false); lock (_guard) { @@ -283,6 +307,79 @@ public Task OnSourceFileUpdatedAsync(Document document, CancellationToken cancel } } + internal static async Task>> GetMatchingDocumentsAsync(Solution solution, Func compilationOutputsProvider, CancellationToken cancellationToken) + { + var projectTasks = solution.Projects.Select(async project => + { + using var debugInfoReaderProvider = GetMethodDebugInfoReader(compilationOutputsProvider(project), project.Name); + if (debugInfoReaderProvider == null) + { + return Array.Empty(); + } + + // Skip projects that do not support Roslyn EnC (e.g. F#, etc). + // Source files of these do not even need to be captured in the solution snapshot. + if (!EditAndContinueWorkspaceService.SupportsEditAndContinue(project)) + { + return Array.Empty(); + } + + var debugInfoReader = debugInfoReaderProvider.CreateEditAndContinueMethodDebugInfoReader(); + + var documentTasks = project.State.DocumentStates.States.Select(async documentState => + { + cancellationToken.ThrowIfCancellationRequested(); + + if (EditAndContinueWorkspaceService.SupportsEditAndContinue(documentState)) + { + var sourceFilePath = documentState.FilePath; + Contract.ThrowIfNull(sourceFilePath); + + // Hydrate the solution snapshot with the content of the file. + // It's important to do this before we start watching for changes so that we have a baseline we can compare future snapshots to. + var sourceText = await documentState.GetTextAsync(cancellationToken).ConfigureAwait(false); + + // TODO: https://github.com/dotnet/roslyn/issues/51993 + // avoid rereading the file in common case - the workspace should create source texts with the right checksum algorithm and encoding + var (source, hasDocument) = TryGetPdbMatchingSourceText(debugInfoReader, sourceFilePath, sourceText.Encoding); + if (source != null) + { + return documentState.Id; + } + } + + return null; + }); + + return await Task.WhenAll(documentTasks).ConfigureAwait(false); + }); + + var documentIdArrays = await Task.WhenAll(projectTasks).ConfigureAwait(false); + + return documentIdArrays.SelectMany(ids => ids.WhereNotNull()).Select(id => KeyValuePairUtil.Create(id, DocumentState.MatchesBuildOutput)); + } + + private static DebugInformationReaderProvider? GetMethodDebugInfoReader(CompilationOutputs compilationOutputs, string projectName) + { + DebugInformationReaderProvider? debugInfoReaderProvider; + try + { + debugInfoReaderProvider = compilationOutputs.OpenPdb(); + + if (debugInfoReaderProvider == null) + { + EditAndContinueWorkspaceService.Log.Write("Source file of project '{0}' doesn't match output PDB: PDB '{1}' not found", projectName, compilationOutputs.PdbDisplayPath); + } + + return debugInfoReaderProvider; + } + catch (Exception e) + { + EditAndContinueWorkspaceService.Log.Write("Source file of project '{0}' doesn't match output PDB: error opening PDB '{1}': {2}", projectName, compilationOutputs.PdbDisplayPath, e.Message); + return null; + } + } + public void CommitSolution(Solution solution) { lock (_guard) @@ -291,9 +388,9 @@ public void CommitSolution(Solution solution) } } - private (SourceText? Source, bool? HasDocument) TryGetPdbMatchingSourceText(string sourceFilePath, Encoding? encoding, Project project) + private static (SourceText? Source, bool? HasDocument) TryGetPdbMatchingSourceText(EditAndContinueMethodDebugInfoReader debugInfoReader, string sourceFilePath, Encoding? encoding) { - var hasDocument = TryReadSourceFileChecksumFromPdb(sourceFilePath, project, out var symChecksum, out var algorithm); + var hasDocument = TryReadSourceFileChecksumFromPdb(debugInfoReader, sourceFilePath, out var symChecksum, out var algorithm); if (hasDocument != true) { return (Source: null, hasDocument); @@ -331,62 +428,31 @@ public void CommitSolution(Solution solution) /// False if the document is not found in the PDB. /// Null if it can't be determined because the PDB is not available or an error occurred while reading the PDB. /// - private bool? TryReadSourceFileChecksumFromPdb(string sourceFilePath, Project project, out ImmutableArray checksum, out SourceHashAlgorithm algorithm) + private static bool? TryReadSourceFileChecksumFromPdb(EditAndContinueMethodDebugInfoReader debugInfoReader, string sourceFilePath, out ImmutableArray checksum, out SourceHashAlgorithm algorithm) { checksum = default; algorithm = default; try { - var compilationOutputs = _debuggingSession.GetCompilationOutputs(project); - - DebugInformationReaderProvider? debugInfoReaderProvider; - try + if (!debugInfoReader.TryGetDocumentChecksum(sourceFilePath, out checksum, out var algorithmId)) { - debugInfoReaderProvider = compilationOutputs.OpenPdb(); - } - catch (Exception e) - { - EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: error opening PDB '{1}': {2}", sourceFilePath, compilationOutputs.PdbDisplayPath, e.Message); - debugInfoReaderProvider = null; + EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: no document", sourceFilePath); + return false; } - if (debugInfoReaderProvider == null) + algorithm = SourceHashAlgorithms.GetSourceHashAlgorithm(algorithmId); + if (algorithm == SourceHashAlgorithm.None) { - EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: PDB '{1}' not found", sourceFilePath, compilationOutputs.PdbDisplayPath); - return null; + // This can only happen if the PDB was post-processed by a misbehaving tool. + EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match PDB: unknown checksum alg", sourceFilePath); } - try - { - var debugInfoReader = debugInfoReaderProvider.CreateEditAndContinueMethodDebugInfoReader(); - if (!debugInfoReader.TryGetDocumentChecksum(sourceFilePath, out checksum, out var algorithmId)) - { - EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: no document", sourceFilePath); - return false; - } - - algorithm = SourceHashAlgorithms.GetSourceHashAlgorithm(algorithmId); - if (algorithm == SourceHashAlgorithm.None) - { - // This can only happen if the PDB was post-processed by a misbehaving tool. - EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match PDB: unknown checksum alg", sourceFilePath); - } - - return true; - } - catch (Exception e) - { - EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: error reading symbols: {1}", sourceFilePath, e.Message); - } - finally - { - debugInfoReaderProvider.Dispose(); - } + return true; } - catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e)) + catch (Exception e) { - EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match PDB: unexpected exception: {1}", sourceFilePath, e.Message); + EditAndContinueWorkspaceService.Log.Write("Source '{0}' doesn't match output PDB: error reading symbols: {1}", sourceFilePath, e.Message); } return null; diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index 7a924a87ebaf7..acf06968a3514 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -87,14 +87,15 @@ internal sealed class DebuggingSession : IDisposable internal DebuggingSession( Solution solution, - Func compilationOutputsProvider) + Func compilationOutputsProvider, + IEnumerable> initialDocumentStates) { _compilationOutputsProvider = compilationOutputsProvider; _projectModuleIds = new Dictionary(); _projectEmitBaselines = new Dictionary(); _modulesPreparedForUpdate = new HashSet(); - LastCommittedSolution = new CommittedSolution(this, solution); + LastCommittedSolution = new CommittedSolution(this, solution, initialDocumentStates); NonRemappableRegions = ImmutableDictionary>.Empty; } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 8a11044872e15..be9c1dfdcb381 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -40,12 +41,11 @@ public Factory() [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] public IWorkspaceService? CreateService(HostWorkspaceServices workspaceServices) - => new EditAndContinueWorkspaceService(workspaceServices.Workspace); + => new EditAndContinueWorkspaceService(); } internal static readonly TraceLog Log = new(2048, "EnC"); - private readonly Workspace _workspace; private readonly EditSessionTelemetry _editSessionTelemetry; private readonly DebuggingSessionTelemetry _debuggingSessionTelemetry; private readonly Func _compilationOutputsProvider; @@ -62,11 +62,9 @@ public Factory() private EditSession? _editSession; internal EditAndContinueWorkspaceService( - Workspace workspace, Func? testCompilationOutputsProvider = null, Action? testReportTelemetry = null) { - _workspace = workspace; _debuggingSessionTelemetry = new DebuggingSessionTelemetry(); _editSessionTelemetry = new EditSessionTelemetry(); _documentsWithReportedDiagnosticsDuringRunMode = new HashSet(); @@ -77,7 +75,6 @@ internal EditAndContinueWorkspaceService( // test only: internal DebuggingSession? Test_GetDebuggingSession() => _debuggingSession; internal EditSession? Test_GetEditSession() => _editSession; - internal Workspace Test_GetWorkspace() => _workspace; private static CompilationOutputs GetCompilationOutputs(Project project) { @@ -97,9 +94,13 @@ public void OnSourceFileUpdated(Document document) } } - public void StartDebuggingSession(Solution solution) + public async ValueTask StartDebuggingSessionAsync(Solution solution, bool captureMatchingDocuments, CancellationToken cancellationToken) { - var previousSession = Interlocked.CompareExchange(ref _debuggingSession, new DebuggingSession(solution, _compilationOutputsProvider), null); + var initialDocumentStates = + captureMatchingDocuments ? await CommittedSolution.GetMatchingDocumentsAsync(solution, _compilationOutputsProvider, cancellationToken).ConfigureAwait(false) : + SpecializedCollections.EmptyEnumerable>(); + + var previousSession = Interlocked.CompareExchange(ref _debuggingSession, new DebuggingSession(solution, _compilationOutputsProvider, initialDocumentStates), null); Contract.ThrowIfFalse(previousSession == null, "New debugging session can't be started until the existing one has ended."); } @@ -153,6 +154,9 @@ public void EndDebuggingSession(out ImmutableArray documentsToReanal internal static bool SupportsEditAndContinue(Project project) => project.LanguageServices.GetService() != null; + internal static bool SupportsEditAndContinue(DocumentState documentState) + => !documentState.Attributes.DesignTimeOnly && documentState.SupportsSyntaxTree && PathUtilities.IsAbsolute(documentState.FilePath); + public async ValueTask> GetDocumentDiagnosticsAsync(Document document, DocumentActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { try @@ -171,7 +175,7 @@ public async ValueTask> GetDocumentDiagnosticsAsync(D } // Document does not compile to the assembly (e.g. cshtml files, .g.cs files generated for completion only) - if (document.State.Attributes.DesignTimeOnly || !document.SupportsSyntaxTree) + if (!SupportsEditAndContinue(document.DocumentState)) { return ImmutableArray.Empty; } diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs index 6c5dde2e6f55a..31a36769c6d49 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -23,7 +24,7 @@ internal interface IEditAndContinueWorkspaceService : IWorkspaceService, IActive void OnSourceFileUpdated(Document document); - void StartDebuggingSession(Solution solution); + ValueTask StartDebuggingSessionAsync(Solution solution, bool captureMatchingDocuments, CancellationToken cancellationToken); void StartEditSession(IManagedEditAndContinueDebuggerService debuggerService, out ImmutableArray documentsToReanalyze); void EndEditSession(out ImmutableArray documentsToReanalyze); void EndDebuggingSession(out ImmutableArray documentsToReanalyze); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs index 9afec4ee7be5d..086f4c12d785f 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs @@ -33,7 +33,7 @@ internal interface ICallback ValueTask CommitSolutionUpdateAsync(CancellationToken cancellationToken); ValueTask DiscardSolutionUpdateAsync(CancellationToken cancellationToken); - ValueTask StartDebuggingSessionAsync(PinnedSolutionInfo solutionInfo, CancellationToken cancellationToken); + ValueTask StartDebuggingSessionAsync(PinnedSolutionInfo solutionInfo, bool captureMatchingDocuments, CancellationToken cancellationToken); ValueTask> StartEditSessionAsync(RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); ValueTask> EndEditSessionAsync(CancellationToken cancellationToken); ValueTask> EndDebuggingSessionAsync(CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs index c07f58cd7fca7..8348ffcc7f5f7 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs @@ -149,18 +149,18 @@ public RemoteEditAndContinueServiceProxy(Workspace workspace) private IEditAndContinueWorkspaceService GetLocalService() => Workspace.Services.GetRequiredService(); - public async ValueTask StartDebuggingSessionAsync(Solution solution, CancellationToken cancellationToken) + public async ValueTask StartDebuggingSessionAsync(Solution solution, bool captureMatchingDocuments, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(Workspace, cancellationToken).ConfigureAwait(false); if (client == null) { - GetLocalService().StartDebuggingSession(solution); + await GetLocalService().StartDebuggingSessionAsync(solution, captureMatchingDocuments, cancellationToken).ConfigureAwait(false); return; } await client.TryInvokeAsync( solution, - (service, solutionInfo, cancellationToken) => service.StartDebuggingSessionAsync(solutionInfo, cancellationToken), + async (service, solutionInfo, cancellationToken) => await service.StartDebuggingSessionAsync(solutionInfo, captureMatchingDocuments, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); } diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index bf59d330743a2..df84daf2f955e 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -30,6 +30,7 @@ + diff --git a/src/VisualStudio/Core/Def/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs b/src/VisualStudio/Core/Def/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs index 68953b3b7d9d7..0e67d440aafb5 100644 --- a/src/VisualStudio/Core/Def/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs +++ b/src/VisualStudio/Core/Def/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs @@ -68,7 +68,7 @@ public async Task StartDebuggingAsync(DebugSessionFlags flags, CancellationToken try { var solution = _proxy.Workspace.CurrentSolution; - await _proxy.StartDebuggingSessionAsync(solution, cancellationToken).ConfigureAwait(false); + await _proxy.StartDebuggingSessionAsync(solution, captureMatchingDocuments: false, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e)) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index 2bbfb8c673aa2..fa8e5dedb6edf 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -43,7 +43,7 @@ internal Document(Project project, DocumentState state) { } - private DocumentState DocumentState => (DocumentState)State; + internal DocumentState DocumentState => (DocumentState)State; /// /// The kind of source code this document contains. diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index 6d6f6c95c7bbe..fdb155dfc8e6c 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs @@ -66,12 +66,12 @@ private DocumentActiveStatementSpanProvider CreateDocumentActiveStatementSpanPro /// /// Remote API. /// - public ValueTask StartDebuggingSessionAsync(PinnedSolutionInfo solutionInfo, CancellationToken cancellationToken) + public ValueTask StartDebuggingSessionAsync(PinnedSolutionInfo solutionInfo, bool captureMatchingDocuments, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => { var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - GetService().StartDebuggingSession(solution); + await GetService().StartDebuggingSessionAsync(solution, captureMatchingDocuments, cancellationToken).ConfigureAwait(false); }, cancellationToken); } From 52503c578ad58e8aa577f4ca399444747f7c7dee Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 19 Mar 2021 13:56:24 -0700 Subject: [PATCH 2/4] Fix --- .../EditAndContinueWorkspaceService.cs | 4 +- .../Watch/Api/WatchHotReloadService.cs | 91 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index be9c1dfdcb381..5330730bbe83e 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -154,8 +154,10 @@ public void EndDebuggingSession(out ImmutableArray documentsToReanal internal static bool SupportsEditAndContinue(Project project) => project.LanguageServices.GetService() != null; + // Note: source generated files have relative paths: https://github.com/dotnet/roslyn/issues/51998 internal static bool SupportsEditAndContinue(DocumentState documentState) - => !documentState.Attributes.DesignTimeOnly && documentState.SupportsSyntaxTree && PathUtilities.IsAbsolute(documentState.FilePath); + => !documentState.Attributes.DesignTimeOnly && documentState.SupportsSyntaxTree && + (PathUtilities.IsAbsolute(documentState.FilePath) || documentState is SourceGeneratedDocumentState); public async ValueTask> GetDocumentDiagnosticsAsync(Document document, DocumentActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs new file mode 100644 index 0000000000000..46882bb862cfd --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.EditAndContinue; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Watch.Api +{ + internal sealed class WatchHotReloadService + { + private sealed class DebuggerService : IManagedEditAndContinueDebuggerService + { + public static readonly DebuggerService Instance = new(); + + public Task> GetActiveStatementsAsync(CancellationToken cancellationToken) + => Task.FromResult(ImmutableArray.Empty); + + public Task GetAvailabilityAsync(Guid module, CancellationToken cancellationToken) + => Task.FromResult(new ManagedEditAndContinueAvailability(ManagedEditAndContinueAvailabilityStatus.Available)); + + public Task PrepareModuleForUpdateAsync(Guid module, CancellationToken cancellationToken) + => Task.CompletedTask; + } + + public readonly struct Update + { + public readonly Guid ModuleId; + public readonly ImmutableArray ILDelta; + public readonly ImmutableArray MetadataDelta; + public readonly ImmutableArray PdbDelta; + + public Update(Guid moduleId, ImmutableArray ilDelta, ImmutableArray metadataDelta, ImmutableArray pdbDelta) + { + ModuleId = moduleId; + ILDelta = ilDelta; + MetadataDelta = metadataDelta; + PdbDelta = pdbDelta; + } + } + + private static readonly SolutionActiveStatementSpanProvider s_solutionActiveStatementSpanProvider = + (_, _) => ValueTaskFactory.FromResult(ImmutableArray.Empty); + + private readonly IEditAndContinueWorkspaceService _encService; + + public WatchHotReloadService(HostWorkspaceServices services) + => _encService = services.GetRequiredService(); + + /// + /// Starts the watcher. + /// + /// Solution that represents sources that match the built binaries on disk. + /// Cancellation token. + /// + public async Task StartSessionAsync(Solution solution, CancellationToken cancellationToken) + => await _encService.StartDebuggingSessionAsync(solution, captureMatchingDocuments: true, cancellationToken).ConfigureAwait(false); + + public async Task<(ImmutableArray updates, ImmutableArray diagnostics)> EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken) + { + _encService.StartEditSession(DebuggerService.Instance, out _); + + var (updates, diagnostics) = await _encService.EmitSolutionUpdateAsync(solution, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); + + if (updates.Status == ManagedModuleUpdateStatus.Ready) + { + _encService.CommitSolutionUpdate(); + } + + _encService.EndEditSession(out _); + + if (updates.Status == ManagedModuleUpdateStatus.Blocked) + { + return default; + } + + return (updates.Updates.SelectAsArray(update => new Update(update.Module, update.ILDelta, update.MetadataDelta, update.PdbDelta)), diagnostics); + } + + public void EndSession() + => _encService.EndDebuggingSession(out _); + } +} From f62c9198291360f72283ac2f7b1d3603c727ed57 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 19 Mar 2021 15:08:26 -0700 Subject: [PATCH 3/4] Return public diagnostic --- .../EditAndContinueWorkspaceServiceTests.cs | 403 +++++++++--------- .../RemoteEditAndContinueServiceTests.cs | 10 +- .../MockEditAndContinueWorkspaceService.cs | 5 +- .../EditAndContinueWorkspaceService.cs | 29 +- .../EmitSolutionUpdateResults.cs | 47 ++ .../IEditAndContinueWorkspaceService.cs | 2 +- .../RemoteEditAndContinueServiceProxy.cs | 18 +- .../Watch/Api/WatchHotReloadService.cs | 16 +- .../RemoteEditAndContinueService.cs | 3 +- 9 files changed, 289 insertions(+), 244 deletions(-) create mode 100644 src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index ce58521132d2b..4de43e166db70 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -164,7 +164,7 @@ private static void EndEditSession(EditAndContinueWorkspaceService service, Immu private static async Task StartDebuggingSessionAsync(EditAndContinueWorkspaceService service, Solution solution, CommittedSolution.DocumentState initialState = CommittedSolution.DocumentState.MatchesBuildOutput) { - await service.StartDebuggingSessionAsync(solution, captureMatchingDocuments: false, CancellationToken.None).ConfigureAwait(false); + await service.StartDebuggingSessionAsync(solution, captureMatchingDocuments: false, CancellationToken.None); var session = service.Test_GetDebuggingSession(); if (initialState != CommittedSolution.DocumentState.None) @@ -181,6 +181,15 @@ private static void EndDebuggingSession(EditAndContinueWorkspaceService service, AssertEx.Equal(documentsWithRunModeDiagnostics.NullToEmpty(), documentsToReanalyze); } + private static async Task<(ManagedModuleUpdates updates, ImmutableArray diagnostics)> EmitSolutionUpdateAsync( + IEditAndContinueWorkspaceService service, + Solution solution, + SolutionActiveStatementSpanProvider activeStatementSpanProvider = null) + { + var result = await service.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider ?? s_noSolutionActiveSpans, CancellationToken.None); + return (result.ModuleUpdates, result.GetDiagnosticData(solution)); + } + internal static void SetDocumentsState(DebuggingSession session, Solution solution, CommittedSolution.DocumentState state) { foreach (var project in solution.Projects) @@ -439,7 +448,7 @@ public async Task StartDebuggingSession_CapturingDocuments() var service = CreateEditAndContinueService(); - await service.StartDebuggingSessionAsync(solution, captureMatchingDocuments: true, CancellationToken.None).ConfigureAwait(false); + await service.StartDebuggingSessionAsync(solution, captureMatchingDocuments: true, CancellationToken.None); var debuggingSession = service.Test_GetDebuggingSession(); @@ -456,7 +465,7 @@ public async Task StartDebuggingSession_CapturingDocuments() StartEditSession(service); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(solution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, solution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); AssertEx.Equal(new[] { $"{projectP.Id} Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFileB.Path)}" }, InspectDiagnostics(emitDiagnostics)); @@ -476,18 +485,18 @@ public async Task RunMode_ProjectThatDoesNotSupportEnC() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // change the source: workspace.ChangeDocument(document1.Id, SourceText.From("dummy2")); var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); } @@ -506,7 +515,7 @@ public async Task RunMode_DesignTimeOnlyDocument() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // update a design-time-only source file: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(d => d.Id == documentInfo.Id); @@ -514,11 +523,11 @@ public async Task RunMode_DesignTimeOnlyDocument() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(d => d.Id == documentInfo.Id); // no updates: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // validate solution update status and emit - changes made in design-time-only documents are ignored: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndDebuggingSession(service); @@ -539,18 +548,18 @@ public async Task RunMode_ProjectNotBuilt() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // change the source: workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }")); var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); } } @@ -569,11 +578,11 @@ public async Task RunMode_ErrorReadingModuleFile() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // change the source: @@ -581,13 +590,13 @@ public async Task RunMode_ErrorReadingModuleFile() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // error not reported here since it might be intermittent and will be reported if the issue persist when applying the update: - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // validate solution update status and emit - changes made during run mode are ignored: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -609,11 +618,11 @@ public async Task RunMode_DocumentOutOfSync() var document1 = project.Documents.Single(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); debuggingSession.LastCommittedSolution.Test_SetDocumentState(document1.Id, CommittedSolution.DocumentState.OutOfSync); // no changes: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // change the source: @@ -621,13 +630,13 @@ public async Task RunMode_DocumentOutOfSync() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // no Rude Edits, since the document is out-of-sync - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // the document is now in-sync (a file watcher observed a change and updated the status): debuggingSession.LastCommittedSolution.Test_SetDocumentState(document2.Id, CommittedSolution.DocumentState.MatchesBuildOutput); - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC1003" }, diagnostics.Select(d => d.Id)); EndDebuggingSession(service, documentsWithRunModeDiagnostics: ImmutableArray.Create(document1.Id)); @@ -662,21 +671,21 @@ public async Task RunMode_FileAdded() var project = documentA.Project; var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // add a source file: var documentB = project.AddDocument("file2.cs", SourceText.From(sourceB), filePath: sourceFileB.Path); workspace.ChangeSolution(documentB.Project.Solution); // no changes in document1: - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(documentA, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(documentA, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics1); // update in document2: - var diagnostics2 = await service.GetDocumentDiagnosticsAsync(documentB, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics2 = await service.GetDocumentDiagnosticsAsync(documentB, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC1003" }, diagnostics2.Select(d => d.Id)); - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndDebuggingSession(service, documentsWithRunModeDiagnostics: ImmutableArray.Create(documentB.Id)); @@ -700,30 +709,30 @@ public async Task RunMode_Diagnostics() var service = CreateEditAndContinueService(); - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // no changes: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); // change the source: workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }")); var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // validate solution update status and emit - changes made during run mode are ignored: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC1003" }, diagnostics.Select(d => d.Id)); EndDebuggingSession(service, documentsWithRunModeDiagnostics: ImmutableArray.Create(document2.Id)); @@ -749,7 +758,7 @@ public async Task RunMode_DifferentDocumentWithSameContent() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // update the document var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); @@ -759,11 +768,11 @@ public async Task RunMode_DifferentDocumentWithSameContent() Assert.Equal(document1.Id, document2.Id); Assert.NotSame(document1, document2); - var diagnostics2 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics2 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics2); // validate solution update status and emit - changes made during run mode are ignored: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndDebuggingSession(service); @@ -785,7 +794,7 @@ public async Task BreakMode_ProjectThatDoesNotSupportEnC() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); // change the source: @@ -794,9 +803,9 @@ public async Task BreakMode_ProjectThatDoesNotSupportEnC() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // validate solution update status and emit: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -826,7 +835,7 @@ public async Task BreakMode_DesignTimeOnlyDocument_Dynamic() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); // change the source: @@ -834,9 +843,9 @@ public async Task BreakMode_DesignTimeOnlyDocument_Dynamic() workspace.ChangeDocument(document1.Id, SourceText.From("class E {}")); // validate solution update status and emit: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -880,7 +889,7 @@ public async Task BreakMode_DesignTimeOnlyDocument_Wpf(bool delayLoad) var service = CreateEditAndContinueService(); - await service.StartDebuggingSessionAsync(workspace.CurrentSolution, captureMatchingDocuments: false, CancellationToken.None).ConfigureAwait(false); + await service.StartDebuggingSessionAsync(workspace.CurrentSolution, captureMatchingDocuments: false, CancellationToken.None); StartEditSession(service); @@ -891,13 +900,13 @@ public async Task BreakMode_DesignTimeOnlyDocument_Wpf(bool delayLoad) var documentC2 = workspace.CurrentSolution.Projects.Single().Documents.Single(d => d.Id == documentC.Id); // no Rude Edits reported: - Assert.Empty(await service.GetDocumentDiagnosticsAsync(documentB2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false)); - Assert.Empty(await service.GetDocumentDiagnosticsAsync(documentC2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false)); + Assert.Empty(await service.GetDocumentDiagnosticsAsync(documentB2, s_noDocumentActiveSpans, CancellationToken.None)); + Assert.Empty(await service.GetDocumentDiagnosticsAsync(documentC2, s_noDocumentActiveSpans, CancellationToken.None)); // validate solution update status and emit: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(emitDiagnostics); @@ -906,9 +915,9 @@ public async Task BreakMode_DesignTimeOnlyDocument_Wpf(bool delayLoad) LoadLibraryToDebuggee(moduleId); // validate solution update status and emit: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(emitDiagnostics); } @@ -943,7 +952,7 @@ public async Task BreakMode_ErrorReadingModuleFile() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); // change the source: @@ -952,12 +961,12 @@ public async Task BreakMode_ErrorReadingModuleFile() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // error not reported here since it might be intermittent and will be reported if the issue persist when applying the update: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); AssertEx.Equal(new[] { $"{project.Id} Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, moduleFile.Path, expectedErrorMessage)}" }, InspectDiagnostics(emitDiagnostics)); @@ -1003,7 +1012,7 @@ public async Task BreakMode_ErrorReadingPdbFile() }; var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source: @@ -1011,13 +1020,13 @@ public async Task BreakMode_ErrorReadingPdbFile() var document2 = workspace.CurrentSolution.GetDocument(document1.Id); // error not reported here since it might be intermittent and will be reported if the issue persist when applying the update: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // an error occurred so we need to call update to determine whether we have changes to apply or not: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); AssertEx.Equal(new[] { $"{project.Id} Warning ENC1006: {string.Format(FeaturesResources.UnableToReadSourceFileOrPdb, sourceFile.Path)}" }, InspectDiagnostics(emitDiagnostics)); @@ -1052,7 +1061,7 @@ public async Task BreakMode_ErrorReadingSourceFile() var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source: @@ -1062,14 +1071,14 @@ public async Task BreakMode_ErrorReadingSourceFile() using var fileLock = File.Open(sourceFile.Path, FileMode.Open, FileAccess.Read, FileShare.None); // error not reported here since it might be intermittent and will be reported if the issue persist when applying the update: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // an error occurred so we need to call update to determine whether we have changes to apply or not: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); // try apply changes: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); AssertEx.Equal(new[] { $"{project.Id} Warning ENC1006: {string.Format(FeaturesResources.UnableToReadSourceFileOrPdb, sourceFile.Path)}" }, InspectDiagnostics(emitDiagnostics)); @@ -1077,7 +1086,7 @@ public async Task BreakMode_ErrorReadingSourceFile() fileLock.Dispose(); // try apply changes again: - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); Assert.NotEmpty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1117,7 +1126,7 @@ public async Task BreakMode_FileAdded() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); // add a source file: @@ -1125,12 +1134,12 @@ public async Task BreakMode_FileAdded() workspace.ChangeSolution(documentB.Project.Solution); documentB = workspace.CurrentSolution.GetDocument(documentB.Id); - var diagnostics2 = await service.GetDocumentDiagnosticsAsync(documentB, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics2 = await service.GetDocumentDiagnosticsAsync(documentB, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics2); - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); EndEditSession(service); @@ -1178,7 +1187,7 @@ void M() var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1189,13 +1198,13 @@ void M() // We do not report module diagnostics until emit. // This is to make the analysis deterministic (not dependent on the current state of the debuggee). - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics1); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); AssertEx.Equal(new[] { $"{project.Id} Error ENC2016: {string.Format(FeaturesResources.EditAndContinueDisallowedByProject, project.Name, "*message*")}" }, InspectDiagnostics(emitDiagnostics)); @@ -1239,7 +1248,7 @@ public async Task BreakMode_Encodings() var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path, encoding: encoding); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1247,10 +1256,10 @@ public async Task BreakMode_Encodings() // Since we find content matching the PDB checksum we update the committed solution with this source text. // If we used wrong encoding this would lead to a false change detected below. var currentDocument = workspace.CurrentSolution.GetDocument(documentId); - await debuggingSession.LastCommittedSolution.OnSourceFileUpdatedAsync(currentDocument, debuggingSession.CancellationToken).ConfigureAwait(false); + await debuggingSession.LastCommittedSolution.OnSourceFileUpdatedAsync(currentDocument, debuggingSession.CancellationToken); // EnC service queries for a document, which triggers read of the source file from disk. - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndEditSession(service); EndDebuggingSession(service); @@ -1267,7 +1276,7 @@ public async Task BreakMode_RudeEdits() var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); @@ -1276,14 +1285,14 @@ public async Task BreakMode_RudeEdits() workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M1() { System.Console.WriteLine(1); } }")); var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC0020: " + string.Format(FeaturesResources.Renaming_0_will_prevent_the_debug_session_from_continuing, FeaturesResources.method) }, diagnostics1.Select(d => $"{d.Id}: {d.GetMessage()}")); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1322,20 +1331,20 @@ class C { int Y => 2; } var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); // change the source: var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); workspace.ChangeDocument(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); - var generatedDocument = (await workspace.CurrentSolution.Projects.Single().GetSourceGeneratedDocumentsAsync().ConfigureAwait(false)).Single(); + var generatedDocument = (await workspace.CurrentSolution.Projects.Single().GetSourceGeneratedDocumentsAsync()).Single(); - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(generatedDocument, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(generatedDocument, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC0020: " + string.Format(FeaturesResources.Renaming_0_will_prevent_the_debug_session_from_continuing, FeaturesResources.property_) }, diagnostics1.Select(d => $"{d.Id}: {d.GetMessage()}")); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1372,7 +1381,7 @@ public async Task BreakMode_RudeEdits_DocumentOutOfSync() workspace.ChangeSolution(document1.Project.Solution); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1381,13 +1390,13 @@ public async Task BreakMode_RudeEdits_DocumentOutOfSync() var document2 = workspace.CurrentSolution.GetDocument(document1.Id); // no Rude Edits, since the document is out-of-sync - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // since the document is out-of-sync we need to call update to determine whether we have changes to apply or not: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); AssertEx.Equal(new[] { $"{project.Id} Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}" }, InspectDiagnostics(emitDiagnostics)); @@ -1396,17 +1405,17 @@ public async Task BreakMode_RudeEdits_DocumentOutOfSync() sourceFile.WriteAllText(source0); // we do not reload the content of out-of-sync file for analyzer query: - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // debugger query will trigger reload of out-of-sync file content: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); // now we see the rude edit: - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC0020" }, diagnostics.Select(d => d.Id)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1447,7 +1456,7 @@ public async Task BreakMode_RudeEdits_DocumentWithoutSequencePoints() var service = CreateEditAndContinueService(); // do not initialize the document state - we will detect the state based on the PDB content. - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1456,15 +1465,15 @@ public async Task BreakMode_RudeEdits_DocumentWithoutSequencePoints() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // Rude Edits reported: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal( new[] { "ENC0023: " + string.Format(FeaturesResources.Adding_an_abstract_0_or_overriding_an_inherited_0_will_prevent_the_debug_session_from_continuing, FeaturesResources.method) }, diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1496,7 +1505,7 @@ public async Task BreakMode_RudeEdits_DelayLoadedModule() var service = CreateEditAndContinueService(); // do not initialize the document state - we will detect the state based on the PDB content. - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service); @@ -1505,14 +1514,14 @@ public async Task BreakMode_RudeEdits_DelayLoadedModule() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // Rude Edits reported: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal( new[] { "ENC0020: " + string.Format(FeaturesResources.Renaming_0_will_prevent_the_debug_session_from_continuing, FeaturesResources.method) }, diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1521,14 +1530,14 @@ public async Task BreakMode_RudeEdits_DelayLoadedModule() LoadLibraryToDebuggee(moduleId); // Rude Edits still reported: - diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + diagnostics = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Equal( new[] { "ENC0020: " + string.Format(FeaturesResources.Renaming_0_will_prevent_the_debug_session_from_continuing, FeaturesResources.method) }, diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1548,7 +1557,7 @@ public async Task BreakMode_SyntaxError() var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); @@ -1558,13 +1567,13 @@ public async Task BreakMode_SyntaxError() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // compilation errors are not reported via EnC diagnostic analyzer: - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics1); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -1593,7 +1602,7 @@ public async Task BreakMode_SemanticError() var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1603,14 +1612,14 @@ public async Task BreakMode_SemanticError() var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // compilation errors are not reported via EnC diagnostic analyzer: - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics1); // The EnC analyzer does not check for and block on all semantic errors as they are already reported by diagnostic analyzer. // Blocking update on semantic errors would be possible, but the status check is only an optimization to avoid emitting. - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); @@ -1648,7 +1657,7 @@ public async Task BreakMode_FileStatus_CompilationError() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); StartEditSession(service); // change C.cs to have a compilation error: @@ -1657,13 +1666,13 @@ public async Task BreakMode_FileStatus_CompilationError() workspace.ChangeDocument(documentC.Id, SourceText.From("class C { void M() { ")); // Common.cs is included in projects B and C. Both of these projects must have no errors, otherwise update is blocked. - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: "Common.cs", CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: "Common.cs", CancellationToken.None)); // No changes in project containing file B.cs. - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: "B.cs", CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: "B.cs", CancellationToken.None)); // All projects must have no errors. - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndEditSession(service); EndDebuggingSession(service); @@ -1681,7 +1690,7 @@ public async Task BreakMode_ValidSignificantChange_EmitError() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1690,13 +1699,13 @@ public async Task BreakMode_ValidSignificantChange_EmitError() workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", encoding: null)); var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics1); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); AssertEx.Equal(new[] { $"{project.Id} Error CS8055: {string.Format(CSharpResources.ERR_EncodinglessSyntaxTree)}" }, InspectDiagnostics(emitDiagnostics)); // no emitted delta: @@ -1715,7 +1724,7 @@ public async Task BreakMode_ValidSignificantChange_EmitError() Assert.Empty(editSession.DebuggingSession.GetBaselineModuleReaders()); // solution update status after discarding an update (still has update ready): - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndEditSession(service); EndDebuggingSession(service); @@ -1767,7 +1776,7 @@ public async Task BreakMode_ValidSignificantChange_ApplyBeforeFileWatcherEvent(b var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1779,13 +1788,13 @@ public async Task BreakMode_ValidSignificantChange_ApplyBeforeFileWatcherEvent(b // Save the document: if (saveDocument) { - await debuggingSession.LastCommittedSolution.OnSourceFileUpdatedAsync(document2, debuggingSession.CancellationToken).ConfigureAwait(false); + await debuggingSession.LastCommittedSolution.OnSourceFileUpdatedAsync(document2, debuggingSession.CancellationToken); sourceFile.WriteAllText(source2); } // EnC service queries for a document, which triggers read of the source file from disk. - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -1799,8 +1808,8 @@ public async Task BreakMode_ValidSignificantChange_ApplyBeforeFileWatcherEvent(b workspace.ChangeDocument(documentId, CreateSourceTextFromFile(sourceFile.Path)); var document3 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var hasChanges = await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var hasChanges = await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); if (saveDocument) @@ -1850,7 +1859,7 @@ public async Task BreakMode_ValidSignificantChange_FileUpdateNotObservedBeforeDe var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -1861,13 +1870,13 @@ public async Task BreakMode_ValidSignificantChange_FileUpdateNotObservedBeforeDe // EnC service queries for a document, but the source file on disk doesn't match the PDB // We don't report rude edits for out-of-sync documents: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document3, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document3, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics); // since the document is out-of-sync we need to call update to determine whether we have changes to apply or not: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); AssertEx.Equal(new[] { $"{project.Id} Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}" }, InspectDiagnostics(emitDiagnostics)); @@ -1877,14 +1886,14 @@ public async Task BreakMode_ValidSignificantChange_FileUpdateNotObservedBeforeDe var currentDocument = workspace.CurrentSolution.GetDocument(documentId); // save (note that this call will fail to match the content with the PDB since it uses the content prior to the actual file write) - await debuggingSession.LastCommittedSolution.OnSourceFileUpdatedAsync(currentDocument, debuggingSession.CancellationToken).ConfigureAwait(false); - var (doc, state) = await debuggingSession.LastCommittedSolution.GetDocumentAndStateAsync(documentId, currentDocument, CancellationToken.None).ConfigureAwait(false); + await debuggingSession.LastCommittedSolution.OnSourceFileUpdatedAsync(currentDocument, debuggingSession.CancellationToken); + var (doc, state) = await debuggingSession.LastCommittedSolution.GetDocumentAndStateAsync(documentId, currentDocument, CancellationToken.None); Assert.Null(doc); Assert.Equal(CommittedSolution.DocumentState.OutOfSync, state); sourceFile.WriteAllText(source1); - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); // the content actually hasn't changed: @@ -1919,7 +1928,7 @@ public async Task BreakMode_ValidSignificantChange_AddedFileNotObservedBeforeDeb var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); // An active statement may be present in the added file since the file exists in the PDB: var activeInstruction1 = new ManagedInstructionId(new ManagedMethodId(moduleId, token: 0x06000001, version: 1), ilOffset: 1); @@ -1948,18 +1957,18 @@ public async Task BreakMode_ValidSignificantChange_AddedFileNotObservedBeforeDeb workspace.ChangeSolution(document1.Project.Solution); // We don't report rude edits for the added document: - var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document1, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics); // TODO: https://github.com/dotnet/roslyn/issues/49938 // We currently create the AS map against the committed solution, which may not contain all documents. - // var spans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false); + // var spans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None); // AssertEx.Equal(new[] { $"({activeLineSpan1}, IsLeafFrame)" }, spans.Single().Select(s => s.ToString())); // No changes. - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); AssertEx.Empty(emitDiagnostics); @@ -1998,14 +2007,14 @@ public async Task BreakMode_ValidSignificantChange_DocumentOutOfSync(bool delayL var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution, initialState: CommittedSolution.DocumentState.None); StartEditSession(service); // no changes have been made to the project - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(updates.Updates); Assert.Empty(emitDiagnostics); @@ -2014,13 +2023,13 @@ public async Task BreakMode_ValidSignificantChange_DocumentOutOfSync(bool delayL workspace.ChangeDocument(document1.Id, SourceText.From(sourceOnDisk, Encoding.UTF8)); var document3 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics = await service.GetDocumentDiagnosticsAsync(document3, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics = await service.GetDocumentDiagnosticsAsync(document3, s_noDocumentActiveSpans, CancellationToken.None); Assert.Empty(diagnostics); // the content of the file is now exactly the same as the compiled document, so there is no change to be applied: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.None, updates.Status); Assert.Empty(emitDiagnostics); @@ -2043,7 +2052,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful(bool commitUpd var moduleId = EmitAndLoadLibraryToDebuggee(sourceV1); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2051,13 +2060,13 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful(bool commitUpd workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics1); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2103,7 +2112,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful(bool commitUpd Assert.Same(newBaseline, editSession.DebuggingSession.Test_GetProjectEmitBaseline(project.Id)); // solution update status after committing an update: - var commitedUpdateSolutionStatus = await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false); + var commitedUpdateSolutionStatus = await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None); Assert.False(commitedUpdateSolutionStatus); } else @@ -2114,7 +2123,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful(bool commitUpd Assert.Null(editSession.Test_GetPendingSolutionUpdate()); // solution update status after committing an update: - var discardedUpdateSolutionStatus = await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false); + var discardedUpdateSolutionStatus = await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None); Assert.True(discardedUpdateSolutionStatus); } @@ -2165,7 +2174,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful_UpdateDeferred var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // module is not loaded: var editSession = StartEditSession(service, activeStatements, loadedModules: _loadedModulesProvider); @@ -2175,9 +2184,9 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful_UpdateDeferred var document2 = workspace.CurrentSolution.Projects.Single().Documents.Single(); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); Assert.Empty(emitDiagnostics); @@ -2222,7 +2231,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful_UpdateDeferred Assert.Same(newBaseline, editSession.DebuggingSession.Test_GetProjectEmitBaseline(project.Id)); // solution update status after committing an update: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndEditSession(service); @@ -2235,7 +2244,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful_UpdateDeferred var document3 = workspace.CurrentSolution.Projects.Single().Documents.Single(); workspace.ChangeDocument(document3.Id, SourceText.From("class C1 { void M1() { int a = 3; System.Console.WriteLine(a); } void M2() { System.Console.WriteLine(2); } }", Encoding.UTF8)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); Assert.Empty(emitDiagnostics); @@ -2295,7 +2304,7 @@ partial class C { int Y = 2; } LoadLibraryToDebuggee(EmitLibrary(new[] { (sourceA1, "test1.cs"), (sourceB1, "test2.cs") })); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2305,7 +2314,7 @@ partial class C { int Y = 2; } workspace.ChangeDocument(documentB.Id, SourceText.From(sourceB2, Encoding.UTF8)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2402,7 +2411,7 @@ class C { int Y => 2; } LoadLibraryToDebuggee(moduleId); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2410,7 +2419,7 @@ class C { int Y => 2; } workspace.ChangeDocument(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2462,7 +2471,7 @@ int M() LoadLibraryToDebuggee(moduleId); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2470,7 +2479,7 @@ int M() workspace.ChangeDocument(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2510,7 +2519,7 @@ partial class C { int X = 1; } LoadLibraryToDebuggee(moduleId); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the source (valid edit): @@ -2518,7 +2527,7 @@ partial class C { int X = 1; } workspace.ChangeDocument(document1.Id, SourceText.From(sourceV2, Encoding.UTF8)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2557,7 +2566,7 @@ class C { int Y => 1; } LoadLibraryToDebuggee(moduleId); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the additional source (valid edit): @@ -2565,7 +2574,7 @@ class C { int Y => 1; } workspace.ChangeAdditionalDocument(additionalDocument1.Id, SourceText.From(additionalSourceV2, Encoding.UTF8)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2600,7 +2609,7 @@ class C { int Y => 1; } LoadLibraryToDebuggee(moduleId); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // change the additional source (valid edit): @@ -2608,7 +2617,7 @@ class C { int Y => 1; } workspace.ChangeAnalyzerConfigDocument(configDocument1.Id, GetAnalyzerConfigText(configV2)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2641,7 +2650,7 @@ public async Task BreakMode_ValidSignificantChange_SourceGenerators_DocumentRemo LoadLibraryToDebuggee(moduleId); var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); // remove the source document (valid edit): @@ -2649,7 +2658,7 @@ public async Task BreakMode_ValidSignificantChange_SourceGenerators_DocumentRemo workspace.ChangeSolution(document1.Project.Solution.RemoveDocument(document1.Id)); // validate solution update status and emit: - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Empty(emitDiagnostics); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); @@ -2708,7 +2717,7 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2720,9 +2729,9 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() workspace.ChangeDocument(projectB.Documents.Single().Id, SourceText.From(source2, Encoding.UTF8)); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); Assert.Empty(emitDiagnostics); @@ -2760,7 +2769,7 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() Assert.Same(newBaselineB1, editSession.DebuggingSession.Test_GetProjectEmitBaseline(projectB.Id)); // solution update status after committing an update: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndEditSession(service); editSession = StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2773,9 +2782,9 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() workspace.ChangeDocument(projectB.Documents.Single().Id, SourceText.From(source3, Encoding.UTF8)); // validate solution update status and emit: - Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); - (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); Assert.Empty(emitDiagnostics); @@ -2813,7 +2822,7 @@ public async Task TwoUpdatesWithLoadedAndUnloadedModule() Assert.Same(newBaselineB2, editSession.DebuggingSession.Test_GetProjectEmitBaseline(projectB.Id)); // solution update status after committing an update: - Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); + Assert.False(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None)); EndEditSession(service); @@ -2874,7 +2883,7 @@ void M() var oldTree = SyntaxFactory.ParseSyntaxTree(source1); var newTree = SyntaxFactory.ParseSyntaxTree(source2); - var changes = await EditAndContinueWorkspaceService.GetDocumentTextChangesAsync(oldTree, newTree, CancellationToken.None).ConfigureAwait(false); + var changes = await EditAndContinueWorkspaceService.GetDocumentTextChangesAsync(oldTree, newTree, CancellationToken.None); AssertEx.Equal(new[] { @@ -2900,7 +2909,7 @@ public async Task BreakMode_ValidSignificantChange_BaselineCreationFailed_NoStre var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // module not loaded StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2909,7 +2918,7 @@ public async Task BreakMode_ValidSignificantChange_BaselineCreationFailed_NoStre var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); AssertEx.Equal(new[] { $"{project.Id} Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "test-pdb", new FileNotFoundException().Message)}" }, InspectDiagnostics(emitDiagnostics)); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); } @@ -2937,7 +2946,7 @@ public async Task BreakMode_ValidSignificantChange_BaselineCreationFailed_Assemb var service = CreateEditAndContinueService(); - await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // module not loaded StartEditSession(service, loadedModules: _loadedModulesProvider); @@ -2946,7 +2955,7 @@ public async Task BreakMode_ValidSignificantChange_BaselineCreationFailed_Assemb var document1 = workspace.CurrentSolution.Projects.Single().Documents.Single(); workspace.ChangeDocument(document1.Id, SourceText.From("class C1 { void M() { System.Console.WriteLine(2); } }", Encoding.UTF8)); - var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(service, workspace.CurrentSolution); AssertEx.Equal(new[] { $"{project.Id} Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "test-assembly", "*message*")}" }, InspectDiagnostics(emitDiagnostics)); Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); @@ -2995,12 +3004,12 @@ public async Task ActiveStatements() var service = CreateEditAndContinueService(); // default if called outside of edit session - Assert.True((await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false)).IsDefault); + Assert.True((await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None)).IsDefault); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); // default if called outside of edit session - Assert.True((await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false)).IsDefault); + Assert.True((await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None)).IsDefault); var moduleId = Guid.NewGuid(); var activeInstruction1 = new ManagedInstructionId(new ManagedMethodId(moduleId, token: 0x06000001, version: 1), ilOffset: 1); @@ -3020,7 +3029,7 @@ public async Task ActiveStatements() var editSession = StartEditSession(service, activeStatements); - var baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None).ConfigureAwait(false); + var baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document1.Id), CancellationToken.None); AssertEx.Equal(new[] { $"({activeLineSpan11}, IsNonLeafFrame)", @@ -3029,7 +3038,7 @@ public async Task ActiveStatements() var trackedActiveSpans1 = ImmutableArray.Create(activeSpan11, activeSpan12); - var currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document1, (_) => new(trackedActiveSpans1), CancellationToken.None).ConfigureAwait(false); + var currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document1, (_) => new(trackedActiveSpans1), CancellationToken.None); AssertEx.Equal(new[] { $"({activeLineSpan11}, IsNonLeafFrame)", @@ -3037,10 +3046,10 @@ public async Task ActiveStatements() }, currentSpans.Select(s => s.ToString())); Assert.Equal(activeLineSpan11, - await service.GetCurrentActiveStatementPositionAsync(document1.Project.Solution, (_, _) => new(trackedActiveSpans1), activeInstruction1, CancellationToken.None).ConfigureAwait(false)); + await service.GetCurrentActiveStatementPositionAsync(document1.Project.Solution, (_, _) => new(trackedActiveSpans1), activeInstruction1, CancellationToken.None)); Assert.Equal(activeLineSpan12, - await service.GetCurrentActiveStatementPositionAsync(document1.Project.Solution, (_, _) => new(trackedActiveSpans1), activeInstruction2, CancellationToken.None).ConfigureAwait(false)); + await service.GetCurrentActiveStatementPositionAsync(document1.Project.Solution, (_, _) => new(trackedActiveSpans1), activeInstruction2, CancellationToken.None)); // change the source (valid edit): workspace.ChangeDocument(documentId, sourceTextV2); @@ -3049,14 +3058,14 @@ public async Task ActiveStatements() // tracking span update triggered by the edit: var trackedActiveSpans2 = ImmutableArray.Create(activeSpan21, activeSpan22); - baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document2.Id), CancellationToken.None).ConfigureAwait(false); + baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document2.Id), CancellationToken.None); AssertEx.Equal(new[] { $"({activeLineSpan11}, IsNonLeafFrame)", $"({activeLineSpan12}, IsLeafFrame)" }, baseSpans.Single().Select(s => s.ToString())); - currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document2, _ => new(trackedActiveSpans2), CancellationToken.None).ConfigureAwait(false); + currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document2, _ => new(trackedActiveSpans2), CancellationToken.None); AssertEx.Equal(new[] { $"({adjustedActiveLineSpan1}, IsNonLeafFrame)", @@ -3064,10 +3073,10 @@ public async Task ActiveStatements() }, currentSpans.Select(s => s.ToString())); Assert.Equal(adjustedActiveLineSpan1, - await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, (_, _) => new(trackedActiveSpans2), activeInstruction1, CancellationToken.None).ConfigureAwait(false)); + await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, (_, _) => new(trackedActiveSpans2), activeInstruction1, CancellationToken.None)); Assert.Equal(adjustedActiveLineSpan2, - await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, (_, _) => new(trackedActiveSpans2), activeInstruction2, CancellationToken.None).ConfigureAwait(false)); + await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, (_, _) => new(trackedActiveSpans2), activeInstruction2, CancellationToken.None)); } [Theory] @@ -3091,7 +3100,7 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy var documentId = document1.Id; var documentFilePath = document1.FilePath; - var sourceTextV1 = await document1.GetTextAsync(CancellationToken.None).ConfigureAwait(false); + var sourceTextV1 = await document1.GetTextAsync(CancellationToken.None); var sourceTextV2 = SourceText.From(sourceV2, Encoding.UTF8); var activeLineSpan11 = sourceTextV1.Lines.GetLinePositionSpan(activeSpan11); @@ -3125,7 +3134,7 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy workspace.ChangeDocument(documentId, sourceTextV2); var document2 = workspace.CurrentSolution.GetDocument(documentId); - var baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(documentId), CancellationToken.None).ConfigureAwait(false); + var baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(documentId), CancellationToken.None); if (isOutOfSync) { @@ -3141,11 +3150,11 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy } // no active statements due to syntax error or out-of-sync document: - var currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document2, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document2, s_noDocumentActiveSpans, CancellationToken.None); Assert.True(currentSpans.IsDefault); - Assert.Null(await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, activeInstruction1, CancellationToken.None).ConfigureAwait(false)); - Assert.Null(await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, activeInstruction2, CancellationToken.None).ConfigureAwait(false)); + Assert.Null(await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, activeInstruction1, CancellationToken.None)); + Assert.Null(await service.GetCurrentActiveStatementPositionAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, activeInstruction2, CancellationToken.None)); } [Fact] @@ -3161,7 +3170,7 @@ public async Task ActiveStatements_ForeignDocument() var service = CreateEditAndContinueService(); - var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution).ConfigureAwait(false); + var debuggingSession = await StartDebuggingSessionAsync(service, workspace.CurrentSolution); var activeStatements = ImmutableArray.Create( new ManagedActiveStatementDebugInfo( @@ -3173,10 +3182,10 @@ public async Task ActiveStatements_ForeignDocument() StartEditSession(service, activeStatements); // active statements are tracked not in non-Roslyn projects: - var currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); + var currentSpans = await service.GetAdjustedActiveStatementSpansAsync(document, s_noDocumentActiveSpans, CancellationToken.None); Assert.True(currentSpans.IsDefault); - var baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document.Id), CancellationToken.None).ConfigureAwait(false); + var baseSpans = await service.GetBaseActiveStatementSpansAsync(workspace.CurrentSolution, ImmutableArray.Create(document.Id), CancellationToken.None); Assert.Empty(baseSpans.Single()); } } diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index e55f2865bae18..620e99cf27888 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -226,11 +226,10 @@ await proxy.StartEditSessionAsync( var syntaxTree = project.Documents.Single().GetSyntaxTreeSynchronously(CancellationToken.None)!; - var documentDiagnostic = DiagnosticData.Create(Diagnostic.Create(diagnosticDescriptor1, Location.Create(syntaxTree, TextSpan.FromBounds(1, 2)), new[] { "doc", "some error" }), document); - var projectDiagnostic = DiagnosticData.Create(Diagnostic.Create(diagnosticDescriptor1, Location.None, new[] { "proj", "some error" }), project); - var solutionDiagnostic = DiagnosticData.Create(Diagnostic.Create(diagnosticDescriptor1, Location.None, new[] { "sol", "some error" }), solution.Options); + var documentDiagnostic = Diagnostic.Create(diagnosticDescriptor1, Location.Create(syntaxTree, TextSpan.FromBounds(1, 2)), new[] { "doc", "some error" }); + var projectDiagnostic = Diagnostic.Create(diagnosticDescriptor1, Location.None, new[] { "proj", "some error" }); - return (new(ManagedModuleUpdateStatus.Ready, deltas), ImmutableArray.Create(documentDiagnostic, projectDiagnostic, solutionDiagnostic)); + return new(new(ManagedModuleUpdateStatus.Ready, deltas), ImmutableArray.Create((project.Id, ImmutableArray.Create(documentDiagnostic, projectDiagnostic)))); }; var updates = await proxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, solutionActiveStatementSpanProvider, diagnosticUpdateSource, CancellationToken.None).ConfigureAwait(false); @@ -242,8 +241,7 @@ await proxy.StartEditSessionAsync( AssertEx.Equal(new[] { $"[{project.Id}] Error ENC1001: test.cs(0, 1, 0, 2): {string.Format(FeaturesResources.ErrorReadingFile, "doc", "some error")}", - $"[{project.Id}] Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "proj", "some error")}", - $"[] Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "sol", "some error")}", + $"[{project.Id}] Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "proj", "some error")}" }, emitDiagnosticsUpdated.Select(update => { diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs index a252a4c6ffb0b..85f6ea86632f0 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs @@ -26,7 +26,7 @@ internal class MockEditAndContinueWorkspaceService : IEditAndContinueWorkspaceSe public EndSession? EndDebuggingSessionImpl; public EndSession? EndEditSessionImpl; public Func? HasChangesImpl; - public Func)>? EmitSolutionUpdateImpl; + public Func? EmitSolutionUpdateImpl; public Func? IsActiveStatementInExceptionRegionImpl; public Action? OnSourceFileUpdatedImpl; public Action? CommitSolutionUpdateImpl; @@ -39,8 +39,7 @@ public void CommitSolutionUpdate() public void DiscardSolutionUpdate() => DiscardSolutionUpdateImpl?.Invoke(); - public ValueTask<(ManagedModuleUpdates Updates, ImmutableArray Diagnostics)> - EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) + public ValueTask EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) => new((EmitSolutionUpdateImpl ?? throw new NotImplementedException()).Invoke(solution, activeStatementSpanProvider)); public void EndDebuggingSession(out ImmutableArray documentsToReanalyze) diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 5330730bbe83e..c52a4b3869d6b 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -384,13 +384,15 @@ public ValueTask HasChangesAsync(Solution solution, SolutionActiveStatemen return editSession.HasChangesAsync(solution, solutionActiveStatementSpanProvider, sourceFilePath, cancellationToken); } - public async ValueTask<(ManagedModuleUpdates Updates, ImmutableArray Diagnostics)> - EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) + public async ValueTask EmitSolutionUpdateAsync( + Solution solution, + SolutionActiveStatementSpanProvider activeStatementSpanProvider, + CancellationToken cancellationToken) { var editSession = _editSession; if (editSession == null) { - return (new(ManagedModuleUpdateStatus.None, ImmutableArray.Empty), ImmutableArray.Empty); + return EmitSolutionUpdateResults.Empty; } var solutionUpdate = await editSession.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); @@ -401,26 +403,7 @@ public ValueTask HasChangesAsync(Solution solution, SolutionActiveStatemen // Note that we may return empty deltas if all updates have been deferred. // The debugger will still call commit or discard on the update batch. - return (solutionUpdate.ModuleUpdates, ToDiagnosticData(solution, solutionUpdate.Diagnostics)); - } - - private static ImmutableArray ToDiagnosticData(Solution solution, ImmutableArray<(ProjectId ProjectId, ImmutableArray Diagnostics)> diagnosticsByProject) - { - using var _ = ArrayBuilder.GetInstance(out var result); - - foreach (var (projectId, diagnostics) in diagnosticsByProject) - { - var project = solution.GetRequiredProject(projectId); - - foreach (var diagnostic in diagnostics) - { - var document = solution.GetDocument(diagnostic.Location.SourceTree); - var data = (document != null) ? DiagnosticData.Create(diagnostic, document) : DiagnosticData.Create(diagnostic, project); - result.Add(data); - } - } - - return result.ToImmutable(); + return new EmitSolutionUpdateResults(solutionUpdate.ModuleUpdates, solutionUpdate.Diagnostics); } public void CommitSolutionUpdate() diff --git a/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs b/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs new file mode 100644 index 0000000000000..7812555ad3021 --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; + +namespace Microsoft.CodeAnalysis.EditAndContinue +{ + internal readonly struct EmitSolutionUpdateResults + { + public static readonly EmitSolutionUpdateResults Empty = + new(new ManagedModuleUpdates(ManagedModuleUpdateStatus.None, ImmutableArray.Empty), + ImmutableArray<(ProjectId ProjectId, ImmutableArray Diagnostic)>.Empty); + + public readonly ManagedModuleUpdates ModuleUpdates; + public readonly ImmutableArray<(ProjectId ProjectId, ImmutableArray Diagnostic)> Diagnostics; + + public EmitSolutionUpdateResults(ManagedModuleUpdates moduleUpdates, ImmutableArray<(ProjectId ProjectId, ImmutableArray Diagnostic)> diagnostics) + { + ModuleUpdates = moduleUpdates; + Diagnostics = diagnostics; + } + + public ImmutableArray GetDiagnosticData(Solution solution) + { + using var _ = ArrayBuilder.GetInstance(out var result); + + foreach (var (projectId, diagnostics) in Diagnostics) + { + var project = solution.GetRequiredProject(projectId); + + foreach (var diagnostic in diagnostics) + { + var document = solution.GetDocument(diagnostic.Location.SourceTree); + var data = (document != null) ? DiagnosticData.Create(diagnostic, document) : DiagnosticData.Create(diagnostic, project); + result.Add(data); + } + } + + return result.ToImmutable(); + } + } +} diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs index 31a36769c6d49..ae4475382680c 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs @@ -17,7 +17,7 @@ internal interface IEditAndContinueWorkspaceService : IWorkspaceService, IActive { ValueTask> GetDocumentDiagnosticsAsync(Document document, DocumentActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); ValueTask HasChangesAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, string? sourceFilePath, CancellationToken cancellationToken); - ValueTask<(ManagedModuleUpdates Updates, ImmutableArray Diagnostics)> EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); + ValueTask EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); void CommitSolutionUpdate(); void DiscardSolutionUpdate(); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs index 8348ffcc7f5f7..7af5110da6809 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteEditAndContinueServiceProxy.cs @@ -301,13 +301,15 @@ public async ValueTask HasChangesAsync(Solution solution, SolutionActiveSt public async ValueTask EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider activeStatementSpanProvider, EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource, CancellationToken cancellationToken) { - ManagedModuleUpdates updates; - ImmutableArray diagnosticsByProject; + ManagedModuleUpdates moduleUpdates; + ImmutableArray diagnosticData; var client = await RemoteHostClient.TryGetClientAsync(Workspace, cancellationToken).ConfigureAwait(false); if (client == null) { - (updates, diagnosticsByProject) = await GetLocalService().EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); + var results = await GetLocalService().EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); + moduleUpdates = results.ModuleUpdates; + diagnosticData = results.GetDiagnosticData(solution); } else { @@ -319,12 +321,12 @@ public async ValueTask EmitSolutionUpdateAsync(Solution so if (result.HasValue) { - (updates, diagnosticsByProject) = result.Value; + (moduleUpdates, diagnosticData) = result.Value; } else { - updates = new ManagedModuleUpdates(ManagedModuleUpdateStatus.Blocked, ImmutableArray.Empty); - diagnosticsByProject = ImmutableArray.Empty; + moduleUpdates = new ManagedModuleUpdates(ManagedModuleUpdateStatus.Blocked, ImmutableArray.Empty); + diagnosticData = ImmutableArray.Empty; } } @@ -332,9 +334,9 @@ public async ValueTask EmitSolutionUpdateAsync(Solution so diagnosticUpdateSource.ClearDiagnostics(); // report emit/apply diagnostics: - diagnosticUpdateSource.ReportDiagnostics(Workspace, solution, diagnosticsByProject); + diagnosticUpdateSource.ReportDiagnostics(Workspace, solution, diagnosticData); - return updates; + return moduleUpdates; } public async ValueTask CommitSolutionUpdateAsync(CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs index 46882bb862cfd..f270b3790e0b2 100644 --- a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; @@ -64,25 +65,30 @@ public WatchHotReloadService(HostWorkspaceServices services) public async Task StartSessionAsync(Solution solution, CancellationToken cancellationToken) => await _encService.StartDebuggingSessionAsync(solution, captureMatchingDocuments: true, cancellationToken).ConfigureAwait(false); - public async Task<(ImmutableArray updates, ImmutableArray diagnostics)> EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken) + public async Task<(ImmutableArray updates, ImmutableArray diagnostics)> EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken) { _encService.StartEditSession(DebuggerService.Instance, out _); - var (updates, diagnostics) = await _encService.EmitSolutionUpdateAsync(solution, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); + var results = await _encService.EmitSolutionUpdateAsync(solution, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); - if (updates.Status == ManagedModuleUpdateStatus.Ready) + if (results.ModuleUpdates.Status == ManagedModuleUpdateStatus.Ready) { _encService.CommitSolutionUpdate(); } _encService.EndEditSession(out _); - if (updates.Status == ManagedModuleUpdateStatus.Blocked) + if (results.ModuleUpdates.Status == ManagedModuleUpdateStatus.Blocked) { return default; } - return (updates.Updates.SelectAsArray(update => new Update(update.Module, update.ILDelta, update.MetadataDelta, update.PdbDelta)), diagnostics); + var updates = results.ModuleUpdates.Updates.SelectAsArray( + update => new Update(update.Module, update.ILDelta, update.MetadataDelta, update.PdbDelta)); + + var diagnostics = results.Diagnostics.SelectMany(d => d.Diagnostic).ToImmutableArray(); + + return (updates, diagnostics); } public void EndSession() diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index fdb155dfc8e6c..e86dceab5dd2b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs @@ -155,7 +155,8 @@ public ValueTask HasChangesAsync(PinnedSolutionInfo solutionInfo, RemoteSe try { - return await service.EmitSolutionUpdateAsync(solution, CreateSolutionActiveStatementSpanProvider(callbackId), cancellationToken).ConfigureAwait(false); + var results = await service.EmitSolutionUpdateAsync(solution, CreateSolutionActiveStatementSpanProvider(callbackId), cancellationToken).ConfigureAwait(false); + return (results.ModuleUpdates, results.GetDiagnosticData(solution)); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { From 64a842d78b4f7bf4f85ebf53bde149bb11c03d3f Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 19 Mar 2021 15:15:28 -0700 Subject: [PATCH 4/4] Fixes --- .../Core/Portable/EditAndContinue/CommittedSolution.cs | 10 ++++------ .../Portable/Microsoft.CodeAnalysis.Features.csproj | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index 3d807b88e655a..97ff477ab2b92 100644 --- a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs +++ b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs @@ -3,19 +3,17 @@ // See the LICENSE file in the project root for more information. using System; -using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Debugging; -using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using System.IO; -using System.Text; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.Emit; namespace Microsoft.CodeAnalysis.EditAndContinue { diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index df84daf2f955e..bf59d330743a2 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -30,7 +30,6 @@ -