Skip to content

Commit 582df15

Browse files
THammond9sharwell
authored andcommitted
All changes made for format string component and alignment component
1 parent d6beabc commit 582df15

9 files changed

Lines changed: 118 additions & 11 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/SpacingRules/SA1001UnitTests.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ namespace StyleCop.Analyzers.Test.SpacingRules
77
using System.Collections.Generic;
88
using System.Threading;
99
using System.Threading.Tasks;
10-
using Microsoft.CodeAnalysis;
1110
using Microsoft.CodeAnalysis.CodeFixes;
1211
using Microsoft.CodeAnalysis.Diagnostics;
1312
using StyleCop.Analyzers.SpacingRules;
@@ -163,6 +162,34 @@ public async Task TestSpaceBeforeCommaFollowedByBracketInArrayDeclAsync()
163162
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
164163
}
165164

165+
[Fact]
166+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
167+
public async Task TestSpaceBeforeCommaWhenPartOfInterpolationAlignmentClauseAsync()
168+
{
169+
string statement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
170+
var t = $""{x[2] ,3}"";";
171+
string fixedStatement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
172+
var t = $""{x[2],3}"";";
173+
174+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments(" not", "preceded").WithLocation(8, 29);
175+
176+
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
177+
}
178+
179+
[Fact]
180+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
181+
public async Task TestSpaceAfterCommaWhenPartOfInterpolationAlignmentClauseAsync()
182+
{
183+
string statement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
184+
var t = $""{x[2], 3}"";";
185+
string fixedStatement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
186+
var t = $""{x[2],3}"";";
187+
188+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(8, 28);
189+
190+
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
191+
}
192+
166193
[Fact]
167194
public async Task TestSpaceOnlyBeforeCommaAsync()
168195
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/SpacingRules/SA1011UnitTests.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ namespace StyleCop.Analyzers.Test.SpacingRules
66
using System.Collections.Generic;
77
using System.Threading;
88
using System.Threading.Tasks;
9-
using Microsoft.CodeAnalysis;
109
using Microsoft.CodeAnalysis.CodeFixes;
1110
using Microsoft.CodeAnalysis.Diagnostics;
1211
using StyleCop.Analyzers.SpacingRules;
@@ -332,6 +331,24 @@ public unsafe string TestMethod(int[] a)
332331
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
333332
}
334333

334+
[Fact]
335+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
336+
public async Task TestColonCanFollowSquareBracketWhenPartOfInterpolationFormatClauseAsync()
337+
{
338+
string testCode = @"
339+
class ClassName
340+
{
341+
void Method()
342+
{
343+
var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
344+
var t = $""{ x[2]:C}"";
345+
}
346+
}
347+
";
348+
349+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
350+
}
351+
335352
/// <inheritdoc/>
336353
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
337354
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/SpacingRules/SA1024UnitTests.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ private int Bar(int value)
3737
case 2:
3838
case 3:
3939
return value;
40+
case 4:
41+
return (int)Convert.ToDouble($""{3:N}"");
42+
return (int)Convert.ToDouble($""{3: N}"");
4043
default:
4144
goto _label;
4245
}
@@ -70,6 +73,9 @@ private int Bar(int value)
7073
case 2:
7174
case 3:
7275
return value;
76+
case 4:
77+
return (int)Convert.ToDouble($""{3:N}"");
78+
return (int)Convert.ToDouble($""{3: N}"");
7379
default:
7480
goto _label;
7581
}
@@ -111,6 +117,9 @@ private int Bar(int value)
111117
case 2:
112118
case 3:
113119
return value;
120+
case 4:
121+
return (int)Convert.ToDouble($""{3:N}"");
122+
return (int)Convert.ToDouble($""{3: N}"");
114123
default:
115124
goto _label;
116125
}
@@ -147,6 +156,9 @@ private int Bar(int value)
147156
case 2:
148157
case 3:
149158
return value;
159+
case 4:
160+
return (int)Convert.ToDouble($""{3:N}"");
161+
return (int)Convert.ToDouble($""{3: N}"");
150162
default:
151163
goto _label;
152164
}
@@ -193,6 +205,9 @@ private int Bar(int value)
193205
case 2:
194206
case 3:
195207
return value;
208+
case 4:
209+
return (int)Convert.ToDouble($""{3:N}"");
210+
return (int)Convert.ToDouble($""{3: N}"");
196211
default:
197212
goto _label;
198213
}
@@ -239,6 +254,9 @@ private int Bar(int value)
239254
case 2:
240255
case 3 :
241256
return value;
257+
case 4:
258+
return (int)Convert.ToDouble($""{3 :N}"");
259+
return (int)Convert.ToDouble($""{3 : N}"");
242260
default :
243261
goto _label;
244262
}
@@ -250,7 +268,9 @@ private int Bar(int value)
250268
this.CSharpDiagnostic().WithLocation(10, 19).WithArguments(" not", "preceded", string.Empty),
251269
this.CSharpDiagnostic().WithLocation(15, 12).WithArguments(" not", "preceded", string.Empty),
252270
this.CSharpDiagnostic().WithLocation(19, 20).WithArguments(" not", "preceded", string.Empty),
253-
this.CSharpDiagnostic().WithLocation(21, 21).WithArguments(" not", "preceded", string.Empty),
271+
this.CSharpDiagnostic().WithLocation(22, 51).WithArguments(" not", "preceded", string.Empty),
272+
this.CSharpDiagnostic().WithLocation(23, 51).WithArguments(" not", "preceded", string.Empty),
273+
this.CSharpDiagnostic().WithLocation(24, 21).WithArguments(" not", "preceded", string.Empty),
254274
};
255275

