-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move source-generators out of ProjectState and entirely into SolutinoCompilationState #72834
Changes from all commits
8af4d04
b625c7a
7d13eae
d26d6d5
e3b7872
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -422,7 +422,7 @@ async Task<InProgressState> CollapseInProgressStateAsync(InProgressState initial | |
// Also transform the compilation that has generated files; we won't do that though if the transformation either would cause problems with | ||
// the generated documents, or if don't have any source generators in the first place. | ||
if (translationAction.CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput && | ||
translationAction.OldProjectState.SourceGenerators.Any()) | ||
GetSourceGenerators(translationAction.OldProjectState).Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: this needs to not instantiate generators, but instead call into OOP to determine this info. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering that the SDK delivers source generators that are enabled by default, it might make sense to just assume the |
||
{ | ||
staleCompilationWithGeneratedDocuments = await translationAction.TransformCompilationAsync(staleCompilationWithGeneratedDocuments, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
@@ -774,7 +774,7 @@ public async ValueTask<TextDocumentStates<SourceGeneratedDocumentState>> GetSour | |
SolutionCompilationState compilationState, CancellationToken cancellationToken) | ||
{ | ||
// If we don't have any generators, then we know we have no generated files, so we can skip the computation entirely. | ||
if (!this.ProjectState.SourceGenerators.Any()) | ||
if (!GetSourceGenerators(this.ProjectState).Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same. |
||
{ | ||
return TextDocumentStates<SourceGeneratedDocumentState>.Empty; | ||
} | ||
|
@@ -787,7 +787,7 @@ public async ValueTask<TextDocumentStates<SourceGeneratedDocumentState>> GetSour | |
public async ValueTask<ImmutableArray<Diagnostic>> GetSourceGeneratorDiagnosticsAsync( | ||
SolutionCompilationState compilationState, CancellationToken cancellationToken) | ||
{ | ||
if (!this.ProjectState.SourceGenerators.Any()) | ||
if (!GetSourceGenerators(this.ProjectState).Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
{ | ||
return []; | ||
} | ||
|
@@ -816,7 +816,7 @@ public async ValueTask<ImmutableArray<Diagnostic>> GetSourceGeneratorDiagnostics | |
|
||
public async ValueTask<GeneratorDriverRunResult?> GetSourceGeneratorRunResultAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken) | ||
{ | ||
if (!this.ProjectState.SourceGenerators.Any()) | ||
if (!GetSourceGenerators(this.ProjectState).Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same. |
||
{ | ||
return null; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -233,7 +233,7 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( | |
// 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()) | ||
if (!GetSourceGenerators(this.ProjectState).Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this call is fine. We're already in OOP. |
||
return (compilationWithoutGeneratedFiles, TextDocumentStates<SourceGeneratedDocumentState>.Empty, generatorDriver); | ||
|
||
// If we don't already have an existing generator driver, create one from scratch | ||
|
@@ -268,7 +268,9 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( | |
|
||
var runResult = generatorDriver.GetRunResult(); | ||
|
||
telemetryCollector?.CollectRunResult(runResult, generatorDriver.GetTimingInfo(), ProjectState); | ||
telemetryCollector?.CollectRunResult( | ||
runResult, generatorDriver.GetTimingInfo(), | ||
g => GetAnalyzerReference(this.ProjectState, g)); | ||
|
||
// We may be able to reuse compilationWithStaleGeneratedTrees if the generated trees are identical. We will assign null | ||
// to compilationWithStaleGeneratedTrees if we at any point realize it can't be used. We'll first check the count of trees | ||
|
@@ -293,7 +295,7 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( | |
if (IsGeneratorRunResultToIgnore(generatorResult)) | ||
continue; | ||
|
||
var generatorAnalyzerReference = this.ProjectState.GetAnalyzerReferenceForGenerator(generatorResult.Generator); | ||
var generatorAnalyzerReference = GetAnalyzerReference(this.ProjectState, generatorResult.Generator); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this call is fine. We're already in OOP. |
||
|
||
foreach (var generatedSource in generatorResult.GeneratedSources) | ||
{ | ||
|
@@ -394,7 +396,7 @@ static GeneratorDriver CreateGeneratorDriver(ProjectState projectState) | |
|
||
return compilationFactory.CreateGeneratorDriver( | ||
projectState.ParseOptions!, | ||
projectState.SourceGenerators.ToImmutableArray(), | ||
GetSourceGenerators(projectState), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this call is fine. We're already in OOP. |
||
projectState.AnalyzerOptions.AnalyzerConfigOptionsProvider, | ||
additionalTexts); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -355,7 +355,7 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver _) | |
.ReplaceAdditionalTexts(this.NewProjectState.AdditionalDocumentStates.SelectAsArray(static documentState => documentState.AdditionalText)) | ||
.WithUpdatedParseOptions(this.NewProjectState.ParseOptions!) | ||
.WithUpdatedAnalyzerConfigOptions(this.NewProjectState.AnalyzerOptions.AnalyzerConfigOptionsProvider) | ||
.ReplaceGenerators(this.NewProjectState.SourceGenerators.ToImmutableArray()); | ||
.ReplaceGenerators(GetSourceGenerators(this.NewProjectState)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this need to not happen on the VS side. |
||
|
||
return generatorDriver; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// 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.Generic; | ||
using System.Collections.Immutable; | ||
using System.Runtime.CompilerServices; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Shared.Collections; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis; | ||
|
||
using AnalyzerReferencesToSourceGenerators = ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, SolutionCompilationState.SourceGeneratorMap>; | ||
|
||
internal partial class SolutionCompilationState | ||
{ | ||
internal sealed record SourceGeneratorMap( | ||
ImmutableArray<ISourceGenerator> SourceGenerators, | ||
ImmutableDictionary<ISourceGenerator, AnalyzerReference> SourceGeneratorToAnalyzerReference); | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// Cached mapping from language (only C#/VB since those are the only languages that support analyzers) to the lists | ||
/// of analyzer references (see <see cref="ProjectState.AnalyzerReferences"/>) to all the <see | ||
/// cref="ISourceGenerator"/>s produced by those references. This should only be created and cached on the OOP side | ||
/// of things so that we don't cause source generators to be loaded (and fixed) within VS (which is .net framework | ||
/// only). | ||
/// </summary> | ||
private static readonly ImmutableArray<(string language, AnalyzerReferencesToSourceGenerators referencesToGenerators, AnalyzerReferencesToSourceGenerators.CreateValueCallback callback)> s_languageToAnalyzerReferencesToSourceGeneratorsMap = | ||
[ | ||
(LanguageNames.CSharp, new(), (static rs => ComputeSourceGenerators(rs, LanguageNames.CSharp))), | ||
(LanguageNames.VisualBasic, new(), (static rs => ComputeSourceGenerators(rs, LanguageNames.VisualBasic))), | ||
]; | ||
|
||
private static SourceGeneratorMap ComputeSourceGenerators(IReadOnlyList<AnalyzerReference> analyzerReferences, string language) | ||
{ | ||
using var generators = TemporaryArray<ISourceGenerator>.Empty; | ||
var generatorToAnalyzerReference = ImmutableDictionary.CreateBuilder<ISourceGenerator, AnalyzerReference>(); | ||
|
||
foreach (var reference in analyzerReferences) | ||
{ | ||
foreach (var generator in reference.GetGenerators(language).Distinct()) | ||
{ | ||
generators.Add(generator); | ||
generatorToAnalyzerReference.Add(generator, reference); | ||
} | ||
} | ||
|
||
return new(generators.ToImmutableAndClear(), generatorToAnalyzerReference.ToImmutable()); | ||
} | ||
|
||
private static ImmutableArray<ISourceGenerator> GetSourceGenerators(ProjectState projectState) | ||
=> GetSourceGenerators(projectState.Language, projectState.AnalyzerReferences); | ||
|
||
private static ImmutableArray<ISourceGenerator> GetSourceGenerators(string language, IReadOnlyList<AnalyzerReference> analyzerReferences) | ||
{ | ||
var map = GetSourceGeneratorMap(language, analyzerReferences); | ||
return map is null ? [] : map.SourceGenerators; | ||
} | ||
|
||
private static AnalyzerReference GetAnalyzerReference(ProjectState projectState, ISourceGenerator sourceGenerator) | ||
{ | ||
var map = GetSourceGeneratorMap(projectState.Language, projectState.AnalyzerReferences); | ||
Contract.ThrowIfNull(map); | ||
return map.SourceGeneratorToAnalyzerReference[sourceGenerator]; | ||
} | ||
|
||
private static SourceGeneratorMap? GetSourceGeneratorMap(string language, IReadOnlyList<AnalyzerReference> analyzerReferences) | ||
{ | ||
var tupleOpt = s_languageToAnalyzerReferencesToSourceGeneratorsMap.FirstOrNull(static (t, language) => t.language == language, language); | ||
if (tupleOpt is null) | ||
return null; | ||
|
||
var tuple = tupleOpt.Value; | ||
return tuple.referencesToGenerators.GetValue(analyzerReferences, tuple.callback); | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was too broad a surface area. we def did not want to expose inside the workspace that you could get to ISourceGenerator instances.