Skip to content

Commit 72bf24a

Browse files
committed
Implement support for local functions in SA1110
1 parent 036e160 commit 72bf24a

2 files changed

Lines changed: 72 additions & 1 deletion

File tree

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,65 @@
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 SA1110CSharp7UnitTests : SA1110UnitTests
913
{
14+
[Fact]
15+
public async Task TestLocalFunctionDeclarationOpeningParenthesisInTheNextLineAsync()
16+
{
17+
var testCode = @"
18+
class Foo
19+
{
20+
public void Method()
21+
{
22+
void Bar
23+
()
24+
{
25+
26+
}
27+
}
28+
}";
29+
var fixedCode = @"
30+
class Foo
31+
{
32+
public void Method()
33+
{
34+
void Bar()
35+
{
36+
37+
}
38+
}
39+
}";
40+
41+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 25);
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 TestLocalFunctionDeclarationOpeningParenthesisInTheSameLineAsync()
50+
{
51+
var testCode = @"
52+
class Foo
53+
{
54+
public void Method()
55+
{
56+
void
57+
Bar()
58+
{
59+
60+
}
61+
}
62+
}";
63+
64+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
65+
}
1066
}
1167
}

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1110OpeningParenthesisMustBeOnDeclarationLine.cs

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

1617
/// <summary>
1718
/// The opening parenthesis or brace in a call to a C# method or indexer, or the declaration of a method or
@@ -51,6 +52,7 @@ internal class SA1110OpeningParenthesisMustBeOnDeclarationLine : DiagnosticAnaly
5152
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.ReadabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
5253

5354
private static readonly Action<SyntaxNodeAnalysisContext> MethodDeclarationAction = HandleMethodDeclaration;
55+
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
5456
private static readonly Action<SyntaxNodeAnalysisContext> ConstructorDeclarationAction = HandleConstructorDeclaration;
5557
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
5658
private static readonly Action<SyntaxNodeAnalysisContext> ObjectCreationExpressionAction = HandleObjectCreationExpression;
@@ -74,6 +76,7 @@ public override void Initialize(AnalysisContext context)
7476
context.EnableConcurrentExecution();
7577

7678
context.RegisterSyntaxNodeAction(MethodDeclarationAction, SyntaxKind.MethodDeclaration);
79+
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
7780
context.RegisterSyntaxNodeAction(ConstructorDeclarationAction, SyntaxKind.ConstructorDeclaration);
7881
context.RegisterSyntaxNodeAction(InvocationExpressionAction, SyntaxKind.InvocationExpression);
7982
context.RegisterSyntaxNodeAction(ObjectCreationExpressionAction, SyntaxKind.ObjectCreationExpression);
@@ -313,6 +316,18 @@ private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
313316
}
314317
}
315318

319+
private static void HandleLocalFunctionStatement(SyntaxNodeAnalysisContext context)
320+
{
321+
var localFunctionStatement = (LocalFunctionStatementSyntaxWrapper)context.Node;
322+
if (localFunctionStatement.ParameterList != null
323+
&& !localFunctionStatement.ParameterList.OpenParenToken.IsMissing
324+
&& !localFunctionStatement.Identifier.IsMissing)
325+
{
326+
bool preserveLayout = localFunctionStatement.ParameterList.Parameters.Any();
327+
CheckIfLocationOfPreviousTokenAndOpenTokenAreTheSame(context, localFunctionStatement.ParameterList.OpenParenToken, preserveLayout);
328+
}
329+
}
330+
316331
private static void CheckIfLocationOfPreviousTokenAndOpenTokenAreTheSame(SyntaxNodeAnalysisContext context, SyntaxToken openToken, bool preserveLayout)
317332
{
318333
var previousToken = openToken.GetPreviousToken();

0 commit comments

Comments
 (0)