Skip to content

Commit 3414e7a

Browse files
authored
Merge pull request #3264 from sharwell/record-order
Support records in several additional rules
2 parents 9b285fc + e6720f1 commit 3414e7a

28 files changed

+528
-590
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ private Document CreateCodeFix(Document document, IndentationSettings indentatio
6969
case SyntaxKind.ClassDeclaration:
7070
case SyntaxKind.InterfaceDeclaration:
7171
case SyntaxKind.StructDeclaration:
72+
case SyntaxKindEx.RecordDeclaration:
7273
case SyntaxKind.EnumDeclaration:
7374
newSyntaxRoot = this.RegisterBaseTypeDeclarationCodeFix(syntaxRoot, (BaseTypeDeclarationSyntax)node, indentationSettings);
7475
break;

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1400CodeFixProvider.cs

+23
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace StyleCop.Analyzers.MaintainabilityRules
1313
using Microsoft.CodeAnalysis.CSharp;
1414
using Microsoft.CodeAnalysis.CSharp.Syntax;
1515
using StyleCop.Analyzers.Helpers;
16+
using StyleCop.Analyzers.Lightup;
1617

1718
/// <summary>
1819
/// Implements a code fix for <see cref="SA1400AccessModifierMustBeDeclared"/>.
@@ -82,6 +83,10 @@ private static Task<Document> GetTransformedDocumentAsync(Document document, Syn
8283
updatedDeclarationNode = HandleStructDeclaration((StructDeclarationSyntax)declarationNode);
8384
break;
8485

86+
case SyntaxKindEx.RecordDeclaration:
87+
updatedDeclarationNode = HandleRecordDeclaration((RecordDeclarationSyntaxWrapper)declarationNode);
88+
break;
89+
8590
case SyntaxKind.DelegateDeclaration:
8691
updatedDeclarationNode = HandleDelegateDeclaration((DelegateDeclarationSyntax)declarationNode);
8792
break;
@@ -194,6 +199,23 @@ private static SyntaxNode HandleStructDeclaration(StructDeclarationSyntax node)
194199
.WithoutFormatting();
195200
}
196201

202+
private static SyntaxNode HandleRecordDeclaration(RecordDeclarationSyntaxWrapper node)
203+
{
204+
SyntaxToken triviaToken = node.Keyword;
205+
if (triviaToken.IsMissing)
206+
{
207+
return null;
208+
}
209+
210+
SyntaxKind defaultVisibility = IsNestedType(node) ? SyntaxKind.PrivateKeyword : SyntaxKind.InternalKeyword;
211+
SyntaxTokenList modifiers = DeclarationModifiersHelper.AddModifier(node.Modifiers, ref triviaToken, defaultVisibility);
212+
return node
213+
.WithKeyword(triviaToken)
214+
.WithModifiers(modifiers)
215+
.SyntaxNode
216+
.WithoutFormatting();
217+
}
218+
197219
private static SyntaxNode HandleDelegateDeclaration(DelegateDeclarationSyntax node)
198220
{
199221
SyntaxToken triviaToken = node.DelegateKeyword;
@@ -355,6 +377,7 @@ private static SyntaxNode FindParentDeclarationNode(SyntaxNode node)
355377
case SyntaxKind.InterfaceDeclaration:
356378
case SyntaxKind.EnumDeclaration:
357379
case SyntaxKind.StructDeclaration:
380+
case SyntaxKindEx.RecordDeclaration:
358381
case SyntaxKind.DelegateDeclaration:
359382
case SyntaxKind.EventDeclaration:
360383
case SyntaxKind.EventFieldDeclaration:

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/SA1205CodeFixProvider.cs

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace StyleCop.Analyzers.OrderingRules
1313
using Microsoft.CodeAnalysis.CSharp;
1414
using Microsoft.CodeAnalysis.CSharp.Syntax;
1515
using StyleCop.Analyzers.Helpers;
16+
using StyleCop.Analyzers.Lightup;
1617

1718
/// <summary>
1819
/// Implements code fixes for <see cref="SA1205PartialElementsMustDeclareAccess"/>.
@@ -25,6 +26,7 @@ internal class SA1205CodeFixProvider : CodeFixProvider
2526
private static readonly ImmutableArray<SyntaxKind> InternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.InternalKeyword);
2627
private static readonly ImmutableArray<SyntaxKind> ProtectedAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.ProtectedKeyword);
2728
private static readonly ImmutableArray<SyntaxKind> ProtectedOrInternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.ProtectedKeyword, SyntaxKind.InternalKeyword);
29+
private static readonly ImmutableArray<SyntaxKind> ProtectedAndInternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.PrivateKeyword, SyntaxKind.ProtectedKeyword);
2830
private static readonly ImmutableArray<SyntaxKind> PrivateAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.PrivateKeyword);
2931
private static readonly ImmutableArray<SyntaxKind> UnexpectedAccessibilityKeywords = ImmutableArray.Create<SyntaxKind>();
3032

