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

Merged
merged 27 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a4246f0
Add new rules
edgarfgp May 5, 2025
5fe09e0
Add syntax tree tests
edgarfgp May 5, 2025
caeb72f
Add a component test
edgarfgp May 5, 2025
fa548f8
Merge branch 'main' into fix-15788
edgarfgp May 5, 2025
76e5921
ad recovery rules
edgarfgp May 6, 2025
5cd56a7
Merge branch 'fix-15788' of github.com:edgarfgp/fsharp into fix-15788
edgarfgp May 6, 2025
9d53ca2
release notes
edgarfgp May 6, 2025
2253c5b
Merge branch 'main' into fix-15788
edgarfgp May 6, 2025
31d8b99
release notes
edgarfgp May 6, 2025
5954b89
Merge branch 'fix-15788' of github.com:edgarfgp/fsharp into fix-15788
edgarfgp May 6, 2025
e0ff08d
Merge branch 'main' into fix-15788
edgarfgp May 7, 2025
3b1e699
Merge branch 'main' into fix-15788
edgarfgp May 12, 2025
b612546
Merge branch 'main' into fix-15788
edgarfgp May 13, 2025
6b82b9a
Merge branch 'main' into fix-15788
edgarfgp May 14, 2025
7440bc7
Merge branch 'main' into fix-15788
edgarfgp May 15, 2025
336fd39
Update syntax tree tests to include WarnDirectives
edgarfgp May 15, 2025
1168259
release notes
edgarfgp May 15, 2025
88b5c37
Merge branch 'fix-15788' of github.com:edgarfgp/fsharp into fix-15788
edgarfgp May 15, 2025
52c115b
Merge branch 'main' into fix-15788
edgarfgp May 16, 2025
955e13a
Merge branch 'main' into fix-15788
edgarfgp May 16, 2025
0326067
LanguageFeature.AllowTypedLetOrUseBang
edgarfgp May 16, 2025
80897f1
Merge branch 'main' into fix-15788
edgarfgp May 19, 2025
5aad722
fixCheckLanguageFeatureAndRecover placement
edgarfgp May 19, 2025
31c5eb2
place YIELD rules together
edgarfgp May 19, 2025
2c06b5c
update tests
edgarfgp May 19, 2025
f0b55bf
Merge branch 'main' into fix-15788
edgarfgp May 20, 2025
5cd90b0
remove redundant rule
edgarfgp May 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### Fixed

* Fixed: Allow `return`, `return!`, `yield`, `yield!` type annotations without parentheses ([PR #18533](https://github.com/dotnet/fsharp/pull/18533))
* Allow `let!` and `use!` type annotations without requiring parentheses ([PR #18508](https://github.com/dotnet/fsharp/pull/18508))
* Fix find all references for F# exceptions ([PR #18565](https://github.com/dotnet/fsharp/pull/18565))
* Shorthand lambda: fix completion for chained calls and analysis for unfinished expression ([PR #18560](https://github.com/dotnet/fsharp/pull/18560))
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
36 changes: 31 additions & 5 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -4426,13 +4426,22 @@ declExpr:
let mForLoopAll = rhs2 parseState 1 2
exprFromParseError (SynExpr.ForEach(spFor, spIn, SeqExprOnly false, true, $2, arbExpr ("forLoopCollection", mFor), arbExpr ("forLoopBody3", mForLoopBodyArb), mForLoopAll)) }

| YIELD declExpr
| YIELD declExpr COLON typ
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
SynExpr.YieldOrReturn(($1, not $1), $2, (unionRanges (rhs parseState 1) $2.Range), trivia) }
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang typedExpr.Range
SynExpr.YieldOrReturn(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $4.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) }
| YIELD declExpr opt_topReturnTypeWithTypeConstraints
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
let expr =
match $3 with
| None -> $2
| Some(_, SynReturnInfo((ty, _), m)) ->
let m = unionRanges $2.Range m
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang m
SynExpr.Typed($2, ty, m)
SynExpr.YieldOrReturn(($1, not $1), expr, (unionRanges (rhs parseState 1) expr.Range), trivia) }

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

| YIELD_BANG declExpr COLON typ
{ let trivia: SynExprYieldOrReturnFromTrivia = { YieldOrReturnFromKeyword = rhs parseState 1 }
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang typedExpr.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)) ->
let m = unionRanges $2.Range m
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang m
SynExpr.Typed($2, ty, m)
SynExpr.YieldOrReturnFrom(($1, not $1), expr, (unionRanges (rhs parseState 1) $2.Range), trivia) }

