diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md index ca425fb63c4..ec394e2c1cc 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md @@ -21,6 +21,7 @@ * Fix FS3356 false positive for instance extension members with same name on different types, introduced by [#18821](https://github.com/dotnet/fsharp/pull/18821). ([PR #19260](https://github.com/dotnet/fsharp/pull/19260)) * Fix graph-based type checking incorrectly resolving dependencies when the same module name is defined across multiple files in the same namespace. ([PR #19280](https://github.com/dotnet/fsharp/pull/19280)) * F# Scripts: Fix default reference paths resolving when an SDK directory is specified. ([PR #19270](https://github.com/dotnet/fsharp/pull/19270)) +* Fix a bug where `let!` and `use!` were incorrectly allowed outside computation expressions. [PR #19347](https://github.com/dotnet/fsharp/pull/19347) ### Added * FSharpType: add ImportILType ([PR #19300](https://github.com/dotnet/fsharp/pull/19300)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index be76ee77ac5..1b56e4422b6 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -6002,7 +6002,13 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy.Commit, env.eAccessRights) cenv.TcArrayOrListComputedExpression cenv env overallTy tpenv (isArray, comp) m - | SynExpr.LetOrUse _ -> + | SynExpr.LetOrUse letOrUse -> + match letOrUse with + | { Bindings = SynBinding(trivia = { LeadingKeyword = leadingKeyword }) :: _ } + when letOrUse.IsBang -> + errorR(Error(FSComp.SR.tcConstructRequiresComputationExpression(), leadingKeyword.Range)) + | _ -> () + TcLinearExprs (TcExprThatCanBeCtorBody cenv) cenv env overallTy tpenv false synExpr id | SynExpr.TryWith (synBodyExpr, synWithClauses, mTryToLast, spTry, spWith, trivia) -> @@ -6110,9 +6116,6 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE | SynExpr.MatchBang (trivia = { MatchBangKeyword = m }) | SynExpr.WhileBang (range = m) -> error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), m)) - | LetOrUse({ Bindings = [ SynBinding(trivia = { LeadingKeyword = leadingKeyword }) ]}, true, _) -> - error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), leadingKeyword.Range)) - | SynExpr.IndexFromEnd (rightExpr, m) -> errorR(Error(FSComp.SR.tcTraitInvocationShouldUseTick(), m)) let adjustedExpr = ParseHelpers.adjustHatPrefixToTyparLookup m rightExpr diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ComputationExpressions/ComputationExpressions.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ComputationExpressions/ComputationExpressions.fs index 8e340726963..1aec462abf9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ComputationExpressions/ComputationExpressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ComputationExpressions/ComputationExpressions.fs @@ -261,3 +261,63 @@ module EmptyBodied = |> shouldFail |> withErrorCode 789 |> withErrorMessage "'{ }' is not a valid expression. Records must include at least one field. Empty sequences are specified by using Seq.empty or an empty list '[]'." + +module LetUseBangTests = + + [] + let ``let! isn't allowed outside of Computation Expression`` () = + FSharp """ + let test = + let! a = 1 + 1 + () + """ + |> asExe + |> compile + |> withErrorCode 750 + |> withErrorMessage "This construct may only be used within computation expressions" + + [] + let ``use! isn't allowed outside of Computation Expression`` () = + FSharp """ + open System + + let test = + use! a = + { new IDisposable with + member this.Dispose() = () + } + () + """ + |> asExe + |> compile + |> withErrorCode 750 + |> withErrorMessage "This construct may only be used within computation expressions" + + [] + let ``let! with and! aren't allowed outside of Computation Expression`` () = + FSharp """ + let test = + let! a = 1 + 1 + and! b = 1 + 1 + () + """ + |> asExe + |> compile + |> withErrorCode 750 + |> withErrorMessage "This construct may only be used within computation expressions" + + [] + let ``When let! is outside of Computation Expression, the analysis lasts`` () = + FSharp """ + let test = + let! a = 1 + 1 + return! 0 + """ + |> asExe + |> compile + |> withDiagnostics [ + (Error 750, Line 3, Col 13, Line 3, Col 17, + "This construct may only be used within computation expressions"); + (Error 748, Line 4, Col 13, Line 4, Col 20, + "This construct may only be used within computation expressions. To return a value from an ordinary function simply write the expression without 'return'.") + ]