256276
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
@@ -284,6 +304,9 @@ private int Bar(int value)
284304
case 2:
285305
case 3:
286306
return value;
307+
case 4:
308+
return (int)Convert.ToDouble($""{3:N}"");
309+
return (int)Convert.ToDouble($""{3: N}"");
287310
default:
288311
goto _label;
289312
}

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1001CommasMustBeSpacedCorrectly.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ namespace StyleCop.Analyzers.SpacingRules
1616
/// <remarks>
1717
/// <para>A violation of this rule occurs when the spacing around a comma is incorrect.</para>
1818
///
19-
/// <para>A comma should always be followed by a single space, unless it is the last character on the line, and a
20-
/// comma should never be preceded by any whitespace, unless it is the first character on the line.</para>
19+
/// <para>A comma should always be followed by a single space, unless it is the last character on the line
20+
/// or it is part of an alignment component, and a comma should never be preceded by any whitespace,
21+
/// unless it is the first character on the line.</para>
2122
/// </remarks>
2223
[DiagnosticAnalyzer(LanguageNames.CSharp)]
2324
internal class SA1001CommasMustBeSpacedCorrectly : DiagnosticAnalyzer
@@ -75,6 +76,9 @@ private static void HandleCommaToken(SyntaxTreeAnalysisContext context, SyntaxTo
7576

7677
// check for a following space
7778
bool missingFollowingSpace = true;
79+
80+
// check for things like $"{x,5}"
81+
var shouldNotHaveFollowingSpace = token.Parent.IsKind(SyntaxKind.InterpolationAlignmentClause);
7882
if (token.HasTrailingTrivia)
7983
{
8084
if (token.TrailingTrivia.First().IsKind(SyntaxKind.WhitespaceTrivia))
@@ -102,11 +106,17 @@ private static void HandleCommaToken(SyntaxTreeAnalysisContext context, SyntaxTo
102106
context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemovePrecedingPreserveLayout, " not", "preceded"));
103107
}
104108