| BINDER headBindingPattern EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
{ (* This rule handles the 'use!' and 'let!' binding expressions in computation expressions *)
(* The BINDER token represents keywords like 'use!' or 'let!' *)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Language

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

[<Fact>]
let ``Version 9.0: Allow CE return and type annotations don't play well together 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)
}
"""
|> withLangVersion90
|> typecheck
|> shouldFail
|> withDiagnostics [
(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.")
]

[<Fact>]
let ``Version 9.0: Allow CE return! and type annotations don't to play well together needing parentheses``() =
FSharp """
module ComputationExpressionTests

type ResultBuilder() =
member _.Return(x) = Ok x
member _.ReturnFrom(x) = x
member _.Bind(m, f) =
match m with
| Ok a -> f a
| Error e -> Error e

let result = ResultBuilder()

let f() =
result {
return! Ok 1 : Result<int, string>
}

let f1() =
result {
return! (Ok 1 : Result<int, string>)
}
"""
|> withLangVersion90
|> typecheck
|> shouldFail
|> withDiagnostics [
(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.")
]

[<Fact>]
let ``Preview: 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)
}
"""
|> withLangVersionPreview
|> asExe
|> ignoreWarnings
|> compileAndRun
|> shouldSucceed

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

type ResultBuilder() =
member _.Return(x) = Ok x
member _.ReturnFrom(x) = x
member _.Bind(m, f) =
match m with
| Ok a -> f a
| Error e -> Error e

let result = ResultBuilder()

let f() =
result {
return! Ok 1 : Result<int, string>
}

let f1() =
result {
return! (Ok 1 : Result<int, string>)
}
"""
|> withLangVersionPreview
|> asExe
|> ignoreWarnings
|> compileAndRun
|> shouldSucceed

[<Fact>]
let ``A CE explicitly using Zero fails without a defined Zero``() =
FSharp """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,4 +644,94 @@ let c = [ { 1;10 } ]
|> withOptions [ "--nowarn:0020" ]
|> withLangVersionPreview
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Version 9.0: Allow SE yield and type annotations don't play well together needing parentheses``() =
FSharp """
module SequenceExpressionTests
open System

let f() =
seq {
yield 1 : int
}

let f1() =
seq {
yield (1 : int)
}
"""
|> withLangVersion90
|> typecheck
|> shouldFail
|> withDiagnostics [
(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.")
]

[<Fact>]
let ``Preview: Allow SE yield and type annotations to play well together without needing parentheses``() =
FSharp """
module SequenceExpressionTests
open System

let f() =
seq {
yield 1 : int
}

let f1() =
seq {
yield (1 : int)
}
"""
|> withLangVersionPreview
|> asExe
|> ignoreWarnings
|> compileAndRun
|> shouldSucceed

[<Fact>]
let ``Version 9.0: Allow SE yield! and type annotations don't play well together needing parentheses``() =
FSharp """
module SequenceExpressionTests
open System

let f() =
seq {
yield! [1;2] : int list
}

let f1() =
seq {
yield! ([1;2] : int list)
}
"""
|> withLangVersion90
|> typecheck
|> shouldFail
|> withDiagnostics [
(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.")
]

[<Fact>]
let ``Preview: Allow SE yield! and type annotations to play well together without needing parentheses``() =
FSharp """
module SequenceExpressionTests
open System

let f() =
seq {
yield! [1;2] : int list
}

let f1() =
seq {
yield! ([1;2] : int list)
}
"""
|> withLangVersionPreview
|> asExe
|> ignoreWarnings
|> compileAndRun
|> shouldSucceed
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
}

37 changes: 37 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,37 @@
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 = []
WarnDirectives = []
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)
}

Loading
Loading