Skip to content

Commit ef1f91c

Browse files
authored
Merge pull request #3625 from bjornhellander/feature/sa1015-explicit-generic-lambda-return-type
Update SA1015 to require trailing space after an explicit generic return type in a lambda expression
2 parents 84e2324 + d932ea1 commit ef1f91c

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/SpacingRules/SA1015CSharp10UnitTests.cs

+33
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,42 @@
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.Test.Verifiers.StyleCopCodeFixVerifier<
12+
StyleCop.Analyzers.SpacingRules.SA1015ClosingGenericBracketsMustBeSpacedCorrectly,
13+
StyleCop.Analyzers.SpacingRules.TokenSpacingCodeFixProvider>;
714

815
public class SA1015CSharp10UnitTests : SA1015CSharp9UnitTests
916
{
17+
[Fact]
18+
[WorkItem(3624, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3624")]
19+
public async Task TestLambdaWithExplicitGenericReturnTypeAsync()
20+
{
21+
const string testCode = @"using System.Threading.Tasks;
22+
23+
public class TestClass
24+
{
25+
public void TestMethod()
26+
{
27+
var _ = Task<int[|>|](int x) => Task.FromResult(x);
28+
}
29+
}";
30+
31+
const string fixedCode = @"using System.Threading.Tasks;
32+
33+
public class TestClass
34+
{
35+
public void TestMethod()
36+
{
37+
var _ = Task<int> (int x) => Task.FromResult(x);
38+
}
39+
}";
40+
41+
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedCode, CancellationToken.None).ConfigureAwait(false);
42+
}
1043
}
1144
}

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1015ClosingGenericBracketsMustBeSpacedCorrectly.cs

+32-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ private static void HandleGreaterThanToken(SyntaxTreeAnalysisContext context, Sy
108108
SyntaxToken nextToken = token.GetNextToken();
109109
switch (nextToken.Kind())
110110
{
111-
case SyntaxKind.OpenParenToken:
112111
// DotToken isn't listed above, but it's required for reasonable member access formatting
113112
case SyntaxKind.DotToken:
114113
// CommaToken isn't listed above, but it's required for reasonable nested generic type arguments formatting
@@ -122,6 +121,10 @@ private static void HandleGreaterThanToken(SyntaxTreeAnalysisContext context, Sy
122121
allowTrailingSpace = false;
123122
break;
124123

124+
case SyntaxKind.OpenParenToken:
125+
AnalyzeWithTrailingOpenParen(nextToken, out allowTrailingNoSpace, out allowTrailingSpace);
126+
break;
127+
125128
case SyntaxKind.CloseParenToken:
126129
case SyntaxKind.GreaterThanToken:
127130
allowTrailingNoSpace = true;
@@ -187,5 +190,33 @@ private static void HandleGreaterThanToken(SyntaxTreeAnalysisContext context, Sy
187190
}
188191
}
189192
}
193+
194+
private static void AnalyzeWithTrailingOpenParen(
195+
SyntaxToken nextToken,
196+
out bool allowTrailingNoSpace,
197+
out bool allowTrailingSpace)
198+
{
199+
switch (nextToken.Parent.Kind())
200+
{
201+
// List<int> (int x) => new List<int> { x }
202+
// ^ ^
203+
case SyntaxKind.ParameterList when nextToken.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression):
204+
allowTrailingNoSpace = false;
205+
allowTrailingSpace = true;
206+
break;
207+
208+
// NOTE: Intentionally keeping redundant cases here as documentation of what is known to occur
209+
// M<int>()
210+
// ^^
211+
case SyntaxKind.ArgumentList:
212+
// void M<T>(T x) { }
213+
// ^^
214+
case SyntaxKind.ParameterList when nextToken.Parent.Parent.IsKind(SyntaxKind.MethodDeclaration):
215+
default:
216+
allowTrailingNoSpace = true;
217+
allowTrailingSpace = false;
218+
break;
219+
}
220+
}
190221
}
191222
}

0 commit comments

Comments
 (0)