FormattableString interpolated string mishandles escaped brackets
When creating a FormattableString from an interpolated string, escaped brackets ({{ and }}) are not correctly handled, resulting in an invalid format string. See the comparison with C# below:
Repro steps
$ csi
Microsoft (R) Visual C# Interactive Compiler version 3.9.0-6.21124.20 ()
Copyright (C) Microsoft Corporation. All rights reserved.
Type "#help" for more information.
> FormattableString str = $"{{Hello}} world";
> str.Format
"{{Hello}} world"
$ dotnet fsi
Microsoft (R) F# Interactive version 11.4.2.0 for F# 5.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
For help type #help;;
> let str = $"{{Hello}} world" : System.FormattableString;;
val str : System.FormattableString =
<ToString exception: Input string was not in a correct format.>
> str.Format;;
val it : string = "{Hello} world"
Expected behavior
The FormattableString created by F# should match the one created by C# for the same interpolated string.
Actual behavior
Escaped brackets are unescaped into the format string, causing it to be invalid.
Known workarounds
Modify the interpolated string or call FormattableStringFactory.Create directly.
Related information
Provide any related information (optional):
- macOS 10.15.7
- .NET Runtime kind (.NET Core, .NET Framework, Mono): mono 6.12.0.140 (2020-02/51d876a041e, .NET Core 5.0.301
- Editing Tools (e.g. Visual Studio Version, Visual Studio)
@vzarytovskii You might like to take a look at this. We should fix this to follow the C# spec
@vzarytovskii I could fix this but we can use this as a first Up for Grabs issue I think?
@vzarytovskii I could fix this but we can use this as a first Up for Grabs issue I think?
Yeah, let's have it as up for grabs, I will share some of the issues in slack and discord, maybe someone will be interested in fixing them.
I've started looking into this. It is tricky because we turn double braces into single braces during lexing already. At the point where we generate FormattableStringFactory.Create call, there is no easy way to determine whether { in a string was originally {{ or not. It can be done probably, but feels hacky... I'll give it a try, but I am open to other suggestions.
Even worse, I discovered this:
> let str = $"{{0}}+{42}" : System.FormattableString;;
val str: System.FormattableString = 42+42
> str.Format;;
val it: string = "{0}+{0}"
I've just noticed that this is still not fixed despite my PR, will have to investigate
edit: not sure what exactly is going on, but even though the added unit tests passes (and would not pass without the fix), behavior in fsi is still as described in this issue.
edit2: I might have gotten confused by combination of not rebuilding at some point and not passing --langversion:preview, bad brain day.
Hi. Great work on fixing it. Is the fix rolling out in .NET 8?
@mrange Yes, if you get the preview version of dotnet sdk, it's already there, but for under --langversion:preview flag