Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsConst.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsReadOnlyAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsVolatile.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\InterpolatedStringBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\InterpolatedStringHandlerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\InterpolatedStringHandlerArgumentAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DefaultInterpolatedStringHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IteratorStateMachineAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ITuple.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\LoadHint.cs" />
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
/// <summary>Indicates which arguments to a method involving an interpolated string handler should be passed to that handler.</summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
/// <summary>Initializes a new instance of the <see cref="InterpolatedStringHandlerArgumentAttribute"/> class.</summary>
/// <param name="argument">The name of the argument that should be passed to the handler.</param>
/// <remarks><see langword="null"/> may be used as the name of the receiver in an instance method.</remarks>
public InterpolatedStringHandlerArgumentAttribute(string argument) => Arguments = new string[] { argument };

/// <summary>Initializes a new instance of the <see cref="InterpolatedStringHandlerArgumentAttribute"/> class.</summary>
/// <param name="arguments">The names of the arguments that should be passed to the handler.</param>
/// <remarks><see langword="null"/> may be used as the name of the receiver in an instance method.</remarks>
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) => Arguments = arguments;

/// <summary>Gets the names of the arguments that should be passed to the handler.</summary>
/// <remarks><see langword="null"/> may be used as the name of the receiver in an instance method.</remarks>
public string[] Arguments { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
/// <summary>Indicates the attributed type is to be used as an interpolated string handler.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerAttribute : Attribute
{
/// <summary>Initializes the <see cref="InterpolatedStringHandlerAttribute"/>.</summary>
public InterpolatedStringHandlerAttribute() { }
}
}
22 changes: 17 additions & 5 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9651,10 +9651,26 @@ public InternalsVisibleToAttribute(string assemblyName) { }
public bool AllInternalsVisible { get { throw null; } set { } }
public string AssemblyName { get { throw null; } }
}
public ref struct InterpolatedStringBuilder
[System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerAttribute : System.Attribute
{
public InterpolatedStringHandlerAttribute() { }
}
[System.AttributeUsage(System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerArgumentAttribute : System.Attribute
{
public InterpolatedStringHandlerArgumentAttribute(string argument) { }
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) { }
public string[] Arguments { get { throw null; } }
}
[System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute]
public ref struct DefaultInterpolatedStringHandler
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) { throw null; }
public DefaultInterpolatedStringHandler(int literalLength, int formattedCount, System.IFormatProvider? provider) { throw null; }
public DefaultInterpolatedStringHandler(int literalLength, int formattedCount, System.IFormatProvider? provider, System.Span<char> initialBuffer) { throw null; }
public void AppendLiteral(string value) { }
public void AppendFormatted(System.ReadOnlySpan<char> value) { }
public void AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) { }
Expand All @@ -9665,10 +9681,6 @@ public void AppendFormatted<T>(T value, int alignment, string? format) { }
public void AppendFormatted(object? value, int alignment = 0, string? format = null) { }
public void AppendFormatted(string? value) { throw null; }
public void AppendFormatted(string? value, int alignment = 0, string? format = null) { }
public static System.Runtime.CompilerServices.InterpolatedStringBuilder Create(int literalLength, int formattedCount) { throw null; }
public static System.Runtime.CompilerServices.InterpolatedStringBuilder Create(int literalLength, int formattedCount, System.IFormatProvider? provider) { throw null; }
public static System.Runtime.CompilerServices.InterpolatedStringBuilder Create(int literalLength, int formattedCount, System.Span<char> scratchBuffer) { throw null; }
public static System.Runtime.CompilerServices.InterpolatedStringBuilder Create(int literalLength, int formattedCount, System.IFormatProvider? provider, System.Span<char> scratchBuffer) { throw null; }
public override string ToString() { throw null; }
public string ToStringAndClear() { throw null; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@
<Compile Include="System\Runtime\NgenServicingAttributesTests.cs" />
<Compile Include="System\Runtime\CompilerServices\AttributesTests.cs" />
<Compile Include="System\Runtime\CompilerServices\ConditionalWeakTableTests.cs" />
<Compile Include="System\Runtime\CompilerServices\DefaultInterpolatedStringHandlerTests.cs" />
<Compile Include="System\Runtime\CompilerServices\FormattableStringFactoryTests.cs" />
<Compile Include="System\Runtime\CompilerServices\InterpolatedStringBuilderTests.cs" />
<Compile Include="System\Runtime\CompilerServices\StrongBoxTests.cs" />
<Compile Include="System\Runtime\CompilerServices\RuntimeHelpersTests.cs" />
<Compile Include="System\Runtime\ConstrainedExecution\PrePrepareMethodAttributeTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,5 +295,30 @@ public static void EnumeratorCancellationAttributeTests()
{
new EnumeratorCancellationAttribute();
}

[Fact]
public static void InterpolatedStringHandlerAttributeTests()
{
new InterpolatedStringHandlerAttribute();
}

[Theory]
[InlineData("")]
[InlineData("param1")]
public static void InterpolatedStringHandlerArgumentAttributeTests(string firstParameterName)
{
var attr1 = new InterpolatedStringHandlerArgumentAttribute(firstParameterName);
Assert.NotNull(attr1.Arguments);
Assert.Same(attr1.Arguments, attr1.Arguments);
Assert.Equal(1, attr1.Arguments.Length);
Assert.Equal(firstParameterName, attr1.Arguments[0]);

string[] arguments = new[] { firstParameterName, "param2" };
var attr2 = new InterpolatedStringHandlerArgumentAttribute(arguments);
Assert.NotNull(attr2.Arguments);
Assert.Same(arguments, attr2.Arguments);
Assert.Equal(firstParameterName, attr2.Arguments[0]);
Assert.Equal("param2", attr2.Arguments[1]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace System.Runtime.CompilerServices.Tests
{
public class InterpolatedStringBuilderTests
public class DefaultInterpolatedStringHandlerTests
{
[Theory]
[InlineData(0, 0)]
Expand All @@ -18,51 +18,46 @@ public class InterpolatedStringBuilderTests
[InlineData(-16, 1)]
public void LengthAndHoleArguments_Valid(int literalLength, int formattedCount)
{
InterpolatedStringBuilder.Create(literalLength, formattedCount);
new DefaultInterpolatedStringHandler(literalLength, formattedCount);

Span<char> scratch1 = stackalloc char[1];
foreach (IFormatProvider provider in new IFormatProvider[] { null, new ConcatFormatter(), CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, new CultureInfo("en-US"), new CultureInfo("fr-FR") })
{
InterpolatedStringBuilder.Create(literalLength, formattedCount, provider);
new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider);

InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, default);
InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, scratch1);
InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, Array.Empty<char>());
InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, new char[256]);
new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, default);
new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, scratch1);
new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, Array.Empty<char>());
new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, new char[256]);
}

