From 3165a3862b100ecbbf0f07514d1421f833baf500 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 12 Sep 2024 20:34:42 +0100 Subject: [PATCH 1/9] Better ranges for CE error reporting --- .../CheckComputationExpressions.fs | 33 ++++++------------- src/Compiler/SyntaxTree/SyntaxTrivia.fs | 3 +- src/Compiler/SyntaxTree/SyntaxTrivia.fsi | 2 ++ src/Compiler/pars.fsy | 8 ++--- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs index 3a3682fbf53..a2cd83176a8 100644 --- a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs @@ -1854,12 +1854,7 @@ let rec TryTranslateComputationExpression // or // --> build.BindReturn(e1, (fun _argN -> match _argN with pat -> expr-without-return)) | SynExpr.LetOrUseBang( - bindDebugPoint = spBind; isUse = false; isFromSource = isFromSource; pat = pat; rhs = rhsExpr; andBangs = []; body = innerComp) -> - - let mBind = - match spBind with - | DebugPointAtBinding.Yes m -> m - | _ -> rhsExpr.Range + bindDebugPoint = spBind; isUse = false; isFromSource = isFromSource; pat = pat; rhs = rhsExpr; andBangs = []; body = innerComp; trivia = { LetOrUseBangKeyword = mBind }) -> if ceenv.isQuery then error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) @@ -1900,7 +1895,8 @@ let rec TryTranslateComputationExpression pat = SynPat.Named(ident = SynIdent(id, _); isThisVal = false) as pat rhs = rhsExpr andBangs = [] - body = innerComp) + body = innerComp + trivia = { LetOrUseBangKeyword = mBind }) | SynExpr.LetOrUseBang( bindDebugPoint = spBind isUse = true @@ -1908,12 +1904,8 @@ let rec TryTranslateComputationExpression pat = SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ])) as pat rhs = rhsExpr andBangs = [] - body = innerComp) -> - - let mBind = - match spBind with - | DebugPointAtBinding.Yes m -> m - | _ -> rhsExpr.Range + body = innerComp + trivia = { LetOrUseBangKeyword = mBind }) -> if ceenv.isQuery then error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) @@ -1988,9 +1980,9 @@ let rec TryTranslateComputationExpression Some(translatedCtxt bindExpr) // 'use! pat = e1 ... in e2' where 'pat' is not a simple name -> error - | SynExpr.LetOrUseBang(isUse = true; pat = pat; andBangs = andBangs) -> + | SynExpr.LetOrUseBang(isUse = true; andBangs = andBangs; trivia = { LetOrUseBangKeyword = mBind }) -> if isNil andBangs then - error (Error(FSComp.SR.tcInvalidUseBangBinding (), pat.Range)) + error (Error(FSComp.SR.tcInvalidUseBangBinding (), mBind)) else let m = match andBangs with @@ -2013,17 +2005,12 @@ let rec TryTranslateComputationExpression rhs = letRhsExpr andBangs = andBangBindings body = innerComp - range = letBindRange) -> + trivia = { LetOrUseBangKeyword = mBind }) -> if not (cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang) then - error (Error(FSComp.SR.tcAndBangNotSupported (), comp.Range)) + error (Error(FSComp.SR.tcAndBangNotSupported (), mBind)) if ceenv.isQuery then - error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), letBindRange)) - - let mBind = - match spBind with - | DebugPointAtBinding.Yes m -> m - | _ -> letRhsExpr.Range + error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) let sources = (letRhsExpr diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fs b/src/Compiler/SyntaxTree/SyntaxTrivia.fs index 0932befd7c1..88079864429 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fs +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fs @@ -93,10 +93,11 @@ type SynExprLetOrUseTrivia = [] type SynExprLetOrUseBangTrivia = { + LetOrUseBangKeyword: range EqualsRange: range option } - static member Zero: SynExprLetOrUseBangTrivia = { EqualsRange = None } + static member Zero: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = Range.Zero; EqualsRange = None } [] type SynExprMatchTrivia = diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi index 24bfc1b7a52..08f8228c3bc 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi @@ -139,6 +139,8 @@ type SynExprLetOrUseTrivia = [] type SynExprLetOrUseBangTrivia = { + /// The syntax range of the `let!` or `use!` keyword. + LetOrUseBangKeyword: range /// The syntax range of the `=` token. EqualsRange: range option } diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 2794edf560e..c930398b24b 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -4419,7 +4419,7 @@ declExpr: { let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5) let mEquals = rhs parseState 3 let m = unionRanges (rhs parseState 1) $8.Range - let trivia: SynExprLetOrUseBangTrivia = { EqualsRange = Some mEquals } + let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals } SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m, trivia) } | OBINDER headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let @@ -4428,7 +4428,7 @@ declExpr: let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range) let mEquals = rhs parseState 3 let m = unionRanges (rhs parseState 1) $8.Range - let trivia: SynExprLetOrUseBangTrivia = { EqualsRange = Some mEquals } + let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals } SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m, trivia) } | OBINDER headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let @@ -4437,12 +4437,12 @@ declExpr: let mEquals = rhs parseState 3 let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) let m = $4.Range.EndRange // zero-width range - let trivia: SynExprLetOrUseBangTrivia = { EqualsRange = Some mEquals } + let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals } SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, [], SynExpr.ImplicitZero m, mAll, trivia) } | DO_BANG typedSequentialExpr IN opt_OBLOCKSEP typedSequentialExprBlock %prec expr_let { let spBind = DebugPointAtBinding.NoneAtDo - let trivia: SynExprLetOrUseBangTrivia = { EqualsRange = None } + let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = Range.Zero; EqualsRange = None } SynExpr.LetOrUseBang(spBind, false, true, SynPat.Const(SynConst.Unit, $2.Range), $2, [], $5, unionRanges (rhs parseState 1) $5.Range, trivia) } | ODO_BANG typedSequentialExprBlock hardwhiteDefnBindingsTerminator %prec expr_let From 9e8d3d654975db8e1d67b5c581ab84f8a5e9d86d Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 12 Sep 2024 22:34:04 +0100 Subject: [PATCH 2/9] format code --- .../Checking/Expressions/CheckComputationExpressions.fs | 9 ++++++++- src/Compiler/SyntaxTree/SyntaxTrivia.fs | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs index a2cd83176a8..a01b3939703 100644 --- a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs @@ -1854,7 +1854,14 @@ let rec TryTranslateComputationExpression // or // --> build.BindReturn(e1, (fun _argN -> match _argN with pat -> expr-without-return)) | SynExpr.LetOrUseBang( - bindDebugPoint = spBind; isUse = false; isFromSource = isFromSource; pat = pat; rhs = rhsExpr; andBangs = []; body = innerComp; trivia = { LetOrUseBangKeyword = mBind }) -> + bindDebugPoint = spBind + isUse = false + isFromSource = isFromSource + pat = pat + rhs = rhsExpr + andBangs = [] + body = innerComp + trivia = { LetOrUseBangKeyword = mBind }) -> if ceenv.isQuery then error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fs b/src/Compiler/SyntaxTree/SyntaxTrivia.fs index 88079864429..bbc5f8ea0ce 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fs +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fs @@ -97,7 +97,11 @@ type SynExprLetOrUseBangTrivia = EqualsRange: range option } - static member Zero: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = Range.Zero; EqualsRange = None } + static member Zero: SynExprLetOrUseBangTrivia = + { + LetOrUseBangKeyword = Range.Zero + EqualsRange = None + } [] type SynExprMatchTrivia = From e94d0b4c16eef9073d8af4c7319695d3a614c4fd Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 12 Sep 2024 23:09:59 +0100 Subject: [PATCH 3/9] add test with new range --- .../Language/ComputationExpressionTests.fs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs index d5c6a5a0a63..7cc0f606d3c 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs @@ -246,4 +246,39 @@ let run r2 r3 = |> shouldFail |> withDiagnostics [ (Error 3345, Line 22, Col 9, Line 22, Col 13, "use! may not be combined with and!") + ] + + [] + let ``This control construct may only be used if the computation expression builder defines a 'Bind' method`` () = + Fsx """ +module Result = + let zip x1 x2 = + match x1,x2 with + | Ok x1res, Ok x2res -> Ok (x1res, x2res) + | Error e, _ -> Error e + | _, Error e -> Error e + +type ResultBuilder() = + member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2 + member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x + member _.Delay(f) = f() + + member _.TryWith(r: Result<'T,'U>, f) = + match r with + | Ok x -> Ok x + | Error e -> f e + +let result = ResultBuilder() + +let run r2 r3 = + result { + let! a = r2 + return! a + } + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 708, Line 23, Col 9, Line 23, Col 13, "This control construct may only be used if the computation expression builder defines a 'Bind' method") ] \ No newline at end of file From 4aba335f127f97be5a7b91e4f7b1c96c40dcbb34 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Wed, 18 Sep 2024 21:37:50 +0100 Subject: [PATCH 4/9] Update SyntaxTree tests --- ...ndBangHaveRangeThatStartsAtAndAndEndsAfterExpression.fs.bsl | 3 ++- ...SynExprAndBangRangeStartsAtAndAndEndsAfterExpression.fs.bsl | 3 ++- .../SynExprLetOrUseBangContainsTheRangeOfTheEqualsSign.fs.bsl | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/service/data/SyntaxTree/ComputationExpression/MultipleSynExprAndBangHaveRangeThatStartsAtAndAndEndsAfterExpression.fs.bsl b/tests/service/data/SyntaxTree/ComputationExpression/MultipleSynExprAndBangHaveRangeThatStartsAtAndAndEndsAfterExpression.fs.bsl index 2c7ca108bc8..72f8528c23f 100644 --- a/tests/service/data/SyntaxTree/ComputationExpression/MultipleSynExprAndBangHaveRangeThatStartsAtAndAndEndsAfterExpression.fs.bsl +++ b/tests/service/data/SyntaxTree/ComputationExpression/MultipleSynExprAndBangHaveRangeThatStartsAtAndAndEndsAfterExpression.fs.bsl @@ -40,7 +40,8 @@ ImplFile EqualsRange = (5,13--5,14) InKeyword = None })], YieldOrReturn ((false, true), Ident bar, (6,4--6,14)), - (3,4--6,14), { EqualsRange = Some (3,13--3,14) }), + (3,4--6,14), { LetOrUseBangKeyword = (3,4--3,8) + EqualsRange = Some (3,13--3,14) }), (2,6--7,1)), (2,0--7,1)), (2,0--7,1))], PreXmlDocEmpty, [], None, (2,0--7,1), { LeadingKeyword = None })], (true, true), { ConditionalDirectives = [] diff --git a/tests/service/data/SyntaxTree/ComputationExpression/SynExprAndBangRangeStartsAtAndAndEndsAfterExpression.fs.bsl b/tests/service/data/SyntaxTree/ComputationExpression/SynExprAndBangRangeStartsAtAndAndEndsAfterExpression.fs.bsl index 105a94f5b46..d0dd3083e64 100644 --- a/tests/service/data/SyntaxTree/ComputationExpression/SynExprAndBangRangeStartsAtAndAndEndsAfterExpression.fs.bsl +++ b/tests/service/data/SyntaxTree/ComputationExpression/SynExprAndBangRangeStartsAtAndAndEndsAfterExpression.fs.bsl @@ -29,7 +29,8 @@ ImplFile EqualsRange = (5,13--5,14) InKeyword = None })], YieldOrReturn ((false, true), Ident bar, (7,4--7,14)), - (3,4--7,14), { EqualsRange = Some (3,13--3,14) }), + (3,4--7,14), { LetOrUseBangKeyword = (3,4--3,8) + EqualsRange = Some (3,13--3,14) }), (2,6--8,1)), (2,0--8,1)), (2,0--8,1))], PreXmlDocEmpty, [], None, (2,0--8,1), { LeadingKeyword = None })], (true, true), { ConditionalDirectives = [] diff --git a/tests/service/data/SyntaxTree/Expression/SynExprLetOrUseBangContainsTheRangeOfTheEqualsSign.fs.bsl b/tests/service/data/SyntaxTree/Expression/SynExprLetOrUseBangContainsTheRangeOfTheEqualsSign.fs.bsl index 7dfd05fc1ad..a0f54029820 100644 --- a/tests/service/data/SyntaxTree/Expression/SynExprLetOrUseBangContainsTheRangeOfTheEqualsSign.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/SynExprLetOrUseBangContainsTheRangeOfTheEqualsSign.fs.bsl @@ -27,7 +27,8 @@ ImplFile InKeyword = None })], YieldOrReturn ((false, true), Const (Unit, (5,11--5,13)), (5,4--5,13)), - (3,4--5,13), { EqualsRange = Some (3,11--3,12) }), + (3,4--5,13), { LetOrUseBangKeyword = (3,4--3,8) + EqualsRange = Some (3,11--3,12) }), (2,5--6,1)), (2,0--6,1)), (2,0--6,1))], PreXmlDocEmpty, [], None, (2,0--6,1), { LeadingKeyword = None })], (true, true), { ConditionalDirectives = [] From 8fedf415262c4d35959dccb89dfcc676b4d3abf5 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 19 Sep 2024 00:03:36 +0100 Subject: [PATCH 5/9] Update SurfaceArea --- ...Sharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl | 4 +++- ...arp.Compiler.Service.SurfaceArea.netstandard20.release.bsl | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 543c92ef5cc..c41cdbbea96 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -10203,7 +10203,9 @@ FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: FSharp.Compiler.SyntaxTr FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] EqualsRange FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_EqualsRange() FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: System.String ToString() -FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) +FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: FSharp.Compiler.Text.Range LetOrUseBangKeyword +FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: FSharp.Compiler.Text.Range get_LetOrUseBangKeyword() +FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Void .ctor(FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia Zero FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia get_Zero() FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] InKeyword diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 543c92ef5cc..c41cdbbea96 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -10203,7 +10203,9 @@ FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: FSharp.Compiler.SyntaxTr FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] EqualsRange FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_EqualsRange() FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: System.String ToString() -FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) +FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: FSharp.Compiler.Text.Range LetOrUseBangKeyword +FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: FSharp.Compiler.Text.Range get_LetOrUseBangKeyword() +FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseBangTrivia: Void .ctor(FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range]) FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia Zero FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia get_Zero() FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] InKeyword From ad193ed034df385e3cc04141a5dd382f2234ce81 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 19 Sep 2024 00:06:34 +0100 Subject: [PATCH 6/9] release notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.200.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md index a2a0f964f7f..ce187cb8421 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md @@ -7,5 +7,6 @@ ### Changed * Make ILTypeDef interface impls calculation lazy. ([PR #17392](https://github.com/dotnet/fsharp/pull/17392)) +* Better ranges for CE `let!` and `use!` error reporting. ([PR #17712](https://github.com/dotnet/fsharp/pull/17712)) ### Breaking Changes From 499cb6e85f31aa82f5481d5541796e5ba62c3186 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 19 Sep 2024 09:07:42 +0100 Subject: [PATCH 7/9] Update tests --- .../Checking/Expressions/CheckComputationExpressions.fs | 7 ++++++- .../Conformance/DataExpressions/ComputationExpressions.fs | 8 ++++---- tests/fsharp/typecheck/sigs/neg61.bsl | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs index a01b3939703..f770290e728 100644 --- a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs @@ -2014,7 +2014,12 @@ let rec TryTranslateComputationExpression body = innerComp trivia = { LetOrUseBangKeyword = mBind }) -> if not (cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang) then - error (Error(FSComp.SR.tcAndBangNotSupported (), mBind)) + let andBangRange = + match andBangBindings with + | [] -> comp.Range + | h :: _ -> h.Trivia.AndBangKeyword + + error (Error(FSComp.SR.tcAndBangNotSupported (), andBangRange)) if ceenv.isQuery then error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) diff --git a/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs b/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs index 715a1e060a1..c6269705d17 100644 --- a/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs +++ b/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs @@ -281,7 +281,7 @@ let ceResult : Trace = return if y then x else -1 } """ - [| FSharpDiagnosticSeverity.Error, 3344, (6, 9, 8, 35), "This feature is not supported in this version of F#. You may need to add /langversion:preview to use this feature." |] + [| FSharpDiagnosticSeverity.Error, 3344, (7, 9, 7, 13), "This feature is not supported in this version of F#. You may need to add /langversion:preview to use this feature." |] [] let ``AndBang TraceMultiBindingMonoid`` () = @@ -582,7 +582,7 @@ let _ = return x + y } """ - [|(FSharpDiagnosticSeverity.Error, 3343, (6, 9, 6, 25), "The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a 'Bind2' method or appropriate 'MergeSources' and 'Bind' methods")|] + [|(FSharpDiagnosticSeverity.Error, 3343, (6, 9, 6, 13), "The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a 'Bind2' method or appropriate 'MergeSources' and 'Bind' methods")|] [] let ``AndBang Negative TraceApplicative missing Bind and BindReturn`` () = @@ -596,7 +596,7 @@ let _ = return x + y } """ - [|(FSharpDiagnosticSeverity.Error, 708, (6, 9, 6, 25), "This control construct may only be used if the computation expression builder defines a 'Bind' method")|] + [|(FSharpDiagnosticSeverity.Error, 708, (6, 9, 6, 13), "This control construct may only be used if the computation expression builder defines a 'Bind' method")|] [] @@ -612,7 +612,7 @@ let _ = return x + y } """ - [| FSharpDiagnosticSeverity.Error, 708, (7, 9, 7, 25), "This control construct may only be used if the computation expression builder defines a 'Bind' method" |] + [| FSharpDiagnosticSeverity.Error, 708, (7, 9, 7, 13), "This control construct may only be used if the computation expression builder defines a 'Bind' method" |] [] let ``AndBang TraceApplicative with do-bang`` () = diff --git a/tests/fsharp/typecheck/sigs/neg61.bsl b/tests/fsharp/typecheck/sigs/neg61.bsl index e0b2fb5eafb..7f0f25588da 100644 --- a/tests/fsharp/typecheck/sigs/neg61.bsl +++ b/tests/fsharp/typecheck/sigs/neg61.bsl @@ -59,7 +59,7 @@ neg61.fs(86,13,86,16): typecheck error FS3141: 'try/finally' expressions may not neg61.fs(92,13,92,70): typecheck error FS3142: 'use' expressions may not be used in queries -neg61.fs(97,13,97,33): typecheck error FS3143: 'let!', 'use!' and 'do!' expressions may not be used in queries +neg61.fs(97,13,97,17): typecheck error FS3143: 'let!', 'use!' and 'do!' expressions may not be used in queries neg61.fs(102,13,102,28): typecheck error FS3145: This is not a known query operator. Query operators are identifiers such as 'select', 'where', 'sortBy', 'thenBy', 'groupBy', 'groupValBy', 'join', 'groupJoin', 'sumBy' and 'averageBy', defined using corresponding methods on the 'QueryBuilder' type. From 16e300bed9e14786514ad1fd1d8550c77e0b6b81 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 19 Sep 2024 20:50:30 +0100 Subject: [PATCH 8/9] Add more test --- .../CheckComputationExpressions.fs | 4 +- .../Language/ComputationExpressionTests.fs | 126 ++++++++++++++++++ tests/fsharp/typecheck/sigs/neg61.bsl | 2 - 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs index f770290e728..5d9f3562ed9 100644 --- a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs @@ -1589,10 +1589,10 @@ let rec TryTranslateComputationExpression Some(TranslateComputationExpression ceenv CompExprTranslationPass.Initial q varSpace innerComp2 translatedCtxt) else - if ceenv.isQuery && not (innerComp1.IsArbExprAndThusAlreadyReportedError) then match innerComp1 with - | SynExpr.JoinIn _ -> () // an error will be reported later when we process innerComp1 as a sequential + | SynExpr.JoinIn _ -> () + | SynExpr.DoBang(range = m) -> errorR (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), m)) | _ -> errorR (Error(FSComp.SR.tcUnrecognizedQueryOperator (), innerComp1.RangeOfFirstPortion)) match diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs index 7cc0f606d3c..7b53bfac0cc 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs @@ -281,4 +281,130 @@ let run r2 r3 = |> shouldFail |> withDiagnostics [ (Error 708, Line 23, Col 9, Line 23, Col 13, "This control construct may only be used if the computation expression builder defines a 'Bind' method") + ] + + [] + let ``do! expressions may not be used in queries`` () = + Fsx """ +query { + do! failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 3, Col 5, Line 3, Col 20, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``let! expressions may not be used in queries`` () = + Fsx """ +query { + let! x = failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 3, Col 5, Line 3, Col 9, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``let!, and! expressions may not be used in queries`` () = + Fsx """ +query { + let! x = failwith "" + and! y = failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 3, Col 5, Line 3, Col 9, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``use! expressions may not be used in queries`` () = + Fsx """ +query { + use! x = failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 3, Col 5, Line 3, Col 9, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``do! expressions may not be used in queries(SynExpr.Sequential)`` () = + Fsx """ +query { + for c in [1..10] do + do! failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 4, Col 5, Line 4, Col 20, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``let! expressions may not be used in queries(SynExpr.Sequential)`` () = + Fsx """ +query { + for c in [1..10] do + let! x = failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 4, Col 5, Line 4, Col 9, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``let!, and! expressions may not be used in queries(SynExpr.Sequential)`` () = + Fsx """ +query { + for c in [1..10] do + let! x = failwith "" + and! y = failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 4, Col 5, Line 4, Col 9, "'let!', 'use!' and 'do!' expressions may not be used in queries") + ] + + [] + let ``use! expressions may not be used in queries(SynExpr.Sequential)`` () = + Fsx """ +query { + for c in [1..10] do + use! x = failwith "" + yield 1 +} + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3143, Line 4, Col 5, Line 4, Col 9, "'let!', 'use!' and 'do!' expressions may not be used in queries") ] \ No newline at end of file diff --git a/tests/fsharp/typecheck/sigs/neg61.bsl b/tests/fsharp/typecheck/sigs/neg61.bsl index 7f0f25588da..8a8bd79e168 100644 --- a/tests/fsharp/typecheck/sigs/neg61.bsl +++ b/tests/fsharp/typecheck/sigs/neg61.bsl @@ -61,8 +61,6 @@ neg61.fs(92,13,92,70): typecheck error FS3142: 'use' expressions may not be used neg61.fs(97,13,97,17): typecheck error FS3143: 'let!', 'use!' and 'do!' expressions may not be used in queries -neg61.fs(102,13,102,28): typecheck error FS3145: This is not a known query operator. Query operators are identifiers such as 'select', 'where', 'sortBy', 'thenBy', 'groupBy', 'groupValBy', 'join', 'groupJoin', 'sumBy' and 'averageBy', defined using corresponding methods on the 'QueryBuilder' type. - neg61.fs(102,13,102,28): typecheck error FS3143: 'let!', 'use!' and 'do!' expressions may not be used in queries neg61.fs(107,13,107,21): typecheck error FS3144: 'return' and 'return!' may not be used in queries From 4ca6a6e6a57da59d5e1ce2f86fee3e1f5f00593a Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 20 Sep 2024 18:31:02 +0100 Subject: [PATCH 9/9] one more test --- .../Language/ComputationExpressionTests.fs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs index 7b53bfac0cc..8333657e699 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs @@ -283,6 +283,41 @@ let run r2 r3 = (Error 708, Line 23, Col 9, Line 23, Col 13, "This control construct may only be used if the computation expression builder defines a 'Bind' method") ] + [] + let ``This control construct may only be used if the computation expression builder defines a 'Using' method`` () = + Fsx """ +module Result = + let zip x1 x2 = + match x1,x2 with + | Ok x1res, Ok x2res -> Ok (x1res, x2res) + | Error e, _ -> Error e + | _, Error e -> Error e + +type ResultBuilder() = + member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2 + member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x + member _.Delay(f) = f() + + member _.TryWith(r: Result<'T,'U>, f) = + match r with + | Ok x -> Ok x + | Error e -> f e + +let result = ResultBuilder() + +let run r2 r3 = + result { + use! a = r2 + return! a + } + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 708, Line 23, Col 9, Line 23, Col 13, "This control construct may only be used if the computation expression builder defines a 'Using' method") + ] + [] let ``do! expressions may not be used in queries`` () = Fsx """