-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Suppress CS1591 warnings for PublicTopLevelProgram.Generated.g.cs #60727
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
Changes from 3 commits
f3fc721
8d6d8ed
3575aa9
d992cb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,30 +10,42 @@ namespace Microsoft.AspNetCore.SourceGenerators; | |
| [Generator] | ||
| public class PublicProgramSourceGenerator : IIncrementalGenerator | ||
| { | ||
| private const string Program = "Program"; | ||
| private const string PublicPartialProgramClassSource = """ | ||
| // <auto-generated /> | ||
| #pragma warning disable CS1591 | ||
| public partial class Program { } | ||
| #pragma warning restore CS1591 | ||
| """; | ||
|
|
||
| public void Initialize(IncrementalGeneratorInitializationContext context) | ||
| { | ||
| var internalGeneratedProgramClass = context.CompilationProvider.Select(static (compilation, cancellationToken) => | ||
| { | ||
| // If there is a user-declared Program in a non-global namespace, skip generation | ||
| if (compilation.GetSymbolsWithName(Program, SymbolFilter.Type, cancellationToken) | ||
| .OfType<INamedTypeSymbol>() | ||
| .Any(symbol => !symbol.ContainingNamespace.IsGlobalNamespace)) | ||
| { | ||
| return false; | ||
| } | ||
|
||
|
|
||
| // Get the entry point associated with the compilation, this maps to the Main method definition | ||
| // Get the containing symbol of the entry point, this maps to the Program class | ||
| compilation.GetEntryPoint(cancellationToken)?.ContainingSymbol is INamedTypeSymbol | ||
| { | ||
| // 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 the program class is already public, we don't need to generate anything. | ||
| DeclaredAccessibility: not Accessibility.Public, | ||
| TypeKind: TypeKind.Class, | ||
| // If there are multiple partial declarations, then do nothing since we don't want | ||
| // to trample on visibility explicitly set by the user | ||
| DeclaringSyntaxReferences: { Length: 1 } declaringSyntaxReferences | ||
| } && | ||
| // If the `Program` class is already declared in user code, we don't need to generate anything. | ||
| declaringSyntaxReferences.Single().GetSyntax(cancellationToken) is not ClassDeclarationSyntax | ||
| ); | ||
| return compilation.GetEntryPoint(cancellationToken)?.ContainingSymbol is INamedTypeSymbol | ||
| { | ||
| // If the program class is already public, we don't need to generate anything | ||
| DeclaredAccessibility: not Accessibility.Public, | ||
| // If the discovered `Program` type is not a class then its not | ||
| // generated and has been defined in source, so we can skip it | ||
| TypeKind: TypeKind.Class, | ||
| // If there are multiple partial declarations, then do nothing since we don't want | ||
| // to trample on visibility explicitly set by the user | ||
| DeclaringSyntaxReferences: { Length: 1 } declaringSyntaxReferences | ||
| } && | ||
| // If the `Program` class is already declared in user code, we don't need to generate anything | ||
| declaringSyntaxReferences.Single().GetSyntax(cancellationToken) is not ClassDeclarationSyntax; | ||
| }); | ||
|
|
||
| context.RegisterSourceOutput(internalGeneratedProgramClass, (context, result) => | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,16 +22,25 @@ public async Task GeneratesSource_ProgramWithTopLevelStatements() | |
|
|
||
| var expected = """ | ||
| // <auto-generated /> | ||
| #pragma warning disable CS1591 | ||
| public partial class Program { } | ||
| #pragma warning restore CS1591 | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source, "PublicTopLevelProgram.Generated.g.cs", expected); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task DoesNotGeneratesSource_IfProgramIsAlreadyPublic() | ||
| [Theory] | ||
| [InlineData("public partial class Program { }")] | ||
| [InlineData(""" | ||
| namespace Foo | ||
| { | ||
| public partial class Program { } | ||
| } | ||
| """)] | ||
| public async Task DoesNotGeneratesSource_IfProgramIsAlreadyPublic(string declaration) | ||
|
||
| { | ||
| var source = """ | ||
| var source = $$""" | ||
| using Microsoft.AspNetCore.Builder; | ||
|
|
||
| var app = WebApplication.Create(); | ||
|
|
@@ -40,16 +49,23 @@ public async Task DoesNotGeneratesSource_IfProgramIsAlreadyPublic() | |
|
|
||
| app.Run(); | ||
|
|
||
| public partial class Program { } | ||
| {{declaration}} | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task DoesNotGeneratesSource_IfProgramDeclaresExplicitInternalAccess() | ||
| [Theory] | ||
| [InlineData("internal partial class Program { }")] | ||
| [InlineData(""" | ||
| namespace Foo | ||
| { | ||
| internal partial class Program { } | ||
| } | ||
| """)] | ||
| public async Task DoesNotGeneratesSource_IfProgramDeclaresExplicitInternalAccess(string declaration) | ||
| { | ||
| var source = """ | ||
| var source = $$""" | ||
| using Microsoft.AspNetCore.Builder; | ||
|
|
||
| var app = WebApplication.Create(); | ||
|
|
@@ -58,7 +74,7 @@ public async Task DoesNotGeneratesSource_IfProgramDeclaresExplicitInternalAccess | |
|
|
||
| app.Run(); | ||
|
|
||
| internal partial class Program { } | ||
| {{declaration}} | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source); | ||
|
|
@@ -86,6 +102,31 @@ public static void Main() | |
| await VerifyCS.VerifyAsync(source); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task DoesNotGeneratorSource_ExplicitPublicProgramClassInNamespace() | ||
| { | ||
| var source = """ | ||
| using Microsoft.AspNetCore.Builder; | ||
|
|
||
| namespace Foo | ||
| { | ||
| public class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var app = WebApplication.Create(); | ||
|
|
||
| app.MapGet("/", () => "Hello, World!"); | ||
|
|
||
| app.Run(); | ||
| } | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task DoesNotGeneratorSource_ExplicitInternalProgramClass() | ||
| { | ||
|
|
@@ -108,6 +149,31 @@ public static void Main() | |
| await VerifyCS.VerifyAsync(source); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task DoesNotGeneratorSource_ExplicitInternalProgramClassInNamespace() | ||
| { | ||
| var source = """ | ||
| using Microsoft.AspNetCore.Builder; | ||
|
|
||
| namespace Foo | ||
| { | ||
| internal class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var app = WebApplication.Create(); | ||
|
|
||
| app.MapGet("/", () => "Hello, World!"); | ||
|
|
||
| app.Run(); | ||
| } | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData("interface")] | ||
| [InlineData("struct")] | ||
|
|
@@ -127,6 +193,33 @@ public static void Main(string[] args) | |
| app.Run(); | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData("interface")] | ||
| [InlineData("struct")] | ||
| public async Task DoesNotGeneratorSource_ExplicitInternalProgramTypeInNamespace(string type) | ||
| { | ||
| var source = $$""" | ||
| using Microsoft.AspNetCore.Builder; | ||
|
|
||
| namespace Foo | ||
| { | ||
| internal {{type}} Program | ||
| { | ||
| public static void Main(string[] args) | ||
| { | ||
| var app = WebApplication.Create(); | ||
|
|
||
| app.MapGet("/", () => "Hello, World!"); | ||
|
|
||
| app.Run(); | ||
| } | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| await VerifyCS.VerifyAsync(source); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is right here. AFAIK, analyzers shouldn't trigger on generated code.
In the past, I've noticed that the
// <auto-generated />marker on the file isn't sufficient. We might also want to emit[GeneratedCode]here to mark this type as generated.You can see how we do it for the OpenAPI XML generator and the minimal API source generator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an analyzer, it's a compile warning. There were discussions in dotnet/roslyn to special case that specific one for generated code, but I'm not sure if anything was done yet, or if any decision was taken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Projects that have
<GenerateDocumentationFile>true</GenerateDocumentationFile>in the .csproj will have the CS1591 compiler warning when the current generated code is output preventing building the solutionCS1591 looks as follows in this case
Missing XML comment for publicly visible type or member 'Program'This
#pragmashould suppress this warningThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something important to note is that the project in the issue only has the CS1591 warning preventing the build due to them also having
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>That being said we still don't want this warning to flag for the generated code when the below option is stated in the project
<GenerateDocumentationFile>true</GenerateDocumentationFile>