InterpolatedStringBuilder.Create(literalLength, formattedCount, Span<char>.Empty);
InterpolatedStringBuilder.Create(literalLength, formattedCount, scratch1);
InterpolatedStringBuilder.Create(literalLength, formattedCount, Array.Empty<char>());
InterpolatedStringBuilder.Create(literalLength, formattedCount, new char[256]);
}

[Fact]
public void ToString_DoesntClear()
{
InterpolatedStringBuilder builder = InterpolatedStringBuilder.Create(0, 0);
builder.AppendLiteral("hi");
DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(0, 0);
handler.AppendLiteral("hi");
for (int i = 0; i < 3; i++)
{
Assert.Equal("hi", builder.ToString());
Assert.Equal("hi", handler.ToString());
}
Assert.Equal("hi", builder.ToStringAndClear());
Assert.Equal("hi", handler.ToStringAndClear());
}

[Fact]
public void ToStringAndClear_Clears()
{
InterpolatedStringBuilder builder = InterpolatedStringBuilder.Create(0, 0);
builder.AppendLiteral("hi");
Assert.Equal("hi", builder.ToStringAndClear());
Assert.Equal(string.Empty, builder.ToStringAndClear());
DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(0, 0);
handler.AppendLiteral("hi");
Assert.Equal("hi", handler.ToStringAndClear());
Assert.Equal(string.Empty, handler.ToStringAndClear());
}

[Fact]
public void AppendLiteral()
{
var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);

