Skip to content

Commit 666d577

Browse files
authored
Merge pull request #2295 from THammond9/SA1011-FixSpaceAfterBracket
All changes made for format string component and alignment component
2 parents 85d5f04 + 6173a13 commit 666d577

14 files changed

Lines changed: 319 additions & 16 deletions

StyleCop.Analyzers/StyleCop.Analyzers.Test/SpacingRules/NumberSignSpacingTestBase.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,113 @@ void Foo()
424424
await this.VerifyCSharpFixAsync(test, fixedTest, cancellationToken: CancellationToken.None).ConfigureAwait(false);
425425
}
426426

427+
[Theory]
428+
[InlineData("")]
429+
[InlineData(" ")]
430+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
431+
public async Task TestSpaceBeforeUnaryOperatorInInterpolationAlignmentClauseAsync(string spacing)
432+
{
433+
string test = $@"namespace Namespace
434+
{{
435+
class Type
436+
{{
437+
void Foo()
438+
{{
439+
string msg = $""{{5,{spacing}{this.Sign}3}}"";
440+
}}
441+
}}
442+
}}
443+
";
444+
445+
await this.VerifyCSharpDiagnosticAsync(test, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
446+
}
447+
448+
[Fact]
449+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
450+
public async Task TestSpaceAfterUnaryOperatorInInterpolationAlignmentClauseAsync()
451+
{
452+
string testFormat = @"namespace Namespace
453+
{{
454+
class Type
455+
{{
456+
void Foo()
457+
{{
458+
string msg = $""{{5,{0}}}"";
459+
}}
460+
}}
461+
}}
462+
";
463+
464+
// in all cases the final output should be the following
465+
string fixedTest = @"namespace Namespace
466+
{
467+
class Type
468+
{
469+
void Foo()
470+
{
471+
string msg = $""{5," + this.Sign + @"3}"";
472+
}
473+
}
474+
}
475+
";
476+
477+
string test = string.Format(testFormat, this.Sign + "3");
478+
await this.VerifyCSharpDiagnosticAsync(test, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
479+
480+
test = string.Format(testFormat, this.Sign + " 3");
481+
DiagnosticResult[] expected =
482+
{
483+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(7, 31),
484+
};
485+
486+
await this.VerifyCSharpDiagnosticAsync(test, expected, CancellationToken.None).ConfigureAwait(false);
487+
await this.VerifyCSharpDiagnosticAsync(fixedTest, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
488+
await this.VerifyCSharpFixAsync(test, fixedTest, cancellationToken: CancellationToken.None).ConfigureAwait(false);
489+
}
490+
491+
[Fact]
492+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
493+
public async Task TestSpaceBeforeAndAfterUnaryOperatorInInterpolationAlignmentClauseAsync()
494+
{
495+
string testFormat = @"namespace Namespace
496+
{{
497+
class Type
498+
{{
499+
void Foo()
500+
{{
501+
string msg = $""{{5,{0}}}"";
502+
}}
503+
}}
504+
}}
505+
";
506+
507+
// in all cases the final output should be the following
508+
string fixedTest = @"namespace Namespace
509+
{
510+
class Type
511+
{
512+
void Foo()
513+
{
514+
string msg = $""{5, " + this.Sign + @"3}"";
515+
}
516+
}
517+
}
518+
";
519+
520+
string test = string.Format(testFormat, " " + this.Sign + "3");
521+
await this.VerifyCSharpDiagnosticAsync(test, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
522+
523+
test = string.Format(testFormat, " " + this.Sign + " 3");
524+
DiagnosticResult[] expected =
525+
{
526+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(7, 32),
527+
};
528+
529+
await this.VerifyCSharpDiagnosticAsync(test, expected, CancellationToken.None).ConfigureAwait(false);
530+
await this.VerifyCSharpDiagnosticAsync(fixedTest, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
531+
await this.VerifyCSharpFixAsync(test, fixedTest, cancellationToken: CancellationToken.None).ConfigureAwait(false);
532+
}
533+
427534
protected override abstract CodeFixProvider GetCSharpCodeFixProvider();
428535
}
429536
}

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

Lines changed: 80 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,86 @@ 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+
193+
[Fact]
194+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
195+
public async Task TestSpaceBeforeAndAfterCommaWhenPartOfInterpolationAlignmentClauseAsync()
196+
{
197+
string statement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
198+
var t = $""{x[2] , 3}"";";
199+
string fixedStatement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
200+
var t = $""{x[2],3}"";";
201+
202+
DiagnosticResult[] expected =
203+
{
204+
this.CSharpDiagnostic().WithArguments(" not", "preceded").WithLocation(8, 29),
205+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(8, 29),
206+
};
207+
208+
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
209+
}
210+
211+
[Fact]
212+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
213+
public async Task TestSpaceAfterCommaWithMinusWhenPartOfInterpolationAlignmentClauseAsync()
214+
{
215+
string statement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
216+
var t = $""{x[2], -3}"";";
217+
string fixedStatement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
218+
var t = $""{x[2],-3}"";";
219+
220+
DiagnosticResult[] expected =
221+
{
222+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(8, 28),
223+
};
224+
225+
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
226+
}
227+
228+
[Fact]
229+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
230+
public async Task TestSpaceAfterCommaWithPlusWhenPartOfInterpolationAlignmentClauseAsync()
231+
{
232+
string statement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
233+
var t = $""{x[2], +3}"";";
234+
string fixedStatement = @"var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
235+
var t = $""{x[2],+3}"";";
236+
237+
DiagnosticResult[] expected =
238+
{
239+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(8, 28),
240+
};
241+
242+
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
243+
}
244+
166245
[Fact]
167246
public async Task TestSpaceOnlyBeforeCommaAsync()
168247
{

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

Lines changed: 36 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,42 @@ 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+
352+
[Fact]
353+
[WorkItem(2289, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2289")]
354+
public async Task TestCommaCanFollowSquareBracketWhenPartOfInterpolationFormatClauseAsync()
355+
{
356+
string testCode = @"
357+
class ClassName
358+
{
359+
void Method()
360+
{
361+
var x = new[] { 1, 2, 3, 4, 5, 6, 7 };
362+
var t = $""{ x[2],3}"";
363+
}
364+
}
365+
";
366+
367+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
368+
}
369+
335370
/// <inheritdoc/>
336371
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
337372
{

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 or it is
20+
/// part of a string interpolation 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
}

0 commit comments

Comments
 (0)