Skip to content

Conversation

@chsienki
Copy link
Member

Adds APIs to convert ISourceGenerators to IIncrementalGenerators and allows the generator driver to only update a sub-set of generators via a new filter parameter.

Fixes #74039
Fixes #74086

@chsienki chsienki requested review from a team as code owners June 24, 2024 17:42
@ghost ghost added the untriaged Issues and PRs which have not yet been triaged by a lead label Jun 24, 2024
/// <param name="generatorFilter">An optional filter that specifies which generators to run. If <c>null</c> all generators will run.</param>
/// <param name="cancellationToken">Used to cancel an in progress operation.</param>
/// <returns>An updated driver that contains the results of the generators running.</returns>
public GeneratorDriver RunGenerators(Compilation compilation, Func<GeneratorFilterContext, bool>? generatorFilter = null, CancellationToken cancellationToken = default)
Copy link
Member Author

Choose a reason for hiding this comment

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

I couldn't figure out a way to make generatorFilter non-optional (as discussed in the API review) without breaking our back compat guidelines.

Another option would be to just make this a differently named method, like RunGeneratorsWithFilter

Copy link
Member

Choose a reason for hiding this comment

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

I don't see the problem - what guidelines would be broken?

Copy link
Member Author

Choose a reason for hiding this comment

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

Our analyzer requires that we don't have multiple overloads with optional parameters: https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md

That means that the signatures when we make generatorFilter required are:

RunGenerators(Compilation, CancellationToken)
RunGenerators(Compilation, Func<> filter, CancellationToken = default)

Anyone who was calling RunGenerators(Compilation) will now be source broken, because we no longer have an overload with only a single required parameter.

Note: It also says that we shouldn't add new optional parameters in the middle of a signature for source-compat. Imagine if you had RunGenerator(compilation, default) that might bind to a different overload now. So we're in a bit of tricky situation.

We can either: make the filter optional, and bend the 'don't put optional in the middle rule', or we can make it non-optional, and introduce a third overload of RunGenerators that only takes a Compilation parameter.

Tagging @333fred for any API shape thoughts on this one?

Copy link
Member

Choose a reason for hiding this comment

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

we can make it non-optional, and introduce a third overload of RunGenerators that only takes a Compilation parameter.

This sounds like the best option to me.

Copy link
Member

Choose a reason for hiding this comment

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

What is the problem with the signature in the current form? Yes it's optional but it just gets filled in as essentially "run everything". That behavior is compatible with the existing overload so that all seems to track for me. What am I missing?

Copy link
Member Author

Choose a reason for hiding this comment

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

shrug I'm just following the guidelines here https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md where it says do not put optional params in the middle of signatures.

I don't see in this case why it would be an issue, as even in source RunGenerators(compilation, default) should still bind to the cancellation token version, but I assume the guidelines exist for a reason and don't want to break them :)

@chsienki
Copy link
Member Author

@dotnet/roslyn-compiler for review please.

@jaredpar
Copy link
Member

@jjonescz PTAL

/// <param name="generatorFilter">An optional filter that specifies which generators to run. If <c>null</c> all generators will run.</param>
/// <param name="cancellationToken">Used to cancel an in progress operation.</param>
/// <returns>An updated driver that contains the results of the generators running.</returns>
public GeneratorDriver RunGenerators(Compilation compilation, Func<GeneratorFilterContext, bool>? generatorFilter = null, CancellationToken cancellationToken = default)
Copy link
Member

Choose a reason for hiding this comment

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

I don't see the problem - what guidelines would be broken?

chsienki added 3 commits June 27, 2024 09:25
Add tests to ensure we re-parse init only trees if the parse options changed between filtered runs
@chsienki chsienki force-pushed the new_generator_apis branch from 1a24208 to 28299c6 Compare June 27, 2024 23:45
/// </summary>
public const string DummySourceExtension = ".dummy";

private readonly string _sourceExtension;
Copy link
Member

Choose a reason for hiding this comment

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

Is this a value that is controllable by customers? If so have reservations about using a magic string. Had trouble tracking this through all the APIs to see where it originates.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it's only created internally. We track the file extension because we actively parse the tree, so we need to pass it in from the outer driver when we wrap an ISourceGenerator.

The generators on this path are never actually used directly, so we can just set it to anything. I chose a magic string so we can assert later on that it isn't used and dings us if we ever do allow it, so we know to fix it.

At some point we should probably refactor these so the string isn't needed at all, but this just seemed simpler for now.

/// <param name="generatorFilter">An optional filter that specifies which generators to run. If <c>null</c> all generators will run.</param>
/// <param name="cancellationToken">Used to cancel an in progress operation.</param>
/// <returns>An updated driver that contains the results of the generators running.</returns>
public GeneratorDriver RunGenerators(Compilation compilation, Func<GeneratorFilterContext, bool>? generatorFilter = null, CancellationToken cancellationToken = default)
Copy link
Member

Choose a reason for hiding this comment

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

What is the problem with the signature in the current form? Yes it's optional but it just gets filled in as essentially "run everything". That behavior is compatible with the existing overload so that all seems to track for me. What am I missing?

@chsienki
Copy link
Member Author

chsienki commented Jul 2, 2024

Helps if I actually push my changes 🤦

@RikkiGibson RikkiGibson modified the milestones: Next, 17.12 P1 Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers Feature - Source Generators Source Generators untriaged Issues and PRs which have not yet been triaged by a lead

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run a single generator at a time .AsIncrementalGenerator extension method

5 participants