Skip to content

Commit d67ac13

Browse files
authored
Allow return|return! yield|yield! and type annotations without parentheses (#18533)
1 parent c63e1ee commit d67ac13

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1122
-7
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
### Fixed
2-
2+
* Fixed: Allow `return`, `return!`, `yield`, `yield!` type annotations without parentheses ([PR #18533](https://github.com/dotnet/fsharp/pull/18533))
33
* Allow `let!` and `use!` type annotations without requiring parentheses ([PR #18508](https://github.com/dotnet/fsharp/pull/18508))
44
* Fix find all references for F# exceptions ([PR #18565](https://github.com/dotnet/fsharp/pull/18565))
55
* Shorthand lambda: fix completion for chained calls and analysis for unfinished expression ([PR #18560](https://github.com/dotnet/fsharp/pull/18560))

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* Fix code completion considers types from own namespace non-imported ([PR #18518](https://github.com/dotnet/fsharp/issues/18518))
3232
* Code completion: fix getting qualifier expression in do statements in type decls ([PR #18524](https://github.com/dotnet/fsharp/pull/18524))
3333
* Fixed: [#18441](https://github.com/dotnet/fsharp/issues/18441) FSI multi-emit unstable. ([PR #18465](https://github.com/dotnet/fsharp/pull/18465))
34+
* Fixed: Allow `return`, `return!`, `yield`, `yield!` type annotations without parentheses ([PR #18533](https://github.com/dotnet/fsharp/pull/18533))
3435

3536
### Added
3637
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))

src/Compiler/pars.fsy

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4426,13 +4426,22 @@ declExpr:
44264426
let mForLoopAll = rhs2 parseState 1 2
44274427
exprFromParseError (SynExpr.ForEach(spFor, spIn, SeqExprOnly false, true, $2, arbExpr ("forLoopCollection", mFor), arbExpr ("forLoopBody3", mForLoopBodyArb), mForLoopAll)) }
44284428

4429-
| YIELD declExpr
4429+
| YIELD declExpr COLON typ
44304430
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
4431-
SynExpr.YieldOrReturn(($1, not $1), $2, (unionRanges (rhs parseState 1) $2.Range), trivia) }
4431+
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
4432+
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang typedExpr.Range
4433+
SynExpr.YieldOrReturn(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $4.Range), trivia) }
44324434

4433-
| YIELD_BANG declExpr
4434-
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
4435-
SynExpr.YieldOrReturnFrom(($1, not $1), $2, (unionRanges (rhs parseState 1) $2.Range), trivia) }
4435+
| YIELD declExpr opt_topReturnTypeWithTypeConstraints
4436+
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
4437+
let expr =
4438+
match $3 with
4439+
| None -> $2
4440+
| Some(_, SynReturnInfo((ty, _), m)) ->
4441+
let m = unionRanges $2.Range m
4442+
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang m
4443+
SynExpr.Typed($2, ty, m)
4444+
SynExpr.YieldOrReturn(($1, not $1), expr, (unionRanges (rhs parseState 1) expr.Range), trivia) }
44364445

44374446
| YIELD recover
44384447
{ let mYieldAll = rhs parseState 1
@@ -4444,6 +4453,23 @@ declExpr:
44444453
let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
44454454
SynExpr.YieldOrReturnFrom(($1, not $1), arbExpr ("yield!", mYieldAll), mYieldAll, trivia) }
44464455

4456+
| YIELD_BANG declExpr COLON typ
4457+
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
4458+
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
4459+
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang typedExpr.Range
4460+
SynExpr.YieldOrReturnFrom(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $2.Range), trivia) }
4461+
4462+
| YIELD_BANG declExpr opt_topReturnTypeWithTypeConstraints
4463+
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
4464+
let expr =
4465+
match $3 with
4466+
| None -> $2
4467+
| Some(_, SynReturnInfo((ty, _), m)) ->
4468+
let m = unionRanges $2.Range m
4469+
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang m
4470+
SynExpr.Typed($2, ty, m)
4471+
SynExpr.YieldOrReturnFrom(($1, not $1), expr, (unionRanges (rhs parseState 1) $2.Range), trivia) }
4472+
44474473
| BINDER headBindingPattern EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
44484474
{ (* This rule handles the 'use!' and 'let!' binding expressions in computation expressions *)
44494475
(* The BINDER token represents keywords like 'use!' or 'let!' *)

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

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
22

33
namespace Language
44

@@ -23,6 +23,142 @@ let x = lb {1; 2;}
2323
|> shouldSucceed
2424
|> ignore
2525

26+
[<Fact>]
27+
let ``Version 9.0: Allow CE return and type annotations don't play well together needing parentheses``() =
28+
FSharp """
29+
module ComputationExpressionTests
30+
open System
31+
32+
type MyType() =
33+
interface IDisposable with
34+
member this.Dispose () = ()
35+
36+
let f () =
37+
async {
38+
return new MyType() : IDisposable
39+
}
40+
41+
let f1 () =
42+
async {
43+
return new MyType() :> IDisposable
44+
}
45+
46+
let f2 () : Async<IDisposable> =
47+
async {
48+
return new MyType()
49+
}
50+
51+
let f3 () =
52+
async {
53+
return (new MyType() : IDisposable)
54+
}
55+
"""
56+
|> withLangVersion90
57+
|> typecheck
58+
|> shouldFail
59+
|> withDiagnostics [
60+
(Error 3350, Line 11, Col 16, Line 11, Col 42, "Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater.")
61+
]
62+
63+
[<Fact>]
64+
let ``Version 9.0: Allow CE return! and type annotations don't to play well together needing parentheses``() =
65+
FSharp """
66+
module ComputationExpressionTests
67+
68+
type ResultBuilder() =
69+
member _.Return(x) = Ok x
70+
member _.ReturnFrom(x) = x
71+
member _.Bind(m, f) =
72+
match m with
73+
| Ok a -> f a
74+
| Error e -> Error e
75+
76+
let result = ResultBuilder()
77+
78+
let f() =
79+
result {
80+
return! Ok 1 : Result<int, string>
81+
}
82+
83+
let f1() =
84+
result {
85+
return! (Ok 1 : Result<int, string>)
86+
}
87+
"""
88+
|> withLangVersion90
89+
|> typecheck
90+
|> shouldFail
91+
|> withDiagnostics [
92+
(Error 3350, Line 16, Col 17, Line 16, Col 43, "Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater.")
93+
]
94+
95+
[<Fact>]
96+
let ``Preview: Allow CE return and type annotations to play well together without needing parentheses``() =
97+
FSharp """
98+
module ComputationExpressionTests
99+
open System
100+
101+
type MyType() =
102+
interface IDisposable with
103+
member this.Dispose () = ()
104+
105+
let f () =
106+
async {
107+
return new MyType() : IDisposable
108+
}
109+
110+
let f1 () =
111+
async {
112+
return new MyType() :> IDisposable
113+
}
114+
115+
let f2 () : Async<IDisposable> =
116+
async {
117+
return new MyType()
118+
}
119+
120+
let f3 () =
121+
async {
122+
return (new MyType() : IDisposable)
123+
}
124+
"""
125+
|> withLangVersionPreview
126+
|> asExe
127+
|> ignoreWarnings
128+
|> compileAndRun
129+
|> shouldSucceed
130+
131+
[<Fact>]
132+
let ``Preview: Allow CE return! and type annotations to play well together without needing parentheses``() =
133+
FSharp """
134+
module ComputationExpressionTests
135+
136+
type ResultBuilder() =
137+
member _.Return(x) = Ok x
138+
member _.ReturnFrom(x) = x
139+
member _.Bind(m, f) =
140+
match m with
141+
| Ok a -> f a
142+
| Error e -> Error e
143+
144+
let result = ResultBuilder()
145+
146+
let f() =
147+
result {
148+
return! Ok 1 : Result<int, string>
149+
}
150+
151+
let f1() =
152+
result {
153+
return! (Ok 1 : Result<int, string>)
154+
}
155+
"""
156+
|> withLangVersionPreview
157+
|> asExe
158+
|> ignoreWarnings
159+
|> compileAndRun
160+
|> shouldSucceed
161+
26162
[<Fact>]
27163
let ``A CE explicitly using Zero fails without a defined Zero``() =
28164
FSharp """

tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,4 +644,94 @@ let c = [ { 1;10 } ]
644644
|> withOptions [ "--nowarn:0020" ]
645645
|> withLangVersionPreview
646646
|> typecheck
647+
|> shouldSucceed
648+
649+
[<Fact>]
650+
let ``Version 9.0: Allow SE yield and type annotations don't play well together needing parentheses``() =
651+
FSharp """
652+
module SequenceExpressionTests
653+
open System
654+
655+
let f() =
656+
seq {
657+
yield 1 : int
658+
}
659+
660+
let f1() =
661+
seq {
662+
yield (1 : int)
663+
}
664+
"""
665+
|> withLangVersion90
666+
|> typecheck
667+
|> shouldFail
668+
|> withDiagnostics [
669+
(Error 3350, Line 7, Col 15, Line 7, Col 22, "Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater.")
670+
]
671+
672+
[<Fact>]
673+
let ``Preview: Allow SE yield and type annotations to play well together without needing parentheses``() =
674+
FSharp """
675+
module SequenceExpressionTests
676+
open System
677+
678+
let f() =
679+
seq {
680+
yield 1 : int
681+
}
682+
683+
let f1() =
684+
seq {
685+
yield (1 : int)
686+
}
687+
"""
688+
|> withLangVersionPreview
689+
|> asExe
690+
|> ignoreWarnings
691+
|> compileAndRun
692+
|> shouldSucceed
693+
694+
[<Fact>]
695+
let ``Version 9.0: Allow SE yield! and type annotations don't play well together needing parentheses``() =
696+
FSharp """
697+
module SequenceExpressionTests
698+
open System
699+
700+
let f() =
701+
seq {
702+
yield! [1;2] : int list
703+
}
704+
705+
let f1() =
706+
seq {
707+
yield! ([1;2] : int list)
708+
}
709+
"""
710+
|> withLangVersion90
711+
|> typecheck
712+
|> shouldFail
713+
|> withDiagnostics [
714+
(Error 3350, Line 7, Col 16, Line 7, Col 32, "Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater.")
715+
]
716+
717+
[<Fact>]
718+
let ``Preview: Allow SE yield! and type annotations to play well together without needing parentheses``() =
719+
FSharp """
720+
module SequenceExpressionTests
721+
open System
722+
723+
let f() =
724+
seq {
725+
yield! [1;2] : int list
726+
}
727+
728+
let f1() =
729+
seq {
730+
yield! ([1;2] : int list)
731+
}
732+
"""
733+
|> withLangVersionPreview
734+
|> asExe
735+
|> ignoreWarnings
736+
|> compileAndRun
647737
|> shouldSucceed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Module
2+
3+
let _ =
4+
async {
5+
return new MyType() : IDisposable
6+
}
7+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
ImplFile
2+
(ParsedImplFileInput
3+
("/root/Expression/Return 01.fs", false, QualifiedNameOfFile Module, [],
4+
[SynModuleOrNamespace
5+
([Module], false, NamedModule,
6+
[Let
7+
(false,
8+
[SynBinding
9+
(None, Normal, false, false, [],
10+
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
11+
SynValData
12+
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
13+
Wild (3,4--3,5), None,
14+
App
15+
(NonAtomic, false, Ident async,
16+
ComputationExpr
17+
(false,
18+
YieldOrReturn
19+
((false, true),
20+
Typed
21+
(New
22+
(false,
23+
LongIdent (SynLongIdent ([MyType], [], [None])),
24+
Const (Unit, (5,25--5,27)), (5,15--5,27)),
25+
LongIdent
26+
(SynLongIdent ([IDisposable], [], [None])),
27+
(5,15--5,41)), (5,8--5,41),
28+
{ YieldOrReturnKeyword = (5,8--5,14) }), (4,10--6,5)),
29+
(4,4--6,5)), (3,4--3,5), NoneAtLet,
30+
{ LeadingKeyword = Let (3,0--3,3)
31+
InlineKeyword = None
32+
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
33+
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
34+
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
35+
{ ConditionalDirectives = []
36+
WarnDirectives = []
37+
CodeComments = [] }, set []))
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Module
2+
3+
let _ =
4+
async {
5+
return (new MyType() : IDisposable)
6+
}
7+

0 commit comments

Comments
 (0)