Skip to content

Commit 114f805

Browse files
THammond9sharwell
authored andcommitted
Correctly handle plus/minus in string interpolation alignment component
1 parent 6e5e389 commit 114f805

6 files changed

Lines changed: 183 additions & 6 deletions

File tree

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

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

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

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,40 @@ public async Task TestSpaceBeforeAndAfterCommaWhenPartOfInterpolationAlignmentCl
208208
await this.TestCommaInStatementOrDeclAsync(statement, expected, fixedStatement).ConfigureAwait(false);
209209
}
210210

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+
211245
[Fact]
212246
public async Task TestSpaceOnlyBeforeCommaAsync()
213247
{

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1021NegativeSignsMustBeSpacedCorrectly.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ namespace StyleCop.Analyzers.SpacingRules
1717
/// <para>A violation of this rule occurs when the spacing around a negative sign is not correct.</para>
1818
///
1919
/// <para>A negative sign should always be preceded by a single space, unless it comes after an opening square
20-
/// bracket, a parenthesis, or is the first character on the line.</para>
20+
/// bracket, a parenthesis, is the first character on the line, or is part of a string interpolation alignment
21+
/// component.</para>
2122
///
2223
/// <para>A negative sign should never be followed by whitespace, and should never be the last character on a
2324
/// line.</para>
@@ -81,6 +82,14 @@ private static void HandleMinusToken(SyntaxTreeAnalysisContext context, SyntaxTo
8182
return;
8283
}
8384

85+
var isInInterpolationAlignmentClause = token.Parent.Parent.IsKind(SyntaxKind.InterpolationAlignmentClause);
86+
if (isInInterpolationAlignmentClause && !token.IsFollowedByWhitespace())
87+
{
88+
// SA1001 is already handling the case like: line.Append($"{testResult.DisplayName, -75}");
89+
// Where the extra space before the minus sign is undesirable.
90+
return;
91+
}
92+
8493
bool precededBySpace = true;
8594
bool firstInLine = token.IsFirstInLine();
8695
bool followsSpecialCharacter = false;
@@ -99,7 +108,7 @@ private static void HandleMinusToken(SyntaxTreeAnalysisContext context, SyntaxTo
99108
|| precedingToken.IsKind(SyntaxKind.CloseParenToken);
100109
}
101110

102-
if (!firstInLine)
111+
if (!firstInLine && !isInInterpolationAlignmentClause)
103112
{
104113
if (!followsSpecialCharacter && !precededBySpace)
105114
{

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1022PositiveSignsMustBeSpacedCorrectly.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ namespace StyleCop.Analyzers.SpacingRules
1717
/// <para>A violation of this rule occurs when the spacing around a positive sign is not correct.</para>
1818
///
1919
/// <para>A positive sign should always be preceded by a single space, unless it comes after an opening square
20-
/// bracket, a parenthesis, or is the first character on the line.</para>
20+
/// bracket, a parenthesis, is the first character on the line, or is part of a string interpolation alignment
21+
/// component.</para>
2122
///
2223
/// <para>A positive sign should never be followed by whitespace, and should never be the last character on a
2324
/// line.</para>
@@ -81,6 +82,14 @@ private static void HandlePlusToken(SyntaxTreeAnalysisContext context, SyntaxTok
8182
return;
8283
}
8384

85+
var isInInterpolationAlignmentClause = token.Parent.Parent.IsKind(SyntaxKind.InterpolationAlignmentClause);
86+
if (isInInterpolationAlignmentClause && !token.IsFollowedByWhitespace())
87+
{
88+
// SA1001 is already handling the case like: line.Append($"{testResult.DisplayName, +75}");
89+
// Where the extra space before the plus sign is undesirable.
90+
return;
91+
}
92+
8493
bool precededBySpace = true;
8594
bool firstInLine = token.IsFirstInLine();
8695
bool followsSpecialCharacter = false;
@@ -99,7 +108,7 @@ private static void HandlePlusToken(SyntaxTreeAnalysisContext context, SyntaxTok
99108
|| precedingToken.IsKind(SyntaxKind.CloseParenToken);
100109
}
101110

102-
if (!firstInLine)
111+
if (!firstInLine && !isInInterpolationAlignmentClause)
103112
{
104113
if (!followsSpecialCharacter && !precededBySpace)
105114
{

documentation/SA1021.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ A negative sign within a C# element is not spaced correctly.
2323

2424
A violation of this rule occurs when the spacing around a negative sign is not correct.
2525

26-
A negative sign should always be preceded by a single space, unless it comes after an opening square bracket, a parenthesis, or is the first character on the line.
26+
A negative sign should always be preceded by a single space, unless it comes after an opening square bracket, a parenthesis, is the first character on the line, or is part of a string interpolation alignment component.
2727

2828
A negative sign should never be followed by whitespace, and should never be the last character on a line.
2929

documentation/SA1022.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ A positive sign within a C# element is not spaced correctly.
2323

2424
A violation of this rule occurs when the spacing around a positive sign is not correct.
2525

26-
A positive sign should always be preceded by a single space, unless it comes after an opening square bracket, a parenthesis, or is the first character on the line.
26+
A positive sign should always be preceded by a single space, unless it comes after an opening square bracket, a parenthesis, is the first character on the line, or is part of a string interpolation alignment component.
2727

2828
A positive sign should never be followed by whitespace, and should never be the last character on a line.
2929

0 commit comments

Comments
 (0)