Skip to content

Commit

Permalink
Use GetEntrypoint API and transformations for better caching
Browse files Browse the repository at this point in the history
  • Loading branch information
captainsafia committed Oct 5, 2024
1 parent 707bd27 commit 3bc89cd
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,35 @@ namespace Microsoft.AspNetCore.SourceGenerators;
[Generator]
public class PublicProgramSourceGenerator : IIncrementalGenerator
{
private const string PublicPartialProgramClassSource = """
// <auto-generated />
public partial class Program { }
""";

public void Initialize(IncrementalGeneratorInitializationContext context)
{
var internalGeneratedProgramClass = context.CompilationProvider.Select((compilation, cancellationToken) =>
{
var program = compilation.GetTypeByMetadataName("Program");
var internalGeneratedProgramClass = context.CompilationProvider
// Get the entry point associated with the compilation, this maps to the Main method definition
.Select((compilation, cancellationToken) => compilation.GetEntryPoint(cancellationToken))
// Get the containing symbol of the entry point, this maps to the Program class
.Select((symbol, _) => symbol?.ContainingSymbol)
// If the program class is already public, we don't need to generate anything.
if (program is null || program.DeclaredAccessibility == Accessibility.Public)
{
return null;
}
// If the discovered `Program` type is an interface, struct or generic type then its not
.Select((symbol, _) => symbol?.DeclaredAccessibility == Accessibility.Public ? null : symbol)
// If the discovered `Program` type is not a class then its not
// generated and has been defined in source, so we can skip it
if (program.TypeKind == TypeKind.Struct || program.TypeKind == TypeKind.Interface || program.IsGenericType)
{
return null;
}
.Select((symbol, _) => symbol is INamedTypeSymbol { TypeKind: TypeKind.Class } ? symbol : null)
// If there are multiple partial declarations, then do nothing since we don't want
// to trample on visibility explicitly set by the user
if (program.DeclaringSyntaxReferences.Length > 1)
{
return null;
}
.Select((symbol, _) => symbol is { DeclaringSyntaxReferences: { Length: 1 } declaringSyntaxReferences } ? declaringSyntaxReferences.Single() : null)
// If the `Program` class is already declared in user code, we don't need to generate anything.
if (program.DeclaringSyntaxReferences.SingleOrDefault()?.GetSyntax(cancellationToken) is ClassDeclarationSyntax)
{
return null;
}
return program;
});
.Select((declaringSyntaxReference, cancellationToken) => declaringSyntaxReference?.GetSyntax(cancellationToken) is ClassDeclarationSyntax ? null : declaringSyntaxReference);

context.RegisterSourceOutput(internalGeneratedProgramClass, (context, symbol) =>
context.RegisterSourceOutput(internalGeneratedProgramClass, (context, result) =>
{
if (symbol is null)
if (result is not null)
{
return;
context.AddSource("PublicTopLevelProgram.Generated.g.cs", PublicPartialProgramClassSource);
}
var output = """
// <auto-generated />
public partial class Program { }
""";
context.AddSource("PublicTopLevelProgram.Generated.cs", output);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public async Task GeneratesSource_ProgramWithTopLevelStatements()
public partial class Program { }
""";

await VerifyCS.VerifyAsync(source, "PublicTopLevelProgram.Generated.cs", expected);
await VerifyCS.VerifyAsync(source, "PublicTopLevelProgram.Generated.g.cs", expected);
}

[Fact]
Expand Down

0 comments on commit 3bc89cd

Please sign in to comment.