Skip to content

Commit 28b75f2

Browse files
committed
Allow no preceding whitespace on negative and positive signs that follow opening braces of interpolated strings
1 parent 08e960e commit 28b75f2

6 files changed

Lines changed: 94 additions & 4 deletions

File tree

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

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

427+
[Fact]
428+
public async Task TestPrefixUnaryOperatorInInterpolationBracesAsync()
429+
{
430+
string testFormat = @"namespace Namespace
431+
{{
432+
class Type
433+
{{
434+
void Foo()
435+
{{
436+
string x = $""{{{0}}}"";
437+
}}
438+
}}
439+
}}
440+
";
441+
442+
// in all cases the final output should be the following
443+
string fixedTest = @"namespace Namespace
444+
{
445+
class Type
446+
{
447+
void Foo()
448+
{
449+
string x = $""{" + this.Sign + @"0}"";
450+
}
451+
}
452+
}
453+
";
454+
455+
await this.VerifyCSharpDiagnosticAsync(fixedTest, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
456+
457+
string test = string.Format(testFormat, this.Sign + "0");
458+
await this.VerifyCSharpDiagnosticAsync(test, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
459+
await this.VerifyCSharpFixAsync(test, fixedTest, numberOfFixAllIterations: 0, cancellationToken: CancellationToken.None).ConfigureAwait(false);
460+
461+
test = string.Format(testFormat, " " + this.Sign + "0");
462+
DiagnosticResult[] expected =
463+
{
464+
this.CSharpDiagnostic().WithArguments(" not", "preceded").WithLocation(7, 28),
465+
};
466+
await this.VerifyCSharpDiagnosticAsync(test, expected, CancellationToken.None).ConfigureAwait(false);
467+
await this.VerifyCSharpFixAsync(test, fixedTest, cancellationToken: CancellationToken.None).ConfigureAwait(false);
468+
469+
test = string.Format(testFormat, this.Sign + " 0");
470+
expected =
471+
new[]
472+
{
473+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(7, 27),
474+
};
475+
await this.VerifyCSharpDiagnosticAsync(test, expected, CancellationToken.None).ConfigureAwait(false);
476+
await this.VerifyCSharpFixAsync(test, fixedTest, cancellationToken: CancellationToken.None).ConfigureAwait(false);
477+
478+
test = string.Format(testFormat, " " + this.Sign + " 0");
479+
expected =
480+
new[]
481+
{
482+
this.CSharpDiagnostic().WithArguments(" not", "preceded").WithLocation(7, 28),
483+
this.CSharpDiagnostic().WithArguments(" not", "followed").WithLocation(7, 28),
484+
};
485+
await this.VerifyCSharpDiagnosticAsync(test, expected, CancellationToken.None).ConfigureAwait(false);
486+
await this.VerifyCSharpFixAsync(test, fixedTest, cancellationToken: CancellationToken.None).ConfigureAwait(false);
487+
}
488+
427489
protected override abstract CodeFixProvider GetCSharpCodeFixProvider();
428490
}
429491
}

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,30 @@ internal static bool IsFirstInLine(this SyntaxToken token, bool allowNonWhitespa
5656
return firstInLine;
5757
}
5858

59+
/// <summary>
60+
/// Gets a value indicating whether the <paramref name="token"/> is an interpolated
61+
/// unary expression.
62+
/// </summary>
63+
/// <param name="token">The token to process.</param>
64+
/// <returns>
65+
/// <see langword="true"/> if <paramref name="token"/> is an interpolated unary expression;
66+
/// otherwise, <see langword="false"/>.
67+
/// </returns>
68+
internal static bool IsInterpolatedUnaryExpression(this SyntaxToken token)
69+
{
70+
SyntaxNode parentNode = (token.Parent.IsKind(SyntaxKind.UnaryMinusExpression)
71+
|| token.Parent.IsKind(SyntaxKind.UnaryPlusExpression))
72+
? token.Parent.Parent
73+
: token.Parent;
74+
75+
if (parentNode.IsKind(SyntaxKind.Interpolation))
76+
{
77+
return true;
78+
}
79+
80+
return false;
81+
}
82+
5983
/// <summary>
6084
/// Gets a value indicating whether the <paramref name="token"/> is last in line.
6185
/// </summary>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private static void HandleMinusToken(SyntaxTreeAnalysisContext context, SyntaxTo
8686
bool followsSpecialCharacter = false;
8787

8888
bool followedBySpace = token.IsFollowedByWhitespace();
89+
bool interpolatedUnaryExpression = token.IsInterpolatedUnaryExpression();
8990
bool lastInLine = token.IsLastInLine();
9091

9192
if (!firstInLine)
@@ -96,7 +97,8 @@ private static void HandleMinusToken(SyntaxTreeAnalysisContext context, SyntaxTo
9697
followsSpecialCharacter =
9798
precedingToken.IsKind(SyntaxKind.OpenBracketToken)
9899
|| precedingToken.IsKind(SyntaxKind.OpenParenToken)
99-
|| precedingToken.IsKind(SyntaxKind.CloseParenToken);
100+
|| precedingToken.IsKind(SyntaxKind.CloseParenToken)
101+
|| (precedingToken.IsKind(SyntaxKind.OpenBraceToken) && interpolatedUnaryExpression);
100102
}
101103

102104
if (!firstInLine)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private static void HandlePlusToken(SyntaxTreeAnalysisContext context, SyntaxTok
8686
bool followsSpecialCharacter = false;
8787

8888
bool followedBySpace = token.IsFollowedByWhitespace();
89+
bool interpolatedUnaryExpression = token.IsInterpolatedUnaryExpression();
8990
bool lastInLine = token.IsLastInLine();
9091

9192
if (!firstInLine)
@@ -96,7 +97,8 @@ private static void HandlePlusToken(SyntaxTreeAnalysisContext context, SyntaxTok
9697
followsSpecialCharacter =
9798
precedingToken.IsKind(SyntaxKind.OpenBracketToken)
9899
|| precedingToken.IsKind(SyntaxKind.OpenParenToken)
99-
|| precedingToken.IsKind(SyntaxKind.CloseParenToken);
100+
|| precedingToken.IsKind(SyntaxKind.CloseParenToken)
101+
|| (precedingToken.IsKind(SyntaxKind.OpenBraceToken) && interpolatedUnaryExpression);
100102
}
101103

102104
if (!firstInLine)

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, a brace of an interpolated string, or is the first character on the line.
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, a brace of an interpolated string, or is the first character on the line.
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)