roslyn: Invalid parsing of interpolated strings with curly braces followed after interpolation with format specifier

Version Used: commit 973b3aa

Steps to Reproduce: Run the following code with net 6.0:

using System;

Console.WriteLine($"{{{12:X}}}");

See here

Expected Behavior: It should print “{C}” like it did in net5.0

Actual Behavior: It prints: “{X}}”

That there is parsing error is very good visible in Visual studios highlighting: grafik

See csharplang discussion: here


Details

With net5.0 it was lowered to:

Console.WriteLine(string.Format("{{{0:X}}}", 12)); //prints "{C}"

but with net6.0 it is lowered to

DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(1, 1);
defaultInterpolatedStringHandler.AppendLiteral("{");
defaultInterpolatedStringHandler.AppendFormatted(12, "X}}");
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear()); //prints "{X}}"

instead of:

DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(2, 1);
defaultInterpolatedStringHandler.AppendLiteral("{");
defaultInterpolatedStringHandler.AppendFormatted(12, "X");
defaultInterpolatedStringHandler.AppendLiteral("}");
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear()); //prints "{C}"

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 16 (15 by maintainers)

Most upvoted comments

Just to note that visual basic displays Console.WriteLine($"{{{12:X}}}") as {C}, which I think is the intended behaviour .

@Timovzl I understand that and agree with you. I was asking whether someone was broken when the bug was fixed previously in .Net Core 3.1, to gauge if that is a problem worth mitigating now that the bug needs to be fixed again.

Some workarounds customers can use today are:

using System;

Console.WriteLine('{' + $"{12:X}" + '}');
Console.WriteLine($"{"{"}{12:X}{"}"}");
Console.WriteLine($"{{{12:X}{"}"}");
Console.WriteLine(String.Concat('{', String.Format("{0:X}", 12), '}'));

defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(0, 3);
defaultInterpolatedStringHandler.AppendLiteral("{");
defaultInterpolatedStringHandler.AppendFormatted(12, "X");
defaultInterpolatedStringHandler.AppendLiteral("}");
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());

And roslyn users can analyze the format string…

@jaredpar

One item I think is missing from this discussion though is what is the option for maintaining the old behavior on the new implementation? Basically how do you get the desired behavior here?

Is there any evidence that anyone would actually want this? Did anyone report an issue when the behavior of string.Format changed in .Net Core 3.1?

Also, it seems the behavior in .Net 6.0 is not even the same as it was in .Net Framework/.Net Core 2.1. The one-liner in the first post produces the following output:

> dotnet run -f net48
{X}
> dotnet run -f netcoreapp2.1
{X}
> dotnet run -f netcoreapp3.1
{C}
> dotnet run -f net5.0       
{C}
> dotnet run -f net6.0       
{X}}

Notice how on .Net 6, it prints two closing braces, while older frameworks output only one.

And probably most importantly how will that solution work in older compilers?

I don’t see the problem. Older compilers delegate to string.Format, so their behavior depends on the runtime. Once this bug is fixed, new compilers will have the same behavior as old compilers on all runtimes (just optimized using interpolated string handlers where available).

The only problematic case I can think of is if someone uses interpolated string handlers on an older framework. But I don’t think that’s a case that’s worth worrying about.