Skip to content

Commit a6e0440

Browse files
committed
Update SA1202 to support interfaces and records
Fixes #3693
1 parent 8eb1db3 commit a6e0440

File tree

5 files changed

+141
-22
lines changed

5 files changed

+141
-22
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/OrderingRules/SA1202CSharp8UnitTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,59 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp8.OrderingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
68
using StyleCop.Analyzers.Test.CSharp7.OrderingRules;
9+
using Xunit;
10+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
11+
StyleCop.Analyzers.OrderingRules.SA1202ElementsMustBeOrderedByAccess,
12+
StyleCop.Analyzers.OrderingRules.ElementOrderCodeFixProvider>;
713

814
public class SA1202CSharp8UnitTests : SA1202CSharp7UnitTests
915
{
16+
/// <summary>
17+
/// Verifies that the analyzer will properly handle property access levels.
18+
/// </summary>
19+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
20+
[Fact]
21+
public async Task TestPropertiesOfInterfaceAsync()
22+
{
23+
var testCode = @"public interface TestClass
24+
{
25+
private string TestProperty1 { get { return """"; } set { } }
26+
protected string {|#0:TestProperty2|} { get; set; }
27+
protected internal string {|#1:TestProperty3|} { get; set; }
28+
internal string {|#2:TestProperty4|} { get; set; }
29+
public string {|#3:TestProperty5|} { get; set; }
30+
string TestProperty0 { get; set; }
31+
}
32+
";
33+
34+
var fixedCode = @"public interface TestClass
35+
{
36+
public string TestProperty5 { get; set; }
37+
string TestProperty0 { get; set; }
38+
internal string TestProperty4 { get; set; }
39+
protected internal string TestProperty3 { get; set; }
40+
protected string TestProperty2 { get; set; }
41+
private string TestProperty1 { get { return """"; } set { } }
42+
}
43+
";
44+
45+
await new CSharpTest
46+
{
47+
TestCode = testCode,
48+
ExpectedDiagnostics =
49+
{
50+
Diagnostic().WithLocation(0).WithArguments("protected", "private"),
51+
Diagnostic().WithLocation(1).WithArguments("protected internal", "protected"),
52+
Diagnostic().WithLocation(2).WithArguments("internal", "protected internal"),
53+
Diagnostic().WithLocation(3).WithArguments("public", "internal"),
54+
},
55+
FixedCode = fixedCode,
56+
NumberOfIncrementalIterations = 5,
57+
NumberOfFixAllIterations = 2,
58+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
59+
}
1060
}
1161
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CommonMemberData.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,37 @@ public static IEnumerable<object[]> DataTypeDeclarationKeywords
3131
}
3232
}
3333

34+
public static IEnumerable<object[]> ReferenceTypeDeclarationKeywords
35+
{
36+
get
37+
{
38+
yield return new[] { "class" };
39+
40+
if (LightupHelpers.SupportsCSharp9)
41+
{
42+
yield return new[] { "record" };
43+
}
44+
45+
if (LightupHelpers.SupportsCSharp10)
46+
{
47+
yield return new[] { "record class" };
48+
}
49+
}
50+
}
51+
52+
public static IEnumerable<object[]> ValueTypeDeclarationKeywords
53+
{
54+
get
55+
{
56+
yield return new[] { "struct" };
57+
58+
if (LightupHelpers.SupportsCSharp10)
59+
{
60+
yield return new[] { "record struct" };
61+
}
62+
}
63+
}
64+
3465
public static IEnumerable<object[]> RecordTypeDeclarationKeywords
3566
{
3667
get

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1202UnitTests.cs

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace StyleCop.Analyzers.Test.OrderingRules
99
using System.Threading.Tasks;
1010
using Microsoft.CodeAnalysis.Testing;
1111
using StyleCop.Analyzers.OrderingRules;
12+
using StyleCop.Analyzers.Test.Helpers;
1213
using Xunit;
1314
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
1415
StyleCop.Analyzers.OrderingRules.SA1202ElementsMustBeOrderedByAccess,
@@ -139,18 +140,20 @@ public class TestClass2 { }
139140
/// <summary>
140141
/// Verifies that the analyzer will properly handle property access levels.
141142
/// </summary>
143+
/// <param name="keyword">The keyword used to declare the type.</param>
142144
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
143-
[Fact]
144-
public async Task TestPropertiesAsync()
145+
[Theory]
146+
[MemberData(nameof(CommonMemberData.ReferenceTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
147+
public async Task TestPropertiesOfClassAsync(string keyword)
145148
{
146-
var testCode = @"public class TestClass
147-
{
148-
private string TestProperty1 { get; set; }
149-
protected string TestProperty2 { get; set; }
150-
protected internal string TestProperty3 { get; set; }
151-
internal string TestProperty4 { get; set; }
152-
public string TestProperty5 { get; set; }
153-
}
149+
var testCode = $@"public {keyword} TestClass
150+
{{
151+
private string TestProperty1 {{ get; set; }}
152+
protected string TestProperty2 {{ get; set; }}
153+
protected internal string TestProperty3 {{ get; set; }}
154+
internal string TestProperty4 {{ get; set; }}
155+
public string TestProperty5 {{ get; set; }}
156+
}}
154157
";
155158

156159
DiagnosticResult[] expected =
@@ -161,14 +164,48 @@ public async Task TestPropertiesAsync()
161164
Diagnostic().WithLocation(7, 19).WithArguments("public", "internal"),
162165
};
163166

164-
var fixedCode = @"public class TestClass
165-
{
166-
public string TestProperty5 { get; set; }
167-
internal string TestProperty4 { get; set; }
168-
protected internal string TestProperty3 { get; set; }
169-
protected string TestProperty2 { get; set; }
170-
private string TestProperty1 { get; set; }
171-
}
167+
var fixedCode = $@"public {keyword} TestClass
168+
{{
169+
public string TestProperty5 {{ get; set; }}
170+
internal string TestProperty4 {{ get; set; }}
171+
protected internal string TestProperty3 {{ get; set; }}
172+
protected string TestProperty2 {{ get; set; }}
173+
private string TestProperty1 {{ get; set; }}
174+
}}
175+
";
176+
177+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
178+
}
179+
180+
/// <summary>
181+
/// Verifies that the analyzer will properly handle property access levels.
182+
/// </summary>
183+
/// <param name="keyword">The keyword used to declare the type.</param>
184+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
185+
[Theory]
186+
[MemberData(nameof(CommonMemberData.ValueTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
187+
public async Task TestPropertiesOfStructAsync(string keyword)
188+
{
189+
var testCode = $@"public {keyword} TestClass
190+
{{
191+
private string TestProperty1 {{ get; set; }}
192+
internal string {{|#0:TestProperty4|}} {{ get; set; }}
193+
public string {{|#1:TestProperty5|}} {{ get; set; }}
194+
}}
195+
";
196+
197+
DiagnosticResult[] expected =
198+
{
199+
Diagnostic().WithLocation(0).WithArguments("internal", "private"),
200+
Diagnostic().WithLocation(1).WithArguments("public", "internal"),
201+
};
202+
203+
var fixedCode = $@"public {keyword} TestClass
204+
{{
205+
public string TestProperty5 {{ get; set; }}
206+
internal string TestProperty4 {{ get; set; }}
207+
private string TestProperty1 {{ get; set; }}
208+
}}
172209
";
173210

174211
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/MemberOrderHelper.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ internal static AccessLevel GetAccessLevelForOrdering(SyntaxNode member, SyntaxT
158158
{
159159
accessibility = AccessLevel.Internal;
160160
}
161+
else if (member.Parent.IsKind(SyntaxKind.InterfaceDeclaration))
162+
{
163+
accessibility = AccessLevel.Public;
164+
}
161165
else
162166
{
163167
accessibility = AccessLevel.Private;

StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1202ElementsMustBeOrderedByAccess.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ internal class SA1202ElementsMustBeOrderedByAccess : DiagnosticAnalyzer
5353
private static readonly DiagnosticDescriptor Descriptor =
5454
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.OrderingRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
5555

56-
private static readonly ImmutableArray<SyntaxKind> TypeDeclarationKinds =
57-
ImmutableArray.Create(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration);
58-
5956
private static readonly ImmutableHashSet<SyntaxKind> MemberKinds = ImmutableHashSet.Create(
6057
SyntaxKind.DelegateDeclaration,
6158
SyntaxKind.EnumDeclaration,
@@ -89,7 +86,7 @@ public override void Initialize(AnalysisContext context)
8986
{
9087
context.RegisterSyntaxNodeAction(CompilationUnitAction, SyntaxKind.CompilationUnit);
9188
context.RegisterSyntaxNodeAction(BaseNamespaceDeclarationAction, SyntaxKinds.BaseNamespaceDeclaration);
92-
context.RegisterSyntaxNodeAction(TypeDeclarationAction, TypeDeclarationKinds);
89+
context.RegisterSyntaxNodeAction(TypeDeclarationAction, SyntaxKinds.TypeDeclaration);
9390
});
9491
}
9592

0 commit comments

Comments
 (0)