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

Update generator public API names: #47222

Merged
merged 8 commits into from
Aug 29, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
44 changes: 22 additions & 22 deletions docs/features/source-generators.cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ Create a generator that will create the missing type when run:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(InitializationContext context) {}
public void Initialize(GeneratorInitializationContext context) {}

public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
context.AddSource("myGeneratedFile.cs", SourceText.From(@"
namespace GeneratedNamespace
Expand All @@ -108,17 +108,17 @@ namespace GeneratedNamespace

**User scenario:** As a generator author I want to be able to transform an external non-C# file into an equivalent C# representation.

**Solution:** Use the additional files property of the `SourceGeneratorContext` to retrieve the contents of the file, convert it to the C# representation and return it.
**Solution:** Use the additional files property of the `GeneratorExecutionContext` to retrieve the contents of the file, convert it to the C# representation and return it.

**Example:**

```csharp
[Generator]
public class FileTransformGenerator : ISourceGenerator
{
public void Initialize(InitializationContext context) {}
public void Initialize(GeneratorInitializationContext context) {}

public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// find anything that matches our files
var myFiles = context.AnalyzerOptions.AdditionalFiles.Where(at => at.Path.EndsWith(".xml"));
Expand Down Expand Up @@ -163,13 +163,13 @@ public partial class UserClass
[Generator]
public class AugmentingGenerator : ISourceGenerator
{
public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
// Register a factory that can create our custom syntax receiver
context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
}

public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// the generator infrastructure will create a receiver and populate it
// we can retrieve the populated instance via the context
Expand Down Expand Up @@ -216,7 +216,7 @@ public partial class {userClass.Identifier}

**User Scenario:** As a generator author I want to be able to add diagnostics to the users compilation.

**Solution:** Diagnostics can be added to the compilation via `SourceGeneratorContext.ReportDiagnostic()`. These can be in response to the content of the users compilation:
**Solution:** Diagnostics can be added to the compilation via `GeneratorExecutionContext.ReportDiagnostic()`. These can be in response to the content of the users compilation:
for instance if the generator is expecting a well formed `AdditionalFile` but can not parse it, the generator could emit a warning notifying the user that generation can not proceed.

For code-based issues, the generator author should also consider implementing a [diagnostic analyzer](https://docs.microsoft.com/en-us/visualstudio/code-quality/roslyn-analyzers-overview?view=vs-2019) that identifies the problem, and offers a code-fix to resolve it.
Expand All @@ -235,7 +235,7 @@ public class MyXmlGenerator : ISourceGenerator
DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// Using the context, get any additional files that end in .xml
IEnumerable<AdditionalText> xmlFiles = context.AdditionalFiles.Where(at => at.Path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase));
Expand All @@ -258,7 +258,7 @@ public class MyXmlGenerator : ISourceGenerator
}
}

public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
}
}
Expand Down Expand Up @@ -390,7 +390,7 @@ using System.Linq;
[Generator]
public class SerializingGenerator : ISourceGenerator
{
public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// check that the users compilation references the expected library
if (!context.Compilation.ReferencedAssemblyNames.Any(ai => ai.Name.Equals("Newtonsoft.Json", StringComparison.OrdinalIgnoreCase)))
Expand All @@ -399,7 +399,7 @@ public class SerializingGenerator : ISourceGenerator
}
}

public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
}
}
Expand Down Expand Up @@ -436,7 +436,7 @@ The author would then have to package the `Newtonsoft.Json` library alongside th
[Generator]
public class JsonUsingGenerator : ISourceGenerator
{
public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// use the newtonsoft.json library, but don't add any source code that depends on it

Expand All @@ -453,7 +453,7 @@ namespace GeneratedNamespace

}

public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
}
}
Expand All @@ -469,7 +469,7 @@ namespace GeneratedNamespace
- As a generator author I want to access key-value pairs that customize the generator output.
- As a user of a generator I want to be able to customize the generated code and override defaults.

**Solution**: Generators can access analyzer config values via the `AnalyzerConfigOptions` property of the `SourceGeneratorContext`. Analyzer config values can either be accessed in the context of a `SyntaxTree`, `AdditionalFile` or globally via `GlobalOptions`. Global options are 'ambient' in that they don't apply to any specific context, but will be included when requesting option within a specific context.
**Solution**: Generators can access analyzer config values via the `AnalyzerConfigOptions` property of the `GeneratorExecutionContext`. Analyzer config values can either be accessed in the context of a `SyntaxTree`, `AdditionalFile` or globally via `GlobalOptions`. Global options are 'ambient' in that they don't apply to any specific context, but will be included when requesting option within a specific context.

A generator is free to use a global option to customize its output. For example, consider a generator that can optionally emit logging. The author may choose to check the value of a global analyzer config value in order to control whether or not to emit the logging code. A user can then choose to enable the setting per project via an `.editorconfig` file:

Expand All @@ -481,7 +481,7 @@ mygenerator_emit_logging = true
[Generator]
public class MyGenerator : ISourceGenerator
{
public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// control logging via analyzerconfig
bool emitLogging = false;
Expand All @@ -493,7 +493,7 @@ public class MyGenerator : ISourceGenerator
// add the source with or without logging...
}

public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
}
}
Expand All @@ -518,7 +518,7 @@ For example, consider a generator that creates source based on additional files,
</ItemGroup>
```

The value of `MyGenerator_EnableLogging` property will then be emitted to a generated analyzer config file before build, with a name of `build_property.MyGenerator_EnableLogging`. The generator is then able read this property from via the `AnalyzerConfigOptions` property of the `SourceGeneratorContext`:
The value of `MyGenerator_EnableLogging` property will then be emitted to a generated analyzer config file before build, with a name of `build_property.MyGenerator_EnableLogging`. The generator is then able read this property from via the `AnalyzerConfigOptions` property of the `GeneratorExecutionContext`:

```c#
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.MyGenerator_EnableLogging", out var emitLoggingSwitch);
Expand Down Expand Up @@ -592,7 +592,7 @@ MyGenerator.cs:
[Generator]
public class MyGenerator : ISourceGenerator
{
public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// global logging from project file
bool emitLoggingGlobal = false;
Expand All @@ -614,7 +614,7 @@ public class MyGenerator : ISourceGenerator
}
}

public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
}
}
Expand All @@ -633,13 +633,13 @@ It is anticipated there will be a mechanism for providing symbol mapping for lig
[Generator]
public class InteractiveGenerator : ISourceGenerator
{
public void Initialize(InitializationContext context)
public void Initialize(GeneratorInitializationContext context)
{
// Register for additional file callbacks
context.RegisterForAdditionalFileChanges(OnAdditionalFilesChanged);
}

public void Execute(SourceGeneratorContext context)
public void Execute(GeneratorExecutionContext context)
{
// generators must always support a total generation pass
}
Expand Down
14 changes: 7 additions & 7 deletions docs/features/source-generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ namespace Microsoft.CodeAnalysis
{
public interface ISourceGenerator
{
void Initialize(InitializationContext context);
void Execute(SourceGeneratorContext context);
void Initialize(GeneratorInitializationContext context);
void Execute(GeneratorExecutionContext context);
}
}
```
Expand All @@ -43,11 +43,11 @@ Since generators are loaded from external assemblies, a generator cannot be used
the assembly in which it is defined.

`ISourceGenerator` has an `Initialize` method that is called by the host (either the IDE or
the command-line compiler) exactly once. `Initialize` passes an instance of `InitializationContext`
the command-line compiler) exactly once. `Initialize` passes an instance of `GeneratorInitializationContext`
which can be used by the generator to register a set of callbacks that affect how future generation
passes will occur.

The main generation pass occurs via the `Execute` method. `Execute` passes an instance of `SourceGeneratorContext`
The main generation pass occurs via the `Execute` method. `Execute` passes an instance of `GeneratorExecutionContext`
that provides access to the current `Compilation` and allows the generator to alter the resulting output `Compilation`
by adding source and reporting diagnostics.

Expand All @@ -57,7 +57,7 @@ collection, allowing for generation decisions to based on more than just the use
```csharp
namespace Microsoft.CodeAnalysis
{
public readonly struct SourceGeneratorContext
public readonly struct GeneratorExecutionContext
{
public ImmutableArray<AdditionalText> AdditionalFiles { get; }

Expand Down Expand Up @@ -124,7 +124,7 @@ file might look something like:
```csharp
namespace Microsoft.CodeAnalysis
{
public struct InitializationContext
public struct GeneratorInitializationContext
{
public void RegisterForAdditionalFileChanges(EditCallback<AdditionalFileEdit> callback){ }
}
Expand All @@ -148,7 +148,7 @@ generator.

By default, generated texts will be persisted to a `GeneratedFiles/{GeneratorAssemblyName}`
sub-folder within `CommandLineArguments.OutputDirectory`. The `fileNameHint` from
`SourceGeneratorContext.AddSource` will be used to create a unique name, with appropriate
`GeneratorExecutionContext.AddSource` will be used to create a unique name, with appropriate
collision renaming applied if required. For instance, on Windows a call to
`AddSource("MyCode", ...);` from `MyGenerator.dll` for a C# project might be
persisted as `obj/debug/GeneratedFiles/MyGenerator.dll/MyCode.cs`.
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/CommandLine/CSharpCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ private protected override Compilation RunGenerators(Compilation input, ParseOpt
return input;
}

var driver = new CSharpGeneratorDriver(parseOptions, generators, analyzerConfigProvider, additionalTexts);
driver.RunFullGeneration(input, out var compilationOut, out var generatorDiagnostics);
var driver = new CSharpGeneratorDriver((CSharpParseOptions)parseOptions, generators, analyzerConfigProvider, additionalTexts);
driver.RunGeneratorsAndUpdateCompilation(input, out var compilationOut, out var generatorDiagnostics);
diagnostics.AddRange(generatorDiagnostics);
return compilationOut;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver.CSharpGeneratorDriver(Microsoft.CodeAnalysis.CSharp.CSharpParseOptions parseOptions, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator> generators, Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider optionsProvider, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.AdditionalText> additionalTexts) -> void
Microsoft.CodeAnalysis.CSharp.Conversion.IsConditionalExpression.get -> bool
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax
Expand Down Expand Up @@ -119,6 +120,8 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.SimpleLambdaExpressionSyntax.Modif
*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax> parameters = default(Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax>)) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax
*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SyntaxToken callingConvention, Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax> parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax
*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SyntaxToken delegateKeyword, Microsoft.CodeAnalysis.SyntaxToken asteriskToken, Microsoft.CodeAnalysis.SyntaxToken callingConvention, Microsoft.CodeAnalysis.SyntaxToken lessThanToken, Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax> parameters, Microsoft.CodeAnalysis.SyntaxToken greaterThanToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax
static Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver.Create(System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.ISourceGenerator> generators, System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.AdditionalText> additionalTexts = null, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions parseOptions = null, Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider optionsProvider = null) -> Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver
static Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver.Create(params Microsoft.CodeAnalysis.ISourceGenerator[] generators) -> Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver
static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options = null, string path = "", System.Text.Encoding encoding = null) -> Microsoft.CodeAnalysis.SyntaxTree
static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.Create(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode root, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options, string path, System.Text.Encoding encoding, System.Collections.Immutable.ImmutableDictionary<string, Microsoft.CodeAnalysis.ReportDiagnostic> diagnosticOptions, bool? isGeneratedCode) -> Microsoft.CodeAnalysis.SyntaxTree
static Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(Microsoft.CodeAnalysis.Text.SourceText text, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options = null, string path = "", System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.SyntaxTree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@
#nullable enable
namespace Microsoft.CodeAnalysis.CSharp
{
/// <summary>
/// A <see cref="GeneratorDriver"/> implementation for the CSharp language.
/// </summary>
public sealed class CSharpGeneratorDriver : GeneratorDriver
{
public CSharpGeneratorDriver(ParseOptions parseOptions, ImmutableArray<ISourceGenerator> generators, AnalyzerConfigOptionsProvider optionsProvider, ImmutableArray<AdditionalText> additionalTexts)
/// <summary>
/// Creates a new instance of <see cref="CSharpGeneratorDriver"/>
/// </summary>
/// <param name="parseOptions">The <see cref="CSharpParseOptions"/> that should be used when parsing generated files.</param>
/// <param name="generators">The generators that will run as part of this driver.</param>
/// <param name="optionsProvider">An <see cref="AnalyzerConfigOptionsProvider"/> that can be used to retrieve analyzer config values by the generators in this driver.</param>
/// <param name="additionalTexts">A list of <see cref="AdditionalText"/>s available to generators in this driver.</param>
public CSharpGeneratorDriver(CSharpParseOptions parseOptions, ImmutableArray<ISourceGenerator> generators, AnalyzerConfigOptionsProvider optionsProvider, ImmutableArray<AdditionalText> additionalTexts)
: base(parseOptions, generators, optionsProvider, additionalTexts)
{
}
Expand All @@ -29,6 +39,25 @@ private CSharpGeneratorDriver(GeneratorDriverState state)
{
}

/// <summary>
/// Creates a new instance of <see cref="CSharpGeneratorDriver"/> with the specified <see cref="ISourceGenerator"/>s and default options
/// </summary>
/// <param name="generators">The generators to create this driver with</param>
/// <returns>A new <see cref="CSharpGeneratorDriver"/> instance.</returns>
public static CSharpGeneratorDriver Create(params ISourceGenerator[] generators)
333fred marked this conversation as resolved.
Show resolved Hide resolved
=> Create(generators, additionalTexts: null);

/// <summary>
/// Creates a new instance of <see cref="CSharpGeneratorDriver"/> with the specified <see cref="ISourceGenerator"/>s and the provided options or default.
/// </summary>
/// <param name="generators">The generators to create this driver with</param>
/// <param name="additionalTexts">A list of <see cref="AdditionalText"/>s available to generators in this driver, or <c>null</c> if there are none.</param>
/// <param name="parseOptions">The <see cref="CSharpParseOptions"/> that should be used when parsing generated files, or <c>null</c> to use <see cref="CSharpParseOptions.Default"/></param>
/// <param name="optionsProvider">An <see cref="AnalyzerConfigOptionsProvider"/> that can be used to retrieve analyzer config values by the generators in this driver, or <c>null</c> if there are none.</param>
/// <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());

internal override SyntaxTree ParseGeneratedSourceText(GeneratedSourceText input, string fileName, CancellationToken cancellationToken)
=> SyntaxFactory.ParseSyntaxTree(input.Text, _state.ParseOptions, fileName, cancellationToken);

Expand Down
Loading