From acd41ef987426229d7e3546907ac3f76ae695312 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Apr 2024 09:21:32 -0700 Subject: [PATCH 1/4] Move code that retrieves source generators to running in OOP only --- ...ationState.CompilationTracker_Generators.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs index cab49931964b5..bafce554350c2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs @@ -46,13 +46,6 @@ private partial class CompilationTracker : ICompilationTracker return (compilationWithGeneratedFiles, generatorInfo.Documents, generatorInfo.Driver); } - if (!this.ProjectState.SourceGenerators.Any()) - { - // We don't have any source generators. Trivially bail out. - var compilationWithGeneratedFiles = compilationWithoutGeneratedFiles; - return (compilationWithGeneratedFiles, TextDocumentStates.Empty, generatorInfo.Driver); - } - return await ComputeNewGeneratorInfoAsync( compilationState, compilationWithoutGeneratedFiles, @@ -124,12 +117,17 @@ private partial class CompilationTracker : ICompilationTracker if (!infosOpt.HasValue) return null; + var infos = infosOpt.Value; + + // If there are no generated documents, bail out immediately. + if (infos.Length == 0) + return (compilationWithoutGeneratedFiles, TextDocumentStates.Empty); + // Next, figure out what is different locally. Specifically, what documents we don't know about, or we // know about but whose text contents are different. using var _1 = ArrayBuilder.GetInstance(out var documentsToAddOrUpdate); using var _2 = PooledDictionary.GetInstance(out var documentIdToIndex); - var infos = infosOpt.Value; foreach (var (documentIdentity, contentIdentity, _) in infos) { var documentId = documentIdentity.DocumentId; @@ -243,6 +241,10 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( Compilation? compilationWithStaleGeneratedTrees, CancellationToken cancellationToken) { + // If we don't have any source generators. Trivially bail out. + if (!this.ProjectState.SourceGenerators.Any()) + return (compilationWithoutGeneratedFiles, TextDocumentStates.Empty, generatorDriver); + // If we don't already have an existing generator driver, create one from scratch generatorDriver ??= CreateGeneratorDriver(this.ProjectState); From c888b57b9ce86e1a0ae2c6dfd4518743a1213d51 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Apr 2024 09:26:22 -0700 Subject: [PATCH 2/4] Add docs --- ...SolutionCompilationState.CompilationTracker_Generators.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs index bafce554350c2..1bc2304e1cf9b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs @@ -241,7 +241,10 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( Compilation? compilationWithStaleGeneratedTrees, CancellationToken cancellationToken) { - // If we don't have any source generators. Trivially bail out. + // If we don't have any source generators. Trivially bail out. Note: this check is intentionally don't in + // the "InCurrentProcess" call so that it will normally run only in the OOP process, thus ensuring that we + // get accurate information about what SourceGenerators we actually have (say, in case they they are rebuilt + // by the user while VS is running). if (!this.ProjectState.SourceGenerators.Any()) return (compilationWithoutGeneratedFiles, TextDocumentStates.Empty, generatorDriver); From 25ee9916c6ebdca26ac8ec342ae489ac360ec293 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Apr 2024 09:37:07 -0700 Subject: [PATCH 3/4] Inline method --- ...tionState.CompilationTracker_Generators.cs | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs index 1bc2304e1cf9b..5634354190f18 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs @@ -46,33 +46,16 @@ private partial class CompilationTracker : ICompilationTracker return (compilationWithGeneratedFiles, generatorInfo.Documents, generatorInfo.Driver); } - return await ComputeNewGeneratorInfoAsync( - compilationState, - compilationWithoutGeneratedFiles, - generatorInfo.Documents, - generatorInfo.Driver, - compilationWithStaleGeneratedTrees, - cancellationToken).ConfigureAwait(false); - } - - private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments, GeneratorDriver? generatorDriver)> ComputeNewGeneratorInfoAsync( - SolutionCompilationState compilationState, - Compilation compilationWithoutGeneratedFiles, - TextDocumentStates oldGeneratedDocuments, - GeneratorDriver? oldGeneratorDriver, - Compilation? compilationWithStaleGeneratedTrees, - CancellationToken cancellationToken) - { // First try to compute the SG docs in the remote process (if we're the host process), syncing the results // back over to us to ensure that both processes are in total agreement about the SG docs and their // contents. var result = await TryComputeNewGeneratorInfoInRemoteProcessAsync( - compilationState, compilationWithoutGeneratedFiles, oldGeneratedDocuments, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); + compilationState, compilationWithoutGeneratedFiles, generatorInfo.Documents, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); if (result.HasValue) { // Since we ran the SG work out of process, we could not have created or modified the driver passed in. // So just pass what we got in right back out. - return (result.Value.compilationWithGeneratedFiles, result.Value.generatedDocuments, oldGeneratorDriver); + return (result.Value.compilationWithGeneratedFiles, result.Value.generatedDocuments, generatorInfo.Driver); } // If that failed (OOP crash, or we are the OOP process ourselves), then generate the SG docs locally. @@ -80,8 +63,8 @@ private partial class CompilationTracker : ICompilationTracker return await ComputeNewGeneratorInfoInCurrentProcessAsync( telemetryCollector, compilationWithoutGeneratedFiles, - oldGeneratedDocuments, - oldGeneratorDriver, + generatorInfo.Documents, + generatorInfo.Driver, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); } From df2cd88f9220caaf9f396a72091f3764f5c5b67d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Apr 2024 09:39:44 -0700 Subject: [PATCH 4/4] Simplify api shapes --- ...tionCompilationState.CompilationTracker.cs | 5 +- ...tionState.CompilationTracker_Generators.cs | 49 ++++++++++--------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 1ba446df1396d..6c524abf9010b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -569,7 +569,7 @@ await compilationState.GetCompilationAsync( } // We will finalize the compilation by adding full contents here. - var (compilationWithGeneratedDocuments, generatedDocuments, generatorDriver) = await AddExistingOrComputeNewGeneratorInfoAsync( + var (compilationWithGeneratedDocuments, nextGeneratorInfo) = await AddExistingOrComputeNewGeneratorInfoAsync( creationPolicy, compilationState, compilationWithoutGeneratedDocuments, @@ -577,9 +577,6 @@ await compilationState.GetCompilationAsync( staleCompilationWithGeneratedDocuments, cancellationToken).ConfigureAwait(false); - // After producing the sg documents, we must always be in the final state for the generator data. - var nextGeneratorInfo = new CompilationTrackerGeneratorInfo(generatedDocuments, generatorDriver); - var finalState = FinalCompilationTrackerState.Create( creationPolicy, compilationWithGeneratedDocuments, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs index 5634354190f18..c281f43cda13c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs @@ -27,7 +27,7 @@ internal partial class SolutionCompilationState { private partial class CompilationTracker : ICompilationTracker { - private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments, GeneratorDriver? generatorDriver)> AddExistingOrComputeNewGeneratorInfoAsync( + private async Task<(Compilation compilationWithGeneratedFiles, CompilationTrackerGeneratorInfo nextGeneratorInfo)> AddExistingOrComputeNewGeneratorInfoAsync( CreationPolicy creationPolicy, SolutionCompilationState compilationState, Compilation compilationWithoutGeneratedFiles, @@ -43,30 +43,35 @@ private partial class CompilationTracker : ICompilationTracker static (state, cancellationToken) => state.GetSyntaxTreeAsync(cancellationToken), cancellationToken).ConfigureAwait(false); var compilationWithGeneratedFiles = compilationWithoutGeneratedFiles.AddSyntaxTrees(generatedSyntaxTrees); - return (compilationWithGeneratedFiles, generatorInfo.Documents, generatorInfo.Driver); - } - // First try to compute the SG docs in the remote process (if we're the host process), syncing the results - // back over to us to ensure that both processes are in total agreement about the SG docs and their - // contents. - var result = await TryComputeNewGeneratorInfoInRemoteProcessAsync( - compilationState, compilationWithoutGeneratedFiles, generatorInfo.Documents, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); - if (result.HasValue) - { - // Since we ran the SG work out of process, we could not have created or modified the driver passed in. - // So just pass what we got in right back out. - return (result.Value.compilationWithGeneratedFiles, result.Value.generatedDocuments, generatorInfo.Driver); + // Return the old generator info as is. + return (compilationWithGeneratedFiles, generatorInfo); } + else + { + // First try to compute the SG docs in the remote process (if we're the host process), syncing the results + // back over to us to ensure that both processes are in total agreement about the SG docs and their + // contents. + var result = await TryComputeNewGeneratorInfoInRemoteProcessAsync( + compilationState, compilationWithoutGeneratedFiles, generatorInfo.Documents, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); + if (result.HasValue) + { + // Since we ran the SG work out of process, we could not have created or modified the driver passed in. + // So just pass what we got in right back out. + return (result.Value.compilationWithGeneratedFiles, new(result.Value.generatedDocuments, generatorInfo.Driver)); + } - // If that failed (OOP crash, or we are the OOP process ourselves), then generate the SG docs locally. - var telemetryCollector = compilationState.SolutionState.Services.GetService(); - return await ComputeNewGeneratorInfoInCurrentProcessAsync( - telemetryCollector, - compilationWithoutGeneratedFiles, - generatorInfo.Documents, - generatorInfo.Driver, - compilationWithStaleGeneratedTrees, - cancellationToken).ConfigureAwait(false); + // If that failed (OOP crash, or we are the OOP process ourselves), then generate the SG docs locally. + var telemetryCollector = compilationState.SolutionState.Services.GetService(); + var (compilationWithGeneratedFiles, nextGeneratedDocuments, nextGeneratorDriver) = await ComputeNewGeneratorInfoInCurrentProcessAsync( + telemetryCollector, + compilationWithoutGeneratedFiles, + generatorInfo.Documents, + generatorInfo.Driver, + compilationWithStaleGeneratedTrees, + cancellationToken).ConfigureAwait(false); + return (compilationWithGeneratedFiles, new(nextGeneratedDocuments, nextGeneratorDriver)); + } } private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments)?> TryComputeNewGeneratorInfoInRemoteProcessAsync(