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 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 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 []))