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

Make host outputs public #74750

Merged
merged 16 commits into from
Aug 27, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ protected override void ResolveEmbeddedFilesFromExternalSourceDirectives(

private protected override GeneratorDriver CreateGeneratorDriver(string baseDirectory, ParseOptions parseOptions, ImmutableArray<ISourceGenerator> generators, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, ImmutableArray<AdditionalText> additionalTexts)
{
return CSharpGeneratorDriver.Create(generators, additionalTexts, (CSharpParseOptions)parseOptions, analyzerConfigOptionsProvider, driverOptions: new GeneratorDriverOptions() { BaseDirectory = baseDirectory });
return CSharpGeneratorDriver.Create(generators, additionalTexts, (CSharpParseOptions)parseOptions, analyzerConfigOptionsProvider, driverOptions: new GeneratorDriverOptions(disabledOutputs: IncrementalGeneratorOutputKind.Host) { BaseDirectory = baseDirectory });
}

private protected override void DiagnoseBadAccesses(TextWriter consoleOutput, ErrorLogger? errorLogger, Compilation compilation, ImmutableArray<Diagnostic> diagnostics)
Expand Down
38 changes: 38 additions & 0 deletions src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10462,6 +10462,44 @@ class C

void RunWithCache() => VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview", "/features:enable-generator-cache" }, generators: new[] { generator.AsSourceGenerator() }, driverCache: cache, analyzers: null);
}

[Fact]
public void Compiler_DoesNot_RunHostOutputs()
chsienki marked this conversation as resolved.
Show resolved Hide resolved
{
var dir = Temp.CreateDirectory();
var src = dir.CreateFile("temp.cs").WriteAllText(@"
class C
{
}");
bool hostOutputRan = false;
bool sourceOutputRan = false;

var generator = new PipelineCallbackGenerator((ctx) =>
{
#pragma warning disable RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, value) =>
{
hostOutputRan = true;
hostCtx.AddOutput("output", "value");
});
#pragma warning restore RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

ctx.RegisterSourceOutput(ctx.CompilationProvider, (spc, po) =>
{
sourceOutputRan = true;
spc.AddSource("output.cs", "//value");
});
});

VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/langversion:preview" }, generators: new[] { generator.AsSourceGenerator() }, analyzers: null);

Assert.False(hostOutputRan);
Assert.True(sourceOutputRan);

// Clean up temp files
CleanupAllGeneratedFiles(src.Path);
Directory.Delete(dir.Path, true);
}

private static int OccurrenceCount(string source, string word)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4465,5 +4465,95 @@ public void GeneratorDriver_ReparsesPostInitTrees_IfParseOptionsChange_WhileSupp
Assert.Same(newParseOptions, result.GeneratedTrees[0].Options);
Assert.Same(newParseOptions, result.GeneratedTrees[1].Options);
}

#pragma warning disable RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

[Fact]
public void GeneratorDriver_Makes_HostOutputs_Available()
{
var generator = new PipelineCallbackGenerator((ctx) => { ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, c) => { hostCtx.AddOutput("a", "value"); }); });

var parseOptions = CSharpParseOptions.Default;
var compilation = CreateCompilation("class Compilation1{}", parseOptions: parseOptions);
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions);

driver = driver.RunGenerators(compilation);
var runResult = driver.GetRunResult();
var result = Assert.Single(runResult.Results);
var (key, value) = Assert.Single(result.HostOutputs);
Assert.Equal("a", key);
Assert.Equal("value", value);
}

[Fact]
public void GeneratorDriver_No_HostOutputs_WhenDisabled()
{
var generator = new PipelineCallbackGenerator((ctx) => { ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, c) => { hostCtx.AddOutput("a", "value"); }); });

var parseOptions = CSharpParseOptions.Default;
var compilation = CreateCompilation("class Compilation1{}", parseOptions: parseOptions);
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.Host));
Copy link
Member

Choose a reason for hiding this comment

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

Consider testing that host outputs are present when another IncrementalGeneratorOutputKind is disabled


driver = driver.RunGenerators(compilation);
var runResult = driver.GetRunResult();
var result = Assert.Single(runResult.Results);
Assert.Empty(result.HostOutputs);
}

[Fact]
public void GeneratorDriver_Multiple_HostOutputs()
{
var generator = new PipelineCallbackGenerator((ctx) => { ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, c) => { hostCtx.AddOutput("a", "value"); hostCtx.AddOutput("b", "value2"); }); });

var parseOptions = CSharpParseOptions.Default;
var compilation = CreateCompilation("class Compilation1{}", parseOptions: parseOptions);
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions);

driver = driver.RunGenerators(compilation);
var runResult = driver.GetRunResult();
var result = Assert.Single(runResult.Results);

Assert.Collection(result.HostOutputs,
(e) => { Assert.Equal("a", e.Key); Assert.Equal("value", e.Value); },
(e) => { Assert.Equal("b", e.Key); Assert.Equal("value2", e.Value); }
);
}

