Skip to content

Commit b9427d9

Browse files
committed
Update SA1000 to handle out, ref, and var keywords
1 parent b06b16e commit b9427d9

4 files changed

Lines changed: 118 additions & 7 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp7/SpacingRules/SA1000CSharp7UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp7.SpacingRules
55
{
6+
using System;
7+
using System.Linq;
8+
using System.Reflection;
9+
using System.Threading.Tasks;
10+
using Microsoft.CodeAnalysis;
611
using StyleCop.Analyzers.Test.SpacingRules;
12+
using TestHelper;
13+
using Xunit;
714

815
public class SA1000CSharp7UnitTests : SA1000UnitTests
916
{
17+
[Fact]
18+
public async Task TestOutVariableDeclarationAsync()
19+
{
20+
string statementWithoutSpace = @"int.TryParse(""0"", out@Int32 x);";
21+
22+
string statementWithSpace = @"int.TryParse(""0"", out @Int32 x);";
23+
24+
await this.TestKeywordStatementAsync(statementWithSpace, EmptyDiagnosticResults, statementWithSpace).ConfigureAwait(false);
25+
26+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("out", string.Empty, "followed").WithLocation(12, 31);
27+
28+
await this.TestKeywordStatementAsync(statementWithoutSpace, expected, statementWithSpace).ConfigureAwait(false);
29+
}
30+
31+
[Fact]
32+
public async Task TestVarKeywordTupleTypeAsync()
33+
{
34+
string statementWithoutSpace = @"var(a, b) = (2, 3);";
35+
36+
string statementWithSpace = @"var (a, b) = (2, 3);";
37+
38+
await this.TestKeywordStatementAsync(statementWithSpace, EmptyDiagnosticResults, statementWithSpace).ConfigureAwait(false);
39+
40+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("var", string.Empty, "followed").WithLocation(12, 13);
41+
42+
await this.TestKeywordStatementAsync(statementWithoutSpace, expected, statementWithSpace).ConfigureAwait(false);
43+
}
44+
45+
[Fact]
46+
public async Task TestRefExpressionAndTypeAsync()
47+
{
48+
string statementWithoutSpace = @"
49+
int a = 0;
50+
ref@Int32 b = ref@Call(ref@a);
51+
52+
ref@Int32 Call(ref@Int32 p)
53+
=> ref@p;
54+
";
55+
56+
string statementWithSpace = @"
57+
int a = 0;
58+
ref @Int32 b = ref @Call(ref @a);
59+
60+
ref @Int32 Call(ref @Int32 p)
61+
=> ref @p;
62+
";
63+
64+
await this.TestKeywordStatementAsync(statementWithSpace, EmptyDiagnosticResults, statementWithSpace).ConfigureAwait(false);
65+
66+
DiagnosticResult[] expected =
67+
{
68+
this.CSharpDiagnostic().WithArguments("ref", string.Empty, "followed").WithLocation(14, 1),
69+
this.CSharpDiagnostic().WithArguments("ref", string.Empty, "followed").WithLocation(14, 15),
70+
this.CSharpDiagnostic().WithArguments("ref", string.Empty, "followed").WithLocation(14, 24),
71+
this.CSharpDiagnostic().WithArguments("ref", string.Empty, "followed").WithLocation(16, 1),
72+
this.CSharpDiagnostic().WithArguments("ref", string.Empty, "followed").WithLocation(16, 16),
73+
this.CSharpDiagnostic().WithArguments("ref", string.Empty, "followed").WithLocation(17, 8),
74+
};
75+
76+
await this.TestKeywordStatementAsync(statementWithoutSpace, expected, statementWithSpace).ConfigureAwait(false);
77+
}
78+
79+
protected override Solution CreateSolution(ProjectId projectId, string language)
80+
{
81+
Solution solution = base.CreateSolution(projectId, language);
82+
Assembly systemRuntime = AppDomain.CurrentDomain.GetAssemblies().Single(x => x.GetName().Name == "System.Runtime");
83+
return solution
84+
.AddMetadataReference(projectId, MetadataReference.CreateFromFile(systemRuntime.Location))
85+
.AddMetadataReference(projectId, MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location));
86+
}
1087
}
1188
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/SpacingRules/SA1000UnitTests.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,20 @@ public async Task TestGotoCaseStatementAsync()
837837
await this.TestKeywordStatementAsync(statementWithoutSpace, expected, statementWithSpace).ConfigureAwait(false);
838838
}
839839

