Skip to content

Commit

Permalink
Merge pull request #37397 from YairHalberstadt/convert-interpolated-s…
Browse files Browse the repository at this point in the history
…trings-treats-char-as-string

Convert To Interpolated Strings refactoring provider should treat cha…
  • Loading branch information
JoeRobich committed Aug 20, 2019
2 parents 818cb28 + 558bd79 commit 68d8f53
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 9 deletions.
Expand Up @@ -54,6 +54,19 @@ void M()
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingOnConcatenatedStrings3()
{
await TestMissingInRegularAndScriptAsync(
@"public class C
{
void M()
{
var v = ""string"" + '.' + [||]""string"";
}
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithStringOnLeft()
{
Expand Down Expand Up @@ -270,6 +283,19 @@ void M()
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingWithMixedStringTypes3()
{
await TestMissingInRegularAndScriptAsync(
@"public class C
{
void M()
{
var v = 1 + @""string"" + 2 + [||]'\n';
}
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithOverloadedOperator()
{
Expand Down Expand Up @@ -728,6 +754,77 @@ void M()
{
var v = $""{1}{(""string"")}"";
}
}");
}

[WorkItem(37324, "https://github.com/dotnet/roslyn/issues/37324")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestConcatenationWithChar()
{
await TestInRegularAndScriptAsync(
@"public class C
{
void M()
{
var hello = ""hello"";
var world = ""world"";
var str = hello [||]+ ' ' + world;
}
}",
@"public class C
{
void M()
{
var hello = ""hello"";
var world = ""world"";
var str = $""{hello} {world}"";
}
}");
}

[WorkItem(37324, "https://github.com/dotnet/roslyn/issues/37324")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestConcatenationWithCharAfterStringLiteral()
{
await TestInRegularAndScriptAsync(
@"public class C
{
void M()
{
var world = ""world"";
var str = ""hello"" [||]+ ' ' + world;
}
}",
@"public class C
{
void M()
{
var world = ""world"";
var str = $""hello {world}"";
}
}");
}

[WorkItem(37324, "https://github.com/dotnet/roslyn/issues/37324")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestConcatenationWithCharBeforeStringLiteral()
{
await TestInRegularAndScriptAsync(
@"public class C
{
void M()
{
var hello = ""hello"";
var str = hello [||]+ ' ' + ""world"";
}
}",
@"public class C
{
void M()
{
var hello = ""hello"";
var str = $""{hello} world"";
}
}");
}
}
Expand Down
Expand Up @@ -462,6 +462,68 @@ Public Class C
Sub M()
dim v = $""{3}string{1}string""
End Sub
End Class")
End Function

<WorkItem(37324, "https://github.com/dotnet/roslyn/issues/37324")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestConcatenationWithChar() As Task
Await TestInRegularAndScriptAsync(
"
Public Class C
Private Sub M()
Dim hello = ""hello""
Dim world = ""world""
Dim str = hello [||]& "" ""c & world
End Sub
End Class",
"
Public Class C
Private Sub M()
Dim hello = ""hello""
Dim world = ""world""
Dim str = $""{hello} {world}""
End Sub
End Class")
End Function

<WorkItem(37324, "https://github.com/dotnet/roslyn/issues/37324")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestConcatenationWithCharAfterStringLiteral() As Task
Await TestInRegularAndScriptAsync(
"
Public Class C
Private Sub M()
Dim world = ""world""
Dim str = ""hello"" [||]& "" ""c & world
End Sub
End Class",
"
Public Class C
Private Sub M()
Dim world = ""world""
Dim str = $""hello {world}""
End Sub
End Class")
End Function

<WorkItem(37324, "https://github.com/dotnet/roslyn/issues/37324")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestConcatenationWithCharBeforeStringLiteral() As Task
Await TestInRegularAndScriptAsync(
"
Public Class C
Private Sub M()
Dim hello = ""hello""
Dim str = hello [||]& "" ""c & ""world""
End Sub
End Class",
"
Public Class C
Private Sub M()
Dim hello = ""hello""
Dim str = $""{hello} world""
End Sub
End Class")
End Function
End Class
Expand Down
Expand Up @@ -27,7 +27,7 @@ protected override SyntaxToken CreateInterpolatedStringStartToken(bool isVerbati
protected override SyntaxToken CreateInterpolatedStringEndToken()
=> SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken);

protected override string GetTextWithoutQuotes(string text, bool isVerbatim)
protected override string GetTextWithoutQuotes(string text, bool isVerbatim, bool isCharacterLiteral)
=> isVerbatim
? text.Substring("@'".Length, text.Length - "@''".Length)
: text.Substring("'".Length, text.Length - "''".Length);
Expand Down
Expand Up @@ -60,7 +60,9 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var pieces = new List<SyntaxNode>();
CollectPiecesDown(syntaxFacts, pieces, top, semanticModel, cancellationToken);

var stringLiterals = pieces.Where(syntaxFacts.IsStringLiteralExpression).ToImmutableArray();
var stringLiterals = pieces
.Where(x => syntaxFacts.IsStringLiteralExpression(x) || syntaxFacts.IsCharacterLiteralExpression(x))
.ToImmutableArray();

// If the entire expression is just concatenated strings, then don't offer to
// make an interpolated string. The user likely manually split this for
Expand Down Expand Up @@ -113,12 +115,13 @@ private Task<Document> UpdateDocumentAsync(Document document, SyntaxNode root, S
var previousContentWasStringLiteralExpression = false;
foreach (var piece in pieces)
{
var currentContentIsStringLiteral = syntaxFacts.IsStringLiteralExpression(piece);
if (currentContentIsStringLiteral)
var isCharacterLiteral = syntaxFacts.IsCharacterLiteralExpression(piece);
var currentContentIsStringOrCharacterLiteral = syntaxFacts.IsStringLiteralExpression(piece) || isCharacterLiteral;
if (currentContentIsStringOrCharacterLiteral)
{
var text = piece.GetFirstToken().Text;
var textWithEscapedBraces = text.Replace("{", "{{").Replace("}", "}}");
var textWithoutQuotes = GetTextWithoutQuotes(textWithEscapedBraces, isVerbatimStringLiteral);
var textWithoutQuotes = GetTextWithoutQuotes(textWithEscapedBraces, isVerbatimStringLiteral, isCharacterLiteral);
if (previousContentWasStringLiteralExpression)
{
// Last part we added to the content list was also an interpolated-string-text-node.
Expand All @@ -145,7 +148,7 @@ private Task<Document> UpdateDocumentAsync(Document document, SyntaxNode root, S
}
// Update this variable to be true every time we encounter a new string literal expression
// so we know to concatinate future string literals together if we encounter them.
previousContentWasStringLiteralExpression = currentContentIsStringLiteral;
previousContentWasStringLiteralExpression = currentContentIsStringOrCharacterLiteral;
}

return generator.InterpolatedStringExpression(startToken, content, endToken);
Expand All @@ -158,7 +161,7 @@ private static SyntaxNode ConcatinateTextToTextNode(SyntaxGenerator generator, S
return generator.InterpolatedStringText(generator.InterpolatedStringTextToken(newText));
}

protected abstract string GetTextWithoutQuotes(string text, bool isVerbatimStringLiteral);
protected abstract string GetTextWithoutQuotes(string text, bool isVerbatimStringLiteral, bool isCharacterLiteral);
protected abstract SyntaxToken CreateInterpolatedStringStartToken(bool isVerbatimStringLiteral);
protected abstract SyntaxToken CreateInterpolatedStringEndToken();

Expand Down
Expand Up @@ -21,8 +21,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString
Return SyntaxFactory.Token(SyntaxKind.DoubleQuoteToken)
End Function

Protected Overrides Function GetTextWithoutQuotes(text As String, isVerbatim As Boolean) As String
Return text.Substring("'".Length, text.Length - "''".Length)
Protected Overrides Function GetTextWithoutQuotes(text As String, isVerbatim As Boolean, isCharacterLiteral As Boolean) As String
If isCharacterLiteral Then
Return text.Substring("'".Length, text.Length - "''C".Length)
Else
Return text.Substring("'".Length, text.Length - "''".Length)
End If
End Function
End Class
End Namespace
Expand Up @@ -1311,6 +1311,9 @@ public override bool IsInterpolatedStringTextToken(SyntaxToken token)
public bool IsStringLiteralExpression(SyntaxNode node)
=> node.Kind() == SyntaxKind.StringLiteralExpression;

public bool IsCharacterLiteralExpression(SyntaxNode node)
=> node.Kind() == SyntaxKind.CharacterLiteralExpression;

public bool IsVerbatimStringLiteral(SyntaxToken token)
=> token.IsVerbatimStringLiteral();

Expand Down
Expand Up @@ -84,6 +84,7 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsVerbatimStringLiteral(SyntaxToken token);
bool IsInterpolatedStringTextToken(SyntaxToken token);
bool IsStringLiteralExpression(SyntaxNode node);
bool IsCharacterLiteralExpression(SyntaxNode node);

bool IsTypeNamedVarInVariableOrFieldDeclaration(SyntaxToken token, SyntaxNode parent);
bool IsTypeNamedDynamic(SyntaxToken token, SyntaxNode parent);
Expand Down
Expand Up @@ -1362,6 +1362,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return node.Kind() = SyntaxKind.StringLiteralExpression
End Function

Public Function IsCharacterLiteralExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsCharacterLiteralExpression
Return node.Kind() = SyntaxKind.CharacterLiteralExpression
End Function

Public Function IsVerbatimStringLiteral(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsVerbatimStringLiteral
' VB does not have verbatim strings
Return False
Expand Down

0 comments on commit 68d8f53

Please sign in to comment.