Skip to content

Commit cc559eb

Browse files
committed
Add tests and documentation for records
Closes #3965
1 parent ff14d11 commit cc559eb

File tree

6 files changed

+140
-0
lines changed

6 files changed

+140
-0
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/SpacingRules/SA1008CSharp9UnitTests.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,83 @@ public async Task TestDeconstructionInTopLevelProgramAsync(string prefix)
110110
FixedCode = fixedCode,
111111
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
112112
}
113+
114+
[Fact]
115+
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
116+
public async Task TestRecordInheritanceAsync()
117+
{
118+
const string testCode = @"
119+
public abstract record BaseQuery<T>;
120+
public record MyQuery1 {|#0:(|} ) : BaseQuery<object>;
121+
public record MyQuery2{|#1:(|} ) : BaseQuery<object>;
122+
public record MyQuery3 {|#2:(|}) : BaseQuery<object>;";
123+
const string fixedCode = @"
124+
public abstract record BaseQuery<T>;
125+
public record MyQuery1() : BaseQuery<object>;
126+
public record MyQuery2() : BaseQuery<object>;
127+
public record MyQuery3() : BaseQuery<object>;";
128+
129+
await new CSharpTest()
130+
{
131+
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
132+
ExpectedDiagnostics =
133+
{
134+
// /0/Test0.cs(3,24): warning SA1008: Opening parenthesis should not be preceded by a space.
135+
Diagnostic(DescriptorNotPreceded).WithLocation(0),
136+
137+
// /0/Test0.cs(3,24): warning SA1008: Opening parenthesis should not be followed by a space.
138+
Diagnostic(DescriptorNotFollowed).WithLocation(0),
139+
140+
// /0/Test0.cs(4,23): warning SA1008: Opening parenthesis should not be followed by a space.
141+
Diagnostic(DescriptorNotFollowed).WithLocation(1),
142+
143+
// /0/Test0.cs(5,24): warning SA1008: Opening parenthesis should not be preceded by a space.
144+
Diagnostic(DescriptorNotPreceded).WithLocation(2),
145+
},
146+
TestCode = testCode,
147+
FixedCode = fixedCode,
148+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
149+
}
150+
151+
[Fact]
152+
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
153+
public async Task TestRecordBaseArgumentsWithMultilineSpacingAsync()
154+
{
155+
const string testCode = @"
156+
public abstract record BaseRecord(string Text);
157+
158+
public record Derived1(string Text)
159+
: BaseRecord {|#0:(|}
160+
Text)
161+
{
162+
}
163+
164+
public record Derived2(string Text)
165+
: BaseRecord {|#1:(|}
166+
Text);
167+
";
168+
169+
const string fixedCode = @"
170+
public abstract record BaseRecord(string Text);
171+
172+
public record Derived1(string Text)
173+
: BaseRecord(
174+
Text)
175+
{
176+
}
177+
178+
public record Derived2(string Text)
179+
: BaseRecord(
180+
Text);
181+
";
182+
183+
DiagnosticResult[] expected =
184+
{
185+
Diagnostic(DescriptorNotPreceded).WithLocation(0),
186+
Diagnostic(DescriptorNotPreceded).WithLocation(1),
187+
};
188+
189+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
190+
}
113191
}
114192
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/SpacingRules/SA1009CSharp9UnitTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,47 @@ public record MyQuery3() : BaseQuery<object>;";
5353
FixedCode = fixedCode,
5454
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
5555
}
56+
57+
[Fact]
58+
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
59+
public async Task TestRecordBaseArgumentsWithMultilineSpacingAsync()
60+
{
61+
const string testCode = @"
62+
public abstract record BaseRecord(string Text);
63+
64+
public record Derived1(string Text)
65+
: BaseRecord(
66+
Text {|#0:)|}
67+
{
68+
}
69+
70+
public record Derived2(string Text)
71+
: BaseRecord(
72+
Text {|#1:)|} ;
73+
";
74+
75+
const string fixedCode = @"
76+
public abstract record BaseRecord(string Text);
77+
78+
public record Derived1(string Text)
79+
: BaseRecord(
80+
Text)
81+
{
82+
}
83+
84+
public record Derived2(string Text)
85+
: BaseRecord(
86+
Text);
87+
";
88+
89+
DiagnosticResult[] expected =
90+
{
91+
Diagnostic(DescriptorNotPreceded).WithLocation(0),
92+
Diagnostic(DescriptorNotPreceded).WithLocation(1),
93+
Diagnostic(DescriptorNotFollowed).WithLocation(1),
94+
};
95+
96+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
97+
}
5698
}
5799
}

documentation/SA1202.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ Default interface members (including members with implementations and static int
4646
ordering as class members. Within an interface, `public` members should appear before `internal`, `protected internal`,
4747
`protected`, `private protected`, and `private` members.
4848

49+
### Records and primary constructors
50+
51+
Records (including positional records) and record structs follow the same ordering rules as classes and structs. Primary
52+
constructor parameters that become properties do not change the expected access ordering for the remaining members.
53+
4954
## How to fix violations
5055

5156
To fix an instance of this violation, order the elements in the file in the order described above.

documentation/SA1313.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,18 @@ The name of a parameter in C# does not begin with a lower-case letter.
2525

2626
A violation of this rule occurs when the name of a parameter does not begin with a lower-case letter.
2727

28+
### Discards
29+
2830
An exception to this rule is made for lambda parameters named `_` and `__`. These parameters are often used to designate a
2931
placeholder parameter which is not actually used in the body of the lambda expression.
3032

33+
### Records and primary constructors
34+
35+
Positional record parameters become public properties, so PascalCase names are acceptable for those parameters even
36+
though they appear in the primary constructor signature.
37+
38+
### Interop code
39+
3140
If the parameter name is intended to match the name of an item associated with Win32 or COM, and thus needs to begin
3241
with an upper-case letter, place the parameter within a special `NativeMethods` class. A `NativeMethods` class is any
3342
class which contains a name ending in `NativeMethods`, and is intended as a placeholder for Win32 or COM wrappers.

documentation/SA1600.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ C# syntax provides a mechanism for inserting documentation for classes and eleme
2525

2626
A violation of this rule occurs if an element is completely missing a documentation header, or if the header is empty. In C# the following types of elements can have documentation headers: classes, constructors, delegates, enums, events, finalizers, indexers, interfaces, methods, properties, records, and structs.
2727

28+
### Records and primary constructors
29+
30+
Record classes and record structs follow the same expectations as classes and structs. Positional parameters in a record declaration become properties; when documenting a public record, include `<param>` tags for those parameters in addition to the `<summary>` text for the record itself.
31+
2832
### Default interface members
2933

3034
Interface members are implicitly `public` unless otherwise specified. Default interface members (methods, properties, indexers, events, etc. that include implementations) follow these rules:

documentation/SA1642.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public MyStruct()
5252
}
5353
```
5454

55+
For C# 9 record types, constructors follow the same pattern: record classes use the word 'class' in the summary text, while record structs use 'struct'.
56+
5557
If the class contains generic parameters, these can be annotated within the `cref` link using either of the following
5658
two formats:
5759

0 commit comments

Comments
 (0)