Skip to content

Commit f206562

Browse files
authored
Merge pull request #4052 from sharwell/records
Add tests and documentation for records
2 parents 1c990cc + 406f7bd commit f206562

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
@@ -151,5 +151,83 @@ void M(int value)
151151
};
152152
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
153153
}
154+
155+
[Fact]
156+
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
157+
public async Task TestRecordInheritanceAsync()
158+
{
159+
const string testCode = @"
160+
public abstract record BaseQuery<T>;
161+
public record MyQuery1 {|#0:(|} ) : BaseQuery<object>;
162+
public record MyQuery2{|#1:(|} ) : BaseQuery<object>;
163+
public record MyQuery3 {|#2:(|}) : BaseQuery<object>;";
164+
const string fixedCode = @"
165+
public abstract record BaseQuery<T>;
166+
public record MyQuery1() : BaseQuery<object>;
167+
public record MyQuery2() : BaseQuery<object>;
168+
public record MyQuery3() : BaseQuery<object>;";
169+
170+
await new CSharpTest()
171+
{
172+
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
173+
ExpectedDiagnostics =
174+
{
175+
// /0/Test0.cs(3,24): warning SA1008: Opening parenthesis should not be preceded by a space.
176+
Diagnostic(DescriptorNotPreceded).WithLocation(0),
177+
178+
// /0/Test0.cs(3,24): warning SA1008: Opening parenthesis should not be followed by a space.
179+
Diagnostic(DescriptorNotFollowed).WithLocation(0),
180+
181+
// /0/Test0.cs(4,23): warning SA1008: Opening parenthesis should not be followed by a space.
182+
Diagnostic(DescriptorNotFollowed).WithLocation(1),
183+
184+
// /0/Test0.cs(5,24): warning SA1008: Opening parenthesis should not be preceded by a space.
185+
Diagnostic(DescriptorNotPreceded).WithLocation(2),
186+
},
187+
TestCode = testCode,
188+
FixedCode = fixedCode,
189+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
190+
}
191+
192+
[Fact]
193+
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
194+
public async Task TestRecordBaseArgumentsWithMultilineSpacingAsync()
195+
{
196+
const string testCode = @"
197+
public abstract record BaseRecord(string Text);
198+
199+
public record Derived1(string Text)
200+
: BaseRecord {|#0:(|}
201+
Text)
202+
{
203+
}
204+
205+
public record Derived2(string Text)
206+
: BaseRecord {|#1:(|}
207+
Text);
208+
";
209+
210+
const string fixedCode = @"
211+
public abstract record BaseRecord(string Text);
212+
213+
public record Derived1(string Text)
214+
: BaseRecord(
215+
Text)
216+
{
217+
}
218+
219+
public record Derived2(string Text)
220+
: BaseRecord(
221+
Text);
222+
";
223+
224+
DiagnosticResult[] expected =
225+
{
226+
Diagnostic(DescriptorNotPreceded).WithLocation(0),
227+
Diagnostic(DescriptorNotPreceded).WithLocation(1),
228+
};
229+
230+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
231+
}
154232
}
155233
}

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)