Skip to content

Commit e7e9760

Browse files
committed
Updating SA1402 to favor a class with the same name as the file.
If the file does not contain a class of the same name continue to fall back to the first class in the file.
1 parent 855ab0a commit e7e9760

2 files changed

Lines changed: 42 additions & 21 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1402UnitTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ public partial class Bar
6060
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
6161
}
6262

63+
[Fact]
64+
public async Task TestPreferFilenameClassAsync()
65+
{
66+
var testCode = @"public class Foo
67+
{
68+
}
69+
public class Test0
70+
{
71+
}";
72+
73+
var fixedCode = @"public class Test0
74+
{
75+
}";
76+
77+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(1, 14);
78+
79+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
80+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
81+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
82+
}
83+
6384
[Fact]
6485
public async Task TestNestedClassesAsync()
6586
{

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1402FileMayOnlyContainASingleClass.cs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace StyleCop.Analyzers.MaintainabilityRules
55
{
66
using System;
77
using System.Collections.Immutable;
8+
using System.IO;
9+
using System.Linq;
810
using Microsoft.CodeAnalysis;
911
using Microsoft.CodeAnalysis.CSharp;
1012
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -60,37 +62,35 @@ private static void HandleCompilationStart(CompilationStartAnalysisContext conte
6062
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
6163
{
6264
var syntaxRoot = context.Tree.GetRoot(context.CancellationToken);
63-
6465
var descentNodes = syntaxRoot.DescendantNodes(descendIntoChildren: node => node != null && !node.IsKind(SyntaxKind.ClassDeclaration));
66+
var classNodes = from descentNode in descentNodes
67+
where descentNode.IsKind(SyntaxKind.ClassDeclaration)
68+
select descentNode as ClassDeclarationSyntax;
6569

66-
string foundClassName = null;
67-
bool isPartialClass = false;
70+
var preferredClassNode = classNodes.FirstOrDefault(n => n.Identifier.Text == Path.GetFileNameWithoutExtension(context.Tree.FilePath)) ?? classNodes.FirstOrDefault();
6871

69-
foreach (var node in descentNodes)
72+
if (preferredClassNode != null)
7073
{
71-
if (node.IsKind(SyntaxKind.ClassDeclaration))
74+
string foundClassName = null;
75+
bool isPartialClass = false;
76+
77+
foundClassName = preferredClassNode.Identifier.Text;
78+
isPartialClass = preferredClassNode.Modifiers.Any(SyntaxKind.PartialKeyword);
79+
80+
foreach (var classNode in classNodes)
7281
{
73-
ClassDeclarationSyntax classDeclaration = node as ClassDeclarationSyntax;
74-
if (foundClassName != null)
82+
if (classNode == preferredClassNode || (isPartialClass && foundClassName == classNode.Identifier.Text))
7583
{
76-
if (isPartialClass && foundClassName == classDeclaration.Identifier.Text)
77-
{
78-
continue;
79-
}
80-
81-
var location = NamedTypeHelpers.GetNameOrIdentifierLocation(node);
82-
if (location != null)
83-
{
84-
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
85-
}
84+
continue;
8685
}
87-
else
86+
87+
var location = NamedTypeHelpers.GetNameOrIdentifierLocation(classNode);
88+
if (location != null)
8889
{
89-
foundClassName = classDeclaration.Identifier.Text;
90-
isPartialClass = classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword);
90+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
9191
}
9292
}
9393
}
9494
}
9595
}
96-
}
96+
}

0 commit comments

Comments
 (0)