fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

FormattableString interpolated string mishandles escaped brackets

Open chkn opened this issue 4 years ago • 3 comments

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)

chkn avatar Jul 04 '21 02:07 chkn

@vzarytovskii You might like to take a look at this. We should fix this to follow the C# spec

dsyme avatar Jan 21 '22 16:01 dsyme

@vzarytovskii I could fix this but we can use this as a first Up for Grabs issue I think?

dsyme avatar Apr 05 '22 01:04 dsyme

@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.

vzarytovskii avatar Apr 05 '22 08:04 vzarytovskii

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.

abonie avatar Nov 28 '22 13:11 abonie

Even worse, I discovered this:

> let str = $"{{0}}+{42}" : System.FormattableString;;
val str: System.FormattableString = 42+42
> str.Format;;
val it: string = "{0}+{0}"

abonie avatar Nov 28 '22 14:11 abonie

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.

abonie avatar Jan 20 '23 12:01 abonie

Hi. Great work on fixing it. Is the fix rolling out in .NET 8?

mrange avatar Jul 31 '23 18:07 mrange

@mrange Yes, if you get the preview version of dotnet sdk, it's already there, but for under --langversion:preview flag

abonie avatar Aug 01 '23 10:08 abonie