Skip to content

Commit 0f73e79

Browse files
authored
Merge pull request #127 from bkoelman/fix-toplevel-statements
Fixed invalid warning on global namespace for compiler-generated top-level Program class
2 parents c7b4c42 + 08a3e47 commit 0f73e79

File tree

5 files changed

+57
-9
lines changed

5 files changed

+57
-9
lines changed

coverage.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ md coverage
44

55
set configuration=Debug
66
set opencover="%USERPROFILE%\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe"
7-
set reportgenerator="%USERPROFILE%\.nuget\packages\ReportGenerator\4.8.12\tools\net47\ReportGenerator.exe"
7+
set reportgenerator="%USERPROFILE%\.nuget\packages\ReportGenerator\5.0.0\tools\net47\ReportGenerator.exe"
88
set testrunner="%USERPROFILE%\.nuget\packages\xunit.runner.console\2.4.1\tools\net472\xunit.console.x86.exe"
99
set target=".\src\CSharpGuidelinesAnalyzer\CSharpGuidelinesAnalyzer.Test\bin\%configuration%\net48\CSharpGuidelinesAnalyzer.Test.dll -noshadow"
1010
set filter="+[CSharpGuidelinesAnalyzer*]* -[CSharpGuidelinesAnalyzer.Test*]*"

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer.Test/CSharpGuidelinesAnalyzer.Test.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
<PrivateAssets>all</PrivateAssets>
2222
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2323
</PackageReference>
24-
<PackageReference Include="FluentAssertions" Version="6.1.0" />
25-
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" PrivateAssets="all" />
26-
<PackageReference Include="Microsoft.CodeAnalysis" Version="3.11.0" />
27-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
24+
<PackageReference Include="FluentAssertions" Version="6.2.0" />
25+
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="all" />
26+
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.0.1" />
27+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
2828
<PackageReference Include="OpenCover" Version="4.7.1221" />
29-
<PackageReference Include="ReportGenerator" Version="4.8.12" />
29+
<PackageReference Include="ReportGenerator" Version="5.0.0" />
3030
<PackageReference Include="ResharperCodeContractNullability" Version="2.0.2" PrivateAssets="all" />
3131
<PackageReference Include="xunit" Version="2.4.1" />
3232
<PackageReference Include="xunit.runner.console" Version="2.4.1">

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer.Test/Specs/Maintainability/NamespaceShouldMatchAssemblyNameSpecs.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CSharpGuidelinesAnalyzer.Rules.Maintainability;
22
using CSharpGuidelinesAnalyzer.Test.TestDataBuilders;
3+
using Microsoft.CodeAnalysis;
34
using Microsoft.CodeAnalysis.Diagnostics;
45
using Xunit;
56

@@ -380,6 +381,44 @@ class [|C|]
380381
"Type 'C' is declared in namespace 'WrongRoot.JetBrains.Annotations', which does not match with assembly name 'Company.ProductName'.");
381382
}
382383

384+
[Fact]
385+
internal void When_top_level_program_it_must_be_skipped()
386+
{
387+
// Arrange
388+
ParsedSourceCode source = new TypeSourceCodeBuilder()
389+
.InAssemblyNamed("Some.Scope.Example")
390+
.WithOutputKind(OutputKind.ConsoleApplication)
391+
.InGlobalScope(@"
392+
System.Console.WriteLine("""");
393+
")
394+
.Build();
395+
396+
// Act and assert
397+
VerifyGuidelineDiagnostic(source);
398+
}
399+
400+
[Fact]
401+
internal void When_explicit_program_in_global_namespace_it_must_be_reported()
402+
{
403+
// Arrange
404+
ParsedSourceCode source = new TypeSourceCodeBuilder()
405+
.InAssemblyNamed("Some.Scope.Example")
406+
.WithOutputKind(OutputKind.ConsoleApplication)
407+
.InGlobalScope(@"
408+
static class [|Program|]
409+
{
410+
static void Main()
411+
{
412+
System.Console.WriteLine("""");
413+
}
414+
}
415+
")
416+
.Build();
417+
418+
// Act and assert
419+
VerifyGuidelineDiagnostic(source, "Type 'Program' is declared in global namespace, which does not match with assembly name 'Some.Scope.Example'.");
420+
}
421+
383422
protected override DiagnosticAnalyzer CreateAnalyzer()
384423
{
385424
return new NamespaceShouldMatchAssemblyNameAnalyzer();

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
<PrivateAssets>all</PrivateAssets>
4747
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4848
</PackageReference>
49-
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" PrivateAssets="all" />
50-
<PackageReference Include="JetBrains.ExternalAnnotations" Version="10.2.100" PrivateAssets="all" />
49+
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="all" />
50+
<PackageReference Include="JetBrains.ExternalAnnotations" Version="10.2.103" PrivateAssets="all" />
5151
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.6.0" PrivateAssets="all" />
5252
<PackageReference Include="ResharperCodeContractNullability" Version="2.0.2" PrivateAssets="all" />
5353
</ItemGroup>

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer/Rules/Maintainability/NamespaceShouldMatchAssemblyNameAnalyzer.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ namespace CSharpGuidelinesAnalyzer.Rules.Maintainability
1212
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1313
public sealed class NamespaceShouldMatchAssemblyNameAnalyzer : DiagnosticAnalyzer
1414
{
15+
// Copied from Microsoft.CodeAnalysis.WellKnownMemberNames, which provides these in later versions.
16+
private const string TopLevelStatementsEntryPointTypeName = "Program";
17+
private const string TopLevelStatementsEntryPointMethodName = "<Main>$";
18+
1519
private const string Title = "Namespace should match with assembly name";
1620
private const string NamespaceMessageFormat = "Namespace '{0}' does not match with assembly name '{1}'.";
1721
private const string TypeInNamespaceMessageFormat = "Type '{0}' is declared in namespace '{1}', which does not match with assembly name '{2}'.";
@@ -94,12 +98,17 @@ private static void AnalyzeNamedType(SymbolAnalysisContext context)
9498
{
9599
var type = (INamedTypeSymbol)context.Symbol;
96100

97-
if (type.ContainingNamespace.IsGlobalNamespace && !type.IsSynthesized())
101+
if (type.ContainingNamespace.IsGlobalNamespace && !type.IsSynthesized() && !IsTopLevelStatementsContainer(type))
98102
{
99103
context.ReportDiagnostic(Diagnostic.Create(GlobalTypeRule, type.Locations[0], type.Name, type.ContainingAssembly.Name));
100104
}
101105
}
102106

107+
private static bool IsTopLevelStatementsContainer([NotNull] INamedTypeSymbol type)
108+
{
109+
return type.Name == TopLevelStatementsEntryPointTypeName && type.GetMembers(TopLevelStatementsEntryPointMethodName).Any();
110+
}
111+
103112
private sealed class TypesInNamespaceVisitor : SymbolVisitor
104113
{
105114
[ItemNotNull]

0 commit comments

Comments
 (0)