Skip to content
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

Refactor ISyntaxInputNodes #58800

Merged
merged 24 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,6 @@ public void Driver_Table_Compacts_State_Tables_And_Drops_Steps_When_Made_Immutab
DriverStateTable.Builder builder2 = GetBuilder(driverStateTable, trackIncrementalGeneratorSteps: true);
builder2.GetLatestStateTableForNode(callbackNode);

// table persisted in driverStateTable does not have any steps
Assert.False(driverStateTable.GetStateTableOrEmpty<int>(callbackNode).HasTrackedSteps);

// table returned from the first instance was compacted by the builder
Assert.NotNull(passedIn);
AssertTableEntries(passedIn!, new[] { (1, EntryState.Cached, 0), (2, EntryState.Cached, 1), (3, EntryState.Cached, 2), (5, EntryState.Cached, 0), (6, EntryState.Cached, 1) });
Expand Down Expand Up @@ -955,11 +952,12 @@ private DriverStateTable.Builder GetBuilder(DriverStateTable previous, bool trac
ImmutableArray<AdditionalText>.Empty,
ImmutableArray<GeneratorState>.Empty,
previous,
SyntaxStore.Empty,
disabledOutputs: IncrementalGeneratorOutputKind.None,
runtime: TimeSpan.Zero,
trackIncrementalGeneratorSteps: trackIncrementalGeneratorSteps);

return new DriverStateTable.Builder(c, state, ImmutableArray<ISyntaxInputNode>.Empty);
return new DriverStateTable.Builder(c, state, SyntaxStore.Empty.ToBuilder(c, ImmutableArray<SyntaxInputNode>.Empty, trackIncrementalGeneratorSteps, cancellationToken: default));
}

private class CallbackNode<T> : IIncrementalGeneratorNode<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,79 @@ class F
Assert.Equal("fieldD", Assert.Single(fieldsCalledFor));
}

[Fact]
public void IncrementalGenerator_With_Syntax_Filter_Doesnt_Run_When_Compilation_Is_Unchanged()
{
var source1 = @"
#pragma warning disable CS0414
class C
{
string fieldA = null;
}";
var source2 = @"
#pragma warning disable CS0414
class D
{
string fieldB = null;
}";
var source3 = @"
#pragma warning disable CS0414
class E
{
string fieldC = null;
}";
var parseOptions = TestOptions.RegularPreview;
Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions);
compilation.VerifyDiagnostics();

List<string> fieldsCalledFor = new List<string>();
var testGenerator = new PipelineCallbackGenerator(context =>
{
var source = context.SyntaxProvider.CreateSyntaxProvider((c, _) => c is FieldDeclarationSyntax fds, (c, _) => ((FieldDeclarationSyntax)c.Node).Declaration.Variables[0].Identifier.ValueText).WithTrackingName("Fields");
context.RegisterSourceOutput(source, (spc, fieldName) =>
{
spc.AddSource(fieldName, "");
fieldsCalledFor.Add(fieldName);
});
});

GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { new IncrementalGeneratorWrapper(testGenerator) }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: false));
driver = driver.RunGenerators(compilation);

var results = driver.GetRunResult();
Assert.Empty(results.Diagnostics);
Assert.EndsWith("fieldA.cs", results.GeneratedTrees[0].FilePath);
Assert.EndsWith("fieldB.cs", results.GeneratedTrees[1].FilePath);
Assert.EndsWith("fieldC.cs", results.GeneratedTrees[2].FilePath);
Assert.Equal(new[] { "fieldA", "fieldB", "fieldC" }, fieldsCalledFor);

// now re-run the drivers with the same compilation
fieldsCalledFor.Clear();
driver = driver.RunGenerators(compilation);
results = driver.GetRunResult();
Assert.Empty(results.Diagnostics);
Assert.Equal(3, results.GeneratedTrees.Length);

// we produced the expected modified sources, but didn't call for any of the trees
Assert.EndsWith("fieldA.cs", results.GeneratedTrees[0].FilePath);
Assert.EndsWith("fieldB.cs", results.GeneratedTrees[1].FilePath);
Assert.EndsWith("fieldC.cs", results.GeneratedTrees[2].FilePath);
Assert.Empty(fieldsCalledFor);

// now re-run the drivers with the same compilation again to ensure we cached the trees correctly
fieldsCalledFor.Clear();
driver = driver.RunGenerators(compilation);
results = driver.GetRunResult();
Assert.Empty(results.Diagnostics);
Assert.Equal(3, results.GeneratedTrees.Length);

