Skip to content

Commit a6f96b6

Browse files
committed
Implement support for local functions in SA1113, SA1114, SA1115
1 parent 352a301 commit a6f96b6

6 files changed

Lines changed: 266 additions & 1 deletion

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp7/ReadabilityRules/SA1113CSharp7UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp7.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
68
using StyleCop.Analyzers.Test.ReadabilityRules;
9+
using TestHelper;
10+
using Xunit;
711

812
public class SA1113CSharp7UnitTests : SA1113UnitTests
913
{
14+
[Fact]
15+
public async Task TestLocalFunctionDeclarationWithTwoParametersCommaPlacedAtTheSameLineAsTheSecondParameterAsync()
16+
{
17+
var testCode = @"public class Foo
18+
{
19+
public void Method()
20+
{
21+
void Bar(string s
22+
, int i)
23+
{
24+
}
25+
}
26+
}";
27+
var fixedCode = @"public class Foo
28+
{
29+
public void Method()
30+
{
31+
void Bar(string s,
32+
int i)
33+
{
34+
}
35+
}
36+
}";
37+
38+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(6, 18);
39+
40+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
41+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
42+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
43+
}
44+
45+
[Fact]
46+
public async Task TestLocalFunctionDeclarationWithThreeParametersCommasPlacedAtTheSameLineAsTheNextParameterAsync()
47+
{
48+
var testCode = @"public class Foo
49+
{
50+
public void Method()
51+
{
52+
void Bar(string s
53+
, int i
54+
, int i2)
55+
{
56+
}
57+
}
58+
}";
59+
var fixedCode = @"public class Foo
60+
{
61+
public void Method()
62+
{
63+
void Bar(string s,
64+
int i,
65+
int i2)
66+
{
67+
}
68+
}
69+
}";
70+
71+
DiagnosticResult[] expected =
72+
{
73+
this.CSharpDiagnostic().WithLocation(6, 18),
74+
this.CSharpDiagnostic().WithLocation(7, 18),
75+
};
76+
77+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
78+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
79+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
80+
}
81+
82+
[Fact]
83+
public async Task TestLocalFunctionDeclarationWithTwoParametersCommaPlacedAtTheSameLineAsTheFirstParameterAsync()
84+
{
85+
var testCode = @"public class Foo
86+
{
87+
public void Method()
88+
{
89+
void Bar(string s,
90+
int i)
91+
{
92+
}
93+
}
94+
}";
95+
96+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
97+
}
1098
}
1199
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp7/ReadabilityRules/SA1114CSharp7UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp7.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
68
using StyleCop.Analyzers.Test.ReadabilityRules;
9+
using TestHelper;
10+
using Xunit;
711

812
public class SA1114CSharp7UnitTests : SA1114UnitTests
913
{
14+
[Fact]
15+
public async Task TestLocalFunctionDeclarationParametersList2LinesAfterOpeningParenthesisAsync()
16+
{
17+
var testCode = @"
18+
class Foo
19+
{
20+
public void Method()
21+
{
22+
void Bar(
23+
24+
string s)
25+
{
26+
27+
}
28+
}
29+
}";
30+
31+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(8, 1);
32+
33+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
34+
}
35+
36+
[Fact]
37+
public async Task TestLocalFunctionDeclarationParametersListOnNextLineAsOpeningParenthesisAsync()
38+
{
39+
var testCode = @"
40+
class Foo
41+
{
42+
public void Method()
43+
{
44+
void Bar(
45+
string s)
46+
{
47+
48+
}
49+
}
50+
}";
51+
52+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
53+
}
54+
55+
[Fact]
56+
public async Task TestLocalFunctionDeclarationParametersListOnSameLineAsOpeningParenthesisAsync()
57+
{
58+
var testCode = @"
59+
class Foo
60+
{
61+
public void Method()
62+
{
63+
void Bar(string s)
64+
{
65+
66+
}
67+
}
68+
}";
69+
70+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
71+
}
72+
73+
[Fact]
74+
public async Task TestLocalFunctionDeclarationNoParametersAsync()
75+
{
76+
var testCode = @"
77+
class Foo
78+
{
79+
public void Method()
80+
{
81+
void Bar(
82+
83+
)
84+
{
85+
86+
}
87+
}
88+
}";
89+
90+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
91+
}
1092
}
1193
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp7/ReadabilityRules/SA1115CSharp7UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp7.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
68
using StyleCop.Analyzers.Test.ReadabilityRules;
9+
using TestHelper;
10+
using Xunit;
711

