Skip to content

Allow typed bindings(and!) in CE without parentheses #18682

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

Draft
wants to merge 36 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1799aaf
make attribute targets mismatches a warning and not an error.
edgarfgp Apr 23, 2025
55507e9
release notes
edgarfgp Apr 23, 2025
1738018
update tests
edgarfgp Apr 23, 2025
65f5bb6
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 24, 2025
0c97b9d
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 27, 2025
6f2b706
update baselines
edgarfgp Apr 29, 2025
e8f1bb0
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 29, 2025
75d8f5e
Update baselines
edgarfgp Apr 29, 2025
4f2e97e
Merge branch 'fix-attr-targets' of github.com:edgarfgp/fsharp into fi…
edgarfgp Apr 29, 2025
63be5d5
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 30, 2025
4248f2a
Move attribute form logic to an AP
edgarfgp Apr 30, 2025
cc96217
Merge branch 'main' into fix-attr-targets
edgarfgp May 1, 2025
e270b88
Merge branch 'main' into fix-attr-targets
edgarfgp May 1, 2025
e0cc65a
Merge branch 'main' into fix-attr-targets
edgarfgp May 2, 2025
1e29d58
Merge branch 'main' into fix-attr-targets
edgarfgp May 5, 2025
1470bf9
Merge branch 'main' into fix-attr-targets
edgarfgp May 6, 2025
8988215
Merge branch 'main' into fix-attr-targets
edgarfgp May 7, 2025
74712e8
Merge branch 'main' into fix-attr-targets
edgarfgp May 12, 2025
967c4a9
Merge branch 'main' of github.com:edgarfgp/fsharp
edgarfgp May 13, 2025
a30cef4
Merge branch 'dotnet:main' into main
edgarfgp May 22, 2025
5fa0480
Merge branch 'dotnet:main' into main
edgarfgp May 24, 2025
15e3d34
Merge branch 'dotnet:main' into main
edgarfgp May 29, 2025
b7ffcf8
Merge branch 'dotnet:main' into main
edgarfgp Jun 6, 2025
549f961
Add new parser rule and syntax tree tests
edgarfgp Jun 10, 2025
b3f4baa
Add CE test that uses let! and and!
edgarfgp Jun 10, 2025
98937bc
more syntax tree tests
edgarfgp Jun 10, 2025
a020505
more syntax tree tests
edgarfgp Jun 10, 2025
1ba22bf
Merge branch 'main' into allow-and-bang-typed-bindings
edgarfgp Jun 11, 2025
dce81de
more syntax tree tests
edgarfgp Jun 10, 2025
c921606
Merge branch 'allow-and-bang-typed-bindings' of github.com:edgarfgp/f…
edgarfgp Jun 11, 2025
86fedfd
Update LanguageFeature and release notes
edgarfgp Jun 11, 2025
aee26ec
update baselines
edgarfgp Jun 11, 2025
4712569
more tests
edgarfgp Jun 18, 2025
43a4c21
Merge branch 'main' into allow-and-bang-typed-bindings
edgarfgp Jun 18, 2025
3a346de
update tests
edgarfgp Jun 18, 2025
2e074f0
Unifying let, let! and! parser rules
edgarfgp Jun 19, 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
Expand Up @@ -7,7 +7,7 @@
* Fix parsing errors using anonymous records and units of measures ([PR #18543](https://github.com/dotnet/fsharp/pull/18543))
* Fix parsing errors using anonymous records and code quotations ([PR #18603](https://github.com/dotnet/fsharp/pull/18603))
* 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))
* Allow `let!`, `use!`, `and!` type annotations without requiring parentheses (([PR #18508](https://github.com/dotnet/fsharp/pull/18508) and [PR #18682](https://github.com/dotnet/fsharp/pull/18682)))
* 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))
* Completion: fix previous namespace considered opened [PR #18609](https://github.com/dotnet/fsharp/pull/18609)
Expand Down
2 changes: 1 addition & 1 deletion docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Warn when `unit` is passed to an `obj`-typed argument ([PR #18330](https://github.com/dotnet/fsharp/pull/18330))
* Fix parsing errors using anonymous records and units of measures ([PR #18543](https://github.com/dotnet/fsharp/pull/18543))
* Scoped Nowarn: added the #warnon compiler directive ([Language suggestion #278](https://github.com/fsharp/fslang-suggestions/issues/278), [RFC FS-1146 PR](https://github.com/fsharp/fslang-design/pull/782), [PR #18049](https://github.com/dotnet/fsharp/pull/18049))
* Allow `let!` and `use!` type annotations without requiring parentheses. ([PR #18508](https://github.com/dotnet/fsharp/pull/18508))
* Allow `let!`, `use!`, `and!` type annotations without requiring parentheses (([PR #18508](https://github.com/dotnet/fsharp/pull/18508) and [PR #18682](https://github.com/dotnet/fsharp/pull/18682)))

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1794,7 +1794,7 @@ let rec TryTranslateComputationExpression
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.UseBangBindingValueDiscard

let supportsTypedLetOrUseBang =
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.AllowTypedLetOrUseBang
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.AllowTypedLetUseAndBang

// use! x = ...
// use! (x) = ...
Expand Down
6 changes: 3 additions & 3 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ type LanguageFeature =
| UseBangBindingValueDiscard
| BetterAnonymousRecordParsing
| ScopedNowarn
| AllowTypedLetOrUseBang
| AllowTypedLetUseAndBang

/// LanguageVersion management
type LanguageVersion(versionText) =
Expand Down Expand Up @@ -236,7 +236,7 @@ type LanguageVersion(versionText) =
LanguageFeature.UseBangBindingValueDiscard, previewVersion
LanguageFeature.BetterAnonymousRecordParsing, previewVersion
LanguageFeature.ScopedNowarn, previewVersion
LanguageFeature.AllowTypedLetOrUseBang, previewVersion
LanguageFeature.AllowTypedLetUseAndBang, previewVersion
]

static let defaultLanguageVersion = LanguageVersion("default")
Expand Down Expand Up @@ -402,7 +402,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.UseBangBindingValueDiscard -> FSComp.SR.featureUseBangBindingValueDiscard ()
| LanguageFeature.BetterAnonymousRecordParsing -> FSComp.SR.featureBetterAnonymousRecordParsing ()
| LanguageFeature.ScopedNowarn -> FSComp.SR.featureScopedNowarn ()
| LanguageFeature.AllowTypedLetOrUseBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()
| LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ type LanguageFeature =
| UseBangBindingValueDiscard
| BetterAnonymousRecordParsing
| ScopedNowarn
| AllowTypedLetOrUseBang
| AllowTypedLetUseAndBang

/// LanguageVersion management
type LanguageVersion =
Expand Down
196 changes: 133 additions & 63 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -3520,6 +3520,57 @@ bindingPattern:
| headBindingPattern
{ $1, $1.Range }

/* Common pattern for bindings that may include type annotations
This rule unifies the pattern parsing for both regular 'let' bindings and
computation expression bindings (let!, use!, and!).

Returns: (pattern, pattern_range, optional_type)
- pattern: The parsed pattern (may be typed if annotation present)
- pattern_range: The full range including any type annotation
- optional_type: The type if annotation was present, None otherwise */
bindingPatternWithOptType:
| headBindingPattern
{ // Simple pattern without type annotation
$1, $1.Range, None }

| headBindingPattern COLON topTypeWithTypeConstraints
{ // Pattern with type annotation (e.g., x: int)
let ty, arity = $3
let mWhole = unionRanges $1.Range ty.Range
// Create a typed pattern node
let typedPat = SynPat.Typed($1, ty, mWhole)
typedPat, mWhole, Some ty }

| headBindingPattern COLON error
{ // Error recovery: incomplete type annotation (e.g., "let x:" with no type)
let mColon = rhs parseState 2
let mWhole = unionRanges $1.Range mColon
// Create a typed pattern with an error type for recovery
let errorType = SynType.FromParseError(mColon.EndRange)
let typedPat = SynPat.Typed($1, errorType, mWhole)
typedPat, mWhole, Some errorType }

/* Common rule for computation expression binding patterns
Handles the pattern part of let!, use!, and! bindings with consistent
type annotation support and language feature checking.

Returns: (pattern, pattern_range, isInline, isMutable, optional_type) */
ceBindingCore:
| opt_inline opt_mutable bindingPatternWithOptType
{ let pat, mPat, tyOpt = $3
let isInline = Option.isSome $1
let isMutable = Option.isSome $2

// For CE bindings, check language feature if type annotation is present
// This ensures that typed let!/use!/and! bindings are only allowed when
// the AllowTypedLetUseAndBang feature is enabled
match tyOpt with
| Some ty ->
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
| None -> ()

pat, mPat, isInline, isMutable, tyOpt }

opt_simplePatterns:
| simplePatterns
{ Some $1 }
Expand Down Expand Up @@ -4099,22 +4150,45 @@ recover:
{ debugPrint("recovering via EOF"); false }

moreBinders:
| AND_BANG headBindingPattern EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
{ let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
/* Refactored and! bindings to use unified ceBindingCore
This ensures consistent handling of patterns and type annotations
across all computation expression bindings */
| AND_BANG ceBindingCore EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
{ // Handle and! bindings with unified pattern parsing
let pat, mPat, isInline, isMutable, tyOpt = $2

// and! bindings don't support inline or mutable modifiers
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))

let mEquals = rhs parseState 3
let m = unionRanges (rhs parseState 1) $4.Range
// Debug point should span the entire binding: from AND_BANG through the expression
let spBind = DebugPointAtBinding.Yes(m)
let mIn = rhs parseState 5
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $6 }

| OAND_BANG headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
{ let report, mIn, _ = $5

// Note: For and!, we always use isRecursive=false and isUse=true
SynExprAndBang(spBind, false, true, pat, $4, m, trivia) :: $6 }

| OAND_BANG ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
{ // Offside-sensitive version of and! binding
let pat, mPat, isInline, isMutable, tyOpt = $2

// and! bindings don't support inline or mutable modifiers
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))

let report, mIn, _ = $5
report "and!" (rhs parseState 1) // report unterminated error
let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
let mEquals = rhs parseState 3
let m = unionRanges (rhs parseState 1) $4.Range
// Debug point should span the entire binding: from OAND_BANG through the expression
let spBind = DebugPointAtBinding.Yes(m)
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $7 }

// Note: For and!, we always use isRecursive=false and isUse=true
SynExprAndBang(spBind, false, true, pat, $4, m, trivia) :: $7 }

| %prec prec_no_more_attr_bindings
{ [] }
Expand Down Expand Up @@ -4429,7 +4503,7 @@ declExpr:
| YIELD declExpr COLON typ
{ let trivia: SynExprYieldOrReturnTrivia = { YieldOrReturnKeyword = rhs parseState 1 }
let typedExpr = SynExpr.Typed($2, $4, unionRanges $2.Range $4.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang typedExpr.Range
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang typedExpr.Range
SynExpr.YieldOrReturn(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $4.Range), trivia) }

| YIELD declExpr opt_topReturnTypeWithTypeConstraints
Expand All @@ -4439,7 +4513,7 @@ declExpr:
| None -> $2
| Some(_, SynReturnInfo((ty, _), m)) ->
let m = unionRanges $2.Range m
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang m
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang m
SynExpr.Typed($2, ty, m)
SynExpr.YieldOrReturn(($1, not $1), expr, (unionRanges (rhs parseState 1) expr.Range), trivia) }

Expand All @@ -4456,7 +4530,7 @@ declExpr:
| 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
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang typedExpr.Range
SynExpr.YieldOrReturnFrom(($1, not $1), typedExpr, (unionRanges (rhs parseState 1) $2.Range), trivia) }

| YIELD_BANG declExpr opt_topReturnTypeWithTypeConstraints
Expand All @@ -4466,73 +4540,69 @@ declExpr:
| None -> $2
| Some(_, SynReturnInfo((ty, _), m)) ->
let m = unionRanges $2.Range m
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang m
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang 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!' *)
(* headBindingPattern represents patterns in the binding:
- Underscore ('_') patterns are preserved as SynPat.Wild
- Named patterns ('__') are represented as SynPat.Named
- Identifiers (like 'value') are represented as SynPat.LongIdent *)
let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5)
/* Refactored let! and use! bindings to use unified ceBindingCore
This ensures consistent handling across all computation expression bindings
while maintaining backward compatibility */
| BINDER ceBindingCore EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
{ // Handle let! and use! bindings with unified pattern parsing
let pat, mPat, isInline, isMutable, tyOpt = $2

// let! and use! bindings don't support inline or mutable modifiers
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))

let mEquals = rhs parseState 3
let m = unionRanges (rhs parseState 1) $8.Range
// Debug point should span from BINDER through the expression (but not the continuation)
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m, trivia) }

| OBINDER headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
{ let report, mIn, _ = $5

// $1 contains the actual keyword ("let!" or "use!")
let isUse = ($1 = "use")
SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, $7, $8, m, trivia) }

| OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
{ // Offside-sensitive version of let!/use! binding
let pat, mPat, isInline, isMutable, tyOpt = $2

// let! and use! bindings don't support inline or mutable modifiers
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))

