Skip to content

Commit 69cc8a4

Browse files
authored
Better error reporting for use (#17811)
1 parent 4b29a25 commit 69cc8a4

File tree

34 files changed

+205
-81
lines changed

34 files changed

+205
-81
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818
* Better ranges for CE `do!` error reporting. ([PR #17779](https://github.com/dotnet/fsharp/pull/17779))
1919
* Better ranges for CE `return, yield, return! and yield!` error reporting. ([PR #17792](https://github.com/dotnet/fsharp/pull/17792))
2020
* Better ranges for CE `match!`. ([PR #17789](https://github.com/dotnet/fsharp/pull/17789))
21+
* Better ranges for CE `use` error reporting. ([PR #17811](https://github.com/dotnet/fsharp/pull/17811))
2122

2223
### Breaking Changes

src/Compiler/Checking/Expressions/CheckComputationExpressions.fs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,11 +1805,8 @@ let rec TryTranslateComputationExpression
18051805
| SynExpr.LetOrUse(
18061806
isUse = true
18071807
bindings = [ SynBinding(kind = SynBindingKind.Normal; headPat = pat; expr = rhsExpr; debugPoint = spBind) ]
1808-
body = innerComp) ->
1809-
let mBind =
1810-
match spBind with
1811-
| DebugPointAtBinding.Yes m -> m
1812-
| _ -> rhsExpr.Range
1808+
body = innerComp
1809+
trivia = { LetOrUseKeyword = mBind }) ->
18131810

18141811
if ceenv.isQuery then
18151812
error (Error(FSComp.SR.tcUseMayNotBeUsedInQueries (), mBind))

src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,10 @@ let TcSequenceExpression (cenv: TcFileState) env tpenv comp (overallTy: OverallT
232232
// 'use x = expr in expr'
233233
| SynExpr.LetOrUse(
234234
isUse = true
235-
bindings = [ SynBinding(kind = SynBindingKind.Normal; headPat = pat; expr = rhsExpr; debugPoint = spBind) ]
235+
bindings = [ SynBinding(kind = SynBindingKind.Normal; headPat = pat; expr = rhsExpr) ]
236236
body = innerComp
237-
range = wholeExprMark) ->
237+
range = wholeExprMark
238+
trivia = { LetOrUseKeyword = mBind }) ->
238239

239240
let bindPatTy = NewInferenceType g
240241
let inputExprTy = NewInferenceType g
@@ -252,11 +253,6 @@ let TcSequenceExpression (cenv: TcFileState) env tpenv comp (overallTy: OverallT
252253
let envinner = { envinner with eIsControlFlow = true }
253254
tcSequenceExprBody envinner genOuterTy tpenv innerComp
254255

255-
let mBind =
256-
match spBind with
257-
| DebugPointAtBinding.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Binding)
258-
| _ -> inputExpr.Range
259-
260256
let inputExprMark = inputExpr.Range
261257

262258
let matchv, matchExpr =

src/Compiler/SyntaxTree/ParseHelpers.fs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,22 @@ let mkLocalBindings (mWhole, BindingSetPreAttrs(_, isRec, isUse, declsPreAttrs,
10491049
else
10501050
Some mIn)
10511051

1052-
SynExpr.LetOrUse(isRec, isUse, decls, body, mWhole, { InKeyword = mIn })
1052+
let mLetOrUse =
1053+
match decls with
1054+
| SynBinding(trivia = trivia) :: _ -> trivia.LeadingKeyword.Range
1055+
| _ -> Range.Zero
1056+
1057+
SynExpr.LetOrUse(
1058+
isRec,
1059+
isUse,
1060+
decls,
1061+
body,
1062+
mWhole,
1063+
{
1064+
LetOrUseKeyword = mLetOrUse
1065+
InKeyword = mIn
1066+
}
1067+
)
10531068

10541069
let mkDefnBindings (mWhole, BindingSetPreAttrs(_, isRec, isUse, declsPreAttrs, _bindingSetRange), attrs, vis, attrsm) =
10551070
if isUse then

src/Compiler/SyntaxTree/SyntaxTrivia.fs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,15 @@ type SynExprDotLambdaTrivia =
8585
[<NoEquality; NoComparison>]
8686
type SynExprLetOrUseTrivia =
8787
{
88+
LetOrUseKeyword: range
8889
InKeyword: range option
8990
}
9091

91-
static member Zero: SynExprLetOrUseTrivia = { InKeyword = None }
92+
static member Zero: SynExprLetOrUseTrivia =
93+
{
94+
InKeyword = None
95+
LetOrUseKeyword = Range.Zero
96+
}
9297

9398
[<NoEquality; NoComparison>]
9499
type SynExprLetOrUseBangTrivia =

src/Compiler/SyntaxTree/SyntaxTrivia.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ type SynExprDotLambdaTrivia =
129129
[<NoEquality; NoComparison>]
130130
type SynExprLetOrUseTrivia =
131131
{
132+
/// The syntax range of the `let` or `use` keyword.
133+
LetOrUseKeyword: range
132134
/// The syntax range of the `in` keyword.
133135
InKeyword: range option
134136
}

tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,4 +644,85 @@ let indexHandler (): Task<string> =
644644
'string'
645645
but here has type
646646
'Task<bool>' ")
647+
]
648+
649+
[<Fact>]
650+
let ``use expressions may not be used in queries(SynExpr.Sequential)`` () =
651+
Fsx """
652+
let x11 =
653+
query { for c in [1..10] do
654+
use x = { new System.IDisposable with __.Dispose() = () }
655+
yield 1 }
656+
"""
657+
|> ignoreWarnings
658+
|> typecheck
659+
|> shouldFail
660+
|> withDiagnostics [
661+
(Error 3142, Line 4, Col 13, Line 4, Col 16, "'use' expressions may not be used in queries")
662+
]
663+
664+
[<Fact>]
665+
let ``use, This control construct may only be used if the computation expression builder defines a 'Using' method`` () =
666+
Fsx """
667+
module Result =
668+
let zip x1 x2 =
669+
match x1,x2 with
670+
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
671+
| Error e, _ -> Error e
672+
| _, Error e -> Error e
673+
674+
type ResultBuilder() =
675+
member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2
676+
member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x
677+
678+
member _.YieldReturn(x: Result<'T,'U>) = x
679+
member _.Return(x: 'T) = Ok x
680+
681+
let result = ResultBuilder()
682+
683+
let run r2 r3 =
684+
result {
685+
use b = r2
686+
return Ok 0
687+
}
688+
"""
689+
|> ignoreWarnings
690+
|> typecheck
691+
|> shouldFail
692+
|> withDiagnostics [
693+
(Error 708, Line 20, Col 9, Line 20, Col 12, "This control construct may only be used if the computation expression builder defines a 'Using' method")
694+
]
695+
696+
[<Fact>]
697+
let ``This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.`` () =
698+
Fsx """
699+
let x18rec2 =
700+
query {
701+
for d in [1..10] do
702+
let rec f x = x + 1 // error expected here - no recursive functions
703+
and g x = f x + 2
704+
select (f d)
705+
}
706+
707+
let x18inline =
708+
query {
709+
for d in [1..10] do
710+
let inline f x = x + 1 // error expected here - no inline functions
711+
select (f d)
712+
}
713+
714+
let x18mutable =
715+
query {
716+
for d in [1..10] do
717+
let mutable v = 1 // error expected here - no mutable values
718+
select (f d)
719+
}
720+
721+
"""
722+
|> typecheck
723+
|> shouldFail
724+
|> withDiagnostics [
725+
(Error 3147, Line 5, Col 17, Line 5, Col 20, "This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.")
726+
(Error 3147, Line 13, Col 20, Line 13, Col 23, "This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.")
727+
(Error 3147, Line 20, Col 21, Line 20, Col 22, "This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.")
647728
]

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10231,7 +10231,9 @@ FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.SyntaxTrivia
1023110231
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] InKeyword
1023210232
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_InKeyword()
1023310233
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: System.String ToString()
10234-
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
10234+
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.Text.Range LetOrUseKeyword
10235+
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.Text.Range get_LetOrUseKeyword()
10236+
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Void .ctor(FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
1023510237
FSharp.Compiler.SyntaxTrivia.SynExprMatchBangTrivia: FSharp.Compiler.Text.Range MatchBangKeyword
1023610238
FSharp.Compiler.SyntaxTrivia.SynExprMatchBangTrivia: FSharp.Compiler.Text.Range WithKeyword
1023710239
FSharp.Compiler.SyntaxTrivia.SynExprMatchBangTrivia: FSharp.Compiler.Text.Range get_MatchBangKeyword()

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10231,7 +10231,9 @@ FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.SyntaxTrivia
1023110231
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] InKeyword
1023210232
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_InKeyword()
1023310233
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: System.String ToString()
10234-
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
10234+
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.Text.Range LetOrUseKeyword
10235+
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: FSharp.Compiler.Text.Range get_LetOrUseKeyword()
10236+
FSharp.Compiler.SyntaxTrivia.SynExprLetOrUseTrivia: Void .ctor(FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
1023510237
FSharp.Compiler.SyntaxTrivia.SynExprMatchBangTrivia: FSharp.Compiler.Text.Range MatchBangKeyword
1023610238
FSharp.Compiler.SyntaxTrivia.SynExprMatchBangTrivia: FSharp.Compiler.Text.Range WithKeyword
1023710239
FSharp.Compiler.SyntaxTrivia.SynExprMatchBangTrivia: FSharp.Compiler.Text.Range get_MatchBangKeyword()

tests/fsharp/typecheck/sigs/neg59.bsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ neg59.fs(89,15,89,18): typecheck error FS3141: 'try/finally' expressions may not
3333

3434
neg59.fs(95,15,95,18): typecheck error FS3141: 'try/finally' expressions may not be used in queries
3535

36-
neg59.fs(102,15,102,64): typecheck error FS3142: 'use' expressions may not be used in queries
36+
neg59.fs(102,15,102,18): typecheck error FS3142: 'use' expressions may not be used in queries
3737

38-
neg59.fs(108,15,108,64): typecheck error FS3142: 'use' expressions may not be used in queries
38+
neg59.fs(108,15,108,18): typecheck error FS3142: 'use' expressions may not be used in queries
3939

4040
neg59.fs(113,15,113,25): typecheck error FS3140: 'while' expressions may not be used in queries
4141

0 commit comments

Comments
 (0)