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

SG: Implementation outputs #54798

Merged
merged 15 commits into from
Jul 20, 2021

Conversation

chsienki
Copy link
Contributor

@chsienki chsienki commented Jul 13, 2021

This implements the idea of 'non semantic' source generation. That is source that can be safely ignored from the IDE perspective (no intellisense impact etc) but required for the on disk build.

I have used the name 'Implementation' for two reasons:

  • Suggests this is an 'implementation detail' and hidden from the user
  • We can introduce a mirror 'Reference Source output' in the future to the be the opposite of this output.
    • This will allow for scenarios where e.g. generating the body of a method is expensive

Note: this doesn't hook up the IDE side of things yet, but just makes the API available for consumption.

Closes: #53609
Api Review: #54723

- Make output kinds public
- Take a parameter during construction
- Don't run outputs passed in as disabled to the generator
@chsienki chsienki added this to the 17.0 milestone Jul 13, 2021
@chsienki chsienki requested a review from a team as a code owner July 13, 2021 21:05
@chsienki
Copy link
Contributor Author

@dotnet/roslyn-compiler for review please :)

@chsienki
Copy link
Contributor Author

Ping @dotnet/roslyn-compiler for a second review please :)

public void RegisterSourceOutput<TSource>(IncrementalValuesProvider<TSource> source, Action<SourceProductionContext, TSource> action) => source.Node.RegisterOutput(new SourceOutputNode<TSource>(source.Node, action.WrapUserAction()));
public void RegisterSourceOutput<TSource>(IncrementalValuesProvider<TSource> source, Action<SourceProductionContext, TSource> action) => source.Node.RegisterOutput(new SourceOutputNode<TSource>(source.Node, action.WrapUserAction(), IncrementalGeneratorOutputKind.Source));

public void RegisterImplementationSourceOutput<TSource>(IncrementalValueProvider<TSource> source, Action<SourceProductionContext, TSource> action) => source.Node.RegisterOutput(new SourceOutputNode<TSource>(source.Node, action.WrapUserAction(), IncrementalGeneratorOutputKind.Implementation));
Copy link
Member

@cston cston Jul 16, 2021

Choose a reason for hiding this comment

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

source.Node.RegisterOutput(new SourceOutputNode(source.Node, action.WrapUserAction(), IncrementalGeneratorOutputKind.Implementation))

Consider extracting a helper method (or two?) for the implementations of these 4 methods. #Resolved

/// <returns>A new <see cref="CSharpGeneratorDriver"/> instance.</returns>
public static CSharpGeneratorDriver Create(IEnumerable<ISourceGenerator> generators, IEnumerable<AdditionalText>? additionalTexts = null, CSharpParseOptions? parseOptions = null, AnalyzerConfigOptionsProvider? optionsProvider = null)
=> new CSharpGeneratorDriver(parseOptions ?? CSharpParseOptions.Default, generators.ToImmutableArray(), optionsProvider ?? CompilerAnalyzerConfigOptionsProvider.Empty, additionalTexts.AsImmutableOrEmpty());
public static CSharpGeneratorDriver Create(IEnumerable<ISourceGenerator> generators, IEnumerable<AdditionalText>? additionalTexts = null, CSharpParseOptions? parseOptions = null, AnalyzerConfigOptionsProvider? optionsProvider = null, IncrementalGeneratorOutputKind disabledOutputs = IncrementalGeneratorOutputKind.None)
Copy link
Member

@cston cston Jul 16, 2021

Choose a reason for hiding this comment

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

IncrementalGeneratorOutputKind disabledOutputs

Will we have additional generator options in the future? If we know now that we will, perhaps it would make sense to pass an options type here rather than a single value. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we plan to have more, but IncrementalGeneratorOutputKind is a [Flags] so we can combine them together like IncrementalGeneratorOutputKind.Implementation | IncrementalGeneratorKind.SomeOtherOutput to build up which nodes we don't want.

Copy link
Member