foreach (string s in new[] { "", "a", "bc", "def", "this is a long string", "!" })
{
Expand All @@ -77,7 +72,7 @@ public void AppendLiteral()
public void AppendFormatted_ReadOnlySpanChar()
{
var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);

foreach (string s in new[] { "", "a", "bc", "def", "this is a longer string", "!" })
{
Expand Down Expand Up @@ -108,7 +103,7 @@ public void AppendFormatted_ReadOnlySpanChar()
public void AppendFormatted_String()
{
var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);

foreach (string s in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" })
{
Expand Down Expand Up @@ -141,7 +136,7 @@ public void AppendFormatted_String_ICustomFormatter()
var provider = new ConcatFormatter();

var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0, provider);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0, provider);

foreach (string s in new[] { null, "", "a" })
{
Expand Down Expand Up @@ -169,7 +164,7 @@ public void AppendFormatted_String_ICustomFormatter()
public void AppendFormatted_ReferenceTypes()
{
var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);

foreach (string rawInput in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" })
{
Expand Down Expand Up @@ -231,22 +226,22 @@ public void AppendFormatted_ReferenceTypes()
public void AppendFormatted_ReferenceTypes_CreateProviderFlowed(bool useScratch)
{
var provider = new CultureInfo("en-US");
InterpolatedStringBuilder builder = useScratch ?
InterpolatedStringBuilder.Create(1, 2, provider, stackalloc char[16]) :
InterpolatedStringBuilder.Create(1, 2, provider);
DefaultInterpolatedStringHandler handler = useScratch ?
new DefaultInterpolatedStringHandler(1, 2, provider, stackalloc char[16]) :
new DefaultInterpolatedStringHandler(1, 2, provider);

foreach (IHasToStringState tss in new IHasToStringState[] { new FormattableStringWrapper("hello"), new SpanFormattableStringWrapper("hello") })
{
builder.AppendFormatted(tss);
handler.AppendFormatted(tss);
Assert.Same(provider, tss.ToStringState.LastProvider);

builder.AppendFormatted(tss, 1);
handler.AppendFormatted(tss, 1);
Assert.Same(provider, tss.ToStringState.LastProvider);

builder.AppendFormatted(tss, "X2");
handler.AppendFormatted(tss, "X2");
Assert.Same(provider, tss.ToStringState.LastProvider);

builder.AppendFormatted(tss, 1, "X2");
handler.AppendFormatted(tss, 1, "X2");
Assert.Same(provider, tss.ToStringState.LastProvider);
}
}
Expand All @@ -257,7 +252,7 @@ public void AppendFormatted_ReferenceTypes_ICustomFormatter()
var provider = new ConcatFormatter();

var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0, provider);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0, provider);

foreach (string s in new[] { null, "", "a" })
{
Expand Down Expand Up @@ -301,7 +296,7 @@ public void AppendFormatted_ValueTypes()
void Test<T>(T t)
{
var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);

// struct
expected.AppendFormat("{0}", t);
Expand Down Expand Up @@ -347,20 +342,20 @@ public void AppendFormatted_ValueTypes_CreateProviderFlowed(bool useScratch)
void Test<T>(T t)
{
var provider = new CultureInfo("en-US");
InterpolatedStringBuilder builder = useScratch ?
InterpolatedStringBuilder.Create(1, 2, provider, stackalloc char[16]) :
InterpolatedStringBuilder.Create(1, 2, provider);
DefaultInterpolatedStringHandler handler = useScratch ?
new DefaultInterpolatedStringHandler(1, 2, provider, stackalloc char[16]) :
new DefaultInterpolatedStringHandler(1, 2, provider);

builder.AppendFormatted(t);
handler.AppendFormatted(t);
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);

builder.AppendFormatted(t, 1);
handler.AppendFormatted(t, 1);
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);

builder.AppendFormatted(t, "X2");
handler.AppendFormatted(t, "X2");
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);

builder.AppendFormatted(t, 1, "X2");
handler.AppendFormatted(t, 1, "X2");
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);
}

Expand All @@ -385,7 +380,7 @@ void AssertTss(T tss, string format)
}

var expected = new StringBuilder();
InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0, provider);
DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0, provider);

// struct
expected.AppendFormat(provider, "{0}", t);
Expand Down Expand Up @@ -422,20 +417,20 @@ void AssertTss(T tss, string format)
public void Grow_Large(bool useScratch)
{
var expected = new StringBuilder();
InterpolatedStringBuilder builder = useScratch ?
InterpolatedStringBuilder.Create(3, 1000, null, stackalloc char[16]) :
InterpolatedStringBuilder.Create(3, 1000);
DefaultInterpolatedStringHandler handler = useScratch ?
new DefaultInterpolatedStringHandler(3, 1000, null, stackalloc char[16]) :
new DefaultInterpolatedStringHandler(3, 1000);

for (int i = 0; i < 1000; i++)
{
builder.AppendFormatted(i);
handler.AppendFormatted(i);
expected.Append(i);

builder.AppendFormatted(i, 3);
handler.AppendFormatted(i, 3);
expected.AppendFormat("{0,3}", i);
}

Assert.Equal(expected.ToString(), builder.ToStringAndClear());
Assert.Equal(expected.ToString(), handler.ToStringAndClear());
}

private static void AssertModeMatchesType<T>(T tss) where T : IHasToStringState
Expand Down