Skip to content

Allow return|return! yield|yield! and type annotations without parentheses #18533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* Fix code completion considers types from own namespace non-imported ([PR #18518](https://github.com/dotnet/fsharp/issues/18518))
* Code completion: fix getting qualifier expression in do statements in type decls ([PR #18524](https://github.com/dotnet/fsharp/pull/18524))
* Fixed: [#18441](https://github.com/dotnet/fsharp/issues/18441) FSI multi-emit unstable. ([PR #18465](https://github.com/dotnet/fsharp/pull/18465))
* Fixed: Allow `return`, `return!`, `yield`, `yield!` type annotations without parentheses ([PR #18533](https://github.com/dotnet/fsharp/pull/18533))

### Added
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))
Expand Down
28 changes: 28 additions & 0 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -4430,6 +4430,34 @@ declExpr:
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
SynExpr.YieldOrReturn(($1, not $1), $2, (unionRanges (rhs parseState 1) $2.Range), trivia) }

| YIELD declExpr COLON typ
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
SynExpr.YieldOrReturn(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $4.Range), trivia) }

| YIELD declExpr opt_topReturnTypeWithTypeConstraints
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
let expr =
match $3 with
| None -> $2
| Some(_, SynReturnInfo((ty, _), m)) ->
SynExpr.Typed($2, ty, unionRanges $2.Range m)
SynExpr.YieldOrReturn(($1, not $1), expr, (unionRanges (rhs parseState 1) expr.Range), trivia) }

| YIELD_BANG declExpr COLON typ
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
SynExpr.YieldOrReturnFrom(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $2.Range), trivia) }

| YIELD_BANG declExpr opt_topReturnTypeWithTypeConstraints
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
let expr =
match $3 with
| None -> $2
| Some(_, SynReturnInfo((ty, _), m)) ->
SynExpr.Typed($2, ty, unionRanges $2.Range m)
SynExpr.YieldOrReturnFrom(($1, not $1), expr, (unionRanges (rhs parseState 1) $2.Range), trivia) }

| YIELD_BANG declExpr
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
SynExpr.YieldOrReturnFrom(($1, not $1), $2, (unionRanges (rhs parseState 1) $2.Range), trivia) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,41 @@ let x = lb {1; 2;}
|> shouldSucceed
|> ignore

[<Fact>]
let ``Allow CE return and type annotations to play well together without needing parentheses``() =
FSharp """
module ComputationExpressionTests
open System

type MyType() =
interface IDisposable with
member this.Dispose () = ()

let f () =
async {
return new MyType() : IDisposable
}

let f1 () =
async {
return new MyType() :> IDisposable
}

let f2 () : Async<IDisposable> =
async {
return new MyType()
}

let f3 () =
async {
return (new MyType() : IDisposable)
}
"""
|> asExe
|> withOptions ["--nowarn:988"]
|> compileAndRun
|> shouldSucceed

[<Fact>]
let ``A CE explicitly using Zero fails without a defined Zero``() =
FSharp """
Expand Down
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 01.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

let _ =
async {
return new MyType() : IDisposable
}

36 changes: 36 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 01.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 01.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
Typed
(New
(false,
LongIdent (SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,25--5,27)), (5,15--5,27)),
LongIdent
(SynLongIdent ([IDisposable], [], [None])),
(5,15--5,41)), (5,8--5,41),
{ YieldOrReturnKeyword = (5,8--5,14) }), (4,10--6,5)),
(4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 02.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

let _ =
async {
return (new MyType() : IDisposable)
}

39 changes: 39 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 02.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 02.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
Paren
(Typed
(New
(false,
LongIdent
(SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,26--5,28)), (5,16--5,28)),
LongIdent
(SynLongIdent ([IDisposable], [], [None])),
(5,16--5,42)), (5,15--5,16), Some (5,42--5,43),
(5,15--5,43)), (5,8--5,43),
{ YieldOrReturnKeyword = (5,8--5,14) }), (4,10--6,5)),
(4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 03.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

let _ =
async {
return new MyType() :> IDisposable
}

36 changes: 36 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 03.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 03.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
Upcast
(New
(false,
LongIdent (SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,25--5,27)), (5,15--5,27)),
LongIdent
(SynLongIdent ([IDisposable], [], [None])),
(5,15--5,42)), (5,8--5,42),
{ YieldOrReturnKeyword = (5,8--5,14) }), (4,10--6,5)),
(4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 04.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

let _ =
async {
return (new MyType() :> IDisposable)
}

39 changes: 39 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 04.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 04.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
Paren
(Upcast
(New
(false,
LongIdent
(SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,26--5,28)), (5,16--5,28)),
LongIdent
(SynLongIdent ([IDisposable], [], [None])),
(5,16--5,43)), (5,15--5,16), Some (5,43--5,44),
(5,15--5,44)), (5,8--5,44),
{ YieldOrReturnKeyword = (5,8--5,14) }), (4,10--6,5)),
(4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 05.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

let _ =
async {
return new MyType()
}

32 changes: 32 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 05.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 05.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
New
(false,
LongIdent (SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,25--5,27)), (5,15--5,27)),
(5,8--5,27), { YieldOrReturnKeyword = (5,8--5,14) }),
(4,10--6,5)), (4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 06.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

let _ =
async {
return (new MyType())
}

34 changes: 34 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 06.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 06.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
Paren
(New
(false,
LongIdent (SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,26--5,28)), (5,16--5,28)),
(5,15--5,16), Some (5,28--5,29), (5,15--5,29)),
(5,8--5,29), { YieldOrReturnKeyword = (5,8--5,14) }),
(4,10--6,5)), (4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 07.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

let _ =
async {
return new MyType() :
}
36 changes: 36 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Return 07.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Return 07.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Wild (3,4--3,5), None,
App
(NonAtomic, false, Ident async,
ComputationExpr
(false,
YieldOrReturn
((false, true),
Typed
(New
(false,
LongIdent (SynLongIdent ([MyType], [], [None])),
Const (Unit, (5,25--5,27)), (5,15--5,27)),
FromParseError (5,29--5,29), (5,15--5,29)),
(5,8--5,29), { YieldOrReturnKeyword = (5,8--5,14) }),
(4,10--6,5)), (4,4--6,5)), (3,4--3,5), NoneAtLet,
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,6--3,7) })], (3,0--6,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(6,4)-(6,5) parse error Unexpected symbol '}' in expression
Loading