840+
[Fact]
841+
public async Task TestVarKeywordAsync()
842+
{
843+
string statementWithoutSpace = @"var@x = ""test"";";
844+
845+
string statementWithSpace = @"var @x = ""test"";";
846+
847+
await this.TestKeywordStatementAsync(statementWithSpace, EmptyDiagnosticResults, statementWithSpace).ConfigureAwait(false);
848+
849+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("var", string.Empty, "followed").WithLocation(12, 13);
850+
851+
await this.TestKeywordStatementAsync(statementWithoutSpace, expected, statementWithSpace).ConfigureAwait(false);
852+
}
853+
840854
[Fact]
841855
public async Task TestMissingSelectTokenAsync()
842856
{
@@ -900,12 +914,12 @@ protected override CodeFixProvider GetCSharpCodeFixProvider()
900914
return new TokenSpacingCodeFixProvider();
901915
}
902916

903-
private Task TestKeywordStatementAsync(string statement, DiagnosticResult expected, string fixedStatement, string returnType = "void", bool asyncMethod = false)
917+
protected Task TestKeywordStatementAsync(string statement, DiagnosticResult expected, string fixedStatement, string returnType = "void", bool asyncMethod = false)
904918
{
905919
return this.TestKeywordStatementAsync(statement, new[] { expected }, fixedStatement, returnType, asyncMethod);
906920
}
907921

908-
private async Task TestKeywordStatementAsync(string statement, DiagnosticResult[] expected, string fixedStatement, string returnType = "void", bool asyncMethod = false)
922+
protected async Task TestKeywordStatementAsync(string statement, DiagnosticResult[] expected, string fixedStatement, string returnType = "void", bool asyncMethod = false)
909923
{
910924
string testCodeFormat = @"
911925
using System;

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1000KeywordsMustBeSpacedCorrectly.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ namespace StyleCop.Analyzers.SpacingRules
1919
/// <para>The following C# keywords must always be followed by a single space: <strong>catch</strong>,
2020
/// <strong>fixed</strong>, <strong>for</strong>, <strong>foreach</strong>, <strong>from</strong>,
2121
/// <strong>group</strong>, <strong>if</strong>, <strong>in</strong>, <strong>into</strong>, <strong>join</strong>,
22-
/// <strong>let</strong>, <strong>lock</strong>, <strong>orderby</strong>, <strong>return</strong>,
23-
/// <strong>select</strong>, <strong>stackalloc</strong>, <strong>switch</strong>, <strong>throw</strong>,
24-
/// <strong>using</strong>, <strong>where</strong>, <strong>while</strong>, <strong>yield</strong>.</para>
22+
/// <strong>let</strong>, <strong>lock</strong>, <strong>orderby</strong>, <strong>out</strong>,
23+
/// <strong>ref</strong>, <strong>return</strong>, <strong>select</strong>, <strong>stackalloc</strong>,
24+
/// <strong>switch</strong>, <strong>throw</strong>, <strong>using</strong>, <strong>var</strong>,
25+
/// <strong>where</strong>, <strong>while</strong>, <strong>yield</strong>.</para>
2526
///
2627
/// <para>The following keywords must not be followed by any space: <strong>checked</strong>,
2728
/// <strong>default</strong>, <strong>sizeof</strong>, <strong>typeof</strong>, <strong>unchecked</strong>.</para>
@@ -46,6 +47,7 @@ internal class SA1000KeywordsMustBeSpacedCorrectly : DiagnosticAnalyzer
4647

4748
private static readonly Action<SyntaxTreeAnalysisContext> SyntaxTreeAction = HandleSyntaxTree;
4849
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
50+
private static readonly Action<SyntaxNodeAnalysisContext> IdentifierNameAction = HandleIdentifierName;
4951
private static readonly ReportDiagnosticCallback<SyntaxTreeAnalysisContext> ReportSyntaxTreeDiagnostic =
5052
(ref SyntaxTreeAnalysisContext context, Diagnostic diagnostic) => context.ReportDiagnostic(diagnostic);
5153

@@ -69,6 +71,9 @@ public override void Initialize(AnalysisContext context)
6971

7072
// handle nameof (which appears as an invocation expression??)
7173
context.RegisterSyntaxNodeAction(InvocationExpressionAction, SyntaxKind.InvocationExpression);
74+
75+
// handle var (which appears as an identifier name??)
76+
context.RegisterSyntaxNodeAction(IdentifierNameAction, SyntaxKind.IdentifierName);
7277
}
7378

