Skip to content

Commit 0c81f2b

Browse files
authored
Fix analyzer RCS1262 (#1339)
1 parent 8ad622f commit 0c81f2b

4 files changed

Lines changed: 88 additions & 7 deletions

File tree

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424
- [TestFramework] Bump `xunit.assert` to `2.6.2` ([PR](https://github.com/dotnet/roslynator/pull/1332))
2525
- Bump Roslyn to 4.7.0 ([PR](https://github.com/dotnet/roslynator/pull/1325))
2626

27+
### Fixed
28+
29+
- Fix analyzer [RCS1262](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1262) ([PR](https://github.com/dotnet/roslynator/pull/1339))
30+
2731
## [4.7.0] - 2023-12-03
2832

2933
### Added

src/Analyzers.CodeFixes/CSharp/CodeFixes/UnnecessaryRawStringLiteralCodeFixProvider.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System.Collections.Immutable;
44
using System.Composition;
5+
using System.Linq;
56
using System.Threading;
67
using System.Threading.Tasks;
78
using Microsoft.CodeAnalysis;
@@ -71,9 +72,26 @@ private static Task<Document> RefactorAsync(
7172
InterpolatedStringExpressionSyntax interpolatedString,
7273
CancellationToken cancellationToken)
7374
{
74-
string newText = interpolatedString.ToString();
75-
int startIndex = interpolatedString.StringStartToken.Text.Length;
76-
newText = "$\"" + newText.Substring(startIndex, newText.Length - startIndex - interpolatedString.StringEndToken.Text.Length) + "\"";
75+
InterpolatedStringExpressionSyntax newInterpolatedString = interpolatedString.ReplaceTokens(
76+
interpolatedString
77+
.Contents
78+
.OfType<InterpolationSyntax>()
79+
.SelectMany(interpolation => new SyntaxToken[] { interpolation.OpenBraceToken, interpolation.CloseBraceToken }),
80+
(token, _) =>
81+
{
82+
if (token.IsKind(SyntaxKind.OpenBraceToken))
83+
{
84+
return SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithTriviaFrom(token);
85+
}
86+
else
87+
{
88+
return SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithTriviaFrom(token);
89+
}
90+
});
91+
92+
string text = newInterpolatedString.ToString();
93+
int startIndex = newInterpolatedString.StringStartToken.Text.Length;
94+
string newText = "$\"" + text.Substring(startIndex, text.Length - startIndex - newInterpolatedString.StringEndToken.Text.Length) + "\"";
7795

7896
return document.WithTextChangeAsync(interpolatedString.Span, newText, cancellationToken);
7997
}

src/Analyzers/CSharp/Analysis/UnnecessaryRawStringLiteralAnalyzer.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private static void AnalyzeStringLiteralExpression(SyntaxNodeAnalysisContext con
4848

4949
string text = info.Text;
5050

51-
if (ContainsBackSlashQuote(text, info.QuoteCount, text.Length - (info.QuoteCount * 2)))
51+
if (ContainsBackSlashOrQuote(text, info.QuoteCount, text.Length - (info.QuoteCount * 2)))
5252
return;
5353

5454
DiagnosticHelpers.ReportDiagnostic(
@@ -72,25 +72,43 @@ private static void AnalyzeInterpolatedStringExpression(SyntaxNodeAnalysisContex
7272
{
7373
string text = interpolatedStringText.TextToken.Text;
7474

75-
if (ContainsBackSlashQuote(text, 0, text.Length))
75+
if (ContainsBackSlashOrQuoteOrOpenBrace(text, 0, text.Length))
7676
return;
7777
}
7878
}
7979

80+
int offset = startToken.ValueText.LastIndexOf('$') + 2;
81+
8082
DiagnosticHelpers.ReportDiagnostic(
8183
context,
8284
DiagnosticRules.UnnecessaryRawStringLiteral,
83-
Location.Create(interpolatedString.SyntaxTree, new TextSpan(startToken.SpanStart + 2, startToken.Span.Length - 2)));
85+
Location.Create(interpolatedString.SyntaxTree, new TextSpan(startToken.SpanStart + offset, startToken.Span.Length - offset)));
86+
}
87+
88+
private static bool ContainsBackSlashOrQuote(string text, int start, int length)
89+
{
90+
for (int pos = start; pos < start + length; pos++)
91+
{
92+
switch (text[pos])
93+
{
94+
case '\\':
95+
case '"':
96+
return true;
97+
}
98+
}
99+
100+
return false;
84101
}
85102

86-
private static bool ContainsBackSlashQuote(string text, int start, int length)
103+
private static bool ContainsBackSlashOrQuoteOrOpenBrace(string text, int start, int length)
87104
{
88105
for (int pos = start; pos < start + length; pos++)
89106
{
90107
switch (text[pos])
91108
{
92109
case '\\':
93110
case '"':
111+
case '{':
94112
return true;
95113
}
96114
}

src/Tests/Analyzers.Tests/RCS1262UnnecessaryRawStringLiteralTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ void M()
5858
");
5959
}
6060

61+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryRawStringLiteral)]
62+
public async Task Test_InterpolatedString_MultipleDollarSigns()
63+
{
64+
await VerifyDiagnosticAndFixAsync(@"
65+
class C
66+
{
67+
void M()
68+
{
69+
string s1 = """";
70+
string s2 = """";
71+
string s3 = $$$""[|""""|] {{{s1}}} foo {{{s2}}} """""";
72+
}
73+
}
74+
", @"
75+
class C
76+
{
77+
void M()
78+
{
79+
string s1 = """";
80+
string s2 = """";
81+
string s3 = $"" {s1} foo {s2} "";
82+
}
83+
}
84+
");
85+
}
86+
6187
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryRawStringLiteral)]
6288
public async Task TestNoDiagnostic_ContainsQuote()
6389
{
@@ -111,6 +137,21 @@ void M()
111137
string s = $"""""" {""""} \t """""";
112138
}
113139
}
140+
");
141+
}
142+
143+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryRawStringLiteral)]
144+
public async Task TestNoDiagnostic_MultipleDollarSigns()
145+
{
146+
await VerifyNoDiagnosticAsync(@"
147+
class C
148+
{
149+
void M()
150+
{
151+
string s = string.Empty;
152+
s = $$""""""{{s}}{s}"""""";
153+
}
154+
}
114155
");
115156
}
116157
}

0 commit comments

Comments
 (0)