Skip to content

Commit c45376a

Browse files
authored
Ensure source generated documents are up-to-date before analyzing EnC changes (#73989)
1 parent 0198365 commit c45376a

File tree

15 files changed

+85
-35
lines changed

15 files changed

+85
-35
lines changed

src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using Microsoft.CodeAnalysis.Host.Mef;
2020
using Microsoft.CodeAnalysis.PooledObjects;
2121
using Microsoft.CodeAnalysis.Shared.TestHooks;
22+
using Microsoft.CodeAnalysis.Shared.Extensions;
2223
using Microsoft.CodeAnalysis.Text;
2324
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
2425
using Microsoft.VisualStudio.Text;
@@ -198,6 +199,9 @@ internal async Task TrackActiveSpansAsync(Solution solution)
198199
return;
199200
}
200201

202+
// Make sure the solution snapshot has all source-generated documents in the affected projects up-to-date:
203+
solution = solution.WithUpToDateSourceGeneratorDocuments(openDocumentIds.Select(static d => d.ProjectId));
204+
201205
var baseActiveStatementSpans = await _spanProvider.GetBaseActiveStatementSpansAsync(solution, openDocumentIds, cancellationToken).ConfigureAwait(false);
202206
if (baseActiveStatementSpans.IsDefault)
203207
{

src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ public async ValueTask CommitUpdatesAsync(CancellationToken cancellationToken)
220220
catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken))
221221
{
222222
}
223+
224+
workspaceProvider.Value.Workspace.EnqueueUpdateSourceGeneratorVersion(projectId: null, forceRegeneration: false);
223225
}
224226

225227
public async ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken)

src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,9 @@ public async ValueTask<EmitSolutionUpdateResults> EmitSolutionUpdateAsync(
518518

519519
var updateId = new UpdateId(Id, Interlocked.Increment(ref _updateOrdinal));
520520

521+
// Make sure the solution snapshot has all source-generated documents up-to-date.
522+
solution = solution.WithUpToDateSourceGeneratorDocuments(solution.ProjectIds);
523+
521524
var solutionUpdate = await EditSession.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, updateId, cancellationToken).ConfigureAwait(false);
522525

523526
solutionUpdate.Log(EditAndContinueService.Log, updateId);

src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ public async ValueTask<DebuggingSessionId> StartDebuggingSessionAsync(
160160
initialDocumentStates = [];
161161
}
162162

163+
// Make sure the solution snapshot has all source-generated documents up-to-date:
164+
solution = solution.WithUpToDateSourceGeneratorDocuments(solution.ProjectIds);
165+
163166
var sessionId = new DebuggingSessionId(Interlocked.Increment(ref s_debuggingSessionId));
164167
var session = new DebuggingSession(sessionId, solution, debuggerService, _compilationOutputsProvider, sourceTextProvider, initialDocumentStates, reportDiagnostics);
165168

src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Microsoft.CodeAnalysis.CSharp;
1818
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
1919
using Microsoft.CodeAnalysis.Emit;
20+
using Microsoft.CodeAnalysis.Host;
2021
using Microsoft.CodeAnalysis.Shared.Extensions;
2122
using Microsoft.CodeAnalysis.Test.Utilities;
2223
using Microsoft.CodeAnalysis.Text;
@@ -2516,8 +2517,9 @@ partial class C { int Y = 2; }
25162517
EndDebuggingSession(debuggingSession);
25172518
}
25182519