7479
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
@@ -93,10 +98,13 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
9398
case SyntaxKind.LetKeyword:
9499
case SyntaxKind.LockKeyword:
95100
case SyntaxKind.OrderByKeyword:
101+
case SyntaxKind.OutKeyword:
102+
case SyntaxKind.RefKeyword:
96103
case SyntaxKind.SelectKeyword:
97104
case SyntaxKind.StackAllocKeyword:
98105
case SyntaxKind.SwitchKeyword:
99106
case SyntaxKind.UsingKeyword:
107+
case SyntaxKind.TypeVarKeyword:
100108
case SyntaxKind.WhereKeyword:
101109
case SyntaxKind.WhileKeyword:
102110
case SyntaxKind.YieldKeyword:
@@ -168,9 +176,21 @@ private static void HandleInvocationExpression(SyntaxNodeAnalysisContext context
168176
}
169177
}
170178

179+
private static void HandleIdentifierName(SyntaxNodeAnalysisContext context)
180+
{
181+
IdentifierNameSyntax identifierNameSyntax = (IdentifierNameSyntax)context.Node;
182+
if (identifierNameSyntax.IsVar)
183+
{
184+
HandleRequiredSpaceToken(ref context, identifierNameSyntax.Identifier);
185+
}
186+
}
187+
171188
private static void HandleRequiredSpaceToken(ref SyntaxTreeAnalysisContext context, SyntaxToken token)
172189
=> HandleRequiredSpaceToken(ReportSyntaxTreeDiagnostic, ref context, token);
173190

191+
private static void HandleRequiredSpaceToken(ref SyntaxNodeAnalysisContext context, SyntaxToken token)
192+
=> HandleRequiredSpaceToken(ReportSyntaxNodeDiagnostic, ref context, token);
193+
174194
private static void HandleRequiredSpaceToken<TContext>(ReportDiagnosticCallback<TContext> reportDiagnostic, ref TContext context, SyntaxToken token)
175195
{
176196
if (token.IsMissing)

documentation/SA1000.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ The spacing around a C# keyword is incorrect.
2424
A violation of this rule occurs when the spacing around a keyword is incorrect.
2525

2626
The following C# keywords must always be followed by a single space: `await`, `case`, `catch`, `fixed`, `for`,
27-
`foreach`, `from`, `group`, `if`, `in`, `into`, `join`, `let`, `lock`, `orderby`, `return`, `select`, `stackalloc`,
28-
`switch`, `using`, `where`, `while`, `yield`.
27+
`foreach`, `from`, `group`, `if`, `in`, `into`, `join`, `let`, `lock`, `orderby`, `out`, `ref`, `return`, `select`,
28+
`stackalloc`, `switch`, `using`, `var`, `where`, `while`, `yield`.
2929

3030
The following keywords must not be followed by any space: `checked`, `default`, `sizeof`, `typeof`, `unchecked`.
3131

0 commit comments

Comments
 (0)