@@ -88,6 +90,8 @@ private static ImmutableArray<SyntaxKind> GetMissingAccessModifiers(Accessibilit
8890
return ProtectedAccessibilityKeywords;
8991
case Accessibility.ProtectedOrInternal:
9092
return ProtectedOrInternalAccessibilityKeywords;
93+
case Accessibility.ProtectedAndInternal:
94+
return ProtectedAndInternalAccessibilityKeywords;
9195
case Accessibility.Private:
9296
return PrivateAccessibilityKeywords;
9397
default:
@@ -108,6 +112,8 @@ private static TypeDeclarationSyntax ReplaceModifiers(TypeDeclarationSyntax node
108112
return ((InterfaceDeclarationSyntax)node).WithModifiers(modifiers);
109113
case SyntaxKind.StructDeclaration:
110114
return ((StructDeclarationSyntax)node).WithModifiers(modifiers);
115+
case SyntaxKindEx.RecordDeclaration:
116+
return ((RecordDeclarationSyntaxWrapper)node).WithModifiers(modifiers);
111117
}
112118

113119
return node;
@@ -125,6 +131,8 @@ private static TypeDeclarationSyntax ReplaceKeyword(TypeDeclarationSyntax node,
125131
return ((InterfaceDeclarationSyntax)node).WithKeyword(keyword);
126132
case SyntaxKind.StructDeclaration:
127133
return ((StructDeclarationSyntax)node).WithKeyword(keyword);
134+
case SyntaxKindEx.RecordDeclaration:
135+
return ((RecordDeclarationSyntaxWrapper)node).WithKeyword(keyword);
128136
}
129137

130138
return node;

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/OrderingRules/SA1201CSharp9UnitTests.cs

+130
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,139 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp9.OrderingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp8.OrderingRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
12+
StyleCop.Analyzers.OrderingRules.SA1201ElementsMustAppearInTheCorrectOrder,
13+
StyleCop.Analyzers.OrderingRules.ElementOrderCodeFixProvider>;
714

815
public class SA1201CSharp9UnitTests : SA1201CSharp8UnitTests
916
{
17+
[Fact]
18+
[WorkItem(3236, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3236")]
19+
public async Task TestOuterOrderWithRecordCorrectOrderAsync()
20+
{
21+
string testCode = @"namespace Foo { }
22+
public delegate void bar();
23+
public enum TestEnum { }
24+
public interface IFoo { }
25+
public struct FooStruct { }
26+
public record FooClass { }
27+
";
28+
29+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
30+
await VerifyCSharpDiagnosticAsync("namespace OuterNamespace { " + testCode + " }", DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
31+
}
32+
33+
[Fact]
34+
[WorkItem(3236, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3236")]
35+
public async Task TestOuterOrderWithRecordWrongOrderAsync()
36+
{
37+
string testCode = @"
38+
namespace Foo { }
39+
public enum TestEnum { }
40+
public delegate void {|#0:bar|}();
41+
public interface IFoo { }
42+
public record FooClass { }
43+
public struct {|#1:FooStruct|} { }
44+
";
45+
var expected = new[]
46+
{
47+
Diagnostic().WithLocation(0).WithArguments("delegate", "enum"),
48+
Diagnostic().WithLocation(1).WithArguments("struct", "record"),
49+
};
50+
51+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
52+
await VerifyCSharpDiagnosticAsync("namespace OuterNamespace { " + testCode + " }", expected, CancellationToken.None).ConfigureAwait(false);
53+
}
54+
55+
[Fact]
56+
public async Task TestTypeMemberOrderCorrectOrderRecordAsync()
57+
{
58+
string testCode = @"public record OuterType
59+
{
60+
public string TestField;
61+
public OuterType(int argument) { TestField = ""foo""; TestProperty = """"; }
62+
public delegate void TestDelegate();
63+
public event TestDelegate TestEvent { add { } remove { } }
64+
public enum TestEnum { }
65+
public interface ITest { }
66+
public string TestProperty { get; set; }
67+
public string this[string arg] { get { return ""foo""; } set { } }
68+
public static explicit operator bool(OuterType t1) { return t1.TestField != null; }
69+
public static OuterType operator +(OuterType t1, OuterType t2) { return t1; }
70+
public void TestMethod () { }
71+
public struct TestStruct { }
72+
public class TestClass1 { }
73+
public record TestRecord1 { }
74+
public class TestClass2 { }
75+
public record TestRecord2 { }
76+
}
77+
";
78+
79+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
80+
}
81+
82+
[Fact]
83+
public async Task TestTypeMemberOrderWrongOrderRecordAsync()
84+
{
85+
string testCode = @"public record OuterType
86+
{
87+
public string TestField;
88+
public OuterType(int argument) { TestField = ""foo""; TestProperty = ""bar""; }
89+
public interface ITest { }
90+
public delegate void TestDelegate();
91+
public event TestDelegate TestEvent { add { } remove { } }
92+
public enum TestEnum { }
93+
public static OuterType operator +(OuterType t1, OuterType t2) { return t1; }
94+
public static explicit operator bool(OuterType t1) { return t1.TestField != null; }
95+
public string TestProperty { get; set; }
96+
public struct TestStruct { }
97+
public void TestMethod () { }
98+
public class TestClass { }
99+
public string this[string arg] { get { return ""foo""; } set { } }
100+
}
101+
";
102+
var expected = new[]
103+
{
104+
Diagnostic().WithLocation(6, 26).WithArguments("delegate", "interface"),
105+
Diagnostic().WithLocation(10, 5).WithArguments("conversion", "operator"),
106+
Diagnostic().WithLocation(11, 19).WithArguments("property", "conversion"),
107+
Diagnostic().WithLocation(13, 17).WithArguments("method", "struct"),
108+
Diagnostic().WithLocation(15, 19).WithArguments("indexer", "class"),
109+
};
110+
111+
string fixedCode = @"public record OuterType
112+
{
113+
public string TestField;
114+
public OuterType(int argument) { TestField = ""foo""; TestProperty = ""bar""; }
115+
public delegate void TestDelegate();
116+
public event TestDelegate TestEvent { add { } remove { } }
117+
public enum TestEnum { }
118+
public interface ITest { }
119+
public string TestProperty { get; set; }
120+
public string this[string arg] { get { return ""foo""; } set { } }
121+
public static explicit operator bool(OuterType t1) { return t1.TestField != null; }
122+
public static OuterType operator +(OuterType t1, OuterType t2) { return t1; }
123+
public void TestMethod () { }
124+
public struct TestStruct { }
125+
public class TestClass { }
126+
}
127+
";
128+
129+
var test = new CSharpTest
130+
{
131+
TestCode = testCode,
132+
FixedCode = fixedCode,
133+
NumberOfIncrementalIterations = 7,
134+
NumberOfFixAllIterations = 3,
135+
};
136+
137+
test.ExpectedDiagnostics.AddRange(expected);
138+
await test.RunAsync(CancellationToken.None).ConfigureAwait(false);
139+
}
10140
}
11141
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1604UnitTests.cs

+7-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading.Tasks;
88
using Microsoft.CodeAnalysis.Testing;
99
using StyleCop.Analyzers.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Helpers;
1011
using StyleCop.Analyzers.Test.Verifiers;
1112
using Xunit;
1213
using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier<StyleCop.Analyzers.DocumentationRules.SA1604ElementDocumentationMustHaveSummary>;
@@ -17,10 +18,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
1718
public class SA1604UnitTests
1819
{
1920
[Theory]
20-
[InlineData("enum")]
21-
[InlineData("class")]
22-
[InlineData("struct")]
23-
[InlineData("interface")]
21+
[MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
2422
public async Task TestTypeNoDocumentationAsync(string typeName)
2523
{
2624
var testCode = @"
@@ -31,10 +29,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName)
3129
}
3230

3331
[Theory]
34-
[InlineData("enum")]
35-
[InlineData("class")]
36-
[InlineData("struct")]
37-
[InlineData("interface")]
32+
[MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
3833
public async Task TestTypeWithDocumentationAsync(string typeName)
3934
{
4035
var testCode = @"
@@ -48,10 +43,7 @@ public async Task TestTypeWithDocumentationAsync(string typeName)
4843
}
4944

5045
[Theory]
51-
[InlineData("enum")]
52-
[InlineData("class")]
53-
[InlineData("struct")]
54-
[InlineData("interface")]
46+
[MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
5547
public async Task TestTypeWithInheritedDocumentationAsync(string typeName)
5648
{
5749
var testCode = @"
@@ -63,10 +55,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName)
6355
}
6456

6557
[Theory]
66-
[InlineData("enum")]
67-
[InlineData("class")]
68-
[InlineData("struct")]
69-
[InlineData("interface")]
58+
[MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
7059
public async Task TestTypeWithoutDocumentationAsync(string typeName)
7160
{
7261
var testCode = @"
@@ -82,14 +71,12 @@ public async Task TestTypeWithoutDocumentationAsync(string typeName)
8271
}
8372

8473
[Theory]
85-
[InlineData("partial class")]
86-
[InlineData("partial struct")]
87-
[InlineData("partial interface")]
74+
[MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
8875
public async Task TestPartialTypeWithoutDocumentationAsync(string typeName)
8976
{
9077
var testCode = @"
9178
///
92-
{0}
79+
partial {0}
9380
TypeName
9481
{{
9582
}}";

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1605UnitTests.cs

+7-19
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading.Tasks;
88
using Microsoft.CodeAnalysis.Testing;
99
using StyleCop.Analyzers.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Helpers;
1011
using StyleCop.Analyzers.Test.Verifiers;
1112
using Xunit;
1213
using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier<StyleCop.Analyzers.DocumentationRules.SA1605PartialElementDocumentationMustHaveSummary>;
@@ -27,9 +28,7 @@ public class SA1605UnitTests
2728
";
2829

2930
[Theory]
30-
[InlineData("class")]
31-
[InlineData("struct")]
32-
[InlineData("interface")]
31+
[MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
3332
public async Task TestTypeNoDocumentationAsync(string typeName)
3433
{
3534
var testCode = @"
@@ -40,9 +39,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName)
4039
}
4140

4241
[Theory]
43-
[InlineData("class")]
44-
[InlineData("struct")]
45-
[InlineData("interface")]
42+
[MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
4643
public async Task TestTypeWithSummaryDocumentationAsync(string typeName)
4744
{
4845
var testCode = @"
@@ -56,9 +53,7 @@ public async Task TestTypeWithSummaryDocumentationAsync(string typeName)
5653
}
5754

5855
[Theory]
59-
[InlineData("class")]
60-
[InlineData("struct")]
61-
[InlineData("interface")]
56+
[MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
6257
public async Task TestTypeWithContentDocumentationAsync(string typeName)
6358
{
6459
var testCode = @"
@@ -72,9 +67,7 @@ public async Task TestTypeWithContentDocumentationAsync(string typeName)
7267
}
7368

7469
[Theory]
75-
[InlineData("class")]
76-
[InlineData("struct")]
77-
[InlineData("interface")]
70+
[MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
7871
public async Task TestTypeWithInheritedDocumentationAsync(string typeName)
7972
{
8073
var testCode = @"
@@ -86,9 +79,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName)
8679
}
8780

8881
[Theory]
89-
[InlineData("class")]
90-
[InlineData("struct")]
91-
[InlineData("interface")]
82+
[MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
9283
public async Task TestTypeWithoutDocumentationAsync(string typeName)
9384
{
9485
var testCode = @"
@@ -104,10 +95,7 @@ public async Task TestTypeWithoutDocumentationAsync(string typeName)
10495
}
10596

10697
[Theory]
107-
[InlineData("enum")]
108-
[InlineData("class")]
109-
[InlineData("struct")]
110-
[InlineData("interface")]
98+
[MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
11199
public async Task TestNonPartialTypeWithoutDocumentationAsync(string typeName)
112100
{
113101
var testCode = @"

0 commit comments

Comments
 (0)