From 4616398b572b161c662326ee50d36f34070419b5 Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jun 2024 11:36:13 -0700 Subject: [PATCH 1/4] Test --- .../EditAndContinue/EditAndContinueWorkspaceServiceTests.cs | 6 +++++- .../EditAndContinue/EditAndContinueWorkspaceTestBase.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 9e2037ca7ff9a..fdc5b55097ca3 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; @@ -2532,7 +2533,10 @@ class C { int Y => 2; } var generator = new TestSourceGenerator() { ExecuteImpl = GenerateSource }; - using var _ = CreateWorkspace(out var solution, out var service); + using var workspace = CreateWorkspace(out var solution, out var service); + var workspaceConfig = Assert.IsType(workspace.Services.GetRequiredService()); + workspaceConfig.Options = new WorkspaceConfigurationOptions(SourceGeneratorExecutionPreference.Balanced); + (solution, var document1) = AddDefaultTestProject(solution, sourceV1, generator); var moduleId = EmitLibrary(sourceV1, generator: generator); diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs index 2ffcbe7c4981f..b702d1023e075 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs @@ -53,7 +53,11 @@ public abstract class EditAndContinueWorkspaceTestBase : TestBase internal TestWorkspace CreateWorkspace(out Solution solution, out EditAndContinueService service, Type[]? additionalParts = null) { - var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features.AddParts(additionalParts), solutionTelemetryId: s_solutionTelemetryId); + var composition = FeaturesTestCompositions.Features + .AddParts(typeof(TestWorkspaceConfigurationService)) + .AddParts(additionalParts); + + var workspace = new TestWorkspace(composition: composition, solutionTelemetryId: s_solutionTelemetryId); solution = workspace.CurrentSolution; service = GetEditAndContinueService(workspace); return workspace; From c791a3148de732fdfba8eca1378c579d05f5af3c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Jun 2024 11:13:31 -0700 Subject: [PATCH 2/4] Provide a helper extension method for features like EnC to fork a solution ensuring they get up to date generated documents Cleanup api and cancellation tokens Docs Update downstream --- .../Portable/Workspace/Solution/Solution.cs | 4 ++-- ...tionCompilationState.CompilationTracker.cs | 14 +---------- ...eneratedFileReplacingCompilationTracker.cs | 12 ++++++++-- ...ionCompilationState.ICompilationTracker.cs | 15 +++++++----- .../Solution/SolutionCompilationState.cs | 8 +++---- .../Workspace/Workspace_SourceGeneration.cs | 2 +- .../Host/RemoteWorkspace.SolutionCreator.cs | 2 +- .../Remote/ServiceHub/Host/RemoteWorkspace.cs | 2 +- .../Core/Extensions/ISolutionExtensions.cs | 23 +++++++++++++++++++ 9 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 4579a8c2d6f17..f21bf1739c637 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1576,8 +1576,8 @@ internal Solution WithFrozenSourceGeneratedDocuments(ImmutableArray<(SourceGener => WithCompilationState(_compilationState.WithFrozenSourceGeneratedDocuments(documents)); /// - internal Solution UpdateSpecificSourceGeneratorExecutionVersions(SourceGeneratorExecutionVersionMap sourceGeneratorExecutionVersionMap, CancellationToken cancellationToken) - => WithCompilationState(_compilationState.UpdateSpecificSourceGeneratorExecutionVersions(sourceGeneratorExecutionVersionMap, cancellationToken)); + internal Solution UpdateSpecificSourceGeneratorExecutionVersions(SourceGeneratorExecutionVersionMap sourceGeneratorExecutionVersionMap) + => WithCompilationState(_compilationState.UpdateSpecificSourceGeneratorExecutionVersions(sourceGeneratorExecutionVersionMap)); /// /// Undoes the operation of ; any frozen source generated document is allowed diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 99684abd9b727..571703a96d05f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -679,13 +679,6 @@ private async Task HasSuccessfullyLoadedSlowAsync( return finalState.HasSuccessfullyLoaded; } - public ICompilationTracker WithCreationPolicy(bool create, bool forceRegeneration, CancellationToken cancellationToken) - { - return create - ? WithCreateCreationPolicy(forceRegeneration) - : WithDoNotCreateCreationPolicy(forceRegeneration, cancellationToken); - } - public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration) { var state = this.ReadState(); @@ -742,13 +735,8 @@ public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration) skeletonReferenceCacheToClone: _skeletonReferenceCache); } - public ICompilationTracker WithDoNotCreateCreationPolicy( - bool forceRegeneration, CancellationToken cancellationToken) + public ICompilationTracker WithDoNotCreateCreationPolicy(CancellationToken cancellationToken) { - // We do not expect this to ever be passed true. This is for freezing generators, and no callers - // (currently) will ask to drop drivers when they do that. - Contract.ThrowIfTrue(forceRegeneration); - var state = this.ReadState(); // We're freezing the solution for features where latency performance is paramount. Do not run SGs or diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs index da0ca13cc63d8..f176a6b378116 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs @@ -75,9 +75,17 @@ public ICompilationTracker Fork(ProjectState newProject, TranslationAction? tran throw new NotImplementedException(); } - public ICompilationTracker WithCreationPolicy(bool create, bool forceRegeneration, CancellationToken cancellationToken) + public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration) { - var underlyingTracker = this.UnderlyingTracker.WithCreationPolicy(create, forceRegeneration, cancellationToken); + var underlyingTracker = this.UnderlyingTracker.WithCreateCreationPolicy(forceRegeneration); + return underlyingTracker == this.UnderlyingTracker + ? this + : new GeneratedFileReplacingCompilationTracker(underlyingTracker, _replacementDocumentStates); + } + + public ICompilationTracker WithDoNotCreateCreationPolicy(CancellationToken cancellationToken) + { + var underlyingTracker = this.UnderlyingTracker.WithDoNotCreateCreationPolicy(cancellationToken); return underlyingTracker == this.UnderlyingTracker ? this : new GeneratedFileReplacingCompilationTracker(underlyingTracker, _replacementDocumentStates); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs index 80ef8db9493ac..73c4ec43554f9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs @@ -40,13 +40,16 @@ bool ContainsAssemblyOrModuleOrDynamic( Task GetCompilationAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); /// - /// Updates the creation policy for this tracker. A value of will set this to and will set it to . + /// Updates the creation policy for this tracker. Setting it to . /// - /// When switching to , this will force source - /// generated documents to be created. - ICompilationTracker WithCreationPolicy(bool create, bool forceRegeneration, CancellationToken cancellationToken); + /// Forces source generated documents to be created by dumping any existing and rerunning generators from scratch for this tracker. + ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration); + + /// + /// Updates the creation policy for this tracker. Setting it to . + /// + ICompilationTracker WithDoNotCreateCreationPolicy(CancellationToken cancellationToken); Task GetDependentVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); Task GetDependentSemanticVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 89a468c56cae7..3b1f467913490 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1262,7 +1262,7 @@ public SolutionCompilationState WithOptions(SolutionOptionSet options) /// will not be touched (and they will stay in the map). /// public SolutionCompilationState UpdateSpecificSourceGeneratorExecutionVersions( - SourceGeneratorExecutionVersionMap sourceGeneratorExecutionVersions, CancellationToken cancellationToken) + SourceGeneratorExecutionVersionMap sourceGeneratorExecutionVersions) { var versionMapBuilder = _sourceGeneratorExecutionVersionMap.Map.ToBuilder(); var newIdToTrackerMapBuilder = _projectIdToTrackerMap.ToBuilder(); @@ -1270,8 +1270,6 @@ public SolutionCompilationState UpdateSpecificSourceGeneratorExecutionVersions( foreach (var (projectId, sourceGeneratorExecutionVersion) in sourceGeneratorExecutionVersions.Map) { - cancellationToken.ThrowIfCancellationRequested(); - var currentExecutionVersion = versionMapBuilder[projectId]; // Nothing to do if already at this version. @@ -1289,7 +1287,7 @@ public SolutionCompilationState UpdateSpecificSourceGeneratorExecutionVersions( // if the major version has changed then we also want to drop the generator driver so that we're rerun // generators from scratch. var forceRegeneration = currentExecutionVersion.MajorVersion != sourceGeneratorExecutionVersion.MajorVersion; - var newTracker = existingTracker.WithCreationPolicy(create: true, forceRegeneration, cancellationToken); + var newTracker = existingTracker.WithCreateCreationPolicy(forceRegeneration); if (newTracker != existingTracker) newIdToTrackerMapBuilder[projectId] = newTracker; } @@ -1325,7 +1323,7 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell // Since we're freezing, set both generators and skeletons to not be created. We don't want to take any // perf hit on either of those at all for our clients. - var newTracker = oldTracker.WithCreationPolicy(create: false, forceRegeneration: false, cancellationToken); + var newTracker = oldTracker.WithDoNotCreateCreationPolicy(cancellationToken); if (oldTracker == newTracker) continue; diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs index 5ff30d9caa013..a53b16e4e9e24 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs @@ -48,7 +48,7 @@ await this.SetCurrentSolutionAsync( oldSolution => { var updates = GetUpdatedSourceGeneratorVersions(oldSolution, projectIds); - return oldSolution.UpdateSpecificSourceGeneratorExecutionVersions(updates, cancellationToken); + return oldSolution.UpdateSpecificSourceGeneratorExecutionVersions(updates); }, static (_, _) => (WorkspaceChangeKind.SolutionChanged, projectId: null, documentId: null), onBeforeUpdate: null, diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index 9637131ca911a..04d1a3945852c 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -137,7 +137,7 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum, Ca } #endif - solution = solution.UpdateSpecificSourceGeneratorExecutionVersions(newVersions, cancellationToken); + solution = solution.UpdateSpecificSourceGeneratorExecutionVersions(newVersions); } #if DEBUG diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index 956e6cf891047..5eb7af3be2a9a 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -256,7 +256,7 @@ private async Task ComputeDisconnectedSolutionAsync( var newVersions = await assetProvider.GetAssetAsync( AssetPathKind.SolutionSourceGeneratorExecutionVersionMap, newSolutionCompilationChecksums.SourceGeneratorExecutionVersionMap, cancellationToken).ConfigureAwait(false); - solution = solution.UpdateSpecificSourceGeneratorExecutionVersions(newVersions, cancellationToken); + solution = solution.UpdateSpecificSourceGeneratorExecutionVersions(newVersions); } return solution; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs index 79f96719795b7..9250abe6c8432 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Shared.Extensions; @@ -79,4 +81,25 @@ public static TextDocument GetRequiredTextDocument(this Solution solution, Docum private static Exception CreateDocumentNotFoundException() => new InvalidOperationException(WorkspaceExtensionsResources.The_solution_does_not_contain_the_specified_document); + +#if !CODE_STYLE + public static Solution WithUpToDateSourceGeneratorDocuments(this Solution solution, ImmutableArray projectIds) + { + // If the solution is already in automatic mode, then SG documents are already always up to date. + var configuration = solution.Services.GetRequiredService().Options; + if (configuration.SourceGeneratorExecution is SourceGeneratorExecutionPreference.Automatic) + return solution; + + var projectIdToSourceGenerationVersion = ImmutableSortedDictionary.CreateBuilder(); + + foreach (var projectId in projectIds) + { + var currentVersion = solution.GetSourceGeneratorExecutionVersion(projectId); + projectIdToSourceGenerationVersion.Add(projectId, currentVersion.IncrementMinorVersion()); + } + + return solution.UpdateSpecificSourceGeneratorExecutionVersions( + new SourceGeneratorExecutionVersionMap(projectIdToSourceGenerationVersion.ToImmutable())); + } +#endif } From 399f0b27acb31902faaefeb053a54a00b96cbfed Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jun 2024 14:01:57 -0700 Subject: [PATCH 3/4] Fix --- .../ActiveStatementTrackingService.cs | 4 ++++ .../Portable/EditAndContinue/DebuggingSession.cs | 3 +++ .../EditAndContinue/EditAndContinueService.cs | 3 +++ .../EditAndContinueWorkspaceServiceTests.cs | 13 ++++++++++--- .../Core/Extensions/ISolutionExtensions.cs | 9 ++++++--- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs index 1109908ec0739..48c04aed76a34 100644 --- a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; @@ -198,6 +199,9 @@ internal async Task TrackActiveSpansAsync(Solution solution) return; } + // Make sure the solution snapshot has all source-generated documents in the affected projects up-to-date: + solution = solution.WithUpToDateSourceGeneratorDocuments(openDocumentIds.Select(static d => d.ProjectId)); + var baseActiveStatementSpans = await _spanProvider.GetBaseActiveStatementSpansAsync(solution, openDocumentIds, cancellationToken).ConfigureAwait(false); if (baseActiveStatementSpans.IsDefault) { diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index e6aa59659e9ad..2f6cbb396eadd 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -518,6 +518,9 @@ public async ValueTask EmitSolutionUpdateAsync( var updateId = new UpdateId(Id, Interlocked.Increment(ref _updateOrdinal)); + // Make sure the solution snapshot has all source-generated documents up-to-date. + solution = solution.WithUpToDateSourceGeneratorDocuments(solution.ProjectIds); + var solutionUpdate = await EditSession.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, updateId, cancellationToken).ConfigureAwait(false); solutionUpdate.Log(EditAndContinueService.Log, updateId); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs index 010063ad16ddb..80b9b57f741b5 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs @@ -160,6 +160,9 @@ public async ValueTask StartDebuggingSessionAsync( initialDocumentStates = []; } + // Make sure the solution snapshot has all source-generated documents up-to-date: + solution = solution.WithUpToDateSourceGeneratorDocuments(solution.ProjectIds); + var sessionId = new DebuggingSessionId(Interlocked.Increment(ref s_debuggingSessionId)); var session = new DebuggingSession(sessionId, solution, debuggerService, _compilationOutputsProvider, sourceTextProvider, initialDocumentStates, reportDiagnostics); diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index fdc5b55097ca3..c87e407278078 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -2517,8 +2517,9 @@ partial class C { int Y = 2; } EndDebuggingSession(debuggingSession); } - [Fact] - public async Task ValidSignificantChange_SourceGenerators_DocumentUpdate_GeneratedDocumentUpdate() + [Theory] + [CombinatorialData] + internal async Task ValidSignificantChange_SourceGenerators_DocumentUpdate_GeneratedDocumentUpdate(SourceGeneratorExecutionPreference executionPreference) { var sourceV1 = @" /* GENERATE: class G { int X => 1; } */ @@ -2535,13 +2536,19 @@ class C { int Y => 2; } using var workspace = CreateWorkspace(out var solution, out var service); var workspaceConfig = Assert.IsType(workspace.Services.GetRequiredService()); - workspaceConfig.Options = new WorkspaceConfigurationOptions(SourceGeneratorExecutionPreference.Balanced); + workspaceConfig.Options = new WorkspaceConfigurationOptions(executionPreference); (solution, var document1) = AddDefaultTestProject(solution, sourceV1, generator); var moduleId = EmitLibrary(sourceV1, generator: generator); LoadLibraryToDebuggee(moduleId); + // Trigger initial source generation before debugging session starts. + // Causes source generator to run on the solution for the first time. + // Futher compilation access won't automatically trigger source generators, + // the EnC service has to do so. + _ = await solution.Projects.Single().GetCompilationAsync(CancellationToken.None); + var debuggingSession = await StartDebuggingSessionAsync(service, solution); EnterBreakState(debuggingSession); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs index 9250abe6c8432..27894dc7cd89b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs @@ -83,7 +83,7 @@ private static Exception CreateDocumentNotFoundException() => new InvalidOperationException(WorkspaceExtensionsResources.The_solution_does_not_contain_the_specified_document); #if !CODE_STYLE - public static Solution WithUpToDateSourceGeneratorDocuments(this Solution solution, ImmutableArray projectIds) + public static Solution WithUpToDateSourceGeneratorDocuments(this Solution solution, IEnumerable projectIds) { // If the solution is already in automatic mode, then SG documents are already always up to date. var configuration = solution.Services.GetRequiredService().Options; @@ -94,8 +94,11 @@ public static Solution WithUpToDateSourceGeneratorDocuments(this Solution soluti foreach (var projectId in projectIds) { - var currentVersion = solution.GetSourceGeneratorExecutionVersion(projectId); - projectIdToSourceGenerationVersion.Add(projectId, currentVersion.IncrementMinorVersion()); + if (!projectIdToSourceGenerationVersion.ContainsKey(projectId)) + { + var currentVersion = solution.GetSourceGeneratorExecutionVersion(projectId); + projectIdToSourceGenerationVersion.Add(projectId, currentVersion.IncrementMinorVersion()); + } } return solution.UpdateSpecificSourceGeneratorExecutionVersions( From f9da02897b1e4555e6b9570da8d6f4d4ca11549c Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 14 Jun 2024 09:52:08 -0700 Subject: [PATCH 4/4] Call EnqueueUpdateSourceGeneratorVersion in CommitUpdatesAsync --- .../Core/EditAndContinue/EditAndContinueLanguageService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index f571e8f6fbb54..15642db98bd89 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -220,6 +220,8 @@ public async ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { } + + workspaceProvider.Value.Workspace.EnqueueUpdateSourceGeneratorVersion(projectId: null, forceRegeneration: false); } public async ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken)