File: CodeGen\CodeGenInterpolatedString.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit\Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit.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.
 
#nullable disable
 
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
    public class InterpolatedStringTests : CSharpTestBase
    {
        [Fact, WorkItem(33713, "https://github.com/dotnet/roslyn/issues/33713")]
        public void AlternateVerbatimString()
        {
            var source = @"
class C
{
    static void Main()
    {
        int i = 42;
        var s = @$""{i}
{i}"";
        System.Console.Write(s);
        var s2 = $@"""";
    }
}";
            var comp = CreateCompilation(source, options: TestOptions.ReleaseExe);
            CompileAndVerify(comp, expectedOutput: @"42
42");
 
            var tree = comp.SyntaxTrees.Single();
            var interpolatedStrings = tree.GetRoot().DescendantNodes().OfType<InterpolatedStringExpressionSyntax>().ToArray();
            var token1 = interpolatedStrings[0].StringStartToken;
            Assert.Equal("@$\"", token1.Text);
            Assert.Equal("@$\"", token1.ValueText);
 
            var token2 = interpolatedStrings[1].StringStartToken;
            Assert.Equal("$@\"", token2.Text);
            Assert.Equal("$@\"", token2.ValueText);
 
            foreach (var token in tree.GetRoot().DescendantTokens().Where(t => t.Kind() != SyntaxKind.EndOfFileToken))
            {
                Assert.False(string.IsNullOrEmpty(token.Text));
                Assert.False(string.IsNullOrEmpty(token.ValueText));
            }
        }
 
        [Fact]
        public void ConstInterpolations()
        {
            var source = @"
using System;
 
public class Test
{
    const string constantabc = ""abc"";
    const string constantnull = null;
 
    static void Main()
    {
        Console.WriteLine($"""");
        Console.WriteLine($""ABC"");
        Console.WriteLine($""{constantabc}"");
        Console.WriteLine($""{constantnull}"");
        Console.WriteLine($""{constantabc}{constantnull}"");
        Console.WriteLine($""({constantabc})({constantnull})"");
    }
}
";
            var comp = CompileAndVerify(source, expectedOutput: @"
ABC
abc
 
abc
(abc)()
");
 
            comp.VerifyDiagnostics();
            comp.VerifyIL("Test.Main", @"
{
  // Code size       61 (0x3d)
  .maxstack  1
  IL_0000:  ldstr      """"
  IL_0005:  call       ""void System.Console.WriteLine(string)""
  IL_000a:  ldstr      ""ABC""
  IL_000f:  call       ""void System.Console.WriteLine(string)""
  IL_0014:  ldstr      ""abc""
  IL_0019:  call       ""void System.Console.WriteLine(string)""
  IL_001e:  ldstr      """"
  IL_0023:  call       ""void System.Console.WriteLine(string)""
  IL_0028:  ldstr      ""abc""
  IL_002d:  call       ""void System.Console.WriteLine(string)""
  IL_0032:  ldstr      ""(abc)()""
  IL_0037:  call       ""void System.Console.WriteLine(string)""
  IL_003c:  ret
}
");
        }
 
        [Fact]
        public void InterpolatedStringIsNeverNull()
        {
            var source = @"
using System;
 
public class Test
{
    static void Main()
    {
        Console.WriteLine(M6(null) == """");
        Console.WriteLine(M6(""abc""));
    }
 
    static string M6(string a) => $""{a}"";
 
}
";
            var comp = CompileAndVerify(source, expectedOutput: @"True
abc
");
 
            comp.VerifyDiagnostics();
            comp.VerifyIL("Test.M6", @"
{
  // Code size       11 (0xb)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  brtrue.s   IL_000a
  IL_0004:  pop
  IL_0005:  ldstr      """"
  IL_000a:  ret
}
");
        }
 
        [Fact]
        public void ConcatenatedStringInterpolations()
        {
            var source = @"
using System;
 
public class Test
{
    static void Main()
    {
        string a = ""a"";
        string b = ""b"";
 
        Console.WriteLine($""a: {a}"");
        Console.WriteLine($""{a + b}"");
        Console.WriteLine($""{a}{b}"");
        Console.WriteLine($""a: {a}, b: {b}"");
        Console.WriteLine($""{{{a}}}"");
        Console.WriteLine(""a:"" + $"" {a}"");
        Console.WriteLine($""a: {$""{a}, b: {b}""}"");
    }
}
";
            var comp = CompileAndVerify(source, expectedOutput: @"a: a
ab
ab
a: a, b: b
{a}
a: a
a: a, b: b
");
 
            comp.VerifyDiagnostics();
            comp.VerifyIL("Test.Main", @"
{
  // Code size      134 (0x86)
  .maxstack  4
  .locals init (string V_0, //a
                string V_1) //b
  IL_0000:  ldstr      ""a""
  IL_0005:  stloc.0
  IL_0006:  ldstr      ""b""
  IL_000b:  stloc.1
  IL_000c:  ldstr      ""a: ""
  IL_0011:  ldloc.0
  IL_0012:  call       ""string string.Concat(string, string)""
  IL_0017:  call       ""void System.Console.WriteLine(string)""
  IL_001c:  ldloc.0
  IL_001d:  ldloc.1
  IL_001e:  call       ""string string.Concat(string, string)""
  IL_0023:  call       ""void System.Console.WriteLine(string)""
  IL_0028:  ldloc.0
  IL_0029:  ldloc.1
  IL_002a:  call       ""string string.Concat(string, string)""
  IL_002f:  call       ""void System.Console.WriteLine(string)""
  IL_0034:  ldstr      ""a: ""
  IL_0039:  ldloc.0
  IL_003a:  ldstr      "", b: ""
  IL_003f:  ldloc.1
  IL_0040:  call       ""string string.Concat(string, string, string, string)""
  IL_0045:  call       ""void System.Console.WriteLine(string)""
  IL_004a:  ldstr      ""{""
  IL_004f:  ldloc.0
  IL_0050:  ldstr      ""}""
  IL_0055:  call       ""string string.Concat(string, string, string)""
  IL_005a:  call       ""void System.Console.WriteLine(string)""
  IL_005f:  ldstr      ""a: ""
  IL_0064:  ldloc.0
  IL_0065:  call       ""string string.Concat(string, string)""
  IL_006a:  call       ""void System.Console.WriteLine(string)""
  IL_006f:  ldstr      ""a: ""
  IL_0074:  ldloc.0
  IL_0075:  ldstr      "", b: ""
  IL_007a:  ldloc.1
  IL_007b:  call       ""string string.Concat(string, string, string, string)""
  IL_0080:  call       ""void System.Console.WriteLine(string)""
  IL_0085:  ret
}
");
        }
 
        [Fact]
        public void NonConcatenatedInterpolations()
        {
            var source = @"
using System;
 
public class Test
{
    static void Main()
    {
        object a = ""a"";
 
        Console.WriteLine($""{a}"");
        Console.WriteLine($""a: {a}"");
    }
}
";
            var comp = CompileAndVerify(source, expectedOutput: @"a
a: a
");
 
            comp.VerifyDiagnostics();
            comp.VerifyIL("Test.Main", @"
{
  // Code size       39 (0x27)
  .maxstack  2
  .locals init (object V_0) //a
  IL_0000:  ldstr      ""a""
  IL_0005:  stloc.0
  IL_0006:  ldstr      ""{0}""
  IL_000b:  ldloc.0
  IL_000c:  call       ""string string.Format(string, object)""
  IL_0011:  call       ""void System.Console.WriteLine(string)""
  IL_0016:  ldstr      ""a: {0}""
  IL_001b:  ldloc.0
  IL_001c:  call       ""string string.Format(string, object)""
  IL_0021:  call       ""void System.Console.WriteLine(string)""
  IL_0026:  ret
}
");
        }
 
        [Fact]
        public void ExpressionsAreNotOptimized()
        {
            var source = @"
using System;
using System.Linq.Expressions;
 
public class Test
{
    static void Main()
    {
        Expression<Func<string, string>> f = a => $""a: {a}"";
 
        Console.Write(f);
    }
}
";
            var comp = CompileAndVerify(source, expectedOutput: @"a => Format(""a: {0}"", a)");
 
            comp.VerifyDiagnostics();
        }
    }
}