[Fact]
public void GeneratorDriver_Multiple_HostOutputs_SameName()
{
var generator = new PipelineCallbackGenerator((ctx) => { ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, c) => { hostCtx.AddOutput("a", "value"); hostCtx.AddOutput("a", "value2"); }); });
chsienki marked this conversation as resolved.
Show resolved Hide resolved

var parseOptions = CSharpParseOptions.Default;
var compilation = CreateCompilation("class Compilation1{}", parseOptions: parseOptions);
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions);

driver = driver.RunGenerators(compilation);
var runResult = driver.GetRunResult();
var result = Assert.Single(runResult.Results);

Assert.Empty(result.HostOutputs);
Assert.IsType<ArgumentException>(result.Exception);
chsienki marked this conversation as resolved.
Show resolved Hide resolved
}

[Fact]
public void GeneratorDriver_Makes_HostOutputs_MultipleGenerators()
{
var generator1 = new PipelineCallbackGenerator((ctx) => { ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, c) => { hostCtx.AddOutput("gen1", "value1"); }); });
var generator2 = new PipelineCallbackGenerator2((ctx) => { ctx.RegisterHostOutput(ctx.CompilationProvider, (hostCtx, c) => { hostCtx.AddOutput("gen2", "value2"); }); });

var parseOptions = CSharpParseOptions.Default;
var compilation = CreateCompilation("class Compilation1{}", parseOptions: parseOptions);
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator1.AsSourceGenerator(), generator2.AsSourceGenerator()], parseOptions: parseOptions);
driver = driver.RunGenerators(compilation);
var runResult = driver.GetRunResult();

Assert.Collection(runResult.Results,
(r) => { var result = Assert.Single(r.HostOutputs); Assert.Equal("gen1", result.Key); Assert.Equal("value1", result.Value); },
(r) => { var result = Assert.Single(r.HostOutputs); Assert.Equal("gen2", result.Key); Assert.Equal("value2", result.Value); }
);
}

#pragma warning restore RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ internal static class RoslynExperiments

internal const string SyntaxTokenParser = "RSEXPERIMENTAL003";
internal const string SyntaxTokenParser_Url = "https://github.com/dotnet/roslyn/issues/73002";

internal const string GeneratorHostOutputs = "RSEXPERIMENTAL004";
internal const string GeneratorHostOutputs_Url = "https://github.com/dotnet/roslyn/issues/74753";
}
11 changes: 10 additions & 1 deletion src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@
Microsoft.CodeAnalysis.GeneratorFilterContext.CancellationToken.get -> System.Threading.CancellationToken
Microsoft.CodeAnalysis.GeneratorFilterContext.Generator.get -> Microsoft.CodeAnalysis.ISourceGenerator!
Microsoft.CodeAnalysis.GeneratorFilterContext.GeneratorFilterContext() -> void
Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!
Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind.Host = 8 -> Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind
Microsoft.CodeAnalysis.IPropertySymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IPropertySymbol?
Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IPropertySymbol?
Microsoft.CodeAnalysis.IPropertySymbol.IsPartialDefinition.get -> bool
Microsoft.CodeAnalysis.ITypeParameterSymbol.AllowsRefLikeType.get -> bool
Microsoft.CodeAnalysis.RuntimeCapability.ByRefLikeGenerics = 8 -> Microsoft.CodeAnalysis.RuntimeCapability
static Microsoft.CodeAnalysis.GeneratorExtensions.AsIncrementalGenerator(this Microsoft.CodeAnalysis.ISourceGenerator! sourceGenerator) -> Microsoft.CodeAnalysis.IIncrementalGenerator!
static Microsoft.CodeAnalysis.GeneratorExtensions.GetGeneratorType(this Microsoft.CodeAnalysis.IIncrementalGenerator! generator) -> System.Type!
static Microsoft.CodeAnalysis.GeneratorExtensions.GetGeneratorType(this Microsoft.CodeAnalysis.IIncrementalGenerator! generator) -> System.Type!
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!

Check failure on line 18 in src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

View check run for this annotation

