Skip to content

Commit e893338

Browse files
authored
Merge pull request #4072 from sharwell/extended-property-patterns
Update for extended property patterns in C# 10
2 parents 3733893 + 300a473 commit e893338

File tree

6 files changed

+126
-0
lines changed

6 files changed

+126
-0
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/ReadabilityRules/SA1101CSharp10UnitTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,63 @@ public bool Method(Test arg)
3131

3232
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
3333
}
34+
35+
[Fact]
36+
[WorkItem(3984, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3984")]
37+
public async Task TestExtendedPropertyPatternInSwitchAsync()
38+
{
39+
var testCode = @"public class Test
40+
{
41+
public Test Child { get; init; }
42+
public int Value { get; init; }
43+
44+
public int Evaluate(Test other)
45+
{
46+
return other switch
47+
{
48+
{ Child.Value: > 0 } and not { Value: 0 } => 1,
49+
{ Child.Value: <= 0 } or ({ Value: 0 } and { Child.Value: 0 }) => 0,
50+
_ => -1,
51+
};
52+
}
53+
}";
54+
55+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
56+
}
57+
58+
[Fact]
59+
[WorkItem(3984, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3984")]
60+
public async Task TestExtendedPropertyPatternWithInstanceMemberAccessAsync()
61+
{
62+
var testCode = @"public class Test
63+
{
64+
private int value;
65+
66+
public Test Child { get; }
67+
public int Value { get; }
68+
69+
public bool Evaluate(Test other)
70+
{
71+
return other is { Child.Value: > 0 } && {|#0:value|} > 0;
72+
}
73+
}";
74+
75+
var fixedCode = @"public class Test
76+
{
77+
private int value;
78+
79+
public Test Child { get; }
80+
public int Value { get; }
81+
82+
public bool Evaluate(Test other)
83+
{
84+
return other is { Child.Value: > 0 } && this.value > 0;
85+
}
86+
}";
87+
88+
var expected = Diagnostic().WithLocation(0);
89+
90+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
91+
}
3492
}
3593
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/SpacingRules/SA1024CSharp10UnitTests.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.CSharp10.SpacingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp9.SpacingRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.SpacingRules.SA1024ColonsMustBeSpacedCorrectly;
12+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
13+
StyleCop.Analyzers.SpacingRules.SA1024ColonsMustBeSpacedCorrectly,
14+
StyleCop.Analyzers.SpacingRules.TokenSpacingCodeFixProvider>;
715

816
public partial class SA1024CSharp10UnitTests : SA1024CSharp9UnitTests
917
{
18+
[Fact]
19+
[WorkItem(3984, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3984")]
20+
public async Task TestExtendedPropertyPatternSpacingAsync()
21+
{
22+
var testCode = @"
23+
public class TestClass
24+
{
25+
public bool Test(SomeType value) => value is { Nested.Value: 1, Other.Value {|#0::|} 2, Third.Value{|#1::|}3 };
26+
}
27+
28+
public class SomeType
29+
{
30+
public SomeType Nested { get; set; }
31+
public SomeType Other { get; set; }
32+
public SomeType Third { get; set; }
33+
public SomeType Child { get; set; }
34+
public int Value { get; set; }
35+
}";
36+
37+
var fixedCode = @"
38+
public class TestClass
39+
{
40+
public bool Test(SomeType value) => value is { Nested.Value: 1, Other.Value: 2, Third.Value: 3 };
41+
}
42+
43+
public class SomeType
44+
{
45+
public SomeType Nested { get; set; }
46+
public SomeType Other { get; set; }
47+
public SomeType Third { get; set; }
48+
public SomeType Child { get; set; }
49+
public int Value { get; set; }
50+
}";
51+
52+
DiagnosticResult[] expected =
53+
{
54+
Diagnostic(DescriptorNotPreceded).WithLocation(0),
55+
Diagnostic(DescriptorFollowed).WithLocation(1),
56+
};
57+
58+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
59+
}
1060
}
1161
}

StyleCop.Analyzers/StyleCop.Analyzers/Lightup/SyntaxKindEx.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ internal static class SyntaxKindEx
7979
public const SyntaxKind PrimaryConstructorBaseType = (SyntaxKind)9065;
8080
public const SyntaxKind FunctionPointerUnmanagedCallingConventionList = (SyntaxKind)9066;
8181
public const SyntaxKind RecordStructDeclaration = (SyntaxKind)9068;
82+
public const SyntaxKind ExpressionColon = (SyntaxKind)9069;
8283
public const SyntaxKind CollectionExpression = (SyntaxKind)9076;
8384
}
8485
}

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1024ColonsMustBeSpacedCorrectly.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ private static void HandleColonToken(SyntaxTreeAnalysisContext context, SyntaxTo
137137
case SyntaxKindEx.CasePatternSwitchLabel:
138138
// NameColon is not explicitly listed in the description of this warning, but the behavior is inferred
139139
case SyntaxKind.NameColon:
140+
case SyntaxKindEx.ExpressionColon:
140141
requireBefore = false;
141142
break;
142143

documentation/SA1024.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ A colon that appears as part of a string interpolation formatting component shou
5151
var s = $"{x:N}";
5252
```
5353

54+
When a colon appears within a property pattern, including nested members referenced through extended property pattern syntax, it is treated like a named element separator. The colon should not be preceded by whitespace, and it should normally be followed by a single space.
55+
56+
```csharp
57+
if (item is { Outer.Inner: value })
58+
{
59+
}
60+
```
61+
5462
Finally, when a colon is used within a conditional statement, it should always contain a single space on either side, unless the colon is the first or last character on the line. For example:
5563

5664
```csharp

documentation/SA1101.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ By default, StyleCop disallows the use of underscores or *m_* to mark local clas
2727

2828
A final advantage of using the 'this.' prefix is that typing *this.* will cause Visual Studio to show the IntelliSense popup, making it quick and easy for the developer to choose the class member to call.
2929

30+
When using property patterns, including the extended property pattern syntax introduced in C# 10, the rule does not require (or allow) `this.` to qualify the properties referenced by the pattern. These expressions refer to members of the value being matched rather than the containing type. For example, no SA1101 diagnostic is reported for the following:
31+
32+
```csharp
33+
if (item is { Outer.Inner: value })
34+
{
35+
}
36+
```
37+
3038
## How to fix violations
3139

3240
To fix a violation of this rule, insert the 'this.' prefix before the call to the class member.

0 commit comments

Comments
 (0)