From 002193a27854be7438044431cbf18630e6b32351 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Thu, 15 Jun 2023 12:18:49 +0200 Subject: [PATCH 1/3] Add unit test for positive case --- .../Language/InterpolatedStringsTests.fs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs index a3eed933be6..16d70c1c154 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs @@ -109,4 +109,23 @@ let s = $"...%-%...{0}" """ |> compile |> shouldFail - |> withSingleDiagnostic (Warning 3376, Line 2, Col 9, Line 2, Col 24, "Bad format specifier: '%'") \ No newline at end of file + |> withSingleDiagnostic (Warning 3376, Line 2, Col 9, Line 2, Col 24, "Bad format specifier: '%'") + + [] + let ``Interpolated expression can be offside`` () = + Fsx """ +let a() = + let b() = + $" +{1}" + b() + +type Foo () = + member _.Bar () = + let x = + $" +{2}" + x + """ + |> compile + |> shouldSucceed \ No newline at end of file From 4252e22a878b0f7cfe69753393bfef2ed9952327 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Wed, 14 Jun 2023 18:07:50 +0200 Subject: [PATCH 2/3] Skip closing contexts on interpolated strings end We do not really care about interpolated string end token being offside in relation to the interpolated string beginning. Not sure if not inserting dummy token is right fix here though. --- src/Compiler/SyntaxTree/LexFilter.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Compiler/SyntaxTree/LexFilter.fs b/src/Compiler/SyntaxTree/LexFilter.fs index 09bc37e0c5f..19f8eae9533 100644 --- a/src/Compiler/SyntaxTree/LexFilter.fs +++ b/src/Compiler/SyntaxTree/LexFilter.fs @@ -1563,9 +1563,11 @@ type LexFilterImpl ( | INTERP_STRING_PART _ -> pushCtxt tokenTup (CtxtParen (token, tokenTup.LexbufState.EndPos)) pushCtxtSeqBlock tokenTup NoAddBlockEnd + | INTERP_STRING_END _ -> () | _ -> // Queue a dummy token at this position to check if any closing rules apply delayToken(pool.UseLocation(tokenTup, ODUMMY token)) + returnToken tokenLexbufState token // Balancing rule. Encountering a 'end' can balance with a 'with' but only when not offside From c1b3688a1433f2aa89552cd9bab53b2e7a5db510 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Tue, 27 Jun 2023 14:38:59 +0200 Subject: [PATCH 3/3] Add baseline tests --- .../InterpolatedStringOffsideInModule.fs | 4 ++ .../InterpolatedStringOffsideInModule.fs.bsl | 34 ++++++++++++++++ .../InterpolatedStringOffsideInNestedLet.fs | 5 +++ ...nterpolatedStringOffsideInNestedLet.fs.bsl | 40 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs create mode 100644 tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs.bsl create mode 100644 tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs create mode 100644 tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs.bsl diff --git a/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs new file mode 100644 index 00000000000..c8f1397e57c --- /dev/null +++ b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs @@ -0,0 +1,4 @@ +module A = + let b = + $" +{0}" \ No newline at end of file diff --git a/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs.bsl b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs.bsl new file mode 100644 index 00000000000..7e6563219a6 --- /dev/null +++ b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInModule.fs.bsl @@ -0,0 +1,34 @@ +ImplFile + (ParsedImplFileInput + ("/root/String/InterpolatedStringOffsideInModule.fs", false, + QualifiedNameOfFile InterpolatedStringOffsideInModule, [], [], + [SynModuleOrNamespace + ([InterpolatedStringOffsideInModule], false, AnonModule, + [NestedModule + (SynComponentInfo + ([], None, [], [A], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), false, + None, (1,0--1,8)), false, + [Let + (false, + [SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((2,4), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (None, SynValInfo ([], SynArgInfo ([], false, None)), + None), + Named (SynIdent (b, None), false, None, (2,8--2,9)), None, + InterpolatedString + ([String (" +", (3,8--4,1)); + FillExpr (Const (Int32 0, (4,1--4,2)), None); + String ("", (4,2--4,4))], Regular, (3,8--4,4)), + (2,8--2,9), Yes (2,4--4,4), + { LeadingKeyword = Let (2,4--2,7) + InlineKeyword = None + EqualsRange = Some (2,10--2,11) })], (2,4--4,4))], false, + (1,0--4,4), { ModuleKeyword = Some (1,0--1,6) + EqualsRange = Some (1,9--1,10) })], PreXmlDocEmpty, + [], None, (1,0--4,4), { LeadingKeyword = None })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) diff --git a/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs new file mode 100644 index 00000000000..9c4e769bf7d --- /dev/null +++ b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs @@ -0,0 +1,5 @@ +let a = + let b = + $" +{0}" + b \ No newline at end of file diff --git a/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs.bsl b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs.bsl new file mode 100644 index 00000000000..d91bffd331d --- /dev/null +++ b/tests/service/data/SyntaxTree/String/InterpolatedStringOffsideInNestedLet.fs.bsl @@ -0,0 +1,40 @@ +ImplFile + (ParsedImplFileInput + ("/root/String/InterpolatedStringOffsideInNestedLet.fs", false, + QualifiedNameOfFile InterpolatedStringOffsideInNestedLet, [], [], + [SynModuleOrNamespace + ([InterpolatedStringOffsideInNestedLet], false, AnonModule, + [Let + (false, + [SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (None, SynValInfo ([], SynArgInfo ([], false, None)), None), + Named (SynIdent (a, None), false, None, (1,4--1,5)), None, + LetOrUse + (false, false, + [SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((2,4), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (None, SynValInfo ([], SynArgInfo ([], false, None)), + None), + Named (SynIdent (b, None), false, None, (2,8--2,9)), + None, + InterpolatedString + ([String (" +", (3,8--4,1)); + FillExpr (Const (Int32 0, (4,1--4,2)), None); + String ("", (4,2--4,4))], Regular, (3,8--4,4)), + (2,8--2,9), Yes (2,4--4,4), + { LeadingKeyword = Let (2,4--2,7) + InlineKeyword = None + EqualsRange = Some (2,10--2,11) })], Ident b, + (2,4--5,5), { InKeyword = None }), (1,4--1,5), NoneAtLet, + { LeadingKeyword = Let (1,0--1,3) + InlineKeyword = None + EqualsRange = Some (1,6--1,7) })], (1,0--5,5))], + PreXmlDocEmpty, [], None, (1,0--5,5), { LeadingKeyword = None })], + (true, true), { ConditionalDirectives = [] + CodeComments = [] }, set []))