Skip to content

Commit 3575aa9

Browse files
Fixed program declaration in explicit namespace
1 parent 8d6d8ed commit 3575aa9

File tree

2 files changed

+115
-14
lines changed

2 files changed

+115
-14
lines changed

src/Framework/AspNetCoreAnalyzers/src/SourceGenerators/PublicTopLevelProgramGenerator.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.SourceGenerators;
1010
[Generator]
1111
public class PublicProgramSourceGenerator : IIncrementalGenerator
1212
{
13+
private const string Program = "Program";
1314
private const string PublicPartialProgramClassSource = """
1415
// <auto-generated />
1516
#pragma warning disable CS1591
@@ -20,22 +21,31 @@ public partial class Program { }
2021
public void Initialize(IncrementalGeneratorInitializationContext context)
2122
{
2223
var internalGeneratedProgramClass = context.CompilationProvider.Select(static (compilation, cancellationToken) =>
24+
{
25+
// If there is a user-declared Program in a non-global namespace, skip generation
26+
if (compilation.GetSymbolsWithName(Program, SymbolFilter.Type, cancellationToken)
27+
.OfType<INamedTypeSymbol>()
28+
.Any(symbol => !symbol.ContainingNamespace.IsGlobalNamespace))
29+
{
30+
return false;
31+
}
32+
2333
// Get the entry point associated with the compilation, this maps to the Main method definition
2434
// Get the containing symbol of the entry point, this maps to the Program class
25-
compilation.GetEntryPoint(cancellationToken)?.ContainingSymbol is INamedTypeSymbol
35+
return compilation.GetEntryPoint(cancellationToken)?.ContainingSymbol is INamedTypeSymbol
2636
{
37+
// If the program class is already public, we don't need to generate anything
38+
DeclaredAccessibility: not Accessibility.Public,
2739
// If the discovered `Program` type is not a class then its not
2840
// generated and has been defined in source, so we can skip it
29-
// If the program class is already public, we don't need to generate anything.
30-
DeclaredAccessibility: not Accessibility.Public,
3141
TypeKind: TypeKind.Class,
3242
// If there are multiple partial declarations, then do nothing since we don't want
3343
// to trample on visibility explicitly set by the user
3444
DeclaringSyntaxReferences: { Length: 1 } declaringSyntaxReferences
3545
} &&
36-
// If the `Program` class is already declared in user code, we don't need to generate anything.
37-
declaringSyntaxReferences.Single().GetSyntax(cancellationToken) is not ClassDeclarationSyntax
38-
);
46+
// If the `Program` class is already declared in user code, we don't need to generate anything
47+
declaringSyntaxReferences.Single().GetSyntax(cancellationToken) is not ClassDeclarationSyntax;
48+
});
3949

4050
context.RegisterSourceOutput(internalGeneratedProgramClass, (context, result) =>
4151
{

src/Framework/AspNetCoreAnalyzers/test/SourceGenerators/PublicTopLevelProgramGeneratorTests.cs

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,17 @@ public partial class Program { }
3030
await VerifyCS.VerifyAsync(source, "PublicTopLevelProgram.Generated.g.cs", expected);
3131
}
3232

33-
[Fact]
34-
public async Task DoesNotGeneratesSource_IfProgramIsAlreadyPublic()
33+
[Theory]
34+
[InlineData("public partial class Program { }")]
35+
[InlineData("""
36+
namespace Foo
37+
{
38+
public partial class Program { }
39+
}
40+
""")]
41+
public async Task DoesNotGeneratesSource_IfProgramIsAlreadyPublic(string declaration)
3542
{
36-
var source = """
43+
var source = $$"""
3744
using Microsoft.AspNetCore.Builder;
3845
3946
var app = WebApplication.Create();
@@ -42,16 +49,23 @@ public async Task DoesNotGeneratesSource_IfProgramIsAlreadyPublic()
4249
4350
app.Run();
4451
45-
public partial class Program { }
52+
{{declaration}}
4653
""";
4754

4855
await VerifyCS.VerifyAsync(source);
4956
}
5057

51-
[Fact]
52-
public async Task DoesNotGeneratesSource_IfProgramDeclaresExplicitInternalAccess()
58+
[Theory]
59+
[InlineData("internal partial class Program { }")]
60+
[InlineData("""
61+
namespace Foo
62+
{
63+
internal partial class Program { }
64+
}
65+
""")]
66+
public async Task DoesNotGeneratesSource_IfProgramDeclaresExplicitInternalAccess(string declaration)
5367
{
54-
var source = """
68+
var source = $$"""
5569
using Microsoft.AspNetCore.Builder;
5670
5771
var app = WebApplication.Create();
@@ -60,7 +74,7 @@ public async Task DoesNotGeneratesSource_IfProgramDeclaresExplicitInternalAccess
6074
6175
app.Run();
6276
63-
internal partial class Program { }
77+
{{declaration}}
6478
""";
6579

6680
await VerifyCS.VerifyAsync(source);
@@ -88,6 +102,31 @@ public static void Main()
88102
await VerifyCS.VerifyAsync(source);
89103
}
90104

105+
[Fact]
106+
public async Task DoesNotGeneratorSource_ExplicitPublicProgramClassInNamespace()
107+
{
108+
var source = """
109+
using Microsoft.AspNetCore.Builder;
110+
111+
namespace Foo
112+
{
113+
public class Program
114+
{
115+
public static void Main()
116+
{
117+
var app = WebApplication.Create();
118+
119+
app.MapGet("/", () => "Hello, World!");
120+
121+
app.Run();
122+
}
123+
}
124+
}
125+
""";
126+
127+
await VerifyCS.VerifyAsync(source);
128+
}
129+
91130
[Fact]
92131
public async Task DoesNotGeneratorSource_ExplicitInternalProgramClass()
93132
{
@@ -110,6 +149,31 @@ public static void Main()
110149
await VerifyCS.VerifyAsync(source);
111150
}
112151

152+
[Fact]
153+
public async Task DoesNotGeneratorSource_ExplicitInternalProgramClassInNamespace()
154+
{
155+
var source = """
156+
using Microsoft.AspNetCore.Builder;
157+
158+
namespace Foo
159+
{
160+
internal class Program
161+
{
162+
public static void Main()
163+
{
164+
var app = WebApplication.Create();
165+
166+
app.MapGet("/", () => "Hello, World!");
167+
168+
app.Run();
169+
}
170+
}
171+
}
172+
""";
173+
174+
await VerifyCS.VerifyAsync(source);
175+
}
176+
113177
[Theory]
114178
[InlineData("interface")]
115179
[InlineData("struct")]
@@ -129,6 +193,33 @@ public static void Main(string[] args)
129193
app.Run();
130194
}
131195
}
196+
""";
197+
198+
await VerifyCS.VerifyAsync(source);
199+
}
200+
201+
[Theory]
202+
[InlineData("interface")]
203+
[InlineData("struct")]
204+
public async Task DoesNotGeneratorSource_ExplicitInternalProgramTypeInNamespace(string type)
205+
{
206+
var source = $$"""
207+
using Microsoft.AspNetCore.Builder;
208+
209+
namespace Foo
210+
{
211+
internal {{type}} Program
212+
{
213+
public static void Main(string[] args)
214+
{
215+
var app = WebApplication.Create();
216+
217+
app.MapGet("/", () => "Hello, World!");
218+
219+
app.Run();
220+
}
221+
}
222+
}
132223
""";
133224

134225
await VerifyCS.VerifyAsync(source);

0 commit comments

Comments
 (0)