|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics;
public partial class RawInterpolationTests : 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,75): error CS1733: Expected expression
// Console.WriteLine($"""Jenny don\'t change your number { /*trash*/ }.""");
Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 75));
}
[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,70): error CS8997: Unterminated raw string literal
// Console.WriteLine($"""Jenny don\'t change your number { """);
Diagnostic(ErrorCode.ERR_UnterminatedRawString, @"
").WithLocation(5, 70),
// (6,5): error CS8997: Unterminated raw string literal
// }
Diagnostic(ErrorCode.ERR_UnterminatedRawString, "}").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 CS8997: Unterminated raw string literal
// }
Diagnostic(ErrorCode.ERR_UnterminatedRawString, "}").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,63): 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, 63),
// (5,73): error CS1035: End-of-file found, '*/' expected
// Console.WriteLine($"""Jenny don\'t change your number { 8675309 /* """);
Diagnostic(ErrorCode.ERR_OpenEndedComment, "").WithLocation(5, 73),
// (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 }.
"""""" );
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (12,1): error CS8999: Line does not start with the same whitespace as the closing line of the raw string literal.
// {
Diagnostic(ErrorCode.ERR_LineDoesNotStartWithSameWhitespace, "{").WithLocation(12, 1));
}
[Fact]
public void TwoInserts02_A()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
var hello = ""Hello"";
var world = ""world"";
Console.WriteLine( $""""""
{
hello
},
{
world }.
"""""" );
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (12,1): error CS8999: Line does not start with the same whitespace as the closing line of the raw string literal.
// {
Diagnostic(ErrorCode.ERR_LineDoesNotStartWithSameWhitespace, " ").WithLocation(12, 1));
}
[Fact]
public void TwoInserts02_B()
{
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,39): error CS8997: Unterminated raw string literal
// Console.WriteLine( $"""{""" );
Diagnostic(ErrorCode.ERR_UnterminatedRawString, @"
").WithLocation(6, 39),
// (7,5): error CS8997: Unterminated raw string literal
// }
Diagnostic(ErrorCode.ERR_UnterminatedRawString, "}").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,21): error CS8997: Unterminated raw string literal
// var x = $""";
Diagnostic(ErrorCode.ERR_UnterminatedRawString, ";").WithLocation(5, 21),
// (5,22): error CS1002: ; expected
// var x = $""";
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 22),
// (5,22): error CS1513: } expected
// var x = $""";
Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 22),
// (5,22): error CS1513: } expected
// var x = $""";
Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 22));
}
[Fact]
public void EmptyFormatSpecifier()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine( $""""""{3:}"""""" );
}
}";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (6,34): error CS8089: Empty format specifier.
// Console.WriteLine( $"""{3:}""" );
Diagnostic(ErrorCode.ERR_EmptyFormatSpecifier, ":").WithLocation(6, 34));
}
[Fact]
public void TrailingSpaceInFormatSpecifier()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine( $""""""{3:d }"""""" );
}
}";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (6,34): error CS8088: A format specifier may not contain trailing whitespace.
// Console.WriteLine( $"""{3:d }""" );
Diagnostic(ErrorCode.ERR_TrailingWhitespaceInFormatSpecifier, ":d ").WithLocation(6, 34));
}
[Fact]
public void TrailingSpaceInFormatSpecifier02()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine( $""""""{3:d
}"""""" );
}
}";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (6,34): error CS8088: A format specifier may not contain trailing whitespace.
// Console.WriteLine( $"""{3:d
Diagnostic(ErrorCode.ERR_TrailingWhitespaceInFormatSpecifier, @":d
").WithLocation(6, 34));
}
[Fact]
public void MissingInterpolationExpression01()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine( $""""""{ }"""""" );
}
}";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (6,34): error CS1733: Expected expression
// Console.WriteLine( $"""{ }""" );
Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(6, 34));
}
[Fact]
public void MissingInterpolationExpression02()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine( $""""""{ }"""""" );
}
}";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (6,34): error CS1733: Expected expression
// Console.WriteLine( $"""{ }""" );
Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(6, 34));
}
[Fact]
public void MissingInterpolationExpression03()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine( ";
var normal = "$\"\"\"";
// 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));
}
[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 EscapesAreNotEscapes()
{
string source =
@"class Program
{
static void Main()
{
var s1 = $"""""" \u007B """""";
var s2 = $"""""" \u007D"""""";
}
}";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics();
}
[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,24): 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, 24),
// (5,24): error CS0150: A constant value is expected
// var s = $"""{1,1E10}""";
Diagnostic(ErrorCode.ERR_ConstantExpected, "1E10").WithLocation(5, 24),
// (6,24): 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, 24),
// (6,24): error CS0150: A constant value is expected
// var t = $"""{1,(int)1E10}""";
Diagnostic(ErrorCode.ERR_ConstantExpected, "(int)1E10").WithLocation(6, 24));
}
[Fact]
public void NestedInterpolated()
{
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,44): 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, 44),
// (6,43): 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, 43),
// (9,43): 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, 43),
// (10,43): 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, 43));
}
[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,37): 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, 37));
}
[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.RegularPreview).VerifyDiagnostics(
// (5,38): error CS8917: The delegate type could not be inferred.
// Console.WriteLine($"""X = { x=>3 }.""");
Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "=>").WithLocation(5, 38),
// (6,37): 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, 37),
// (7,37): 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, 37));
}
[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(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();
}
[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,41): error CS8089: Empty format specifier.
// var x = $"""{ Math.Abs(value: 1):}}""";
Diagnostic(ErrorCode.ERR_EmptyFormatSpecifier, ":").WithLocation(6, 41),
// (6,43): error CS9007: Too many closing braces for raw string literal
// var x = $"""{ Math.Abs(value: 1):}}""";
Diagnostic(ErrorCode.ERR_TooManyCloseBracesForRawString, "}").WithLocation(6, 43));
}
[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,23): error CS0150: A constant value is expected
// string s = $"""{0,a}""";
Diagnostic(ErrorCode.ERR_ConstantExpected, "a").WithLocation(4, 23));
}
[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();
}
[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 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(
// (6,24): error CS8026: Feature 'raw string literals' is not available in C# 5. Please use language version 11.0 or greater.
// string other = """world""";
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, @"""""""world""""""").WithArguments("raw string literals", "11.0").WithLocation(6, 24),
// (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(61355, "https://github.com/dotnet/roslyn/issues/61355")]
public void StringFormatLowering1()
{
string source =
@"using System;
class Program
{
static void Main(string[] args)
{
int count = 31;
string op = ""shl"";
var value = $$""""""
{
// Code size 5 (0x5)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.s {{count}}
IL_0003: {{op}}
IL_0004: ret
}
"""""";
Console.WriteLine(value);
}
}";
var expectedOutput = @"{
// Code size 5 (0x5)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.s 31
IL_0003: shl
IL_0004: ret
}";
var verifier = CompileAndVerify(source, expectedOutput: expectedOutput);
verifier.VerifyIL("Program.Main", @"
{
// Code size 32 (0x20)
.maxstack 3
.locals init (int V_0, //count
string V_1) //op
IL_0000: ldc.i4.s 31
IL_0002: stloc.0
IL_0003: ldstr ""shl""
IL_0008: stloc.1
IL_0009: ldstr ""{{
// Code size 5 (0x5)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.s {0}
IL_0003: {1}
IL_0004: ret
}}""
IL_000e: ldloc.0
IL_000f: box ""int""
IL_0014: ldloc.1
IL_0015: call ""string string.Format(string, object, object)""
IL_001a: call ""void System.Console.WriteLine(string)""
IL_001f: ret
}");
}
}
|