Choose a reason for hiding this comment

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

Adding more IncrementalGeneratorOutputKind values can be accomodated with the current overload but do we expect to add options other than IncrementatlGeneratorOutputKind?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I see what you're saying. Yeah actually that probably makes sense.


if (disabledOutput.HasFlag((IncrementalGeneratorOutputKind)kind))
{
Assert.DoesNotContain(result.Results[0].GeneratedSources, (s) => s.HintName == Enum.GetName(typeof(IncrementalGeneratorOutputKind), kind) + ".cs");
Copy link
Member

@cston cston Jul 16, 2021

Choose a reason for hiding this comment

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

(s) => s.HintName == Enum.GetName(typeof(IncrementalGeneratorOutputKind), kind) + ".cs"

Consider extracting a local function for use here and below. #Closed

/// </summary>
public readonly struct GeneratorDriverOptions
{
public static GeneratorDriverOptions Default = new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None);
Copy link
Member

Choose a reason for hiding this comment

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

static

readonly

/// </summary>
public readonly struct GeneratorDriverOptions
{
public static GeneratorDriverOptions Default = new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None);
Copy link
Member

Choose a reason for hiding this comment

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

Default

Minor point: This field seems unnecessary since the type is a struct so it's inexpensive to create at each call site and because the value is equivalent to default(GeneratorDriverOptions).

// 3.11 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
[EditorBrowsable(EditorBrowsableState.Never)]
public static CSharpGeneratorDriver Create(IEnumerable<ISourceGenerator> generators, IEnumerable<AdditionalText>? additionalTexts, CSharpParseOptions? parseOptions, AnalyzerConfigOptionsProvider? optionsProvider)
=> new CSharpGeneratorDriver(parseOptions ?? CSharpParseOptions.Default, generators.ToImmutableArray(), optionsProvider ?? CompilerAnalyzerConfigOptionsProvider.Empty, additionalTexts.AsImmutableOrEmpty(), driverOptions: GeneratorDriverOptions.Default);
Copy link
Member

Choose a reason for hiding this comment

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

new CSharpGeneratorDriver(parseOptions ?? CSharpParseOptions.Default, generators.ToImmutableArray(), optionsProvider ?? CompilerAnalyzerConfigOptionsProvider.Empty, additionalTexts.AsImmutableOrEmpty(), driverOptions: GeneratorDriverOptions.Default);

Consider calling the new Create() instead to avoid duplicating the handling of optional values: Create(parseOptions, optionsProvider, additionalTexts, driverOptions: null)

Public Shared Function Create(generators As ImmutableArray(Of ISourceGenerator), Optional additionalTexts As ImmutableArray(Of AdditionalText) = Nothing, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional analyzerConfigOptionsProvider As AnalyzerConfigOptionsProvider = Nothing) As VisualBasicGeneratorDriver
Return New VisualBasicGeneratorDriver(parseOptions, generators, If(analyzerConfigOptionsProvider, CompilerAnalyzerConfigOptionsProvider.Empty), additionalTexts.NullToEmpty())
Public Shared Function Create(generators As ImmutableArray(Of ISourceGenerator), Optional additionalTexts As ImmutableArray(Of AdditionalText) = Nothing, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional analyzerConfigOptionsProvider As AnalyzerConfigOptionsProvider = Nothing, Optional driverOptions As GeneratorDriverOptions? = Nothing) As VisualBasicGeneratorDriver
Return New VisualBasicGeneratorDriver(parseOptions, generators, If(analyzerConfigOptionsProvider, CompilerAnalyzerConfigOptionsProvider.Empty), additionalTexts.NullToEmpty(), If(driverOptions, DirectCast(Nothing, GeneratorDriverOptions)))
Copy link
Member

Choose a reason for hiding this comment

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

If(driverOptions, DirectCast(Nothing, GeneratorDriverOptions))

Can this be simplified to If(driverOptions, Nothing)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incremental Source Generators should support 'non semantic' outputs
5 participants