Azure Pipelines / roslyn-CI (Correctness Correctness_Analyzers)

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt#L18

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt(18,1): error RS0017: (NETCORE_ENGINEERING_TELEMETRY=Build) Symbol '[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!' is part of the declared API, but is either not public or could not be found (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 18 in src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

View check run for this annotation

Azure Pipelines / roslyn-CI (Correctness Correctness_Analyzers)

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt#L18

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt(18,1): error RS0017: (NETCORE_ENGINEERING_TELEMETRY=Build) Symbol '[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!' is part of the declared API, but is either not public or could not be found (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 18 in src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

View check run for this annotation

Azure Pipelines / roslyn-CI (Correctness Correctness_Analyzers)

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt#L18

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt(18,1): error RS0017: (NETCORE_ENGINEERING_TELEMETRY=Build) Symbol '[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!' is part of the declared API, but is either not public or could not be found (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 18 in src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

View check run for this annotation

Azure Pipelines / roslyn-CI

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt#L18

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt(18,1): error RS0017: (NETCORE_ENGINEERING_TELEMETRY=Build) Symbol '[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!' is part of the declared API, but is either not public or could not be found (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 18 in src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

View check run for this annotation

Azure Pipelines / roslyn-CI

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt#L18

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt(18,1): error RS0017: (NETCORE_ENGINEERING_TELEMETRY=Build) Symbol '[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!' is part of the declared API, but is either not public or could not be found (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 18 in src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

View check run for this annotation

Azure Pipelines / roslyn-CI

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt#L18

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt(18,1): error RS0017: (NETCORE_ENGINEERING_TELEMETRY=Build) Symbol '[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!' is part of the declared API, but is either not public or could not be found (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext.AddOutput(string! name, object! value) -> void
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext.CancellationToken.get -> System.Threading.CancellationToken
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext.HostOutputProductionContext() -> void
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext.RegisterHostOutput<TSource>(Microsoft.CodeAnalysis.IncrementalValueProvider<TSource> source, System.Action<Microsoft.CodeAnalysis.HostOutputProductionContext, TSource>! action) -> void
[RSEXPERIMENTAL004]Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext.RegisterHostOutput<TSource>(Microsoft.CodeAnalysis.IncrementalValuesProvider<TSource> source, System.Action<Microsoft.CodeAnalysis.HostOutputProductionContext, TSource>! action) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ namespace Microsoft.CodeAnalysis
/// </remarks>
public abstract class GeneratorDriver
{
internal const IncrementalGeneratorOutputKind HostKind = (IncrementalGeneratorOutputKind)0b100000; // several steps higher than IncrementalGeneratorOutputKind.Implementation

internal readonly GeneratorDriverState _state;

internal GeneratorDriver(GeneratorDriverState state)
Expand Down Expand Up @@ -323,7 +321,7 @@ internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, Diagnos
try
{
// We do not support incremental step tracking for v1 generators, as the pipeline is implicitly defined.
var context = UpdateOutputs(generatorState.OutputNodes, IncrementalGeneratorOutputKind.Source | IncrementalGeneratorOutputKind.Implementation | HostKind, new GeneratorRunStateTable.Builder(state.TrackIncrementalSteps), cancellationToken, driverStateBuilder);
var context = UpdateOutputs(generatorState.OutputNodes, IncrementalGeneratorOutputKind.Source | IncrementalGeneratorOutputKind.Implementation | IncrementalGeneratorOutputKind.Host, new GeneratorRunStateTable.Builder(state.TrackIncrementalSteps), cancellationToken, driverStateBuilder);
(var sources, var generatorDiagnostics, var generatorRunStateTable, var hostOutputs) = context.ToImmutableAndFree();
generatorDiagnostics = FilterDiagnostics(compilation, generatorDiagnostics, driverDiagnostics: diagnosticsBag, cancellationToken);

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 @@ -25,7 +25,7 @@ internal readonly struct GeneratorState
ImmutableArray<Diagnostic>.Empty,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty,
ImmutableArray<(string, string)>.Empty,
ImmutableDictionary<string, object>.Empty,
exception: null,
elapsedTime: TimeSpan.Zero);

Expand All @@ -40,7 +40,7 @@ public GeneratorState(ImmutableArray<GeneratedSyntaxTree> postInitTrees, Immutab
ImmutableArray<Diagnostic>.Empty,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty,
ImmutableArray<(string, string)>.Empty,
ImmutableDictionary<string, object>.Empty,
exception: null,
elapsedTime: TimeSpan.Zero)
{
Expand All @@ -54,7 +54,7 @@ private GeneratorState(
ImmutableArray<Diagnostic> diagnostics,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps,
ImmutableArray<(string Key, string Value)> hostOutputs,
ImmutableDictionary<string, object> hostOutputs,
Exception? exception,
TimeSpan elapsedTime)
{
Expand All @@ -75,7 +75,7 @@ public GeneratorState WithResults(ImmutableArray<GeneratedSyntaxTree> generatedT
ImmutableArray<Diagnostic> diagnostics,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps,
ImmutableArray<(string Key, string Value)> hostOutputs,
ImmutableDictionary<string, object> hostOutputs,
TimeSpan elapsedTime)
{
return new GeneratorState(this.PostInitTrees,
Expand All @@ -99,7 +99,7 @@ public GeneratorState WithError(Exception exception, Diagnostic error, TimeSpan
ImmutableArray.Create(error),
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty,
ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty,
ImmutableArray<(string, string)>.Empty,
ImmutableDictionary<string, object>.Empty,
exception,
elapsedTime);
}
Expand All @@ -124,7 +124,7 @@ public GeneratorState WithError(Exception exception, Diagnostic error, TimeSpan

internal ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> OutputSteps { get; }

internal ImmutableArray<(string Key, string Value)> HostOutputs { get; }
internal ImmutableDictionary<string, object> HostOutputs { get; }

internal bool RequiresPostInitReparse(ParseOptions parseOptions) => PostInitTrees.Any(static (t, parseOptions) => t.Tree.Options != parseOptions, parseOptions);
}
Expand Down
Loading
Loading