Skip to content

Commit b59d5e8

Browse files
committed
Move SA1402 filename from type logic to shared location.
SA1649 uses similar logic in determining which class (of multiple) should be kept in the file.
1 parent f292bbd commit b59d5e8

3 files changed

Lines changed: 64 additions & 49 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1649FileNameMustMatchTypeName.cs

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.DocumentationRules
77
using System.Collections.Immutable;
88
using System.IO;
99
using System.Linq;
10+
using Helpers;
1011
using Microsoft.CodeAnalysis;
1112
using Microsoft.CodeAnalysis.CSharp;
1213
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -84,22 +85,12 @@ public void HandleSyntaxTreeAction(SyntaxTreeAnalysisContext context)
8485
}
8586

8687
var fileName = Path.GetFileName(context.Tree.FilePath);
87-
string expectedFileName;
88-
switch (this.fileNamingConvention)
89-
{
90-
case FileNamingConvention.Metadata:
91-
expectedFileName = GetMetadataFileName(firstTypeDeclaration);
92-
break;
93-
94-
default:
95-
expectedFileName = GetStyleCopFileName(firstTypeDeclaration);
96-
break;
97-
}
88+
var expectedFileName = NamedTypeHelpers.GetConventionalFileName(firstTypeDeclaration, this.fileNamingConvention);
9889

9990
if (string.Compare(fileName, expectedFileName, StringComparison.OrdinalIgnoreCase) != 0)
10091
{
10192
if (this.fileNamingConvention == FileNamingConvention.StyleCop
102-
&& string.Compare(fileName, GetSimpleFileName(firstTypeDeclaration), StringComparison.OrdinalIgnoreCase) == 0)
93+
&& string.Compare(fileName, NamedTypeHelpers.GetSimpleFileName(firstTypeDeclaration), StringComparison.OrdinalIgnoreCase) == 0)
10394
{
10495
return;
10596
}
@@ -117,32 +108,6 @@ private static TypeDeclarationSyntax GetFirstTypeDeclaration(SyntaxNode root)
117108
.OfType<TypeDeclarationSyntax>()
118109
.FirstOrDefault();
119110
}
120-
121-
private static string GetStyleCopFileName(TypeDeclarationSyntax firstTypeDeclaration)
122-
{
123-
if (firstTypeDeclaration.TypeParameterList == null)
124-
{
125-
return $"{firstTypeDeclaration.Identifier.ValueText}.cs";
126-
}
127-
128-
var typeParameterList = string.Join(",", firstTypeDeclaration.TypeParameterList.Parameters.Select(p => p.Identifier.ValueText));
129-
return $"{firstTypeDeclaration.Identifier.ValueText}{{{typeParameterList}}}.cs";
130-
}
131-
132-
private static string GetSimpleFileName(TypeDeclarationSyntax firstTypeDeclaration)
133-
{
134-
return $"{firstTypeDeclaration.Identifier.ValueText}.cs";
135-
}
136-
137-
private static string GetMetadataFileName(TypeDeclarationSyntax firstTypeDeclaration)
138-
{
139-
if (firstTypeDeclaration.TypeParameterList == null)
140-
{
141-
return $"{firstTypeDeclaration.Identifier.ValueText}.cs";
142-
}
143-
144-
return $"{firstTypeDeclaration.Identifier.ValueText}`{firstTypeDeclaration.Arity}.cs";
145-
}
146111
}
147112
}
148-
}
113+
}

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/NamedTypeHelpers.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Helpers
77
using System.Linq;
88
using Microsoft.CodeAnalysis;
99
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Settings.ObjectModel;
1011

