Skip to content

Commit 92754eb

Browse files
Better CE error reporting when using use! with and! (#17671)
* Better `CE` error reporting when using `use!` with `and!` * release notes * use `and!` range instead of `use!` * Add AndBangKeyword trivia range * Update tests * surface area * Update SyntaxTreeTests --------- Co-authored-by: Vlad Zarytovskii <[email protected]>
1 parent ac3d3c8 commit 92754eb

File tree

13 files changed

+146
-11
lines changed

13 files changed

+146
-11
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* Applied nullable reference types to FSharp.Compiler.Service itself ([PR #15310](https://github.com/dotnet/fsharp/pull/15310))
4545
* Ensure that isinteractive multi-emit backing fields are not public. ([Issue #17439](https://github.com/dotnet/fsharp/issues/17438)), ([PR #17439](https://github.com/dotnet/fsharp/pull/17439))
4646
* Better error reporting for unions with duplicated fields. ([PR #17521](https://github.com/dotnet/fsharp/pull/17521))
47+
* Better CE error reporting when using `use!` with `and!` ([PR #17671](https://github.com/dotnet/fsharp/pull/17671))
4748
* Better error reporting for let bindings. ([PR #17601](https://github.com/dotnet/fsharp/pull/17601))
4849
* Optimize ILTypeDef interface impls reading from metadata. ([PR #17382](https://github.com/dotnet/fsharp/pull/17382))
4950
* Better error reporting for active patterns. ([PR #17666](https://github.com/dotnet/fsharp/pull/17666))

src/Compiler/Checking/Expressions/CheckComputationExpressions.fs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,12 +1987,17 @@ let rec TryTranslateComputationExpression
19871987

19881988
Some(translatedCtxt bindExpr)
19891989

1990-
// 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error
1990+
// 'use! pat = e1 ... in e2' where 'pat' is not a simple name -> error
19911991
| SynExpr.LetOrUseBang(isUse = true; pat = pat; andBangs = andBangs) ->
19921992
if isNil andBangs then
19931993
error (Error(FSComp.SR.tcInvalidUseBangBinding (), pat.Range))
19941994
else
1995-
error (Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs (), comp.Range))
1995+
let m =
1996+
match andBangs with
1997+
| [] -> comp.Range
1998+
| h :: _ -> h.Trivia.AndBangKeyword
1999+
2000+
error (Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs (), m))
19962001

19972002
// 'let! pat1 = expr1 and! pat2 = expr2 in ...' -->
19982003
// build.BindN(expr1, expr2, ...)

src/Compiler/SyntaxTree/SyntaxTree.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,14 @@ type SynExprAndBang =
870870
range: range *
871871
trivia: SynExprAndBangTrivia
872872

873+
member x.Range =
874+
match x with
875+
| SynExprAndBang(range = range) -> range
876+
877+
member this.Trivia =
878+
match this with
879+
| SynExprAndBang(trivia = trivia) -> trivia
880+
873881
[<NoEquality; NoComparison>]
874882
type SynExprRecordField =
875883
| SynExprRecordField of

src/Compiler/SyntaxTree/SyntaxTree.fsi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,12 @@ type SynExprAndBang =
982982
range: range *
983983
trivia: SynExprAndBangTrivia
984984

985+
/// Gets the syntax range of this construct
986+
member Range: range
987+
988+
/// Gets the trivia associated with this construct
989+
member Trivia: SynExprAndBangTrivia
990+
985991
[<NoEquality; NoComparison>]
986992
type SynExprRecordField =
987993
| SynExprRecordField of

src/Compiler/SyntaxTree/SyntaxTrivia.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ type SynBindingTrivia =
270270
[<NoEquality; NoComparison>]
271271
type SynExprAndBangTrivia =
272272
{
273+
AndBangKeyword: range
273274
EqualsRange: range
274275
InKeyword: range option
275276
}

src/Compiler/SyntaxTree/SyntaxTrivia.fsi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,10 @@ type SynBindingTrivia =
334334
[<NoEquality; NoComparison>]
335335
type SynExprAndBangTrivia =
336336
{
337+
/// The syntax range of the `and!` keyword
338+
AndBangKeyword: range
337339
/// The syntax range of the `=` token.
338340
EqualsRange: range
339-
340341
/// The syntax range of the `in` keyword.
341342
InKeyword: range option
342343
}

src/Compiler/pars.fsy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4079,15 +4079,17 @@ moreBinders:
40794079
let mEquals = rhs parseState 3
40804080
let m = unionRanges (rhs parseState 1) $4.Range
40814081
let mIn = rhs parseState 5
4082-
SynExprAndBang(spBind, $1, true, $2, $4, m, { EqualsRange = mEquals; InKeyword = Some mIn }) :: $6 }
4082+
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
4083+
SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $6 }
40834084

40844085
| OAND_BANG headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
40854086
{ let report, mIn, _ = $5
40864087
report "and!" (rhs parseState 1) // report unterminated error
40874088
let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
40884089
let mEquals = rhs parseState 3
40894090
let m = unionRanges (rhs parseState 1) $4.Range
4090-
SynExprAndBang(spBind, $1, true, $2, $4, m, { EqualsRange = mEquals; InKeyword = mIn }) :: $7 }
4091+
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
4092+
SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $7 }
40914093

40924094
| %prec prec_no_more_attr_bindings
40934095
{ [] }

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,98 @@ let x = A.Prop { return 0 }
152152
|> compile
153153
|> shouldSucceed
154154
|> ignore
155+
156+
[<Fact>]
157+
let ``use! may not be combined with and!`` () =
158+
Fsx """
159+
module Result =
160+
let zip x1 x2 =
161+
match x1,x2 with
162+
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
163+
| Error e, _ -> Error e
164+
| _, Error e -> Error e
165+
166+
type ResultBuilder() =
167+
member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2
168+
member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x
169+
170+
let result = ResultBuilder()
171+
172+
let run r2 r3 =
173+
result {
174+
use! b = r2
175+
and! c = r3
176+
return b - c
177+
}
178+
"""
179+
|> ignoreWarnings
180+
|> typecheck
181+
|> shouldFail
182+
|> withDiagnostics [
183+
(Error 3345, Line 18, Col 9, Line 18, Col 13, "use! may not be combined with and!")
184+
]
185+
186+
[<Fact>]
187+
let ``multiple use! may not be combined with and!`` () =
188+
Fsx """
189+
module Result =
190+
let zip x1 x2 =
191+
match x1,x2 with
192+
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
193+
| Error e, _ -> Error e
194+
| _, Error e -> Error e
195+
196+
type ResultBuilder() =
197+
member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2
198+
member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x
199+
200+
let result = ResultBuilder()
201+
202+
let run r2 r3 =
203+
result {
204+
use! b = r2
205+
and! c = r3
206+
use! d = r2
207+
return b - c
208+
}
209+
"""
210+
|> ignoreWarnings
211+
|> typecheck
212+
|> shouldFail
213+
|> withDiagnostics [
214+
(Error 3345, Line 18, Col 9, Line 18, Col 13, "use! may not be combined with and!")
215+
]
216+
217+
[<Fact>]
218+
let ``multiple use! may not be combined with multiple and!`` () =
219+
Fsx """
220+
module Result =
221+
let zip x1 x2 =
222+
match x1,x2 with
223+
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
224+
| Error e, _ -> Error e
225+
| _, Error e -> Error e
226+
227+
type ResultBuilder() =
228+
member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2
229+
member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x
230+
member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x
231+
member _.Bind(x: Result<'T,'U>, f) = Result.bind f x
232+
233+
let result = ResultBuilder()
234+
235+
let run r2 r3 =
236+
result {
237+
let! c = r3
238+
and! c = r3
239+
use! b = r2
240+
and! c = r3
241+
return b - c
242+
}
243+
"""
244+
|> ignoreWarnings
245+
|> typecheck
246+
|> shouldFail
247+
|> withDiagnostics [
248+
(Error 3345, Line 22, Col 9, Line 22, Col 13, "use! may not be combined with and!")
249+
]

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7740,8 +7740,12 @@ FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynExpr get_body()
77407740
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynExprAndBang NewSynExprAndBang(FSharp.Compiler.Syntax.DebugPointAtBinding, Boolean, Boolean, FSharp.Compiler.Syntax.SynPat, FSharp.Compiler.Syntax.SynExpr, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia)
77417741
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynPat get_pat()
77427742
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynPat pat
7743+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia Trivia
7744+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia get_Trivia()
77437745
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia get_trivia()
77447746
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia trivia
7747+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range Range
7748+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range get_Range()
77457749
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range get_range()
77467750
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range range
77477751
FSharp.Compiler.Syntax.SynExprAndBang: Int32 Tag
@@ -10139,12 +10143,14 @@ FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: Microsoft.FSharp.Core.FSharpOpti
1013910143
FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_BarRange()
1014010144
FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: System.String ToString()
1014110145
FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], FSharp.Compiler.Text.Range)
10146+
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range AndBangKeyword
1014210147
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range EqualsRange
10148+
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range get_AndBangKeyword()
1014310149
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range get_EqualsRange()
1014410150
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] InKeyword
1014510151
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_InKeyword()
1014610152
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: System.String ToString()
10147-
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Void .ctor(FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
10153+
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Void .ctor(FSharp.Compiler.Text.Range, FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
1014810154
FSharp.Compiler.SyntaxTrivia.SynExprAnonRecdTrivia: FSharp.Compiler.Text.Range OpeningBraceRange
1014910155
FSharp.Compiler.SyntaxTrivia.SynExprAnonRecdTrivia: FSharp.Compiler.Text.Range get_OpeningBraceRange()
1015010156
FSharp.Compiler.SyntaxTrivia.SynExprAnonRecdTrivia: System.String ToString()

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7740,8 +7740,12 @@ FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynExpr get_body()
77407740
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynExprAndBang NewSynExprAndBang(FSharp.Compiler.Syntax.DebugPointAtBinding, Boolean, Boolean, FSharp.Compiler.Syntax.SynPat, FSharp.Compiler.Syntax.SynExpr, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia)
77417741
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynPat get_pat()
77427742
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Syntax.SynPat pat
7743+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia Trivia
7744+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia get_Trivia()
77437745
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia get_trivia()
77447746
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia trivia
7747+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range Range
7748+
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range get_Range()
77457749
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range get_range()
77467750
FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range range
77477751
FSharp.Compiler.Syntax.SynExprAndBang: Int32 Tag
@@ -10139,12 +10143,14 @@ FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: Microsoft.FSharp.Core.FSharpOpti
1013910143
FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_BarRange()
1014010144
FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: System.String ToString()
1014110145
FSharp.Compiler.SyntaxTrivia.SynEnumCaseTrivia: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], FSharp.Compiler.Text.Range)
10146+
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range AndBangKeyword
1014210147
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range EqualsRange
10148+
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range get_AndBangKeyword()
1014310149
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: FSharp.Compiler.Text.Range get_EqualsRange()
1014410150
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] InKeyword
1014510151
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] get_InKeyword()
1014610152
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: System.String ToString()
10147-
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Void .ctor(FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
10153+
FSharp.Compiler.SyntaxTrivia.SynExprAndBangTrivia: Void .ctor(FSharp.Compiler.Text.Range, FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range])
1014810154
FSharp.Compiler.SyntaxTrivia.SynExprAnonRecdTrivia: FSharp.Compiler.Text.Range OpeningBraceRange
1014910155
FSharp.Compiler.SyntaxTrivia.SynExprAnonRecdTrivia: FSharp.Compiler.Text.Range get_OpeningBraceRange()
1015010156
FSharp.Compiler.SyntaxTrivia.SynExprAnonRecdTrivia: System.String ToString()

0 commit comments

Comments
 (0)