let report, mIn, _ = $5
report (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
let mEquals = rhs parseState 3
let m = unionRanges (rhs parseState 1) $8.Range
// Debug point should span from OBINDER through the expression (but not the continuation)
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m, trivia) }

| BINDER headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
{ // Handle type annotations on patterns in let!/use! bindings
// Examples: let! x: int = async { return 1 }
// use! _: IDisposable = async { return new MemoryStream() }
let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 7)
let pat =
match $3 with
| None -> $2
| Some (_, SynReturnInfo((ty, _), _)) ->
SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang pat.Range
let mEquals = rhs parseState 4
let m = unionRanges (rhs parseState 1) $9.Range
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, pat, $5, $8, $9, m, trivia) }

| OBINDER headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
{ // Handle type annotations on patterns in let!/use! bindings (offside-sensitive version)
// This rule maintains consistent handling of binding constructs across different syntactic contexts
let report, mIn, _ = $6
report (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $5.Range)
let pat =
match $3 with
| None -> $2
| Some (_, SynReturnInfo((ty, _), _)) ->
SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang pat.Range
let mEquals = rhs parseState 4
let m = unionRanges (rhs parseState 1) $9.Range
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, pat, $5, $8, $9, m, trivia) }

| OBINDER headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
{ // error recovery that allows intellisense when writing incomplete computation expressions

let isUse = ($1 = "use")
SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, $7, $8, m, trivia) }

| OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
{ // Error recovery for incomplete let!/use! bindings
// Allows intellisense to work when writing incomplete computation expressions
let pat, mPat, isInline, isMutable, tyOpt = $2

// Error checking for invalid modifiers
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))

// Debug point should span from OBINDER through the expression
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
let mEquals = rhs parseState 3
let mAll = unionRanges (rhs parseState 1) (rhs parseState 7)
let m = $4.Range.EndRange // zero-width range
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, [], SynExpr.ImplicitZero m, mAll, trivia) }

let isUse = ($1 = "use")
// Use ImplicitZero as the continuation expression for error recovery
SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, [], SynExpr.ImplicitZero m, mAll, trivia) }

| DO_BANG typedSequentialExpr IN opt_OBLOCKSEP typedSequentialExprBlock %prec expr_let
{ let spBind = DebugPointAtBinding.NoneAtDo
Expand Down
Loading
Loading