2519-
[Fact]
2520-
public async Task ValidSignificantChange_SourceGenerators_DocumentUpdate_GeneratedDocumentUpdate()
2520+
[Theory]
2521+
[CombinatorialData]
2522+
internal async Task ValidSignificantChange_SourceGenerators_DocumentUpdate_GeneratedDocumentUpdate(SourceGeneratorExecutionPreference executionPreference)
25212523
{
25222524
var sourceV1 = @"
25232525
/* GENERATE: class G { int X => 1; } */
@@ -2532,12 +2534,21 @@ class C { int Y => 2; }
25322534

25332535
var generator = new TestSourceGenerator() { ExecuteImpl = GenerateSource };
25342536

2535-
using var _ = CreateWorkspace(out var solution, out var service);
2537+
using var workspace = CreateWorkspace(out var solution, out var service);
2538+
var workspaceConfig = Assert.IsType<TestWorkspaceConfigurationService>(workspace.Services.GetRequiredService<IWorkspaceConfigurationService>());
2539+
workspaceConfig.Options = new WorkspaceConfigurationOptions(executionPreference);
2540+
25362541
(solution, var document1) = AddDefaultTestProject(solution, sourceV1, generator);
25372542

25382543
var moduleId = EmitLibrary(sourceV1, generator: generator);
25392544
LoadLibraryToDebuggee(moduleId);
25402545

2546+
// Trigger initial source generation before debugging session starts.
2547+
// Causes source generator to run on the solution for the first time.
2548+
// Futher compilation access won't automatically trigger source generators,
2549+
// the EnC service has to do so.
2550+
_ = await solution.Projects.Single().GetCompilationAsync(CancellationToken.None);
2551+
25412552
var debuggingSession = await StartDebuggingSessionAsync(service, solution);
25422553

25432554
EnterBreakState(debuggingSession);

src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ public abstract class EditAndContinueWorkspaceTestBase : TestBase
5353

5454
internal TestWorkspace CreateWorkspace(out Solution solution, out EditAndContinueService service, Type[]? additionalParts = null)
5555
{
56-
var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features.AddParts(additionalParts), solutionTelemetryId: s_solutionTelemetryId);
56+
var composition = FeaturesTestCompositions.Features
57+
.AddParts(typeof(TestWorkspaceConfigurationService))
58+
.AddParts(additionalParts);
59+
60+
var workspace = new TestWorkspace(composition: composition, solutionTelemetryId: s_solutionTelemetryId);
5761
solution = workspace.CurrentSolution;
5862
service = GetEditAndContinueService(workspace);
5963
return workspace;

src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,8 +1576,8 @@ internal Solution WithFrozenSourceGeneratedDocuments(ImmutableArray<(SourceGener
15761576
=> WithCompilationState(_compilationState.WithFrozenSourceGeneratedDocuments(documents));
15771577

15781578
/// <inheritdoc cref="SolutionCompilationState.UpdateSpecificSourceGeneratorExecutionVersions"/>
1579-
internal Solution UpdateSpecificSourceGeneratorExecutionVersions(SourceGeneratorExecutionVersionMap sourceGeneratorExecutionVersionMap, CancellationToken cancellationToken)
1580-
=> WithCompilationState(_compilationState.UpdateSpecificSourceGeneratorExecutionVersions(sourceGeneratorExecutionVersionMap, cancellationToken));
1579+
internal Solution UpdateSpecificSourceGeneratorExecutionVersions(SourceGeneratorExecutionVersionMap sourceGeneratorExecutionVersionMap)
1580+
=> WithCompilationState(_compilationState.UpdateSpecificSourceGeneratorExecutionVersions(sourceGeneratorExecutionVersionMap));
15811581

15821582
/// <summary>
15831583
/// Undoes the operation of <see cref="WithFrozenSourceGeneratedDocument"/>; any frozen source generated document is allowed

src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -679,13 +679,6 @@ private async Task<bool> HasSuccessfullyLoadedSlowAsync(
679679
return finalState.HasSuccessfullyLoaded;
680680
}
681681

682-
public ICompilationTracker WithCreationPolicy(bool create, bool forceRegeneration, CancellationToken cancellationToken)
683-
{
684-
return create
685-
? WithCreateCreationPolicy(forceRegeneration)
686-
: WithDoNotCreateCreationPolicy(forceRegeneration, cancellationToken);
687-
}
688-
689682
public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration)
690683
{
691684
var state = this.ReadState();
@@ -742,13 +735,8 @@ public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration)
742735
skeletonReferenceCacheToClone: _skeletonReferenceCache);
743736
}
744737

745-
public ICompilationTracker WithDoNotCreateCreationPolicy(
746-
bool forceRegeneration, CancellationToken cancellationToken)
738+
public ICompilationTracker WithDoNotCreateCreationPolicy(CancellationToken cancellationToken)
747739
{
748-
// We do not expect this to ever be passed true. This is for freezing generators, and no callers
749-
// (currently) will ask to drop drivers when they do that.
750-
Contract.ThrowIfTrue(forceRegeneration);
751-
752740
var state = this.ReadState();
753741

754742
// We're freezing the solution for features where latency performance is paramount. Do not run SGs or

src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,17 @@ public ICompilationTracker Fork(ProjectState newProject, TranslationAction? tran
7575
throw new NotImplementedException();
7676
}
7777

78-
public ICompilationTracker WithCreationPolicy(bool create, bool forceRegeneration, CancellationToken cancellationToken)
78+
public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration)
7979
{
80-
var underlyingTracker = this.UnderlyingTracker.WithCreationPolicy(create, forceRegeneration, cancellationToken);
80+
var underlyingTracker = this.UnderlyingTracker.WithCreateCreationPolicy(forceRegeneration);
81+
return underlyingTracker == this.UnderlyingTracker
82+
? this
83+
: new GeneratedFileReplacingCompilationTracker(underlyingTracker, _replacementDocumentStates);
84+
}
85+
86+
public ICompilationTracker WithDoNotCreateCreationPolicy(CancellationToken cancellationToken)
87+
{
88+
var underlyingTracker = this.UnderlyingTracker.WithDoNotCreateCreationPolicy(cancellationToken);
8189
return underlyingTracker == this.UnderlyingTracker
8290
? this
8391
: new GeneratedFileReplacingCompilationTracker(underlyingTracker, _replacementDocumentStates);

src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ bool ContainsAssemblyOrModuleOrDynamic(
4040
Task<Compilation> GetCompilationAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken);
4141

4242
/// <summary>
43-
/// Updates the creation policy for this tracker. A value of <see langword="true"/> will set this to <see
44-
/// cref="CreationPolicy.Create"/> and <see langword="false"/> will set it to <see
45-
/// cref="CreationPolicy.DoNotCreate"/>.
43+
/// Updates the creation policy for this tracker. Setting it to <see cref="CreationPolicy.Create"/>.
4644
/// </summary>
47-
/// <param name="forceRegeneration">When switching to <paramref name="create"/>, this will force source
48-
/// generated documents to be created.</param>
49-
ICompilationTracker WithCreationPolicy(bool create, bool forceRegeneration, CancellationToken cancellationToken);
45+
/// <param name="forceRegeneration">Forces source generated documents to be created by dumping any existing <see
46+
/// cref="GeneratorDriver"/> and rerunning generators from scratch for this tracker.</param>
47+
ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration);
48+
49+
/// <summary>
50+
/// Updates the creation policy for this tracker. Setting it to <see cref="CreationPolicy.DoNotCreate"/>.
51+
/// </summary>
52+
ICompilationTracker WithDoNotCreateCreationPolicy(CancellationToken cancellationToken);
5053

5154
Task<VersionStamp> GetDependentVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken);
5255
Task<VersionStamp> GetDependentSemanticVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken);

0 commit comments

Comments
 (0)