// we produced the expected modified sources, but didn't call for any of the trees
Assert.EndsWith("fieldA.cs", results.GeneratedTrees[0].FilePath);
Assert.EndsWith("fieldB.cs", results.GeneratedTrees[1].FilePath);
Assert.EndsWith("fieldC.cs", results.GeneratedTrees[2].FilePath);
Assert.Empty(fieldsCalledFor);
}

[Fact]
public void IncrementalGenerator_With_Syntax_Filter_And_Changed_Tree_Order()
{
Expand Down Expand Up @@ -1334,7 +1407,10 @@ class E
});
});

GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { new IncrementalGeneratorWrapper(testGenerator) }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true));
GeneratorDriver driver = CSharpGeneratorDriver.Create(
new[] { new IncrementalGeneratorWrapper(testGenerator) },
parseOptions: parseOptions,
driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true));
driver = driver.RunGenerators(compilation);

var results = driver.GetRunResult();
Expand Down Expand Up @@ -1452,7 +1528,10 @@ class C
});
});

GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { new IncrementalGeneratorWrapper(testGenerator) }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true));
GeneratorDriver driver = CSharpGeneratorDriver.Create(
new[] { new IncrementalGeneratorWrapper(testGenerator) },
parseOptions: parseOptions,
driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true));
driver = driver.RunGenerators(compilation);
var results = driver.GetRunResult();
Assert.Collection(results.Results[0].TrackedSteps["Fields"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal GeneratorDriver(GeneratorDriverState state)
internal GeneratorDriver(ParseOptions parseOptions, ImmutableArray<ISourceGenerator> generators, AnalyzerConfigOptionsProvider optionsProvider, ImmutableArray<AdditionalText> additionalTexts, GeneratorDriverOptions driverOptions)
{
(var filteredGenerators, var incrementalGenerators) = GetIncrementalGenerators(generators, SourceExtension);
_state = new GeneratorDriverState(parseOptions, optionsProvider, filteredGenerators, incrementalGenerators, additionalTexts, ImmutableArray.Create(new GeneratorState[filteredGenerators.Length]), DriverStateTable.Empty, driverOptions.DisabledOutputs, runtime: TimeSpan.Zero, driverOptions.TrackIncrementalGeneratorSteps);
_state = new GeneratorDriverState(parseOptions, optionsProvider, filteredGenerators, incrementalGenerators, additionalTexts, ImmutableArray.Create(new GeneratorState[filteredGenerators.Length]), DriverStateTable.Empty, SyntaxStore.Empty, driverOptions.DisabledOutputs, runtime: TimeSpan.Zero, driverOptions.TrackIncrementalGeneratorSteps);
}

public GeneratorDriver RunGenerators(Compilation compilation, CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -171,7 +171,7 @@ internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, Diagnos
var state = _state;
var stateBuilder = ArrayBuilder<GeneratorState>.GetInstance(state.Generators.Length);
var constantSourcesBuilder = ArrayBuilder<SyntaxTree>.GetInstance();
var syntaxInputNodes = ArrayBuilder<ISyntaxInputNode>.GetInstance();
var syntaxInputNodes = ArrayBuilder<SyntaxInputNode>.GetInstance();

for (int i = 0; i < state.IncrementalGenerators.Length; i++)
{
Expand All @@ -183,7 +183,7 @@ internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, Diagnos
if (!generatorState.Initialized)
{
var outputBuilder = ArrayBuilder<IIncrementalGeneratorOutputNode>.GetInstance();
var inputBuilder = ArrayBuilder<ISyntaxInputNode>.GetInstance();
var inputBuilder = ArrayBuilder<SyntaxInputNode>.GetInstance();
var postInitSources = ImmutableArray<GeneratedSyntaxTree>.Empty;
var pipelineContext = new IncrementalGeneratorInitializationContext(inputBuilder, outputBuilder, SourceExtension);

Expand Down Expand Up @@ -241,7 +241,9 @@ internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, Diagnos
}
constantSourcesBuilder.Free();

var driverStateBuilder = new DriverStateTable.Builder(compilation, _state, syntaxInputNodes.ToImmutableAndFree(), cancellationToken);
var syntaxStoreBuilder = _state.SyntaxStore.ToBuilder(compilation, syntaxInputNodes.ToImmutableAndFree(), _state.TrackIncrementalSteps, cancellationToken);

var driverStateBuilder = new DriverStateTable.Builder(compilation, _state, syntaxStoreBuilder, cancellationToken);
for (int i = 0; i < state.IncrementalGenerators.Length; i++)
{
var generatorState = stateBuilder[i];
Expand All @@ -266,7 +268,7 @@ internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, Diagnos
}
}

state = state.With(stateTable: driverStateBuilder.ToImmutable(), generatorStates: stateBuilder.ToImmutableAndFree(), runTime: timer.Elapsed);
state = state.With(stateTable: driverStateBuilder.ToImmutable(), syntaxStore: syntaxStoreBuilder.ToImmutable(), generatorStates: stateBuilder.ToImmutableAndFree(), runTime: timer.Elapsed);
Copy link
Member

@cston cston Jan 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two distinct instances of SyntaxStore - from driverStateBuilder.ToImmutable().SyntaxStore and from syntaxStoreBuilder.ToImmutable(). If it's necessary to pass a SyntaxStore instance directly to With(), consider refactoring to:

Suggested change
state = state.With(stateTable: driverStateBuilder.ToImmutable(), syntaxStore: syntaxStoreBuilder.ToImmutable(), generatorStates: stateBuilder.ToImmutableAndFree(), runTime: timer.Elapsed);
var stateTable = driverStateBuilder.ToImmutable();
state = state.With(stateTable, stateTable.SyntaxStore, generatorStates: stateBuilder.ToImmutableAndFree(), runTime: timer.Elapsed);
``` #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DriverStateTable doesnt actually hold onto the SyntaxStore so we only have the single instance.

return state;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal GeneratorDriverState(ParseOptions parseOptions,
ImmutableArray<AdditionalText> additionalTexts,
ImmutableArray<GeneratorState> generatorStates,
DriverStateTable stateTable,
SyntaxStore syntaxStore,
IncrementalGeneratorOutputKind disabledOutputs,
TimeSpan runtime,
bool trackIncrementalGeneratorSteps)
Expand All @@ -29,6 +30,7 @@ internal GeneratorDriverState(ParseOptions parseOptions,
ParseOptions = parseOptions;
OptionsProvider = optionsProvider;
StateTable = stateTable;
SyntaxStore = syntaxStore;
DisabledOutputs = disabledOutputs;
RunTime = runtime;
TrackIncrementalSteps = trackIncrementalGeneratorSteps;
Expand Down Expand Up @@ -80,6 +82,8 @@ internal GeneratorDriverState(ParseOptions parseOptions,

internal readonly DriverStateTable StateTable;

internal readonly SyntaxStore SyntaxStore;

/// <summary>
/// A bit field containing the output kinds that should not be produced by this generator driver.
/// </summary>
Expand All @@ -95,6 +99,7 @@ internal GeneratorDriverState With(
ImmutableArray<GeneratorState>? generatorStates = null,
ImmutableArray<AdditionalText>? additionalTexts = null,
DriverStateTable? stateTable = null,
SyntaxStore? syntaxStore = null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if this and 'GeneratorState' were records. I don't think we've had many opportunities to dogfood records.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I think in the distant past there was even a PROTOTYPE comment about doing that, but the features went in side by side and we didn't get a chance to switch over. I've opened #59199 to track doing that, but I'll keep it separate from this PR to reduce churn in an already churn-y PR.

ParseOptions? parseOptions = null,
AnalyzerConfigOptionsProvider? optionsProvider = null,
IncrementalGeneratorOutputKind? disabledOutputs = null,
Expand All @@ -108,6 +113,7 @@ internal GeneratorDriverState With(
additionalTexts ?? this.AdditionalTexts,
generatorStates ?? this.GeneratorStates,
stateTable ?? this.StateTable,
syntaxStore ?? this.SyntaxStore,
disabledOutputs ?? this.DisabledOutputs,
runTime ?? this.RunTime,
this.TrackIncrementalSteps
Expand Down
12 changes: 6 additions & 6 deletions src/Compilers/Core/Portable/SourceGeneration/GeneratorState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ public GeneratorState(GeneratorInfo info)
/// Creates a new generator state that contains information and constant trees
/// </summary>
public GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees)
: this(info, postInitTrees, ImmutableArray<ISyntaxInputNode>.Empty, ImmutableArray<IIncrementalGeneratorOutputNode>.Empty)
: this(info, postInitTrees, ImmutableArray<SyntaxInputNode>.Empty, ImmutableArray<IIncrementalGeneratorOutputNode>.Empty)
{
}

/// <summary>
/// Creates a new generator state that contains information, constant trees and an execution pipeline
/// </summary>
public GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees, ImmutableArray<ISyntaxInputNode> inputNodes, ImmutableArray<IIncrementalGeneratorOutputNode> outputNodes)
public GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees, ImmutableArray<SyntaxInputNode> inputNodes, ImmutableArray<IIncrementalGeneratorOutputNode> outputNodes)
: this(info, postInitTrees, inputNodes, outputNodes, ImmutableArray<GeneratedSyntaxTree>.Empty, ImmutableArray<Diagnostic>.Empty, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty, exception: null, elapsedTime: TimeSpan.Zero)
{
}
Expand All @@ -40,19 +40,19 @@ public GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> po
/// Creates a new generator state that contains an exception and the associated diagnostic
/// </summary>
public GeneratorState(GeneratorInfo info, Exception e, Diagnostic error, TimeSpan elapsedTime)
: this(info, ImmutableArray<GeneratedSyntaxTree>.Empty, ImmutableArray<ISyntaxInputNode>.Empty, ImmutableArray<IIncrementalGeneratorOutputNode>.Empty, ImmutableArray<GeneratedSyntaxTree>.Empty, ImmutableArray.Create(error), ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty, exception: e, elapsedTime: elapsedTime)
: this(info, ImmutableArray<GeneratedSyntaxTree>.Empty, ImmutableArray<SyntaxInputNode>.Empty, ImmutableArray<IIncrementalGeneratorOutputNode>.Empty, ImmutableArray<GeneratedSyntaxTree>.Empty, ImmutableArray.Create(error), ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty, exception: e, elapsedTime: elapsedTime)
{
}

/// <summary>
/// Creates a generator state that contains results
/// </summary>
public GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees, ImmutableArray<ISyntaxInputNode> inputNodes, ImmutableArray<IIncrementalGeneratorOutputNode> outputNodes, ImmutableArray<GeneratedSyntaxTree> generatedTrees, ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps, TimeSpan elapsedTime)
public GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees, ImmutableArray<SyntaxInputNode> inputNodes, ImmutableArray<IIncrementalGeneratorOutputNode> outputNodes, ImmutableArray<GeneratedSyntaxTree> generatedTrees, ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps, TimeSpan elapsedTime)
: this(info, postInitTrees, inputNodes, outputNodes, generatedTrees, diagnostics, executedSteps, outputSteps, exception: null, elapsedTime)
{
}

private GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees, ImmutableArray<ISyntaxInputNode> inputNodes, ImmutableArray<IIncrementalGeneratorOutputNode> outputNodes, ImmutableArray<GeneratedSyntaxTree> generatedTrees, ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps, Exception? exception, TimeSpan elapsedTime)
private GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> postInitTrees, ImmutableArray<SyntaxInputNode> inputNodes, ImmutableArray<IIncrementalGeneratorOutputNode> outputNodes, ImmutableArray<GeneratedSyntaxTree> generatedTrees, ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps, Exception? exception, TimeSpan elapsedTime)
{
this.Initialized = true;
this.PostInitTrees = postInitTrees;
Expand All @@ -71,7 +71,7 @@ private GeneratorState(GeneratorInfo info, ImmutableArray<GeneratedSyntaxTree> p

internal ImmutableArray<GeneratedSyntaxTree> PostInitTrees { get; }

internal ImmutableArray<ISyntaxInputNode> InputNodes { get; }
internal ImmutableArray<SyntaxInputNode> InputNodes { get; }

internal ImmutableArray<IIncrementalGeneratorOutputNode> OutputNodes { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ namespace Microsoft.CodeAnalysis
/// </summary>
public readonly struct IncrementalGeneratorInitializationContext
{
private readonly ArrayBuilder<ISyntaxInputNode> _syntaxInputBuilder;
private readonly ArrayBuilder<SyntaxInputNode> _syntaxInputBuilder;
private readonly ArrayBuilder<IIncrementalGeneratorOutputNode> _outputNodes;
private readonly string _sourceExtension;

internal IncrementalGeneratorInitializationContext(ArrayBuilder<ISyntaxInputNode> syntaxInputBuilder, ArrayBuilder<IIncrementalGeneratorOutputNode> outputNodes, string sourceExtension)
internal IncrementalGeneratorInitializationContext(ArrayBuilder<SyntaxInputNode> syntaxInputBuilder, ArrayBuilder<IIncrementalGeneratorOutputNode> outputNodes, string sourceExtension)
{
_syntaxInputBuilder = syntaxInputBuilder;
_outputNodes = outputNodes;
Expand Down
Loading