1112
internal static class NamedTypeHelpers
1213
{
@@ -151,5 +152,38 @@ internal static bool IsImplementingAnInterfaceMember(ISymbol memberSymbol)
151152
.Select(typeSymbol.FindImplementationForInterfaceMember)
152153
.Any(x => memberSymbol.Equals(x));
153154
}
155+
156+
internal static string GetConventionalFileName(TypeDeclarationSyntax typeDeclaration, FileNamingConvention convention)
157+
{
158+
if (typeDeclaration.TypeParameterList == null)
159+
{
160+
return GetSimpleFileName(typeDeclaration);
161+
}
162+
163+
switch (convention)
164+
{
165+
case FileNamingConvention.Metadata:
166+
return GetMetadataFileName(typeDeclaration);
167+
168+
default:
169+
return GetStyleCopFileName(typeDeclaration);
170+
}
171+
}
172+
173+
internal static string GetSimpleFileName(TypeDeclarationSyntax typeDeclaration)
174+
{
175+
return $"{typeDeclaration.Identifier.ValueText}.cs";
176+
}
177+
178+
private static string GetMetadataFileName(TypeDeclarationSyntax typeDeclaration)
179+
{
180+
return $"{typeDeclaration.Identifier.ValueText}`{typeDeclaration.Arity}.cs";
181+
}
182+
183+
private static string GetStyleCopFileName(TypeDeclarationSyntax typeDeclaration)
184+
{
185+
var typeParameterList = string.Join(",", typeDeclaration.TypeParameterList.Parameters.Select(p => p.Identifier.ValueText));
186+
return $"{typeDeclaration.Identifier.ValueText}{{{typeParameterList}}}.cs";
187+
}
154188
}
155189
}

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

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace StyleCop.Analyzers.MaintainabilityRules
1111
using Microsoft.CodeAnalysis.CSharp;
1212
using Microsoft.CodeAnalysis.CSharp.Syntax;
1313
using Microsoft.CodeAnalysis.Diagnostics;
14+
using Settings.ObjectModel;
1415
using StyleCop.Analyzers.Helpers;
1516

1617
/// <summary>
@@ -33,6 +34,7 @@ internal class SA1402FileMayOnlyContainASingleClass : DiagnosticAnalyzer
3334
/// The ID for diagnostics produced by the <see cref="SA1402FileMayOnlyContainASingleClass"/> analyzer.
3435
/// </summary>
3536
public const string DiagnosticId = "SA1402";
37+
3638
private const string Title = "File may only contain a single class";
3739
private const string MessageFormat = "File may only contain a single class";
3840
private const string Description = "A C# code file contains more than one unique class.";
@@ -42,7 +44,6 @@ internal class SA1402FileMayOnlyContainASingleClass : DiagnosticAnalyzer
4244
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.MaintainabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
4345

4446
private static readonly Action<CompilationStartAnalysisContext> CompilationStartAction = HandleCompilationStart;
45-
private static readonly Action<SyntaxTreeAnalysisContext> SyntaxTreeAction = HandleSyntaxTree;
4647

4748
/// <inheritdoc/>
4849
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
@@ -56,21 +57,36 @@ public override void Initialize(AnalysisContext context)
5657

5758
private static void HandleCompilationStart(CompilationStartAnalysisContext context)
5859
{
59-
context.RegisterSyntaxTreeActionHonorExclusions(SyntaxTreeAction);
60+
var analyzer = new Analyzer(context.Options);
61+
context.RegisterSyntaxTreeActionHonorExclusions(analyzer.HandleSyntaxTree);
6062
}
6163

62-
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
64+
private class Analyzer
6365
{
64-
var syntaxRoot = context.Tree.GetRoot(context.CancellationToken);
65-
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;
66+
private readonly FileNamingConvention fileNamingConvention;
6967

70-
var preferredClassNode = classNodes.FirstOrDefault(n => n.Identifier.Text == Path.GetFileNameWithoutExtension(context.Tree.FilePath)) ?? classNodes.FirstOrDefault();
68+
public Analyzer(AnalyzerOptions options)
69+
{
70+
StyleCopSettings settings = options.GetStyleCopSettings();
71+
this.fileNamingConvention = settings.DocumentationRules.FileNamingConvention;
72+
}
7173

72-
if (preferredClassNode != null)
74+
public void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
7375
{
76+
var syntaxRoot = context.Tree.GetRoot(context.CancellationToken);
77+
var descentNodes = syntaxRoot.DescendantNodes(descendIntoChildren: node => node != null && !node.IsKind(SyntaxKind.ClassDeclaration));
78+
var classNodes = from descentNode in descentNodes
79+
where descentNode.IsKind(SyntaxKind.ClassDeclaration)
80+
select descentNode as ClassDeclarationSyntax;
81+
82+
var fileName = Path.GetFileName(context.Tree.FilePath);
83+
var preferredClassNode = classNodes.FirstOrDefault(n => NamedTypeHelpers.GetConventionalFileName(n, this.fileNamingConvention) == fileName) ?? classNodes.FirstOrDefault();
84+
85+
if (preferredClassNode == null)
86+
{
87+
return;
88+
}
89+
7490
string foundClassName = null;
7591
bool isPartialClass = false;
7692

0 commit comments

Comments
 (0)