From d932ea1c9387c25377a70adfdb64f238145bb98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Hellander?= Date: Sat, 1 Apr 2023 08:03:44 +0200 Subject: [PATCH] Update SA1015 to require trailing space after an explicit generic return type in a lambda expression #3624 --- .../SpacingRules/SA1015CSharp10UnitTests.cs | 33 +++++++++++++++++++ ...ingGenericBracketsMustBeSpacedCorrectly.cs | 33 ++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/SpacingRules/SA1015CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/SpacingRules/SA1015CSharp10UnitTests.cs index ab1eea360..8a9a87fd8 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/SpacingRules/SA1015CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/SpacingRules/SA1015CSharp10UnitTests.cs @@ -3,9 +3,42 @@ namespace StyleCop.Analyzers.Test.CSharp10.SpacingRules { + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.Test.CSharp9.SpacingRules; + using Xunit; + using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< + StyleCop.Analyzers.SpacingRules.SA1015ClosingGenericBracketsMustBeSpacedCorrectly, + StyleCop.Analyzers.SpacingRules.TokenSpacingCodeFixProvider>; public class SA1015CSharp10UnitTests : SA1015CSharp9UnitTests { + [Fact] + [WorkItem(3624, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3624")] + public async Task TestLambdaWithExplicitGenericReturnTypeAsync() + { + const string testCode = @"using System.Threading.Tasks; + +public class TestClass +{ + public void TestMethod() + { + var _ = Task|](int x) => Task.FromResult(x); + } +}"; + + const string fixedCode = @"using System.Threading.Tasks; + +public class TestClass +{ + public void TestMethod() + { + var _ = Task (int x) => Task.FromResult(x); + } +}"; + + await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedCode, CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1015ClosingGenericBracketsMustBeSpacedCorrectly.cs b/StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1015ClosingGenericBracketsMustBeSpacedCorrectly.cs index 2fd8aa85b..7b5642f12 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1015ClosingGenericBracketsMustBeSpacedCorrectly.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1015ClosingGenericBracketsMustBeSpacedCorrectly.cs @@ -108,7 +108,6 @@ private static void HandleGreaterThanToken(SyntaxTreeAnalysisContext context, Sy SyntaxToken nextToken = token.GetNextToken(); switch (nextToken.Kind()) { - case SyntaxKind.OpenParenToken: // DotToken isn't listed above, but it's required for reasonable member access formatting case SyntaxKind.DotToken: // 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 allowTrailingSpace = false; break; + case SyntaxKind.OpenParenToken: + AnalyzeWithTrailingOpenParen(nextToken, out allowTrailingNoSpace, out allowTrailingSpace); + break; + case SyntaxKind.CloseParenToken: case SyntaxKind.GreaterThanToken: allowTrailingNoSpace = true; @@ -187,5 +190,33 @@ private static void HandleGreaterThanToken(SyntaxTreeAnalysisContext context, Sy } } } + + private static void AnalyzeWithTrailingOpenParen( + SyntaxToken nextToken, + out bool allowTrailingNoSpace, + out bool allowTrailingSpace) + { + switch (nextToken.Parent.Kind()) + { + // List (int x) => new List { x } + // ^ ^ + case SyntaxKind.ParameterList when nextToken.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression): + allowTrailingNoSpace = false; + allowTrailingSpace = true; + break; + + // NOTE: Intentionally keeping redundant cases here as documentation of what is known to occur + // M() + // ^^ + case SyntaxKind.ArgumentList: + // void M(T x) { } + // ^^ + case SyntaxKind.ParameterList when nextToken.Parent.Parent.IsKind(SyntaxKind.MethodDeclaration): + default: + allowTrailingNoSpace = true; + allowTrailingSpace = false; + break; + } + } } }