Skip to content

Commit 6399f49

Browse files
authored
Merge pull request #3209 from sharwell/positional-record
Update naming rules to support records
2 parents 6b94444 + d6a8075 commit 6399f49

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/NamingRules/SA1300CSharp9UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp9.NamingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.CSharp;
69
using StyleCop.Analyzers.Test.CSharp8.NamingRules;
10+
using StyleCop.Analyzers.Test.Verifiers;
11+
using Xunit;
12+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
13+
StyleCop.Analyzers.NamingRules.SA1300ElementMustBeginWithUpperCaseLetter,
14+
StyleCop.Analyzers.NamingRules.RenameToUpperCaseCodeFixProvider>;
715

816
public class SA1300CSharp9UnitTests : SA1300CSharp8UnitTests
917
{
18+
[Fact]
19+
public async Task TestPositionalRecord1Async()
20+
{
21+
var testCode = @"
22+
public record {|#0:r|}(int A)
23+
{
24+
public r(int a, int b)
25+
: this(A: a)
26+
{
27+
}
28+
}
29+
";
30+
31+
var fixedCode = @"
32+
public record R(int A)
33+
{
34+
public R(int a, int b)
35+
: this(A: a)
36+
{
37+
}
38+
}
39+
";
40+
41+
await new CSharpTest(LanguageVersion.CSharp9)
42+
{
43+
ReferenceAssemblies = GenericAnalyzerTest.ReferenceAssembliesNet50,
44+
TestCode = testCode,
45+
ExpectedDiagnostics =
46+
{
47+
// /0/Test0.cs(2,15): warning SA1300: Element 'r' should begin with an uppercase letter
48+
Diagnostic().WithLocation(0).WithArguments("r"),
49+
50+
// /0/Test0.cs(2,15): warning SA1300: Element 'r' should begin with an uppercase letter
51+
Diagnostic().WithLocation(0).WithArguments("r"),
52+
},
53+
FixedCode = fixedCode,
54+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
55+
}
56+
57+
[Fact]
58+
public async Task TestPositionalRecord2Async()
59+
{
60+
var testCode = @"
61+
public record R(int [|a|])
62+
{
63+
public R(int a, int b)
64+
: this(a: a)
65+
{
66+
}
67+
}
68+
";
69+
70+
var fixedCode = @"
71+
public record R(int A)
72+
{
73+
public R(int a, int b)
74+
: this(A: a)
75+
{
76+
}
77+
}
78+
";
79+
80+
await new CSharpTest(LanguageVersion.CSharp9)
81+
{
82+
ReferenceAssemblies = GenericAnalyzerTest.ReferenceAssembliesNet50,
83+
TestCode = testCode,
84+
FixedCode = fixedCode,
85+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
86+
}
1087
}
1188
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/NamingRules/SA1313CSharp9UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp9.NamingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.CSharp;
69
using StyleCop.Analyzers.Test.CSharp8.NamingRules;
10+
using StyleCop.Analyzers.Test.Verifiers;
11+
using Xunit;
12+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
13+
StyleCop.Analyzers.NamingRules.SA1313ParameterNamesMustBeginWithLowerCaseLetter,
14+
StyleCop.Analyzers.NamingRules.RenameToLowerCaseCodeFixProvider>;
715

816
public class SA1313CSharp9UnitTests : SA1313CSharp8UnitTests
917
{
18+
[Fact]
19+
[WorkItem(3168, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3168")]
20+
[WorkItem(3181, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3181")]
21+
public async Task TestPositionalRecordAsync()
22+
{
23+
var testCode = @"
24+
public record R(int A)
25+
{
26+
public R(int [|A|], int [|B|])
27+
: this(A)
28+
{
29+
}
30+
}
31+
";
32+
33+
var fixedCode = @"
34+
public record R(int A)
35+
{
36+
public R(int a, int b)
37+
: this(a)
38+
{
39+
}
40+
}
41+
";
42+
43+
await new CSharpTest(LanguageVersion.CSharp9)
44+
{
45+
ReferenceAssemblies = GenericAnalyzerTest.ReferenceAssembliesNet50,
46+
TestCode = testCode,
47+
FixedCode = fixedCode,
48+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
49+
}
1050
}
1151
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ internal static class SyntaxKindEx
5050
public const SyntaxKind SuppressNullableWarningExpression = (SyntaxKind)9054;
5151
public const SyntaxKind NullableDirectiveTrivia = (SyntaxKind)9055;
5252
public const SyntaxKind WithInitializerExpression = (SyntaxKind)9062;
53+
public const SyntaxKind RecordDeclaration = (SyntaxKind)9063;
5354
}
5455
}

StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1300ElementMustBeginWithUpperCaseLetter.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ internal class SA1300ElementMustBeginWithUpperCaseLetter : DiagnosticAnalyzer
5151

5252
private static readonly Action<SyntaxNodeAnalysisContext, StyleCopSettings> NamespaceDeclarationAction = HandleNamespaceDeclaration;
5353
private static readonly Action<SyntaxNodeAnalysisContext> ClassDeclarationAction = HandleClassDeclaration;
54+
private static readonly Action<SyntaxNodeAnalysisContext> RecordDeclarationAction = HandleRecordDeclaration;
5455
private static readonly Action<SyntaxNodeAnalysisContext> EnumDeclarationAction = HandleEnumDeclaration;
5556
private static readonly Action<SyntaxNodeAnalysisContext> EnumMemberDeclarationAction = HandleEnumMemberDeclaration;
5657
private static readonly Action<SyntaxNodeAnalysisContext> StructDeclarationAction = HandleStructDeclaration;
@@ -60,6 +61,7 @@ internal class SA1300ElementMustBeginWithUpperCaseLetter : DiagnosticAnalyzer
6061
private static readonly Action<SyntaxNodeAnalysisContext> MethodDeclarationAction = HandleMethodDeclaration;
6162
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
6263
private static readonly Action<SyntaxNodeAnalysisContext> PropertyDeclarationAction = HandlePropertyDeclaration;
64+
private static readonly Action<SyntaxNodeAnalysisContext> ParameterAction = HandleParameter;
6365

6466
/// <inheritdoc/>
6567
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
@@ -75,6 +77,7 @@ public override void Initialize(AnalysisContext context)
7577
// Note: Fields are handled by SA1303 through SA1311
7678
context.RegisterSyntaxNodeAction(NamespaceDeclarationAction, SyntaxKind.NamespaceDeclaration);
7779
context.RegisterSyntaxNodeAction(ClassDeclarationAction, SyntaxKind.ClassDeclaration);
80+
context.RegisterSyntaxNodeAction(RecordDeclarationAction, SyntaxKindEx.RecordDeclaration);
7881
context.RegisterSyntaxNodeAction(EnumDeclarationAction, SyntaxKind.EnumDeclaration);
7982
context.RegisterSyntaxNodeAction(EnumMemberDeclarationAction, SyntaxKind.EnumMemberDeclaration);
8083
context.RegisterSyntaxNodeAction(StructDeclarationAction, SyntaxKind.StructDeclaration);
@@ -84,6 +87,7 @@ public override void Initialize(AnalysisContext context)
8487
context.RegisterSyntaxNodeAction(MethodDeclarationAction, SyntaxKind.MethodDeclaration);
8588
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
8689
context.RegisterSyntaxNodeAction(PropertyDeclarationAction, SyntaxKind.PropertyDeclaration);
90+
context.RegisterSyntaxNodeAction(ParameterAction, SyntaxKind.Parameter);
8791
}
8892

8993
private static void HandleNamespaceDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettings settings)
@@ -121,6 +125,11 @@ private static void HandleClassDeclaration(SyntaxNodeAnalysisContext context)
121125
CheckElementNameToken(context, ((ClassDeclarationSyntax)context.Node).Identifier);
122126
}
123127

128+
private static void HandleRecordDeclaration(SyntaxNodeAnalysisContext context)
129+
{
130+
CheckElementNameToken(context, ((TypeDeclarationSyntax)context.Node).Identifier);
131+
}
132+
124133
private static void HandleEnumDeclaration(SyntaxNodeAnalysisContext context)
125134
{
126135
CheckElementNameToken(context, ((EnumDeclarationSyntax)context.Node).Identifier);
@@ -203,6 +212,19 @@ private static void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context)
203212
CheckElementNameToken(context, propertyDeclaration.Identifier);
204213
}
205214

215+
private static void HandleParameter(SyntaxNodeAnalysisContext context)
216+
{
217+
var parameterDeclaration = (ParameterSyntax)context.Node;
218+
if (!parameterDeclaration.Parent.IsKind(SyntaxKind.ParameterList)
219+
|| !parameterDeclaration.Parent.Parent.IsKind(SyntaxKindEx.RecordDeclaration))
220+
{
221+
// Only positional parameters of records are treated as properties
222+
return;
223+
}
224+
225+
CheckElementNameToken(context, parameterDeclaration.Identifier);
226+
}
227+
206228
private static void CheckElementNameToken(SyntaxNodeAnalysisContext context, SyntaxToken identifier, bool allowUnderscoreDigit = false)
207229
{
208230
if (identifier.IsMissing)

StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace StyleCop.Analyzers.NamingRules
1010
using Microsoft.CodeAnalysis.CSharp.Syntax;
1111
using Microsoft.CodeAnalysis.Diagnostics;
1212
using StyleCop.Analyzers.Helpers;
13+
using StyleCop.Analyzers.Lightup;
1314

1415
/// <summary>
1516
/// The name of a parameter in C# does not begin with a lower-case letter.
@@ -64,6 +65,12 @@ private static void HandleParameter(SyntaxNodeAnalysisContext context)
6465
return;
6566
}
6667

68+
if (syntax.Parent.Parent.IsKind(SyntaxKindEx.RecordDeclaration))
69+
{
70+
// Positional parameters of a record are treated as properties for naming conventions
71+
return;
72+
}
73+
6774
var identifier = syntax.Identifier;
6875
if (identifier.IsMissing)
6976
{

0 commit comments

Comments
 (0)