812
public class SA1115CSharp7UnitTests : SA1115UnitTests
913
{
14+
[Fact]
15+
public async Task TestLocalFunctionDeclarationEmptyLinesBetweenParametersAsync()
16+
{
17+
var testCode = @"
18+
class Foo
19+
{
20+
public void Method()
21+
{
22+
void Bar(int i, int z,
23+
24+
string s,
25+
26+
int j,
27+
int k)
28+
{
29+
}
30+
}
31+
}";
32+
33+
DiagnosticResult expected1 = this.CSharpDiagnostic().WithLocation(8, 1);
34+
DiagnosticResult expected2 = this.CSharpDiagnostic().WithLocation(10, 1);
35+
36+
await this.VerifyCSharpDiagnosticAsync(testCode, new[] { expected1, expected2 }, CancellationToken.None).ConfigureAwait(false);
37+
}
38+
39+
[Fact]
40+
public async Task TestLocalFunctionDeclarationSecondParameterOnTheNextLineAsync()
41+
{
42+
var testCode = @"
43+
class Foo
44+
{
45+
public void Method()
46+
{
47+
void Bar(int i,
48+
string s)
49+
{
50+
}
51+
}
52+
}";
53+
54+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
55+
}
56+
57+
[Fact]
58+
public async Task TestLocalFunctionDeclarationParametersAtTheSameLineAsync()
59+
{
60+
var testCode = @"
61+
class Foo
62+
{
63+
public void Method()
64+
{
65+
void Bar(int i, string s)
66+
{
67+
}
68+
}
69+
}";
70+
71+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
72+
}
1073
}
1174
}

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1113CommaMustBeOnSameLineAsPreviousParameter.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ namespace StyleCop.Analyzers.ReadabilityRules
99
using Microsoft.CodeAnalysis.CSharp;
1010
using Microsoft.CodeAnalysis.CSharp.Syntax;
1111
using Microsoft.CodeAnalysis.Diagnostics;
12-
using SpacingRules;
1312
using StyleCop.Analyzers.Helpers;
13+
using StyleCop.Analyzers.Lightup;
14+
using StyleCop.Analyzers.SpacingRules;
1415

1516
/// <summary>
1617
/// A comma between two parameters in a call to a C# method or indexer, or in the declaration of a method or
@@ -57,6 +58,7 @@ internal class SA1113CommaMustBeOnSameLineAsPreviousParameter : DiagnosticAnalyz
5758
SyntaxKind.OperatorDeclaration);
5859

5960
private static readonly Action<SyntaxNodeAnalysisContext> BaseMethodDeclarationAction = HandleBaseMethodDeclaration;
61+
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
6062
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
6163
private static readonly Action<SyntaxNodeAnalysisContext> ObjectCreationExpressionAction = HandleObjectCreationExpression;
6264
private static readonly Action<SyntaxNodeAnalysisContext> IndexerDeclarationAction = HandleIndexerDeclaration;
@@ -80,6 +82,7 @@ public override void Initialize(AnalysisContext context)
8082
context.EnableConcurrentExecution();
8183

8284
context.RegisterSyntaxNodeAction(BaseMethodDeclarationAction, BaseMethodDeclarationKinds);
85+
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
8386
context.RegisterSyntaxNodeAction(InvocationExpressionAction, SyntaxKind.InvocationExpression);
8487
context.RegisterSyntaxNodeAction(ObjectCreationExpressionAction, SyntaxKind.ObjectCreationExpression);
8588
context.RegisterSyntaxNodeAction(IndexerDeclarationAction, SyntaxKind.IndexerDeclaration);
@@ -194,6 +197,12 @@ private static void HandleBaseMethodDeclaration(SyntaxNodeAnalysisContext contex
194197
HandleBaseParameterListSyntax(context, baseMethodDeclaration.ParameterList);
195198
}
196199

