File: ConvertToInterpolatedString\ConvertConcatenationToInterpolatedStringTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertToInterpolatedString;
 
using VerifyCS = CSharpCodeRefactoringVerifier<CSharpConvertConcatenationToInterpolatedStringRefactoringProvider>;
 
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public class ConvertConcatenationToInterpolatedStringTests
{
    [Fact]
    public async Task TestMissingOnSimpleString()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = [||]"string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnConcatenatedStrings1()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = [||]"string" + "string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnConcatenatedStrings2()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = "string" + [||]"string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnConcatenatedStrings3()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = "string" + '.' + [||]"string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithStringOnLeft()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = [||]"string" + 1;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"string{1}";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestRightSideOfString()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = "string"[||] + 1;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"string{1}";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithStringOnRight()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 1 + [||]"string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{1}string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithComplexExpressionOnLeft()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 1 + 2 + [||]"string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{1 + 2}string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithTrivia1()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v =
                        // Leading trivia
                        1 + 2 + [||]"string" /* trailing trivia */;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v =
                        // Leading trivia
                        $"{1 + 2}string" /* trailing trivia */;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithComplexExpressions()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 1 + 2 + [||]"string" + 3 + 4;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{1 + 2}string{3}{4}";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithEscapes1()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = "\r" + 2 + [||]"string" + 3 + "\n";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"\r{2}string{3}\n";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithEscapes2()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = "\\r" + 2 + [||]"string" + 3 + "\\n";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"\\r{2}string{3}\\n";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithVerbatimString1()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 1 + [||]@"string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $@"{1}string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingWithMixedStringTypes1()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = 1 + [||]@"string" + 2 + "string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingWithMixedStringTypes2()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = 1 + @"string" + 2 + [||]"string";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingWithMixedStringTypes3()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = 1 + @"string" + 2 + [||]'\n';
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithOverloadedOperator()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class D
            {
                public static bool operator +(D d, string s) => false;
                public static bool operator +(string s, D d) => false;
            }
 
            public class C
            {
                void M()
                {
                    D d = null;
                    var v = 1 + [||]"string" + d;
                }
            }
            """,
            """
            public class D
            {
                public static bool operator +(D d, string s) => false;
                public static bool operator +(string s, D d) => false;
            }
 
            public class C
            {
                void M()
                {
                    D d = null;
                    var v = $"{1}string" + d;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithOverloadedOperator2()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class D
            {
                public static int operator +(D d, string s) => 0;
                public static int operator +(string s, D d) => 0;
            }
 
            public class C
            {
                void M()
                {
                    D d = null;
                    var v = d + [||]"string" + 1;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16820")]
    public async Task TestWithMultipleStringConcatenations()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = "A" + 1 + [||]"B" + "C";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"A{1}BC";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16820")]
    public async Task TestWithMultipleStringConcatenations2()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = "A" + [||]"B" + "C" + 1;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"ABC{1}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16820")]
    public async Task TestWithMultipleStringConcatenations3()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = "A" + 1 + [||]"B" + "C" + 2 +"D"+ "E"+ "F" + 3;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"A{1}BC{2}DEF{3}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16820")]
    public async Task TestWithMultipleStringConcatenations4()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = "A" + 1 + [||]"B" + @"C";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20943")]
    public async Task TestMissingWithDynamic1()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            class C
            {
                void M()
                {
                    dynamic a = "b";
                    string c = [||]"d" + a + "e";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20943")]
    public async Task TestMissingWithDynamic2()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            class C
            {
                void M()
                {
                    dynamic dynamic = null;
                    var x = dynamic.someVal + [||]" $";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23536")]
    public async Task TestWithStringLiteralWithBraces()
    {
        {
            await VerifyCS.VerifyRefactoringAsync(
                """
                public class C
                {
                    void M()
                    {
                        var v = 1 + [||]"{string}";
                    }
                }
                """,
                """
                public class C
                {
                    void M()
                    {
                        var v = $"{1}{{string}}";
                    }
                }
                """);
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23536")]
    public async Task TestWithStringLiteralWithBraces2()
    {
        {
            await VerifyCS.VerifyRefactoringAsync(
                """
                public class C
                {
                    void M()
                    {
                        var v = 1 + [||]"{string}" + "{string}";
                    }
                }
                """,
                """
                public class C
                {
                    void M()
                    {
                        var v = $"{1}{{string}}{{string}}";
                    }
                }
                """);
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23536")]
    public async Task TestWithStringLiteralWithDoubleBraces()
    {
        {
            await VerifyCS.VerifyRefactoringAsync(
                """
                public class C
                {
                    void M()
                    {
                        var v = 1 + [||]"{{string}}";
                    }
                }
                """,
                """
                public class C
                {
                    void M()
                    {
                        var v = $"{1}{{{{string}}}}";
                    }
                }
                """);
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23536")]
    public async Task TestWithMultipleStringLiteralsWithBraces()
    {
        {
            await VerifyCS.VerifyRefactoringAsync(
                """
                public class C
                {
                    void M()
                    {
                        var v = "{" + 1 + [||]"}";
                    }
                }
                """,
                """
                public class C
                {
                    void M()
                    {
                        var v = $"{{{1}}}";
                    }
                }
                """);
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23536")]
    public async Task TestWithVerbatimStringWithBraces()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 1 + [||]@"{string}";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $@"{1}{{string}}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23536")]
    public async Task TestWithMultipleVerbatimStringsWithBraces()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = @"{" + 1 + [||]@"}";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $@"{{{1}}}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
    [WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithSelectionOnEntireToBeInterpolatedString()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = [|"string" + 1|];
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"string{1}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestMissingWithSelectionOnPartOfToBeInterpolatedStringPrefix()
    {
        // see comment in AbstractConvertConcatenationToInterpolatedStringRefactoringProvider:ComputeRefactoringsAsync
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = [|"string" + 1|] + "string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
    [WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestMissingWithSelectionOnPartOfToBeInterpolatedStringSuffix()
    {
        // see comment in AbstractConvertConcatenationToInterpolatedStringRefactoringProvider:ComputeRefactoringsAsync
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = "string" + [|1 + "string"|];
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
    [WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestMissingWithSelectionOnMiddlePartOfToBeInterpolatedString()
    {
        // see comment in AbstractConvertConcatenationToInterpolatedStringRefactoringProvider:ComputeRefactoringsAsync
        await VerifyCS.VerifyRefactoringAsync("""
            public class C
            {
                void M()
                {
                    var v = "a" + [|1 + "string"|] + "b";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithSelectionExceedingToBeInterpolatedString()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    [|var v = "string" + 1|];
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"string{1}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithCaretBeforeNonStringToken()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = [||]3 + "string" + 1 + "string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{3}string{1}string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithCaretAfterNonStringToken()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 3[||] + "string" + 1 + "string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{3}string{1}string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithCaretBeforePlusToken()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 3 [||]+ "string" + 1 + "string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{3}string{1}string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithCaretAfterPlusToken()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 3 +[||] "string" + 1 + "string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{3}string{1}string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithCaretBeforeLastPlusToken()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 3 + "string" + 1 [||]+ "string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{3}string{1}string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16981")]
    public async Task TestWithCaretAfterLastPlusToken()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 3 + "string" + 1 +[||] "string";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{3}string{1}string";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32864")]
    public async Task TestConcatenationWithNoStringLiterals()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var v = 1 [||]+ ("string");
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var v = $"{1}{"string"}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37324")]
    public async Task TestConcatenationWithChar()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            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}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37324")]
    public async Task TestConcatenationWithCharAfterStringLiteral()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var world = "world";
                    var str = "hello" [||]+ ' ' + world;
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var world = "world";
                    var str = $"hello {world}";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37324")]
    public async Task TestConcatenationWithCharBeforeStringLiteral()
    {
        await VerifyCS.VerifyRefactoringAsync(
            """
            public class C
            {
                void M()
                {
                    var hello = "hello";
                    var str = hello [||]+ ' ' + "world";
                }
            }
            """,
            """
            public class C
            {
                void M()
                {
                    var hello = "hello";
                    var str = $"{hello} world";
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40413")]
    public async Task TestConcatenationWithConstMemberCSharp9()
    {
        // lang=c#-test
        var code = """
            class C
            {
                const string Hello = "Hello";
                const string World = "World";
                const string Message = Hello + " " + [||]World;
            }
            """;
        await new VerifyCS.Test
        {
            LanguageVersion = LanguageVersion.CSharp9,
            TestCode = code,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40413")]
    public async Task TestConcatenationWithConstMember()
    {
        await new VerifyCS.Test
        {
            LanguageVersion = LanguageVersion.Preview,
            TestCode = """
                class C
                {
                    const string Hello = "Hello";
                    const string World = "World";
                    const string Message = Hello + " " + [||]World;
                }
                """,
            FixedCode = """
                class C
                {
                    const string Hello = "Hello";
                    const string World = "World";
                    const string Message = $"{Hello} {World}";
                }
                """,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40413")]
    public async Task TestConcatenationWithConstDeclaration()
    {
        var code = """
            class C
            {
                void M() {
                    const string Hello = "Hello";
                    const string World = "World";
                    const string Message = Hello + " " + [||]World;
                }
            }
            """;
        var fixedCode = """
            class C
            {
                void M() {
                    const string Hello = "Hello";
                    const string World = "World";
                    const string Message = $"{Hello} {World}";
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            LanguageVersion = LanguageVersion.CSharp9,
            TestCode = code,
        }.RunAsync();
 
        await new VerifyCS.Test
        {
            LanguageVersion = LanguageVersion.Preview,
            TestCode = code,
            FixedCode = fixedCode,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40413")]
    public async Task TestConcatenationWithInlineString()
    {
        await VerifyCS.VerifyRefactoringAsync("""
            using System;
            class C
            {
                void M() {
                    const string Hello = "Hello";
                    const string World = "World";
                    Console.WriteLine(Hello + " " + [||]World);
                }
            }
            """,
            """
            using System;
            class C
            {
                void M() {
                    const string Hello = "Hello";
                    const string World = "World";
                    Console.WriteLine($"{Hello} {World}");
                }
            }
            """);
    }
 
    [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/49229")]
    [InlineData(@"[|""a"" + $""{1:000}""|]",
                 """
                 $"a{1:000}"
                 """)]
    [InlineData(@"[|""a"" + $""b{1:000}""|]",
                 """
                 $"ab{1:000}"
                 """)]
    [InlineData(@"[|$""a{1:000}"" + ""b""|]",
                 """
                 $"a{1:000}b"
                 """)]
    [InlineData(@"[|""a"" + $""b{1:000}c"" + ""d""|]",
                 """
                 $"ab{1:000}cd"
                 """)]
    [InlineData(@"[|""a"" + $""{1:000}b"" + ""c""|]",
                 """
                 $"a{1:000}bc"
                 """)]
    [InlineData(@"[|""a"" + $""{1:000}"" + $""{2:000}"" + ""b""|]",
                 """
                 $"a{1:000}{2:000}b"
                 """)]
    [InlineData(@"[|@""a"" + @$""{1:000}""|]",
                 """
                 $@"a{1:000}"
                 """)]
    [InlineData(@"[|@""a"" + $""{1:000}""|]",
                 """
                 $@"a{$"{1:000}"}"
                 """)]
    [InlineData(@"[|""a"" + @$""{1:000}""|]",
                 """
                 $"a{@$"{1:000}"}"
                 """)]
    public async Task TestInliningOfInterpolatedString(string before, string after)
    {
        var initialMarkup = $@"
class C
{{
    void M() {{
        _ = {before};
    }}
}}";
        var expected = $@"
class C
{{
    void M() {{
        _ = {after};
    }}
}}";
        await VerifyCS.VerifyRefactoringAsync(initialMarkup, expected);
    }
 
    [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/49229")]
    [InlineData("""
        "\t" [|+|] 1
        """,
        """
        $"\t{1}"
        """)]
    [InlineData("""
        "😀" [|+|] 1
        """,
        """
        $"😀{1}"
        """)]
    [InlineData("""
        "\u2764" [|+|] 1
        """,
        """
        $"\u2764{1}"
        """)]
    [InlineData("""
        "\"" [|+|] 1
        """,
        """
        $"\"{1}"
        """)]
    [InlineData("""
        "{}" [|+|] 1
        """,
        """
        $"{{}}{1}"
        """)]
    public async Task TestUnicodeAndEscapeHandling(string before, string after)
    {
        var initialMarkup = $$"""
            class C
            {
                void M() {
                    _ = {{before}};
                }
            }
            """;
        var expected = $$"""
            class C
            {
                void M() {
                    _ = {{after}};
                }
            }
            """;
        await VerifyCS.VerifyRefactoringAsync(initialMarkup, expected);
    }
 
    [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/49229")]
    [InlineData("""
        "a" [|+|] (1 + 1)
        """,
        """
        $"a{1 + 1}"
        """)]
    [InlineData("""
        "a" [||]+ (1 + 1) + "b" + (2 + 2)
        """,
        """
        $"a{1 + 1}b{2 + 2}"
        """)]
    [InlineData("""
        "a" [|+|] (true ? "t" : "f")
        """,
        """
        $"a{(true ? "t" : "f")}"
        """)]
    [InlineData("""
        "a" [|+|] $"{(1 + 1)}"
        """,
        """
        $"a{(1 + 1)}"
        """)]
    public async Task TestRemovalOfSuperflousParenthesis(string before, string after)
    {
        var initialMarkup = $$"""
            class C
            {
                void M() {
                    _ = {{before}};
                }
            }
            """;
        var expected = $$"""
            class C
            {
                void M() {
                    _ = {{after}};
                }
            }
            """;
        await VerifyCS.VerifyRefactoringAsync(initialMarkup, expected);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69721")]
    public async Task TestToString1()
    {
        await new VerifyCS.Test
        {
            TestCode = """
                struct ValueTuple<T>
                {
                    public T Item1;
                    public T Item2;
 
                    public override string ToString()
                    {
                        return [||]"(" + Item1.ToString() + ", " + Item2.ToString() + ")";
                    }
                }
                """,
            FixedCode = """
                struct ValueTuple<T>
                {
                    public T Item1;
                    public T Item2;
                
                    public override string ToString()
                    {
                        return $"({Item1.ToString()}, {Item2.ToString()})";
                    }
                }
                """,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69721")]
    public async Task TestToString1_Net6()
    {
        await new VerifyCS.Test
        {
            TestCode = """
                struct ValueTuple<T>
                {
                    public T Item1;
                    public T Item2;
 
                    public override string ToString()
                    {
                        return [||]"(" + Item1.ToString() + ", " + Item2.ToString() + ")";
                    }
                }
                """,
            FixedCode = """
                struct ValueTuple<T>
                {
                    public T Item1;
                    public T Item2;
                
                    public override string ToString()
                    {
                        return $"({Item1}, {Item2})";
                    }
                }
                """,
            ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69721")]
    public async Task TestToString2()
    {
        await new VerifyCS.Test
        {
            TestCode = """
                struct ValueTuple<T>
                {
                    public override string ToString()
                    {
                        return [||]"(" + (1 + 1).ToString() + ", " + (2 + 2).ToString() + ")";
                    }
                }
                """,
            FixedCode = """
                struct ValueTuple<T>
                {
                    public override string ToString()
                    {
                        return $"({(1 + 1).ToString()}, {(2 + 2).ToString()})";
                    }
                }
                """,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69721")]
    public async Task TestToString2_Net6()
    {
        await new VerifyCS.Test
        {
            TestCode = """
                struct ValueTuple<T>
                {
                    public override string ToString()
                    {
                        return [||]"(" + (1 + 1).ToString() + ", " + (2 + 2).ToString() + ")";
                    }
                }
                """,
            FixedCode = """
                struct ValueTuple<T>
                {
                    public override string ToString()
                    {
                        return $"({1 + 1}, {2 + 2})";
                    }
                }
                """,
            ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
        }.RunAsync();
    }
}