Skip to content

Commit 20b8fca

Browse files
committed
Implement support for local functions in SA1111
1 parent 72bf24a commit 20b8fca

2 files changed

Lines changed: 137 additions & 2 deletions

File tree

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

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,121 @@
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 SA1111CSharp7UnitTests : SA1111UnitTests
913
{
14+
[Fact]
15+
public async Task TestLocalFunctionDeclarationWithOneParameterClosingParenthesisOnTheNextLineAsTheLastParameterAsync()
16+
{
17+
var testCode = @"
18+
class Foo
19+
{
20+
public void Method()
21+
{
22+
void Bar(string s
23+
)
24+
{
25+
26+
}
27+
}
28+
}";
29+
var fixedCode = @"
30+
class Foo
31+
{
32+
public void Method()
33+
{
34+
void Bar(string s)
35+
{
36+
37+
}
38+
}
39+
}";
40+
41+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 1);
42+
43+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
44+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
45+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
46+
}
47+
48+
[Fact]
49+
public async Task TestLocalFunctionDeclarationWithThreeParameterClosingParenthesisOnTheNextLineAsTheLastParameterAsync()
50+
{
51+
var testCode = @"
52+
class Foo
53+
{
54+
public void Method()
55+
{
56+
void Bar(string s,
57+
int i,
58+
object o
59+
)
60+
{
61+
62+
}
63+
}
64+
}";
65+
var fixedCode = @"
66+
class Foo
67+
{
68+
public void Method()
69+
{
70+
void Bar(string s,
71+
int i,
72+
object o)
73+
{
74+
75+
}
76+
}
77+
}";
78+
79+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(9, 1);
80+
81+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
82+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
83+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
84+
}
85+
86+
[Fact]
87+
public async Task TestLocalFunctionDeclarationWithNoParameterClosingParenthesisOnTheNextLineAsTheLastParameterAsync()
88+
{
89+
var testCode = @"
90+
class Foo
91+
{
92+
public void Method()
93+
{
94+
void Bar(
95+
)
96+
{
97+
98+
}
99+
}
100+
}";
101+
102+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
103+
}
104+
105+
[Fact]
106+
public async Task TestLocalFunctionDeclarationWithOneParameterClosingParenthesisOnTheSameLineAsTheLastParameterAsync()
107+
{
108+
var testCode = @"
109+
class Foo
110+
{
111+
public void Method()
112+
{
113+
void Bar(string s)
114+
{
115+
116+
}
117+
}
118+
}";
119+
120+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
121+
}
10122
}
11123
}

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1111ClosingParenthesisMustBeOnLineOfLastParameter.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
namespace StyleCop.Analyzers.ReadabilityRules
55
{
66
using System;
7-
using System.Collections.Generic;
87
using System.Collections.Immutable;
98
using Microsoft.CodeAnalysis;
109
using Microsoft.CodeAnalysis.CSharp;
1110
using Microsoft.CodeAnalysis.CSharp.Syntax;
1211
using Microsoft.CodeAnalysis.Diagnostics;
13-
using SpacingRules;
1412
using StyleCop.Analyzers.Helpers;
13+
using StyleCop.Analyzers.Lightup;
14+
using StyleCop.Analyzers.SpacingRules;
1515

1616
/// <summary>
1717
/// The closing parenthesis or bracket in a call to a C# method or indexer, or the declaration of a method or
@@ -59,6 +59,7 @@ internal class SA1111ClosingParenthesisMustBeOnLineOfLastParameter : DiagnosticA
5959
SyntaxKind.ConversionOperatorDeclaration);
6060

6161
private static readonly Action<SyntaxNodeAnalysisContext> BaseMethodDeclarationAction = HandleBaseMethodDeclaration;
62+
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
6263
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
6364
private static readonly Action<SyntaxNodeAnalysisContext> ObjectCreationExpressionAction = HandleObjectCreationExpression;
6465
private static readonly Action<SyntaxNodeAnalysisContext> IndexerDeclarationAction = HandleIndexerDeclaration;
@@ -80,6 +81,7 @@ public override void Initialize(AnalysisContext context)
8081
context.EnableConcurrentExecution();
8182

8283
context.RegisterSyntaxNodeAction(BaseMethodDeclarationAction, HandledMethodSyntaxKinds);
84+
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
8385
context.RegisterSyntaxNodeAction(InvocationExpressionAction, SyntaxKind.InvocationExpression);
8486
context.RegisterSyntaxNodeAction(ObjectCreationExpressionAction, SyntaxKind.ObjectCreationExpression);
8587
context.RegisterSyntaxNodeAction(IndexerDeclarationAction, SyntaxKind.IndexerDeclaration);
@@ -332,6 +334,27 @@ private static void HandleBaseMethodDeclaration(SyntaxNodeAnalysisContext contex
332334
}
333335
}
334336

337+
private static void HandleLocalFunctionStatement(SyntaxNodeAnalysisContext context)
338+
{
339+
var localFunctionStatementSyntax = (LocalFunctionStatementSyntaxWrapper)context.Node;
340+
341+
if (localFunctionStatementSyntax.ParameterList == null ||
342+
localFunctionStatementSyntax.ParameterList.IsMissing ||
343+
!localFunctionStatementSyntax.ParameterList.Parameters.Any())
344+
{
345+
return;
346+
}
347+
348+
var lastParameter = localFunctionStatementSyntax.ParameterList
349+
.Parameters
350+
.Last();
351+
352+
if (!localFunctionStatementSyntax.ParameterList.CloseParenToken.IsMissing)
353+
{
354+
CheckIfLocationOfLastArgumentOrParameterAndCloseTokenAreTheSame(context, lastParameter, localFunctionStatementSyntax.ParameterList.CloseParenToken);
355+
}
356+
}
357+
335358
private static void CheckIfLocationOfLastArgumentOrParameterAndCloseTokenAreTheSame(
336359
SyntaxNodeAnalysisContext context,
337360
CSharpSyntaxNode parameterOrArgument,

0 commit comments

Comments
 (0)