200+
private static void HandleLocalFunctionStatement(SyntaxNodeAnalysisContext context)
201+
{
202+
var localFunctionStatement = (LocalFunctionStatementSyntaxWrapper)context.Node;
203+
HandleBaseParameterListSyntax(context, localFunctionStatement.ParameterList);
204+
}
205+
197206
private static void HandleConstructorInitializer(SyntaxNodeAnalysisContext context)
198207
{
199208
var constructorInitializer = (ConstructorInitializerSyntax)context.Node;

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1114ParameterListMustFollowDeclaration.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace StyleCop.Analyzers.ReadabilityRules
1111
using Microsoft.CodeAnalysis.CSharp.Syntax;
1212
using Microsoft.CodeAnalysis.Diagnostics;
1313
using StyleCop.Analyzers.Helpers;
14+
using StyleCop.Analyzers.Lightup;
1415

1516
/// <summary>
1617
/// The start of the parameter list for a method or indexer call or declaration does not begin on the same line as
@@ -58,6 +59,7 @@ internal class SA1114ParameterListMustFollowDeclaration : DiagnosticAnalyzer
5859
ImmutableArray.Create(SyntaxKind.MethodDeclaration, SyntaxKind.ConstructorDeclaration, SyntaxKind.OperatorDeclaration, SyntaxKind.ConversionOperatorDeclaration);
5960

6061
private static readonly Action<SyntaxNodeAnalysisContext> BaseMethodDeclarationAction = HandleBaseMethodDeclaration;
62+
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
6163
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
6264
private static readonly Action<SyntaxNodeAnalysisContext> ObjectCreationExpressionAction = HandleObjectCreationExpression;
6365
private static readonly Action<SyntaxNodeAnalysisContext> IndexerDeclarationAction = HandleIndexerDeclaration;
@@ -80,6 +82,7 @@ public override void Initialize(AnalysisContext context)
8082
context.EnableConcurrentExecution();
8183

8284
context.RegisterSyntaxNodeAction(BaseMethodDeclarationAction, BaseMethodDeclarationKinds);
85+
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
8386
context.RegisterSyntaxNodeAction(InvocationExpressionAction, SyntaxKind.InvocationExpression);
8487
context.RegisterSyntaxNodeAction(ObjectCreationExpressionAction, SyntaxKind.ObjectCreationExpression);
8588
context.RegisterSyntaxNodeAction(IndexerDeclarationAction, SyntaxKind.IndexerDeclaration);
@@ -168,6 +171,13 @@ private static void HandleBaseMethodDeclaration(SyntaxNodeAnalysisContext contex
168171
AnalyzeParametersList(context, methodDeclaration.ParameterList);
169172
}
170173

174+
private static void HandleLocalFunctionStatement(SyntaxNodeAnalysisContext context)
175+
{
176+
var localFunctionStatement = (LocalFunctionStatementSyntaxWrapper)context.Node;
177+
178+
AnalyzeParametersList(context, localFunctionStatement.ParameterList);
179+
}
180+
171181
private static void AnalyzeRankSpecifiers(SyntaxNodeAnalysisContext context, ArrayCreationExpressionSyntax arrayCreation)
172182
{
173183
if (!arrayCreation.Type.RankSpecifiers.Any())

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1115ParameterMustFollowComma.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace StyleCop.Analyzers.ReadabilityRules
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
/// A parameter within a C# method or indexer call or declaration does not begin on the same line as the previous
@@ -58,6 +59,7 @@ internal class SA1115ParameterMustFollowComma : DiagnosticAnalyzer
5859
ImmutableArray.Create(SyntaxKind.ConstructorDeclaration, SyntaxKind.MethodDeclaration, SyntaxKind.OperatorDeclaration);
5960

6061
private static readonly Action<SyntaxNodeAnalysisContext> BaseMethodDeclarationAction = HandleBaseMethodDeclaration;
62+
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
6163
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
6264
private static readonly Action<SyntaxNodeAnalysisContext> ObjectCreationExpressionAction = HandleObjectCreationExpression;
6365
private static readonly Action<SyntaxNodeAnalysisContext> IndexerDeclarationAction = HandleIndexerDeclaration;
@@ -83,6 +85,7 @@ public override void Initialize(AnalysisContext context)
8385
context.EnableConcurrentExecution();
8486

8587
context.RegisterSyntaxNodeAction(BaseMethodDeclarationAction, BaseMethodDeclarationKinds);
88+
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
8689
context.RegisterSyntaxNodeAction(InvocationExpressionAction, SyntaxKind.InvocationExpression);
8790
context.RegisterSyntaxNodeAction(ObjectCreationExpressionAction, SyntaxKind.ObjectCreationExpression);
8891
context.RegisterSyntaxNodeAction(IndexerDeclarationAction, SyntaxKind.IndexerDeclaration);
@@ -214,6 +217,16 @@ private static void HandleBaseMethodDeclaration(SyntaxNodeAnalysisContext contex
214217
}
215218
}
216219

220+
private static void HandleLocalFunctionStatement(SyntaxNodeAnalysisContext context)
221+
{
222+
var localFunctionStatement = (LocalFunctionStatementSyntaxWrapper)context.Node;
223+
224+
if (localFunctionStatement.ParameterList != null)
225+
{
226+
AnalyzeSyntaxList(context, localFunctionStatement.ParameterList.Parameters);
227+
}
228+
}
229+
217230
private static void AnalyzeSyntaxList<TNode>(SyntaxNodeAnalysisContext context, SeparatedSyntaxList<TNode> syntaxList)
218231
where TNode : SyntaxNode
219232
{

0 commit comments

Comments
 (0)