/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs
19 735 строк
773 KB
Julien Couvreur
Extension indexers: interpolation handler follow-ups (#82218)
06 фев 2026, 09:04
Не верифицирован
06 фев 2026, 09:04
5f33f0d
Код
Авторство
О чём код?
// 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.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class InterpolationTests : CompilingTestBase { [Fact] public void TestSimpleInterp() { string source = @"using System; class Program { public static void Main(string[] args) { var number = 8675309; Console.WriteLine($""Jenny don\'t change your number { number }.""); Console.WriteLine($""Jenny don\'t change your number { number , -12 }.""); Console.WriteLine($""Jenny don\'t change your number { number , 12 }.""); Console.WriteLine($""Jenny don\'t change your number { number :###-####}.""); Console.WriteLine($""Jenny don\'t change your number { number , -12 :###-####}.""); Console.WriteLine($""Jenny don\'t change your number { number , 12 :###-####}.""); Console.WriteLine($""{number}""); } }"; string expectedOutput = @"Jenny don't change your number 8675309. Jenny don't change your number 8675309 . Jenny don't change your number 8675309. Jenny don't change your number 867-5309. Jenny don't change your number 867-5309 . Jenny don't change your number 867-5309. 8675309"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestOnlyInterp() { string source = @"using System; class Program { public static void Main(string[] args) { var number = 8675309; Console.WriteLine($""{number}""); } }"; string expectedOutput = @"8675309"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestDoubleInterp01() { string source = @"using System; class Program { public static void Main(string[] args) { var number = 8675309; Console.WriteLine($""{number}{number}""); } }"; string expectedOutput = @"86753098675309"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestDoubleInterp02() { string source = @"using System; class Program { public static void Main(string[] args) { var number = 8675309; Console.WriteLine($""Jenny don\'t change your number { number :###-####} { number :###-####}.""); } }"; string expectedOutput = @"Jenny don't change your number 867-5309 867-5309."; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestEmptyInterp() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""Jenny don\'t change your number { /*trash*/ }.""); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (5,73): error CS1733: Expected expression // Console.WriteLine("Jenny don\'t change your number \{ /*trash*/ }."); Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 73) ); } [Fact] public void TestHalfOpenInterp01() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""Jenny don\'t change your number { ""); } }"; // too many diagnostics perhaps, but it starts the right way. CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (5,63): error CS1010: Newline in constant // Console.WriteLine($"Jenny don\'t change your number { "); Diagnostic(ErrorCode.ERR_NewlineInConst, "").WithLocation(5, 63), // (6,5): error CS1039: Unterminated string literal // } Diagnostic(ErrorCode.ERR_UnterminatedStringLit, "}").WithLocation(6, 5), // (6,6): error CS1026: ) expected // } Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(6, 6), // (6,6): error CS1002: ; expected // } Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 6), // (7,2): error CS1513: } expected // } Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(7, 2)); } [Fact] public void TestHalfOpenInterp02() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""Jenny don\'t change your number { 8675309 // ""); } }"; // too many diagnostics perhaps, but it starts the right way. CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,5): error CS1039: Unterminated string literal // } Diagnostic(ErrorCode.ERR_UnterminatedStringLit, "}").WithLocation(6, 5), // (6,6): error CS1026: ) expected // } Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(6, 6), // (6,6): error CS1002: ; expected // } Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 6), // (7,2): error CS1513: } expected // } Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(7, 2)); } [Fact] public void TestHalfOpenInterp03() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""Jenny don\'t change your number { 8675309 /* ""); } }"; // too many diagnostics perhaps, but it starts the right way. CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (5,60): error CS8076: Missing close delimiter '}' for interpolated expression started with '{'. // Console.WriteLine($"Jenny don\'t change your number { 8675309 /* "); Diagnostic(ErrorCode.ERR_UnclosedExpressionHole, " {").WithLocation(5, 60), // (5,71): error CS1035: End-of-file found, '*/' expected // Console.WriteLine($"Jenny don\'t change your number { 8675309 /* "); Diagnostic(ErrorCode.ERR_OpenEndedComment, "").WithLocation(5, 71), // (7,2): error CS1026: ) expected // } Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(7, 2), // (7,2): error CS1002: ; expected // } Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(7, 2), // (7,2): error CS1513: } expected // } Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(7, 2), // (7,2): error CS1513: } expected // } Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(7, 2)); } [Fact] public void LambdaInInterp() { string source = @"using System; class Program { static void Main(string[] args) { //Console.WriteLine(""jenny {0:(408) ###-####}"", new object[] { ((Func<int>)(() => { return number; })).Invoke() }); Console.WriteLine($""jenny { ((Func<int>)(() => { return number; })).Invoke() :(408) ###-####}""); } static int number = 8675309; } "; string expectedOutput = @"jenny (408) 867-5309"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void OneLiteral() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $""Hello"" ); } }"; string expectedOutput = @"Hello"; var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); verifier.VerifyIL("Program.Main", @" { // Code size 11 (0xb) .maxstack 1 IL_0000: ldstr ""Hello"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ret } "); } [Fact] public void OneInsert() { string source = @"using System; class Program { static void Main(string[] args) { var hello = $""Hello""; Console.WriteLine( $""{hello}"" ); } }"; string expectedOutput = @"Hello"; var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); verifier.VerifyIL("Program.Main", @" { // Code size 20 (0x14) .maxstack 2 IL_0000: ldstr ""Hello"" IL_0005: dup IL_0006: brtrue.s IL_000e IL_0008: pop IL_0009: ldstr """" IL_000e: call ""void System.Console.WriteLine(string)"" IL_0013: ret } "); } [Fact] public void TwoInserts() { string source = @"using System; class Program { static void Main(string[] args) { var hello = $""Hello""; var world = $""world"" ; Console.WriteLine( $""{hello}, { world }."" ); } }"; string expectedOutput = @"Hello, world."; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TwoInserts02() { string source = @"using System; class Program { static void Main(string[] args) { var hello = $""Hello""; var world = $""world"" ; Console.WriteLine( $@""{ hello }, { world }."" ); } }"; string expectedOutput = @"Hello, world."; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact, WorkItem(306, "https://github.com/dotnet/roslyn/issues/306"), WorkItem(308, "https://github.com/dotnet/roslyn/issues/308")] public void DynamicInterpolation() { string source = @"using System; using System.Linq.Expressions; class Program { static void Main(string[] args) { dynamic nil = null; dynamic a = new string[] {""Hello"", ""world""}; Console.WriteLine($""<{nil}>""); Console.WriteLine($""<{a}>""); } Expression<Func<string>> M(dynamic d) { return () => $""Dynamic: {d}""; } }"; string expectedOutput = @"<> <System.String[]>"; var verifier = CompileAndVerify(source, new[] { CSharpRef }, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] public void UnclosedInterpolation01() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $""{"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,31): error CS1010: Newline in constant // Console.WriteLine( $"{" ); Diagnostic(ErrorCode.ERR_NewlineInConst, "").WithLocation(6, 31), // (7,5): error CS1039: Unterminated string literal // } Diagnostic(ErrorCode.ERR_UnterminatedStringLit, "}").WithLocation(7, 5), // (7,6): error CS1026: ) expected // } Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(7, 6), // (7,6): error CS1002: ; expected // } Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(7, 6), // (8,2): error CS1513: } expected // } Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(8, 2)); } [Fact] public void UnclosedInterpolation02() { string source = @"class Program { static void Main(string[] args) { var x = $"";"; // The precise error messages are not important, but this must be an error. CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (5,19): error CS1039: Unterminated string literal // var x = $"; Diagnostic(ErrorCode.ERR_UnterminatedStringLit, ";").WithLocation(5, 19), // (5,20): error CS1002: ; expected // var x = $"; Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 20), // (5,20): error CS1513: } expected // var x = $"; Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 20), // (5,20): error CS1513: } expected // var x = $"; Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 20)); } [Fact] public void EmptyFormatSpecifier() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $""{3:}"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,32): error CS8089: Empty format specifier. // Console.WriteLine( $"{3:}" ); Diagnostic(ErrorCode.ERR_EmptyFormatSpecifier, ":").WithLocation(6, 32) ); } [Fact] public void InvalidCharInFormatSpecifier() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $""{3:{}"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,33): error CS1056: Unerwartetes Zeichen "{". // Console.WriteLine( $"{3:{}" ); Diagnostic(ErrorCode.ERR_UnexpectedCharacter, "{").WithArguments("{").WithLocation(6, 33) ); } [Fact] public void TrailingSpaceInFormatSpecifier() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $""{3:d }"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,32): error CS8088: A format specifier may not contain trailing whitespace. // Console.WriteLine( $"{3:d }" ); Diagnostic(ErrorCode.ERR_TrailingWhitespaceInFormatSpecifier, ":d ").WithLocation(6, 32) ); } [Fact] public void TrailingSpaceInFormatSpecifier02() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $@""{3:d }"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,33): error CS8088: A format specifier may not contain trailing whitespace. // Console.WriteLine( $@"{3:d Diagnostic(ErrorCode.ERR_TrailingWhitespaceInFormatSpecifier, @":d ").WithLocation(6, 33) ); } [Fact] public void MissingInterpolationExpression01() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $""{ }"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,32): error CS1733: Expected expression // Console.WriteLine( $"{ }" ); Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(6, 32) ); } [Fact] public void MissingInterpolationExpression02() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( $@""{ }"" ); } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,33): error CS1733: Expected expression // Console.WriteLine( $@"{ }" ); Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(6, 33) ); } [Fact] public void MissingInterpolationExpression03() { string source = @"using System; class Program { static void Main(string[] args) { Console.WriteLine( "; var normal = "$\""; var verbat = "$@\""; // ensure reparsing of interpolated string token is precise in error scenarios (assertions do not fail) Assert.True(SyntaxFactory.ParseSyntaxTree(source + normal).GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + normal + " ").GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + normal + "{").GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + normal + "{ ").GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + verbat).GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + verbat + " ").GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + verbat + "{").GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); Assert.True(SyntaxFactory.ParseSyntaxTree(source + verbat + "{ ").GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); } [Fact] public void MisplacedNewline01() { string source = @"using System; class Program { static void Main(string[] args) { var s = $""{ @"" "" } ""; } }"; // The precise error messages are not important, but this must be an error. Assert.True(SyntaxFactory.ParseSyntaxTree(source).GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); } [Fact] public void MisplacedNewline02() { string source = @"using System; class Program { static void Main(string[] args) { var s = $""{ @"" ""} ""; } }"; // The precise error messages are not important, but this must be an error. Assert.True(SyntaxFactory.ParseSyntaxTree(source).GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); } [Fact] public void PreprocessorInsideInterpolation() { string source = @"class Program { static void Main() { var s = $@""{ #region : #endregion 0 }""; } }"; // The precise error messages are not important, but this must be an error. Assert.True(SyntaxFactory.ParseSyntaxTree(source).GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); } [Fact] public void EscapedCurly() { string source = @"class Program { static void Main() { var s1 = $"" \u007B ""; var s2 = $"" \u007D""; } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (5,21): error CS8087: A '{' character may only be escaped by doubling '{{' in an interpolated string. // var s1 = $" \u007B "; Diagnostic(ErrorCode.ERR_EscapedCurly, @"\u007B").WithArguments("{").WithLocation(5, 21), // (6,21): error CS8087: A '}' character may only be escaped by doubling '}}' in an interpolated string. // var s2 = $" \u007D"; Diagnostic(ErrorCode.ERR_EscapedCurly, @"\u007D").WithArguments("}").WithLocation(6, 21) ); } [Fact, WorkItem(1119878, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1119878")] public void NoFillIns01() { string source = @"class Program { static void Main() { System.Console.Write($""{{ x }}""); System.Console.WriteLine($@""This is a test""); } }"; string expectedOutput = @"{ x }This is a test"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void BadAlignment() { string source = @"class Program { static void Main() { var s = $""{1,1E10}""; var t = $""{1,(int)1E10}""; } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (5,22): error CS0266: Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?) // var s = $"{1,1E10}"; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "1E10").WithArguments("double", "int").WithLocation(5, 22), // (5,22): error CS0150: A constant value is expected // var s = $"{1,1E10}"; Diagnostic(ErrorCode.ERR_ConstantExpected, "1E10").WithLocation(5, 22), // (6,22): error CS0221: Constant value '10000000000' cannot be converted to a 'int' (use 'unchecked' syntax to override) // var t = $"{1,(int)1E10}"; Diagnostic(ErrorCode.ERR_ConstOutOfRangeChecked, "(int)1E10").WithArguments("10000000000", "int").WithLocation(6, 22), // (6,22): error CS0150: A constant value is expected // var t = $"{1,(int)1E10}"; Diagnostic(ErrorCode.ERR_ConstantExpected, "(int)1E10").WithLocation(6, 22) ); } [Fact] public void NestedInterpolatedVerbatim() { string source = @"using System; class Program { static void Main(string[] args) { var s = $@""{$@""{1}""}""; Console.WriteLine(s); } }"; string expectedOutput = @"1"; CompileAndVerify(source, expectedOutput: expectedOutput); } // Since the platform type System.FormattableString is not yet in our platforms (at the // time of writing), we explicitly include the required platform types into the sources under test. private const string formattableString = @" /*============================================================ ** ** Class: FormattableString ** ** ** Purpose: implementation of the FormattableString ** class. ** ===========================================================*/ namespace System { /// <summary> /// A composite format string along with the arguments to be formatted. An instance of this /// type may result from the use of the C# or VB language primitive ""interpolated string"". /// </summary> public abstract class FormattableString : IFormattable { /// <summary> /// The composite format string. /// </summary> public abstract string Format { get; } /// <summary> /// Returns an object array that contains zero or more objects to format. Clients should not /// mutate the contents of the array. /// </summary> public abstract object[] GetArguments(); /// <summary> /// The number of arguments to be formatted. /// </summary> public abstract int ArgumentCount { get; } /// <summary> /// Returns one argument to be formatted from argument position <paramref name=""index""/>. /// </summary> public abstract object GetArgument(int index); /// <summary> /// Format to a string using the given culture. /// </summary> public abstract string ToString(IFormatProvider formatProvider); string IFormattable.ToString(string ignored, IFormatProvider formatProvider) { return ToString(formatProvider); } /// <summary> /// Format the given object in the invariant culture. This static method may be /// imported in C# by /// <code> /// using static System.FormattableString; /// </code>. /// Within the scope /// of that import directive an interpolated string may be formatted in the /// invariant culture by writing, for example, /// <code> /// Invariant($""{{ lat = {latitude}; lon = {longitude} }}"") /// </code> /// </summary> public static string Invariant(FormattableString formattable) { if (formattable == null) { throw new ArgumentNullException(""formattable""); } return formattable.ToString(Globalization.CultureInfo.InvariantCulture); } public override string ToString() { return ToString(Globalization.CultureInfo.CurrentCulture); } } } /*============================================================ ** ** Class: FormattableStringFactory ** ** ** Purpose: implementation of the FormattableStringFactory ** class. ** ===========================================================*/ namespace System.Runtime.CompilerServices { /// <summary> /// A factory type used by compilers to create instances of the type <see cref=""FormattableString""/>. /// </summary> public static class FormattableStringFactory { /// <summary> /// Create a <see cref=""FormattableString""/> from a composite format string and object /// array containing zero or more objects to format. /// </summary> public static FormattableString Create(string format, params object[] arguments) { if (format == null) { throw new ArgumentNullException(""format""); } if (arguments == null) { throw new ArgumentNullException(""arguments""); } return new ConcreteFormattableString(format, arguments); } private sealed class ConcreteFormattableString : FormattableString { private readonly string _format; private readonly object[] _arguments; internal ConcreteFormattableString(string format, object[] arguments) { _format = format; _arguments = arguments; } public override string Format { get { return _format; } } public override object[] GetArguments() { return _arguments; } public override int ArgumentCount { get { return _arguments.Length; } } public override object GetArgument(int index) { return _arguments[index]; } public override string ToString(IFormatProvider formatProvider) { return string.Format(formatProvider, Format, _arguments); } } } } "; [Fact] public void TargetType01() { string source = @"using System; class Program { public static void Main(string[] args) { IFormattable f = $""test""; Console.Write(f is System.FormattableString); } }"; CompileAndVerify(source + formattableString, expectedOutput: "True"); } [Fact] public void TargetType02() { string source = @"using System; interface I1 { void M(String s); } interface I2 { void M(FormattableString s); } interface I3 { void M(IFormattable s); } interface I4 : I1, I2 {} interface I5 : I1, I3 {} interface I6 : I2, I3 {} interface I7 : I1, I2, I3 {} class C : I1, I2, I3, I4, I5, I6, I7 { public void M(String s) { Console.Write(1); } public void M(FormattableString s) { Console.Write(2); } public void M(IFormattable s) { Console.Write(3); } } class Program { public static void Main(string[] args) { C c = new C(); ((I1)c).M($""""); ((I2)c).M($""""); ((I3)c).M($""""); ((I4)c).M($""""); ((I5)c).M($""""); ((I6)c).M($""""); ((I7)c).M($""""); ((C)c).M($""""); } }"; CompileAndVerify(source + formattableString, expectedOutput: "12311211"); } [Fact] public void MissingHelper() { string source = @"using System; class Program { public static void Main(string[] args) { IFormattable f = $""test""; } }"; CreateCompilationWithMscorlib40(source).VerifyEmitDiagnostics( // (5,26): error CS0518: Predefined type 'System.Runtime.CompilerServices.FormattableStringFactory' is not defined or imported // IFormattable f = $"test"; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"$""test""").WithArguments("System.Runtime.CompilerServices.FormattableStringFactory").WithLocation(5, 26) ); } [Fact] public void AsyncInterp() { string source = @"using System; using System.Threading.Tasks; class Program { public static void Main(string[] args) { Task<string> hello = Task.FromResult(""Hello""); Task<string> world = Task.FromResult(""world""); M(hello, world).Wait(); } public static async Task M(Task<string> hello, Task<string> world) { Console.WriteLine($""{ await hello }, { await world }!""); } }"; CompileAndVerify( source, references: new[] { MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929 }, expectedOutput: "Hello, world!", targetFramework: TargetFramework.Empty); } [Fact] public void AlignmentExpression() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""X = { 123 , -(3+4) }.""); } }"; CompileAndVerify(source + formattableString, expectedOutput: "X = 123 ."); } [Fact] public void AlignmentMagnitude() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""X = { 123 , (32768) }.""); Console.WriteLine($""X = { 123 , -(32768) }.""); Console.WriteLine($""X = { 123 , (32767) }.""); Console.WriteLine($""X = { 123 , -(32767) }.""); Console.WriteLine($""X = { 123 , int.MaxValue }.""); Console.WriteLine($""X = { 123 , int.MinValue }.""); } }"; CreateCompilation(source).VerifyDiagnostics( // (5,42): warning CS8094: Alignment value 32768 has a magnitude greater than 32767 and may result in a large formatted string. // Console.WriteLine($"X = { 123 , (32768) }."); Diagnostic(ErrorCode.WRN_AlignmentMagnitude, "32768").WithArguments("32768", "32767").WithLocation(5, 42), // (6,41): warning CS8094: Alignment value -32768 has a magnitude greater than 32767 and may result in a large formatted string. // Console.WriteLine($"X = { 123 , -(32768) }."); Diagnostic(ErrorCode.WRN_AlignmentMagnitude, "-(32768)").WithArguments("-32768", "32767").WithLocation(6, 41), // (9,41): warning CS8094: Alignment value 2147483647 has a magnitude greater than 32767 and may result in a large formatted string. // Console.WriteLine($"X = { 123 , int.MaxValue }."); Diagnostic(ErrorCode.WRN_AlignmentMagnitude, "int.MaxValue").WithArguments("2147483647", "32767").WithLocation(9, 41), // (10,41): warning CS8094: Alignment value -2147483648 has a magnitude greater than 32767 and may result in a large formatted string. // Console.WriteLine($"X = { 123 , int.MinValue }."); Diagnostic(ErrorCode.WRN_AlignmentMagnitude, "int.MinValue").WithArguments("-2147483648", "32767").WithLocation(10, 41) ); } [WorkItem(1097388, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097388")] [Fact] public void InterpolationExpressionMustBeValue01() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""X = { String }.""); Console.WriteLine($""X = { null }.""); } }"; CreateCompilation(source).VerifyDiagnostics( // (5,35): error CS0119: 'string' is a type, which is not valid in the given context // Console.WriteLine($"X = { String }."); Diagnostic(ErrorCode.ERR_BadSKunknown, "String").WithArguments("string", "type").WithLocation(5, 35) ); } [Fact] public void InterpolationExpressionMustBeValue02() { string source = @"using System; class Program { public static void Main(string[] args) { Console.WriteLine($""X = { x=>3 }.""); Console.WriteLine($""X = { Program.Main }.""); Console.WriteLine($""X = { Program.Main(null) }.""); } }"; CreateCompilation(source, parseOptions: TestOptions.Regular9).VerifyDiagnostics( // (5,36): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type // Console.WriteLine($"X = { x=>3 }."); Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "=>").WithArguments("lambda expression", "object").WithLocation(5, 36), // (6,43): error CS0428: Cannot convert method group 'Main' to non-delegate type 'object'. Did you intend to invoke the method? // Console.WriteLine($"X = { Program.Main }."); Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Main").WithArguments("Main", "object").WithLocation(6, 43), // (7,35): error CS0029: Cannot implicitly convert type 'void' to 'object' // Console.WriteLine($"X = { Program.Main(null) }."); Diagnostic(ErrorCode.ERR_NoImplicitConv, "Program.Main(null)").WithArguments("void", "object").WithLocation(7, 35)); CreateCompilation(source).VerifyDiagnostics( // (5,36): error CS8917: The delegate type could not be inferred. // Console.WriteLine($"X = { x=>3 }."); Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "=>").WithLocation(5, 36), // (6,35): warning CS8974: Converting method group 'Main' to non-delegate type 'object'. Did you intend to invoke the method? // Console.WriteLine($"X = { Program.Main }."); Diagnostic(ErrorCode.WRN_MethGrpToNonDel, "Program.Main").WithArguments("Main", "object").WithLocation(6, 35), // (7,35): error CS0029: Cannot implicitly convert type 'void' to 'object' // Console.WriteLine($"X = { Program.Main(null) }."); Diagnostic(ErrorCode.ERR_NoImplicitConv, "Program.Main(null)").WithArguments("void", "object").WithLocation(7, 35)); } [WorkItem(1097428, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097428")] [Fact] public void BadCorelib01() { var text = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } } public struct Int32 { private Int32 m_value; Int32 Use(Int32 b) { m_value = b; return m_value; } } public struct Char { } public class String { } internal class Program { public static void Main() { var s = $""X = { 1 } ""; } } }"; CreateEmptyCompilation(text, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.DebugExe) .VerifyEmitDiagnostics(new CodeAnalysis.Emit.EmitOptions(runtimeMetadataVersion: "x.y"), // (15,21): error CS0117: 'string' does not contain a definition for 'Format' // var s = $"X = { 1 } "; Diagnostic(ErrorCode.ERR_NoSuchMember, @"$""X = { 1 } """).WithArguments("string", "Format").WithLocation(15, 21) ); } [WorkItem(1097428, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097428")] [Fact] public void BadCorelib02() { var text = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } } public struct Int32 { private Int32 m_value; Int32 Use(Int32 b) { m_value = b; return m_value; } } public struct Char { } public class String { public static Boolean Format(string format, int arg) { return true; } } internal class Program { public static void Main() { var s = $""X = { 1 } ""; } } }"; CreateEmptyCompilation(text, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.DebugExe) .VerifyEmitDiagnostics(new CodeAnalysis.Emit.EmitOptions(runtimeMetadataVersion: "x.y"), // (17,21): error CS0029: Cannot implicitly convert type 'bool' to 'string' // var s = $"X = { 1 } "; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""X = { 1 } """).WithArguments("bool", "string").WithLocation(17, 21) ); } [Fact] public void SillyCoreLib01() { var text = @"namespace System { interface IFormattable { } namespace Runtime.CompilerServices { public static class FormattableStringFactory { public static Bozo Create(string format, int arg) { return new Bozo(); } } } public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } } public struct Int32 { private Int32 m_value; Int32 Use(Int32 b) { m_value = b; return m_value; } } public struct Char { } public class String { public static Bozo Format(string format, int arg) { return new Bozo(); } } public class FormattableString { } public class Bozo { public static implicit operator string(Bozo bozo) { return ""zz""; } public static implicit operator FormattableString(Bozo bozo) { return new FormattableString(); } } internal class Program { public static void Main() { var s1 = $""X = { 1 } ""; FormattableString s2 = $""X = { 1 } ""; } } }"; var comp = CreateEmptyCompilation(text, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: Test.Utilities.TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); var compilation = CompileAndVerify(comp, verify: Verification.Fails); compilation.VerifyIL("System.Program.Main", @"{ // Code size 35 (0x23) .maxstack 2 IL_0000: ldstr ""X = {0} "" IL_0005: ldc.i4.1 IL_0006: call ""System.Bozo string.Format(string, int)"" IL_000b: call ""string System.Bozo.op_Implicit(System.Bozo)"" IL_0010: pop IL_0011: ldstr ""X = {0} "" IL_0016: ldc.i4.1 IL_0017: call ""System.Bozo System.Runtime.CompilerServices.FormattableStringFactory.Create(string, int)"" IL_001c: call ""System.FormattableString System.Bozo.op_Implicit(System.Bozo)"" IL_0021: pop IL_0022: ret }"); } [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] #if NET [InlineData(TargetFramework.Net60)] [InlineData(TargetFramework.Net50)] #endif [InlineData(TargetFramework.NetFramework)] [InlineData(TargetFramework.NetStandard20)] [InlineData(TargetFramework.Mscorlib461)] [InlineData(TargetFramework.Mscorlib40)] [Theory] public void InterpolatedStringWithCurlyBracesAndFormatSpecifier(TargetFramework framework) { var text = @"using System; class App{ public static void Main(){ var str = $""Before {{{12:X}}} After""; Console.WriteLine(str); } }"; var parseOptions = new CSharpParseOptions( languageVersion: LanguageVersion.CSharp10, documentationMode: DocumentationMode.Parse, kind: SourceCodeKind.Regular ); var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); //string.Format was fixed in dotnet core 3 var expectedOutput = #if NETCOREAPP3_0_OR_GREATER "Before {C} After" #else "Before {X} After" #endif ; var comp = CreateCompilation(text, targetFramework: framework, parseOptions: parseOptions, options: compOptions); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); switch (framework) { case TargetFramework.Net60: checkNet60IL(verifier); break; default: checkNet50IL(verifier); break; } static void checkNet50IL(CompilationVerifier verifier) { verifier.VerifyIL("App.Main", @"{ // Code size 27 (0x1b) .maxstack 2 .locals init (string V_0) //str IL_0000: nop IL_0001: ldstr ""Before {{{0:X}}} After"" IL_0006: ldc.i4.s 12 IL_0008: box ""int"" IL_000d: call ""string string.Format(string, object)"" IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: call ""void System.Console.WriteLine(string)"" IL_0019: nop IL_001a: ret }"); } static void checkNet60IL(CompilationVerifier verifier) { verifier.VerifyIL("App.Main", @"{ // Code size 68 (0x44) .maxstack 3 .locals init (string V_0, //str System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: ldc.i4.s 15 IL_0005: ldc.i4.1 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""Before {"" IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: nop IL_0018: ldloca.s V_1 IL_001a: ldc.i4.s 12 IL_001c: ldstr ""X"" IL_0021: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, string)"" IL_0026: nop IL_0027: ldloca.s V_1 IL_0029: ldstr ""} After"" IL_002e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0033: nop IL_0034: ldloca.s V_1 IL_0036: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_003b: stloc.0 IL_003c: ldloc.0 IL_003d: call ""void System.Console.WriteLine(string)"" IL_0042: nop IL_0043: ret }"); } } [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] #if NET [InlineData(TargetFramework.Net60)] [InlineData(TargetFramework.Net50)] #endif [InlineData(TargetFramework.NetFramework)] [InlineData(TargetFramework.NetStandard20)] [InlineData(TargetFramework.Mscorlib461)] [InlineData(TargetFramework.Mscorlib40)] [Theory] public void RawInterpolatedStringWithCurlyBracesAndFormatSpecifier(TargetFramework framework) { var text = @"using System; class App{ public static void Main(){ var str = $$""""""Before {{{12:X}}} After""""""; Console.WriteLine(str); } }"; var parseOptions = TestOptions.Regular11; var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); //string.Format was fixed in dotnet core 3 var expectedOutput = #if NETCOREAPP3_0_OR_GREATER "Before {C} After" #else "Before {X} After" #endif ; var comp = CreateCompilation(text, targetFramework: framework, parseOptions: parseOptions, options: compOptions); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); switch (framework) { case TargetFramework.Net60: checkNet60IL(verifier); break; default: checkNet50IL(verifier); break; } static void checkNet50IL(CompilationVerifier verifier) { verifier.VerifyIL("App.Main", @"{ // Code size 27 (0x1b) .maxstack 2 .locals init (string V_0) //str IL_0000: nop IL_0001: ldstr ""Before {{{0:X}}} After"" IL_0006: ldc.i4.s 12 IL_0008: box ""int"" IL_000d: call ""string string.Format(string, object)"" IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: call ""void System.Console.WriteLine(string)"" IL_0019: nop IL_001a: ret }"); } static void checkNet60IL(CompilationVerifier verifier) { verifier.VerifyIL("App.Main", @"{ // Code size 68 (0x44) .maxstack 3 .locals init (string V_0, //str System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: ldc.i4.s 15 IL_0005: ldc.i4.1 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""Before {"" IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: nop IL_0018: ldloca.s V_1 IL_001a: ldc.i4.s 12 IL_001c: ldstr ""X"" IL_0021: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, string)"" IL_0026: nop IL_0027: ldloca.s V_1 IL_0029: ldstr ""} After"" IL_002e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0033: nop IL_0034: ldloca.s V_1 IL_0036: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_003b: stloc.0 IL_003c: ldloc.0 IL_003d: call ""void System.Console.WriteLine(string)"" IL_0042: nop IL_0043: ret }"); } } [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] #if NET [InlineData(TargetFramework.Net60)] [InlineData(TargetFramework.Net50)] #endif [InlineData(TargetFramework.NetFramework)] [InlineData(TargetFramework.NetStandard20)] [InlineData(TargetFramework.Mscorlib461)] [InlineData(TargetFramework.Mscorlib40)] [Theory] public void InterpolatedStringWithCurlyBracesAndAllStringValues(TargetFramework framework) { var text = @"using System; class App{ public static void Main(){ string a = ""a""; var str = $""Before {{{a}}} After""; Console.WriteLine(str); } }"; var parseOptions = new CSharpParseOptions( languageVersion: LanguageVersion.CSharp10, documentationMode: DocumentationMode.Parse, kind: SourceCodeKind.Regular ); var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); var expectedOutput = "Before {a} After"; var comp = CreateCompilation(text, targetFramework: framework, parseOptions: parseOptions, options: compOptions); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("App.Main", @"{ // Code size 32 (0x20) .maxstack 3 .locals init (string V_0, //a string V_1) //str IL_0000: nop IL_0001: ldstr ""a"" IL_0006: stloc.0 IL_0007: ldstr ""Before {"" IL_000c: ldloc.0 IL_000d: ldstr ""} After"" IL_0012: call ""string string.Concat(string, string, string)"" IL_0017: stloc.1 IL_0018: ldloc.1 IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: nop IL_001f: ret }"); } [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] #if NET [InlineData(TargetFramework.Net60)] [InlineData(TargetFramework.Net50)] #endif [InlineData(TargetFramework.NetFramework)] [InlineData(TargetFramework.NetStandard20)] [InlineData(TargetFramework.Mscorlib461)] [InlineData(TargetFramework.Mscorlib40)] [Theory] public void RawInterpolatedStringWithCurlyBracesAndAllStringValues(TargetFramework framework) { var text = @"using System; class App{ public static void Main(){ string a = ""a""; var str = $$""""""Before {{{a}}} After""""""; Console.WriteLine(str); } }"; var parseOptions = TestOptions.Regular11; var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); var expectedOutput = "Before {a} After"; var comp = CreateCompilation(text, targetFramework: framework, parseOptions: parseOptions, options: compOptions); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("App.Main", @"{ // Code size 32 (0x20) .maxstack 3 .locals init (string V_0, //a string V_1) //str IL_0000: nop IL_0001: ldstr ""a"" IL_0006: stloc.0 IL_0007: ldstr ""Before {"" IL_000c: ldloc.0 IL_000d: ldstr ""} After"" IL_0012: call ""string string.Concat(string, string, string)"" IL_0017: stloc.1 IL_0018: ldloc.1 IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: nop IL_001f: ret }"); } [WorkItem(1097386, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097386")] [Fact] public void Syntax01() { var text = @"using System; class Program { static void Main(string[] args) { var x = $""{ Math.Abs(value: 1):\}""; var y = x; } }"; CreateCompilation(text).VerifyDiagnostics( // (6,40): error CS8087: A '}' character may only be escaped by doubling '}}' in an interpolated string. // var x = $"{ Math.Abs(value: 1):\}"; Diagnostic(ErrorCode.ERR_EscapedCurly, @"\").WithArguments("}").WithLocation(6, 40), // (6,40): error CS1009: Unrecognized escape sequence // var x = $"{ Math.Abs(value: 1):\}"; Diagnostic(ErrorCode.ERR_IllegalEscape, @"\}").WithLocation(6, 40) ); } [WorkItem(1097941, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097941")] [Fact] public void Syntax02() { var text = @"using S = System; class C { void M() { var x = $""{ (S: } }"; // the precise diagnostics do not matter, as long as it is an error and not a crash. Assert.True(SyntaxFactory.ParseSyntaxTree(text).GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)); } [WorkItem(1097386, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097386")] [Fact] public void Syntax03() { var text = @"using System; class Program { static void Main(string[] args) { var x = $""{ Math.Abs(value: 1):}}""; var y = x; } } "; CreateCompilation(text).VerifyDiagnostics( // (6,39): error CS8089: Empty format specifier. // var x = $"{ Math.Abs(value: 1):}}"; Diagnostic(ErrorCode.ERR_EmptyFormatSpecifier, ":").WithLocation(6, 39), // (6,41): error CS8086: A '}' character must be escaped (by doubling) in an interpolated string. // var x = $"{ Math.Abs(value: 1):}}"; Diagnostic(ErrorCode.ERR_UnescapedCurly, "}").WithArguments("}").WithLocation(6, 41) ); } [WorkItem(1099105, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1099105")] [Fact] public void NoUnexpandedForm() { string source = @"using System; class Program { public static void Main(string[] args) { string[] arr1 = new string[] { ""xyzzy"" }; object[] arr2 = arr1; Console.WriteLine($""-{null}-""); Console.WriteLine($""-{arr1}-""); Console.WriteLine($""-{arr2}-""); } }"; CompileAndVerify(source + formattableString, expectedOutput: @"-- -System.String[]- -System.String[]-"); } [WorkItem(1097386, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097386")] [Fact] public void Dynamic01() { var text = @"class C { const dynamic a = a; string s = $""{0,a}""; }"; CreateCompilationWithMscorlib40AndSystemCore(text).VerifyDiagnostics( // (3,19): error CS0110: The evaluation of the constant value for 'C.a' involves a circular definition // const dynamic a = a; Diagnostic(ErrorCode.ERR_CircConstValue, "a").WithArguments("C.a").WithLocation(3, 19), // (3,23): error CS0134: 'C.a' is of type 'dynamic'. A const field of a reference type other than string can only be initialized with null. // const dynamic a = a; Diagnostic(ErrorCode.ERR_NotNullConstRefField, "a").WithArguments("C.a", "dynamic").WithLocation(3, 23), // (4,21): error CS0150: A constant value is expected // string s = $"{0,a}"; Diagnostic(ErrorCode.ERR_ConstantExpected, "a").WithLocation(4, 21) ); } [WorkItem(1099238, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1099238")] [Fact] public void Syntax04() { var text = @"using System; using System.Linq.Expressions; class Program { static void Main() { Expression<Func<string>> e = () => $""\u1{0:\u2}""; Console.WriteLine(e); } }"; CreateCompilationWithMscorlib40AndSystemCore(text).VerifyDiagnostics( // (8,46): error CS1009: Unrecognized escape sequence // Expression<Func<string>> e = () => $"\u1{0:\u2}"; Diagnostic(ErrorCode.ERR_IllegalEscape, @"\u1").WithLocation(8, 46), // (8,52): error CS1009: Unrecognized escape sequence // Expression<Func<string>> e = () => $"\u1{0:\u2}"; Diagnostic(ErrorCode.ERR_IllegalEscape, @"\u2").WithLocation(8, 52) ); } [Fact, WorkItem(1098612, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1098612")] public void MissingConversionFromFormattableStringToIFormattable() { var text = @"namespace System.Runtime.CompilerServices { public static class FormattableStringFactory { public static FormattableString Create(string format, params object[] arguments) { return null; } } } namespace System { public abstract class FormattableString { } } static class C { static void Main() { System.IFormattable i = $""{""""}""; } }"; CreateCompilationWithMscorlib40AndSystemCore(text).VerifyEmitDiagnostics( // (23,33): error CS0029: Cannot implicitly convert type 'FormattableString' to 'IFormattable' // System.IFormattable i = $"{""}"; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""{""""}""").WithArguments("System.FormattableString", "System.IFormattable").WithLocation(23, 33) ); } [Fact] public void VoidReturningFactory() { var text = @" namespace System.Runtime.CompilerServices { public static class FormattableStringFactory { public static void Create(string format, params object[] arguments) { } } } static class C { static void Main() { System.IFormattable i = $""{""""}""; } }"; CreateCompilation(text).VerifyDiagnostics( // (16,33): error CS0029: Cannot implicitly convert type 'void' to 'IFormattable' // System.IFormattable i = $"{""}"; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""{""""}""").WithArguments("void", "System.IFormattable").WithLocation(16, 33) ); } [Theory, WorkItem(54702, "https://github.com/dotnet/roslyn/issues/54702")] [InlineData(@"$""{s1}{s2}""", @"$""{s1}{s2}{s3}""", @"$""{s1}{s2}{s3}{s4}""", @"$""{s1}{s2}{s3}{s4}{s5}""")] [InlineData(@"$""{s1}"" + $""{s2}""", @"$""{s1}"" + $""{s2}"" + $""{s3}""", @"$""{s1}"" + $""{s2}"" + $""{s3}"" + $""{s4}""", @"$""{s1}"" + $""{s2}"" + $""{s3}"" + $""{s4}"" + $""{s5}""")] public void InterpolatedStringHandler_ConcatPreferencesForAllStringElements(string twoComponents, string threeComponents, string fourComponents, string fiveComponents) { var code = @" using System; Console.WriteLine(TwoComponents()); Console.WriteLine(ThreeComponents()); Console.WriteLine(FourComponents()); Console.WriteLine(FiveComponents()); string TwoComponents() { string s1 = ""1""; string s2 = ""2""; return " + twoComponents + @"; } string ThreeComponents() { string s1 = ""1""; string s2 = ""2""; string s3 = ""3""; return " + threeComponents + @"; } string FourComponents() { string s1 = ""1""; string s2 = ""2""; string s3 = ""3""; string s4 = ""4""; return " + fourComponents + @"; } string FiveComponents() { string s1 = ""1""; string s2 = ""2""; string s3 = ""3""; string s4 = ""4""; string s5 = ""5""; return " + fiveComponents + @"; } "; var handler = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { code, handler }, expectedOutput: @" 12 123 1234 value:1 value:2 value:3 value:4 value:5 "); verifier.VerifyIL("Program.<<Main>$>g__TwoComponents|0_0()", @" { // Code size 18 (0x12) .maxstack 2 .locals init (string V_0) //s2 IL_0000: ldstr ""1"" IL_0005: ldstr ""2"" IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: call ""string string.Concat(string, string)"" IL_0011: ret } "); verifier.VerifyIL("Program.<<Main>$>g__ThreeComponents|0_1()", @" { // Code size 25 (0x19) .maxstack 3 .locals init (string V_0, //s2 string V_1) //s3 IL_0000: ldstr ""1"" IL_0005: ldstr ""2"" IL_000a: stloc.0 IL_000b: ldstr ""3"" IL_0010: stloc.1 IL_0011: ldloc.0 IL_0012: ldloc.1 IL_0013: call ""string string.Concat(string, string, string)"" IL_0018: ret } "); verifier.VerifyIL("Program.<<Main>$>g__FourComponents|0_2()", @" { // Code size 32 (0x20) .maxstack 4 .locals init (string V_0, //s2 string V_1, //s3 string V_2) //s4 IL_0000: ldstr ""1"" IL_0005: ldstr ""2"" IL_000a: stloc.0 IL_000b: ldstr ""3"" IL_0010: stloc.1 IL_0011: ldstr ""4"" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.1 IL_0019: ldloc.2 IL_001a: call ""string string.Concat(string, string, string, string)"" IL_001f: ret } "); verifier.VerifyIL("Program.<<Main>$>g__FiveComponents|0_3()", @" { // Code size 89 (0x59) .maxstack 3 .locals init (string V_0, //s1 string V_1, //s2 string V_2, //s3 string V_3, //s4 string V_4, //s5 System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) IL_0000: ldstr ""1"" IL_0005: stloc.0 IL_0006: ldstr ""2"" IL_000b: stloc.1 IL_000c: ldstr ""3"" IL_0011: stloc.2 IL_0012: ldstr ""4"" IL_0017: stloc.3 IL_0018: ldstr ""5"" IL_001d: stloc.s V_4 IL_001f: ldloca.s V_5 IL_0021: ldc.i4.0 IL_0022: ldc.i4.5 IL_0023: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0028: ldloca.s V_5 IL_002a: ldloc.0 IL_002b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0030: ldloca.s V_5 IL_0032: ldloc.1 IL_0033: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0038: ldloca.s V_5 IL_003a: ldloc.2 IL_003b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0040: ldloca.s V_5 IL_0042: ldloc.3 IL_0043: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0048: ldloca.s V_5 IL_004a: ldloc.s V_4 IL_004c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0051: ldloca.s V_5 IL_0053: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0058: ret } "); } [Theory] [CombinatorialData] public void InterpolatedStringHandler_OverloadsAndBoolReturns( bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg, [CombinatorialValues(@"$""base{a}{a,1}{a:X}{a,2:Y}""", @"$""base"" + $""{a}"" + $""{a,1}"" + $""{a:X}"" + $""{a,2:Y}""")] string expression) { var source = @"int a = 1; System.Console.WriteLine(" + expression + @");"; string interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters, useBoolReturns, constructorBoolArg: constructorBoolArg); string expectedOutput = useDefaultParameters ? @"base value:1,alignment:0:format: value:1,alignment:1:format: value:1,alignment:0:format:X value:1,alignment:2:format:Y" : @"base value:1 value:1,alignment:1 value:1:format:X value:1,alignment:2:format:Y"; string expectedIl = getIl(); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput); verifier.VerifyIL("<top-level-statements-entry-point>", expectedIl); var comp1 = CreateCompilation(interpolatedStringBuilder); foreach (var reference in new[] { comp1.EmitToImageReference(), comp1.ToMetadataReference() }) { var comp2 = CreateCompilation(source, new[] { reference }); verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput); verifier.VerifyIL("<top-level-statements-entry-point>", expectedIl); } string getIl() => (useDefaultParameters, useBoolReturns, constructorBoolArg) switch { (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: false) => @" { // Code size 80 (0x50) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.4 IL_0005: ldc.i4.4 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""base"" IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: ldloca.s V_1 IL_0019: ldloc.0 IL_001a: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_001f: ldloca.s V_1 IL_0021: ldloc.0 IL_0022: ldc.i4.1 IL_0023: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int)"" IL_0028: ldloca.s V_1 IL_002a: ldloc.0 IL_002b: ldstr ""X"" IL_0030: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, string)"" IL_0035: ldloca.s V_1 IL_0037: ldloc.0 IL_0038: ldc.i4.2 IL_0039: ldstr ""Y"" IL_003e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0043: ldloca.s V_1 IL_0045: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_004a: call ""void System.Console.WriteLine(string)"" IL_004f: ret } ", (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: false) => @" { // Code size 84 (0x54) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.4 IL_0005: ldc.i4.4 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""base"" IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: ldloca.s V_1 IL_0019: ldloc.0 IL_001a: ldc.i4.0 IL_001b: ldnull IL_001c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0021: ldloca.s V_1 IL_0023: ldloc.0 IL_0024: ldc.i4.1 IL_0025: ldnull IL_0026: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_002b: ldloca.s V_1 IL_002d: ldloc.0 IL_002e: ldc.i4.0 IL_002f: ldstr ""X"" IL_0034: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0039: ldloca.s V_1 IL_003b: ldloc.0 IL_003c: ldc.i4.2 IL_003d: ldstr ""Y"" IL_0042: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0047: ldloca.s V_1 IL_0049: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_004e: call ""void System.Console.WriteLine(string)"" IL_0053: ret } ", (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: false) => @" { // Code size 92 (0x5c) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.4 IL_0005: ldc.i4.4 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""base"" IL_0012: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: brfalse.s IL_004d IL_0019: ldloca.s V_1 IL_001b: ldloc.0 IL_001c: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0021: brfalse.s IL_004d IL_0023: ldloca.s V_1 IL_0025: ldloc.0 IL_0026: ldc.i4.1 IL_0027: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int)"" IL_002c: brfalse.s IL_004d IL_002e: ldloca.s V_1 IL_0030: ldloc.0 IL_0031: ldstr ""X"" IL_0036: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, string)"" IL_003b: brfalse.s IL_004d IL_003d: ldloca.s V_1 IL_003f: ldloc.0 IL_0040: ldc.i4.2 IL_0041: ldstr ""Y"" IL_0046: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_004b: br.s IL_004e IL_004d: ldc.i4.0 IL_004e: pop IL_004f: ldloca.s V_1 IL_0051: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0056: call ""void System.Console.WriteLine(string)"" IL_005b: ret } ", (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: false) => @" { // Code size 96 (0x60) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.4 IL_0005: ldc.i4.4 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""base"" IL_0012: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: brfalse.s IL_0051 IL_0019: ldloca.s V_1 IL_001b: ldloc.0 IL_001c: ldc.i4.0 IL_001d: ldnull IL_001e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0023: brfalse.s IL_0051 IL_0025: ldloca.s V_1 IL_0027: ldloc.0 IL_0028: ldc.i4.1 IL_0029: ldnull IL_002a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_002f: brfalse.s IL_0051 IL_0031: ldloca.s V_1 IL_0033: ldloc.0 IL_0034: ldc.i4.0 IL_0035: ldstr ""X"" IL_003a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_003f: brfalse.s IL_0051 IL_0041: ldloca.s V_1 IL_0043: ldloc.0 IL_0044: ldc.i4.2 IL_0045: ldstr ""Y"" IL_004a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_004f: br.s IL_0052 IL_0051: ldc.i4.0 IL_0052: pop IL_0053: ldloca.s V_1 IL_0055: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_005a: call ""void System.Console.WriteLine(string)"" IL_005f: ret } ", (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: true) => @" { // Code size 84 (0x54) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.4 IL_0003: ldc.i4.4 IL_0004: ldloca.s V_2 IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_000b: stloc.1 IL_000c: ldloc.2 IL_000d: brfalse.s IL_0047 IL_000f: ldloca.s V_1 IL_0011: ldstr ""base"" IL_0016: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_001b: ldloca.s V_1 IL_001d: ldloc.0 IL_001e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0023: ldloca.s V_1 IL_0025: ldloc.0 IL_0026: ldc.i4.1 IL_0027: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int)"" IL_002c: ldloca.s V_1 IL_002e: ldloc.0 IL_002f: ldstr ""X"" IL_0034: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, string)"" IL_0039: ldloca.s V_1 IL_003b: ldloc.0 IL_003c: ldc.i4.2 IL_003d: ldstr ""Y"" IL_0042: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0047: ldloca.s V_1 IL_0049: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_004e: call ""void System.Console.WriteLine(string)"" IL_0053: ret } ", (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: true) => @" { // Code size 88 (0x58) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.4 IL_0003: ldc.i4.4 IL_0004: ldloca.s V_2 IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_000b: stloc.1 IL_000c: ldloc.2 IL_000d: brfalse.s IL_004b IL_000f: ldloca.s V_1 IL_0011: ldstr ""base"" IL_0016: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_001b: ldloca.s V_1 IL_001d: ldloc.0 IL_001e: ldc.i4.0 IL_001f: ldnull IL_0020: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0025: ldloca.s V_1 IL_0027: ldloc.0 IL_0028: ldc.i4.1 IL_0029: ldnull IL_002a: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_002f: ldloca.s V_1 IL_0031: ldloc.0 IL_0032: ldc.i4.0 IL_0033: ldstr ""X"" IL_0038: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_003d: ldloca.s V_1 IL_003f: ldloc.0 IL_0040: ldc.i4.2 IL_0041: ldstr ""Y"" IL_0046: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_004b: ldloca.s V_1 IL_004d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0052: call ""void System.Console.WriteLine(string)"" IL_0057: ret } ", (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: true) => @" { // Code size 96 (0x60) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.4 IL_0003: ldc.i4.4 IL_0004: ldloca.s V_2 IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_000b: stloc.1 IL_000c: ldloc.2 IL_000d: brfalse.s IL_0051 IL_000f: ldloca.s V_1 IL_0011: ldstr ""base"" IL_0016: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_001b: brfalse.s IL_0051 IL_001d: ldloca.s V_1 IL_001f: ldloc.0 IL_0020: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0025: brfalse.s IL_0051 IL_0027: ldloca.s V_1 IL_0029: ldloc.0 IL_002a: ldc.i4.1 IL_002b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int)"" IL_0030: brfalse.s IL_0051 IL_0032: ldloca.s V_1 IL_0034: ldloc.0 IL_0035: ldstr ""X"" IL_003a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, string)"" IL_003f: brfalse.s IL_0051 IL_0041: ldloca.s V_1 IL_0043: ldloc.0 IL_0044: ldc.i4.2 IL_0045: ldstr ""Y"" IL_004a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_004f: br.s IL_0052 IL_0051: ldc.i4.0 IL_0052: pop IL_0053: ldloca.s V_1 IL_0055: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_005a: call ""void System.Console.WriteLine(string)"" IL_005f: ret } ", (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: true) => @" { // Code size 100 (0x64) .maxstack 4 .locals init (int V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.4 IL_0003: ldc.i4.4 IL_0004: ldloca.s V_2 IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_000b: stloc.1 IL_000c: ldloc.2 IL_000d: brfalse.s IL_0055 IL_000f: ldloca.s V_1 IL_0011: ldstr ""base"" IL_0016: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_001b: brfalse.s IL_0055 IL_001d: ldloca.s V_1 IL_001f: ldloc.0 IL_0020: ldc.i4.0 IL_0021: ldnull IL_0022: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0027: brfalse.s IL_0055 IL_0029: ldloca.s V_1 IL_002b: ldloc.0 IL_002c: ldc.i4.1 IL_002d: ldnull IL_002e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0033: brfalse.s IL_0055 IL_0035: ldloca.s V_1 IL_0037: ldloc.0 IL_0038: ldc.i4.0 IL_0039: ldstr ""X"" IL_003e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0043: brfalse.s IL_0055 IL_0045: ldloca.s V_1 IL_0047: ldloc.0 IL_0048: ldc.i4.2 IL_0049: ldstr ""Y"" IL_004e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int, int, string)"" IL_0053: br.s IL_0056 IL_0055: ldc.i4.0 IL_0056: pop IL_0057: ldloca.s V_1 IL_0059: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_005e: call ""void System.Console.WriteLine(string)"" IL_0063: ret } ", }; } [Fact] public void UseOfSpanInInterpolationHole_CSharp9() { var source = @" using System; ReadOnlySpan<char> span = stackalloc char[1]; Console.WriteLine($""{span}"");"; var comp = CreateCompilation(new[] { source, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false) }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics( // (4,22): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. // Console.WriteLine($"{span}"); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "span").WithArguments("interpolated string handlers", "10.0").WithLocation(4, 22) ); } [ConditionalTheory(typeof(MonoOrCoreClrOnly))] [CombinatorialData] public void UseOfSpanInInterpolationHole(bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg, [CombinatorialValues(@"$""base{a}{a,1}{a:X}{a,2:Y}""", @"$""base"" + $""{a}"" + $""{a,1}"" + $""{a:X}"" + $""{a,2:Y}""")] string expression) { var source = @" using System; ReadOnlySpan<char> a = ""1""; System.Console.WriteLine(" + expression + ");"; string interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters, useBoolReturns, constructorBoolArg: constructorBoolArg); string expectedOutput = useDefaultParameters ? @"base value:1,alignment:0:format: value:1,alignment:1:format: value:1,alignment:0:format:X value:1,alignment:2:format:Y" : @"base value:1 value:1,alignment:1 value:1:format:X value:1,alignment:2:format:Y"; string expectedIl = getIl(); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput, targetFramework: TargetFramework.Net50, parseOptions: TestOptions.Regular10); verifier.VerifyIL("<top-level-statements-entry-point>", expectedIl); var comp1 = CreateCompilation(interpolatedStringBuilder, targetFramework: TargetFramework.Net50); foreach (var reference in new[] { comp1.EmitToImageReference(), comp1.ToMetadataReference() }) { var comp2 = CreateCompilation(source, new[] { reference }, targetFramework: TargetFramework.Net50, parseOptions: TestOptions.Regular10); verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput); verifier.VerifyIL("<top-level-statements-entry-point>", expectedIl); } string getIl() => (useDefaultParameters, useBoolReturns, constructorBoolArg) switch { (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: false) => @" { // Code size 89 (0x59) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 IL_000e: ldc.i4.4 IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0014: ldloca.s V_1 IL_0016: ldstr ""base"" IL_001b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0020: ldloca.s V_1 IL_0022: ldloc.0 IL_0023: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>)"" IL_0028: ldloca.s V_1 IL_002a: ldloc.0 IL_002b: ldc.i4.1 IL_002c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int)"" IL_0031: ldloca.s V_1 IL_0033: ldloc.0 IL_0034: ldstr ""X"" IL_0039: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, string)"" IL_003e: ldloca.s V_1 IL_0040: ldloc.0 IL_0041: ldc.i4.2 IL_0042: ldstr ""Y"" IL_0047: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_004c: ldloca.s V_1 IL_004e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0053: call ""void System.Console.WriteLine(string)"" IL_0058: ret } ", (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: false) => @" { // Code size 93 (0x5d) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 IL_000e: ldc.i4.4 IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0014: ldloca.s V_1 IL_0016: ldstr ""base"" IL_001b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0020: ldloca.s V_1 IL_0022: ldloc.0 IL_0023: ldc.i4.0 IL_0024: ldnull IL_0025: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_002a: ldloca.s V_1 IL_002c: ldloc.0 IL_002d: ldc.i4.1 IL_002e: ldnull IL_002f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0034: ldloca.s V_1 IL_0036: ldloc.0 IL_0037: ldc.i4.0 IL_0038: ldstr ""X"" IL_003d: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0042: ldloca.s V_1 IL_0044: ldloc.0 IL_0045: ldc.i4.2 IL_0046: ldstr ""Y"" IL_004b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0050: ldloca.s V_1 IL_0052: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0057: call ""void System.Console.WriteLine(string)"" IL_005c: ret } ", (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: false) => @" { // Code size 101 (0x65) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 IL_000e: ldc.i4.4 IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0014: ldloca.s V_1 IL_0016: ldstr ""base"" IL_001b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0020: brfalse.s IL_0056 IL_0022: ldloca.s V_1 IL_0024: ldloc.0 IL_0025: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>)"" IL_002a: brfalse.s IL_0056 IL_002c: ldloca.s V_1 IL_002e: ldloc.0 IL_002f: ldc.i4.1 IL_0030: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int)"" IL_0035: brfalse.s IL_0056 IL_0037: ldloca.s V_1 IL_0039: ldloc.0 IL_003a: ldstr ""X"" IL_003f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, string)"" IL_0044: brfalse.s IL_0056 IL_0046: ldloca.s V_1 IL_0048: ldloc.0 IL_0049: ldc.i4.2 IL_004a: ldstr ""Y"" IL_004f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0054: br.s IL_0057 IL_0056: ldc.i4.0 IL_0057: pop IL_0058: ldloca.s V_1 IL_005a: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_005f: call ""void System.Console.WriteLine(string)"" IL_0064: ret } ", (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: false) => @" { // Code size 105 (0x69) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 IL_000e: ldc.i4.4 IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0014: ldloca.s V_1 IL_0016: ldstr ""base"" IL_001b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0020: brfalse.s IL_005a IL_0022: ldloca.s V_1 IL_0024: ldloc.0 IL_0025: ldc.i4.0 IL_0026: ldnull IL_0027: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_002c: brfalse.s IL_005a IL_002e: ldloca.s V_1 IL_0030: ldloc.0 IL_0031: ldc.i4.1 IL_0032: ldnull IL_0033: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0038: brfalse.s IL_005a IL_003a: ldloca.s V_1 IL_003c: ldloc.0 IL_003d: ldc.i4.0 IL_003e: ldstr ""X"" IL_0043: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0048: brfalse.s IL_005a IL_004a: ldloca.s V_1 IL_004c: ldloc.0 IL_004d: ldc.i4.2 IL_004e: ldstr ""Y"" IL_0053: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0058: br.s IL_005b IL_005a: ldc.i4.0 IL_005b: pop IL_005c: ldloca.s V_1 IL_005e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0063: call ""void System.Console.WriteLine(string)"" IL_0068: ret } ", (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: true) => @" { // Code size 93 (0x5d) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 IL_000d: ldloca.s V_2 IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_0014: stloc.1 IL_0015: ldloc.2 IL_0016: brfalse.s IL_0050 IL_0018: ldloca.s V_1 IL_001a: ldstr ""base"" IL_001f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0024: ldloca.s V_1 IL_0026: ldloc.0 IL_0027: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>)"" IL_002c: ldloca.s V_1 IL_002e: ldloc.0 IL_002f: ldc.i4.1 IL_0030: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int)"" IL_0035: ldloca.s V_1 IL_0037: ldloc.0 IL_0038: ldstr ""X"" IL_003d: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, string)"" IL_0042: ldloca.s V_1 IL_0044: ldloc.0 IL_0045: ldc.i4.2 IL_0046: ldstr ""Y"" IL_004b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0050: ldloca.s V_1 IL_0052: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0057: call ""void System.Console.WriteLine(string)"" IL_005c: ret } ", (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: true) => @" { // Code size 105 (0x69) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 IL_000d: ldloca.s V_2 IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_0014: stloc.1 IL_0015: ldloc.2 IL_0016: brfalse.s IL_005a IL_0018: ldloca.s V_1 IL_001a: ldstr ""base"" IL_001f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0024: brfalse.s IL_005a IL_0026: ldloca.s V_1 IL_0028: ldloc.0 IL_0029: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>)"" IL_002e: brfalse.s IL_005a IL_0030: ldloca.s V_1 IL_0032: ldloc.0 IL_0033: ldc.i4.1 IL_0034: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int)"" IL_0039: brfalse.s IL_005a IL_003b: ldloca.s V_1 IL_003d: ldloc.0 IL_003e: ldstr ""X"" IL_0043: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, string)"" IL_0048: brfalse.s IL_005a IL_004a: ldloca.s V_1 IL_004c: ldloc.0 IL_004d: ldc.i4.2 IL_004e: ldstr ""Y"" IL_0053: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0058: br.s IL_005b IL_005a: ldc.i4.0 IL_005b: pop IL_005c: ldloca.s V_1 IL_005e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0063: call ""void System.Console.WriteLine(string)"" IL_0068: ret } ", (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: true) => @" { // Code size 97 (0x61) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 IL_000d: ldloca.s V_2 IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_0014: stloc.1 IL_0015: ldloc.2 IL_0016: brfalse.s IL_0054 IL_0018: ldloca.s V_1 IL_001a: ldstr ""base"" IL_001f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0024: ldloca.s V_1 IL_0026: ldloc.0 IL_0027: ldc.i4.0 IL_0028: ldnull IL_0029: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_002e: ldloca.s V_1 IL_0030: ldloc.0 IL_0031: ldc.i4.1 IL_0032: ldnull IL_0033: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0038: ldloca.s V_1 IL_003a: ldloc.0 IL_003b: ldc.i4.0 IL_003c: ldstr ""X"" IL_0041: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0046: ldloca.s V_1 IL_0048: ldloc.0 IL_0049: ldc.i4.2 IL_004a: ldstr ""Y"" IL_004f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0054: ldloca.s V_1 IL_0056: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_005b: call ""void System.Console.WriteLine(string)"" IL_0060: ret } ", (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: true) => @" { // Code size 109 (0x6d) .maxstack 4 .locals init (System.ReadOnlySpan<char> V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" IL_0005: call ""System.ReadOnlySpan<char> string.op_Implicit(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 IL_000d: ldloca.s V_2 IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" IL_0014: stloc.1 IL_0015: ldloc.2 IL_0016: brfalse.s IL_005e IL_0018: ldloca.s V_1 IL_001a: ldstr ""base"" IL_001f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0024: brfalse.s IL_005e IL_0026: ldloca.s V_1 IL_0028: ldloc.0 IL_0029: ldc.i4.0 IL_002a: ldnull IL_002b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_0030: brfalse.s IL_005e IL_0032: ldloca.s V_1 IL_0034: ldloc.0 IL_0035: ldc.i4.1 IL_0036: ldnull IL_0037: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_003c: brfalse.s IL_005e IL_003e: ldloca.s V_1 IL_0040: ldloc.0 IL_0041: ldc.i4.0 IL_0042: ldstr ""X"" IL_0047: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_004c: brfalse.s IL_005e IL_004e: ldloca.s V_1 IL_0050: ldloc.0 IL_0051: ldc.i4.2 IL_0052: ldstr ""Y"" IL_0057: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>, int, string)"" IL_005c: br.s IL_005f IL_005e: ldc.i4.0 IL_005f: pop IL_0060: ldloca.s V_1 IL_0062: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0067: call ""void System.Console.WriteLine(string)"" IL_006c: ret } ", }; } [Theory] [InlineData(@"$""base{Throw()}{a = 2}""")] [InlineData(@"$""base"" + $""{Throw()}"" + $""{a = 2}""")] public void BoolReturns_ShortCircuit(string expression) { var source = @" using System; int a = 1; Console.Write(" + expression + @"); Console.WriteLine(a); string Throw() => throw new Exception();"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: true, returnExpression: "false"); CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" base 1"); } [Theory] [CombinatorialData] public void BoolOutParameter_ShortCircuits(bool useBoolReturns, [CombinatorialValues(@"$""{Throw()}{a = 2}""", @"$""{Throw()}"" + $""{a = 2}""")] string expression) { var source = @" using System; int a = 1; Console.WriteLine(a); Console.WriteLine(" + expression + @"); Console.WriteLine(a); string Throw() => throw new Exception(); "; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: useBoolReturns, constructorBoolArg: true, constructorSuccessResult: false); CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" 1 1"); } [Theory] [InlineData(@"$""base{await Hole()}""")] [InlineData(@"$""base"" + $""{await Hole()}""")] public void AwaitInHoles_UsesFormat(string expression) { var source = @" using System; using System.Threading.Tasks; Console.WriteLine(" + expression + @"); Task<int> Hole() => Task.FromResult(1);"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @"base1"); verifier.VerifyIL("Program.<<Main>$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", !expression.Contains("+") ? @" { // Code size 164 (0xa4) .maxstack 3 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter<int> V_2, System.Exception V_3) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_003e IL_000a: call ""System.Threading.Tasks.Task<int> Program.<<Main>$>g__Hole|0_0()"" IL_000f: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0014: stloc.2 IL_0015: ldloca.s V_2 IL_0017: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_001c: brtrue.s IL_005a IL_001e: ldarg.0 IL_001f: ldc.i4.0 IL_0020: dup IL_0021: stloc.0 IL_0022: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0027: ldarg.0 IL_0028: ldloc.2 IL_0029: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_002e: ldarg.0 IL_002f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0034: ldloca.s V_2 IL_0036: ldarg.0 IL_0037: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<<Main>$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<<Main>$>d__0)"" IL_003c: leave.s IL_00a3 IL_003e: ldarg.0 IL_003f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_0044: stloc.2 IL_0045: ldarg.0 IL_0046: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_004b: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0051: ldarg.0 IL_0052: ldc.i4.m1 IL_0053: dup IL_0054: stloc.0 IL_0055: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_005a: ldloca.s V_2 IL_005c: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0061: stloc.1 IL_0062: ldstr ""base{0}"" IL_0067: ldloc.1 IL_0068: box ""int"" IL_006d: call ""string string.Format(string, object)"" IL_0072: call ""void System.Console.WriteLine(string)"" IL_0077: leave.s IL_0090 } catch System.Exception { IL_0079: stloc.3 IL_007a: ldarg.0 IL_007b: ldc.i4.s -2 IL_007d: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0082: ldarg.0 IL_0083: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0088: ldloc.3 IL_0089: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_008e: leave.s IL_00a3 } IL_0090: ldarg.0 IL_0091: ldc.i4.s -2 IL_0093: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0098: ldarg.0 IL_0099: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_009e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00a3: ret } " : @" { // Code size 174 (0xae) .maxstack 3 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter<int> V_2, System.Exception V_3) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_003e IL_000a: call ""System.Threading.Tasks.Task<int> Program.<<Main>$>g__Hole|0_0()"" IL_000f: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0014: stloc.2 IL_0015: ldloca.s V_2 IL_0017: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_001c: brtrue.s IL_005a IL_001e: ldarg.0 IL_001f: ldc.i4.0 IL_0020: dup IL_0021: stloc.0 IL_0022: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0027: ldarg.0 IL_0028: ldloc.2 IL_0029: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_002e: ldarg.0 IL_002f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0034: ldloca.s V_2 IL_0036: ldarg.0 IL_0037: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<<Main>$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<<Main>$>d__0)"" IL_003c: leave.s IL_00ad IL_003e: ldarg.0 IL_003f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_0044: stloc.2 IL_0045: ldarg.0 IL_0046: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_004b: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0051: ldarg.0 IL_0052: ldc.i4.m1 IL_0053: dup IL_0054: stloc.0 IL_0055: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_005a: ldloca.s V_2 IL_005c: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0061: stloc.1 IL_0062: ldstr ""base"" IL_0067: ldstr ""{0}"" IL_006c: ldloc.1 IL_006d: box ""int"" IL_0072: call ""string string.Format(string, object)"" IL_0077: call ""string string.Concat(string, string)"" IL_007c: call ""void System.Console.WriteLine(string)"" IL_0081: leave.s IL_009a } catch System.Exception { IL_0083: stloc.3 IL_0084: ldarg.0 IL_0085: ldc.i4.s -2 IL_0087: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_008c: ldarg.0 IL_008d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0092: ldloc.3 IL_0093: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_0098: leave.s IL_00ad } IL_009a: ldarg.0 IL_009b: ldc.i4.s -2 IL_009d: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_00a2: ldarg.0 IL_00a3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_00a8: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00ad: ret }"); } [Theory] [InlineData(@"$""base{hole}""")] [InlineData(@"$""base"" + $""{hole}""")] public void NoAwaitInHoles_UsesBuilder(string expression) { var source = @" using System; using System.Threading.Tasks; var hole = await Hole(); Console.WriteLine(" + expression + @"); Task<int> Hole() => Task.FromResult(1);"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" base value:1"); verifier.VerifyIL("Program.<<Main>$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 185 (0xb9) .maxstack 3 .locals init (int V_0, int V_1, //hole System.Runtime.CompilerServices.TaskAwaiter<int> V_2, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, System.Exception V_4) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_003e IL_000a: call ""System.Threading.Tasks.Task<int> Program.<<Main>$>g__Hole|0_0()"" IL_000f: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0014: stloc.2 IL_0015: ldloca.s V_2 IL_0017: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_001c: brtrue.s IL_005a IL_001e: ldarg.0 IL_001f: ldc.i4.0 IL_0020: dup IL_0021: stloc.0 IL_0022: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0027: ldarg.0 IL_0028: ldloc.2 IL_0029: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_002e: ldarg.0 IL_002f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0034: ldloca.s V_2 IL_0036: ldarg.0 IL_0037: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<<Main>$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<<Main>$>d__0)"" IL_003c: leave.s IL_00b8 IL_003e: ldarg.0 IL_003f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_0044: stloc.2 IL_0045: ldarg.0 IL_0046: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_004b: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0051: ldarg.0 IL_0052: ldc.i4.m1 IL_0053: dup IL_0054: stloc.0 IL_0055: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_005a: ldloca.s V_2 IL_005c: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0061: stloc.1 IL_0062: ldc.i4.4 IL_0063: ldc.i4.1 IL_0064: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0069: stloc.3 IL_006a: ldloca.s V_3 IL_006c: ldstr ""base"" IL_0071: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0076: ldloca.s V_3 IL_0078: ldloc.1 IL_0079: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_007e: ldloca.s V_3 IL_0080: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0085: call ""void System.Console.WriteLine(string)"" IL_008a: leave.s IL_00a5 } catch System.Exception { IL_008c: stloc.s V_4 IL_008e: ldarg.0 IL_008f: ldc.i4.s -2 IL_0091: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0096: ldarg.0 IL_0097: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_009c: ldloc.s V_4 IL_009e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_00a3: leave.s IL_00b8 } IL_00a5: ldarg.0 IL_00a6: ldc.i4.s -2 IL_00a8: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_00ad: ldarg.0 IL_00ae: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_00b3: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b8: ret } "); } [Theory] [InlineData(@"$""base{hole}""")] [InlineData(@"$""base"" + $""{hole}""")] public void NoAwaitInHoles_AwaitInExpression_UsesBuilder(string expression) { var source = @" using System; using System.Threading.Tasks; var hole = 2; Test(await M(1), " + expression + @", await M(3)); void Test(int i1, string s, int i2) => Console.WriteLine(s); Task<int> M(int i) { Console.WriteLine(i); return Task.FromResult(1); }"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" 1 3 base value:2"); verifier.VerifyIL("Program.<<Main>$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 328 (0x148) .maxstack 3 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter<int> V_2, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, System.Exception V_4) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0050 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq IL_00dc IL_0011: ldarg.0 IL_0012: ldc.i4.2 IL_0013: stfld ""int Program.<<Main>$>d__0.<hole>5__2"" IL_0018: ldc.i4.1 IL_0019: call ""System.Threading.Tasks.Task<int> Program.<<Main>$>g__M|0_1(int)"" IL_001e: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0023: stloc.2 IL_0024: ldloca.s V_2 IL_0026: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_002b: brtrue.s IL_006c IL_002d: ldarg.0 IL_002e: ldc.i4.0 IL_002f: dup IL_0030: stloc.0 IL_0031: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0036: ldarg.0 IL_0037: ldloc.2 IL_0038: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_003d: ldarg.0 IL_003e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0043: ldloca.s V_2 IL_0045: ldarg.0 IL_0046: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<<Main>$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<<Main>$>d__0)"" IL_004b: leave IL_0147 IL_0050: ldarg.0 IL_0051: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_0056: stloc.2 IL_0057: ldarg.0 IL_0058: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_005d: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0063: ldarg.0 IL_0064: ldc.i4.m1 IL_0065: dup IL_0066: stloc.0 IL_0067: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_006c: ldarg.0 IL_006d: ldloca.s V_2 IL_006f: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0074: stfld ""int Program.<<Main>$>d__0.<>7__wrap2"" IL_0079: ldarg.0 IL_007a: ldc.i4.4 IL_007b: ldc.i4.1 IL_007c: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0081: stloc.3 IL_0082: ldloca.s V_3 IL_0084: ldstr ""base"" IL_0089: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_008e: ldloca.s V_3 IL_0090: ldarg.0 IL_0091: ldfld ""int Program.<<Main>$>d__0.<hole>5__2"" IL_0096: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_009b: ldloca.s V_3 IL_009d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_00a2: stfld ""string Program.<<Main>$>d__0.<>7__wrap3"" IL_00a7: ldc.i4.3 IL_00a8: call ""System.Threading.Tasks.Task<int> Program.<<Main>$>g__M|0_1(int)"" IL_00ad: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_00b2: stloc.2 IL_00b3: ldloca.s V_2 IL_00b5: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_00ba: brtrue.s IL_00f8 IL_00bc: ldarg.0 IL_00bd: ldc.i4.1 IL_00be: dup IL_00bf: stloc.0 IL_00c0: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_00c5: ldarg.0 IL_00c6: ldloc.2 IL_00c7: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_00cc: ldarg.0 IL_00cd: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_00d2: ldloca.s V_2 IL_00d4: ldarg.0 IL_00d5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<<Main>$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<<Main>$>d__0)"" IL_00da: leave.s IL_0147 IL_00dc: ldarg.0 IL_00dd: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_00e2: stloc.2 IL_00e3: ldarg.0 IL_00e4: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_00e9: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_00ef: ldarg.0 IL_00f0: ldc.i4.m1 IL_00f1: dup IL_00f2: stloc.0 IL_00f3: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_00f8: ldloca.s V_2 IL_00fa: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_00ff: stloc.1 IL_0100: ldarg.0 IL_0101: ldfld ""int Program.<<Main>$>d__0.<>7__wrap2"" IL_0106: ldarg.0 IL_0107: ldfld ""string Program.<<Main>$>d__0.<>7__wrap3"" IL_010c: ldloc.1 IL_010d: call ""void Program.<<Main>$>g__Test|0_0(int, string, int)"" IL_0112: ldarg.0 IL_0113: ldnull IL_0114: stfld ""string Program.<<Main>$>d__0.<>7__wrap3"" IL_0119: leave.s IL_0134 } catch System.Exception { IL_011b: stloc.s V_4 IL_011d: ldarg.0 IL_011e: ldc.i4.s -2 IL_0120: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0125: ldarg.0 IL_0126: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_012b: ldloc.s V_4 IL_012d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_0132: leave.s IL_0147 } IL_0134: ldarg.0 IL_0135: ldc.i4.s -2 IL_0137: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_013c: ldarg.0 IL_013d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0142: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_0147: ret } "); } [Theory, WorkItem(55609, "https://github.com/dotnet/roslyn/issues/55609")] [InlineData(@"$""base{hole}""")] [InlineData(@"$""base"" + $""{hole}""")] public void DynamicInHoles_UsesFormat(string expression) { var source = @" using System; using System.Threading.Tasks; dynamic hole = 1; Console.WriteLine(" + expression + @"); "; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerifyWithCSharp(new[] { source, interpolatedStringBuilder }, expectedOutput: @"base1"); verifier.VerifyIL("<top-level-statements-entry-point>", expression.Contains('+') ? @" { // Code size 34 (0x22) .maxstack 3 .locals init (object V_0) //hole IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldstr ""base"" IL_000c: ldstr ""{0}"" IL_0011: ldloc.0 IL_0012: call ""string string.Format(string, object)"" IL_0017: call ""string string.Concat(string, string)"" IL_001c: call ""void System.Console.WriteLine(string)"" IL_0021: ret } " : @" { // Code size 24 (0x18) .maxstack 2 .locals init (object V_0) //hole IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldstr ""base{0}"" IL_000c: ldloc.0 IL_000d: call ""string string.Format(string, object)"" IL_0012: call ""void System.Console.WriteLine(string)"" IL_0017: ret } "); } [Theory, WorkItem(55609, "https://github.com/dotnet/roslyn/issues/55609")] [InlineData(@"$""{hole}base""")] [InlineData(@"$""{hole}"" + $""base""")] public void DynamicInHoles_UsesFormat2(string expression) { var source = @" using System; using System.Threading.Tasks; dynamic hole = 1; Console.WriteLine(" + expression + @"); "; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerifyWithCSharp(new[] { source, interpolatedStringBuilder }, expectedOutput: @"1base"); verifier.VerifyIL("<top-level-statements-entry-point>", expression.Contains('+') ? @" { // Code size 34 (0x22) .maxstack 2 .locals init (object V_0) //hole IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldstr ""{0}"" IL_000c: ldloc.0 IL_000d: call ""string string.Format(string, object)"" IL_0012: ldstr ""base"" IL_0017: call ""string string.Concat(string, string)"" IL_001c: call ""void System.Console.WriteLine(string)"" IL_0021: ret } " : @" { // Code size 24 (0x18) .maxstack 2 .locals init (object V_0) //hole IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldstr ""{0}base"" IL_000c: ldloc.0 IL_000d: call ""string string.Format(string, object)"" IL_0012: call ""void System.Console.WriteLine(string)"" IL_0017: ret } "); } [Fact] public void ImplicitConversionsInConstructor() { var code = @" using System.Runtime.CompilerServices; CustomHandler c = $""""; [InterpolatedStringHandler] struct CustomHandler { public CustomHandler(object literalLength, object formattedCount) {} } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerAttribute }); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 19 (0x13) .maxstack 2 IL_0000: ldc.i4.0 IL_0001: box ""int"" IL_0006: ldc.i4.0 IL_0007: box ""int"" IL_000c: newobj ""CustomHandler..ctor(object, object)"" IL_0011: pop IL_0012: ret } "); } [Fact] public void MissingCreate_01() { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { public override string ToString() => throw null; public void Dispose() => throw null; public void AppendLiteral(string value) => throw null; public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics( // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 2 arguments // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "2").WithLocation(1, 5), // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 3 arguments // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "3").WithLocation(1, 5) ); } [Fact] public void MissingCreate_02() { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { public DefaultInterpolatedStringHandler(int literalLength) => throw null; public override string ToString() => throw null; public void Dispose() => throw null; public void AppendLiteral(string value) => throw null; public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics( // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 2 arguments // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "2").WithLocation(1, 5), // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 3 arguments // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "3").WithLocation(1, 5) ); } [Fact] public void MissingCreate_03() { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { public DefaultInterpolatedStringHandler(ref int literalLength, int formattedCount) => throw null; public override string ToString() => throw null; public void Dispose() => throw null; public void AppendLiteral(string value) => throw null; public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics( // (1,5): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BadArgRef, @"$""{(object)1}""").WithArguments("1", "ref").WithLocation(1, 5) ); } [Theory] [InlineData(null)] [InlineData("public string ToStringAndClear(int literalLength) => throw null;")] [InlineData("public void ToStringAndClear() => throw null;")] [InlineData("public static string ToStringAndClear() => throw null;")] public void MissingWellKnownMethod_ToStringAndClear(string toStringAndClearMethod) { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; " + toStringAndClearMethod + @" public override string ToString() => throw null; public void AppendLiteral(string value) => throw null; public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( // (1,5): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear' // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "ToStringAndClear").WithLocation(1, 5) ); } [Fact] public void ObsoleteCreateMethod() { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { [System.Obsolete(""Constructor is obsolete"", error: true)] public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; public void Dispose() => throw null; public override string ToString() => throw null; public void AppendLiteral(string value) => throw null; public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics( // (1,5): error CS0619: 'DefaultInterpolatedStringHandler.DefaultInterpolatedStringHandler(int, int)' is obsolete: 'Constructor is obsolete' // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.DefaultInterpolatedStringHandler(int, int)", "Constructor is obsolete").WithLocation(1, 5) ); } [Fact] public void ObsoleteAppendLiteralMethod() { var code = @"_ = $""base{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; public void Dispose() => throw null; public override string ToString() => throw null; [System.Obsolete(""AppendLiteral is obsolete"", error: true)] public void AppendLiteral(string value) => throw null; public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics( // (1,7): error CS0619: 'DefaultInterpolatedStringHandler.AppendLiteral(string)' is obsolete: 'AppendLiteral is obsolete' // _ = $"base{(object)1}"; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "base").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "AppendLiteral is obsolete").WithLocation(1, 7) ); } [Fact] public void ObsoleteAppendFormattedMethod() { var code = @"_ = $""base{(object)1}"";"; var interpolatedStringBuilder = @" namespace System.Runtime.CompilerServices { public ref struct DefaultInterpolatedStringHandler { public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; public void Dispose() => throw null; public override string ToString() => throw null; public void AppendLiteral(string value) => throw null; [System.Obsolete(""AppendFormatted is obsolete"", error: true)] public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } } "; var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); comp.VerifyDiagnostics( // (1,11): error CS0619: 'DefaultInterpolatedStringHandler.AppendFormatted<T>(T, int, string)' is obsolete: 'AppendFormatted is obsolete' // _ = $"base{(object)1}"; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "{(object)1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<T>(T, int, string)", "AppendFormatted is obsolete").WithLocation(1, 11) ); } private const string UnmanagedCallersOnlyIl = @" .class public auto ansi sealed beforefieldinit System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 40 00 00 00 01 00 54 02 09 49 6e 68 65 72 69 74 65 64 00 ) .field public class [mscorlib]System.Type[] CallConvs .field public string EntryPoint .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { ldarg.0 call instance void [mscorlib]System.Attribute::.ctor() ret } }"; [Fact] public void UnmanagedCallersOnlyAppendFormattedMethod() { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" .class public sequential ansi sealed beforefieldinit System.Runtime.CompilerServices.DefaultInterpolatedStringHandler extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = ( 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 69 6c 65 72 2e 01 00 00 ) .pack 0 .size 1 .method public hidebysig specialname rtspecialname instance void .ctor ( int32 literalLength, int32 formattedCount ) cil managed { ldnull throw } .method public hidebysig instance void Dispose () cil managed { ldnull throw } .method public hidebysig virtual instance string ToString () cil managed { ldnull throw } .method public hidebysig instance void AppendLiteral ( string 'value' ) cil managed { ldnull throw } .method public hidebysig instance void AppendFormatted<T> ( !!T hole, [opt] int32 'alignment', [opt] string format ) cil managed { .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 ) .param [2] = int32(0) .param [3] = nullref ldnull throw } } "; var comp = CreateCompilationWithIL(code, ilSource: interpolatedStringBuilder + UnmanagedCallersOnlyIl); comp.VerifyDiagnostics( // (1,7): error CS0570: 'DefaultInterpolatedStringHandler.AppendFormatted<T>(T, int, string)' is not supported by the language // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BindToBogus, "{(object)1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<T>(T, int, string)").WithLocation(1, 7) ); } [Fact] public void UnmanagedCallersOnlyToStringMethod() { var code = @"_ = $""{(object)1}"";"; var interpolatedStringBuilder = @" .class public sequential ansi sealed beforefieldinit System.Runtime.CompilerServices.DefaultInterpolatedStringHandler extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = ( 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 69 6c 65 72 2e 01 00 00 ) .pack 0 .size 1 .method public hidebysig specialname rtspecialname instance void .ctor ( int32 literalLength, int32 formattedCount ) cil managed { ldnull throw } .method public hidebysig instance string ToStringAndClear () cil managed { .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 ) ldnull throw } .method public hidebysig instance void AppendLiteral ( string 'value' ) cil managed { ldnull throw } .method public hidebysig instance void AppendFormatted<T> ( !!T hole, [opt] int32 'alignment', [opt] string format ) cil managed { .param [2] = int32(0) .param [3] = nullref ldnull throw } } "; var comp = CreateCompilationWithIL(code, ilSource: interpolatedStringBuilder + UnmanagedCallersOnlyIl); comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( // (1,5): error CS0570: 'DefaultInterpolatedStringHandler.ToStringAndClear()' is not supported by the language // _ = $"{(object)1}"; Diagnostic(ErrorCode.ERR_BindToBogus, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()").WithLocation(1, 5) ); } [Theory] [InlineData(@"$""{i}{s}""")] [InlineData(@"$""{i}"" + $""{s}""")] public void UnsupportedArgumentType(string expression) { var source = @" unsafe { int* i = null; var s = new S(); _ = " + expression + @"; } ref struct S { }"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: true, useBoolReturns: false); var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics( // 0.cs(6,11): error CS0306: The type 'int*' may not be used as a type argument // _ = $"{i}{s}"; Diagnostic(ErrorCode.ERR_BadTypeArgument, "{i}").WithArguments("int*").WithLocation(6, 11), // 0.cs(6,14): error CS9244: The type 'S' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'DefaultInterpolatedStringHandler.AppendFormatted<T>(T, int, string)' // _ = $"{i}{s}"; Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "{s}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<T>(T, int, string)", "T", "S").WithLocation(6, 5 + expression.Length) ); } [Theory] [InlineData(@"$""{b switch { true => 1, false => null }}{(!b ? null : 2)}{default}{null}""")] [InlineData(@"$""{b switch { true => 1, false => null }}"" + $""{(!b ? null : 2)}"" + $""{default}"" + $""{null}""")] public void TargetTypedInterpolationHoles(string expression) { var source = @" bool b = true; System.Console.WriteLine(" + expression + @");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" value:1 value:2 value: value:"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 81 (0x51) .maxstack 3 .locals init (bool V_0, //b object V_1, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_2 IL_0004: ldc.i4.0 IL_0005: ldc.i4.4 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloc.0 IL_000c: brfalse.s IL_0017 IL_000e: ldc.i4.1 IL_000f: box ""int"" IL_0014: stloc.1 IL_0015: br.s IL_0019 IL_0017: ldnull IL_0018: stloc.1 IL_0019: ldloca.s V_2 IL_001b: ldloc.1 IL_001c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)"" IL_0021: ldloca.s V_2 IL_0023: ldloc.0 IL_0024: brfalse.s IL_002e IL_0026: ldc.i4.2 IL_0027: box ""int"" IL_002c: br.s IL_002f IL_002e: ldnull IL_002f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)"" IL_0034: ldloca.s V_2 IL_0036: ldnull IL_0037: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_003c: ldloca.s V_2 IL_003e: ldnull IL_003f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0044: ldloca.s V_2 IL_0046: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_004b: call ""void System.Console.WriteLine(string)"" IL_0050: ret } "); } [Theory] [InlineData(@"$""{(null, default)}{new()}""")] [InlineData(@"$""{(null, default)}"" + $""{new()}""")] public void TargetTypedInterpolationHoles_Errors(string expression) { var source = @"System.Console.WriteLine(" + expression + @");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( // (1,29): error CS1503: Argument 1: cannot convert from '(<null>, default)' to 'object' // System.Console.WriteLine($"{(null, default)}{new()}"); Diagnostic(ErrorCode.ERR_BadArgType, "(null, default)").WithArguments("1", "(<null>, default)", "object").WithLocation(1, 29), // (1,29): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. // System.Console.WriteLine($"{(null, default)}{new()}"); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(null, default)").WithArguments("interpolated string handlers", "10.0").WithLocation(1, 29), // (1,46): error CS1729: 'string' does not contain a constructor that takes 0 arguments // System.Console.WriteLine($"{(null, default)}{new()}"); Diagnostic(ErrorCode.ERR_BadCtorArgCount, "new()").WithArguments("string", "0").WithLocation(1, 19 + expression.Length) ); } [Fact] public void RefTernary() { var source = @" bool b = true; int i = 1; System.Console.WriteLine($""{(!b ? ref i : ref i)}"");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @"value:1"); } [Fact] public void NestedInterpolatedStrings_01() { var source = @" int i = 1; System.Console.WriteLine($""{$""{i}""}"");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @"value:1"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 32 (0x20) .maxstack 3 .locals init (int V_0, //i System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.0 IL_0005: ldc.i4.1 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldloc.0 IL_000e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0013: ldloca.s V_1 IL_0015: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_001a: call ""void System.Console.WriteLine(string)"" IL_001f: ret } "); } [Theory] [InlineData(@"$""{$""{i1}""}{$""{i2}""}""")] [InlineData(@"$""{$""{i1}""}"" + $""{$""{i2}""}""")] public void NestedInterpolatedStrings_02(string expression) { var source = @" int i1 = 1; int i2 = 2; System.Console.WriteLine(" + expression + @");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" value:1 value:2"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 63 (0x3f) .maxstack 4 .locals init (int V_0, //i1 int V_1, //i2 System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.2 IL_0003: stloc.1 IL_0004: ldloca.s V_2 IL_0006: ldc.i4.0 IL_0007: ldc.i4.1 IL_0008: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0015: ldloca.s V_2 IL_0017: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_001c: ldloca.s V_3 IL_001e: ldc.i4.0 IL_001f: ldc.i4.1 IL_0020: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0025: ldloca.s V_3 IL_0027: ldloc.1 IL_0028: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_002d: ldloca.s V_3 IL_002f: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0034: call ""string string.Concat(string, string)"" IL_0039: call ""void System.Console.WriteLine(string)"" IL_003e: ret } "); } [Fact] public void ExceptionFilter_01() { var source = @" using System; int i = 1; try { Console.WriteLine(""Starting try""); throw new MyException { Prop = i }; } // Test DefaultInterpolatedStringHandler renders specially, so we're actually comparing to ""value:Prop"" plus some whitespace catch (MyException e) when (e.ToString() == $""{i}"".Trim()) { Console.WriteLine(""Caught""); } class MyException : Exception { public int Prop { get; set; } public override string ToString() => ""value:"" + Prop.ToString(); }"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" Starting try Caught"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 95 (0x5f) .maxstack 4 .locals init (int V_0, //i System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 .try { IL_0002: ldstr ""Starting try"" IL_0007: call ""void System.Console.WriteLine(string)"" IL_000c: newobj ""MyException..ctor()"" IL_0011: dup IL_0012: ldloc.0 IL_0013: callvirt ""void MyException.Prop.set"" IL_0018: throw } filter { IL_0019: isinst ""MyException"" IL_001e: dup IL_001f: brtrue.s IL_0025 IL_0021: pop IL_0022: ldc.i4.0 IL_0023: br.s IL_004f IL_0025: callvirt ""string object.ToString()"" IL_002a: ldloca.s V_1 IL_002c: ldc.i4.0 IL_002d: ldc.i4.1 IL_002e: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0033: ldloca.s V_1 IL_0035: ldloc.0 IL_0036: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_003b: ldloca.s V_1 IL_003d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0042: callvirt ""string string.Trim()"" IL_0047: call ""bool string.op_Equality(string, string)"" IL_004c: ldc.i4.0 IL_004d: cgt.un IL_004f: endfilter } // end filter { // handler IL_0051: pop IL_0052: ldstr ""Caught"" IL_0057: call ""void System.Console.WriteLine(string)"" IL_005c: leave.s IL_005e } IL_005e: ret } "); } [ConditionalFact(typeof(MonoOrCoreClrOnly), typeof(NoIOperationValidation))] public void ExceptionFilter_02() { var source = @" using System; Span<char> s = new char[] { 'i' }; try { Console.WriteLine(""Starting try""); throw new MyException { Prop = s.ToString() }; } // Test DefaultInterpolatedStringHandler renders specially, so we're actually comparing to ""value:Prop"" plus some whitespace catch (MyException e) when (e.ToString() == $""{(ReadOnlySpan<char>)s}"".Trim()) { Console.WriteLine(""Caught""); } class MyException : Exception { public string Prop { get; set; } public override string ToString() => ""value:"" + Prop.ToString(); }"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false); var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, targetFramework: TargetFramework.NetCoreApp, expectedOutput: @" Starting try Caught"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 127 (0x7f) .maxstack 4 .locals init (System.Span<char> V_0, //s System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: newarr ""char"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.s 105 IL_000a: stelem.i2 IL_000b: call ""System.Span<char> System.Span<char>.op_Implicit(char[])"" IL_0010: stloc.0 .try { IL_0011: ldstr ""Starting try"" IL_0016: call ""void System.Console.WriteLine(string)"" IL_001b: newobj ""MyException..ctor()"" IL_0020: dup IL_0021: ldloca.s V_0 IL_0023: constrained. ""System.Span<char>"" IL_0029: callvirt ""string object.ToString()"" IL_002e: callvirt ""void MyException.Prop.set"" IL_0033: throw } filter { IL_0034: isinst ""MyException"" IL_0039: dup IL_003a: brtrue.s IL_0040 IL_003c: pop IL_003d: ldc.i4.0 IL_003e: br.s IL_006f IL_0040: callvirt ""string object.ToString()"" IL_0045: ldloca.s V_1 IL_0047: ldc.i4.0 IL_0048: ldc.i4.1 IL_0049: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_004e: ldloca.s V_1 IL_0050: ldloc.0 IL_0051: call ""System.ReadOnlySpan<char> System.Span<char>.op_Implicit(System.Span<char>)"" IL_0056: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>)"" IL_005b: ldloca.s V_1 IL_005d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0062: callvirt ""string string.Trim()"" IL_0067: call ""bool string.op_Equality(string, string)"" IL_006c: ldc.i4.0 IL_006d: cgt.un IL_006f: endfilter } // end filter { // handler IL_0071: pop IL_0072: ldstr ""Caught"" IL_0077: call ""void System.Console.WriteLine(string)"" IL_007c: leave.s IL_007e } IL_007e: ret } "); } [ConditionalTheory(typeof(MonoOrCoreClrOnly), typeof(NoIOperationValidation))] [InlineData(@"$""{s}{c}""")] [InlineData(@"$""{s}"" + $""{c}""")] public void ImplicitUserDefinedConversionInHole(string expression) { var source = @" using System; S s = default; C c = new C(); Console.WriteLine(" + expression + @"); ref struct S { public static implicit operator ReadOnlySpan<char>(S s) => ""S converted""; } class C { public static implicit operator ReadOnlySpan<char>(C s) => ""C converted""; }"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false); var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, targetFramework: TargetFramework.NetCoreApp); // ILVerify: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: @" value:S converted value:C"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 57 (0x39) .maxstack 3 .locals init (S V_0, //s C V_1, //c System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: newobj ""C..ctor()"" IL_000d: stloc.1 IL_000e: ldloca.s V_2 IL_0010: ldc.i4.0 IL_0011: ldc.i4.2 IL_0012: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0017: ldloca.s V_2 IL_0019: ldloc.0 IL_001a: call ""System.ReadOnlySpan<char> S.op_Implicit(S)"" IL_001f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char>)"" IL_0024: ldloca.s V_2 IL_0026: ldloc.1 IL_0027: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<C>(C)"" IL_002c: ldloca.s V_2 IL_002e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0033: call ""void System.Console.WriteLine(string)"" IL_0038: ret } "); } [Fact] public void ExplicitUserDefinedConversionInHole() { var source = @" using System; S s = default; Console.WriteLine($""{s}""); ref struct S { public static explicit operator ReadOnlySpan<char>(S s) => ""S converted""; } "; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false); var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics( // 0.cs(5,21): error CS9244: The type 'S' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'DefaultInterpolatedStringHandler.AppendFormatted<T>(T)' // Console.WriteLine($"{s}"); Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "{s}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<T>(T)", "T", "S").WithLocation(5, 21) ); } [Theory] [InlineData(@"$""Text{1}""")] [InlineData(@"$""Text"" + $""{1}""")] public void ImplicitUserDefinedConversionInLiteral(string expression) { var source = @" using System; Console.WriteLine(" + expression + @"); public struct CustomStruct { public static implicit operator CustomStruct(string s) => new CustomStruct { S = s }; public string S { get; set; } public override string ToString() => ""literal:"" + S; } namespace System.Runtime.CompilerServices { using System.Text; public ref struct DefaultInterpolatedStringHandler { private readonly StringBuilder _builder; public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public string ToStringAndClear() => _builder.ToString(); public void AppendLiteral(CustomStruct s) => _builder.AppendLine(s.ToString()); public void AppendFormatted(object o) => _builder.AppendLine(""value:"" + o.ToString()); } }"; var verifier = CompileAndVerify(source, expectedOutput: @" literal:Text value:1"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 52 (0x34) .maxstack 3 .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.4 IL_0003: ldc.i4.1 IL_0004: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldstr ""Text"" IL_0010: call ""CustomStruct CustomStruct.op_Implicit(string)"" IL_0015: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(CustomStruct)"" IL_001a: ldloca.s V_0 IL_001c: ldc.i4.1 IL_001d: box ""int"" IL_0022: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)"" IL_0027: ldloca.s V_0 IL_0029: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_002e: call ""void System.Console.WriteLine(string)"" IL_0033: ret } "); } [Theory] [InlineData(@"$""Text{1}""")] [InlineData(@"$""Text"" + $""{1}""")] public void ExplicitUserDefinedConversionInLiteral(string expression) { var source = @" using System; Console.WriteLine(" + expression + @"); public struct CustomStruct { public static explicit operator CustomStruct(string s) => new CustomStruct { S = s }; public string S { get; set; } public override string ToString() => ""literal:"" + S; } namespace System.Runtime.CompilerServices { using System.Text; public ref struct DefaultInterpolatedStringHandler { private readonly StringBuilder _builder; public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public string ToStringAndClear() => _builder.ToString(); public void AppendLiteral(CustomStruct s) => _builder.AppendLine(s.ToString()); public void AppendFormatted(object o) => _builder.AppendLine(""value:"" + o.ToString()); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,21): error CS1503: Argument 1: cannot convert from 'string' to 'CustomStruct' // Console.WriteLine($"Text{1}"); Diagnostic(ErrorCode.ERR_BadArgType, "Text").WithArguments("1", "string", "CustomStruct").WithLocation(4, 21) ); } [Fact, WorkItem(58346, "https://github.com/dotnet/roslyn/issues/58346")] public void UserDefinedConversion_AsFromTypeOfConversion_01() { var code = @" struct S { public static implicit operator S(CustomHandler c) => default; static void M() { /*<bind>*/S s = $"""";/*</bind>*/ } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, handler }); comp.VerifyDiagnostics( // (8,25): error CS0029: Cannot implicitly convert type 'string' to 'S' // /*<bind>*/S s = $"";/*<bind>*/ Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""""").WithArguments("string", "S").WithLocation(8, 25) ); VerifyOperationTreeForTest<LocalDeclarationStatementSyntax>(comp, @" IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'S s = $"""";') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'S s = $""""') Declarators: IVariableDeclaratorOperation (Symbol: S s) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 's = $""""') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= $""""') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsInvalid, IsImplicit) (Syntax: '$""""') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: """", IsInvalid) (Syntax: '$""""') Parts(0) Initializer: null "); } [Fact, WorkItem(58346, "https://github.com/dotnet/roslyn/issues/58346")] public void UserDefinedConversion_AsFromTypeOfConversion_02() { var code = @" struct S { public static implicit operator S(CustomHandler c) => default; static void M() { /*<bind>*/S s = (S)$"""";/*</bind>*/ } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, handler }); comp.VerifyDiagnostics( // (8,25): error CS0030: Cannot convert type 'string' to 'S' // /*<bind>*/S s = (S)$"";/*<bind>*/ Diagnostic(ErrorCode.ERR_NoExplicitConv, @"(S)$""""").WithArguments("string", "S").WithLocation(8, 25) ); VerifyOperationTreeForTest<LocalDeclarationStatementSyntax>(comp, @" IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'S s = (S)$"""";') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'S s = (S)$""""') Declarators: IVariableDeclaratorOperation (Symbol: S s) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 's = (S)$""""') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= (S)$""""') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsInvalid) (Syntax: '(S)$""""') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: """", IsInvalid) (Syntax: '$""""') Parts(0) Initializer: null "); } [Fact, WorkItem(58346, "https://github.com/dotnet/roslyn/issues/58346")] public void UserDefinedConversion_AsFromTypeOfConversion_03() { var code = @" /*<bind>*/S s = (CustomHandler)$"""";/*</bind>*/ struct S { public static implicit operator S(CustomHandler c) { System.Console.WriteLine(""In handler""); return default; } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, handler }); CompileAndVerify(comp, expectedOutput: "In handler").VerifyDiagnostics(); VerifyOperationTreeForTest<LocalDeclarationStatementSyntax>(comp, @" IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'S s = (Cust ... andler)$"""";') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'S s = (CustomHandler)$""""') Declarators: IVariableDeclaratorOperation (Symbol: S s) (OperationKind.VariableDeclarator, Type: null) (Syntax: 's = (CustomHandler)$""""') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= (CustomHandler)$""""') IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: S S.op_Implicit(CustomHandler c)) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: '(CustomHandler)$""""') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: S S.op_Implicit(CustomHandler c)) Operand: IInterpolatedStringHandlerCreationOperation (HandlerAppendCallsReturnBool: False, HandlerCreationHasSuccessParameter: False) (OperationKind.InterpolatedStringHandlerCreation, Type: CustomHandler) (Syntax: '(CustomHandler)$""""') Creation: IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount)) (OperationKind.ObjectCreation, Type: CustomHandler, IsImplicit) (Syntax: '$""""') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$""""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$""""') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$""""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$""""') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Content: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: """") (Syntax: '$""""') Parts(0) Initializer: null "); } [Theory] [InlineData(@"$""Text{1}""")] [InlineData(@"$""Text"" + $""{1}""")] public void InvalidBuilderReturnType(string expression) { var source = @" using System; Console.WriteLine(" + expression + @"); namespace System.Runtime.CompilerServices { using System.Text; public ref struct DefaultInterpolatedStringHandler { private readonly StringBuilder _builder; public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public string ToStringAndClear() => _builder.ToString(); public int AppendLiteral(string s) => 0; public int AppendFormatted(object o) => 0; } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,21): error CS8941: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendLiteral(string)' is malformed. It does not return 'void' or 'bool'. // Console.WriteLine($"Text{1}"); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)").WithLocation(4, 21), // (4,25): error CS8941: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendFormatted(object)' is malformed. It does not return 'void' or 'bool'. // Console.WriteLine($"Text{1}"); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)").WithLocation(4, 15 + expression.Length) ); } [Fact] public void MissingAppendMethods() { var source = @" using System.Runtime.CompilerServices; CustomHandler c = $""Literal{1}""; [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } } "; var comp = CreateCompilation(new[] { source, InterpolatedStringHandlerAttribute }); comp.VerifyDiagnostics( // (4,21): error CS1061: 'CustomHandler' does not contain a definition for 'AppendLiteral' and no accessible extension method 'AppendLiteral' accepting a first argument of type 'CustomHandler' could be found (are you missing a using directive or an assembly reference?) // CustomHandler c = $"Literal{1}"; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Literal").WithArguments("CustomHandler", "AppendLiteral").WithLocation(4, 21), // (4,21): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. // CustomHandler c = $"Literal{1}"; Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "Literal").WithArguments("?.()").WithLocation(4, 21), // (4,28): error CS1061: 'CustomHandler' does not contain a definition for 'AppendFormatted' and no accessible extension method 'AppendFormatted' accepting a first argument of type 'CustomHandler' could be found (are you missing a using directive or an assembly reference?) // CustomHandler c = $"Literal{1}"; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "{1}").WithArguments("CustomHandler", "AppendFormatted").WithLocation(4, 28), // (4,28): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. // CustomHandler c = $"Literal{1}"; Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{1}").WithArguments("?.()").WithLocation(4, 28) ); } [Fact] public void MissingBoolType() { var handlerSource = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var handlerRef = CreateCompilation(handlerSource).EmitToImageReference(); var source = @"CustomHandler c = $""Literal{1}"";"; var comp = CreateCompilation(source, references: new[] { handlerRef }); comp.MakeTypeMissing(SpecialType.System_Boolean); comp.VerifyDiagnostics( // (1,19): error CS0518: Predefined type 'System.Boolean' is not defined or imported // CustomHandler c = $"Literal{1}"; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"$""Literal{1}""").WithArguments("System.Boolean").WithLocation(1, 19) ); } [Fact] public void MissingVoidType() { var handlerSource = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); var handlerRef = CreateCompilation(handlerSource).EmitToImageReference(); var source = @" class C { public bool M() { CustomHandler c = $""Literal{1}""; return true; } } "; var comp = CreateCompilation(source, references: new[] { handlerRef }); comp.MakeTypeMissing(SpecialType.System_Void); comp.VerifyEmitDiagnostics(); } [Theory] [InlineData(@"$""Text{1}""", @"$""{1}Text""")] [InlineData(@"$""Text"" + $""{1}""", @"$""{1}"" + $""Text""")] public void MixedBuilderReturnTypes_01(string expression1, string expression2) { var source = @" using System; Console.WriteLine(" + expression1 + @"); Console.WriteLine(" + expression2 + @"); namespace System.Runtime.CompilerServices { using System.Text; public ref struct DefaultInterpolatedStringHandler { private readonly StringBuilder _builder; public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public string ToStringAndClear() => _builder.ToString(); public bool AppendLiteral(string s) => true; public void AppendFormatted(object o) { } } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,25): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendFormatted(object)' has inconsistent return type. Expected to return 'bool'. // Console.WriteLine($"Text{1}"); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)", "bool").WithLocation(4, 15 + expression1.Length), // (5,24): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendLiteral(string)' has inconsistent return type. Expected to return 'void'. // Console.WriteLine($"{1}Text"); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "void").WithLocation(5, 14 + expression2.Length) ); } [Theory] [InlineData(@"$""Text{1}""", @"$""{1}Text""")] [InlineData(@"$""Text"" + $""{1}""", @"$""{1}"" + $""Text""")] public void MixedBuilderReturnTypes_02(string expression1, string expression2) { var source = @" using System; Console.WriteLine(" + expression1 + @"); Console.WriteLine(" + expression2 + @"); namespace System.Runtime.CompilerServices { using System.Text; public ref struct DefaultInterpolatedStringHandler { private readonly StringBuilder _builder; public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public string ToStringAndClear() => _builder.ToString(); public void AppendLiteral(string s) { } public bool AppendFormatted(object o) => true; } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,25): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendFormatted(object)' has inconsistent return type. Expected to return 'void'. // Console.WriteLine($"Text{1}"); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)", "void").WithLocation(4, 15 + expression1.Length), // (5,24): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendLiteral(string)' has inconsistent return type. Expected to return 'bool'. // Console.WriteLine($"{1}Text"); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "bool").WithLocation(5, 14 + expression2.Length) ); } [Fact] public void MixedBuilderReturnTypes_03() { var source = @" using System; Console.WriteLine($""{1}""); namespace System.Runtime.CompilerServices { using System.Text; public ref struct DefaultInterpolatedStringHandler { private readonly StringBuilder _builder; public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public string ToStringAndClear() => _builder.ToString(); public bool AppendLiteral(string s) => true; public void AppendFormatted(object o) { _builder.AppendLine(""value:"" + o.ToString()); } } }"; CompileAndVerify(source, expectedOutput: "value:1"); } [Fact] public void MixedBuilderReturnTypes_04() { var source = @" using System; using System.Text; using System.Runtime.CompilerServices; Console.WriteLine((CustomHandler)$""l""); [InterpolatedStringHandler] public class CustomHandler { private readonly StringBuilder _builder; public CustomHandler(int literalLength, int formattedCount) { _builder = new StringBuilder(); } public override string ToString() => _builder.ToString(); public bool AppendFormatted(object o) => true; public void AppendLiteral(string s) { _builder.AppendLine(""literal:"" + s.ToString()); } } "; CompileAndVerify(new[] { source, InterpolatedStringHandlerAttribute }, expectedOutput: "literal:l"); } private static void VerifyInterpolatedStringExpression(CSharpCompilation comp, string handlerType = "CustomHandler") { var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var descendentNodes = tree.GetRoot().DescendantNodes(); var interpolatedString = (ExpressionSyntax)descendentNodes.OfType<BinaryExpressionSyntax>() .Where(b => b.DescendantNodes().OfType<InterpolatedStringExpressionSyntax>().Any()) .FirstOrDefault() ?? descendentNodes.OfType<InterpolatedStringExpressionSyntax>().Single(); var semanticInfo = model.GetSemanticInfoSummary(interpolatedString); Assert.Equal(SpecialType.System_String, semanticInfo.Type.SpecialType); Assert.Equal(handlerType, semanticInfo.ConvertedType.ToTestDisplayString()); Assert.Equal(ConversionKind.InterpolatedStringHandler, semanticInfo.ImplicitConversion.Kind); Assert.True(semanticInfo.ImplicitConversion.Exists); Assert.True(semanticInfo.ImplicitConversion.IsValid); Assert.True(semanticInfo.ImplicitConversion.IsInterpolatedStringHandler); Assert.Null(semanticInfo.ImplicitConversion.Method); if (interpolatedString is BinaryExpressionSyntax) { Assert.False(semanticInfo.ConstantValue.HasValue); AssertEx.Equal("System.String System.String.op_Addition(System.String left, System.String right)", semanticInfo.Symbol.ToTestDisplayString()); } // https://github.com/dotnet/roslyn/issues/54505 Assert IConversionOperation.IsImplicit when IOperation is implemented for interpolated strings. } private CompilationVerifier CompileAndVerifyOnCorrectPlatforms(CSharpCompilation compilation, string expectedOutput) => CompileAndVerify( compilation, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); [Theory] [CombinatorialData] public void CustomHandlerLocal([CombinatorialValues("class", "struct")] string type, bool useBoolReturns, [CombinatorialValues(@"$""Literal{1,2:f}""", @"$""Literal"" + $""{1,2:f}""")] string expression) { var code = @" CustomHandler builder = " + expression + @"; System.Console.WriteLine(builder.ToString());"; var builder = GetInterpolatedStringCustomHandlerType("CustomHandler", type, useBoolReturns); var comp = CreateCompilation(new[] { code, builder }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" literal:Literal value:1 alignment:2 format:f"); verifier.VerifyIL("<top-level-statements-entry-point>", getIl()); string getIl() => (type, useBoolReturns) switch { (type: "struct", useBoolReturns: true) => @" { // Code size 67 (0x43) .maxstack 4 .locals init (CustomHandler V_0, //builder CustomHandler V_1) IL_0000: ldloca.s V_1 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_1 IL_000b: ldstr ""Literal"" IL_0010: call ""bool CustomHandler.AppendLiteral(string)"" IL_0015: brfalse.s IL_002c IL_0017: ldloca.s V_1 IL_0019: ldc.i4.1 IL_001a: box ""int"" IL_001f: ldc.i4.2 IL_0020: ldstr ""f"" IL_0025: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_002a: br.s IL_002d IL_002c: ldc.i4.0 IL_002d: pop IL_002e: ldloc.1 IL_002f: stloc.0 IL_0030: ldloca.s V_0 IL_0032: constrained. ""CustomHandler"" IL_0038: callvirt ""string object.ToString()"" IL_003d: call ""void System.Console.WriteLine(string)"" IL_0042: ret } ", (type: "struct", useBoolReturns: false) => @" { // Code size 61 (0x3d) .maxstack 4 .locals init (CustomHandler V_0, //builder CustomHandler V_1) IL_0000: ldloca.s V_1 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_1 IL_000b: ldstr ""Literal"" IL_0010: call ""void CustomHandler.AppendLiteral(string)"" IL_0015: ldloca.s V_1 IL_0017: ldc.i4.1 IL_0018: box ""int"" IL_001d: ldc.i4.2 IL_001e: ldstr ""f"" IL_0023: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0028: ldloc.1 IL_0029: stloc.0 IL_002a: ldloca.s V_0 IL_002c: constrained. ""CustomHandler"" IL_0032: callvirt ""string object.ToString()"" IL_0037: call ""void System.Console.WriteLine(string)"" IL_003c: ret } ", (type: "class", useBoolReturns: true) => @" { // Code size 55 (0x37) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldstr ""Literal"" IL_000e: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0013: brfalse.s IL_0029 IL_0015: ldloc.0 IL_0016: ldc.i4.1 IL_0017: box ""int"" IL_001c: ldc.i4.2 IL_001d: ldstr ""f"" IL_0022: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: callvirt ""string object.ToString()"" IL_0031: call ""void System.Console.WriteLine(string)"" IL_0036: ret } ", (type: "class", useBoolReturns: false) => @" { // Code size 47 (0x2f) .maxstack 5 IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: dup IL_0008: ldstr ""Literal"" IL_000d: callvirt ""void CustomHandler.AppendLiteral(string)"" IL_0012: dup IL_0013: ldc.i4.1 IL_0014: box ""int"" IL_0019: ldc.i4.2 IL_001a: ldstr ""f"" IL_001f: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0024: callvirt ""string object.ToString()"" IL_0029: call ""void System.Console.WriteLine(string)"" IL_002e: ret } ", _ => throw ExceptionUtilities.Unreachable() }; } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void CustomHandlerMethodArgument(string expression) { var code = @" M(" + expression + @"); void M(CustomHandler b) { System.Console.WriteLine(b.ToString()); }"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL(@"<top-level-statements-entry-point>", @" { // Code size 50 (0x32) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: box ""int"" IL_000f: ldc.i4.2 IL_0010: ldstr ""f"" IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001a: brfalse.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""Literal"" IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_0031: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"($""{1,2:f}"" + $""Literal"")")] public void ExplicitHandlerCast_InCode(string expression) { var code = @"System.Console.WriteLine((CustomHandler)" + expression + @");"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); SyntaxNode syntax = tree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().Single(); var semanticInfo = model.GetSemanticInfoSummary(syntax); Assert.Equal("CustomHandler", semanticInfo.Type.ToTestDisplayString()); Assert.Equal(SpecialType.System_Object, semanticInfo.ConvertedType.SpecialType); Assert.Equal(ConversionKind.ImplicitReference, semanticInfo.ImplicitConversion.Kind); syntax = ((CastExpressionSyntax)syntax).Expression; Assert.Equal(expression, syntax.ToString()); semanticInfo = model.GetSemanticInfoSummary(syntax); Assert.Equal(SpecialType.System_String, semanticInfo.Type.SpecialType); Assert.Equal(SpecialType.System_String, semanticInfo.ConvertedType.SpecialType); Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind); // https://github.com/dotnet/roslyn/issues/54505 Assert cast is explicit after IOperation is implemented var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 42 (0x2a) .maxstack 5 IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: dup IL_0008: ldc.i4.1 IL_0009: box ""int"" IL_000e: ldc.i4.2 IL_000f: ldstr ""f"" IL_0014: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0019: dup IL_001a: ldstr ""Literal"" IL_001f: callvirt ""void CustomHandler.AppendLiteral(string)"" IL_0024: call ""void System.Console.WriteLine(object)"" IL_0029: ret } "); } [Theory, WorkItem(55345, "https://github.com/dotnet/roslyn/issues/55345")] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void HandlerConversionPreferredOverStringForNonConstant(string expression) { var code = @" CultureInfoNormalizer.Normalize(); C.M(" + expression + @"); CultureInfoNormalizer.Reset(); class C { public static void M(CustomHandler b) { System.Console.WriteLine(b.ToString()); } public static void M(string s) { System.Console.WriteLine(s); } } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }, parseOptions: TestOptions.Regular10); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL(@"<top-level-statements-entry-point>", @" { // Code size 60 (0x3c) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldc.i4.7 IL_0006: ldc.i4.1 IL_0007: newobj ""CustomHandler..ctor(int, int)"" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ldc.i4.1 IL_000f: box ""int"" IL_0014: ldc.i4.2 IL_0015: ldstr ""f"" IL_001a: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001f: brfalse.s IL_002e IL_0021: ldloc.0 IL_0022: ldstr ""Literal"" IL_0027: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_002c: br.s IL_002f IL_002e: ldc.i4.0 IL_002f: pop IL_0030: ldloc.0 IL_0031: call ""void C.M(CustomHandler)"" IL_0036: call ""void CultureInfoNormalizer.Reset()"" IL_003b: ret } "); comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }, parseOptions: TestOptions.Regular9); verifier = CompileAndVerify(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL(@"<top-level-statements-entry-point>", expression.Contains('+') ? @" { // Code size 42 (0x2a) .maxstack 2 IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldstr ""{0,2:f}"" IL_000a: ldc.i4.1 IL_000b: box ""int"" IL_0010: call ""string string.Format(string, object)"" IL_0015: ldstr ""Literal"" IL_001a: call ""string string.Concat(string, string)"" IL_001f: call ""void C.M(string)"" IL_0024: call ""void CultureInfoNormalizer.Reset()"" IL_0029: ret } " : @" { // Code size 32 (0x20) .maxstack 2 IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldstr ""{0,2:f}Literal"" IL_000a: ldc.i4.1 IL_000b: box ""int"" IL_0010: call ""string string.Format(string, object)"" IL_0015: call ""void C.M(string)"" IL_001a: call ""void CultureInfoNormalizer.Reset()"" IL_001f: ret } "); } [Theory] [InlineData(@"$""{""Literal""}""")] [InlineData(@"$""{""Lit""}"" + $""{""eral""}""")] public void StringPreferredOverHandlerConversionForConstant(string expression) { var code = @" C.M(" + expression + @"); class C { public static void M(CustomHandler b) { throw null; } public static void M(string s) { System.Console.WriteLine(s); } } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); var verifier = CompileAndVerify(comp, expectedOutput: @"Literal"); verifier.VerifyIL(@"<top-level-statements-entry-point>", @" { // Code size 11 (0xb) .maxstack 1 IL_0000: ldstr ""Literal"" IL_0005: call ""void C.M(string)"" IL_000a: ret } "); } [Theory] [InlineData(@"$""{1}{2}""")] [InlineData(@"$""{1}"" + $""{2}""")] public void HandlerConversionPreferredOverStringForNonConstant_AttributeConstructor(string expression) { var code = @" using System; [Attr(" + expression + @")] class Attr : Attribute { public Attr(string s) {} public Attr(CustomHandler c) {} } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); comp.VerifyDiagnostics( // (4,7): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [Attr($"{1}{2}")] Diagnostic(ErrorCode.ERR_BadAttributeArgument, expression).WithLocation(4, 7) ); var attr = comp.SourceAssembly.SourceModule.GlobalNamespace.GetTypeMember("Attr"); // Note that for usage in attributes, we don't use the custom handler. This is because it's an error scenario regardless, and we want to avoid // potential binding cycles. Assert.Equal("Attr..ctor(System.String s)", attr.GetAttributes().Single().AttributeConstructor.ToTestDisplayString()); } [Theory] [InlineData(@"$""{""Literal""}""")] [InlineData(@"$""{""Lit""}"" + $""{""eral""}""")] public void StringPreferredOverHandlerConversionForConstant_AttributeConstructor(string expression) { var code = @" using System; [Attr(" + expression + @")] class Attr : Attribute { public Attr(string s) {} public Attr(CustomHandler c) {} } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate); void validate(ModuleSymbol m) { var attr = m.GlobalNamespace.GetTypeMember("Attr"); Assert.Equal("Attr..ctor(System.String s)", attr.GetAttributes().Single().AttributeConstructor.ToTestDisplayString()); } } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void MultipleBuilderTypes(string expression) { var code = @" C.M(" + expression + @"); class C { public static void M(CustomHandler1 c) => throw null; public static void M(CustomHandler2 c) => throw null; }"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler1", "struct", useBoolReturns: false), GetInterpolatedStringCustomHandlerType("CustomHandler2", "struct", useBoolReturns: false, includeOneTimeHelpers: false) }); comp.VerifyDiagnostics( // (2,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(CustomHandler1)' and 'C.M(CustomHandler2)' // C.M($""); Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(CustomHandler1)", "C.M(CustomHandler2)").WithLocation(2, 3) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericOverloadResolution_01(string expression) { var code = @" using System; C.M(" + expression + @"); class C { public static void M<T>(T t) => throw null; public static void M(CustomHandler c) => Console.WriteLine(c); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 50 (0x32) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: box ""int"" IL_000f: ldc.i4.2 IL_0010: ldstr ""f"" IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001a: brfalse.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""Literal"" IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: call ""void C.M(CustomHandler)"" IL_0031: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericOverloadResolution_02(string expression) { var code = @" using System; C.M(" + expression + @"); class C { public static void M<T>(T t) where T : CustomHandler => throw null; public static void M(CustomHandler c) => Console.WriteLine(c); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 50 (0x32) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: box ""int"" IL_000f: ldc.i4.2 IL_0010: ldstr ""f"" IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001a: brfalse.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""Literal"" IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: call ""void C.M(CustomHandler)"" IL_0031: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericOverloadResolution_03(string expression) { var code = @" C.M(" + expression + @"); class C { public static void M<T>(T t) where T : CustomHandler => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); comp.VerifyDiagnostics( // (2,3): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'C.M<T>(T)'. There is no implicit reference conversion from 'string' to 'CustomHandler'. // C.M($"{1,2:f}Literal"); Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "M").WithArguments("C.M<T>(T)", "CustomHandler", "T", "string").WithLocation(2, 3) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericInference_01(string expression) { var code = @" C.M(" + expression + @", default(CustomHandler)); C.M(default(CustomHandler), " + expression + @"); class C { public static void M<T>(T t1, T t2) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (2,3): error CS0411: The type arguments for method 'C.M<T>(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // C.M($"{1,2:f}Literal", default(CustomHandler)); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M<T>(T, T)").WithLocation(2, 3), // (3,3): error CS0411: The type arguments for method 'C.M<T>(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // C.M(default(CustomHandler), $"{1,2:f}Literal"); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M<T>(T, T)").WithLocation(3, 3) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericInference_02(string expression) { var code = @" using System; C.M(default(CustomHandler), () => " + expression + @"); class C { public static void M<T>(T t1, Func<T> t2) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); comp.VerifyDiagnostics( // (3,3): error CS0411: The type arguments for method 'C.M<T>(T, Func<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // C.M(default(CustomHandler), () => $"{1,2:f}Literal"); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M<T>(T, System.Func<T>)").WithLocation(3, 3) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericInference_03(string expression) { var code = @" using System; C.M(" + expression + @", default(CustomHandler)); class C { public static void M<T>(T t1, T t2) => Console.WriteLine(t1); } partial class CustomHandler { public static implicit operator CustomHandler(string s) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: true) }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 51 (0x33) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: box ""int"" IL_000f: ldc.i4.2 IL_0010: ldstr ""f"" IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001a: brfalse.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""Literal"" IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: ldnull IL_002d: call ""void C.M<CustomHandler>(CustomHandler, CustomHandler)"" IL_0032: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void GenericInference_04(string expression) { var code = @" using System; C.M(default(CustomHandler), () => " + expression + @"); class C { public static void M<T>(T t1, Func<T> t2) => Console.WriteLine(t2()); } partial class CustomHandler { public static implicit operator CustomHandler(string s) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: true) }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("Program.<>c.<<Main>$>b__0_0()", @" { // Code size 45 (0x2d) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: box ""int"" IL_000f: ldc.i4.2 IL_0010: ldstr ""f"" IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001a: brfalse.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""Literal"" IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void LambdaReturnInference_01(string expression) { var code = @" using System; Func<CustomHandler> f = () => " + expression + @"; Console.WriteLine(f()); "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0()", @" { // Code size 45 (0x2d) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: box ""int"" IL_000f: ldc.i4.2 IL_0010: ldstr ""f"" IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_001a: brfalse.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""Literal"" IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.0 IL_002c: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void LambdaReturnInference_02(string expression) { var code = @" using System; CultureInfoNormalizer.Normalize(); C.M(() => " + expression + @"); CultureInfoNormalizer.Reset(); class C { public static void M(Func<string> f) => Console.WriteLine(f()); public static void M(Func<CustomHandler> f) => throw null; } "; // Interpolated string handler conversions are not considered when determining the natural type of an expression: the natural return type of this lambda is string, // so we don't even consider that there is a conversion from interpolated string expression to CustomHandler here (Sections 12.6.3.13 and 12.6.3.15 of the spec). var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); var verifier = CompileAndVerify(comp, expectedOutput: @"1.00Literal"); // No DefaultInterpolatedStringHandler was included in the compilation, so it falls back to string.Format verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0()", !expression.Contains('+') ? @" { // Code size 17 (0x11) .maxstack 2 IL_0000: ldstr ""{0,2:f}Literal"" IL_0005: ldc.i4.1 IL_0006: box ""int"" IL_000b: call ""string string.Format(string, object)"" IL_0010: ret } " : @" { // Code size 27 (0x1b) .maxstack 2 IL_0000: ldstr ""{0,2:f}"" IL_0005: ldc.i4.1 IL_0006: box ""int"" IL_000b: call ""string string.Format(string, object)"" IL_0010: ldstr ""Literal"" IL_0015: call ""string string.Concat(string, string)"" IL_001a: ret } "); } [Theory] [InlineData(@"$""{new S { Field = ""Field"" }}""")] [InlineData(@"$""{new S { Field = ""Field"" }}"" + $""""")] public void LambdaReturnInference_03(string expression) { // Same as 2, but using a type that isn't allowed in an interpolated string. There is an implicit conversion error on the ref struct // when converting to a string, because S cannot be a component of an interpolated string. This conversion error causes the lambda to // fail to bind as Func<string>, even though the natural return type is string, and the only successful bind is Func<CustomHandler>. var code = @" using System; C.M(() => " + expression + @"); static class C { public static void M(Func<string> f) => throw null; public static void M(Func<CustomHandler> f) => Console.WriteLine(f()); } public partial class CustomHandler { public void AppendFormatted(S value) => _builder.AppendLine(""value:"" + value.Field); } public ref struct S { public string Field { get; set; } } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"value:Field"); verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0()", @" { // Code size 35 (0x23) .maxstack 4 .locals init (S V_0) IL_0000: ldc.i4.0 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: dup IL_0008: ldloca.s V_0 IL_000a: initobj ""S"" IL_0010: ldloca.s V_0 IL_0012: ldstr ""Field"" IL_0017: call ""void S.Field.set"" IL_001c: ldloc.0 IL_001d: callvirt ""void CustomHandler.AppendFormatted(S)"" IL_0022: ret } "); } [Theory] [InlineData(@"$""{new S { Field = ""Field"" }}""")] [InlineData(@"$""{new S { Field = ""Field"" }}"" + $""""")] public void LambdaReturnInference_04(string expression) { // Same as 3, but with S added to DefaultInterpolatedStringHandler (which then allows the lambda to be bound as Func<string>, matching the natural return type) var code = @" using System; C.M(() => " + expression + @"); static class C { public static void M(Func<string> f) => Console.WriteLine(f()); public static void M(Func<CustomHandler> f) => throw null; } public partial class CustomHandler { public void AppendFormatted(S value) => throw null; } public ref struct S { public string Field { get; set; } } namespace System.Runtime.CompilerServices { public ref partial struct DefaultInterpolatedStringHandler { public void AppendFormatted(S value) => _builder.AppendLine(""value:"" + value.Field); } } "; string[] source = new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: false), GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: true, useBoolReturns: false) }; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (3,11): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. // C.M(() => $"{new S { Field = "Field" }}"); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, expression).WithArguments("interpolated string handlers", "10.0").WithLocation(3, 11), // (3,14): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. // C.M(() => $"{new S { Field = "Field" }}"); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, @"new S { Field = ""Field"" }").WithArguments("interpolated string handlers", "10.0").WithLocation(3, 14) ); comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"value:Field"); verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0()", @" { // Code size 45 (0x2d) .maxstack 3 .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0, S V_1) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.0 IL_0003: ldc.i4.1 IL_0004: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldloca.s V_1 IL_000d: initobj ""S"" IL_0013: ldloca.s V_1 IL_0015: ldstr ""Field"" IL_001a: call ""void S.Field.set"" IL_001f: ldloc.1 IL_0020: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(S)"" IL_0025: ldloca.s V_0 IL_0027: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_002c: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void LambdaReturnInference_05(string expression) { var code = @" using System; C.M(b => { if (b) return default(CustomHandler); else return " + expression + @"; }); static class C { public static void M(Func<bool, string> f) => throw null; public static void M(Func<bool, CustomHandler> f) => Console.WriteLine(f(false)); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0(bool)", @" { // Code size 55 (0x37) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldarg.1 IL_0001: brfalse.s IL_000d IL_0003: ldloca.s V_0 IL_0005: initobj ""CustomHandler"" IL_000b: ldloc.0 IL_000c: ret IL_000d: ldloca.s V_0 IL_000f: ldc.i4.7 IL_0010: ldc.i4.1 IL_0011: call ""CustomHandler..ctor(int, int)"" IL_0016: ldloca.s V_0 IL_0018: ldc.i4.1 IL_0019: box ""int"" IL_001e: ldc.i4.2 IL_001f: ldstr ""f"" IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0029: ldloca.s V_0 IL_002b: ldstr ""Literal"" IL_0030: call ""void CustomHandler.AppendLiteral(string)"" IL_0035: ldloc.0 IL_0036: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void LambdaReturnInference_06(string expression) { // Same as 5, but with an implicit conversion from the builder type to string. This implicit conversion // means that a best common type can be inferred for all branches of the lambda expression (Section 12.6.3.15 of the spec) // and because there is a best common type, the inferred return type of the lambda is string. Since the inferred return type // has an identity conversion to the return type of Func<bool, string>, that is preferred. var code = @" using System; CultureInfoNormalizer.Normalize(); C.M(b => { if (b) return default(CustomHandler); else return " + expression + @"; }); CultureInfoNormalizer.Reset(); static class C { public static void M(Func<bool, string> f) => Console.WriteLine(f(false)); public static void M(Func<bool, CustomHandler> f) => throw null; } public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0(bool)", !expression.Contains('+') ? @" { // Code size 35 (0x23) .maxstack 2 .locals init (CustomHandler V_0) IL_0000: ldarg.1 IL_0001: brfalse.s IL_0012 IL_0003: ldloca.s V_0 IL_0005: initobj ""CustomHandler"" IL_000b: ldloc.0 IL_000c: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0011: ret IL_0012: ldstr ""{0,2:f}Literal"" IL_0017: ldc.i4.1 IL_0018: box ""int"" IL_001d: call ""string string.Format(string, object)"" IL_0022: ret } " : @" { // Code size 45 (0x2d) .maxstack 2 .locals init (CustomHandler V_0) IL_0000: ldarg.1 IL_0001: brfalse.s IL_0012 IL_0003: ldloca.s V_0 IL_0005: initobj ""CustomHandler"" IL_000b: ldloc.0 IL_000c: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0011: ret IL_0012: ldstr ""{0,2:f}"" IL_0017: ldc.i4.1 IL_0018: box ""int"" IL_001d: call ""string string.Format(string, object)"" IL_0022: ldstr ""Literal"" IL_0027: call ""string string.Concat(string, string)"" IL_002c: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void LambdaReturnInference_07(string expression) { // Same as 5, but with an implicit conversion from string to the builder type. var code = @" using System; C.M(b => { if (b) return default(CustomHandler); else return " + expression + @"; }); static class C { public static void M(Func<bool, string> f) => Console.WriteLine(f(false)); public static void M(Func<bool, CustomHandler> f) => Console.WriteLine(f(false)); } public partial struct CustomHandler { public static implicit operator CustomHandler(string s) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL(@"Program.<>c.<<Main>$>b__0_0(bool)", @" { // Code size 55 (0x37) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldarg.1 IL_0001: brfalse.s IL_000d IL_0003: ldloca.s V_0 IL_0005: initobj ""CustomHandler"" IL_000b: ldloc.0 IL_000c: ret IL_000d: ldloca.s V_0 IL_000f: ldc.i4.7 IL_0010: ldc.i4.1 IL_0011: call ""CustomHandler..ctor(int, int)"" IL_0016: ldloca.s V_0 IL_0018: ldc.i4.1 IL_0019: box ""int"" IL_001e: ldc.i4.2 IL_001f: ldstr ""f"" IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0029: ldloca.s V_0 IL_002b: ldstr ""Literal"" IL_0030: call ""void CustomHandler.AppendLiteral(string)"" IL_0035: ldloc.0 IL_0036: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void LambdaReturnInference_08(string expression) { // Same as 5, but with an implicit conversion from the builder type to string and from string to the builder type. var code = @" using System; C.M(b => { if (b) return default(CustomHandler); else return " + expression + @"; }); static class C { public static void M(Func<bool, string> f) => Console.WriteLine(f(false)); public static void M(Func<bool, CustomHandler> f) => throw null; } public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => throw null; public static implicit operator CustomHandler(string c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(Func<bool, string>)' and 'C.M(Func<bool, CustomHandler>)' // C.M(b => Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.Func<bool, string>)", "C.M(System.Func<bool, CustomHandler>)").WithLocation(3, 3) ); } [Theory] [InlineData(@"$""{1}""")] [InlineData(@"$""{1}"" + $""{2}""")] public void LambdaInference_AmbiguousInOlderLangVersions(string expression) { var code = @" using System; C.M(param => { param = " + expression + @"; }); static class C { public static void M(Action<string> f) => throw null; public static void M(Action<CustomHandler> f) => throw null; } "; var source = new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); // This successful emit is being caused by https://github.com/dotnet/roslyn/issues/53761, along with the duplicate diagnostics in LambdaReturnInference_04 // We should not be changing binding behavior based on LangVersion. comp.VerifyEmitDiagnostics(); comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(Action<string>)' and 'C.M(Action<CustomHandler>)' // C.M(param => Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.Action<string>)", "C.M(System.Action<CustomHandler>)").WithLocation(3, 3) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void TernaryTypes_01(string expression) { var code = @" using System; var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 76 (0x4c) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.0 IL_0001: box ""bool"" IL_0006: unbox.any ""bool"" IL_000b: brtrue.s IL_0038 IL_000d: ldloca.s V_0 IL_000f: ldc.i4.7 IL_0010: ldc.i4.1 IL_0011: call ""CustomHandler..ctor(int, int)"" IL_0016: ldloca.s V_0 IL_0018: ldc.i4.1 IL_0019: box ""int"" IL_001e: ldc.i4.2 IL_001f: ldstr ""f"" IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0029: ldloca.s V_0 IL_002b: ldstr ""Literal"" IL_0030: call ""void CustomHandler.AppendLiteral(string)"" IL_0035: ldloc.0 IL_0036: br.s IL_0041 IL_0038: ldloca.s V_0 IL_003a: initobj ""CustomHandler"" IL_0040: ldloc.0 IL_0041: box ""CustomHandler"" IL_0046: call ""void System.Console.WriteLine(object)"" IL_004b: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void TernaryTypes_02(string expression) { // Same as 01, but with a conversion from CustomHandler to string. The rules here are similar to LambdaReturnInference_06 var code = @" using System; CultureInfoNormalizer.Normalize(); var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); CultureInfoNormalizer.Reset(); public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", !expression.Contains('+') ? @" { // Code size 61 (0x3d) .maxstack 2 .locals init (CustomHandler V_0) IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldc.i4.0 IL_0006: box ""bool"" IL_000b: unbox.any ""bool"" IL_0010: brtrue.s IL_0024 IL_0012: ldstr ""{0,2:f}Literal"" IL_0017: ldc.i4.1 IL_0018: box ""int"" IL_001d: call ""string string.Format(string, object)"" IL_0022: br.s IL_0032 IL_0024: ldloca.s V_0 IL_0026: initobj ""CustomHandler"" IL_002c: ldloc.0 IL_002d: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0032: call ""void System.Console.WriteLine(string)"" IL_0037: call ""void CultureInfoNormalizer.Reset()"" IL_003c: ret } " : @" { // Code size 71 (0x47) .maxstack 2 .locals init (CustomHandler V_0) IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldc.i4.0 IL_0006: box ""bool"" IL_000b: unbox.any ""bool"" IL_0010: brtrue.s IL_002e IL_0012: ldstr ""{0,2:f}"" IL_0017: ldc.i4.1 IL_0018: box ""int"" IL_001d: call ""string string.Format(string, object)"" IL_0022: ldstr ""Literal"" IL_0027: call ""string string.Concat(string, string)"" IL_002c: br.s IL_003c IL_002e: ldloca.s V_0 IL_0030: initobj ""CustomHandler"" IL_0036: ldloc.0 IL_0037: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_003c: call ""void System.Console.WriteLine(string)"" IL_0041: call ""void CultureInfoNormalizer.Reset()"" IL_0046: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void TernaryTypes_03(string expression) { // Same as 02, but with a target-type var code = @" using System; CustomHandler x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,19): error CS0029: Cannot implicitly convert type 'string' to 'CustomHandler' // CustomHandler x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"(bool)(object)false ? default(CustomHandler) : " + expression).WithArguments("string", "CustomHandler").WithLocation(4, 19) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void TernaryTypes_04(string expression) { // Same 01, but with a conversion from string to CustomHandler. The rules here are similar to LambdaReturnInference_07 var code = @" using System; var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator CustomHandler(string c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 76 (0x4c) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.0 IL_0001: box ""bool"" IL_0006: unbox.any ""bool"" IL_000b: brtrue.s IL_0038 IL_000d: ldloca.s V_0 IL_000f: ldc.i4.7 IL_0010: ldc.i4.1 IL_0011: call ""CustomHandler..ctor(int, int)"" IL_0016: ldloca.s V_0 IL_0018: ldc.i4.1 IL_0019: box ""int"" IL_001e: ldc.i4.2 IL_001f: ldstr ""f"" IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0029: ldloca.s V_0 IL_002b: ldstr ""Literal"" IL_0030: call ""void CustomHandler.AppendLiteral(string)"" IL_0035: ldloc.0 IL_0036: br.s IL_0041 IL_0038: ldloca.s V_0 IL_003a: initobj ""CustomHandler"" IL_0040: ldloc.0 IL_0041: box ""CustomHandler"" IL_0046: call ""void System.Console.WriteLine(object)"" IL_004b: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void TernaryTypes_05(string expression) { // Same 01, but with a conversion from string to CustomHandler and CustomHandler to string. var code = @" using System; var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator CustomHandler(string c) => throw null; public static implicit operator string(CustomHandler c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,9): error CS0172: Type of conditional expression cannot be determined because 'CustomHandler' and 'string' implicitly convert to one another // var x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; Diagnostic(ErrorCode.ERR_AmbigQM, @"(bool)(object)false ? default(CustomHandler) : " + expression).WithArguments("CustomHandler", "string").WithLocation(4, 9) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void TernaryTypes_06(string expression) { // Same 05, but with a target type var code = @" using System; CustomHandler x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator CustomHandler(string c) => throw null; public static implicit operator string(CustomHandler c) => c.ToString(); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 76 (0x4c) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldc.i4.0 IL_0001: box ""bool"" IL_0006: unbox.any ""bool"" IL_000b: brtrue.s IL_0038 IL_000d: ldloca.s V_0 IL_000f: ldc.i4.7 IL_0010: ldc.i4.1 IL_0011: call ""CustomHandler..ctor(int, int)"" IL_0016: ldloca.s V_0 IL_0018: ldc.i4.1 IL_0019: box ""int"" IL_001e: ldc.i4.2 IL_001f: ldstr ""f"" IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0029: ldloca.s V_0 IL_002b: ldstr ""Literal"" IL_0030: call ""void CustomHandler.AppendLiteral(string)"" IL_0035: ldloc.0 IL_0036: br.s IL_0041 IL_0038: ldloca.s V_0 IL_003a: initobj ""CustomHandler"" IL_0040: ldloc.0 IL_0041: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0046: call ""void System.Console.WriteLine(string)"" IL_004b: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void SwitchTypes_01(string expression) { // Switch expressions infer a best type based on _types_, not based on expressions (section 12.6.3.15 of the spec). Because this is based on types // and not on expression conversions, no best type can be found for this switch expression. var code = @" using System; var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,29): error CS8506: No best type was found for the switch expression. // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(4, 29) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void SwitchTypes_02(string expression) { // Same as 01, but with a conversion from CustomHandler. This allows the switch expression to infer a best-common type, which is string. var code = @" using System; CultureInfoNormalizer.Normalize(); var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); CultureInfoNormalizer.Reset(); public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", !expression.Contains('+') ? @" { // Code size 64 (0x40) .maxstack 2 .locals init (string V_0, CustomHandler V_1) IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldc.i4.0 IL_0006: box ""bool"" IL_000b: unbox.any ""bool"" IL_0010: brfalse.s IL_0023 IL_0012: ldloca.s V_1 IL_0014: initobj ""CustomHandler"" IL_001a: ldloc.1 IL_001b: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0020: stloc.0 IL_0021: br.s IL_0034 IL_0023: ldstr ""{0,2:f}Literal"" IL_0028: ldc.i4.1 IL_0029: box ""int"" IL_002e: call ""string string.Format(string, object)"" IL_0033: stloc.0 IL_0034: ldloc.0 IL_0035: call ""void System.Console.WriteLine(string)"" IL_003a: call ""void CultureInfoNormalizer.Reset()"" IL_003f: ret } " : @" { // Code size 74 (0x4a) .maxstack 2 .locals init (string V_0, CustomHandler V_1) IL_0000: call ""void CultureInfoNormalizer.Normalize()"" IL_0005: ldc.i4.0 IL_0006: box ""bool"" IL_000b: unbox.any ""bool"" IL_0010: brfalse.s IL_0023 IL_0012: ldloca.s V_1 IL_0014: initobj ""CustomHandler"" IL_001a: ldloc.1 IL_001b: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0020: stloc.0 IL_0021: br.s IL_003e IL_0023: ldstr ""{0,2:f}"" IL_0028: ldc.i4.1 IL_0029: box ""int"" IL_002e: call ""string string.Format(string, object)"" IL_0033: ldstr ""Literal"" IL_0038: call ""string string.Concat(string, string)"" IL_003d: stloc.0 IL_003e: ldloc.0 IL_003f: call ""void System.Console.WriteLine(string)"" IL_0044: call ""void CultureInfoNormalizer.Reset()"" IL_0049: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void SwitchTypes_03(string expression) { // Same 02, but with a target-type. The natural type will fail to compile, so the switch will use a target type (unlike TernaryTypes_03, which fails to compile). var code = @" using System; CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => c.ToString(); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (CustomHandler V_0, CustomHandler V_1) IL_0000: ldc.i4.0 IL_0001: box ""bool"" IL_0006: unbox.any ""bool"" IL_000b: brfalse.s IL_0019 IL_000d: ldloca.s V_1 IL_000f: initobj ""CustomHandler"" IL_0015: ldloc.1 IL_0016: stloc.0 IL_0017: br.s IL_0043 IL_0019: ldloca.s V_1 IL_001b: ldc.i4.7 IL_001c: ldc.i4.1 IL_001d: call ""CustomHandler..ctor(int, int)"" IL_0022: ldloca.s V_1 IL_0024: ldc.i4.1 IL_0025: box ""int"" IL_002a: ldc.i4.2 IL_002b: ldstr ""f"" IL_0030: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0035: ldloca.s V_1 IL_0037: ldstr ""Literal"" IL_003c: call ""void CustomHandler.AppendLiteral(string)"" IL_0041: ldloc.1 IL_0042: stloc.0 IL_0043: ldloc.0 IL_0044: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0049: call ""void System.Console.WriteLine(string)"" IL_004e: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void SwitchTypes_04(string expression) { // Same as 01, but with a conversion to CustomHandler. This allows the switch expression to infer a best-common type, which is CustomHandler. var code = @" using System; var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator CustomHandler(string c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (CustomHandler V_0, CustomHandler V_1) IL_0000: ldc.i4.0 IL_0001: box ""bool"" IL_0006: unbox.any ""bool"" IL_000b: brfalse.s IL_0019 IL_000d: ldloca.s V_1 IL_000f: initobj ""CustomHandler"" IL_0015: ldloc.1 IL_0016: stloc.0 IL_0017: br.s IL_0043 IL_0019: ldloca.s V_1 IL_001b: ldc.i4.7 IL_001c: ldc.i4.1 IL_001d: call ""CustomHandler..ctor(int, int)"" IL_0022: ldloca.s V_1 IL_0024: ldc.i4.1 IL_0025: box ""int"" IL_002a: ldc.i4.2 IL_002b: ldstr ""f"" IL_0030: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0035: ldloca.s V_1 IL_0037: ldstr ""Literal"" IL_003c: call ""void CustomHandler.AppendLiteral(string)"" IL_0041: ldloc.1 IL_0042: stloc.0 IL_0043: ldloc.0 IL_0044: box ""CustomHandler"" IL_0049: call ""void System.Console.WriteLine(object)"" IL_004e: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void SwitchTypes_05(string expression) { // Same as 01, but with conversions in both directions. No best common type can be found. var code = @" using System; var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator CustomHandler(string c) => throw null; public static implicit operator string(CustomHandler c) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,29): error CS8506: No best type was found for the switch expression. // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(4, 29) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void SwitchTypes_06(string expression) { // Same as 05, but with a target type. var code = @" using System; CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator CustomHandler(string c) => throw null; public static implicit operator string(CustomHandler c) => c.ToString(); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (CustomHandler V_0, CustomHandler V_1) IL_0000: ldc.i4.0 IL_0001: box ""bool"" IL_0006: unbox.any ""bool"" IL_000b: brfalse.s IL_0019 IL_000d: ldloca.s V_1 IL_000f: initobj ""CustomHandler"" IL_0015: ldloc.1 IL_0016: stloc.0 IL_0017: br.s IL_0043 IL_0019: ldloca.s V_1 IL_001b: ldc.i4.7 IL_001c: ldc.i4.1 IL_001d: call ""CustomHandler..ctor(int, int)"" IL_0022: ldloca.s V_1 IL_0024: ldc.i4.1 IL_0025: box ""int"" IL_002a: ldc.i4.2 IL_002b: ldstr ""f"" IL_0030: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0035: ldloca.s V_1 IL_0037: ldstr ""Literal"" IL_003c: call ""void CustomHandler.AppendLiteral(string)"" IL_0041: ldloc.1 IL_0042: stloc.0 IL_0043: ldloc.0 IL_0044: call ""string CustomHandler.op_Implicit(CustomHandler)"" IL_0049: call ""void System.Console.WriteLine(string)"" IL_004e: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void PassAsRefWithoutKeyword_01(string expression) { var code = @" M(" + expression + @"); void M(ref CustomHandler c) => System.Console.WriteLine(c);"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 48 (0x30) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: ldc.i4.2 IL_0012: ldstr ""f"" IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_001c: ldloca.s V_0 IL_001e: ldstr ""Literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloca.s V_0 IL_002a: call ""void Program.<<Main>$>g__M|0_0(ref CustomHandler)"" IL_002f: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void PassAsRefWithoutKeyword_02(string expression) { var code = $$""" M({{expression}}); M(ref {{expression}}); void M(ref CustomHandler c) => System.Console.WriteLine(c); """; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (1,3): error CS1620: Argument 1 must be passed with the 'ref' keyword // M($"{1,2:f}Literal"); Diagnostic(ErrorCode.ERR_BadArgRef, expression).WithArguments("1", "ref").WithLocation(1, 3), // (2,7): error CS1510: A ref or out value must be an assignable variable // M(ref $"{1,2:f}Literal"); Diagnostic(ErrorCode.ERR_RefLvalueExpected, expression).WithLocation(2, 7) ); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void PassAsRefWithoutKeyword_03(string expression) { var code = @" M(" + expression + @"); void M(in CustomHandler c) => System.Console.WriteLine(c);"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 45 (0x2d) .maxstack 5 .locals init (CustomHandler V_0) IL_0000: ldc.i4.7 IL_0001: ldc.i4.1 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: dup IL_0008: ldc.i4.1 IL_0009: box ""int"" IL_000e: ldc.i4.2 IL_000f: ldstr ""f"" IL_0014: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0019: dup IL_001a: ldstr ""Literal"" IL_001f: callvirt ""void CustomHandler.AppendLiteral(string)"" IL_0024: stloc.0 IL_0025: ldloca.s V_0 IL_0027: call ""void Program.<<Main>$>g__M|0_0(in CustomHandler)"" IL_002c: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void PassAsRefWithoutKeyword_04(string expression) { var code = @" M(" + expression + @"); void M(in CustomHandler c) => System.Console.WriteLine(c);"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 48 (0x30) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: ldc.i4.2 IL_0012: ldstr ""f"" IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_001c: ldloca.s V_0 IL_001e: ldstr ""Literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloca.s V_0 IL_002a: call ""void Program.<<Main>$>g__M|0_0(in CustomHandler)"" IL_002f: ret } "); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79888")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void PassAsRefWithoutKeyword_05_WithReorder(string refKind) { var code = $$""" BadFunc( message: $"abc", value2: 0, value1: 1); static void BadFunc( {{refKind}} InterpolatedStringHandler message, in int? value1, int? value2) { System.Console.Write(value1); System.Console.Write(value2); } [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolatedStringHandler { public InterpolatedStringHandler( int literalLength, int formattedCount) { } public void AppendLiteral(string value) { System.Console.Write(value); } } """; var verifier = CompileAndVerify(code, targetFramework: TargetFramework.Net90, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "abc10" : null, verify: Verification.FailsPEVerify); verifier.VerifyIL("<top-level-statements-entry-point>", $$""" { // Code size 47 (0x2f) .maxstack 3 .locals init (int? V_0, InterpolatedStringHandler V_1, int? V_2) IL_0000: ldloca.s V_1 IL_0002: ldc.i4.3 IL_0003: ldc.i4.0 IL_0004: call "InterpolatedStringHandler..ctor(int, int)" IL_0009: ldloca.s V_1 IL_000b: ldstr "abc" IL_0010: call "void InterpolatedStringHandler.AppendLiteral(string)" IL_0015: ldloca.s V_1 IL_0017: ldloca.s V_0 IL_0019: ldc.i4.0 IL_001a: call "int?..ctor(int)" IL_001f: ldc.i4.1 IL_0020: newobj "int?..ctor(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_2 IL_0028: ldloc.0 IL_0029: call "void Program.<<Main>$>g__BadFunc|0_0({{refKind}} InterpolatedStringHandler, in int?, int?)" IL_002e: ret } """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79888")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void PassAsRefWithoutKeyword_05_WithoutReorder(string refKind) { var code = $$""" BadFunc( message: $"abc", value1: 1, value2: 0); static void BadFunc( {{refKind}} InterpolatedStringHandler message, in int? value1, int? value2) { System.Console.Write(value1); System.Console.Write(value2); } [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolatedStringHandler { public InterpolatedStringHandler( int literalLength, int formattedCount) { } public void AppendLiteral(string value) { System.Console.Write(value); } } """; var verifier = CompileAndVerify(code, targetFramework: TargetFramework.Net90, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "abc10" : null, verify: Verification.FailsPEVerify); verifier.VerifyIL("<top-level-statements-entry-point>", $$""" { // Code size 44 (0x2c) .maxstack 3 .locals init (InterpolatedStringHandler V_0, int? V_1) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: ldc.i4.0 IL_0004: call "InterpolatedStringHandler..ctor(int, int)" IL_0009: ldloca.s V_0 IL_000b: ldstr "abc" IL_0010: call "void InterpolatedStringHandler.AppendLiteral(string)" IL_0015: ldloca.s V_0 IL_0017: ldc.i4.1 IL_0018: newobj "int?..ctor(int)" IL_001d: stloc.1 IL_001e: ldloca.s V_1 IL_0020: ldc.i4.0 IL_0021: newobj "int?..ctor(int)" IL_0026: call "void Program.<<Main>$>g__BadFunc|0_0({{refKind}} InterpolatedStringHandler, in int?, int?)" IL_002b: ret } """); } [Theory] [CombinatorialData] public void RefOverloadResolution_Struct([CombinatorialValues("in", "ref")] string refKind, [CombinatorialValues(@"$""{1,2:f}Literal""", @"$""{1,2:f}"" + $""Literal""")] string expression) { var code = @" C.M(" + expression + @"); class C { public static void M(CustomHandler c) => System.Console.WriteLine(c); public static void M(" + refKind + @" CustomHandler c) => throw null; }"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 47 (0x2f) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: ldc.i4.2 IL_0012: ldstr ""f"" IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_001c: ldloca.s V_0 IL_001e: ldstr ""Literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloc.0 IL_0029: call ""void C.M(CustomHandler)"" IL_002e: ret } "); } [Theory] [CombinatorialData] public void RefOverloadResolution_Class([CombinatorialValues("in", "ref")] string refKind, [CombinatorialValues(@"$""{1,2:f}Literal""", @"$""{1,2:f}"" + $""Literal""")] string expression) { var code = @" C.M(" + expression + @"); class C { public static void M(CustomHandler c) => System.Console.WriteLine(c); public static void M(" + refKind + @" CustomHandler c) => System.Console.WriteLine(c); }"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 47 (0x2f) .maxstack 4 .locals init (CustomHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: ldc.i4.2 IL_0012: ldstr ""f"" IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_001c: ldloca.s V_0 IL_001e: ldstr ""Literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloc.0 IL_0029: call ""void C.M(CustomHandler)"" IL_002e: ret } "); } [Theory] [InlineData(@"$""{1,2:f}Literal""")] [InlineData(@"$""{1,2:f}"" + $""Literal""")] public void RefOverloadResolution_MultipleBuilderTypes(string expression) { var code = @" C.M(" + expression + @"); class C { public static void M(CustomHandler1 c) => System.Console.WriteLine(c); public static void M(ref CustomHandler2 c) => throw null; }"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler1", "struct", useBoolReturns: false), GetInterpolatedStringCustomHandlerType("CustomHandler2", "struct", useBoolReturns: false, includeOneTimeHelpers: false) }); VerifyInterpolatedStringExpression(comp, "CustomHandler1"); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 alignment:2 format:f literal:Literal"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 47 (0x2f) .maxstack 4 .locals init (CustomHandler1 V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler1..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: ldc.i4.2 IL_0012: ldstr ""f"" IL_0017: call ""void CustomHandler1.AppendFormatted(object, int, string)"" IL_001c: ldloca.s V_0 IL_001e: ldstr ""Literal"" IL_0023: call ""void CustomHandler1.AppendLiteral(string)"" IL_0028: ldloc.0 IL_0029: call ""void C.M(CustomHandler1)"" IL_002e: ret } "); } private const string InterpolatedStringHandlerAttributesVB = @" Namespace System.Runtime.CompilerServices <AttributeUsage(AttributeTargets.Class Or AttributeTargets.Struct, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class InterpolatedStringHandlerAttribute Inherits Attribute End Class <AttributeUsage(AttributeTargets.Parameter, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class InterpolatedStringHandlerArgumentAttribute Inherits Attribute Public Sub New(argument As String) Arguments = { argument } End Sub Public Sub New(ParamArray arguments() as String) Me.Arguments = arguments End Sub Public ReadOnly Property Arguments As String() End Class End Namespace "; [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_NonHandlerType(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(" + expression + @"); class C { public static void M([InterpolatedStringHandlerArgumentAttribute] string s) {} } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute }); comp.VerifyDiagnostics( // (8,27): error CS8946: 'string' is not an interpolated string handler type. // public static void M([InterpolatedStringHandlerArgumentAttribute] string s) {} Diagnostic(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, "InterpolatedStringHandlerArgumentAttribute").WithArguments("string").WithLocation(8, 27) ); var sParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", sParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(sParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(sParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_NonHandlerType_Metadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i as Integer, <InterpolatedStringHandlerArgument(""i"")> c As String) End Sub End Class "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); // Note: there is no compilation error here because the natural type of a string is still string, and // we just bind to that method without checking the handler attribute. var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics(); var sParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", sParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(sParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(sParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_InvalidArgument(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(" + expression + @"); class C { public static void M([InterpolatedStringHandlerArgumentAttribute(1)] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (8,70): error CS1503: Argument 1: cannot convert from 'int' to 'string' // public static void M([InterpolatedStringHandlerArgumentAttribute(1)] CustomHandler c) {} Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "string").WithLocation(8, 70) ); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_01(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(" + expression + @"); class C { public static void M([InterpolatedStringHandlerArgumentAttribute(""NonExistant"")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M($"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 5), // (8,27): error CS8945: 'NonExistant' is not a valid parameter name from 'C.M(CustomHandler)'. // public static void M([InterpolatedStringHandlerArgumentAttribute("NonExistant")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""NonExistant"")").WithArguments("NonExistant", "C.M(CustomHandler)").WithLocation(8, 27) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_01_FromMetadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(<InterpolatedStringHandlerArgument(""NonExistant"")> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 5), // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 5), // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 5) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_02(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(1, " + expression + @"); class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"", ""NonExistant"")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 8), // (8,34): error CS8945: 'NonExistant' is not a valid parameter name from 'C.M(int, CustomHandler)'. // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("i", "NonExistant")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""i"", ""NonExistant"")").WithArguments("NonExistant", "C.M(int, CustomHandler)").WithLocation(8, 34) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_02_FromMetadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i As Integer, <InterpolatedStringHandlerArgument(""i"", ""NonExistant"")> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_03(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(1, " + expression + @"); class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""NonExistant1"", ""NonExistant2"")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 8), // (8,34): error CS8945: 'NonExistant1' is not a valid parameter name from 'C.M(int, CustomHandler)'. // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("NonExistant1", "NonExistant2")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""NonExistant1"", ""NonExistant2"")").WithArguments("NonExistant1", "C.M(int, CustomHandler)").WithLocation(8, 34), // (8,34): error CS8945: 'NonExistant2' is not a valid parameter name from 'C.M(int, CustomHandler)'. // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("NonExistant1", "NonExistant2")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""NonExistant1"", ""NonExistant2"")").WithArguments("NonExistant2", "C.M(int, CustomHandler)").WithLocation(8, 34) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_03_FromMetadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i As Integer, <InterpolatedStringHandlerArgument(""NonExistant1"", ""NonExistant2"")> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_ReferenceSelf(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(1, " + expression + @"); class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""c"")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 8), // (8,34): error CS8948: InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("c")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_CannotUseSelfAsInterpolatedStringHandlerArgument, @"InterpolatedStringHandlerArgumentAttribute(""c"")").WithLocation(8, 34) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_ReferencesSelf_FromMetadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(<InterpolatedStringHandlerArgument(""c"")> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 5), // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 5), // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 5) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_01(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(1, " + expression + @"); class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(new string[] { null })] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 8), // (8,34): error CS8943: null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(new string[] { null })] CustomHandler c) {} Diagnostic(ErrorCode.ERR_NullInvalidInterpolatedStringHandlerArgumentName, "InterpolatedStringHandlerArgumentAttribute(new string[] { null })").WithLocation(8, 34) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact, WorkItem(58025, "https://github.com/dotnet/roslyn/issues/58025")] public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_02() { var code = @" using System.Runtime.CompilerServices; C.M(1, $""""); class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute((string[])null)] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 8), // (8,34): error CS8943: null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute((string[])null)] CustomHandler c) {} Diagnostic(ErrorCode.ERR_NullInvalidInterpolatedStringHandlerArgumentName, "InterpolatedStringHandlerArgumentAttribute((string[])null)").WithLocation(8, 34) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_01() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i As Integer, <InterpolatedStringHandlerArgument({ Nothing })> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_02() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i As Integer, <InterpolatedStringHandlerArgument({ Nothing, ""i"" })> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_03() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i As Integer, <InterpolatedStringHandlerArgument(CStr(Nothing))> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_04() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(i As Integer, <InterpolatedStringHandlerArgument(DirectCast(Nothing, String()))> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_ThisOnStaticMethod(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(" + expression + @"); class C { public static void M([InterpolatedStringHandlerArgumentAttribute("""")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M($"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 5), // (8,27): error CS8944: 'C.M(CustomHandler)' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // public static void M([InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute("""")").WithArguments("C.M(CustomHandler)").WithLocation(8, 27) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"{""""}")] [InlineData(@"""""")] public void InterpolatedStringHandlerArgumentAttributeError_ThisOnStaticMethod_FromMetadata(string arg) { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(<InterpolatedStringHandlerArgument(" + arg + @")> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 5), // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 5), // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 5) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_ThisOnConstructor(string expression) { var code = @" using System.Runtime.CompilerServices; _ = new C(" + expression + @"); class C { public C([InterpolatedStringHandlerArgumentAttribute("""")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (4,11): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // _ = new C($"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 11), // (8,15): error CS8944: 'C.C(CustomHandler)' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // public C([InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) {} Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute("""")").WithArguments("C.C(CustomHandler)").WithLocation(8, 15) ); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod(".ctor").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"{""""}")] [InlineData(@"""""")] public void InterpolatedStringHandlerArgumentAttributeError_ThisOnConstructor_FromMetadata(string arg) { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Sub New(<InterpolatedStringHandlerArgument(" + arg + @")> c As CustomHandler) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"_ = new C($"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,11): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // _ = new C($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 11), // (1,11): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // _ = new C($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 11), // (1,11): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // _ = new C($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 11) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod(".ctor").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerAttributeArgumentError_SubstitutedTypeSymbol(string expression) { var code = @" using System.Runtime.CompilerServices; C<CustomHandler>.M(" + expression + @"); public class C<T> { public static void M([InterpolatedStringHandlerArgumentAttribute] T t) { } } "; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, customHandler }); comp.VerifyDiagnostics( // (4,20): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler t' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C<CustomHandler>.M($"" + $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).WithArguments("CustomHandler t", "CustomHandler").WithLocation(4, 20), // (8,27): error CS8946: 'T' is not an interpolated string handler type. // public static void M([InterpolatedStringHandlerArgumentAttribute] T t) { } Diagnostic(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, "InterpolatedStringHandlerArgumentAttribute").WithArguments("T").WithLocation(8, 27) ); var c = comp.SourceModule.GlobalNamespace.GetTypeMember("C"); var handler = comp.SourceModule.GlobalNamespace.GetTypeMember("CustomHandler"); var substitutedC = c.WithTypeArguments(ImmutableArray.Create(TypeWithAnnotations.Create(handler))); var cParam = substitutedC.GetMethod("M").Parameters.Single(); Assert.IsType<SubstitutedParameterSymbol>(cParam); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Fact] public void InterpolatedStringHandlerArgumentAttributeError_SubstitutedTypeSymbol_FromMetadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C(Of T) Public Shared Sub M(<InterpolatedStringHandlerArgument()> c As T) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation(@"C<CustomHandler>.M($"""");", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics( // (1,20): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // C<CustomHandler>.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(1, 20), // (1,20): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // C<CustomHandler>.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 20), // (1,20): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C<CustomHandler>.M($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 20) ); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C`1").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttributeWarn_ParameterAfterHandler([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""text""", @"$""text"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; public class C { public static void M([InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c, int i) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i:"" + i.ToString()); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var goodCode = @" int i = 10; C.M(i: i, c: " + expression + @"); "; var comp = CreateCompilation(new[] { code, goodCode, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validate, symbolValidator: validate, expectedOutput: @" i:10 literal:text "); verifier.VerifyDiagnostics( // (6,27): warning CS8947: Parameter 'i' occurs after 'c' in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to // reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, int i) => Console.WriteLine(c.ToString()); Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "c").WithLocation(6, 27) ); verifyIL(verifier); var badCode = @"C.M(" + expression + @", 1);"; comp = CreateCompilation(new[] { code, badCode, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (1,10): error CS8950: Parameter 'i' is an argument to the interpolated string handler conversion on parameter 'c', but is specified after the interpolated string constant. Reorder the arguments to move 'i' before 'c'. // C.M($"", 1); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, "1").WithArguments("i", "c").WithLocation(1, 7 + expression.Length), // (6,27): warning CS8947: Parameter 'i' occurs after 'c' in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to // reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, int i) => Console.WriteLine(c.ToString()); Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "c").WithLocation(6, 27) ); static void validate(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.First(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(1, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } void verifyIL(CompilationVerifier verifier) { verifier.VerifyIL("<top-level-statements-entry-point>", extraConstructorArg == "" ? @" { // Code size 36 (0x24) .maxstack 4 .locals init (int V_0, int V_1, CustomHandler V_2) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: stloc.1 IL_0005: ldloca.s V_2 IL_0007: ldc.i4.4 IL_0008: ldc.i4.0 IL_0009: ldloc.0 IL_000a: call ""CustomHandler..ctor(int, int, int)"" IL_000f: ldloca.s V_2 IL_0011: ldstr ""text"" IL_0016: call ""bool CustomHandler.AppendLiteral(string)"" IL_001b: pop IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call ""void C.M(CustomHandler, int)"" IL_0023: ret } " : @" { // Code size 43 (0x2b) .maxstack 4 .locals init (int V_0, int V_1, CustomHandler V_2, bool V_3) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: stloc.1 IL_0005: ldc.i4.4 IL_0006: ldc.i4.0 IL_0007: ldloc.0 IL_0008: ldloca.s V_3 IL_000a: newobj ""CustomHandler..ctor(int, int, int, out bool)"" IL_000f: stloc.2 IL_0010: ldloc.3 IL_0011: brfalse.s IL_0021 IL_0013: ldloca.s V_2 IL_0015: ldstr ""text"" IL_001a: call ""bool CustomHandler.AppendLiteral(string)"" IL_001f: br.s IL_0022 IL_0021: ldc.i4.0 IL_0022: pop IL_0023: ldloc.2 IL_0024: ldloc.1 IL_0025: call ""void C.M(CustomHandler, int)"" IL_002a: ret } "); } } [Fact] public void InterpolatedStringHandlerArgumentAttributeWarn_ParameterAfterHandler_FromMetadata() { var vbCode = @" Imports System.Runtime.CompilerServices Public Class C Public Shared Sub M(<InterpolatedStringHandlerArgument(""i"")> c As CustomHandler, i As Integer) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var comp = CreateCompilation("", references: new[] { vbComp.EmitToImageReference() }); comp.VerifyEmitDiagnostics(); var customHandler = comp.GetTypeByMetadataName("CustomHandler"); Assert.True(customHandler.IsInterpolatedStringHandlerType); var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.First(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(1, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_OptionalNotSpecifiedAtCallsite(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(" + expression + @"); public class C { public static void M([InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c, int i = 0) { } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) : this(literalLength, formattedCount) { } } "; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, customHandler }); comp.VerifyDiagnostics( // (4,5): error CS8951: Parameter 'i' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter 'c'. Specify the value of 'i' before 'c'. // C.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, expression).WithArguments("i", "c").WithLocation(4, 5), // (8,27): warning CS8947: Parameter 'i' occurs after 'c' in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder // parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, int i = 0) { } Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "c").WithLocation(8, 27) ); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttributeError_ParamsNotSpecifiedAtCallsite(string expression) { var code = @" using System.Runtime.CompilerServices; C.M(" + expression + @"); public class C { public static void M([InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c, params int[] i) { } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int[] i) : this(literalLength, formattedCount) { } } "; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, customHandler }); comp.VerifyDiagnostics( // (4,5): error CS8951: Parameter 'i' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter 'c'. Specify the value of 'i' before 'c'. // C.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, expression).WithArguments("i", "c").WithLocation(4, 5), // (8,27): warning CS8947: Parameter 'i' occurs after 'c' in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder // parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, params int[] i) { } Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "c").WithLocation(8, 27) ); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MissingConstructor(string expression) { var code = @" using System.Runtime.CompilerServices; public class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); CompileAndVerify(comp, sourceSymbolValidator: validate, symbolValidator: validate).VerifyDiagnostics(); CreateCompilation(@"C.M(1, " + expression + @");", new[] { comp.ToMetadataReference() }).VerifyDiagnostics( // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).WithArguments("CustomHandler", "3").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 4 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).WithArguments("CustomHandler", "4").WithLocation(1, 8) ); static void validate(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_InaccessibleConstructor_01(string expression) { var code = @" using System.Runtime.CompilerServices; public class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) {} } public partial struct CustomHandler { private CustomHandler(int literalLength, int formattedCount, int i) : this() {} static void InCustomHandler() { C.M(1, " + expression + @"); } } "; var executableCode = @"C.M(1, " + expression + @");"; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (1,8): error CS0122: 'CustomHandler.CustomHandler(int, int, int)' is inaccessible due to its protection level // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadAccess, expression).WithArguments("CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 8) ); var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. CompileAndVerify(dependency, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); comp = CreateCompilation(executableCode, new[] { dependency.EmitToImageReference() }); comp.VerifyDiagnostics( // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 4 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).WithArguments("CustomHandler", "4").WithLocation(1, 8), // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).WithArguments("CustomHandler", "3").WithLocation(1, 8) ); comp = CreateCompilation(executableCode, new[] { dependency.ToMetadataReference() }); comp.VerifyDiagnostics( // (1,8): error CS0122: 'CustomHandler.CustomHandler(int, int, int)' is inaccessible due to its protection level // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadAccess, expression).WithArguments("CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 8) ); static void validate(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); } } private void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(CSharpParseOptions parseOptions, string mRef, string customHandlerRef, string expression, params DiagnosticDescription[] expectedDiagnostics) { var code = @" using System.Runtime.CompilerServices; int i = 0; C.M(" + mRef + @" i, " + expression + @"); public class C { public static void M(" + mRef + @" int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) { " + (mRef == "out" ? "i = 0;" : "") + @" } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, " + customHandlerRef + @" int i) : this() { " + (customHandlerRef == "out" ? "i = 0;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, parseOptions: parseOptions); comp.VerifyDiagnostics(expectedDiagnostics); var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefNone(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "ref", "", expression, // (5,9): error CS1615: Argument 3 may not be passed with the 'ref' keyword // C.M(ref i, $""); Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "ref").WithLocation(5, 9)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefOut(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "ref", "out", expression, // (5,9): error CS1620: Argument 3 must be passed with the 'out' keyword // C.M(ref i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "out").WithLocation(5, 9)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefIn(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(TestOptions.Regular11, "ref", "in", expression, // 0.cs(5,9): error CS9194: Argument 3 may not be passed with the 'ref' keyword in language version 11.0. To pass 'ref' arguments to 'in' parameters, upgrade to language version 12.0 or greater. // C.M(ref i, $""); Diagnostic(ErrorCode.ERR_BadArgExtraRefLangVersion, "i").WithArguments("3", "11.0", "12.0").WithLocation(5, 9)); } [Theory, CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefIn_CSharp12( [CombinatorialValues(@"$""text""", @"$""text"" + $""""")] string expression) { var code = $$""" using System; using System.Runtime.CompilerServices; int x = 10; C.M(ref x, {{expression}}); public class C { public static void M(ref int i, [InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, in int i) : this(literalLength, formattedCount) { _builder.AppendLine("i:" + i.ToString()); } } """; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validate, symbolValidator: validate, expectedOutput: """ i:10 literal:text """); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 36 (0x24) .maxstack 4 .locals init (int V_0, //x int& V_1, CustomHandler V_2) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: ldc.i4.4 IL_0008: ldc.i4.0 IL_0009: ldloc.1 IL_000a: newobj "CustomHandler..ctor(int, int, in int)" IL_000f: stloc.2 IL_0010: ldloca.s V_2 IL_0012: ldstr "text" IL_0017: call "bool CustomHandler.AppendLiteral(string)" IL_001c: pop IL_001d: ldloc.2 IL_001e: call "void C.M(ref int, CustomHandler)" IL_0023: ret } """); static void validate(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InNone(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "in", "", expression, // (5,8): error CS1615: Argument 3 may not be passed with the 'in' keyword // C.M(in i, $""); Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "in").WithLocation(5, 8)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InOut(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "in", "out", expression, // (5,8): error CS1620: Argument 3 must be passed with the 'out' keyword // C.M(in i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "out").WithLocation(5, 8)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InRef(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "in", "ref", expression, // (5,8): error CS1620: Argument 3 must be passed with the 'ref' keyword // C.M(in i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(5, 8)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_OutNone(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "out", "", expression, // (5,9): error CS1615: Argument 3 may not be passed with the 'out' keyword // C.M(out i, $""); Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "out").WithLocation(5, 9)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_OutRef(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "out", "ref", expression, // (5,9): error CS1620: Argument 3 must be passed with the 'ref' keyword // C.M(out i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(5, 9)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_NoneRef(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "", "ref", expression, // (5,6): error CS1620: Argument 3 must be passed with the 'ref' keyword // C.M( i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(5, 6)); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_NoneOut(string expression) { InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(null, "", "out", expression, // (5,6): error CS1620: Argument 3 must be passed with the 'out' keyword // C.M( i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "out").WithLocation(5, 6)); } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_MismatchedType([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; public class C { public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) {} } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, string s" + extraConstructorArg + @") : this() { " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var executableCode = @"C.M(1, " + expression + @");"; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var expectedDiagnostics = extraConstructorArg == "" ? new DiagnosticDescription[] { // (1,5): error CS1503: Argument 3: cannot convert from 'int' to 'string' // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("3", "int", "string").WithLocation(1, 5) } : new DiagnosticDescription[] { // (1,5): error CS1503: Argument 3: cannot convert from 'int' to 'string' // C.M(1, $""); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("3", "int", "string").WithLocation(1, 5), // (1,8): error CS7036: There is no argument given that corresponds to the required parameter 'success' of 'CustomHandler.CustomHandler(int, int, string, out bool)' // C.M(1, $""); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("success", "CustomHandler.CustomHandler(int, int, string, out bool)").WithLocation(1, 8) }; var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics(expectedDiagnostics); // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); CompileAndVerify(dependency, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); foreach (var d in new[] { dependency.EmitToImageReference(), dependency.ToMetadataReference() }) { comp = CreateCompilation(executableCode, new[] { d }); comp.VerifyDiagnostics(expectedDiagnostics); } static void validate(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_SingleArg([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""2""", @"$""2"" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; public class C { public static string M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) => c.ToString(); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i:"" + i.ToString()); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var executableCode = @" using System; int i = 10; Console.WriteLine(C.M(i, " + expression + @")); "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" i:10 literal:2"); verifier.VerifyDiagnostics(); verifyIL(extraConstructorArg, verifier); var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); foreach (var d in new[] { dependency.EmitToImageReference(), dependency.ToMetadataReference() }) { verifier = CompileAndVerify(executableCode, new[] { d }, expectedOutput: @" i:10 literal:2"); verifier.VerifyDiagnostics(); verifyIL(extraConstructorArg, verifier); } static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); } static void verifyIL(string extraConstructorArg, CompilationVerifier verifier) { verifier.VerifyIL("<top-level-statements-entry-point>", extraConstructorArg == "" ? @" { // Code size 39 (0x27) .maxstack 5 .locals init (int V_0, CustomHandler V_1) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldloca.s V_1 IL_0006: ldc.i4.1 IL_0007: ldc.i4.0 IL_0008: ldloc.0 IL_0009: call ""CustomHandler..ctor(int, int, int)"" IL_000e: ldloca.s V_1 IL_0010: ldstr ""2"" IL_0015: call ""bool CustomHandler.AppendLiteral(string)"" IL_001a: pop IL_001b: ldloc.1 IL_001c: call ""string C.M(int, CustomHandler)"" IL_0021: call ""void System.Console.WriteLine(string)"" IL_0026: ret } " : @" { // Code size 46 (0x2e) .maxstack 5 .locals init (int V_0, CustomHandler V_1, bool V_2) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldc.i4.1 IL_0005: ldc.i4.0 IL_0006: ldloc.0 IL_0007: ldloca.s V_2 IL_0009: newobj ""CustomHandler..ctor(int, int, int, out bool)"" IL_000e: stloc.1 IL_000f: ldloc.2 IL_0010: brfalse.s IL_0020 IL_0012: ldloca.s V_1 IL_0014: ldstr ""2"" IL_0019: call ""bool CustomHandler.AppendLiteral(string)"" IL_001e: br.s IL_0021 IL_0020: ldc.i4.0 IL_0021: pop IL_0022: ldloc.1 IL_0023: call ""string C.M(int, CustomHandler)"" IL_0028: call ""void System.Console.WriteLine(string)"" IL_002d: ret } "); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_MultipleArgs([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; public class C { public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"")] CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i:"" + i.ToString()); _builder.AppendLine(""s:"" + s); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var executableCode = @" int i = 10; string s = ""arg""; C.M(i, s, " + expression + @"); "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); string expectedOutput = @" i:10 s:arg literal:literal "; var verifier = base.CompileAndVerify((Compilation)comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: expectedOutput); verifier.VerifyDiagnostics(); verifyIL(extraConstructorArg, verifier); var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); foreach (var d in new[] { dependency.EmitToImageReference(), dependency.ToMetadataReference() }) { verifier = CompileAndVerify(executableCode, new[] { d }, expectedOutput: expectedOutput); verifier.VerifyDiagnostics(); verifyIL(extraConstructorArg, verifier); } static void validator(ModuleSymbol verifier) { var cParam = verifier.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); } static void verifyIL(string extraConstructorArg, CompilationVerifier verifier) { verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 44 (0x2c) .maxstack 7 .locals init (string V_0, //s int V_1, string V_2, CustomHandler V_3) IL_0000: ldc.i4.s 10 IL_0002: ldstr ""arg"" IL_0007: stloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldloc.0 IL_000b: stloc.2 IL_000c: ldloc.2 IL_000d: ldloca.s V_3 IL_000f: ldc.i4.7 IL_0010: ldc.i4.0 IL_0011: ldloc.1 IL_0012: ldloc.2 IL_0013: call ""CustomHandler..ctor(int, int, int, string)"" IL_0018: ldloca.s V_3 IL_001a: ldstr ""literal"" IL_001f: call ""bool CustomHandler.AppendLiteral(string)"" IL_0024: pop IL_0025: ldloc.3 IL_0026: call ""void C.M(int, string, CustomHandler)"" IL_002b: ret } " : @" { // Code size 52 (0x34) .maxstack 7 .locals init (string V_0, //s int V_1, string V_2, CustomHandler V_3, bool V_4) IL_0000: ldc.i4.s 10 IL_0002: ldstr ""arg"" IL_0007: stloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldloc.0 IL_000b: stloc.2 IL_000c: ldloc.2 IL_000d: ldc.i4.7 IL_000e: ldc.i4.0 IL_000f: ldloc.1 IL_0010: ldloc.2 IL_0011: ldloca.s V_4 IL_0013: newobj ""CustomHandler..ctor(int, int, int, string, out bool)"" IL_0018: stloc.3 IL_0019: ldloc.s V_4 IL_001b: brfalse.s IL_002b IL_001d: ldloca.s V_3 IL_001f: ldstr ""literal"" IL_0024: call ""bool CustomHandler.AppendLiteral(string)"" IL_0029: br.s IL_002c IL_002b: ldc.i4.0 IL_002c: pop IL_002d: ldloc.3 IL_002e: call ""void C.M(int, string, CustomHandler)"" IL_0033: ret } "); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_RefKindsMatch([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression, [CombinatorialValues("in", "ref readonly")] string modifier) { var code = @" using System; using System.Runtime.CompilerServices; int i = 1; string s = null; object o; C.M(i, ref s, out o, " + expression + @"); Console.WriteLine(s); Console.WriteLine(o); public class C { public static void M(" + modifier + @" int i, ref string s, out object o, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"", ""o"")] CustomHandler c) { Console.WriteLine(s); o = ""o in M""; s = ""s in M""; Console.Write(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, " + modifier + @" int i, ref string s, out object o" + extraConstructorArg + @") : this(literalLength, formattedCount) { o = null; s = ""s in constructor""; _builder.AppendLine(""i:"" + i.ToString()); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" s in constructor i:1 literal:literal s in M o in M "); verifier.VerifyDiagnostics(modifier == "ref readonly" ? new[] { // 0.cs(8,5): warning CS9192: Argument 1 should be passed with 'ref' or 'in' keyword // C.M(i, ref s, out o, $"literal"); Diagnostic(ErrorCode.WRN_ArgExpectedRefOrIn, "i").WithArguments("1").WithLocation(8, 5) } : DiagnosticDescription.None); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 67 (0x43) .maxstack 8 .locals init (int V_0, //i string V_1, //s object V_2, //o int& V_3, string& V_4, object& V_5, CustomHandler V_6) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldnull IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: stloc.3 IL_0007: ldloc.3 IL_0008: ldloca.s V_1 IL_000a: stloc.s V_4 IL_000c: ldloc.s V_4 IL_000e: ldloca.s V_2 IL_0010: stloc.s V_5 IL_0012: ldloc.s V_5 IL_0014: ldc.i4.7 IL_0015: ldc.i4.0 IL_0016: ldloc.3 IL_0017: ldloc.s V_4 IL_0019: ldloc.s V_5 IL_001b: newobj ""CustomHandler..ctor(int, int, " + modifier + @" int, ref string, out object)"" IL_0020: stloc.s V_6 IL_0022: ldloca.s V_6 IL_0024: ldstr ""literal"" IL_0029: call ""bool CustomHandler.AppendLiteral(string)"" IL_002e: pop IL_002f: ldloc.s V_6 IL_0031: call ""void C.M(" + modifier + @" int, ref string, out object, CustomHandler)"" IL_0036: ldloc.1 IL_0037: call ""void System.Console.WriteLine(string)"" IL_003c: ldloc.2 IL_003d: call ""void System.Console.WriteLine(object)"" IL_0042: ret } " : @" { // Code size 76 (0x4c) .maxstack 9 .locals init (int V_0, //i string V_1, //s object V_2, //o int& V_3, string& V_4, object& V_5, CustomHandler V_6, bool V_7) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldnull IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: stloc.3 IL_0007: ldloc.3 IL_0008: ldloca.s V_1 IL_000a: stloc.s V_4 IL_000c: ldloc.s V_4 IL_000e: ldloca.s V_2 IL_0010: stloc.s V_5 IL_0012: ldloc.s V_5 IL_0014: ldc.i4.7 IL_0015: ldc.i4.0 IL_0016: ldloc.3 IL_0017: ldloc.s V_4 IL_0019: ldloc.s V_5 IL_001b: ldloca.s V_7 IL_001d: newobj ""CustomHandler..ctor(int, int, " + modifier + @" int, ref string, out object, out bool)"" IL_0022: stloc.s V_6 IL_0024: ldloc.s V_7 IL_0026: brfalse.s IL_0036 IL_0028: ldloca.s V_6 IL_002a: ldstr ""literal"" IL_002f: call ""bool CustomHandler.AppendLiteral(string)"" IL_0034: br.s IL_0037 IL_0036: ldc.i4.0 IL_0037: pop IL_0038: ldloc.s V_6 IL_003a: call ""void C.M(" + modifier + @" int, ref string, out object, CustomHandler)"" IL_003f: ldloc.1 IL_0040: call ""void System.Console.WriteLine(string)"" IL_0045: ldloc.2 IL_0046: call ""void System.Console.WriteLine(object)"" IL_004b: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(3).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0, 1, 2 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ReorderedAttributePositions([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C.M(GetInt(), GetString(), " + expression + @"); int GetInt() { Console.WriteLine(""GetInt""); return 10; } string GetString() { Console.WriteLine(""GetString""); return ""str""; } public class C { public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""s"", ""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, string s, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""s:"" + s); _builder.AppendLine(""i:"" + i.ToString()); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" GetInt GetString s:str i:10 literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 45 (0x2d) .maxstack 7 .locals init (string V_0, int V_1, CustomHandler V_2) IL_0000: call ""int Program.<<Main>$>g__GetInt|0_0()"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: call ""string Program.<<Main>$>g__GetString|0_1()"" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ldloca.s V_2 IL_0010: ldc.i4.7 IL_0011: ldc.i4.0 IL_0012: ldloc.0 IL_0013: ldloc.1 IL_0014: call ""CustomHandler..ctor(int, int, string, int)"" IL_0019: ldloca.s V_2 IL_001b: ldstr ""literal"" IL_0020: call ""bool CustomHandler.AppendLiteral(string)"" IL_0025: pop IL_0026: ldloc.2 IL_0027: call ""void C.M(int, string, CustomHandler)"" IL_002c: ret } " : @" { // Code size 52 (0x34) .maxstack 7 .locals init (string V_0, int V_1, CustomHandler V_2, bool V_3) IL_0000: call ""int Program.<<Main>$>g__GetInt|0_0()"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: call ""string Program.<<Main>$>g__GetString|0_1()"" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ldc.i4.7 IL_000f: ldc.i4.0 IL_0010: ldloc.0 IL_0011: ldloc.1 IL_0012: ldloca.s V_3 IL_0014: newobj ""CustomHandler..ctor(int, int, string, int, out bool)"" IL_0019: stloc.2 IL_001a: ldloc.3 IL_001b: brfalse.s IL_002b IL_001d: ldloca.s V_2 IL_001f: ldstr ""literal"" IL_0024: call ""bool CustomHandler.AppendLiteral(string)"" IL_0029: br.s IL_002c IL_002b: ldc.i4.0 IL_002c: pop IL_002d: ldloc.2 IL_002e: call ""void C.M(int, string, CustomHandler)"" IL_0033: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 1, 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ParametersReordered([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; GetC().M(s: GetString(), i: GetInt(), c: " + expression + @"); C GetC() { Console.WriteLine(""GetC""); return new C { Field = 5 }; } int GetInt() { Console.WriteLine(""GetInt""); return 10; } string GetString() { Console.WriteLine(""GetString""); return ""str""; } public class C { public int Field; public void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""s"", """", ""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, string s, C c, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""s:"" + s); _builder.AppendLine(""c.Field:"" + c.Field.ToString()); _builder.AppendLine(""i:"" + i.ToString()); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" GetC GetString GetInt s:str c.Field:5 i:10 literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 56 (0x38) .maxstack 9 .locals init (C V_0, string V_1, int V_2, string V_3, CustomHandler V_4) IL_0000: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call ""string Program.<<Main>$>g__GetString|0_2()"" IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: stloc.3 IL_000f: call ""int Program.<<Main>$>g__GetInt|0_1()"" IL_0014: stloc.2 IL_0015: ldloc.2 IL_0016: ldloc.3 IL_0017: ldloca.s V_4 IL_0019: ldc.i4.7 IL_001a: ldc.i4.0 IL_001b: ldloc.1 IL_001c: ldloc.0 IL_001d: ldloc.2 IL_001e: call ""CustomHandler..ctor(int, int, string, C, int)"" IL_0023: ldloca.s V_4 IL_0025: ldstr ""literal"" IL_002a: call ""bool CustomHandler.AppendLiteral(string)"" IL_002f: pop IL_0030: ldloc.s V_4 IL_0032: callvirt ""void C.M(int, string, CustomHandler)"" IL_0037: ret } " : @" { // Code size 65 (0x41) .maxstack 9 .locals init (C V_0, string V_1, int V_2, string V_3, CustomHandler V_4, bool V_5) IL_0000: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call ""string Program.<<Main>$>g__GetString|0_2()"" IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: stloc.3 IL_000f: call ""int Program.<<Main>$>g__GetInt|0_1()"" IL_0014: stloc.2 IL_0015: ldloc.2 IL_0016: ldloc.3 IL_0017: ldc.i4.7 IL_0018: ldc.i4.0 IL_0019: ldloc.1 IL_001a: ldloc.0 IL_001b: ldloc.2 IL_001c: ldloca.s V_5 IL_001e: newobj ""CustomHandler..ctor(int, int, string, C, int, out bool)"" IL_0023: stloc.s V_4 IL_0025: ldloc.s V_5 IL_0027: brfalse.s IL_0037 IL_0029: ldloca.s V_4 IL_002b: ldstr ""literal"" IL_0030: call ""bool CustomHandler.AppendLiteral(string)"" IL_0035: br.s IL_0038 IL_0037: ldc.i4.0 IL_0038: pop IL_0039: ldloc.s V_4 IL_003b: callvirt ""void C.M(int, string, CustomHandler)"" IL_0040: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 1, -1, 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_Duplicated([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C.M(GetInt(), """", " + expression + @"); int GetInt() { Console.WriteLine(""GetInt""); return 10; } public class C { public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i1, int i2" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i1:"" + i1.ToString()); _builder.AppendLine(""i2:"" + i2.ToString()); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" GetInt i1:10 i2:10 literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 43 (0x2b) .maxstack 7 .locals init (int V_0, CustomHandler V_1) IL_0000: call ""int Program.<<Main>$>g__GetInt|0_0()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldstr """" IL_000c: ldloca.s V_1 IL_000e: ldc.i4.7 IL_000f: ldc.i4.0 IL_0010: ldloc.0 IL_0011: ldloc.0 IL_0012: call ""CustomHandler..ctor(int, int, int, int)"" IL_0017: ldloca.s V_1 IL_0019: ldstr ""literal"" IL_001e: call ""bool CustomHandler.AppendLiteral(string)"" IL_0023: pop IL_0024: ldloc.1 IL_0025: call ""void C.M(int, string, CustomHandler)"" IL_002a: ret } " : @" { // Code size 50 (0x32) .maxstack 7 .locals init (int V_0, CustomHandler V_1, bool V_2) IL_0000: call ""int Program.<<Main>$>g__GetInt|0_0()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldstr """" IL_000c: ldc.i4.7 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: ldloc.0 IL_0010: ldloca.s V_2 IL_0012: newobj ""CustomHandler..ctor(int, int, int, int, out bool)"" IL_0017: stloc.1 IL_0018: ldloc.2 IL_0019: brfalse.s IL_0029 IL_001b: ldloca.s V_1 IL_001d: ldstr ""literal"" IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ldloc.1 IL_002c: call ""void C.M(int, string, CustomHandler)"" IL_0031: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0, 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_EmptyWithMatchingConstructor([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C.M(1, """", " + expression + @"); public class C { public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute()] CustomHandler c) => Console.WriteLine(c.ToString()); } [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount" + extraConstructorArg + @") { " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: "CustomHandler").VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 19 (0x13) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: ldstr """" IL_0006: ldc.i4.0 IL_0007: ldc.i4.0 IL_0008: newobj ""CustomHandler..ctor(int, int)"" IL_000d: call ""void C.M(int, string, CustomHandler)"" IL_0012: ret } " : @" { // Code size 21 (0x15) .maxstack 5 .locals init (bool V_0) IL_0000: ldc.i4.1 IL_0001: ldstr """" IL_0006: ldc.i4.0 IL_0007: ldc.i4.0 IL_0008: ldloca.s V_0 IL_000a: newobj ""CustomHandler..ctor(int, int, out bool)"" IL_000f: call ""void C.M(int, string, CustomHandler)"" IL_0014: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_EmptyWithoutMatchingConstructor([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; public class C { public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute()] CustomHandler c) { } } [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i" + extraConstructorArg + @") { " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }); // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); CreateCompilation(@"C.M(1, """", " + expression + @");", new[] { comp.EmitToImageReference() }).VerifyDiagnostics( (extraConstructorArg == "") ? new[] { // (1,12): error CS7036: There is no argument given that corresponds to the required parameter 'i' of 'CustomHandler.CustomHandler(int, int, int)' // C.M(1, "", $""); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("i", "CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 12), // (1,12): error CS1615: Argument 3 may not be passed with the 'out' keyword // C.M(1, "", $""); Diagnostic(ErrorCode.ERR_BadArgExtraRef, expression).WithArguments("3", "out").WithLocation(1, 12) } : new[] { // (1,12): error CS7036: There is no argument given that corresponds to the required parameter 'i' of 'CustomHandler.CustomHandler(int, int, int, out bool)' // C.M(1, "", $""); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("i", "CustomHandler.CustomHandler(int, int, int, out bool)").WithLocation(1, 12), // (1,12): error CS7036: There is no argument given that corresponds to the required parameter 'success' of 'CustomHandler.CustomHandler(int, int, int, out bool)' // C.M(1, "", $""); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("success", "CustomHandler.CustomHandler(int, int, int, out bool)").WithLocation(1, 12) } ); static void validate(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_OnIndexerRvalue([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; var c = new C(); Console.WriteLine(c[10, ""str"", " + expression + @"]); public class C { public string this[int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"")] CustomHandler c] { get => c.ToString(); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i1, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i1:"" + i1.ToString()); _builder.AppendLine(""s:"" + s); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" i1:10 s:str literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 52 (0x34) .maxstack 8 .locals init (int V_0, string V_1, CustomHandler V_2) IL_0000: newobj ""C..ctor()"" IL_0005: ldc.i4.s 10 IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldstr ""str"" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldloca.s V_2 IL_0012: ldc.i4.7 IL_0013: ldc.i4.0 IL_0014: ldloc.0 IL_0015: ldloc.1 IL_0016: call ""CustomHandler..ctor(int, int, int, string)"" IL_001b: ldloca.s V_2 IL_001d: ldstr ""literal"" IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" IL_0027: pop IL_0028: ldloc.2 IL_0029: callvirt ""string C.this[int, string, CustomHandler].get"" IL_002e: call ""void System.Console.WriteLine(string)"" IL_0033: ret } " : @" { // Code size 59 (0x3b) .maxstack 8 .locals init (int V_0, string V_1, CustomHandler V_2, bool V_3) IL_0000: newobj ""C..ctor()"" IL_0005: ldc.i4.s 10 IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldstr ""str"" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldc.i4.7 IL_0011: ldc.i4.0 IL_0012: ldloc.0 IL_0013: ldloc.1 IL_0014: ldloca.s V_3 IL_0016: newobj ""CustomHandler..ctor(int, int, int, string, out bool)"" IL_001b: stloc.2 IL_001c: ldloc.3 IL_001d: brfalse.s IL_002d IL_001f: ldloca.s V_2 IL_0021: ldstr ""literal"" IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" IL_002b: br.s IL_002e IL_002d: ldc.i4.0 IL_002e: pop IL_002f: ldloc.2 IL_0030: callvirt ""string C.this[int, string, CustomHandler].get"" IL_0035: call ""void System.Console.WriteLine(string)"" IL_003a: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetIndexer<PropertySymbol>("Item").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_OnIndexerLvalue([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; var c = new C(); c[10, ""str"", " + expression + @"] = """"; public class C { public string this[int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"")] CustomHandler c] { set => Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i1, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i1:"" + i1.ToString()); _builder.AppendLine(""s:"" + s); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" i1:10 s:str literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 52 (0x34) .maxstack 8 .locals init (int V_0, string V_1, CustomHandler V_2) IL_0000: newobj ""C..ctor()"" IL_0005: ldc.i4.s 10 IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldstr ""str"" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldloca.s V_2 IL_0012: ldc.i4.7 IL_0013: ldc.i4.0 IL_0014: ldloc.0 IL_0015: ldloc.1 IL_0016: call ""CustomHandler..ctor(int, int, int, string)"" IL_001b: ldloca.s V_2 IL_001d: ldstr ""literal"" IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" IL_0027: pop IL_0028: ldloc.2 IL_0029: ldstr """" IL_002e: callvirt ""void C.this[int, string, CustomHandler].set"" IL_0033: ret } " : @" { // Code size 59 (0x3b) .maxstack 8 .locals init (int V_0, string V_1, CustomHandler V_2, bool V_3) IL_0000: newobj ""C..ctor()"" IL_0005: ldc.i4.s 10 IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldstr ""str"" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldc.i4.7 IL_0011: ldc.i4.0 IL_0012: ldloc.0 IL_0013: ldloc.1 IL_0014: ldloca.s V_3 IL_0016: newobj ""CustomHandler..ctor(int, int, int, string, out bool)"" IL_001b: stloc.2 IL_001c: ldloc.3 IL_001d: brfalse.s IL_002d IL_001f: ldloca.s V_2 IL_0021: ldstr ""literal"" IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" IL_002b: br.s IL_002e IL_002d: ldc.i4.0 IL_002e: pop IL_002f: ldloc.2 IL_0030: ldstr """" IL_0035: callvirt ""void C.this[int, string, CustomHandler].set"" IL_003a: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetIndexer<PropertySymbol>("Item").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ThisParameter([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; (new C(5)).M((int)10, ""str"", " + expression + @"); public class C { public int Prop { get; } public C(int i) => Prop = i; public void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", """", ""s"")] CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i1, C c, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i1:"" + i1.ToString()); _builder.AppendLine(""c.Prop:"" + c.Prop.ToString()); _builder.AppendLine(""s:"" + s); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" i1:10 c.Prop:5 s:str literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", (extraConstructorArg == "") ? @" { // Code size 51 (0x33) .maxstack 9 .locals init (C V_0, int V_1, string V_2, CustomHandler V_3) IL_0000: ldc.i4.5 IL_0001: newobj ""C..ctor(int)"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s 10 IL_000a: stloc.1 IL_000b: ldloc.1 IL_000c: ldstr ""str"" IL_0011: stloc.2 IL_0012: ldloc.2 IL_0013: ldloca.s V_3 IL_0015: ldc.i4.7 IL_0016: ldc.i4.0 IL_0017: ldloc.1 IL_0018: ldloc.0 IL_0019: ldloc.2 IL_001a: call ""CustomHandler..ctor(int, int, int, C, string)"" IL_001f: ldloca.s V_3 IL_0021: ldstr ""literal"" IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" IL_002b: pop IL_002c: ldloc.3 IL_002d: callvirt ""void C.M(int, string, CustomHandler)"" IL_0032: ret } " : @" { // Code size 59 (0x3b) .maxstack 9 .locals init (C V_0, int V_1, string V_2, CustomHandler V_3, bool V_4) IL_0000: ldc.i4.5 IL_0001: newobj ""C..ctor(int)"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s 10 IL_000a: stloc.1 IL_000b: ldloc.1 IL_000c: ldstr ""str"" IL_0011: stloc.2 IL_0012: ldloc.2 IL_0013: ldc.i4.7 IL_0014: ldc.i4.0 IL_0015: ldloc.1 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: ldloca.s V_4 IL_001a: newobj ""CustomHandler..ctor(int, int, int, C, string, out bool)"" IL_001f: stloc.3 IL_0020: ldloc.s V_4 IL_0022: brfalse.s IL_0032 IL_0024: ldloca.s V_3 IL_0026: ldstr ""literal"" IL_002b: call ""bool CustomHandler.AppendLiteral(string)"" IL_0030: br.s IL_0033 IL_0032: ldc.i4.0 IL_0033: pop IL_0034: ldloc.3 IL_0035: callvirt ""void C.M(int, string, CustomHandler)"" IL_003a: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0, -1, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [InlineData(@"$""literal""")] [InlineData(@"$"""" + $""literal""")] public void InterpolatedStringHandlerArgumentAttribute_OnConstructor(string expression) { var code = @" using System; using System.Runtime.CompilerServices; _ = new C(5, " + expression + @"); public class C { public int Prop { get; } public C(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")]CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) : this(literalLength, formattedCount) { _builder.AppendLine(""i:"" + i.ToString()); } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" i:5 literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 34 (0x22) .maxstack 5 .locals init (int V_0, CustomHandler V_1) IL_0000: ldc.i4.5 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldloca.s V_1 IL_0005: ldc.i4.7 IL_0006: ldc.i4.0 IL_0007: ldloc.0 IL_0008: call ""CustomHandler..ctor(int, int, int)"" IL_000d: ldloca.s V_1 IL_000f: ldstr ""literal"" IL_0014: call ""bool CustomHandler.AppendLiteral(string)"" IL_0019: pop IL_001a: ldloc.1 IL_001b: newobj ""C..ctor(int, CustomHandler)"" IL_0020: pop IL_0021: ret } "); } [Theory] [CombinatorialData] public void RefReturningMethodAsReceiver_RefParameter([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression, [CombinatorialValues("class", "struct")] string receiverType) { var code = @" using System; using System.Runtime.CompilerServices; C c = new C(1); GetC(ref c).M(" + expression + @"); Console.WriteLine(c.I); ref C GetC(ref C c) { Console.WriteLine(""GetC""); return ref c; } public " + receiverType + @" C { public int I; public C(int i) { I = i; } public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref C c" + extraConstructorArg + @") : this(literalLength, formattedCount) { c = new C(2); " + (extraConstructorArg != "" ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics(extraConstructorArg != "" ? new[] { // (6,1): error CS1620: Argument 3 must be passed with the 'ref' keyword // GetC(ref c).M($"literal" + $""); Diagnostic(ErrorCode.ERR_BadArgRef, "GetC(ref c)").WithArguments("3", "ref").WithLocation(6, 1), // (6,15): error CS7036: There is no argument given that corresponds to the required parameter 'success' of 'CustomHandler.CustomHandler(int, int, ref C, out bool)' // GetC(ref c).M($"literal" + $""); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("success", "CustomHandler.CustomHandler(int, int, ref C, out bool)").WithLocation(6, 15) } : new[] { // (6,1): error CS1620: Argument 3 must be passed with the 'ref' keyword // GetC(ref c).M($"literal" + $""); Diagnostic(ErrorCode.ERR_BadArgRef, "GetC(ref c)").WithArguments("3", "ref").WithLocation(6, 1) }); } [Theory] [CombinatorialData] public void RefReturningMethodAsReceiver_MismatchedRefness_01([CombinatorialValues("ref readonly", "")] string refness, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; C c = new C(1); GetC().M(" + expression + @"); " + refness + @" C GetC() => throw null; public class C { public C(int i) { } public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) { } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref C c) : this(literalLength, formattedCount) { } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (5,1): error CS1620: Argument 3 must be passed with the 'ref' keyword // GetC().M($"literal" + $""); Diagnostic(ErrorCode.ERR_BadArgRef, "GetC()").WithArguments("3", "ref").WithLocation(5, 1) ); } [Theory] [CombinatorialData] public void RefReturningMethodAsReceiver_MismatchedRefness_02([CombinatorialValues("in", "")] string refness, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C c = new C(1); GetC(ref c).M(" + expression + @"); ref C GetC(ref C c) => ref c; public class C { public int I; public C(int i) { I = i; } public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount," + refness + @" C c) : this(literalLength, formattedCount) { _builder.Append(c.I); } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); // ILVerify: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. var verifier = CompileAndVerify( new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, expectedOutput: "1literal:literal", symbolValidator: validator, sourceSymbolValidator: validator, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.FailsILVerify : Verification.Skipped); verifier.VerifyIL("<top-level-statements-entry-point>", refness == "in" ? @" { // Code size 47 (0x2f) .maxstack 4 .locals init (C V_0, //c C V_1, CustomHandler V_2) IL_0000: ldc.i4.1 IL_0001: newobj ""C..ctor(int)"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call ""ref C Program.<<Main>$>g__GetC|0_0(ref C)"" IL_000e: ldind.ref IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldc.i4.7 IL_0012: ldc.i4.0 IL_0013: ldloca.s V_1 IL_0015: newobj ""CustomHandler..ctor(int, int, in C)"" IL_001a: stloc.2 IL_001b: ldloca.s V_2 IL_001d: ldstr ""literal"" IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" IL_0027: pop IL_0028: ldloc.2 IL_0029: callvirt ""void C.M(CustomHandler)"" IL_002e: ret } " : @" { // Code size 47 (0x2f) .maxstack 5 .locals init (C V_0, //c C V_1, CustomHandler V_2) IL_0000: ldc.i4.1 IL_0001: newobj ""C..ctor(int)"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call ""ref C Program.<<Main>$>g__GetC|0_0(ref C)"" IL_000e: ldind.ref IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldloca.s V_2 IL_0013: ldc.i4.7 IL_0014: ldc.i4.0 IL_0015: ldloc.1 IL_0016: call ""CustomHandler..ctor(int, int, C)"" IL_001b: ldloca.s V_2 IL_001d: ldstr ""literal"" IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" IL_0027: pop IL_0028: ldloc.2 IL_0029: callvirt ""void C.M(CustomHandler)"" IL_002e: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { -1 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory, CombinatorialData] [WorkItem(56624, "https://github.com/dotnet/roslyn/issues/56624")] public void RefOrOutParameter_AsReceiver([CombinatorialValues("ref", "out")] string parameterRefness, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C c = default; localFunc(" + parameterRefness + @" c); void localFunc(" + parameterRefness + @" C c) { c = new C(1); c.M(" + expression + @"); } public class C { public int I; public C(int i) { I = i; } public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { _builder.Append(c.I); } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, expectedOutput: "1literal:literal", symbolValidator: validator, sourceSymbolValidator: validator); verifier.VerifyDiagnostics(); verifier.VerifyIL($"Program.<<Main>$>g__localFunc|0_0({parameterRefness} C)", @" { // Code size 42 (0x2a) .maxstack 5 .locals init (C V_0, CustomHandler V_1) IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: newobj ""C..ctor(int)"" IL_0007: stind.ref IL_0008: ldarg.0 IL_0009: ldind.ref IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: ldloca.s V_1 IL_000e: ldc.i4.7 IL_000f: ldc.i4.0 IL_0010: ldloc.0 IL_0011: call ""CustomHandler..ctor(int, int, C)"" IL_0016: ldloca.s V_1 IL_0018: ldstr ""literal"" IL_001d: call ""bool CustomHandler.AppendLiteral(string)"" IL_0022: pop IL_0023: ldloc.1 IL_0024: callvirt ""void C.M(CustomHandler)"" IL_0029: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { -1 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void StructReceiver_Rvalue(string expression) { var code = @" using System; using System.Runtime.CompilerServices; S s1 = new S { I = 1 }; S s2 = new S { I = 2 }; s1.M(s2, " + expression + @"); public struct S { public int I; public void M(S s2, [InterpolatedStringHandlerArgument("""", ""s2"")]CustomHandler handler) { Console.WriteLine(""s1.I:"" + this.I.ToString()); Console.WriteLine(""s2.I:"" + s2.I.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, S s1, S s2) : this(literalLength, formattedCount) { s1.I = 3; s2.I = 4; } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" s1.I:1 s2.I:2"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 63 (0x3f) .maxstack 6 .locals init (S V_0, //s1 S V_1, //s2 S V_2, S& V_3) IL_0000: ldloca.s V_2 IL_0002: initobj ""S"" IL_0008: ldloca.s V_2 IL_000a: ldc.i4.1 IL_000b: stfld ""int S.I"" IL_0010: ldloc.2 IL_0011: stloc.0 IL_0012: ldloca.s V_2 IL_0014: initobj ""S"" IL_001a: ldloca.s V_2 IL_001c: ldc.i4.2 IL_001d: stfld ""int S.I"" IL_0022: ldloc.2 IL_0023: stloc.1 IL_0024: ldloca.s V_0 IL_0026: stloc.3 IL_0027: ldloc.3 IL_0028: ldloc.1 IL_0029: stloc.2 IL_002a: ldloc.2 IL_002b: ldc.i4.0 IL_002c: ldc.i4.0 IL_002d: ldloc.3 IL_002e: ldobj ""S"" IL_0033: ldloc.2 IL_0034: newobj ""CustomHandler..ctor(int, int, S, S)"" IL_0039: call ""void S.M(S, CustomHandler)"" IL_003e: ret } "); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: @" s1.I:1 s2.I:2"); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void StructReceiver_Rvalue_ObjectCreationReceiver_01() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; new StructLogger(true, 1).Log($""log:{i++}""); Console.WriteLine($""(1) i={i}""); internal readonly struct StructLogger { private readonly bool _disabled; private readonly int _id; public bool Disabled => _disabled; public int Id => _id; public StructLogger(bool disabled, int id) { _disabled = disabled; _id = id; Console.WriteLine(""Creating StructLogger""); } public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) => Console.WriteLine($""StructLogger#{_id}: "" + handler.GetContent()); } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from StructLogger#{structLogger.Id}""); enabled = !structLogger.Disabled; _builder = structLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating StructLogger Creating DummyHandler from StructLogger#1 StructLogger#1: (1) i=0"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (int V_0, //i StructLogger V_1, DummyHandler V_2, bool V_3) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""StructLogger..ctor(bool, int)"" IL_000b: ldc.i4.4 IL_000c: ldc.i4.1 IL_000d: ldloc.1 IL_000e: ldloca.s V_3 IL_0010: newobj ""DummyHandler..ctor(int, int, StructLogger, out bool)"" IL_0015: stloc.2 IL_0016: ldloc.3 IL_0017: brfalse.s IL_0031 IL_0019: ldloca.s V_2 IL_001b: ldstr ""log:"" IL_0020: call ""void DummyHandler.AppendLiteral(string)"" IL_0025: ldloca.s V_2 IL_0027: ldloc.0 IL_0028: dup IL_0029: ldc.i4.1 IL_002a: add IL_002b: stloc.0 IL_002c: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0031: ldloca.s V_1 IL_0033: ldloc.2 IL_0034: call ""void StructLogger.Log(DummyHandler)"" IL_0039: ldstr ""(1) i={0}"" IL_003e: ldloc.0 IL_003f: box ""int"" IL_0044: call ""string string.Format(string, object)"" IL_0049: call ""void System.Console.WriteLine(string)"" IL_004e: ret } "); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void StructReceiver_Rvalue_ObjectCreationReceiver_02() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; new StructLogger(true, 1).Log($""log:{i++}""); Console.WriteLine($""(1) i={i}""); var s = new StructLogger(true, 2); s.Log($""log:{i++}""); Console.WriteLine($""(2) i={i}""); internal readonly struct StructLogger { private readonly bool _disabled; private readonly int _id; public bool Disabled => _disabled; public int Id => _id; public StructLogger(bool disabled, int id) { _disabled = disabled; _id = id; } public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) => Console.WriteLine($""StructLogger#{_id}: "" + handler.GetContent()); } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from StructLogger#{structLogger.Id}""); enabled = !structLogger.Disabled; _builder = structLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating DummyHandler from StructLogger#1 StructLogger#1: (1) i=0 Creating DummyHandler from StructLogger#2 StructLogger#2: (2) i=0 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 177 (0xb1) .maxstack 4 .locals init (int V_0, //i StructLogger V_1, //s StructLogger V_2, DummyHandler V_3, bool V_4, StructLogger& V_5, DummyHandler V_6) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_2 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""StructLogger..ctor(bool, int)"" IL_000b: ldc.i4.4 IL_000c: ldc.i4.1 IL_000d: ldloc.2 IL_000e: ldloca.s V_4 IL_0010: newobj ""DummyHandler..ctor(int, int, StructLogger, out bool)"" IL_0015: stloc.3 IL_0016: ldloc.s V_4 IL_0018: brfalse.s IL_0034 IL_001a: ldloca.s V_3 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: nop IL_0027: ldloca.s V_3 IL_0029: ldloc.0 IL_002a: dup IL_002b: ldc.i4.1 IL_002c: add IL_002d: stloc.0 IL_002e: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0033: nop IL_0034: ldloca.s V_2 IL_0036: ldloc.3 IL_0037: call ""void StructLogger.Log(DummyHandler)"" IL_003c: nop IL_003d: ldstr ""(1) i={0}"" IL_0042: ldloc.0 IL_0043: box ""int"" IL_0048: call ""string string.Format(string, object)"" IL_004d: call ""void System.Console.WriteLine(string)"" IL_0052: nop IL_0053: ldloca.s V_1 IL_0055: ldc.i4.1 IL_0056: ldc.i4.2 IL_0057: call ""StructLogger..ctor(bool, int)"" IL_005c: ldloca.s V_1 IL_005e: stloc.s V_5 IL_0060: ldc.i4.4 IL_0061: ldc.i4.1 IL_0062: ldloc.s V_5 IL_0064: ldobj ""StructLogger"" IL_0069: ldloca.s V_4 IL_006b: newobj ""DummyHandler..ctor(int, int, StructLogger, out bool)"" IL_0070: stloc.s V_6 IL_0072: ldloc.s V_4 IL_0074: brfalse.s IL_0090 IL_0076: ldloca.s V_6 IL_0078: ldstr ""log:"" IL_007d: call ""void DummyHandler.AppendLiteral(string)"" IL_0082: nop IL_0083: ldloca.s V_6 IL_0085: ldloc.0 IL_0086: dup IL_0087: ldc.i4.1 IL_0088: add IL_0089: stloc.0 IL_008a: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_008f: nop IL_0090: ldloc.s V_5 IL_0092: ldloc.s V_6 IL_0094: call ""void StructLogger.Log(DummyHandler)"" IL_0099: nop IL_009a: ldstr ""(2) i={0}"" IL_009f: ldloc.0 IL_00a0: box ""int"" IL_00a5: call ""string string.Format(string, object)"" IL_00aa: call ""void System.Console.WriteLine(string)"" IL_00af: nop IL_00b0: ret } "); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void StructArgument_Rvalue_ObjectCreationArgument_01() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; Log(new StructLogger(true, 1), $""log:{i++}""); Console.WriteLine($""(1) i={i}""); static void Log(StructLogger logger, [InterpolatedStringHandlerArgument(""logger"")] DummyHandler handler) => Console.WriteLine($""StructLogger#{logger._id}: "" + handler.GetContent()); internal readonly struct StructLogger { private readonly bool _disabled; public readonly int _id; public bool Disabled => _disabled; public int Id => _id; public StructLogger(bool disabled, int id) { _disabled = disabled; _id = id; } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from StructLogger#{structLogger.Id}""); enabled = !structLogger.Disabled; _builder = structLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating DummyHandler from StructLogger#1 StructLogger#1: (1) i=0"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 78 (0x4e) .maxstack 5 .locals init (int V_0, //i StructLogger V_1, DummyHandler V_2, bool V_3) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""StructLogger..ctor(bool, int)"" IL_000b: ldloc.1 IL_000c: ldc.i4.4 IL_000d: ldc.i4.1 IL_000e: ldloc.1 IL_000f: ldloca.s V_3 IL_0011: newobj ""DummyHandler..ctor(int, int, StructLogger, out bool)"" IL_0016: stloc.2 IL_0017: ldloc.3 IL_0018: brfalse.s IL_0032 IL_001a: ldloca.s V_2 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: ldloca.s V_2 IL_0028: ldloc.0 IL_0029: dup IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.0 IL_002d: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0032: ldloc.2 IL_0033: call ""void Program.<<Main>$>g__Log|0_0(StructLogger, DummyHandler)"" IL_0038: ldstr ""(1) i={0}"" IL_003d: ldloc.0 IL_003e: box ""int"" IL_0043: call ""string string.Format(string, object)"" IL_0048: call ""void System.Console.WriteLine(string)"" IL_004d: ret } "); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void StructArgument_Rvalue_ObjectCreationArgument_02() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; Log(new StructLogger(true, 1), $""log:{i++}""); Console.WriteLine($""(1) i={i}""); static void Log(in StructLogger logger, [InterpolatedStringHandlerArgument(""logger"")] DummyHandler handler) => Console.WriteLine($""StructLogger#{logger._id}: "" + handler.GetContent()); internal readonly struct StructLogger { private readonly bool _disabled; public readonly int _id; public bool Disabled => _disabled; public int Id => _id; public StructLogger(bool disabled, int id) { _disabled = disabled; _id = id; } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, in StructLogger structLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from StructLogger#{structLogger.Id}""); enabled = !structLogger.Disabled; _builder = structLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating DummyHandler from StructLogger#1 StructLogger#1: (1) i=0"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 80 (0x50) .maxstack 4 .locals init (int V_0, //i StructLogger V_1, DummyHandler V_2, bool V_3) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""StructLogger..ctor(bool, int)"" IL_000b: ldc.i4.4 IL_000c: ldc.i4.1 IL_000d: ldloca.s V_1 IL_000f: ldloca.s V_3 IL_0011: newobj ""DummyHandler..ctor(int, int, in StructLogger, out bool)"" IL_0016: stloc.2 IL_0017: ldloc.3 IL_0018: brfalse.s IL_0032 IL_001a: ldloca.s V_2 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: ldloca.s V_2 IL_0028: ldloc.0 IL_0029: dup IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.0 IL_002d: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0032: ldloca.s V_1 IL_0034: ldloc.2 IL_0035: call ""void Program.<<Main>$>g__Log|0_0(in StructLogger, DummyHandler)"" IL_003a: ldstr ""(1) i={0}"" IL_003f: ldloc.0 IL_0040: box ""int"" IL_0045: call ""string string.Format(string, object)"" IL_004a: call ""void System.Console.WriteLine(string)"" IL_004f: ret }"); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void StructArgument_Rvalue_ObjectCreationArgument_03() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; Log(ref new StructLogger(true, 1), $""log:{i++}""); Console.WriteLine($""(1) i={i}""); static void Log(ref StructLogger logger, [InterpolatedStringHandlerArgument(""logger"")] DummyHandler handler) => Console.WriteLine($""StructLogger#{logger._id}: "" + handler.GetContent()); internal readonly struct StructLogger { private readonly bool _disabled; public readonly int _id; public bool Disabled => _disabled; public int Id => _id; public StructLogger(bool disabled, int id) { _disabled = disabled; _id = id; } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ref StructLogger structLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from StructLogger#{structLogger.Id}""); enabled = !structLogger.Disabled; _builder = structLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (7,9): error CS1510: A ref or out value must be an assignable variable // Log(ref new StructLogger(true, 1), $"log:{i++}"); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new StructLogger(true, 1)").WithLocation(7, 9) ); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void ReferenceReceiver_Rvalue_ObjectCreationReceiver_01() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; new ClassLogger(true, 1).Log($""log:{i++}""); Console.WriteLine($""(1) i={i}""); internal readonly struct ClassLogger { private readonly bool _disabled; private readonly int _id; public bool Disabled => _disabled; public int Id => _id; public ClassLogger(bool disabled, int id) { _disabled = disabled; _id = id; } public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) => Console.WriteLine($""ClassLogger#{_id}: "" + handler.GetContent()); } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ClassLogger ClassLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from ClassLogger#{ClassLogger.Id}""); enabled = !ClassLogger.Disabled; _builder = ClassLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating DummyHandler from ClassLogger#1 ClassLogger#1: (1) i=0"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (int V_0, //i ClassLogger V_1, DummyHandler V_2, bool V_3) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""ClassLogger..ctor(bool, int)"" IL_000b: ldc.i4.4 IL_000c: ldc.i4.1 IL_000d: ldloc.1 IL_000e: ldloca.s V_3 IL_0010: newobj ""DummyHandler..ctor(int, int, ClassLogger, out bool)"" IL_0015: stloc.2 IL_0016: ldloc.3 IL_0017: brfalse.s IL_0031 IL_0019: ldloca.s V_2 IL_001b: ldstr ""log:"" IL_0020: call ""void DummyHandler.AppendLiteral(string)"" IL_0025: ldloca.s V_2 IL_0027: ldloc.0 IL_0028: dup IL_0029: ldc.i4.1 IL_002a: add IL_002b: stloc.0 IL_002c: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0031: ldloca.s V_1 IL_0033: ldloc.2 IL_0034: call ""void ClassLogger.Log(DummyHandler)"" IL_0039: ldstr ""(1) i={0}"" IL_003e: ldloc.0 IL_003f: box ""int"" IL_0044: call ""string string.Format(string, object)"" IL_0049: call ""void System.Console.WriteLine(string)"" IL_004e: ret } "); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void ReferenceArgument_Rvalue_ObjectCreationArgument_01() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; Log(new ClassLogger(true, 1), $""log:{i++}""); Console.WriteLine($""(1) i={i}""); static void Log(ClassLogger logger, [InterpolatedStringHandlerArgument(""logger"")] DummyHandler handler) => Console.WriteLine($""ClassLogger#{logger._id}: "" + handler.GetContent()); internal readonly struct ClassLogger { private readonly bool _disabled; public readonly int _id; public bool Disabled => _disabled; public int Id => _id; public ClassLogger(bool disabled, int id) { _disabled = disabled; _id = id; } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ClassLogger ClassLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from ClassLogger#{ClassLogger.Id}""); enabled = !ClassLogger.Disabled; _builder = ClassLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating DummyHandler from ClassLogger#1 ClassLogger#1: (1) i=0"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 78 (0x4e) .maxstack 5 .locals init (int V_0, //i ClassLogger V_1, DummyHandler V_2, bool V_3) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""ClassLogger..ctor(bool, int)"" IL_000b: ldloc.1 IL_000c: ldc.i4.4 IL_000d: ldc.i4.1 IL_000e: ldloc.1 IL_000f: ldloca.s V_3 IL_0011: newobj ""DummyHandler..ctor(int, int, ClassLogger, out bool)"" IL_0016: stloc.2 IL_0017: ldloc.3 IL_0018: brfalse.s IL_0032 IL_001a: ldloca.s V_2 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: ldloca.s V_2 IL_0028: ldloc.0 IL_0029: dup IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.0 IL_002d: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0032: ldloc.2 IL_0033: call ""void Program.<<Main>$>g__Log|0_0(ClassLogger, DummyHandler)"" IL_0038: ldstr ""(1) i={0}"" IL_003d: ldloc.0 IL_003e: box ""int"" IL_0043: call ""string string.Format(string, object)"" IL_0048: call ""void System.Console.WriteLine(string)"" IL_004d: ret } "); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void ReferenceArgument_Rvalue_ObjectCreationArgument_02() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; Log(new ClassLogger(true, 1), $""log:{i++}""); Console.WriteLine($""(1) i={i}""); static void Log(in ClassLogger logger, [InterpolatedStringHandlerArgument(""logger"")] DummyHandler handler) => Console.WriteLine($""ClassLogger#{logger._id}: "" + handler.GetContent()); internal readonly struct ClassLogger { private readonly bool _disabled; public readonly int _id; public bool Disabled => _disabled; public int Id => _id; public ClassLogger(bool disabled, int id) { _disabled = disabled; _id = id; } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, in ClassLogger ClassLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from ClassLogger#{ClassLogger.Id}""); enabled = !ClassLogger.Disabled; _builder = ClassLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" Creating DummyHandler from ClassLogger#1 ClassLogger#1: (1) i=0"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 80 (0x50) .maxstack 4 .locals init (int V_0, //i ClassLogger V_1, DummyHandler V_2, bool V_3) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: ldc.i4.1 IL_0006: call ""ClassLogger..ctor(bool, int)"" IL_000b: ldc.i4.4 IL_000c: ldc.i4.1 IL_000d: ldloca.s V_1 IL_000f: ldloca.s V_3 IL_0011: newobj ""DummyHandler..ctor(int, int, in ClassLogger, out bool)"" IL_0016: stloc.2 IL_0017: ldloc.3 IL_0018: brfalse.s IL_0032 IL_001a: ldloca.s V_2 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: ldloca.s V_2 IL_0028: ldloc.0 IL_0029: dup IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.0 IL_002d: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0032: ldloca.s V_1 IL_0034: ldloc.2 IL_0035: call ""void Program.<<Main>$>g__Log|0_0(in ClassLogger, DummyHandler)"" IL_003a: ldstr ""(1) i={0}"" IL_003f: ldloc.0 IL_0040: box ""int"" IL_0045: call ""string string.Format(string, object)"" IL_004a: call ""void System.Console.WriteLine(string)"" IL_004f: ret }"); } [Fact, WorkItem(58514, "https://github.com/dotnet/roslyn/issues/58514")] public void ReferenceArgument_Rvalue_ObjectCreationArgument_03() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var i = 0; Log(ref new ClassLogger(true, 1), $""log:{i++}""); Console.WriteLine($""(1) i={i}""); static void Log(ref ClassLogger logger, [InterpolatedStringHandlerArgument(""logger"")] DummyHandler handler) => Console.WriteLine($""ClassLogger#{logger._id}: "" + handler.GetContent()); internal readonly struct ClassLogger { private readonly bool _disabled; public readonly int _id; public bool Disabled => _disabled; public int Id => _id; public ClassLogger(bool disabled, int id) { _disabled = disabled; _id = id; } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ref ClassLogger ClassLogger, out bool enabled) { Console.WriteLine($""Creating DummyHandler from ClassLogger#{ClassLogger.Id}""); enabled = !ClassLogger.Disabled; _builder = ClassLogger.Disabled ? null : new StringBuilder(); } public string GetContent() => _builder?.ToString(); public void AppendLiteral(string s) => _builder?.Append(s); public void AppendFormatted<T>(T t) => _builder?.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (7,9): error CS1510: A ref or out value must be an assignable variable // Log(ref new ClassLogger(true, 1), $"log:{i++}"); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new ClassLogger(true, 1)").WithLocation(7, 9) ); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void StructReceiver_Lvalue_01(string expression) { var code = @" using System; using System.Runtime.CompilerServices; S s1 = new S { I = 1 }; S s2 = new S { I = 2 }; s1.M(ref s2, " + expression + @"); public struct S { public int I; public void M(ref S s2, [InterpolatedStringHandlerArgument("""", ""s2"")]CustomHandler handler) { Console.WriteLine(""s1.I:"" + this.I.ToString()); Console.WriteLine(""s2.I:"" + s2.I.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref S s1, ref S s2) : this(literalLength, formattedCount) { s1.I = 3; s2.I = 4; } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (8,1): error CS1620: Argument 3 must be passed with the 'ref' keyword // s1.M(ref s2, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "s1").WithArguments("3", "ref").WithLocation(8, 1) ); } [Fact] [WorkItem(65470, "https://github.com/dotnet/roslyn/issues/65470")] public void StructReceiver_Lvalue_02() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var l = new StructLogger(); Console.WriteLine(""logged = {0}"", l._logged); l = test(l); Console.WriteLine(""logged = {0}"", l._logged); StructLogger test(StructLogger l) { l.Log($""log:{0}""); return l; } internal struct StructLogger { public int _logged; public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) { _logged++; Console.WriteLine($""StructLogger: "" + handler.GetContent()); } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 Creating DummyHandler StructLogger: log:0 logged = 1 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__test|0_0", @" { // Code size 47 (0x2f) .maxstack 5 .locals init (StructLogger& V_0, DummyHandler V_1) IL_0000: ldarga.s V_0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldloca.s V_1 IL_0006: ldc.i4.4 IL_0007: ldc.i4.1 IL_0008: ldloc.0 IL_0009: ldobj ""StructLogger"" IL_000e: call ""DummyHandler..ctor(int, int, StructLogger)"" IL_0013: ldloca.s V_1 IL_0015: ldstr ""log:"" IL_001a: call ""void DummyHandler.AppendLiteral(string)"" IL_001f: ldloca.s V_1 IL_0021: ldc.i4.0 IL_0022: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0027: ldloc.1 IL_0028: call ""void StructLogger.Log(DummyHandler)"" IL_002d: ldarg.0 IL_002e: ret } "); } [Fact] public void StructReceiver_Lvalue_03() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var l = new StructLogger(); Console.WriteLine(""logged = {0}"", l._logged); test(ref l); Console.WriteLine(""logged = {0}"", l._logged); void test(ref StructLogger l) { l.Log($""log:{0}""); } internal struct StructLogger { public int _logged; public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) { _logged++; Console.WriteLine($""StructLogger: "" + handler.GetContent()); } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 Creating DummyHandler StructLogger: log:0 logged = 1 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__test|0_0", @" { // Code size 45 (0x2d) .maxstack 5 .locals init (StructLogger& V_0, DummyHandler V_1) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldloca.s V_1 IL_0005: ldc.i4.4 IL_0006: ldc.i4.1 IL_0007: ldloc.0 IL_0008: ldobj ""StructLogger"" IL_000d: call ""DummyHandler..ctor(int, int, StructLogger)"" IL_0012: ldloca.s V_1 IL_0014: ldstr ""log:"" IL_0019: call ""void DummyHandler.AppendLiteral(string)"" IL_001e: ldloca.s V_1 IL_0020: ldc.i4.0 IL_0021: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0026: ldloc.1 IL_0027: call ""void StructLogger.Log(DummyHandler)"" IL_002c: ret } "); } [Fact] public void StructReceiver_Lvalue_04() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var l = new StructLogger(); Console.WriteLine(""logged = {0}"", l._logged); l = test1(l); Console.WriteLine(""logged = {0}"", l._logged); l = test2(l); Console.WriteLine(""logged = {0}"", l._logged); test3(ref l); Console.WriteLine(""logged = {0}"", l._logged); test4(ref l); Console.WriteLine(""logged = {0}"", l._logged); T test1<T>(T l) where T : ILogger { l.Log($""log:{-1}""); return l; } T test2<T>(T l) where T : struct, ILogger { l.Log($""log:{-2}""); return l; } void test3<T>(ref T l) where T : ILogger { l.Log($""log:{-3}""); } void test4<T>(ref T l) where T : struct, ILogger { l.Log($""log:{-4}""); } interface ILogger { void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler); } internal struct StructLogger : ILogger { public int _logged; public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) { _logged++; Console.WriteLine($""StructLogger: "" + handler.GetContent()); } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ILogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 Creating DummyHandler StructLogger: log:-1 logged = 1 Creating DummyHandler StructLogger: log:-2 logged = 2 Creating DummyHandler StructLogger: log:-3 logged = 3 Creating DummyHandler StructLogger: log:-4 logged = 4 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__test1|0_0<T>", @" { // Code size 88 (0x58) .maxstack 5 .locals init (T& V_0, T V_1, T& V_2, T V_3, DummyHandler V_4) IL_0000: ldarga.s V_0 IL_0002: stloc.2 IL_0003: ldloca.s V_3 IL_0005: initobj ""T"" IL_000b: ldloc.3 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e IL_0013: ldloc.2 IL_0014: ldobj ""T"" IL_0019: stloc.1 IL_001a: ldloca.s V_1 IL_001c: br.s IL_001f IL_001e: ldloc.2 IL_001f: stloc.0 IL_0020: ldloc.0 IL_0021: ldloca.s V_4 IL_0023: ldc.i4.4 IL_0024: ldc.i4.1 IL_0025: ldloc.0 IL_0026: ldobj ""T"" IL_002b: box ""T"" IL_0030: call ""DummyHandler..ctor(int, int, ILogger)"" IL_0035: ldloca.s V_4 IL_0037: ldstr ""log:"" IL_003c: call ""void DummyHandler.AppendLiteral(string)"" IL_0041: ldloca.s V_4 IL_0043: ldc.i4.m1 IL_0044: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0049: ldloc.s V_4 IL_004b: constrained. ""T"" IL_0051: callvirt ""void ILogger.Log(DummyHandler)"" IL_0056: ldarg.0 IL_0057: ret } "); verifier.VerifyIL("Program.<<Main>$>g__test2|0_1<T>", @" { // Code size 59 (0x3b) .maxstack 5 .locals init (T& V_0, DummyHandler V_1) IL_0000: ldarga.s V_0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldloca.s V_1 IL_0006: ldc.i4.4 IL_0007: ldc.i4.1 IL_0008: ldloc.0 IL_0009: ldobj ""T"" IL_000e: box ""T"" IL_0013: call ""DummyHandler..ctor(int, int, ILogger)"" IL_0018: ldloca.s V_1 IL_001a: ldstr ""log:"" IL_001f: call ""void DummyHandler.AppendLiteral(string)"" IL_0024: ldloca.s V_1 IL_0026: ldc.i4.s -2 IL_0028: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_002d: ldloc.1 IL_002e: constrained. ""T"" IL_0034: callvirt ""void ILogger.Log(DummyHandler)"" IL_0039: ldarg.0 IL_003a: ret } "); verifier.VerifyIL("Program.<<Main>$>g__test3|0_2<T>", @" { // Code size 87 (0x57) .maxstack 5 .locals init (T& V_0, T V_1, T& V_2, T V_3, DummyHandler V_4) IL_0000: ldarg.0 IL_0001: stloc.2 IL_0002: ldloca.s V_3 IL_0004: initobj ""T"" IL_000a: ldloc.3 IL_000b: box ""T"" IL_0010: brtrue.s IL_001d IL_0012: ldloc.2 IL_0013: ldobj ""T"" IL_0018: stloc.1 IL_0019: ldloca.s V_1 IL_001b: br.s IL_001e IL_001d: ldloc.2 IL_001e: stloc.0 IL_001f: ldloc.0 IL_0020: ldloca.s V_4 IL_0022: ldc.i4.4 IL_0023: ldc.i4.1 IL_0024: ldloc.0 IL_0025: ldobj ""T"" IL_002a: box ""T"" IL_002f: call ""DummyHandler..ctor(int, int, ILogger)"" IL_0034: ldloca.s V_4 IL_0036: ldstr ""log:"" IL_003b: call ""void DummyHandler.AppendLiteral(string)"" IL_0040: ldloca.s V_4 IL_0042: ldc.i4.s -3 IL_0044: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0049: ldloc.s V_4 IL_004b: constrained. ""T"" IL_0051: callvirt ""void ILogger.Log(DummyHandler)"" IL_0056: ret } "); verifier.VerifyIL("Program.<<Main>$>g__test4|0_3<T>", @" { // Code size 57 (0x39) .maxstack 5 .locals init (T& V_0, DummyHandler V_1) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldloca.s V_1 IL_0005: ldc.i4.4 IL_0006: ldc.i4.1 IL_0007: ldloc.0 IL_0008: ldobj ""T"" IL_000d: box ""T"" IL_0012: call ""DummyHandler..ctor(int, int, ILogger)"" IL_0017: ldloca.s V_1 IL_0019: ldstr ""log:"" IL_001e: call ""void DummyHandler.AppendLiteral(string)"" IL_0023: ldloca.s V_1 IL_0025: ldc.i4.s -4 IL_0027: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_002c: ldloc.1 IL_002d: constrained. ""T"" IL_0033: callvirt ""void ILogger.Log(DummyHandler)"" IL_0038: ret } "); } [Fact] public void StructReceiver_Lvalue_05() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var l = new StructLogger(); Console.WriteLine(""logged = {0}"", l._logged); test3(ref l); Console.WriteLine(""logged = {0}"", l._logged); test4(ref l); Console.WriteLine(""logged = {0}"", l._logged); void test3<T>(ref T l) where T : ILogger { get3(ref l)[$""log:{-3}""] += 1; } void test4<T>(ref T l) where T : struct, ILogger { get4(ref l)[$""log:{-4}""] += 1; } ref T get3<T>(ref T l) where T : ILogger { Console.WriteLine(""get3""); return ref l; } ref T get4<T>(ref T l) where T : struct, ILogger { Console.WriteLine(""get4""); return ref l; } interface ILogger { int this[[InterpolatedStringHandlerArgument("""")] DummyHandler handler] { get;set; } } internal struct StructLogger : ILogger { public int _logged; public int this[[InterpolatedStringHandlerArgument("""")] DummyHandler handler] { get { Console.WriteLine($""StructLogger get: "" + handler.GetContent()); Console.WriteLine(_logged); _logged++; return 0; } set { Console.WriteLine($""StructLogger set: "" + handler.GetContent()); Console.WriteLine(_logged); _logged++; } } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ILogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 get3 Creating DummyHandler StructLogger get: log:-3 0 StructLogger set: log:-3 1 logged = 2 get4 Creating DummyHandler StructLogger get: log:-4 2 StructLogger set: log:-4 3 logged = 4 ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__test3|0_0<T>", @" { // Code size 110 (0x6e) .maxstack 4 .locals init (T& V_0, T V_1, T& V_2, DummyHandler V_3, T V_4, DummyHandler V_5) IL_0000: ldarg.0 IL_0001: call ""ref T Program.<<Main>$>g__get3|0_2<T>(ref T)"" IL_0006: stloc.2 IL_0007: ldloca.s V_4 IL_0009: initobj ""T"" IL_000f: ldloc.s V_4 IL_0011: box ""T"" IL_0016: brtrue.s IL_0023 IL_0018: ldloc.2 IL_0019: ldobj ""T"" IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: br.s IL_0024 IL_0023: ldloc.2 IL_0024: stloc.0 IL_0025: ldloca.s V_5 IL_0027: ldc.i4.4 IL_0028: ldc.i4.1 IL_0029: ldloc.0 IL_002a: ldobj ""T"" IL_002f: box ""T"" IL_0034: call ""DummyHandler..ctor(int, int, ILogger)"" IL_0039: ldloca.s V_5 IL_003b: ldstr ""log:"" IL_0040: call ""void DummyHandler.AppendLiteral(string)"" IL_0045: ldloca.s V_5 IL_0047: ldc.i4.s -3 IL_0049: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_004e: ldloc.s V_5 IL_0050: stloc.3 IL_0051: ldloc.0 IL_0052: ldloc.3 IL_0053: ldloc.0 IL_0054: ldloc.3 IL_0055: constrained. ""T"" IL_005b: callvirt ""int ILogger.this[DummyHandler].get"" IL_0060: ldc.i4.1 IL_0061: add IL_0062: constrained. ""T"" IL_0068: callvirt ""void ILogger.this[DummyHandler].set"" IL_006d: ret } "); verifier.VerifyIL("Program.<<Main>$>g__test4|0_1<T>", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (T& V_0, DummyHandler V_1, DummyHandler V_2) IL_0000: ldarg.0 IL_0001: call ""ref T Program.<<Main>$>g__get4|0_3<T>(ref T)"" IL_0006: stloc.0 IL_0007: ldloca.s V_2 IL_0009: ldc.i4.4 IL_000a: ldc.i4.1 IL_000b: ldloc.0 IL_000c: ldobj ""T"" IL_0011: box ""T"" IL_0016: call ""DummyHandler..ctor(int, int, ILogger)"" IL_001b: ldloca.s V_2 IL_001d: ldstr ""log:"" IL_0022: call ""void DummyHandler.AppendLiteral(string)"" IL_0027: ldloca.s V_2 IL_0029: ldc.i4.s -4 IL_002b: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_0030: ldloc.2 IL_0031: stloc.1 IL_0032: ldloc.0 IL_0033: ldloc.1 IL_0034: ldloc.0 IL_0035: ldloc.1 IL_0036: constrained. ""T"" IL_003c: callvirt ""int ILogger.this[DummyHandler].get"" IL_0041: ldc.i4.1 IL_0042: add IL_0043: constrained. ""T"" IL_0049: callvirt ""void ILogger.this[DummyHandler].set"" IL_004e: ret } "); } [Fact] public void StructReceiver_Lvalue_06() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var c = new Container(); Console.WriteLine(""logged = {0}"", c.Logger._logged); test3(c); Console.WriteLine(""logged = {0}"", c.Logger._logged); void test3(Container c) { get3(c).Logger[$""log:{-3}""] += 1; } Container get3(Container c) { Console.WriteLine(""get3""); return c; } class Container { public StructLogger Logger = default; } internal struct StructLogger { public int _logged; public int this[[InterpolatedStringHandlerArgument("""")] DummyHandler handler] { get { Console.WriteLine($""StructLogger get: "" + handler.GetContent()); Console.WriteLine(_logged); _logged++; return 0; } set { Console.WriteLine($""StructLogger set: "" + handler.GetContent()); Console.WriteLine(_logged); _logged++; } } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 get3 Creating DummyHandler StructLogger get: log:-3 0 StructLogger set: log:-3 1 logged = 2 ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); } [Fact] public void StructReceiver_Lvalue_07() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var c = new Container<StructLogger>(); Console.WriteLine(""logged = {0}"", c.Logger._logged); test3(c); Console.WriteLine(""logged = {0}"", c.Logger._logged); test4(c); Console.WriteLine(""logged = {0}"", c.Logger._logged); void test3<T>(Container<T> c) where T : ILogger { get3(c).Logger[$""log:{-3}""] += 1; } void test4<T>(Container<T> c) where T : struct, ILogger { get4(c).Logger[$""log:{-4}""] += 1; } Container<T> get3<T>(Container<T> c) where T : ILogger { Console.WriteLine(""get3""); return c; } Container<T> get4<T>(Container<T> c) where T : ILogger { Console.WriteLine(""get4""); return c; } class Container<T> where T : ILogger { public T Logger = default; } interface ILogger { int this[[InterpolatedStringHandlerArgument("""")] DummyHandler handler] { get;set; } } internal struct StructLogger : ILogger { public int _logged; public int this[[InterpolatedStringHandlerArgument("""")] DummyHandler handler] { get { Console.WriteLine($""StructLogger get: "" + handler.GetContent()); Console.WriteLine(_logged); _logged++; return 0; } set { Console.WriteLine($""StructLogger set: "" + handler.GetContent()); Console.WriteLine(_logged); _logged++; } } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, ILogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 get3 Creating DummyHandler StructLogger get: log:-3 0 StructLogger set: log:-3 1 logged = 2 get4 Creating DummyHandler StructLogger get: log:-4 2 StructLogger set: log:-4 3 logged = 4 ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); } [Fact] public void StructReceiver_Lvalue_08() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var l = new StructLogger(); Console.WriteLine(""logged = {0}"", l._logged); test(ref l); Console.WriteLine(""logged = {0}"", l._logged); void test(ref readonly StructLogger l) { l.Log($""log:{0}""); } internal struct StructLogger { public int _logged; public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) { _logged++; Console.WriteLine($""StructLogger: "" + handler.GetContent()); } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 Creating DummyHandler StructLogger: log:0 logged = 0 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__test|0_0", @" { // Code size 53 (0x35) .maxstack 5 .locals init (StructLogger& V_0, StructLogger V_1, DummyHandler V_2) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldobj ""StructLogger"" IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000b: ldloca.s V_2 IL_000d: ldc.i4.4 IL_000e: ldc.i4.1 IL_000f: ldloc.0 IL_0010: ldobj ""StructLogger"" IL_0015: call ""DummyHandler..ctor(int, int, StructLogger)"" IL_001a: ldloca.s V_2 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: ldloca.s V_2 IL_0028: ldc.i4.0 IL_0029: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_002e: ldloc.2 IL_002f: call ""void StructLogger.Log(DummyHandler)"" IL_0034: ret } "); } [Fact] public void StructReceiver_Lvalue_09() { var code = @" using System; using System.Runtime.CompilerServices; using System.Text; var l = new StructLogger(); Console.WriteLine(""logged = {0}"", l._logged); test(in l); Console.WriteLine(""logged = {0}"", l._logged); void test(in StructLogger l) { l.Log($""log:{0}""); } internal struct StructLogger { public int _logged; public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) { _logged++; Console.WriteLine($""StructLogger: "" + handler.GetContent()); } } [InterpolatedStringHandler] internal ref struct DummyHandler { private readonly StringBuilder _builder; public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) { Console.WriteLine($""Creating DummyHandler""); _builder = new StringBuilder(); } public string GetContent() => _builder.ToString(); public void AppendLiteral(string s) => _builder.Append(s); public void AppendFormatted<T>(T t) => _builder.Append(t); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" logged = 0 Creating DummyHandler StructLogger: log:0 logged = 0 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__test|0_0", @" { // Code size 53 (0x35) .maxstack 5 .locals init (StructLogger& V_0, StructLogger V_1, DummyHandler V_2) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldobj ""StructLogger"" IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000b: ldloca.s V_2 IL_000d: ldc.i4.4 IL_000e: ldc.i4.1 IL_000f: ldloc.0 IL_0010: ldobj ""StructLogger"" IL_0015: call ""DummyHandler..ctor(int, int, StructLogger)"" IL_001a: ldloca.s V_2 IL_001c: ldstr ""log:"" IL_0021: call ""void DummyHandler.AppendLiteral(string)"" IL_0026: ldloca.s V_2 IL_0028: ldc.i4.0 IL_0029: call ""void DummyHandler.AppendFormatted<int>(int)"" IL_002e: ldloc.2 IL_002f: call ""void StructLogger.Log(DummyHandler)"" IL_0034: ret } "); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void StructParameter_ByVal(string expression) { var code = @" using System; using System.Runtime.CompilerServices; S s = new S { I = 1 }; S.M(s, " + expression + @"); public struct S { public int I; public static void M(S s, [InterpolatedStringHandlerArgument(""s"")]CustomHandler handler) { Console.WriteLine(""s.I:"" + s.I.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, S s) : this(literalLength, formattedCount) { s.I = 2; } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @"s.I:1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 33 (0x21) .maxstack 4 .locals init (S V_0) IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.1 IL_000b: stfld ""int S.I"" IL_0010: ldloc.0 IL_0011: stloc.0 IL_0012: ldloc.0 IL_0013: ldc.i4.0 IL_0014: ldc.i4.0 IL_0015: ldloc.0 IL_0016: newobj ""CustomHandler..ctor(int, int, S)"" IL_001b: call ""void S.M(S, CustomHandler)"" IL_0020: ret } "); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void StructParameter_ByRef(string expression) { var code = @" using System; using System.Runtime.CompilerServices; S s = new S { I = 1 }; S.M(ref s, " + expression + @"); public struct S { public int I; public static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")]CustomHandler handler) { Console.WriteLine(""s.I:"" + s.I.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref S s) : this(literalLength, formattedCount) { s.I = 2; } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @"s.I:2"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 36 (0x24) .maxstack 4 .locals init (S V_0, //s S V_1, S& V_2) IL_0000: ldloca.s V_1 IL_0002: initobj ""S"" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.1 IL_000b: stfld ""int S.I"" IL_0010: ldloc.1 IL_0011: stloc.0 IL_0012: ldloca.s V_0 IL_0014: stloc.2 IL_0015: ldloc.2 IL_0016: ldc.i4.0 IL_0017: ldc.i4.0 IL_0018: ldloc.2 IL_0019: newobj ""CustomHandler..ctor(int, int, ref S)"" IL_001e: call ""void S.M(ref S, CustomHandler)"" IL_0023: ret } "); } [Theory] [CombinatorialData] public void SideEffects(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal""", @"$"""" + $""literal""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; GetReceiver().M( GetArg(""Unrelated parameter 1""), GetArg(""Second value""), GetArg(""Unrelated parameter 2""), GetArg(""First value""), " + expression + @", GetArg(""Unrelated parameter 4"")); C GetReceiver() { Console.WriteLine(""GetReceiver""); return new C() { Prop = ""Prop"" }; } string GetArg(string s) { Console.WriteLine(s); return s; } public class C { public string Prop { get; set; } public void M(string param1, string param2, string param3, string param4, [InterpolatedStringHandlerArgument(""param4"", """", ""param2"")] CustomHandler c, string param6) => Console.WriteLine(c.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, string s1, C c, string s2" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { Console.WriteLine(""Handler constructor""); _builder.AppendLine(""s1:"" + s1); _builder.AppendLine(""c.Prop:"" + c.Prop); _builder.AppendLine(""s2:"" + s2); " + (validityParameter ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns); var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, expectedOutput: @" GetReceiver Unrelated parameter 1 Second value Unrelated parameter 2 First value Handler constructor Unrelated parameter 4 s1:First value c.Prop:Prop s2:Second value literal:literal "); verifier.VerifyDiagnostics(); } [Theory] [InlineData(@"$""literal""")] [InlineData(@"$""literal"" + $""""")] public void InterpolatedStringHandlerArgumentsAttribute_ConversionFromArgumentType(string expression) { var code = @" using System; using System.Globalization; using System.Runtime.CompilerServices; int i = 1; C.M(i, " + expression + @"); public class C { public static implicit operator C(int i) => throw null; public static implicit operator C(double d) { Console.WriteLine(d.ToString(""G"", CultureInfo.InvariantCulture)); return new C(); } public override string ToString() => ""C""; public static void M(double d, [InterpolatedStringHandlerArgument(""d"")] CustomHandler handler) => Console.WriteLine(handler.ToString()); } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" 1 literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 39 (0x27) .maxstack 5 .locals init (double V_0, CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: conv.r8 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldloca.s V_1 IL_0006: ldc.i4.7 IL_0007: ldc.i4.0 IL_0008: ldloc.0 IL_0009: call ""C C.op_Implicit(double)"" IL_000e: call ""CustomHandler..ctor(int, int, C)"" IL_0013: ldloca.s V_1 IL_0015: ldstr ""literal"" IL_001a: call ""bool CustomHandler.AppendLiteral(string)"" IL_001f: pop IL_0020: ldloc.1 IL_0021: call ""void C.M(double, CustomHandler)"" IL_0026: ret } "); static void validator(ModuleSymbol module) { var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); Assert.Equal(new[] { 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); } } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_01(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal{i}""", @"$""literal"" + $""{i}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 3; GetC()[GetInt(1), " + expression + @"] += GetInt(2); static C GetC() { Console.WriteLine(""GetC""); return new C() { Prop = 2 }; } static int GetInt(int i) { Console.WriteLine(""GetInt"" + i.ToString()); return 1; } public class C { public int Prop { get; set; } public int this[int arg1, [InterpolatedStringHandlerArgument(""arg1"", """")] CustomHandler c] { get { Console.WriteLine(""Indexer getter""); return 0; } set { Console.WriteLine(""Indexer setter""); Console.WriteLine(c.ToString()); } } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int arg1, C c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { Console.WriteLine(""Handler constructor""); _builder.AppendLine(""arg1:"" + arg1); _builder.AppendLine(""C.Prop:"" + c.Prop.ToString()); " + (validityParameter ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: useBoolReturns); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" GetC GetInt1 Handler constructor Indexer getter GetInt2 Indexer setter arg1:1 C.Prop:2 literal:literal value:3 alignment:0 format: "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", getIl()); string getIl() => (useBoolReturns, validityParameter) switch { (useBoolReturns: false, validityParameter: false) => @" { // Code size 85 (0x55) .maxstack 6 .locals init (int V_0, //i C V_1, int V_2, int V_3, CustomHandler V_4, CustomHandler V_5) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldc.i4.1 IL_0009: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000e: stloc.2 IL_000f: ldloc.2 IL_0010: stloc.3 IL_0011: ldloca.s V_5 IL_0013: ldc.i4.7 IL_0014: ldc.i4.1 IL_0015: ldloc.2 IL_0016: ldloc.1 IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" IL_001c: ldloca.s V_5 IL_001e: ldstr ""literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloca.s V_5 IL_002a: ldloc.0 IL_002b: box ""int"" IL_0030: ldc.i4.0 IL_0031: ldnull IL_0032: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0037: ldloc.s V_5 IL_0039: stloc.s V_4 IL_003b: ldloc.1 IL_003c: ldloc.3 IL_003d: ldloc.s V_4 IL_003f: ldloc.1 IL_0040: ldloc.3 IL_0041: ldloc.s V_4 IL_0043: callvirt ""int C.this[int, CustomHandler].get"" IL_0048: ldc.i4.2 IL_0049: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_004e: add IL_004f: callvirt ""void C.this[int, CustomHandler].set"" IL_0054: ret } ", (useBoolReturns: false, validityParameter: true) => @" { // Code size 94 (0x5e) .maxstack 6 .locals init (int V_0, //i CustomHandler V_1, bool V_2, C V_3, int V_4, int V_5, CustomHandler V_6) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.3 IL_0008: ldc.i4.1 IL_0009: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000e: stloc.s V_4 IL_0010: ldloc.s V_4 IL_0012: stloc.s V_5 IL_0014: ldc.i4.7 IL_0015: ldc.i4.1 IL_0016: ldloc.s V_4 IL_0018: ldloc.3 IL_0019: ldloca.s V_2 IL_001b: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" IL_0020: stloc.1 IL_0021: ldloc.2 IL_0022: brfalse.s IL_003f IL_0024: ldloca.s V_1 IL_0026: ldstr ""literal"" IL_002b: call ""void CustomHandler.AppendLiteral(string)"" IL_0030: ldloca.s V_1 IL_0032: ldloc.0 IL_0033: box ""int"" IL_0038: ldc.i4.0 IL_0039: ldnull IL_003a: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_003f: ldloc.1 IL_0040: stloc.s V_6 IL_0042: ldloc.3 IL_0043: ldloc.s V_5 IL_0045: ldloc.s V_6 IL_0047: ldloc.3 IL_0048: ldloc.s V_5 IL_004a: ldloc.s V_6 IL_004c: callvirt ""int C.this[int, CustomHandler].get"" IL_0051: ldc.i4.2 IL_0052: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_0057: add IL_0058: callvirt ""void C.this[int, CustomHandler].set"" IL_005d: ret } ", (useBoolReturns: true, validityParameter: false) => @" { // Code size 91 (0x5b) .maxstack 6 .locals init (int V_0, //i C V_1, int V_2, int V_3, CustomHandler V_4, CustomHandler V_5) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldc.i4.1 IL_0009: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000e: stloc.2 IL_000f: ldloc.2 IL_0010: stloc.3 IL_0011: ldloca.s V_5 IL_0013: ldc.i4.7 IL_0014: ldc.i4.1 IL_0015: ldloc.2 IL_0016: ldloc.1 IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" IL_001c: ldloca.s V_5 IL_001e: ldstr ""literal"" IL_0023: call ""bool CustomHandler.AppendLiteral(string)"" IL_0028: brfalse.s IL_003b IL_002a: ldloca.s V_5 IL_002c: ldloc.0 IL_002d: box ""int"" IL_0032: ldc.i4.0 IL_0033: ldnull IL_0034: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_0039: br.s IL_003c IL_003b: ldc.i4.0 IL_003c: pop IL_003d: ldloc.s V_5 IL_003f: stloc.s V_4 IL_0041: ldloc.1 IL_0042: ldloc.3 IL_0043: ldloc.s V_4 IL_0045: ldloc.1 IL_0046: ldloc.3 IL_0047: ldloc.s V_4 IL_0049: callvirt ""int C.this[int, CustomHandler].get"" IL_004e: ldc.i4.2 IL_004f: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_0054: add IL_0055: callvirt ""void C.this[int, CustomHandler].set"" IL_005a: ret } ", (useBoolReturns: true, validityParameter: true) => @" { // Code size 97 (0x61) .maxstack 6 .locals init (int V_0, //i C V_1, int V_2, int V_3, CustomHandler V_4, CustomHandler V_5, bool V_6) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldc.i4.1 IL_0009: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000e: stloc.2 IL_000f: ldloc.2 IL_0010: stloc.3 IL_0011: ldc.i4.7 IL_0012: ldc.i4.1 IL_0013: ldloc.2 IL_0014: ldloc.1 IL_0015: ldloca.s V_6 IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" IL_001c: stloc.s V_5 IL_001e: ldloc.s V_6 IL_0020: brfalse.s IL_0041 IL_0022: ldloca.s V_5 IL_0024: ldstr ""literal"" IL_0029: call ""bool CustomHandler.AppendLiteral(string)"" IL_002e: brfalse.s IL_0041 IL_0030: ldloca.s V_5 IL_0032: ldloc.0 IL_0033: box ""int"" IL_0038: ldc.i4.0 IL_0039: ldnull IL_003a: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_003f: br.s IL_0042 IL_0041: ldc.i4.0 IL_0042: pop IL_0043: ldloc.s V_5 IL_0045: stloc.s V_4 IL_0047: ldloc.1 IL_0048: ldloc.3 IL_0049: ldloc.s V_4 IL_004b: ldloc.1 IL_004c: ldloc.3 IL_004d: ldloc.s V_4 IL_004f: callvirt ""int C.this[int, CustomHandler].get"" IL_0054: ldc.i4.2 IL_0055: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_005a: add IL_005b: callvirt ""void C.this[int, CustomHandler].set"" IL_0060: ret } ", }; } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_02(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal{i}""", @"$""literal"" + $""{i}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 3; GetC()[GetInt(1), " + expression + @"] += GetInt(2); static C GetC() { Console.WriteLine(""GetC""); return new C() { Prop = 2 }; } static int GetInt(int i) { Console.WriteLine(""GetInt"" + i.ToString()); return 1; } public class C { private int field; public int Prop { get; set; } public ref int this[int arg1, [InterpolatedStringHandlerArgument(""arg1"", """")] CustomHandler c] { get { Console.WriteLine(""Indexer getter""); Console.WriteLine(c.ToString()); return ref field; } } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int arg1, C c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { Console.WriteLine(""Handler constructor""); _builder.AppendLine(""arg1:"" + arg1); _builder.AppendLine(""C.Prop:"" + c.Prop.ToString()); " + (validityParameter ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: useBoolReturns); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" GetC GetInt1 Handler constructor Indexer getter arg1:1 C.Prop:2 literal:literal value:3 alignment:0 format: GetInt2 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", getIl()); string getIl() => (useBoolReturns, validityParameter) switch { (useBoolReturns: false, validityParameter: false) => @" { // Code size 72 (0x48) .maxstack 7 .locals init (int V_0, //i C V_1, int V_2, CustomHandler V_3) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldloca.s V_3 IL_0013: ldc.i4.7 IL_0014: ldc.i4.1 IL_0015: ldloc.2 IL_0016: ldloc.1 IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" IL_001c: ldloca.s V_3 IL_001e: ldstr ""literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloca.s V_3 IL_002a: ldloc.0 IL_002b: box ""int"" IL_0030: ldc.i4.0 IL_0031: ldnull IL_0032: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0037: ldloc.3 IL_0038: callvirt ""ref int C.this[int, CustomHandler].get"" IL_003d: dup IL_003e: ldind.i4 IL_003f: ldc.i4.2 IL_0040: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_0045: add IL_0046: stind.i4 IL_0047: ret } ", (useBoolReturns: false, validityParameter: true) => @" { // Code size 81 (0x51) .maxstack 6 .locals init (int V_0, //i C V_1, int V_2, int V_3, CustomHandler V_4, bool V_5) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: stloc.3 IL_0012: ldc.i4.7 IL_0013: ldc.i4.1 IL_0014: ldloc.2 IL_0015: ldloc.1 IL_0016: ldloca.s V_5 IL_0018: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" IL_001d: stloc.s V_4 IL_001f: ldloc.s V_5 IL_0021: brfalse.s IL_003e IL_0023: ldloca.s V_4 IL_0025: ldstr ""literal"" IL_002a: call ""void CustomHandler.AppendLiteral(string)"" IL_002f: ldloca.s V_4 IL_0031: ldloc.0 IL_0032: box ""int"" IL_0037: ldc.i4.0 IL_0038: ldnull IL_0039: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_003e: ldloc.3 IL_003f: ldloc.s V_4 IL_0041: callvirt ""ref int C.this[int, CustomHandler].get"" IL_0046: dup IL_0047: ldind.i4 IL_0048: ldc.i4.2 IL_0049: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_004e: add IL_004f: stind.i4 IL_0050: ret } ", (useBoolReturns: true, validityParameter: false) => @" { // Code size 78 (0x4e) .maxstack 7 .locals init (int V_0, //i C V_1, int V_2, CustomHandler V_3) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldloca.s V_3 IL_0013: ldc.i4.7 IL_0014: ldc.i4.1 IL_0015: ldloc.2 IL_0016: ldloc.1 IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" IL_001c: ldloca.s V_3 IL_001e: ldstr ""literal"" IL_0023: call ""bool CustomHandler.AppendLiteral(string)"" IL_0028: brfalse.s IL_003b IL_002a: ldloca.s V_3 IL_002c: ldloc.0 IL_002d: box ""int"" IL_0032: ldc.i4.0 IL_0033: ldnull IL_0034: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_0039: br.s IL_003c IL_003b: ldc.i4.0 IL_003c: pop IL_003d: ldloc.3 IL_003e: callvirt ""ref int C.this[int, CustomHandler].get"" IL_0043: dup IL_0044: ldind.i4 IL_0045: ldc.i4.2 IL_0046: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_004b: add IL_004c: stind.i4 IL_004d: ret } ", (useBoolReturns: true, validityParameter: true) => @" { // Code size 83 (0x53) .maxstack 7 .locals init (int V_0, //i C V_1, int V_2, CustomHandler V_3, bool V_4) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldc.i4.7 IL_0012: ldc.i4.1 IL_0013: ldloc.2 IL_0014: ldloc.1 IL_0015: ldloca.s V_4 IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" IL_001c: stloc.3 IL_001d: ldloc.s V_4 IL_001f: brfalse.s IL_0040 IL_0021: ldloca.s V_3 IL_0023: ldstr ""literal"" IL_0028: call ""bool CustomHandler.AppendLiteral(string)"" IL_002d: brfalse.s IL_0040 IL_002f: ldloca.s V_3 IL_0031: ldloc.0 IL_0032: box ""int"" IL_0037: ldc.i4.0 IL_0038: ldnull IL_0039: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_003e: br.s IL_0041 IL_0040: ldc.i4.0 IL_0041: pop IL_0042: ldloc.3 IL_0043: callvirt ""ref int C.this[int, CustomHandler].get"" IL_0048: dup IL_0049: ldind.i4 IL_004a: ldc.i4.2 IL_004b: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_0050: add IL_0051: stind.i4 IL_0052: ret } ", }; } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_RefReturningMethod(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal{i}""", @"$""literal"" + $""{i}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 3; GetC().M(GetInt(1), " + expression + @") += GetInt(2); static C GetC() { Console.WriteLine(""GetC""); return new C() { Prop = 2 }; } static int GetInt(int i) { Console.WriteLine(""GetInt"" + i.ToString()); return 1; } public class C { private int field; public int Prop { get; set; } public ref int M(int arg1, [InterpolatedStringHandlerArgument(""arg1"", """")] CustomHandler c) { Console.WriteLine(""M""); Console.WriteLine(c.ToString()); return ref field; } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int arg1, C c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { Console.WriteLine(""Handler constructor""); _builder.AppendLine(""arg1:"" + arg1); _builder.AppendLine(""C.Prop:"" + c.Prop.ToString()); " + (validityParameter ? "success = true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: useBoolReturns); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" GetC GetInt1 Handler constructor M arg1:1 C.Prop:2 literal:literal value:3 alignment:0 format: GetInt2 "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", getIl()); string getIl() => (useBoolReturns, validityParameter) switch { (useBoolReturns: false, validityParameter: false) => @" { // Code size 72 (0x48) .maxstack 7 .locals init (int V_0, //i C V_1, int V_2, CustomHandler V_3) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldloca.s V_3 IL_0013: ldc.i4.7 IL_0014: ldc.i4.1 IL_0015: ldloc.2 IL_0016: ldloc.1 IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" IL_001c: ldloca.s V_3 IL_001e: ldstr ""literal"" IL_0023: call ""void CustomHandler.AppendLiteral(string)"" IL_0028: ldloca.s V_3 IL_002a: ldloc.0 IL_002b: box ""int"" IL_0030: ldc.i4.0 IL_0031: ldnull IL_0032: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0037: ldloc.3 IL_0038: callvirt ""ref int C.M(int, CustomHandler)"" IL_003d: dup IL_003e: ldind.i4 IL_003f: ldc.i4.2 IL_0040: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_0045: add IL_0046: stind.i4 IL_0047: ret } ", (useBoolReturns: false, validityParameter: true) => @" { // Code size 81 (0x51) .maxstack 6 .locals init (int V_0, //i C V_1, int V_2, int V_3, CustomHandler V_4, bool V_5) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: stloc.3 IL_0012: ldc.i4.7 IL_0013: ldc.i4.1 IL_0014: ldloc.2 IL_0015: ldloc.1 IL_0016: ldloca.s V_5 IL_0018: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" IL_001d: stloc.s V_4 IL_001f: ldloc.s V_5 IL_0021: brfalse.s IL_003e IL_0023: ldloca.s V_4 IL_0025: ldstr ""literal"" IL_002a: call ""void CustomHandler.AppendLiteral(string)"" IL_002f: ldloca.s V_4 IL_0031: ldloc.0 IL_0032: box ""int"" IL_0037: ldc.i4.0 IL_0038: ldnull IL_0039: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_003e: ldloc.3 IL_003f: ldloc.s V_4 IL_0041: callvirt ""ref int C.M(int, CustomHandler)"" IL_0046: dup IL_0047: ldind.i4 IL_0048: ldc.i4.2 IL_0049: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_004e: add IL_004f: stind.i4 IL_0050: ret } ", (useBoolReturns: true, validityParameter: false) => @" { // Code size 78 (0x4e) .maxstack 7 .locals init (int V_0, //i C V_1, int V_2, CustomHandler V_3) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldloca.s V_3 IL_0013: ldc.i4.7 IL_0014: ldc.i4.1 IL_0015: ldloc.2 IL_0016: ldloc.1 IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" IL_001c: ldloca.s V_3 IL_001e: ldstr ""literal"" IL_0023: call ""bool CustomHandler.AppendLiteral(string)"" IL_0028: brfalse.s IL_003b IL_002a: ldloca.s V_3 IL_002c: ldloc.0 IL_002d: box ""int"" IL_0032: ldc.i4.0 IL_0033: ldnull IL_0034: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_0039: br.s IL_003c IL_003b: ldc.i4.0 IL_003c: pop IL_003d: ldloc.3 IL_003e: callvirt ""ref int C.M(int, CustomHandler)"" IL_0043: dup IL_0044: ldind.i4 IL_0045: ldc.i4.2 IL_0046: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_004b: add IL_004c: stind.i4 IL_004d: ret } ", (useBoolReturns: true, validityParameter: true) => @" { // Code size 83 (0x53) .maxstack 7 .locals init (int V_0, //i C V_1, int V_2, CustomHandler V_3, bool V_4) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: call ""C Program.<<Main>$>g__GetC|0_0()"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldc.i4.7 IL_0012: ldc.i4.1 IL_0013: ldloc.2 IL_0014: ldloc.1 IL_0015: ldloca.s V_4 IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" IL_001c: stloc.3 IL_001d: ldloc.s V_4 IL_001f: brfalse.s IL_0040 IL_0021: ldloca.s V_3 IL_0023: ldstr ""literal"" IL_0028: call ""bool CustomHandler.AppendLiteral(string)"" IL_002d: brfalse.s IL_0040 IL_002f: ldloca.s V_3 IL_0031: ldloc.0 IL_0032: box ""int"" IL_0037: ldc.i4.0 IL_0038: ldnull IL_0039: call ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_003e: br.s IL_0041 IL_0040: ldc.i4.0 IL_0041: pop IL_0042: ldloc.3 IL_0043: callvirt ""ref int C.M(int, CustomHandler)"" IL_0048: dup IL_0049: ldind.i4 IL_004a: ldc.i4.2 IL_004b: call ""int Program.<<Main>$>g__GetInt|0_1(int)"" IL_0050: add IL_0051: stind.i4 IL_0052: ret } ", }; } [Theory] [InlineData(@"$""literal""")] [InlineData(@"$"""" + $""literal""")] public void InterpolatedStringHandlerArgumentsAttribute_CollectionInitializerAdd(string expression) { var code = @" using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; _ = new C(1) { " + expression + @" }; public class C : IEnumerable<int> { public int Field; public C(int i) { Field = i; } public void Add([InterpolatedStringHandlerArgument("""")] CustomHandler c) { Console.WriteLine(c.ToString()); } public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { _builder.AppendLine(""c.Field:"" + c.Field.ToString()); } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" c.Field:1 literal:literal "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 37 (0x25) .maxstack 5 .locals init (C V_0, CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: newobj ""C..ctor(int)"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldloca.s V_1 IL_000a: ldc.i4.7 IL_000b: ldc.i4.0 IL_000c: ldloc.0 IL_000d: call ""CustomHandler..ctor(int, int, C)"" IL_0012: ldloca.s V_1 IL_0014: ldstr ""literal"" IL_0019: call ""void CustomHandler.AppendLiteral(string)"" IL_001e: ldloc.1 IL_001f: callvirt ""void C.Add(CustomHandler)"" IL_0024: ret } "); } [Theory] [InlineData(@"$""literal""")] [InlineData(@"$"""" + $""literal""")] public void InterpolatedStringHandlerArgumentsAttribute_DictionaryInitializer(string expression) { var code = @" using System; using System.Runtime.CompilerServices; _ = new C(1) { [" + expression + @"] = 1 }; public class C { public int Field; public C(int i) { Field = i; } public int this[[InterpolatedStringHandlerArgument("""")] CustomHandler c] { set => Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { _builder.AppendLine(""c.Field:"" + c.Field.ToString()); } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); comp.VerifyDiagnostics( // (5,17): error CS8976: Interpolated string handler conversions that reference the instance being indexed cannot be used in indexer member initializers. // _ = new C(1) { [$"literal"] = 1 }; Diagnostic(ErrorCode.ERR_InterpolatedStringsReferencingInstanceCannotBeInObjectInitializers, expression).WithLocation(5, 17) ); } [Theory] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_AttributeOnAppendFormatCall(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""{$""Inner string""}{2}""", @"$""{$""Inner string""}"" + $""{2}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C.M(1, " + expression + @"); class C { public static void M(int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler handler) { Console.WriteLine(handler.ToString()); } } public partial class CustomHandler { private int I = 0; public CustomHandler(int literalLength, int formattedCount, int i" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { Console.WriteLine(""int constructor""); I = i; " + (validityParameter ? "success = true;" : "") + @" } public CustomHandler(int literalLength, int formattedCount, CustomHandler c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { Console.WriteLine(""CustomHandler constructor""); _builder.AppendLine(""c.I:"" + c.I.ToString()); " + (validityParameter ? "success = true;" : "") + @" } public " + (useBoolReturns ? "bool" : "void") + @" AppendFormatted([InterpolatedStringHandlerArgument("""")]CustomHandler c) { _builder.AppendLine(""CustomHandler AppendFormatted""); _builder.Append(c.ToString()); " + (useBoolReturns ? "return true;" : "") + @" } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: useBoolReturns); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @" int constructor CustomHandler constructor CustomHandler AppendFormatted c.I:1 literal:Inner string value:2 alignment:0 format: "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", getIl()); string getIl() => (useBoolReturns, validityParameter) switch { (useBoolReturns: false, validityParameter: false) => @" { // Code size 59 (0x3b) .maxstack 6 .locals init (int V_0, CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.2 IL_0005: ldloc.0 IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" IL_000b: dup IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: ldc.i4.s 12 IL_0010: ldc.i4.0 IL_0011: ldloc.1 IL_0012: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" IL_0017: dup IL_0018: ldstr ""Inner string"" IL_001d: callvirt ""void CustomHandler.AppendLiteral(string)"" IL_0022: callvirt ""void CustomHandler.AppendFormatted(CustomHandler)"" IL_0027: dup IL_0028: ldc.i4.2 IL_0029: box ""int"" IL_002e: ldc.i4.0 IL_002f: ldnull IL_0030: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0035: call ""void C.M(int, CustomHandler)"" IL_003a: ret } ", (useBoolReturns: false, validityParameter: true) => @" { // Code size 77 (0x4d) .maxstack 6 .locals init (int V_0, CustomHandler V_1, bool V_2, CustomHandler V_3, CustomHandler V_4, bool V_5) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.2 IL_0005: ldloc.0 IL_0006: ldloca.s V_2 IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" IL_000d: stloc.1 IL_000e: ldloc.2 IL_000f: brfalse.s IL_0046 IL_0011: ldloc.1 IL_0012: stloc.3 IL_0013: ldloc.3 IL_0014: ldc.i4.s 12 IL_0016: ldc.i4.0 IL_0017: ldloc.3 IL_0018: ldloca.s V_5 IL_001a: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" IL_001f: stloc.s V_4 IL_0021: ldloc.s V_5 IL_0023: brfalse.s IL_0031 IL_0025: ldloc.s V_4 IL_0027: ldstr ""Inner string"" IL_002c: callvirt ""void CustomHandler.AppendLiteral(string)"" IL_0031: ldloc.s V_4 IL_0033: callvirt ""void CustomHandler.AppendFormatted(CustomHandler)"" IL_0038: ldloc.1 IL_0039: ldc.i4.2 IL_003a: box ""int"" IL_003f: ldc.i4.0 IL_0040: ldnull IL_0041: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0046: ldloc.1 IL_0047: call ""void C.M(int, CustomHandler)"" IL_004c: ret } ", (useBoolReturns: true, validityParameter: false) => @" { // Code size 68 (0x44) .maxstack 5 .locals init (int V_0, CustomHandler V_1, CustomHandler V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.2 IL_0005: ldloc.0 IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: stloc.2 IL_000e: ldloc.2 IL_000f: ldc.i4.s 12 IL_0011: ldc.i4.0 IL_0012: ldloc.2 IL_0013: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" IL_0018: dup IL_0019: ldstr ""Inner string"" IL_001e: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0023: pop IL_0024: callvirt ""bool CustomHandler.AppendFormatted(CustomHandler)"" IL_0029: brfalse.s IL_003b IL_002b: ldloc.1 IL_002c: ldc.i4.2 IL_002d: box ""int"" IL_0032: ldc.i4.0 IL_0033: ldnull IL_0034: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_0039: br.s IL_003c IL_003b: ldc.i4.0 IL_003c: pop IL_003d: ldloc.1 IL_003e: call ""void C.M(int, CustomHandler)"" IL_0043: ret } ", (useBoolReturns: true, validityParameter: true) => @" { // Code size 87 (0x57) .maxstack 6 .locals init (int V_0, CustomHandler V_1, bool V_2, CustomHandler V_3, CustomHandler V_4, bool V_5) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.2 IL_0005: ldloc.0 IL_0006: ldloca.s V_2 IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" IL_000d: stloc.1 IL_000e: ldloc.2 IL_000f: brfalse.s IL_004e IL_0011: ldloc.1 IL_0012: stloc.3 IL_0013: ldloc.3 IL_0014: ldc.i4.s 12 IL_0016: ldc.i4.0 IL_0017: ldloc.3 IL_0018: ldloca.s V_5 IL_001a: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" IL_001f: stloc.s V_4 IL_0021: ldloc.s V_5 IL_0023: brfalse.s IL_0033 IL_0025: ldloc.s V_4 IL_0027: ldstr ""Inner string"" IL_002c: callvirt ""bool CustomHandler.AppendLiteral(string)"" IL_0031: br.s IL_0034 IL_0033: ldc.i4.0 IL_0034: pop IL_0035: ldloc.s V_4 IL_0037: callvirt ""bool CustomHandler.AppendFormatted(CustomHandler)"" IL_003c: brfalse.s IL_004e IL_003e: ldloc.1 IL_003f: ldc.i4.2 IL_0040: box ""int"" IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" IL_004c: br.s IL_004f IL_004e: ldc.i4.0 IL_004f: pop IL_0050: ldloc.1 IL_0051: call ""void C.M(int, CustomHandler)"" IL_0056: ret } ", }; } [Theory] [InlineData(@"$""literal""")] [InlineData(@"$"""" + $""literal""")] public void DiscardsUsedAsParameters(string expression) { var code = @" using System; using System.Runtime.CompilerServices; C.M(out _, " + expression + @"); public class C { public static void M(out int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler c) { i = 0; Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, out int i) : this(literalLength, formattedCount) { i = 1; } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: @"literal:literal"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 31 (0x1f) .maxstack 4 .locals init (int V_0, CustomHandler V_1) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.0 IL_0004: ldloca.s V_0 IL_0006: newobj ""CustomHandler..ctor(int, int, out int)"" IL_000b: stloc.1 IL_000c: ldloca.s V_1 IL_000e: ldstr ""literal"" IL_0013: call ""void CustomHandler.AppendLiteral(string)"" IL_0018: ldloc.1 IL_0019: call ""void C.M(out int, CustomHandler)"" IL_001e: ret } "); } [Fact] public void DiscardsUsedAsParameters_DefinedInVB() { var vb = @" Imports System Imports System.Runtime.CompilerServices Imports System.Runtime.InteropServices Public Class C Public Shared Sub M(<Out> ByRef i As Integer, <InterpolatedStringHandlerArgument(""i"")>c As CustomHandler) Console.WriteLine(i) End Sub End Class <InterpolatedStringHandler> Public Structure CustomHandler Public Sub New(literalLength As Integer, formattedCount As Integer, <Out> ByRef i As Integer) i = 1 End Sub End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vb, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var code = @"C.M(out _, $"""");"; var comp = CreateCompilation(code, new[] { vbComp.EmitToImageReference() }); var verifier = CompileAndVerify(comp, expectedOutput: @"1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 17 (0x11) .maxstack 4 .locals init (int V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.0 IL_0003: ldc.i4.0 IL_0004: ldloca.s V_0 IL_0006: newobj ""CustomHandler..ctor(int, int, out int)"" IL_000b: call ""void C.M(out int, CustomHandler)"" IL_0010: ret } "); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void DisallowedInExpressionTrees(string expression) { var code = @" using System; using System.Linq.Expressions; Expression<Func<CustomHandler>> expr = () => " + expression + @"; "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, handler }); comp.VerifyDiagnostics( // (5,46): error CS8952: An expression tree may not contain an interpolated string handler conversion. // Expression<Func<CustomHandler>> expr = () => $""; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsInterpolatedStringHandlerConversion, expression).WithLocation(5, 46) ); } [Fact, WorkItem(55114, "https://github.com/dotnet/roslyn/issues/55114")] public void AsStringInExpressionTrees_01() { var code = @" using System; using System.Linq.Expressions; Expression<Func<string, string>> e = o => $""{o.Length}"";"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 127 (0x7f) .maxstack 7 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""o"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""string string.Format(string, object)"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldloc.0 IL_0045: ldtoken ""int string.Length.get"" IL_004a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_004f: castclass ""System.Reflection.MethodInfo"" IL_0054: call ""System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" IL_0059: ldtoken ""object"" IL_005e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0063: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_0068: stelem.ref IL_0069: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_006e: ldc.i4.1 IL_006f: newarr ""System.Linq.Expressions.ParameterExpression"" IL_0074: dup IL_0075: ldc.i4.0 IL_0076: ldloc.0 IL_0077: stelem.ref IL_0078: call ""System.Linq.Expressions.Expression<System.Func<string, string>> System.Linq.Expressions.Expression.Lambda<System.Func<string, string>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_007d: pop IL_007e: ret } "); } [Fact, WorkItem(55114, "https://github.com/dotnet/roslyn/issues/55114")] public void AsStringInExpressionTrees_02() { var code = @" using System.Linq.Expressions; Expression e = (string o) => $""{o.Length}"";"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 127 (0x7f) .maxstack 7 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""o"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""string string.Format(string, object)"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldloc.0 IL_0045: ldtoken ""int string.Length.get"" IL_004a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_004f: castclass ""System.Reflection.MethodInfo"" IL_0054: call ""System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" IL_0059: ldtoken ""object"" IL_005e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0063: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_0068: stelem.ref IL_0069: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_006e: ldc.i4.1 IL_006f: newarr ""System.Linq.Expressions.ParameterExpression"" IL_0074: dup IL_0075: ldc.i4.0 IL_0076: ldloc.0 IL_0077: stelem.ref IL_0078: call ""System.Linq.Expressions.Expression<System.Func<string, string>> System.Linq.Expressions.Expression.Lambda<System.Func<string, string>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_007d: pop IL_007e: ret } "); } [Fact, WorkItem(55114, "https://github.com/dotnet/roslyn/issues/55114")] public void AsStringInExpressionTrees_03() { var code = @" using System; using System.Linq.Expressions; Expression<Func<Func<string, string>>> e = () => o => $""{o.Length}"";"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 137 (0x89) .maxstack 7 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""o"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""string string.Format(string, object)"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldloc.0 IL_0045: ldtoken ""int string.Length.get"" IL_004a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_004f: castclass ""System.Reflection.MethodInfo"" IL_0054: call ""System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" IL_0059: ldtoken ""object"" IL_005e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0063: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_0068: stelem.ref IL_0069: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_006e: ldc.i4.1 IL_006f: newarr ""System.Linq.Expressions.ParameterExpression"" IL_0074: dup IL_0075: ldc.i4.0 IL_0076: ldloc.0 IL_0077: stelem.ref IL_0078: call ""System.Linq.Expressions.Expression<System.Func<string, string>> System.Linq.Expressions.Expression.Lambda<System.Func<string, string>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_007d: call ""System.Linq.Expressions.ParameterExpression[] System.Array.Empty<System.Linq.Expressions.ParameterExpression>()"" IL_0082: call ""System.Linq.Expressions.Expression<System.Func<System.Func<string, string>>> System.Linq.Expressions.Expression.Lambda<System.Func<System.Func<string, string>>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_0087: pop IL_0088: ret } "); } [Fact, WorkItem(55114, "https://github.com/dotnet/roslyn/issues/55114")] public void AsStringInExpressionTrees_04() { var code = @" using System; using System.Linq.Expressions; Expression e = Func<string, string> () => (string o) => $""{o.Length}"";"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 137 (0x89) .maxstack 7 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""o"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""string string.Format(string, object)"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldloc.0 IL_0045: ldtoken ""int string.Length.get"" IL_004a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_004f: castclass ""System.Reflection.MethodInfo"" IL_0054: call ""System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" IL_0059: ldtoken ""object"" IL_005e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0063: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_0068: stelem.ref IL_0069: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_006e: ldc.i4.1 IL_006f: newarr ""System.Linq.Expressions.ParameterExpression"" IL_0074: dup IL_0075: ldc.i4.0 IL_0076: ldloc.0 IL_0077: stelem.ref IL_0078: call ""System.Linq.Expressions.Expression<System.Func<string, string>> System.Linq.Expressions.Expression.Lambda<System.Func<string, string>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_007d: call ""System.Linq.Expressions.ParameterExpression[] System.Array.Empty<System.Linq.Expressions.ParameterExpression>()"" IL_0082: call ""System.Linq.Expressions.Expression<System.Func<System.Func<string, string>>> System.Linq.Expressions.Expression.Lambda<System.Func<System.Func<string, string>>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_0087: pop IL_0088: ret } "); } [Fact, WorkItem(55114, "https://github.com/dotnet/roslyn/issues/55114")] public void AsStringInExpressionTrees_05() { var code = @" using System; using System.Linq.Expressions; Expression<Func<string, string>> e = o => $""{o.Length}"" + $""literal"";"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 167 (0xa7) .maxstack 7 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""o"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""string string.Format(string, object)"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldloc.0 IL_0045: ldtoken ""int string.Length.get"" IL_004a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_004f: castclass ""System.Reflection.MethodInfo"" IL_0054: call ""System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" IL_0059: ldtoken ""object"" IL_005e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0063: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_0068: stelem.ref IL_0069: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_006e: ldstr ""literal"" IL_0073: ldtoken ""string"" IL_0078: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_007d: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0082: ldtoken ""string string.Concat(string, string)"" IL_0087: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_008c: castclass ""System.Reflection.MethodInfo"" IL_0091: call ""System.Linq.Expressions.BinaryExpression System.Linq.Expressions.Expression.Add(System.Linq.Expressions.Expression, System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" IL_0096: ldc.i4.1 IL_0097: newarr ""System.Linq.Expressions.ParameterExpression"" IL_009c: dup IL_009d: ldc.i4.0 IL_009e: ldloc.0 IL_009f: stelem.ref IL_00a0: call ""System.Linq.Expressions.Expression<System.Func<string, string>> System.Linq.Expressions.Expression.Lambda<System.Func<string, string>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_00a5: pop IL_00a6: ret } "); } [Theory] [CombinatorialData] public void CustomHandlerUsedAsArgumentToCustomHandler(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C.M(1, " + expression + @", " + expression + @"); public class C { public static void M(int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c1, [InterpolatedStringHandlerArgument(""c1"")] CustomHandler c2) => Console.WriteLine(c2.ToString()); } public partial class CustomHandler { private int i; public CustomHandler(int literalLength, int formattedCount, int i" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { _builder.AppendLine(""i:"" + i.ToString()); this.i = i; " + (validityParameter ? "success = true;" : "") + @" } public CustomHandler(int literalLength, int formattedCount, CustomHandler c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) { _builder.AppendLine(""c.i:"" + c.i.ToString()); " + (validityParameter ? "success = true;" : "") + @" } }"; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: "c.i:1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", getIl()); string getIl() => (useBoolReturns, validityParameter) switch { (useBoolReturns: false, validityParameter: false) => @" { // Code size 27 (0x1b) .maxstack 5 .locals init (int V_0, CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: ldloc.1 IL_0010: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" IL_0015: call ""void C.M(int, CustomHandler, CustomHandler)"" IL_001a: ret } ", (useBoolReturns: false, validityParameter: true) => @" { // Code size 31 (0x1f) .maxstack 6 .locals init (int V_0, CustomHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: ldloca.s V_2 IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: ldc.i4.0 IL_0010: ldc.i4.0 IL_0011: ldloc.1 IL_0012: ldloca.s V_2 IL_0014: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" IL_0019: call ""void C.M(int, CustomHandler, CustomHandler)"" IL_001e: ret } ", (useBoolReturns: true, validityParameter: false) => @" { // Code size 27 (0x1b) .maxstack 5 .locals init (int V_0, CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: ldloc.1 IL_0010: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" IL_0015: call ""void C.M(int, CustomHandler, CustomHandler)"" IL_001a: ret } ", (useBoolReturns: true, validityParameter: true) => @" { // Code size 31 (0x1f) .maxstack 6 .locals init (int V_0, CustomHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: ldloca.s V_2 IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: ldc.i4.0 IL_0010: ldc.i4.0 IL_0011: ldloc.1 IL_0012: ldloca.s V_2 IL_0014: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" IL_0019: call ""void C.M(int, CustomHandler, CustomHandler)"" IL_001e: ret } ", }; } [Fact, WorkItem(1370647, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1370647")] public void AsFormattableString() { var code = @" M($""{1}"" + $""literal""); System.FormattableString s = $""{1}"" + $""literal""; void M(System.FormattableString s) { } "; var comp = CreateCompilation(code); comp.VerifyDiagnostics( // (2,3): error CS1503: Argument 1: cannot convert from 'string' to 'System.FormattableString' // M($"{1}" + $"literal"); Diagnostic(ErrorCode.ERR_BadArgType, @"$""{1}"" + $""literal""").WithArguments("1", "string", "System.FormattableString").WithLocation(2, 3), // (3,30): error CS0029: Cannot implicitly convert type 'string' to 'System.FormattableString' // System.FormattableString s = $"{1}" + $"literal"; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""{1}"" + $""literal""").WithArguments("string", "System.FormattableString").WithLocation(3, 30) ); } [Fact, WorkItem(1370647, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1370647")] public void AsIFormattable() { var code = @" M($""{1}"" + $""literal""); System.IFormattable s = $""{1}"" + $""literal""; void M(System.IFormattable s) { } "; var comp = CreateCompilation(code); comp.VerifyDiagnostics( // (2,3): error CS1503: Argument 1: cannot convert from 'string' to 'System.IFormattable' // M($"{1}" + $"literal"); Diagnostic(ErrorCode.ERR_BadArgType, @"$""{1}"" + $""literal""").WithArguments("1", "string", "System.IFormattable").WithLocation(2, 3), // (3,25): error CS0029: Cannot implicitly convert type 'string' to 'System.IFormattable' // System.IFormattable s = $"{1}" + $"literal"; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""{1}"" + $""literal""").WithArguments("string", "System.IFormattable").WithLocation(3, 25) ); } [Theory] [CombinatorialData] public void DefiniteAssignment_01(bool useBoolReturns, bool trailingOutParameter, [CombinatorialValues(@"$""{i = 1}{M(out var o)}{s = o.ToString()}""", @"$""{i = 1}"" + $""{M(out var o)}"" + $""{s = o.ToString()}""")] string expression) { var code = @" int i; string s; CustomHandler c = " + expression + @"; _ = i.ToString(); _ = o.ToString(); _ = s.ToString(); string M(out object o) { o = null; return null; } "; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns, includeTrailingOutConstructorParameter: trailingOutParameter); var comp = CreateCompilation(new[] { code, customHandler }); if (trailingOutParameter) { comp.VerifyDiagnostics( // (6,5): error CS0165: Use of unassigned local variable 'i' // _ = i.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "i").WithArguments("i").WithLocation(6, 5), // (7,5): error CS0165: Use of unassigned local variable 'o' // _ = o.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "o").WithArguments("o").WithLocation(7, 5), // (8,5): error CS0165: Use of unassigned local variable 's' // _ = s.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(8, 5) ); } else if (useBoolReturns) { comp.VerifyDiagnostics( // (7,5): error CS0165: Use of unassigned local variable 'o' // _ = o.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "o").WithArguments("o").WithLocation(7, 5), // (8,5): error CS0165: Use of unassigned local variable 's' // _ = s.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(8, 5) ); } else { comp.VerifyDiagnostics(); } } [Theory] [CombinatorialData] public void DefiniteAssignment_02(bool useBoolReturns, bool trailingOutParameter, [CombinatorialValues(@"$""{i = 1}""", @"$"""" + $""{i = 1}""", @"$""{i = 1}"" + $""""")] string expression) { var code = @" int i; CustomHandler c = " + expression + @"; _ = i.ToString(); "; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns, includeTrailingOutConstructorParameter: trailingOutParameter); var comp = CreateCompilation(new[] { code, customHandler }); if (trailingOutParameter) { comp.VerifyDiagnostics( // (5,5): error CS0165: Use of unassigned local variable 'i' // _ = i.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "i").WithArguments("i").WithLocation(5, 5) ); } else { comp.VerifyDiagnostics(); } } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void DynamicConstruction_01(string expression) { var code = @" using System.Runtime.CompilerServices; dynamic d = 1; M(d, " + expression + @"); void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int d) : this() {} public CustomHandler(int literalLength, int formattedCount, long d) : this() {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute }); comp.VerifyDiagnostics( // (4,6): error CS8953: An interpolated string handler construction cannot use dynamic. Manually construct an instance of 'CustomHandler'. // M(d, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, expression).WithArguments("CustomHandler").WithLocation(4, 6) ); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void DynamicConstruction_02(string expression) { var code = @" using System.Runtime.CompilerServices; int i = 1; M(i, " + expression + @"); void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int d) : this() {} public CustomHandler(int literalLength, int formattedCount, long d) : this() {} } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute }); comp.VerifyDiagnostics( // (4,6): error CS8953: An interpolated string handler construction cannot use dynamic. Manually construct an instance of 'CustomHandler'. // M(d, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, expression).WithArguments("CustomHandler").WithLocation(4, 6) ); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void DynamicConstruction_03(string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 1; M(i, " + expression + @"); void M(int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler c) {} public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, dynamic d) : this(literalLength, formattedCount) { Console.WriteLine(""d:"" + d.ToString()); } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false, includeOneTimeHelpers: false); var comp = CreateCompilation(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: "d:1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 22 (0x16) .maxstack 4 .locals init (int V_0) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: box ""int"" IL_000b: newobj ""CustomHandler..ctor(int, int, dynamic)"" IL_0010: call ""void Program.<<Main>$>g__M|0_0(int, CustomHandler)"" IL_0015: ret } "); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void DynamicConstruction_04(string expression) { var code = @" using System; using System.Runtime.CompilerServices; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(dynamic literalLength, int formattedCount) { Console.WriteLine(""ctor""); } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: "ctor"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 18 (0x12) .maxstack 2 IL_0000: ldc.i4.0 IL_0001: box ""int"" IL_0006: ldc.i4.0 IL_0007: newobj ""CustomHandler..ctor(dynamic, int)"" IL_000c: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_0011: ret } "); } [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] public void DynamicConstruction_05(string expression) { var code = @" using System; using System.Runtime.CompilerServices; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { Console.WriteLine(""ctor""); } public CustomHandler(dynamic literalLength, int formattedCount) { throw null; } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: "ctor"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 13 (0xd) .maxstack 2 IL_0000: ldc.i4.0 IL_0001: ldc.i4.0 IL_0002: newobj ""CustomHandler..ctor(int, int)"" IL_0007: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_000c: ret } "); } [Theory] [InlineData(@"$""Literal""")] [InlineData(@"$"""" + $""Literal""")] public void DynamicConstruction_06(string expression) { var code = @" using System; using System.Runtime.CompilerServices; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendLiteral(dynamic d) { Console.WriteLine(""AppendLiteral""); } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: "AppendLiteral"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 28 (0x1c) .maxstack 3 .locals init (CustomHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.7 IL_0003: ldc.i4.0 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldstr ""Literal"" IL_0010: call ""void CustomHandler.AppendLiteral(dynamic)"" IL_0015: ldloc.0 IL_0016: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_001b: ret } "); } [Theory] [InlineData(@"$""{1}""")] [InlineData(@"$""{1}"" + $""""")] public void DynamicConstruction_07(string expression) { var code = @" using System; using System.Runtime.CompilerServices; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(dynamic d) { Console.WriteLine(""AppendFormatted""); } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: "AppendFormatted"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 29 (0x1d) .maxstack 3 .locals init (CustomHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.0 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: call ""void CustomHandler.AppendFormatted(dynamic)"" IL_0016: ldloc.0 IL_0017: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_001c: ret } "); } [Theory] [InlineData(@"$""literal{d}""")] [InlineData(@"$""literal"" + $""{d}""")] public void DynamicConstruction_08_01(string expression) { var code = @" using System; using System.Runtime.CompilerServices; dynamic d = 1; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendLiteral(dynamic d) { Console.WriteLine(""AppendLiteral""); } public void AppendFormatted(dynamic d) { Console.WriteLine(""AppendFormatted""); } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: @" AppendLiteral AppendFormatted"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 128 (0x80) .maxstack 9 .locals init (object V_0, //d CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldloca.s V_1 IL_0009: ldc.i4.7 IL_000a: ldc.i4.1 IL_000b: call ""CustomHandler..ctor(int, int)"" IL_0010: ldloca.s V_1 IL_0012: ldstr ""literal"" IL_0017: call ""void CustomHandler.AppendLiteral(dynamic)"" IL_001c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0021: brtrue.s IL_0062 IL_0023: ldc.i4 0x100 IL_0028: ldstr ""AppendFormatted"" IL_002d: ldnull IL_002e: ldtoken ""Program"" IL_0033: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0038: ldc.i4.2 IL_0039: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"" IL_003e: dup IL_003f: ldc.i4.0 IL_0040: ldc.i4.s 9 IL_0042: ldnull IL_0043: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0048: stelem.ref IL_0049: dup IL_004a: ldc.i4.1 IL_004b: ldc.i4.0 IL_004c: ldnull IL_004d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0052: stelem.ref IL_0053: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)"" IL_0058: call ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" IL_005d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0062: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0067: ldfld ""<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic> System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>>.Target"" IL_006c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0071: ldloca.s V_1 IL_0073: ldloc.0 IL_0074: callvirt ""void <>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, ref CustomHandler, dynamic)"" IL_0079: ldloc.1 IL_007a: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_007f: ret } "); } [Theory] [InlineData(@"$""literal{d}""")] [InlineData(@"$""literal"" + $""{d}""")] public void DynamicConstruction_08_02(string expression) { var code = @" using System; using System.Runtime.CompilerServices; dynamic d = 1; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendLiteral(dynamic d) { Console.WriteLine(""AppendLiteral""); } public void AppendFormatted(dynamic d) { Console.WriteLine(""---""); } public void AppendFormatted(int d) { Console.WriteLine(""AppendFormatted""); } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: @" AppendLiteral AppendFormatted"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 128 (0x80) .maxstack 9 .locals init (object V_0, //d CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldloca.s V_1 IL_0009: ldc.i4.7 IL_000a: ldc.i4.1 IL_000b: call ""CustomHandler..ctor(int, int)"" IL_0010: ldloca.s V_1 IL_0012: ldstr ""literal"" IL_0017: call ""void CustomHandler.AppendLiteral(dynamic)"" IL_001c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0021: brtrue.s IL_0062 IL_0023: ldc.i4 0x100 IL_0028: ldstr ""AppendFormatted"" IL_002d: ldnull IL_002e: ldtoken ""Program"" IL_0033: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0038: ldc.i4.2 IL_0039: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"" IL_003e: dup IL_003f: ldc.i4.0 IL_0040: ldc.i4.s 9 IL_0042: ldnull IL_0043: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0048: stelem.ref IL_0049: dup IL_004a: ldc.i4.1 IL_004b: ldc.i4.0 IL_004c: ldnull IL_004d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0052: stelem.ref IL_0053: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)"" IL_0058: call ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" IL_005d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0062: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0067: ldfld ""<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic> System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>>.Target"" IL_006c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>> Program.<>o__0.<>p__0"" IL_0071: ldloca.s V_1 IL_0073: ldloc.0 IL_0074: callvirt ""void <>A{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, ref CustomHandler, dynamic)"" IL_0079: ldloc.1 IL_007a: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_007f: ret } "); } [Theory] [InlineData(@"$""literal{d}""")] [InlineData(@"$""literal"" + $""{d}""")] public void DynamicConstruction_09(string expression) { var code = @" using System; using System.Runtime.CompilerServices; dynamic d = 1; M(" + expression + @"); void M(CustomHandler c) {} [InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public bool AppendLiteral(dynamic d) { Console.WriteLine(""AppendLiteral""); return true; } public bool AppendFormatted(dynamic d) { Console.WriteLine(""---""); return true; } public bool AppendFormatted(long d) { Console.WriteLine(""AppendFormatted""); return true; } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib461AndCSharp); var verifier = CompileAndVerify(comp, expectedOutput: @" AppendLiteral AppendFormatted"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 196 (0xc4) .maxstack 11 .locals init (object V_0, //d CustomHandler V_1) IL_0000: ldc.i4.1 IL_0001: box ""int"" IL_0006: stloc.0 IL_0007: ldloca.s V_1 IL_0009: ldc.i4.7 IL_000a: ldc.i4.1 IL_000b: call ""CustomHandler..ctor(int, int)"" IL_0010: ldloca.s V_1 IL_0012: ldstr ""literal"" IL_0017: call ""bool CustomHandler.AppendLiteral(dynamic)"" IL_001c: brfalse IL_00bb IL_0021: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>> Program.<>o__0.<>p__1"" IL_0026: brtrue.s IL_004c IL_0028: ldc.i4.0 IL_0029: ldtoken ""bool"" IL_002e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0033: ldtoken ""Program"" IL_0038: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003d: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)"" IL_0042: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" IL_0047: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>> Program.<>o__0.<>p__1"" IL_004c: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>> Program.<>o__0.<>p__1"" IL_0051: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>>.Target"" IL_0056: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>> Program.<>o__0.<>p__1"" IL_005b: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>> Program.<>o__0.<>p__0"" IL_0060: brtrue.s IL_009d IL_0062: ldc.i4.0 IL_0063: ldstr ""AppendFormatted"" IL_0068: ldnull IL_0069: ldtoken ""Program"" IL_006e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0073: ldc.i4.2 IL_0074: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"" IL_0079: dup IL_007a: ldc.i4.0 IL_007b: ldc.i4.s 9 IL_007d: ldnull IL_007e: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0083: stelem.ref IL_0084: dup IL_0085: ldc.i4.1 IL_0086: ldc.i4.0 IL_0087: ldnull IL_0088: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_008d: stelem.ref IL_008e: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)"" IL_0093: call ""System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>> System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" IL_0098: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>> Program.<>o__0.<>p__0"" IL_009d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>> Program.<>o__0.<>p__0"" IL_00a2: ldfld ""<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic> System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>>.Target"" IL_00a7: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>> Program.<>o__0.<>p__0"" IL_00ac: ldloca.s V_1 IL_00ae: ldloc.0 IL_00af: callvirt ""dynamic <>F{00000008}<System.Runtime.CompilerServices.CallSite, CustomHandler, dynamic, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, ref CustomHandler, dynamic)"" IL_00b4: callvirt ""bool System.Func<System.Runtime.CompilerServices.CallSite, dynamic, bool>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)"" IL_00b9: br.s IL_00bc IL_00bb: ldc.i4.0 IL_00bc: pop IL_00bd: ldloc.1 IL_00be: call ""void Program.<<Main>$>g__M|0_0(CustomHandler)"" IL_00c3: ret } "); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] public void DynamicConstruction10( [CombinatorialValues(@"$""literal{d}""", @"$""literal"" + $""{d}""")] string expression, [CombinatorialValues("object", "dynamic", "string")] string type) { var source = $$""" using System; using System.Runtime.CompilerServices; using System.Text; dynamic d = "Hello world!"; Console.WriteLine(Interpolate({{expression}})); static string Interpolate(CustomInterpolationHandler text) { return text.ToString(); } [InterpolatedStringHandler] public ref struct CustomInterpolationHandler { private StringBuilder StringBuilder; public CustomInterpolationHandler(int literalLength, int formattedCount) { StringBuilder = new StringBuilder(); } public void AppendLiteral(string text) { StringBuilder.Append(text); } public void AppendFormatted({{type}} item) { StringBuilder.Append(item); } public override string ToString() { return StringBuilder.ToString(); } } """; var comp = CreateCompilation([source, InterpolatedStringHandlerAttribute], targetFramework: TargetFramework.StandardAndCSharp); comp.VerifyDiagnostics( // (7,31): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomInterpolationHandler'. // Console.WriteLine(Interpolate($"literal" + $"{d}")); Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) ); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] public void DynamicConstruction11([CombinatorialValues(@"$""literal{d}""", @"$""literal"" + $""{d}""")] string expression) { var source = $$""" using System; using System.Runtime.CompilerServices; using System.Text; dynamic d = "Hello world!"; Console.WriteLine(Interpolate({{expression}})); static string Interpolate(CustomInterpolationHandler text) { return text.ToString(); } [InterpolatedStringHandler] public ref struct CustomInterpolationHandler { private StringBuilder StringBuilder; public CustomInterpolationHandler(int literalLength, int formattedCount) { StringBuilder = new StringBuilder(); } public void AppendLiteral(string text) { StringBuilder.Append(text); } public void AppendFormatted<T>(T item) { StringBuilder.Append(item); } public override string ToString() { return StringBuilder.ToString(); } } """; CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( // (7,31): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomInterpolationHandler'. // Console.WriteLine(Interpolate($"literal" + $"{d}")); Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) ); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] public void DynamicConstruction12([CombinatorialValues(@"$""literal{d}""", @"$""literal"" + $""{d}""")] string expression) { var source = $$""" using System; using System.Runtime.CompilerServices; using System.Text; dynamic d = "Hello world!"; Console.WriteLine(Interpolate({{expression}})); static string Interpolate(CustomInterpolationHandler text) { return text.ToString(); } [InterpolatedStringHandler] public ref struct CustomInterpolationHandler { private StringBuilder StringBuilder; public CustomInterpolationHandler(int literalLength, int formattedCount) { StringBuilder = new StringBuilder(); } public void AppendLiteral(string text) { StringBuilder.Append(text); } public void AppendFormatted(object item) { StringBuilder.Append(item); } public void AppendFormatted(string item) { StringBuilder.Append(item); } public override string ToString() { return StringBuilder.ToString(); } } """; CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( // (7,31): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomInterpolationHandler'. // Console.WriteLine(Interpolate($"literal" + $"{d}")); Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) ); } [Theory] [InlineData(@"$""{s}""")] [InlineData(@"$""{s}"" + $""""")] public void RefEscape_01A(string expression) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { Span<char> s; public CustomHandler(int literalLength, int formattedCount) : this() {} public void AppendFormatted(Span<char> s) => this.s = s; public static CustomHandler M() { Span<char> s = stackalloc char[10]; return " + expression + @"; } } "; var expectedDiagnostics = new[] { // (17,18): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(Span<char>)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope // return $"{s}"; Diagnostic(ErrorCode.ERR_EscapeCall, "{s}").WithArguments("CustomHandler.AppendFormatted(System.Span<char>)", "s").WithLocation(17, 18), // (17,19): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope // return $"{s}"; Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(17, 19) }; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); } // As above but with scoped parameter in AppendFormatted(). [WorkItem(63262, "https://github.com/dotnet/roslyn/issues/63262")] [Theory] [InlineData(@"$""{s}""")] [InlineData(@"$""{s}"" + $""""")] public void RefEscape_01B(string expression) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(scoped Span<char> s) { } public static CustomHandler M() { Span<char> s = stackalloc char[10]; return " + expression + @"; } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } [Theory] [InlineData(@"$""{s}""")] [InlineData(@"$""{s}"" + $""""")] public void RefEscape_02(string expression) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { Span<char> s; public CustomHandler(int literalLength, int formattedCount) : this() {} public void AppendFormatted(Span<char> s) => this.s = s; public static ref CustomHandler M() { Span<char> s = stackalloc char[10]; return " + expression + @"; } } "; var expectedDiagnostics = new[] { // (17,9): error CS8150: By-value returns may only be used in methods that return by value // return $"{s}"; Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "return").WithLocation(17, 9) }; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); } [Theory] [InlineData(@"$""{s}""")] [InlineData(@"$""{s}"" + $""""")] public void RefEscape_03(string expression) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { Span<char> s; public CustomHandler(int literalLength, int formattedCount) : this() {} public void AppendFormatted(Span<char> s) => this.s = s; public static ref CustomHandler M() { Span<char> s = stackalloc char[10]; return ref " + expression + @"; } } "; var expectedDiagnostics = new[] { // (17,20): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return ref $"{s}"; Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, expression).WithLocation(17, 20) }; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); } [Theory] [InlineData(@"$""{s}""")] [InlineData(@"$""{s}"" + $""""")] public void RefEscape_04(string expression) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { S1 s1; public CustomHandler(int literalLength, int formattedCount, ref S1 s1) : this() { this.s1 = s1; } public void AppendFormatted(Span<char> s) => this.s1.s = s; public static void M(ref S1 s1) { Span<char> s = stackalloc char[10]; M2(ref s1, " + expression + @"); } public static void M2(ref S1 s1, [InterpolatedStringHandlerArgument(""s1"")] ref CustomHandler handler) {} } public ref struct S1 { public Span<char> s; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, ref CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, " + expression + @")").WithArguments("CustomHandler.M2(ref S1, ref CustomHandler)", "handler").WithLocation(17, 9), // (17,22): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(Span<char>)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_EscapeCall, "{s}").WithArguments("CustomHandler.AppendFormatted(System.Span<char>)", "s").WithLocation(17, 22), // (17,23): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(17, 23)); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, ref CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, " + expression + @")").WithArguments("CustomHandler.M2(ref S1, ref CustomHandler)", "handler").WithLocation(17, 9), // (17,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s1").WithLocation(17, 16), // (17,20): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, ref S1)' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_EscapeCall, expression).WithArguments("CustomHandler.CustomHandler(int, int, ref S1)", "s1").WithLocation(17, 20)); } [Theory] [InlineData(@"$""{s1}""")] [InlineData(@"$""{s1}"" + $""""")] public void RefEscape_05(string expression) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { Span<char> s; public CustomHandler(int literalLength, int formattedCount, ref Span<char> s) : this() { this.s = s; } public void AppendFormatted(S1 s1) => s1.s = this.s; public static void M(ref S1 s1) { Span<char> s = stackalloc char[10]; M2(ref s, " + expression + @"); } public static void M2(ref Span<char> s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler handler) {} } public ref struct S1 { public Span<char> s; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } [Theory] [InlineData(@"$""{s2}""")] [InlineData(@"$""{s2}"" + $""""")] public void RefEscape_06(string expression) { var code = @" using System; using System.Runtime.CompilerServices; Span<char> s = stackalloc char[5]; Span<char> s2 = stackalloc char[10]; s.TryWrite(" + expression + @"); public static class MemoryExtensions { public static bool TryWrite(this Span<char> span, [InterpolatedStringHandlerArgument(""span"")] CustomHandler builder) => true; } [InterpolatedStringHandler] public ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, Span<char> s) : this() { } public bool AppendFormatted(Span<char> s) => true; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } [Theory] [InlineData(@"$""{s2}""")] [InlineData(@"$""{s2}"" + $""""")] public void RefEscape_07(string expression) { var code = @" using System; using System.Runtime.CompilerServices; Span<char> s = stackalloc char[5]; Span<char> s2 = stackalloc char[10]; s.TryWrite(" + expression + @"); public static class MemoryExtensions { public static bool TryWrite(this Span<char> span, [InterpolatedStringHandlerArgument(""span"")] ref CustomHandler builder) => true; } [InterpolatedStringHandler] public ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, Span<char> s) : this() { } public bool AppendFormatted(Span<char> s) => true; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] public void RefEscape_08(LanguageVersion languageVersion) { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { Span<char> s; public CustomHandler(int literalLength, int formattedCount, ref Span<char> s) : this() { this.s = s; } public static CustomHandler M() { Span<char> s = stackalloc char[10]; ref CustomHandler c = ref M2(ref s, $""""); return c; } public static ref CustomHandler M2(ref Span<char> s, [InterpolatedStringHandlerArgument(""s"")] ref CustomHandler handler) { return ref handler; } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (16,16): error CS8352: Cannot use variable 'c' in this context because it may expose referenced variables outside of their declaration scope // return c; Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("c").WithLocation(16, 16) ); } [Fact] public void RefEscape_09() { var code = @" using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] public ref struct CustomHandler { Span<char> s; public CustomHandler(int literalLength, int formattedCount, ref S1 s1) : this() { s1.Handler = this; } public static void M(ref S1 s1) { Span<char> s2 = stackalloc char[10]; M2(ref s1, $""{s2}""); } public static void M2(ref S1 s1, [InterpolatedStringHandlerArgument(""s1"")] CustomHandler handler) { } public void AppendFormatted(Span<char> s) { this.s = s; } } public ref struct S1 { public CustomHandler Handler; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (15,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, $""{s2}"")").WithArguments("CustomHandler.M2(ref S1, CustomHandler)", "handler").WithLocation(15, 9), // (15,22): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(Span<char>)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_EscapeCall, "{s2}").WithArguments("CustomHandler.AppendFormatted(System.Span<char>)", "s").WithLocation(15, 22), // (15,23): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(15, 23) ); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (10,100): error CS8352: Cannot use variable 'out CustomHandler this' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref S1 s1) : this() { s1.Handler = this; } Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler this").WithLocation(10, 100), // (15,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, $""{s2}"")").WithArguments("CustomHandler.M2(ref S1, CustomHandler)", "handler").WithLocation(15, 9), // (15,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s1").WithLocation(15, 16), // (15,20): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, ref S1)' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_EscapeCall, @"$""{s2}""").WithArguments("CustomHandler.CustomHandler(int, int, ref S1)", "s1").WithLocation(15, 20) ); } [Fact] public void RefEscape_10() { var code = @"using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref S s) : this() { s.Handler = this; } public void AppendFormatted(int i) { } } ref struct S { public CustomHandler Handler; } class Program { static void Main() { S s = default; M(ref s, $""{1}""); } static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler handler) { } }"; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (5,97): error CS8352: Cannot use variable 'out CustomHandler this' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref S s) : this() { s.Handler = this; } Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler this").WithLocation(5, 97), // (17,9): error CS8350: This combination of arguments to 'Program.M(ref S, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M(ref s, $"{1}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M(ref s, $""{1}"")").WithArguments("Program.M(ref S, CustomHandler)", "handler").WithLocation(17, 9), // (17,15): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // M(ref s, $"{1}"); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s").WithLocation(17, 15), // (17,18): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, ref S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope // M(ref s, $"{1}"); Diagnostic(ErrorCode.ERR_EscapeCall, @"$""{1}""").WithArguments("CustomHandler.CustomHandler(int, int, ref S)", "s").WithLocation(17, 18)); } [WorkItem(63262, "https://github.com/dotnet/roslyn/issues/63262")] [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] public void RefEscape_11A(LanguageVersion languageVersion) { var code = @"using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(Span<char> s) { } } class Program { static void F1() { Span<char> s = stackalloc char[10]; M($""{s}""); } static void F2() { Span<char> s = stackalloc char[10]; CustomHandler h2 = new CustomHandler(0, 1); h2.AppendFormatted(s); // 1 M(ref h2); } static void M(ref CustomHandler handler) { } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (20,9): error CS8350: This combination of arguments to 'CustomHandler.AppendFormatted(Span<char>)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // h2.AppendFormatted(s); // 1 Diagnostic(ErrorCode.ERR_CallArgMixing, "h2.AppendFormatted(s)").WithArguments("CustomHandler.AppendFormatted(System.Span<char>)", "s").WithLocation(20, 9), // (20,28): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope // h2.AppendFormatted(s); // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(20, 28)); } // As above but with scoped parameter in AppendFormatted(). [WorkItem(63262, "https://github.com/dotnet/roslyn/issues/63262")] [Fact] public void RefEscape_11B() { var code = @"using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(scoped Span<char> s) { } } class Program { static void F1() { Span<char> s = stackalloc char[10]; M($""{s}""); } static void F2() { Span<char> s = stackalloc char[10]; CustomHandler h2 = new CustomHandler(0, 1); h2.AppendFormatted(s); M(ref h2); } static void M(ref CustomHandler handler) { } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } [WorkItem(63262, "https://github.com/dotnet/roslyn/issues/63262")] [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] public void RefEscape_12A(LanguageVersion languageVersion) { var code = @"using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(Span<char> s) { } } class Program { static CustomHandler F1() { Span<char> s = stackalloc char[10]; CustomHandler h1 = $""{s}""; return h1; // 1 } static CustomHandler F2() { Span<char> s = stackalloc char[10]; CustomHandler h2 = new CustomHandler(0, 1); h2.AppendFormatted(s); // 2 return h2; } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (15,16): error CS8352: Cannot use variable 'h1' in this context because it may expose referenced variables outside of their declaration scope // return h1; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "h1").WithArguments("h1").WithLocation(15, 16), // (21,9): error CS8350: This combination of arguments to 'CustomHandler.AppendFormatted(Span<char>)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // h2.AppendFormatted(s); // 2 Diagnostic(ErrorCode.ERR_CallArgMixing, "h2.AppendFormatted(s)").WithArguments("CustomHandler.AppendFormatted(System.Span<char>)", "s").WithLocation(21, 9), // (21,28): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope // h2.AppendFormatted(s); // 2 Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(21, 28)); } // As above but with scoped parameter in AppendFormatted(). [WorkItem(63262, "https://github.com/dotnet/roslyn/issues/63262")] [Fact] public void RefEscape_12B() { var code = @"using System; using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(scoped Span<char> s) { } } class Program { static CustomHandler F1() { Span<char> s = stackalloc char[10]; CustomHandler h1 = $""{s}""; return h1; } static CustomHandler F2() { Span<char> s = stackalloc char[10]; CustomHandler h2 = new CustomHandler(0, 1); h2.AppendFormatted(s); return h2; } static CustomHandler F3() { Span<char> s = stackalloc char[10]; scoped CustomHandler h3 = new CustomHandler(0, 1); h3.AppendFormatted(s); return h3; // 1 } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (29,16): error CS8352: Cannot use variable 'h3' in this context because it may expose referenced variables outside of their declaration scope // return h3; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "h3").WithArguments("h3").WithLocation(29, 16)); } [WorkItem(63306, "https://github.com/dotnet/roslyn/issues/63306")] [Fact] public void RefEscape_13A() { var code = @"using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(in int i) { } } class Program { static CustomHandler F1() { int i = 1; return $""{i}""; // 1 } static CustomHandler F2() { return $""{2}""; // 2 } static CustomHandler F3() { int i = 3; CustomHandler h3 = $""{i}""; return h3; // 3 } static CustomHandler F4() { CustomHandler h4 = $""{4}""; return h4; // 4 } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (13,18): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return $"{i}"; // 1 Diagnostic(ErrorCode.ERR_EscapeCall, "{i}").WithArguments("CustomHandler.AppendFormatted(in int)", "i").WithLocation(13, 18), // (13,19): error CS8168: Cannot return local 'i' by reference because it is not a ref local // return $"{i}"; // 1 Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(13, 19), // (17,18): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return $"{2}"; // 2 Diagnostic(ErrorCode.ERR_EscapeCall, "{2}").WithArguments("CustomHandler.AppendFormatted(in int)", "i").WithLocation(17, 18), // (17,19): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return $"{2}"; // 2 Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "2").WithLocation(17, 19), // (23,16): error CS8352: Cannot use variable 'h3' in this context because it may expose referenced variables outside of their declaration scope // return h3; // 3 Diagnostic(ErrorCode.ERR_EscapeVariable, "h3").WithArguments("h3").WithLocation(23, 16), // (28,16): error CS8352: Cannot use variable 'h4' in this context because it may expose referenced variables outside of their declaration scope // return h4; // 4 Diagnostic(ErrorCode.ERR_EscapeVariable, "h4").WithArguments("h4").WithLocation(28, 16)); // The `in int` parameter cannot be captured in C# 11. comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } // As above but with scoped parameter in AppendFormatted(). [Fact] public void RefEscape_13B() { var code = @"using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(scoped in int i) { } } class Program { static CustomHandler F1() { int i = 1; return $""{i}""; } static CustomHandler F2() { return $""{2}""; } static CustomHandler F3() { int i = 3; CustomHandler h3 = $""{i}""; return h3; } static CustomHandler F4() { CustomHandler h4 = $""{4}""; return h4; } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } [Fact] public void RefEscape_14() { string source = """ using System.Runtime.CompilerServices; [InterpolatedStringHandler] struct CustomHandler { public CustomHandler(int literalLength, int formattedCount) { } } ref struct R { } class Program { static R F1() { R r = F2($""); return r; } static R F2(ref CustomHandler handler) { return default; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (13,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return r; Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(13, 16)); } [Fact] public void RefEscape_15() { string source = """ using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } public void AppendFormatted(int i) { } } ref struct R { public CustomHandler Handler; public object this[[InterpolatedStringHandlerArgument("")] CustomHandler handler] => null; } class Program { static R F() { R r = new R(); _ = r[$"{1}"]; return r; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,97): error CS8352: Cannot use variable 'out CustomHandler this' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler this").WithLocation(5, 97), // (18,13): error CS1620: Argument 3 must be passed with the 'ref' keyword // _ = r[$"{1}"]; Diagnostic(ErrorCode.ERR_BadArgRef, "r").WithArguments("3", "ref").WithLocation(18, 13)); } [Fact] public void RefEscape_16() { string source = """ using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } public void AppendFormatted(int i) { } } ref struct R { public CustomHandler Handler; public object this[ref R r, [InterpolatedStringHandlerArgument("r")] CustomHandler handler] => null; } class Program { static R F() { R r = new R(); _ = r[ref r, $"{1}"]; return r; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,97): error CS8352: Cannot use variable 'out CustomHandler this' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler this").WithLocation(5, 97), // (11,24): error CS0631: ref and out are not valid in this context // public object this[ref R r, [InterpolatedStringHandlerArgument("r")] CustomHandler handler] => null; Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(11, 24), // (18,13): error CS8350: This combination of arguments to 'R.this[ref R, CustomHandler]' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // _ = r[ref r, $"{1}"]; Diagnostic(ErrorCode.ERR_CallArgMixing, @"r[ref r, $""{1}""]").WithArguments("R.this[ref R, CustomHandler]", "handler").WithLocation(18, 13), // (18,19): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // _ = r[ref r, $"{1}"]; Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "r").WithLocation(18, 19), // (18,22): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, ref R)' in this context because it may expose variables referenced by parameter 'r' outside of their declaration scope // _ = r[ref r, $"{1}"]; Diagnostic(ErrorCode.ERR_EscapeCall, @"$""{1}""").WithArguments("CustomHandler.CustomHandler(int, int, ref R)", "r").WithLocation(18, 22)); } [Fact] public void RefEscape_17() { string source = """ using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } public void AppendFormatted(int i) { } } ref struct R { public CustomHandler Handler; public R(ref R r, [InterpolatedStringHandlerArgument("r")] CustomHandler handler) { } } class Program { static R F() { R x = new R(); R y = new R(ref x, $"{1}"); return x; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,97): error CS8352: Cannot use variable 'out CustomHandler this' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler this").WithLocation(5, 97), // (18,15): error CS8350: This combination of arguments to 'R.R(ref R, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // R y = new R(ref x, $"{1}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"new R(ref x, $""{1}"")").WithArguments("R.R(ref R, CustomHandler)", "handler").WithLocation(18, 15), // (18,25): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // R y = new R(ref x, $"{1}"); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(18, 25), // (18,28): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, ref R)' in this context because it may expose variables referenced by parameter 'r' outside of their declaration scope // R y = new R(ref x, $"{1}"); Diagnostic(ErrorCode.ERR_EscapeCall, @"$""{1}""").WithArguments("CustomHandler.CustomHandler(int, int, ref R)", "r").WithLocation(18, 28)); } [Fact] public void RefEscape_18() { string source = """ using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { private ref readonly int _i; public CustomHandler(int literalLength, int formattedCount, in int i = 0) { _i = ref i; } public void AppendFormatted(int i) { } } class Program { static CustomHandler F1() { return $"{1}"; } static CustomHandler F2() { CustomHandler h2 = $"{2}"; return h2; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (13,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return $"{1}"; Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, @"$""{1}""").WithLocation(13, 16), // (13,16): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return $"{1}"; Diagnostic(ErrorCode.ERR_EscapeCall, @"$""{1}""").WithArguments("CustomHandler.CustomHandler(int, int, in int)", "i").WithLocation(13, 16), // (18,16): error CS8352: Cannot use variable 'h2' in this context because it may expose referenced variables outside of their declaration scope // return h2; Diagnostic(ErrorCode.ERR_EscapeVariable, "h2").WithArguments("h2").WithLocation(18, 16)); } [WorkItem(63306, "https://github.com/dotnet/roslyn/issues/63306")] [Fact] public void RefEscape_19() { string source = """ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { private ref readonly int _i; public CustomHandler(int literalLength, int formattedCount) { } public void AppendFormatted(int x, [UnscopedRef] in int y = 0) { _i = ref y; } } class Program { static CustomHandler F1() { return $"{1}"; } static CustomHandler F2() { CustomHandler h2 = $"{2}"; return h2; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (14,18): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return $"{1}"; Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "{1}").WithLocation(14, 18), // (14,18): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(int, in int)' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope // return $"{1}"; Diagnostic(ErrorCode.ERR_EscapeCall, "{1}").WithArguments("CustomHandler.AppendFormatted(int, in int)", "y").WithLocation(14, 18), // (19,16): error CS8352: Cannot use variable 'h2' in this context because it may expose referenced variables outside of their declaration scope // return h2; Diagnostic(ErrorCode.ERR_EscapeVariable, "h2").WithArguments("h2").WithLocation(19, 16)); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_01() { string source = """ using System; using System.Runtime.CompilerServices; static class Program { static void Main() { var r = new R(); r .F($"[{42}]") .F($"[{"str".AsSpan()}]"); } } readonly ref struct R { public R F([InterpolatedStringHandlerArgument("")] CustomHandler handler) => this; } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } public void AppendFormatted(ReadOnlySpan<char> value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_02() { string source = """ using System.Runtime.CompilerServices; static class Program { static void Main() { var r = new R(); r .F($"{42}") .F($"{42}"); } } readonly ref struct R { public R F([InterpolatedStringHandlerArgument("")] CustomHandler handler) => this; } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_03() { string source = """ using System.Runtime.CompilerServices; static class Program { static void Main() { var r = new R(); F( F(r, $"{42}"), $"{42}"); } static R F(R r, [InterpolatedStringHandlerArgument("r")] CustomHandler handler) => r; } readonly ref struct R { } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_04() { string source = """ using System.Runtime.CompilerServices; static class Program { static void Main() { var r = new R(); r = new R( new R(r, $"{42}"), $"{42}"); } } readonly ref struct R { public R(R r, [InterpolatedStringHandlerArgument("r")] CustomHandler handler) { } } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_05() { string source = """ using System.Runtime.CompilerServices; static class Program { static void Main() { var r = new R(); r = r [$"{42}"] [$"{42}"]; } } readonly ref struct R { public R this[[InterpolatedStringHandlerArgument("")] CustomHandler handler] => this; } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_06() { string source = """ using System; using System.Runtime.CompilerServices; static class Program { static R M() { R r = default; Span<byte> span = stackalloc byte[42]; return r .F($"{span}") .F($"{span}"); } } readonly ref struct R { public R F([InterpolatedStringHandlerArgument("")] CustomHandler handler) => this; } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } public void AppendFormatted(Span<byte> span) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_06b() { string source = """ using System; using System.Runtime.CompilerServices; static class Program { static R M() { R r = default; Span<byte> span = stackalloc byte[42]; return r .F($"{span}") .F($"{span}"); } } ref struct R { public R F([InterpolatedStringHandlerArgument("")] CustomHandler handler) => this; } [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } public void AppendFormatted(Span<byte> span) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,16): error CS8350: This combination of arguments to 'R.F(CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // return r Diagnostic(ErrorCode.ERR_CallArgMixing, @"r .F($""{span}"")").WithArguments("R.F(CustomHandler)", "handler").WithLocation(9, 16), // (9,16): error CS8347: Cannot use a result of 'R.F(CustomHandler)' in this context because it may expose variables referenced by parameter 'handler' outside of their declaration scope // return r Diagnostic(ErrorCode.ERR_EscapeCall, @"r .F($""{span}"")").WithArguments("R.F(CustomHandler)", "handler").WithLocation(9, 16), // (10,18): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(Span<byte>)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope // .F($"{span}") Diagnostic(ErrorCode.ERR_EscapeCall, "{span}").WithArguments("CustomHandler.AppendFormatted(System.Span<byte>)", "span").WithLocation(10, 18), // (10,18): error CS8347: Cannot use a result of 'CustomHandler.AppendFormatted(Span<byte>)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope // .F($"{span}") Diagnostic(ErrorCode.ERR_EscapeCall, "{span}").WithArguments("CustomHandler.AppendFormatted(System.Span<byte>)", "span").WithLocation(10, 18), // (10,19): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // .F($"{span}") Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(10, 19), // (10,19): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // .F($"{span}") Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(10, 19)); } [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_07() { string source = """ using System.Runtime.CompilerServices; static class Program { static R M() { int i = 0; var r = new R(ref i); return r .F($"{i}") .F($"{i}"); } } readonly ref struct R { private readonly ref int _i; public R(ref int i) { _i = ref i; } public R F([InterpolatedStringHandlerArgument("")] CustomHandler handler) => this; } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return r Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(8, 16)); } [WorkItem(67070, "https://github.com/dotnet/roslyn/issues/67070")] [ConditionalFact(typeof(CoreClrOnly))] public void RefEscape_NestedCalls_08() { string source = """ using System.Runtime.CompilerServices; static class Program { static void Main() { var r = new R(); r .F($"{1}", $"{2}") .F($"{3}", $"{4}"); } } readonly ref struct R { public R F([InterpolatedStringHandlerArgument("")] CustomHandler h1, [InterpolatedStringHandlerArgument("")] CustomHandler h2) => this; } [InterpolatedStringHandler] readonly ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, R r) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T value) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } [Fact] public void RefEscape_ForEachExpression() { string source = """ using System.Runtime.CompilerServices; [InterpolatedStringHandler] ref struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } public void AppendFormatted(int i) { } } ref struct R { public CustomHandler Handler; } ref struct Enumerable { public static Enumerable Create(ref R r, [InterpolatedStringHandlerArgument("r")] CustomHandler handler) => default; public Enumerator GetEnumerator() => default; } ref struct Enumerator { public R Current => throw null; public bool MoveNext() => false; } class Program { static void F(ref R r) { foreach (var i in Enumerable.Create(ref r, $"{1}")) { } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,97): error CS8352: Cannot use variable 'out CustomHandler this' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref R r) : this() { r.Handler = this; } Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler this").WithLocation(5, 97), // (26,27): error CS8350: This combination of arguments to 'Enumerable.Create(ref R, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // foreach (var i in Enumerable.Create(ref r, $"{1}")) Diagnostic(ErrorCode.ERR_CallArgMixing, @"Enumerable.Create(ref r, $""{1}"")").WithArguments("Enumerable.Create(ref R, CustomHandler)", "handler").WithLocation(26, 27), // (26,49): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // foreach (var i in Enumerable.Create(ref r, $"{1}")) Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "r").WithLocation(26, 49), // (26,52): error CS8347: Cannot use a result of 'CustomHandler.CustomHandler(int, int, ref R)' in this context because it may expose variables referenced by parameter 'r' outside of their declaration scope // foreach (var i in Enumerable.Create(ref r, $"{1}")) Diagnostic(ErrorCode.ERR_EscapeCall, @"$""{1}""").WithArguments("CustomHandler.CustomHandler(int, int, ref R)", "r").WithLocation(26, 52)); } [Theory, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] [InlineData(@"$""{{ {i} }}""")] [InlineData(@"$""{{ "" + $""{i}"" + $"" }}""")] public void BracesAreEscaped_01(string expression) { var code = @" int i = 1; System.Console.WriteLine(" + expression + @");"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" { value:1 }"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 56 (0x38) .maxstack 3 .locals init (int V_0, //i System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.4 IL_0005: ldc.i4.1 IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldstr ""{ "" IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_0017: ldloca.s V_1 IL_0019: ldloc.0 IL_001a: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_001f: ldloca.s V_1 IL_0021: ldstr "" }"" IL_0026: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" IL_002b: ldloca.s V_1 IL_002d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0032: call ""void System.Console.WriteLine(string)"" IL_0037: ret } "); } [Theory, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] [InlineData(@"$""{{ {i} }}""")] [InlineData(@"$""{{ "" + $""{i}"" + $"" }}""")] public void BracesAreEscaped_02(string expression) { var code = @" int i = 1; CustomHandler c = " + expression + @"; System.Console.WriteLine(c.ToString());"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" literal:{ value:1 alignment:0 format: literal: }"); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 71 (0x47) .maxstack 4 .locals init (int V_0, //i CustomHandler V_1, //c CustomHandler V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_2 IL_0004: ldc.i4.4 IL_0005: ldc.i4.1 IL_0006: call ""CustomHandler..ctor(int, int)"" IL_000b: ldloca.s V_2 IL_000d: ldstr ""{ "" IL_0012: call ""void CustomHandler.AppendLiteral(string)"" IL_0017: ldloca.s V_2 IL_0019: ldloc.0 IL_001a: box ""int"" IL_001f: ldc.i4.0 IL_0020: ldnull IL_0021: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0026: ldloca.s V_2 IL_0028: ldstr "" }"" IL_002d: call ""void CustomHandler.AppendLiteral(string)"" IL_0032: ldloc.2 IL_0033: stloc.1 IL_0034: ldloca.s V_1 IL_0036: constrained. ""CustomHandler"" IL_003c: callvirt ""string object.ToString()"" IL_0041: call ""void System.Console.WriteLine(string)"" IL_0046: ret } "); } [Fact] public void InterpolatedStringsAddedUnderObjectAddition() { var code = @" int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; System.Console.WriteLine($""{i1}"" + $""{i2}"" + $""{i3}"" + i4);"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 value:2 value:3 4 "); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 66 (0x42) .maxstack 3 .locals init (int V_0, //i1 int V_1, //i2 int V_2, //i3 int V_3, //i4 System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.2 IL_0003: stloc.1 IL_0004: ldc.i4.3 IL_0005: stloc.2 IL_0006: ldc.i4.4 IL_0007: stloc.3 IL_0008: ldloca.s V_4 IL_000a: ldc.i4.0 IL_000b: ldc.i4.3 IL_000c: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_0011: ldloca.s V_4 IL_0013: ldloc.0 IL_0014: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0019: ldloca.s V_4 IL_001b: ldloc.1 IL_001c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0021: ldloca.s V_4 IL_0023: ldloc.2 IL_0024: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0029: ldloca.s V_4 IL_002b: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0030: ldloca.s V_3 IL_0032: call ""string int.ToString()"" IL_0037: call ""string string.Concat(string, string)"" IL_003c: call ""void System.Console.WriteLine(string)"" IL_0041: ret } "); } [Theory] [InlineData(@"$""({i1}),"" + $""[{i2}],"" + $""{{{i3}}}""")] [InlineData(@"($""({i1}),"" + $""[{i2}],"") + $""{{{i3}}}""")] [InlineData(@"$""({i1}),"" + ($""[{i2}],"" + $""{{{i3}}}"")")] public void InterpolatedStringsAddedUnderObjectAddition2(string expression) { var code = $@" int i1 = 1; int i2 = 2; int i3 = 3; System.Console.WriteLine({expression});"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); CompileAndVerify(comp, expectedOutput: @" ( value:1 ), [ value:2 ], { value:3 } "); } [Fact] public void InterpolatedStringsAddedUnderObjectAddition3() { var code = @" #nullable enable using System; try { var s = string.Empty; Console.WriteLine($""{s = null}{s.Length}"" + $""""); } catch (NullReferenceException) { Console.WriteLine(""Null reference exception caught.""); } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); CompileAndVerify(comp, expectedOutput: "Null reference exception caught.").VerifyIL("<top-level-statements-entry-point>", @" { // Code size 65 (0x41) .maxstack 3 .locals init (string V_0, //s System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) .try { IL_0000: ldsfld ""string string.Empty"" IL_0005: stloc.0 IL_0006: ldc.i4.0 IL_0007: ldc.i4.2 IL_0008: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldnull IL_0011: dup IL_0012: stloc.0 IL_0013: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" IL_0018: ldloca.s V_1 IL_001a: ldloc.0 IL_001b: callvirt ""int string.Length.get"" IL_0020: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0025: ldloca.s V_1 IL_0027: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_002c: call ""void System.Console.WriteLine(string)"" IL_0031: leave.s IL_0040 } catch System.NullReferenceException { IL_0033: pop IL_0034: ldstr ""Null reference exception caught."" IL_0039: call ""void System.Console.WriteLine(string)"" IL_003e: leave.s IL_0040 } IL_0040: ret } ").VerifyDiagnostics( // (9,36): warning CS8602: Dereference of a possibly null reference. // Console.WriteLine($"{s = null}{s.Length}" + $""); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 36) ); } [Fact, WorkItem("https://github.com/dotnet/runtime/issues/78991")] public void InterpolatedStringsAddedUnderObjectAddition_AdditionsWithMoreParts_01() { var code = """ using System; _ = $"1{Environment.NewLine}" + $"2{Environment.NewLine}"; """; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); CompileAndVerify(comp).VerifyIL("<top-level-statements-entry-point>", """ { // Code size 27 (0x1b) .maxstack 4 IL_0000: ldstr "1" IL_0005: call "string System.Environment.NewLine.get" IL_000a: ldstr "2" IL_000f: call "string System.Environment.NewLine.get" IL_0014: call "string string.Concat(string, string, string, string)" IL_0019: pop IL_001a: ret } """); } [Fact, WorkItem("https://github.com/dotnet/runtime/issues/78991")] public void InterpolatedStringsAddedUnderObjectAddition_AdditionsWithMoreParts_02() { var code = """ using System; _ = $"1{Environment.NewLine}" + $"2{Environment.NewLine}" + $"3"; """; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); CompileAndVerify(comp).VerifyIL("<top-level-statements-entry-point>", """ { // Code size 78 (0x4e) .maxstack 3 .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: ldc.i4.2 IL_0004: call "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" IL_0009: ldloca.s V_0 IL_000b: ldstr "1" IL_0010: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_0015: ldloca.s V_0 IL_0017: call "string System.Environment.NewLine.get" IL_001c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" IL_0021: ldloca.s V_0 IL_0023: ldstr "2" IL_0028: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_002d: ldloca.s V_0 IL_002f: call "string System.Environment.NewLine.get" IL_0034: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" IL_0039: ldloca.s V_0 IL_003b: ldstr "3" IL_0040: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_0045: ldloca.s V_0 IL_0047: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" IL_004c: pop IL_004d: ret } """); } [Fact, WorkItem("https://github.com/dotnet/runtime/issues/78991")] public void InterpolatedStringsAddedUnderObjectAddition_AdditionsWithMoreParts_03() { var code = """ using System; _ = $"1{Environment.NewLine}" + $"2{Environment.NewLine}" + $"3{Environment.NewLine}" + $"4{Environment.NewLine}"; """; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); CompileAndVerify(comp).VerifyIL("<top-level-statements-entry-point>", """ { // Code size 114 (0x72) .maxstack 3 .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.4 IL_0003: ldc.i4.4 IL_0004: call "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" IL_0009: ldloca.s V_0 IL_000b: ldstr "1" IL_0010: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_0015: ldloca.s V_0 IL_0017: call "string System.Environment.NewLine.get" IL_001c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" IL_0021: ldloca.s V_0 IL_0023: ldstr "2" IL_0028: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_002d: ldloca.s V_0 IL_002f: call "string System.Environment.NewLine.get" IL_0034: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" IL_0039: ldloca.s V_0 IL_003b: ldstr "3" IL_0040: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_0045: ldloca.s V_0 IL_0047: call "string System.Environment.NewLine.get" IL_004c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" IL_0051: ldloca.s V_0 IL_0053: ldstr "4" IL_0058: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" IL_005d: ldloca.s V_0 IL_005f: call "string System.Environment.NewLine.get" IL_0064: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" IL_0069: ldloca.s V_0 IL_006b: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" IL_0070: pop IL_0071: ret } """); } [Fact] public void InterpolatedStringsAddedUnderObjectAddition_DefiniteAssignment() { var code = @" object o1; object o2; object o3; _ = $""{o1 = null}"" + $""{o2 = null}"" + $""{o3 = null}"" + 1; o1.ToString(); o2.ToString(); o3.ToString(); "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: true) }); comp.VerifyDiagnostics( // (7,1): error CS0165: Use of unassigned local variable 'o2' // o2.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "o2").WithArguments("o2").WithLocation(7, 1), // (8,1): error CS0165: Use of unassigned local variable 'o3' // o3.ToString(); Diagnostic(ErrorCode.ERR_UseDefViolation, "o3").WithArguments("o3").WithLocation(8, 1) ); } [Theory] [InlineData(@"($""{i1}"" + $""{i2}"") + $""{i3}""")] [InlineData(@"$""{i1}"" + ($""{i2}"" + $""{i3}"")")] public void ParenthesizedAdditiveExpression_01(string expression) { var code = @" int i1 = 1; int i2 = 2; int i3 = 3; CustomHandler c = " + expression + @"; System.Console.WriteLine(c.ToString());"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:0 format: value:2 alignment:0 format: value:3 alignment:0 format: "); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 82 (0x52) .maxstack 4 .locals init (int V_0, //i1 int V_1, //i2 int V_2, //i3 CustomHandler V_3, //c CustomHandler V_4) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.2 IL_0003: stloc.1 IL_0004: ldc.i4.3 IL_0005: stloc.2 IL_0006: ldloca.s V_4 IL_0008: ldc.i4.0 IL_0009: ldc.i4.3 IL_000a: call ""CustomHandler..ctor(int, int)"" IL_000f: ldloca.s V_4 IL_0011: ldloc.0 IL_0012: box ""int"" IL_0017: ldc.i4.0 IL_0018: ldnull IL_0019: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_001e: ldloca.s V_4 IL_0020: ldloc.1 IL_0021: box ""int"" IL_0026: ldc.i4.0 IL_0027: ldnull IL_0028: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_002d: ldloca.s V_4 IL_002f: ldloc.2 IL_0030: box ""int"" IL_0035: ldc.i4.0 IL_0036: ldnull IL_0037: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_003c: ldloc.s V_4 IL_003e: stloc.3 IL_003f: ldloca.s V_3 IL_0041: constrained. ""CustomHandler"" IL_0047: callvirt ""string object.ToString()"" IL_004c: call ""void System.Console.WriteLine(string)"" IL_0051: ret }"); } [Fact] public void ParenthesizedAdditiveExpression_02() { var code = @" int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int i6 = 6; CustomHandler c = /*<bind>*/((($""{i1}"" + $""{i2}"") + $""{i3}"") + ($""{i4}"" + ($""{i5}"" + $""{i6}""))) + (($""{i1}"" + ($""{i2}"" + $""{i3}"")) + (($""{i4}"" + $""{i5}"") + $""{i6}""))/*</bind>*/; System.Console.WriteLine(c.ToString());"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:0 format: value:2 alignment:0 format: value:3 alignment:0 format: value:4 alignment:0 format: value:5 alignment:0 format: value:6 alignment:0 format: value:1 alignment:0 format: value:2 alignment:0 format: value:3 alignment:0 format: value:4 alignment:0 format: value:5 alignment:0 format: value:6 alignment:0 format: "); verifier.VerifyDiagnostics(); VerifyOperationTreeForTest<BinaryExpressionSyntax>(comp, @" IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Left: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '(($""{i1}"" + ... + $""{i6}""))') Left: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '($""{i1}"" + ... ) + $""{i3}""') Left: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '$""{i1}"" + $""{i2}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i1}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i1}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i1}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i1') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i1 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i1}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i1}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i1}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i1}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i2}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i2}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i2}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i2') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i2') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i2 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i2') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i2}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i2}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i2}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i2}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i3}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i3}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i3}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i3') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i3') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i3 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i3') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i3}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i3}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i3}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i3}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '$""{i4}"" + ( ... + $""{i6}"")') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i4}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i4}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i4}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i4') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i4') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i4 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i4') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i4}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i4}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i4}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i4}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '$""{i5}"" + $""{i6}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i5}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i5}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i5}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i5') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i5') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i5 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i5') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i5}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i5}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i5}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i5}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i6}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i6}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i6}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i6') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i6') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i6 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i6') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i6}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i6}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i6}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i6}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '($""{i1}"" + ... + $""{i6}"")') Left: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '$""{i1}"" + ( ... + $""{i3}"")') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i1}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i1}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i1}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i1') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i1 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i1}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i1}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i1}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i1}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '$""{i2}"" + $""{i3}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i2}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i2}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i2}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i2') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i2') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i2 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i2') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i2}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i2}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i2}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i2}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i3}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i3}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i3}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i3') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i3') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i3 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i3') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i3}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i3}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i3}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i3}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '($""{i4}"" + ... ) + $""{i6}""') Left: IInterpolatedStringAdditionOperation (OperationKind.InterpolatedStringAddition, Type: null) (Syntax: '$""{i4}"" + $""{i5}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i4}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i4}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i4}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i4') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i4') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i4 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i4') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i4}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i4}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i4}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i4}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i5}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i5}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i5}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i5') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i5') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i5 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i5') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i5}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i5}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i5}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i5}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i6}""') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{i6}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Object o, [System.Int32 alignment = 0], [System.String format = null])) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{i6}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'i6') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i6') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILocalReferenceOperation: i6 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i6') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: alignment) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i6}') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '{i6}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: format) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{i6}') IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: '{i6}') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) "); } [Fact] public void ParenthesizedAdditiveExpression_03() { var code = @" int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int i6 = 6; string s = (($""{i1}"" + $""{i2}"") + $""{i3}"") + ($""{i4}"" + ($""{i5}"" + $""{i6}"")); System.Console.WriteLine(s);"; var verifier = CompileAndVerify(code, expectedOutput: @"123456"); verifier.VerifyDiagnostics(); } [Fact] public void ParenthesizedAdditiveExpression_04() { var code = @" using System.Threading.Tasks; int i1 = 2; int i2 = 3; string s = $""{await GetInt()}"" + ($""{i1}"" + $""{i2}""); System.Console.WriteLine(s); Task<int> GetInt() => Task.FromResult(1); "; var verifier = CompileAndVerify(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }, expectedOutput: @" 1value:2 value:3"); verifier.VerifyDiagnostics(); // Note the two DefaultInterpolatedStringHandlers in the IL here. In a future rewrite step in the LocalRewriter, we can potentially // transform the tree to change its shape and pull out all individual Append calls in a sequence (regardless of the level of the tree) // and combine these and other unequal tree shapes. For now, we're going with a simple solution where, if the entire binary expression // cannot be combined, none of it is. verifier.VerifyIL("Program.<<Main>$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 245 (0xf5) .maxstack 4 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter<int> V_2, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4, System.Exception V_5) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_004f IL_000a: ldarg.0 IL_000b: ldc.i4.2 IL_000c: stfld ""int Program.<<Main>$>d__0.<i1>5__2"" IL_0011: ldarg.0 IL_0012: ldc.i4.3 IL_0013: stfld ""int Program.<<Main>$>d__0.<i2>5__3"" IL_0018: call ""System.Threading.Tasks.Task<int> Program.<<Main>$>g__GetInt|0_0()"" IL_001d: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0022: stloc.2 IL_0023: ldloca.s V_2 IL_0025: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_002a: brtrue.s IL_006b IL_002c: ldarg.0 IL_002d: ldc.i4.0 IL_002e: dup IL_002f: stloc.0 IL_0030: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_0035: ldarg.0 IL_0036: ldloc.2 IL_0037: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_003c: ldarg.0 IL_003d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_0042: ldloca.s V_2 IL_0044: ldarg.0 IL_0045: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<<Main>$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<<Main>$>d__0)"" IL_004a: leave IL_00f4 IL_004f: ldarg.0 IL_0050: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_0055: stloc.2 IL_0056: ldarg.0 IL_0057: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<<Main>$>d__0.<>u__1"" IL_005c: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0062: ldarg.0 IL_0063: ldc.i4.m1 IL_0064: dup IL_0065: stloc.0 IL_0066: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_006b: ldloca.s V_2 IL_006d: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0072: stloc.1 IL_0073: ldstr ""{0}"" IL_0078: ldloc.1 IL_0079: box ""int"" IL_007e: call ""string string.Format(string, object)"" IL_0083: ldc.i4.0 IL_0084: ldc.i4.1 IL_0085: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_008a: stloc.3 IL_008b: ldloca.s V_3 IL_008d: ldarg.0 IL_008e: ldfld ""int Program.<<Main>$>d__0.<i1>5__2"" IL_0093: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_0098: ldloca.s V_3 IL_009a: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_009f: ldc.i4.0 IL_00a0: ldc.i4.1 IL_00a1: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" IL_00a6: stloc.s V_4 IL_00a8: ldloca.s V_4 IL_00aa: ldarg.0 IL_00ab: ldfld ""int Program.<<Main>$>d__0.<i2>5__3"" IL_00b0: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted<int>(int)"" IL_00b5: ldloca.s V_4 IL_00b7: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_00bc: call ""string string.Concat(string, string, string)"" IL_00c1: call ""void System.Console.WriteLine(string)"" IL_00c6: leave.s IL_00e1 } catch System.Exception { IL_00c8: stloc.s V_5 IL_00ca: ldarg.0 IL_00cb: ldc.i4.s -2 IL_00cd: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_00d2: ldarg.0 IL_00d3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_00d8: ldloc.s V_5 IL_00da: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_00df: leave.s IL_00f4 } IL_00e1: ldarg.0 IL_00e2: ldc.i4.s -2 IL_00e4: stfld ""int Program.<<Main>$>d__0.<>1__state"" IL_00e9: ldarg.0 IL_00ea: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<Main>$>d__0.<>t__builder"" IL_00ef: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00f4: ret }"); } [Fact] public void ParenthesizedAdditiveExpression_05() { var code = @" int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int i6 = 6; string s = /*<bind>*/((($""{i1}"" + $""{i2}"") + $""{i3}"") + ($""{i4}"" + ($""{i5}"" + $""{i6}""))) + (($""{i1}"" + ($""{i2}"" + $""{i3}"")) + (($""{i4}"" + $""{i5}"") + $""{i6}""))/*</bind>*/; System.Console.WriteLine(s);"; var comp = CreateCompilation(code); var verifier = CompileAndVerify(comp, expectedOutput: @"123456123456"); verifier.VerifyDiagnostics(); VerifyOperationTreeForTest<BinaryExpressionSyntax>(comp, @" IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '((($""{i1}"" ... + $""{i6}""))') Left: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '(($""{i1}"" + ... + $""{i6}""))') Left: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '($""{i1}"" + ... ) + $""{i3}""') Left: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{i1}"" + $""{i2}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i1}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i1}') Expression: ILocalReferenceOperation: i1 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i1') Alignment: null FormatString: null Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i2}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i2}') Expression: ILocalReferenceOperation: i2 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i2') Alignment: null FormatString: null Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i3}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i3}') Expression: ILocalReferenceOperation: i3 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i3') Alignment: null FormatString: null Right: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{i4}"" + ( ... + $""{i6}"")') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i4}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i4}') Expression: ILocalReferenceOperation: i4 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i4') Alignment: null FormatString: null Right: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{i5}"" + $""{i6}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i5}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i5}') Expression: ILocalReferenceOperation: i5 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i5') Alignment: null FormatString: null Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i6}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i6}') Expression: ILocalReferenceOperation: i6 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i6') Alignment: null FormatString: null Right: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '($""{i1}"" + ... + $""{i6}"")') Left: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{i1}"" + ( ... + $""{i3}"")') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i1}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i1}') Expression: ILocalReferenceOperation: i1 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i1') Alignment: null FormatString: null Right: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{i2}"" + $""{i3}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i2}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i2}') Expression: ILocalReferenceOperation: i2 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i2') Alignment: null FormatString: null Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i3}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i3}') Expression: ILocalReferenceOperation: i3 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i3') Alignment: null FormatString: null Right: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '($""{i4}"" + ... ) + $""{i6}""') Left: IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{i4}"" + $""{i5}""') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i4}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i4}') Expression: ILocalReferenceOperation: i4 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i4') Alignment: null FormatString: null Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i5}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i5}') Expression: ILocalReferenceOperation: i5 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i5') Alignment: null FormatString: null Right: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{i6}""') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i6}') Expression: ILocalReferenceOperation: i6 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i6') Alignment: null FormatString: null "); } [ConditionalFact(typeof(CoreClrOnly)), WorkItem("https://github.com/dotnet/roslyn/issues/68834")] public void ParenthesizedAdditiveExpression_06() { var src = """ using System.Runtime.CompilerServices; DefaultInterpolatedStringHandler s1 = $"a" + $"b" + ($"c" + $"-"); System.Console.Write(s1.ToString()); DefaultInterpolatedStringHandler s2 = $"a" + ($"b" + $"c") + $"-"; System.Console.Write(s2.ToString()); DefaultInterpolatedStringHandler s3 = ($"a" + $"b") + $"c" + $"-"; System.Console.Write(s3.ToString()); """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: "abc-abc-abc-").VerifyDiagnostics(); } [Theory] [InlineData(@"$""{1}"", $""{2}""")] [InlineData(@"$""{1}"" + $"""", $""{2}"" + $""""")] public void TupleDeclaration_01(string initializer) { var code = @" (CustomHandler c1, CustomHandler c2) = (" + initializer + @"); System.Console.Write(c1.ToString()); System.Console.WriteLine(c2.ToString());"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:0 format: value:2 alignment:0 format: "); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 89 (0x59) .maxstack 5 .locals init (CustomHandler V_0, //c1 CustomHandler V_1, //c2 CustomHandler V_2) IL_0000: ldloca.s V_2 IL_0002: ldc.i4.0 IL_0003: ldc.i4.1 IL_0004: call ""CustomHandler..ctor(int, int)"" IL_0009: ldloca.s V_2 IL_000b: ldc.i4.1 IL_000c: box ""int"" IL_0011: ldc.i4.0 IL_0012: ldnull IL_0013: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0018: ldloc.2 IL_0019: ldloca.s V_2 IL_001b: ldc.i4.0 IL_001c: ldc.i4.1 IL_001d: call ""CustomHandler..ctor(int, int)"" IL_0022: ldloca.s V_2 IL_0024: ldc.i4.2 IL_0025: box ""int"" IL_002a: ldc.i4.0 IL_002b: ldnull IL_002c: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0031: ldloc.2 IL_0032: stloc.1 IL_0033: stloc.0 IL_0034: ldloca.s V_0 IL_0036: constrained. ""CustomHandler"" IL_003c: callvirt ""string object.ToString()"" IL_0041: call ""void System.Console.Write(string)"" IL_0046: ldloca.s V_1 IL_0048: constrained. ""CustomHandler"" IL_004e: callvirt ""string object.ToString()"" IL_0053: call ""void System.Console.WriteLine(string)"" IL_0058: ret } "); } [Theory] [InlineData(@"$""{1}"", $""{2}""")] [InlineData(@"$""{1}"" + $"""", $""{2}"" + $""""")] public void TupleDeclaration_02(string initializer) { var code = @" (CustomHandler c1, CustomHandler c2) t = (" + initializer + @"); System.Console.Write(t.c1.ToString()); System.Console.WriteLine(t.c2.ToString());"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); var verifier = CompileAndVerify(comp, expectedOutput: @" value:1 alignment:0 format: value:2 alignment:0 format: "); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 104 (0x68) .maxstack 6 .locals init (System.ValueTuple<CustomHandler, CustomHandler> V_0, //t CustomHandler V_1) IL_0000: ldloca.s V_0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.0 IL_0005: ldc.i4.1 IL_0006: call ""CustomHandler..ctor(int, int)"" IL_000b: ldloca.s V_1 IL_000d: ldc.i4.1 IL_000e: box ""int"" IL_0013: ldc.i4.0 IL_0014: ldnull IL_0015: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_001a: ldloc.1 IL_001b: ldloca.s V_1 IL_001d: ldc.i4.0 IL_001e: ldc.i4.1 IL_001f: call ""CustomHandler..ctor(int, int)"" IL_0024: ldloca.s V_1 IL_0026: ldc.i4.2 IL_0027: box ""int"" IL_002c: ldc.i4.0 IL_002d: ldnull IL_002e: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0033: ldloc.1 IL_0034: call ""System.ValueTuple<CustomHandler, CustomHandler>..ctor(CustomHandler, CustomHandler)"" IL_0039: ldloca.s V_0 IL_003b: ldflda ""CustomHandler System.ValueTuple<CustomHandler, CustomHandler>.Item1"" IL_0040: constrained. ""CustomHandler"" IL_0046: callvirt ""string object.ToString()"" IL_004b: call ""void System.Console.Write(string)"" IL_0050: ldloca.s V_0 IL_0052: ldflda ""CustomHandler System.ValueTuple<CustomHandler, CustomHandler>.Item2"" IL_0057: constrained. ""CustomHandler"" IL_005d: callvirt ""string object.ToString()"" IL_0062: call ""void System.Console.WriteLine(string)"" IL_0067: ret } "); } [Theory, WorkItem(55609, "https://github.com/dotnet/roslyn/issues/55609")] [InlineData(@"$""{h1}{h2}""")] [InlineData(@"$""{h1}"" + $""{h2}""")] public void RefStructHandler_DynamicInHole(string expression) { var code = @" dynamic h1 = 1; dynamic h2 = 2; CustomHandler c = " + expression + ";"; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "ref struct", useBoolReturns: false); var comp = CreateCompilationWithCSharp(new[] { code, handler }); comp.VerifyDiagnostics( // 0.cs(4,19): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomHandler'. // CustomHandler c = $"{h1}" + $"{h2}"; Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomHandler").WithLocation(4, 19), // 0.cs(4,19): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomHandler'. // CustomHandler c = $"{h1}" + $"{h2}"; Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomHandler").WithLocation(4, 19) ); } [Theory] [InlineData(@"$""Literal{1}""")] [InlineData(@"$""Literal"" + $""{1}""")] public void ConversionInParamsArguments_01(string expression) { var code = @" using System; using System.Linq; M(" + expression + ", " + expression + @"); void M(params CustomHandler[] handlers) { Console.WriteLine(string.Join(Environment.NewLine, handlers.Select(h => h.ToString()))); } "; var verifier = CompileAndVerify(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, expectedOutput: @" literal:Literal value:1 alignment:0 format: literal:Literal value:1 alignment:0 format: "); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 100 (0x64) .maxstack 7 .locals init (CustomHandler V_0) IL_0000: ldc.i4.2 IL_0001: newarr ""CustomHandler"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldloca.s V_0 IL_000a: ldc.i4.7 IL_000b: ldc.i4.1 IL_000c: call ""CustomHandler..ctor(int, int)"" IL_0011: ldloca.s V_0 IL_0013: ldstr ""Literal"" IL_0018: call ""void CustomHandler.AppendLiteral(string)"" IL_001d: ldloca.s V_0 IL_001f: ldc.i4.1 IL_0020: box ""int"" IL_0025: ldc.i4.0 IL_0026: ldnull IL_0027: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_002c: ldloc.0 IL_002d: stelem ""CustomHandler"" IL_0032: dup IL_0033: ldc.i4.1 IL_0034: ldloca.s V_0 IL_0036: ldc.i4.7 IL_0037: ldc.i4.1 IL_0038: call ""CustomHandler..ctor(int, int)"" IL_003d: ldloca.s V_0 IL_003f: ldstr ""Literal"" IL_0044: call ""void CustomHandler.AppendLiteral(string)"" IL_0049: ldloca.s V_0 IL_004b: ldc.i4.1 IL_004c: box ""int"" IL_0051: ldc.i4.0 IL_0052: ldnull IL_0053: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0058: ldloc.0 IL_0059: stelem ""CustomHandler"" IL_005e: call ""void Program.<<Main>$>g__M|0_0(params CustomHandler[])"" IL_0063: ret } "); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/71488")] public void ConversionInParamsArguments_02() { var code = @" using System; using System.Linq; using System.Runtime.CompilerServices; M("""", $""test""); void M(string s, [InterpolatedStringHandlerArgument(nameof(s))] params CustomHandler[] handlers) { Console.WriteLine(string.Join(Environment.NewLine, handlers.Select(h => h.ToString()))); } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); comp.VerifyEmitDiagnostics( // 0.cs(8,19): error CS8946: 'CustomHandler[]' is not an interpolated string handler type. // void M(string s, [InterpolatedStringHandlerArgument(nameof(s))] params CustomHandler[] handlers) Diagnostic(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, "InterpolatedStringHandlerArgument(nameof(s))").WithArguments("CustomHandler[]").WithLocation(8, 19) ); } [Theory] [InlineData("static")] [InlineData("")] public void ArgumentsOnLocalFunctions_01(string mod) { var code = @" using System.Runtime.CompilerServices; M($""""); " + mod + @" void M([InterpolatedStringHandlerArgument("""")] CustomHandler c) { } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (4,3): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 3), // (6,10): error CS8944: 'M(CustomHandler)' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // void M([InterpolatedStringHandlerArgument("")] CustomHandler c) { } Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgument("""")").WithArguments("M(CustomHandler)").WithLocation(6, 10 + mod.Length) ); } [Theory] [InlineData("static")] [InlineData("")] public void ArgumentsOnLocalFunctions_02(string mod) { var code = @" using System; using System.Runtime.CompilerServices; M(1, $""""); " + mod + @" void M(int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) : this(literalLength, formattedCount) => _builder.Append(""i:"" + i.ToString()); } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: @"i:1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 17 (0x11) .maxstack 4 .locals init (int V_0) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" IL_000b: call ""void Program.<<Main>$>g__M|0_0(int, CustomHandler)"" IL_0010: ret } "); } [Theory] [InlineData("static")] [InlineData("")] public void ArgumentsOnLambdas_01(string mod) { var code = @" using System.Runtime.CompilerServices; var a = " + mod + @" ([InterpolatedStringHandlerArgument("""")] CustomHandler c) => { }; a($""""); "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (4,12): warning CS8971: InterpolatedStringHandlerArgument has no effect when applied to lambda parameters and will be ignored at the call site. // var a = ([InterpolatedStringHandlerArgument("")] CustomHandler c) => { }; Diagnostic(ErrorCode.WRN_InterpolatedStringHandlerArgumentAttributeIgnoredOnLambdaParameters, @"InterpolatedStringHandlerArgument("""")").WithLocation(4, 12 + mod.Length), // (4,12): error CS8944: 'lambda expression' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // var a = ([InterpolatedStringHandlerArgument("")] CustomHandler c) => { }; Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgument("""")").WithArguments("lambda expression").WithLocation(4, 12 + mod.Length) ); } [Theory] [InlineData("static")] [InlineData("")] public void ArgumentsOnLambdas_02(string mod) { var code = @" using System; using System.Runtime.CompilerServices; var a = " + mod + @" (int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); a(1, $""""); partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) => throw null; } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: @""); verifier.VerifyDiagnostics( // (5,19): warning CS8971: InterpolatedStringHandlerArgument has no effect when applied to lambda parameters and will be ignored at the call site. // var a = (int i, [InterpolatedStringHandlerArgument("i")] CustomHandler c) => Console.WriteLine(c.ToString()); Diagnostic(ErrorCode.WRN_InterpolatedStringHandlerArgumentAttributeIgnoredOnLambdaParameters, @"InterpolatedStringHandlerArgument(""i"")").WithLocation(5, 19 + mod.Length) ); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 45 (0x2d) .maxstack 4 IL_0000: ldsfld ""System.Action<int, CustomHandler> Program.<>c.<>9__0_0"" IL_0005: dup IL_0006: brtrue.s IL_001f IL_0008: pop IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" IL_000e: ldftn ""void Program.<>c.<<Main>$>b__0_0(int, CustomHandler)"" IL_0014: newobj ""System.Action<int, CustomHandler>..ctor(object, System.IntPtr)"" IL_0019: dup IL_001a: stsfld ""System.Action<int, CustomHandler> Program.<>c.<>9__0_0"" IL_001f: ldc.i4.1 IL_0020: ldc.i4.0 IL_0021: ldc.i4.0 IL_0022: newobj ""CustomHandler..ctor(int, int)"" IL_0027: callvirt ""void System.Action<int, CustomHandler>.Invoke(int, CustomHandler)"" IL_002c: ret } "); } [Fact] public void ArgumentsOnDelegateTypes_01() { var code = @" using System.Runtime.CompilerServices; M m = null; m($""""); delegate void M([InterpolatedStringHandlerArgument("""")] CustomHandler c); "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (6,3): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // m($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(6, 3), // (8,18): error CS8944: 'M.Invoke(CustomHandler)' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // delegate void M([InterpolatedStringHandlerArgument("")] CustomHandler c); Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgument("""")").WithArguments("M.Invoke(CustomHandler)").WithLocation(8, 18) ); } [Fact] public void ArgumentsOnDelegateTypes_02() { var vbCode = @" Imports System.Runtime.CompilerServices Public Delegate Sub M(<InterpolatedStringHandlerArgument("""")> c As CustomHandler) <InterpolatedStringHandler> Public Structure CustomHandler End Structure "; var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); vbComp.VerifyDiagnostics(); var code = @" M m = null; m($""""); "; var comp = CreateCompilation(code, references: new[] { vbComp.EmitToImageReference() }); comp.VerifyDiagnostics( // (4,3): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // m($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(4, 3), // (4,3): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments // m($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(4, 3), // (4,3): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments // m($""); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(4, 3) ); } [Fact] public void ArgumentsOnDelegateTypes_03() { var code = @" using System; using System.Runtime.CompilerServices; M m = (i, c) => Console.WriteLine(c.ToString()); m(1, $""""); delegate void M(int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c); partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) : this(literalLength, formattedCount) => _builder.Append(""i:"" + i.ToString()); } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: @"i:1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 48 (0x30) .maxstack 5 .locals init (int V_0) IL_0000: ldsfld ""M Program.<>c.<>9__0_0"" IL_0005: dup IL_0006: brtrue.s IL_001f IL_0008: pop IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" IL_000e: ldftn ""void Program.<>c.<<Main>$>b__0_0(int, CustomHandler)"" IL_0014: newobj ""M..ctor(object, System.IntPtr)"" IL_0019: dup IL_001a: stsfld ""M Program.<>c.<>9__0_0"" IL_001f: ldc.i4.1 IL_0020: stloc.0 IL_0021: ldloc.0 IL_0022: ldc.i4.0 IL_0023: ldc.i4.0 IL_0024: ldloc.0 IL_0025: newobj ""CustomHandler..ctor(int, int, int)"" IL_002a: callvirt ""void M.Invoke(int, CustomHandler)"" IL_002f: ret } "); } [Fact] public void HandlerConstructorWithDefaultArgument_01() { var code = @" using System; using System.Runtime.CompilerServices; C.M($""""); class C { public static void M(CustomHandler c) => Console.WriteLine(c.ToString()); } [InterpolatedStringHandler] partial struct CustomHandler { private int _i = 0; public CustomHandler(int literalLength, int formattedCount, int i = 1) => _i = i; public override string ToString() => _i.ToString(); } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }, expectedOutput: @"1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 14 (0xe) .maxstack 3 IL_0000: ldc.i4.0 IL_0001: ldc.i4.0 IL_0002: ldc.i4.1 IL_0003: newobj ""CustomHandler..ctor(int, int, int)"" IL_0008: call ""void C.M(CustomHandler)"" IL_000d: ret } "); } [Fact] public void HandlerConstructorWithDefaultArgument_02() { var code = @" using System; using System.Runtime.CompilerServices; C.M($""Literal""); class C { public static void M(CustomHandler c) => Console.WriteLine(c.ToString()); } [InterpolatedStringHandler] partial struct CustomHandler { private string _s = null; public CustomHandler(int literalLength, int formattedCount, out bool isValid, int i = 1) { _s = i.ToString(); isValid = false; } public void AppendLiteral(string s) => _s += s; public override string ToString() => _s; } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }, expectedOutput: @"1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 33 (0x21) .maxstack 4 .locals init (CustomHandler V_0, bool V_1) IL_0000: ldc.i4.7 IL_0001: ldc.i4.0 IL_0002: ldloca.s V_1 IL_0004: ldc.i4.1 IL_0005: newobj ""CustomHandler..ctor(int, int, out bool, int)"" IL_000a: stloc.0 IL_000b: ldloc.1 IL_000c: brfalse.s IL_001a IL_000e: ldloca.s V_0 IL_0010: ldstr ""Literal"" IL_0015: call ""void CustomHandler.AppendLiteral(string)"" IL_001a: ldloc.0 IL_001b: call ""void C.M(CustomHandler)"" IL_0020: ret } "); } [Fact] public void HandlerConstructorWithDefaultArgument_03() { var code = @" using System; using System.Runtime.CompilerServices; C.M(1, $""""); class C { public static void M(int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); } [InterpolatedStringHandler] partial struct CustomHandler { private string _s = null; public CustomHandler(int literalLength, int formattedCount, int i1, int i2 = 2) { _s = i1.ToString() + i2.ToString(); } public void AppendLiteral(string s) => _s += s; public override string ToString() => _s; } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }, expectedOutput: @"12"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 18 (0x12) .maxstack 5 .locals init (int V_0) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: ldc.i4.2 IL_0007: newobj ""CustomHandler..ctor(int, int, int, int)"" IL_000c: call ""void C.M(int, CustomHandler)"" IL_0011: ret } "); } [Fact] public void HandlerConstructorWithDefaultArgument_04() { var code = @" using System; using System.Runtime.CompilerServices; C.M(1, $""Literal""); class C { public static void M(int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); } [InterpolatedStringHandler] partial struct CustomHandler { private string _s = null; public CustomHandler(int literalLength, int formattedCount, int i1, out bool isValid, int i2 = 2) { _s = i1.ToString() + i2.ToString(); isValid = false; } public void AppendLiteral(string s) => _s += s; public override string ToString() => _s; } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }, expectedOutput: @"12"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 37 (0x25) .maxstack 6 .locals init (int V_0, CustomHandler V_1, bool V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.7 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: ldloca.s V_2 IL_0008: ldc.i4.2 IL_0009: newobj ""CustomHandler..ctor(int, int, int, out bool, int)"" IL_000e: stloc.1 IL_000f: ldloc.2 IL_0010: brfalse.s IL_001e IL_0012: ldloca.s V_1 IL_0014: ldstr ""Literal"" IL_0019: call ""void CustomHandler.AppendLiteral(string)"" IL_001e: ldloc.1 IL_001f: call ""void C.M(int, CustomHandler)"" IL_0024: ret } "); } [Fact] public void HandlerExtensionMethod_01() { var code = @" $""Test"".M(); public static class StringExt { public static void M(this CustomHandler handler) => throw null; } "; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (2,1): error CS1929: 'string' does not contain a definition for 'M' and the best extension method overload 'StringExt.M(CustomHandler)' requires a receiver of type 'CustomHandler' // $"Test".M(); Diagnostic(ErrorCode.ERR_BadInstanceArgType, @"$""Test""").WithArguments("string", "M", "StringExt.M(CustomHandler)", "CustomHandler").WithLocation(2, 1) ); } [Fact] public void HandlerExtensionMethod_02() { var code = @" using System.Runtime.CompilerServices; var s = new S1(); s.M($""""); public struct S1 { public S1() { } public int Field = 1; } public static class S1Ext { public static void M(this S1 s, [InterpolatedStringHandlerArgument("""")] CustomHandler c) => throw null; } partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, S1 s) => throw null; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (5,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler c' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. // s.M($""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler c", "CustomHandler").WithLocation(5, 5), // (15,38): error CS8944: 'S1Ext.M(S1, CustomHandler)' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // public static void M(this S1 s, [InterpolatedStringHandlerArgument("")] CustomHandler c) => throw null; Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgument("""")").WithArguments("S1Ext.M(S1, CustomHandler)").WithLocation(15, 38) ); } [Fact] public void HandlerExtensionMethod_03() { var code = @" using System; using System.Runtime.CompilerServices; var s = new S1(); s.M($""""); public struct S1 { public S1() { } public int Field = 1; } public static class S1Ext { public static void M(this S1 s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler c) => Console.WriteLine(c.ToString()); } partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, S1 s) : this(literalLength, formattedCount) => _builder.Append(""s.Field:"" + s.Field); } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: "s.Field:1"); verifier.VerifyDiagnostics(); } [Fact] public void HandlerExtensionMethod_04() { var code = @" using System; using System.Runtime.CompilerServices; var s = new S1(); s.M($""""); public struct S1 { public S1() { } public int Field = 1; } public static class S1Ext { public static void M(ref this S1 s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler c) => Console.WriteLine(s.Field); } partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref S1 s) : this(literalLength, formattedCount) => s.Field = 2; } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: "2"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 25 (0x19) .maxstack 4 .locals init (S1 V_0, //s S1& V_1) IL_0000: ldloca.s V_0 IL_0002: call ""S1..ctor()"" IL_0007: ldloca.s V_0 IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: ldc.i4.0 IL_000c: ldc.i4.0 IL_000d: ldloc.1 IL_000e: newobj ""CustomHandler..ctor(int, int, ref S1)"" IL_0013: call ""void S1Ext.M(ref S1, CustomHandler)"" IL_0018: ret } "); } [Theory, CombinatorialData] public void HandlerExtensionMethod_05([CombinatorialValues("in", "ref readonly")] string modifier) { var code = @" using System; using System.Runtime.CompilerServices; var s = new S1(); s.M($""""); public struct S1 { public S1() { } public int Field = 1; } public static class S1Ext { public static void M(" + modifier + @" this S1 s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler c) => Console.WriteLine(c.ToString()); } partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, " + modifier + @" S1 s) : this(literalLength, formattedCount) => _builder.Append(""s.Field:"" + s.Field); } "; var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: "s.Field:1"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 25 (0x19) .maxstack 4 .locals init (S1 V_0, //s S1& V_1) IL_0000: ldloca.s V_0 IL_0002: call ""S1..ctor()"" IL_0007: ldloca.s V_0 IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: ldc.i4.0 IL_000c: ldc.i4.0 IL_000d: ldloc.1 IL_000e: newobj ""CustomHandler..ctor(int, int, " + modifier + @" S1)"" IL_0013: call ""void S1Ext.M(" + modifier + @" S1, CustomHandler)"" IL_0018: ret } "); } [Fact] public void HandlerExtensionMethod_06() { var code = @" using System.Runtime.CompilerServices; var s = new S1(); s.M($""""); public struct S1 { public S1() { } public int Field = 1; } public static class S1Ext { public static void M(in this S1 s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler c) => throw null; } partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, ref S1 s) => throw null; } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (5,1): error CS1620: Argument 3 must be passed with the 'ref' keyword // s.M($""); Diagnostic(ErrorCode.ERR_BadArgRef, "s").WithArguments("3", "ref").WithLocation(5, 1) ); } [Fact] public void HandlerExtensionMethod_07() { var code = @" using System.Runtime.CompilerServices; var s = new S1(); s.M($""text""); public struct S1 { public S1() { } public int Field = 1; } public static class S1Ext { public static void M(ref this S1 s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler c) => System.Console.WriteLine(c.ToString()); } partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, in S1 s) : this(literalLength, formattedCount) { } } "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, parseOptions: TestOptions.Regular11); comp.VerifyDiagnostics( // 0.cs(5,1): error CS9194: Argument 3 may not be passed with the 'ref' keyword in language version 11.0. To pass 'ref' arguments to 'in' parameters, upgrade to language version 12.0 or greater. // s.M($"text"); Diagnostic(ErrorCode.ERR_BadArgExtraRefLangVersion, "s").WithArguments("3", "11.0", "12.0").WithLocation(5, 1) ); var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, expectedOutput: "literal:text"); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 39 (0x27) .maxstack 4 .locals init (S1 V_0, //s S1& V_1, CustomHandler V_2) IL_0000: ldloca.s V_0 IL_0002: call "S1..ctor()" IL_0007: ldloca.s V_0 IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: ldc.i4.4 IL_000c: ldc.i4.0 IL_000d: ldloc.1 IL_000e: newobj "CustomHandler..ctor(int, int, in S1)" IL_0013: stloc.2 IL_0014: ldloca.s V_2 IL_0016: ldstr "text" IL_001b: call "void CustomHandler.AppendLiteral(string)" IL_0020: ldloc.2 IL_0021: call "void S1Ext.M(ref S1, CustomHandler)" IL_0026: ret } """); } [Fact] public void NoStandaloneConstructor() { var code = @" using System.Runtime.CompilerServices; CustomHandler c = $""""; [InterpolatedStringHandler] struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, string s) {} } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }); comp.VerifyDiagnostics( // (4,19): error CS7036: There is no argument given that corresponds to the required parameter 's' of 'CustomHandler.CustomHandler(int, int, string)' // CustomHandler c = $""; Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("s", "CustomHandler.CustomHandler(int, int, string)").WithLocation(4, 19), // (4,19): error CS1615: Argument 3 may not be passed with the 'out' keyword // CustomHandler c = $""; Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""""").WithArguments("3", "out").WithLocation(4, 19) ); } [Theory] [InlineData(@"$""literal{1}""")] [InlineData(@"$""literal"" + $""{1}""")] public void ReferencingThis_TopLevelObjectInitializer(string expression) { var code = @" using System.Runtime.CompilerServices; _ = new C2 { [" + expression + @"] = { A = 1, B = 2 } }; public class C2 { public C3 this[[InterpolatedStringHandlerArgument("""")] CustomHandler c] { get => new C3(); set { } } } public class C3 { public int A { get => 0; set { } } public int B { get => 0; set { } } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C2 c) : this(literalLength, formattedCount) { } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (4,15): error CS8976: Interpolated string handler conversions that reference the instance being indexed cannot be used in indexer member initializers. // _ = new C2 { [$"literal" + $"{1}"] = { A = 1, B = 2 } }; Diagnostic(ErrorCode.ERR_InterpolatedStringsReferencingInstanceCannotBeInObjectInitializers, expression).WithLocation(4, 15) ); } [Theory] [InlineData(@"$""literal{1}""")] [InlineData(@"$""literal"" + $""{1}""")] public void ReferencingThis_NestedObjectInitializer(string expression) { var code = @" using System; using System.Runtime.CompilerServices; _ = new C1 { C2 = { [" + expression + @"] = { A = 1, B = 2 } } }; class C1 { public C2 C2 { get => null; set { } } } public class C2 { public C3 this[[InterpolatedStringHandlerArgument("""")] CustomHandler c] { get => new C3(); set { } } } public class C3 { public int A { get => 0; set { } } public int B { get => 0; set { } } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C2 c) : this(literalLength, formattedCount) { Console.WriteLine(""CustomHandler ctor""); } } "; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }); comp.VerifyDiagnostics( // (5,22): error CS8976: Interpolated string handler conversions that reference the instance being indexed cannot be used in indexer member initializers. // _ = new C1 { C2 = { [$"literal{1}"] = { A = 1, B = 2 } } }; Diagnostic(ErrorCode.ERR_InterpolatedStringsReferencingInstanceCannotBeInObjectInitializers, expression).WithLocation(5, 22) ); } [Theory] [InlineData(@"$""literal{1}""")] [InlineData(@"$""literal"" + $""{1}""")] public void NotReferencingThis_NestedObjectInitializer(string expression) { var code = @" using System; using System.Runtime.CompilerServices; _ = new C1 { C2 = { [3, " + expression + @"] = { A = 1, B = 2 } } }; class C1 { public C2 C2 { get { Console.WriteLine(""get_C2""); return new C2(); } set { } } } public class C2 { public C3 this[int arg1, [InterpolatedStringHandlerArgument(""arg1"")] CustomHandler c] { get { Console.WriteLine(""Indexer""); return new C3(); } set { } } } public class C3 { public int A { get => 0; set { } } public int B { get => 0; set { } } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int arg1) : this(literalLength, formattedCount) { Console.WriteLine(""CustomHandler ctor""); } } "; CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, expectedOutput: @" CustomHandler ctor get_C2 Indexer get_C2 Indexer"); } [Fact] public void InterpolatedStringBeforeCSharp6() { var text = @" class C { string M() { return $""hello""; } }"; CreateCompilation(text, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)).VerifyDiagnostics( // (6,16): error CS8026: Feature 'interpolated strings' is not available in C# 5. Please use language version 6 or greater. // return $"hello"; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, @"$""hello""").WithArguments("interpolated strings", "6").WithLocation(6, 16)); } [Fact] public void InterpolatedStringWithReplacementBeforeCSharp6() { var text = @" class C { string M() { string other = ""world""; return $""hello + {other}""; } }"; CreateCompilation(text, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)).VerifyDiagnostics( // (7,16): error CS8026: Feature 'interpolated strings' is not available in C# 5. Please use language version 6 or greater. // return $"hello + {other}"; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, @"$""hello + {other}""").WithArguments("interpolated strings", "6").WithLocation(7, 16)); } [Fact, WorkItem(1566008, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1566008")] public void InterpolatedStringInForeach_HasErrors() { var text = """ int i = 1; /*<bind>*/foreach (($"{i}") in new int[0]) {}/*</bind>*/ """; var comp = CreateCompilation(text).VerifyDiagnostics( // (1,5): warning CS0219: The variable 'i' is assigned but its value is never used // int i = 1; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "i").WithArguments("i").WithLocation(1, 5), // (2,29): error CS0230: Type and identifier are both required in a foreach statement // /*<bind>*/foreach (($"{i}") in new int[0]) {}/*</bind>*/ Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(2, 29) ); VerifyOperationTreeForTest<ForEachVariableStatementSyntax>(comp, expectedOperationTree: """ IForEachLoopOperation (LoopKind.ForEach, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'foreach (($ ... int[0]) {}') LoopControlVariable: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$"{i}"') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i}') Expression: ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') Alignment: null FormatString: null Collection: IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Int32[]) (Syntax: 'new int[0]') Dimension Sizes(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Initializer: null Body: IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{}') NextVariables(0) """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/66235")] [InlineData(@"""""")] [InlineData(@"@""""")] [InlineData(@"""""""test""""""")] public void ConvertStringLiteralToInterpolatedStringHandlerError_ByVal(string stringLiteral) { var code = $$""" class C { void M() { ByVal({{stringLiteral}}); ByVal(ref {{stringLiteral}}); ByVal(in {{stringLiteral}}); ByVal(out {{stringLiteral}}); } void ByVal(System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { } } """; CreateCompilation(code, targetFramework: TargetFramework.Net60).VerifyDiagnostics( // (5,15): error CS9205: Expected interpolated string // ByVal(""); Diagnostic(ErrorCode.ERR_ExpectedInterpolatedString, stringLiteral).WithLocation(5, 15), // (6,19): error CS1510: A ref or out value must be an assignable variable // ByVal(ref ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(6, 19), // (7,18): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // ByVal(in ""); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, stringLiteral).WithLocation(7, 18), // (8,19): error CS1510: A ref or out value must be an assignable variable // ByVal(out ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(8, 19) ); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/66235")] [InlineData(@"""""")] [InlineData(@"@""""")] [InlineData(@"""""""test""""""")] public void ConvertStringLiteralToInterpolatedStringHandlerError_ByRef(string stringLiteral) { var code = $$""" class C { void M() { ByRef({{stringLiteral}}); ByRef(ref {{stringLiteral}}); ByRef(in {{stringLiteral}}); ByRef(out {{stringLiteral}}); } void ByRef(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { } } """; CreateCompilation(code, targetFramework: TargetFramework.Net60).VerifyDiagnostics( // (5,15): error CS9205: Expected interpolated string // ByRef(""); Diagnostic(ErrorCode.ERR_ExpectedInterpolatedString, stringLiteral).WithLocation(5, 15), // (6,19): error CS1510: A ref or out value must be an assignable variable // ByRef(ref ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(6, 19), // (7,18): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // ByRef(in ""); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, stringLiteral).WithLocation(7, 18), // (8,19): error CS1510: A ref or out value must be an assignable variable // ByRef(out ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(8, 19) ); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/66235")] [InlineData(@"""""")] [InlineData(@"@""""")] [InlineData(@"""""""test""""""")] public void ConvertStringLiteralToInterpolatedStringHandlerError_ByIn(string stringLiteral) { var code = $$""" class C { void M() { ByIn({{stringLiteral}}); ByIn(ref {{stringLiteral}}); ByIn(in {{stringLiteral}}); ByIn(out {{stringLiteral}}); } void ByIn(in System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { } } """; CreateCompilation(code, targetFramework: TargetFramework.Net60).VerifyDiagnostics( // (5,14): error CS9205: Expected interpolated string // ByIn(""); Diagnostic(ErrorCode.ERR_ExpectedInterpolatedString, stringLiteral).WithLocation(5, 14), // (6,18): error CS1510: A ref or out value must be an assignable variable // ByIn(ref ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(6, 18), // (7,17): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // ByIn(in ""); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, stringLiteral).WithLocation(7, 17), // (8,18): error CS1510: A ref or out value must be an assignable variable // ByIn(out ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(8, 18) ); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/66235")] [InlineData(@"""""")] [InlineData(@"@""""")] [InlineData(@"""""""test""""""")] public void ConvertStringLiteralToInterpolatedStringHandlerError_ByOut(string stringLiteral) { var code = $$""" class C { void M() { ByOut({{stringLiteral}}); ByOut(ref {{stringLiteral}}); ByOut(in {{stringLiteral}}); ByOut(out {{stringLiteral}}); } void ByOut(out System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { s = default; } } """; CreateCompilation(code, targetFramework: TargetFramework.Net60).VerifyDiagnostics( // (5,15): error CS1620: Argument 1 must be passed with the 'out' keyword // ByOut(""); Diagnostic(ErrorCode.ERR_BadArgRef, stringLiteral).WithArguments("1", "out").WithLocation(5, 15), // (6,19): error CS1510: A ref or out value must be an assignable variable // ByOut(ref ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(6, 19), // (7,18): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // ByOut(in ""); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, stringLiteral).WithLocation(7, 18), // (8,19): error CS1510: A ref or out value must be an assignable variable // ByOut(out ""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, stringLiteral).WithLocation(8, 19) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66235")] public void ConvertStringLiteralToInterpolatedStringHandlerError_NotStringLiteral() { var code = """ class C { void M(string s) { ByVal(s); ByRef(s); ByIn(s); } void ByVal(System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { } void ByRef(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { } void ByIn(in System.Runtime.CompilerServices.DefaultInterpolatedStringHandler s) { } } """; CreateCompilation(code, targetFramework: TargetFramework.Net60).VerifyDiagnostics( // (5,15): error CS1503: Argument 1: cannot convert from 'string' to 'System.Runtime.CompilerServices.DefaultInterpolatedStringHandler' // ByVal(s); Diagnostic(ErrorCode.ERR_BadArgType, "s").WithArguments("1", "string", "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler").WithLocation(5, 15), // (6,15): error CS1620: Argument 1 must be passed with the 'ref' keyword // ByRef(s); Diagnostic(ErrorCode.ERR_BadArgRef, "s").WithArguments("1", "ref").WithLocation(6, 15), // (7,14): error CS1503: Argument 1: cannot convert from 'string' to 'System.Runtime.CompilerServices.DefaultInterpolatedStringHandler' // ByIn(s); Diagnostic(ErrorCode.ERR_BadArgType, "s").WithArguments("1", "string", "in System.Runtime.CompilerServices.DefaultInterpolatedStringHandler").WithLocation(7, 14)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68110")] public void DefaultSyntaxValueReentrancy_01() { var source = """ #nullable enable [A(3, X = 6)] public struct A { public int X; public A(int x, A a = $"{1}") { } } """; var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp); var a = compilation.GlobalNamespace.GetTypeMember("A").InstanceConstructors.Where(c => !c.IsDefaultValueTypeConstructor()).Single(); Assert.Null(a.Parameters[1].ExplicitDefaultValue); Assert.True(a.Parameters[1].HasExplicitDefaultValue); compilation.VerifyDiagnostics( // (3,2): error CS0616: 'A' is not an attribute class // [A(3, X = 6)] Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "A").WithArguments("A").WithLocation(3, 2), // (3,2): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [A(3, X = 6)] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A(3, X = 6)").WithLocation(3, 2), // (8,27): error CS1736: Default parameter value for 'a' must be a compile-time constant // public A(int x, A a = $"{1}") { } Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, @"$""{1}""").WithArguments("a").WithLocation(8, 27)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68110")] public void DefaultSyntaxValueReentrancy_02() { var source = """ #nullable enable [A(3, X = 6)] public struct A { public int X; public A(int x, A a = M($"{1}")) { } public static void M(ref A a) {} } """; var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp); var a = compilation.GlobalNamespace.GetTypeMember("A").InstanceConstructors.Where(c => !c.IsDefaultValueTypeConstructor()).Single(); Assert.Null(a.Parameters[1].ExplicitDefaultValue); Assert.True(a.Parameters[1].HasExplicitDefaultValue); compilation.VerifyDiagnostics( // (3,2): error CS0616: 'A' is not an attribute class // [A(3, X = 6)] Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "A").WithArguments("A").WithLocation(3, 2), // (3,2): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [A(3, X = 6)] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A(3, X = 6)").WithLocation(3, 2), // (8,29): error CS1620: Argument 1 must be passed with the 'ref' keyword // public A(int x, A a = M($"{1}")) { } Diagnostic(ErrorCode.ERR_BadArgRef, @"$""{1}""").WithArguments("1", "ref").WithLocation(8, 29)); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/73667")] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ThisParameter_ReferenceTypeReferenceReceiver( [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = $$""" using System; using System.Runtime.CompilerServices; C c = new C(5); ref C c2 = ref c; c2.M({{expression}}, c2 = null); public class C { public int Prop { get; } public C(int i) => Prop = i; public void M([InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c, C cNull) { Console.WriteLine(cNull is null ? "cNull" : null); Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { _builder.AppendLine("c.Prop:" + c.Prop.ToString()); } } """; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: """ cNull c.Prop:5 literal:literal """); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 52 (0x34) .maxstack 5 .locals init (C V_0, //c C& V_1, //c2 C V_2, CustomHandler V_3, C V_4) IL_0000: ldc.i4.5 IL_0001: newobj "C..ctor(int)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: ldind.ref IL_000c: stloc.2 IL_000d: ldloc.2 IL_000e: ldloca.s V_3 IL_0010: ldc.i4.7 IL_0011: ldc.i4.0 IL_0012: ldloc.2 IL_0013: call "CustomHandler..ctor(int, int, C)" IL_0018: ldloca.s V_3 IL_001a: ldstr "literal" IL_001f: call "bool CustomHandler.AppendLiteral(string)" IL_0024: pop IL_0025: ldloc.3 IL_0026: ldloc.1 IL_0027: ldnull IL_0028: dup IL_0029: stloc.s V_4 IL_002b: stind.ref IL_002c: ldloc.s V_4 IL_002e: callvirt "void C.M(CustomHandler, C)" IL_0033: ret } """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/73667")] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ThisParameter_ReferenceTypeReferenceReceiver_ReverseOrder( [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = $$""" using System; using System.Runtime.CompilerServices; C c = new C(5); ref C c2 = ref c; c2.M(c2 = null, {{expression}}); public class C { public int Prop { get; } public C(int i) => Prop = i; public void M(C cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) { Console.WriteLine(cNull is null ? "cNull" : null); Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { _builder.AppendLine("c.Prop:" + c.Prop.ToString()); } } """; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); var verifier = CompileAndVerify(comp, expectedOutput: """ cNull c.Prop:5 literal:literal """); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 51 (0x33) .maxstack 6 .locals init (C V_0, //c C& V_1, //c2 C V_2, C V_3, CustomHandler V_4) IL_0000: ldc.i4.5 IL_0001: newobj "C..ctor(int)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: ldind.ref IL_000c: stloc.2 IL_000d: ldloc.2 IL_000e: ldloc.1 IL_000f: ldnull IL_0010: dup IL_0011: stloc.3 IL_0012: stind.ref IL_0013: ldloc.3 IL_0014: ldloca.s V_4 IL_0016: ldc.i4.7 IL_0017: ldc.i4.0 IL_0018: ldloc.2 IL_0019: call "CustomHandler..ctor(int, int, C)" IL_001e: ldloca.s V_4 IL_0020: ldstr "literal" IL_0025: call "bool CustomHandler.AppendLiteral(string)" IL_002a: pop IL_002b: ldloc.s V_4 IL_002d: callvirt "void C.M(C, CustomHandler)" IL_0032: ret } """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/73667")] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ThisParameter_InterfaceConstrainedRefReceiver( [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = $$""" using System; using System.Runtime.CompilerServices; class Program { static void Main() { C c = new C(5); Test(ref c); } static void Test<T>(ref T c2) where T : I1 { c2.M(c2 = default, {{expression}}); } } public interface I1 { int Prop { get; } void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c); } public class C : I1 { public int Prop { get; } public C(int i) => Prop = i; public void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) { Console.WriteLine(cNull is null); Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, I1 c) : this(literalLength, formattedCount) { _builder.AppendLine("c.Prop:" + c.Prop.ToString()); } } """; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: """ True c.Prop:5 literal:literal """); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Test<T>(ref T)", """ { // Code size 104 (0x68) .maxstack 6 .locals init (T& V_0, T V_1, T& V_2, T V_3, CustomHandler V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_3 IL_0005: initobj "T" IL_000b: ldloc.3 IL_000c: box "T" IL_0011: brtrue.s IL_001e IL_0013: ldloc.2 IL_0014: ldobj "T" IL_0019: stloc.1 IL_001a: ldloca.s V_1 IL_001c: br.s IL_001f IL_001e: ldloc.2 IL_001f: stloc.0 IL_0020: ldloc.0 IL_0021: ldarg.0 IL_0022: ldloca.s V_3 IL_0024: initobj "T" IL_002a: ldloc.3 IL_002b: dup IL_002c: stloc.3 IL_002d: stobj "T" IL_0032: ldloc.3 IL_0033: box "T" IL_0038: ldloca.s V_4 IL_003a: ldc.i4.7 IL_003b: ldc.i4.0 IL_003c: ldloc.0 IL_003d: ldobj "T" IL_0042: box "T" IL_0047: call "CustomHandler..ctor(int, int, I1)" IL_004c: ldloca.s V_4 IL_004e: ldstr "literal" IL_0053: call "bool CustomHandler.AppendLiteral(string)" IL_0058: pop IL_0059: ldloc.s V_4 IL_005b: constrained. "T" IL_0061: callvirt "void I1.M(I1, CustomHandler)" IL_0066: nop IL_0067: ret } """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/73667")] [CombinatorialData] public void InterpolatedStringHandlerArgumentAttribute_ThisParameter_InterfaceAndClassConstrainedRefReceiver( [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = $$""" using System; using System.Runtime.CompilerServices; class Program { static void Main() { C c = new C(5); Test(ref c); } static void Test<T>(ref T c2) where T : class, I1 { c2.M(c2 = default, {{expression}}); } } public interface I1 { int Prop { get; } void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c); } public class C : I1 { public int Prop { get; } public C(int i) => Prop = i; public void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) { Console.WriteLine(cNull is null); Console.WriteLine(c.ToString()); } } public partial struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, I1 c) : this(literalLength, formattedCount) { _builder.AppendLine("c.Prop:" + c.Prop.ToString()); } } """; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: """ True c.Prop:5 literal:literal """); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Test<T>(ref T)", """ { // Code size 73 (0x49) .maxstack 6 .locals init (T V_0, T V_1, CustomHandler V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: box "T" IL_000e: ldarg.0 IL_000f: ldloca.s V_1 IL_0011: initobj "T" IL_0017: ldloc.1 IL_0018: dup IL_0019: stloc.1 IL_001a: stobj "T" IL_001f: ldloc.1 IL_0020: box "T" IL_0025: ldloca.s V_2 IL_0027: ldc.i4.7 IL_0028: ldc.i4.0 IL_0029: ldloc.0 IL_002a: box "T" IL_002f: call "CustomHandler..ctor(int, int, I1)" IL_0034: ldloca.s V_2 IL_0036: ldstr "literal" IL_003b: call "bool CustomHandler.AppendLiteral(string)" IL_0040: pop IL_0041: ldloc.2 IL_0042: callvirt "void I1.M(I1, CustomHandler)" IL_0047: nop IL_0048: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74427")] public void InterpolatedStringAsInputToUserDefinedConversion_01() { var source = """ class C { static void Main() { var y = (C1)$"dog"; // works System.Console.WriteLine(y); } } class C1 { System.FormattableString x; public C1(System.FormattableString x) { this.x = x; } public static implicit operator C1(System.FormattableString x) => new C1(x); public override string ToString() { return ("C1:") + x.ToString(); } } """; CompileAndVerify(source, expectedOutput: "C1:dog"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74427")] public void InterpolatedStringAsInputToUserDefinedConversion_02() { var source = """ class C { static void Main() { var y = (C1)$"dog"; System.Console.WriteLine(y); } } class C1 { System.IFormattable x; public C1(System.IFormattable x) { this.x = x; } public static implicit operator C1(System.IFormattable x) => new C1(x); public override string ToString() { return ("C1:") + x.ToString(); } } """; CreateCompilation(source).VerifyDiagnostics( // (18,37): error CS0552: 'C1.implicit operator C1(IFormattable)': user-defined conversions to or from an interface are not allowed // public static implicit operator C1(System.IFormattable x) => new C1(x); Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C1").WithArguments("C1.implicit operator C1(System.IFormattable)").WithLocation(18, 37) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74677")] public void StackOverflow() { var source = """ using System; [Obsolete($"{Test2}")] class Test2 { } """; CreateCompilation(source).VerifyEmitDiagnostics( // (3,14): error CS0119: 'Test2' is a type, which is not valid in the given context // [Obsolete($"{Test2}")] Diagnostic(ErrorCode.ERR_BadSKunknown, "Test2").WithArguments("Test2", "type").WithLocation(3, 14) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76444")] public void UnconvertedNestedTargetTypedScenario_01() { var source = """ int a = 1; var b = true ? $"{a.}"='a' """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,21): error CS1001: Identifier expected // var b = true ? $"{a.}"='a' Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 21), // (3,27): error CS1003: Syntax error, ':' expected // var b = true ? $"{a.}"='a' Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 27), // (3,27): error CS1733: Expected expression // var b = true ? $"{a.}"='a' Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(3, 27), // (3,27): error CS1002: ; expected // var b = true ? $"{a.}"='a' Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 27) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var incompleteDeclaration = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().ElementAt(1); VerifyOperationTree(comp, model.GetOperation(incompleteDeclaration), """ IVariableDeclaratorOperation (Symbol: ? b) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'b = true ? $"{a.}"='a'') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= true ? $"{a.}"='a'') IConditionalOperation (OperationKind.Conditional, Type: ?, IsInvalid) (Syntax: 'true ? $"{a.}"='a'') Condition: ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') WhenTrue: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String, IsInvalid) (Syntax: '$"{a.}"='a'') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$"{a.}"') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null, IsInvalid) (Syntax: '{a.}') Expression: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'a.') Children(1): ILocalReferenceOperation: a (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'a') Alignment: null FormatString: null Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsInvalid, IsImplicit) (Syntax: ''a'') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Char, Constant: a, IsInvalid) (Syntax: ''a'') WhenFalse: IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '') Children(0) """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76444")] public void UnconvertedNestedTargetTypedScenario_02() { var source = """ int a = 1; $"{a.}"='a' """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,6): error CS1001: Identifier expected // $"{a.}"='a' Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 6), // (3,12): error CS1002: ; expected // $"{a.}"='a' Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 12) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var incompleteAssignment = tree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>().Single(); VerifyOperationTree(comp, model.GetOperation(incompleteAssignment), """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String, IsInvalid) (Syntax: '$"{a.}"='a'') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$"{a.}"') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null, IsInvalid) (Syntax: '{a.}') Expression: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'a.') Children(1): ILocalReferenceOperation: a (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'a') Alignment: null FormatString: null Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsInvalid, IsImplicit) (Syntax: ''a'') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Char, Constant: a, IsInvalid) (Syntax: ''a'') """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76444")] public void UnconvertedNestedTargetTypedScenario_03() { var source = """ int a = 1; _ = new C() { $"{a.}"='a' } a switch { 1 => null } = 1; class C { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,5): error CS0747: Invalid initializer member declarator // $"{a.}"='a' Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, @"$""{a.}""='a'").WithLocation(5, 5), // (5,10): error CS1001: Identifier expected // $"{a.}"='a' Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(5, 10), // (6,2): error CS1002: ; expected // } Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 2), // (8,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // a switch { 1 => null } = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "a switch { 1 => null }").WithLocation(8, 1), // (8,3): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // a switch { 1 => null } = 1; Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(8, 3) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var incompleteAssignment = tree.GetRoot().DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single(); VerifyOperationTree(comp, model.GetOperation(incompleteAssignment), """ IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C() ... }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C, IsInvalid) (Syntax: '{ ... }') Initializers(1): IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid, IsImplicit) (Syntax: '$"{a.}"='a'') Children(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String, IsInvalid) (Syntax: '$"{a.}"='a'') Left: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$"{a.}"') Parts(1): IInterpolationOperation (OperationKind.Interpolation, Type: null, IsInvalid) (Syntax: '{a.}') Expression: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'a.') Children(1): ILocalReferenceOperation: a (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'a') Alignment: null FormatString: null Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsInvalid, IsImplicit) (Syntax: ''a'') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Char, Constant: a, IsInvalid) (Syntax: ''a'') """); } [Fact(Skip = "Tracked by https://github.com/dotnet/roslyn/issues/82273")] public void InterpolationHandler_Indexer_InObjectInitializer() { // Tracked by https://github.com/dotnet/roslyn/issues/82273 // Crashes in Release and hits an assertion (AssertContainingContextIsForThisCreation) in Debug in ControlFlowGraphBuilder var code = """ public class Program { public static void Main() { /*<bind>*/ _ = new C() { [$"{1}"] = 1 }; /*</bind>*/ } } public class C { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("")] CustomHandler h] { get => throw null!; } } [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, C c) { } public void AppendFormatted(int i) {} } """; DiagnosticDescription[] expectedDiagnostics = [ // (6,24): error CS8976: Interpolated string handler conversions that reference the instance being indexed cannot be used in indexer member initializers. // _ = new C() { [$"{1}"] = 1 }; Diagnostic(ErrorCode.ERR_InterpolatedStringsReferencingInstanceCannotBeInObjectInitializers, @"$""{1}""").WithLocation(6, 24) ]; var comp = CreateCompilation(code, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics(expectedDiagnostics); string expectedOperationTree = """ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: '_ = new C() ... 1}"] = 1 };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C, IsInvalid) (Syntax: '_ = new C() ... {1}"] = 1 }') Left: IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C() { [$"{1}"] = 1 }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C, IsInvalid) (Syntax: '{ [$"{1}"] = 1 }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: '[$"{1}"] = 1') Left: IInvalidOperation (OperationKind.Invalid, Type: System.Int32, IsInvalid) (Syntax: '[$"{1}"]') Children(1): IInterpolatedStringHandlerCreationOperation (HandlerAppendCallsReturnBool: False, HandlerCreationHasSuccessParameter: False) (OperationKind.InterpolatedStringHandlerCreation, Type: CustomHandler, IsInvalid, IsImplicit) (Syntax: '$"{1}"') Creation: IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, C c)) (OperationKind.ObjectCreation, Type: CustomHandler, IsInvalid, IsImplicit) (Syntax: '$"{1}"') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '$"{1}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: '$"{1}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '$"{1}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid, IsImplicit) (Syntax: '$"{1}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'C') IInterpolatedStringHandlerArgumentPlaceholderOperation (CallsiteReceiver) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: 'C') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Content: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$"{1}"') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsInvalid, IsImplicit) (Syntax: '{1}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: '{1}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsInvalid, IsImplicit) (Syntax: '$"{1}"') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') """; VerifyOperationTreeAndDiagnosticsForTest<ExpressionStatementSyntax>(comp, expectedOperationTree, expectedDiagnostics); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainDeclaration = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First(); var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(mainDeclaration.Body, model); ControlFlowGraphVerifier.VerifyGraph(comp, """ """, graph, symbol); } } }