105-
if (missingFollowingSpace)
109+
if (missingFollowingSpace && !shouldNotHaveFollowingSpace)
106110
{
107111
// comma should{} be {followed} by whitespace
108112
context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.InsertFollowing, string.Empty, "followed"));
109113
}
114+
115+
if (!missingFollowingSpace && shouldNotHaveFollowingSpace)
116+
{
117+
// comma should{ not} be {followed} by whitespace
118+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemoveFollowing, " not", "followed"));
119+
}
110120
}
111121
}
112122
}

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1011ClosingSquareBracketsMustBeSpacedCorrectly.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace StyleCop.Analyzers.SpacingRules
55
{
66
using System;
7-
using System.Collections.Generic;
87
using System.Collections.Immutable;
98
using Microsoft.CodeAnalysis;
109
using Microsoft.CodeAnalysis.CSharp;
@@ -22,8 +21,9 @@ namespace StyleCop.Analyzers.SpacingRules
2221
/// line.</para>
2322
///
2423
/// <para>A closing square bracket should be followed by whitespace, unless it is the last character on the line, it
25-
/// is followed by a closing bracket or an opening parenthesis, it is followed by a comma or semicolon, or it is
26-
/// followed by certain types of operator symbols.</para>
24+
/// is followed by a closing bracket or an opening parenthesis, it is followed by a comma or semicolon, it is
25+
/// followed by a alignment component or format string component, or it is followed by certain types of operator
26+
/// symbols.</para>
2727
/// </remarks>
2828
[DiagnosticAnalyzer(LanguageNames.CSharp)]
2929
internal class SA1011ClosingSquareBracketsMustBeSpacedCorrectly : DiagnosticAnalyzer
@@ -124,6 +124,11 @@ private static void HandleCloseBracketToken(SyntaxTreeAnalysisContext context, S
124124
precedesSpecialCharacter = nextToken.Parent is InterpolationSyntax;
125125
break;
126126

127+
case SyntaxKind.ColonToken:
128+
precedesSpecialCharacter = nextToken.Parent.IsKind(SyntaxKind.InterpolationFormatClause);
129+
suppressFollowingSpaceError = false;
130+
break;
131+
127132
default:
128133
precedesSpecialCharacter = false;
129134
break;

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1024ColonsMustBeSpacedCorrectly.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ namespace StyleCop.Analyzers.SpacingRules
4242
/// }
4343
/// </code>
4444
///
45+
/// <para>A colon that appears as part of format string component should not have leading whitespace characters. For
46+
/// example:</para>
47+
///
48+
/// <code language="cs">
49+
/// var s = $"{x:N}";
50+
/// </code>
51+
///
4552
/// <para>Finally, when a colon is used within a conditional statement, it should always contain a single space on
4653
/// either side, unless the colon is the first or last character on the line. For example:</para>
4754
///
@@ -99,6 +106,7 @@ private static void HandleColonToken(SyntaxTreeAnalysisContext context, SyntaxTo
99106
}
100107

101108
bool requireBefore;
109+
var checkRequireAfter = true;
102110
switch (token.Parent.Kind())
103111
{
104112
case SyntaxKind.BaseList:
@@ -118,6 +126,11 @@ private static void HandleColonToken(SyntaxTreeAnalysisContext context, SyntaxTo
118126
requireBefore = false;
119127
break;
120128

129+
case SyntaxKind.InterpolationFormatClause:
130+
requireBefore = false;
131+
checkRequireAfter = false;
132+
break;
133+
121134
default:
122135
return;
123136
}
@@ -155,7 +168,7 @@ private static void HandleColonToken(SyntaxTreeAnalysisContext context, SyntaxTo
155168
context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, requireBefore ? string.Empty : " not", "preceded", string.Empty));
156169
}
157170

158-
if (missingFollowingSpace)
171+
if (missingFollowingSpace && checkRequireAfter)
159172
{
160173
// colon should{} be {followed}{} by a space
161174
context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.InsertFollowing, string.Empty, "followed", string.Empty));

documentation/SA1001.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ A comma should be followed by a single space, except in the following cases.
2727

2828
* A comma may appear at the end of a line
2929
* A comma should not be followed by a space when used in an open generic type in a `typeof` expression
30+
* A comma is part of an alignment component. For example:`$"{x,3}"`
3031

3132
A comma should never be preceded by a space or appear as the first token on a line.
3233

documentation/SA1011.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ A violation of this rule occurs when the spacing around a closing square bracket
2525

2626
A closing square bracket should never be preceded by whitespace, unless it is the first character on the line.
2727

28-
A closing square bracket should be followed by whitespace, unless it is the last character on the line, it is followed by a closing bracket or an opening parenthesis, it is followed by a comma or semicolon, or it is followed by certain types of operator symbols.
28+
A closing square bracket should be followed by whitespace unless:
29+
* It is the last character on the line
30+
* It is followed by a closing bracket or an opening parenthesis
31+
* It is followed by a comma or semicolon
32+
* It is followed by a alignment component or format string component. For example: `$"{x[i],3}"` or `$"{x[i]:C}"`
33+
* It is followed by certain types of operator symbols.
2934

3035
## How to fix violations
3136

documentation/SA1024.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ switch (x)
4545
}
4646
```
4747

48+
A colon that appears as part of format string component should not have leading whitespace characters. For example:
49+
50+
```csharp
51+
var s = $"{x:N}";
52+
```
53+
4854
Finally, when a colon is used within a conditional statement, it should always contain a single space on either side, unless the colon is the first or last character on the line. For example:
4955

5056
```csharp

0 commit comments

Comments
 (0)