@@ -3520,6 +3520,57 @@ bindingPattern:
3520
3520
| headBindingPattern
3521
3521
{ $1, $1.Range }
3522
3522
3523
+ /* Common pattern for bindings that may include type annotations
3524
+ This rule unifies the pattern parsing for both regular 'let' bindings and
3525
+ computation expression bindings (let!, use!, and!).
3526
+
3527
+ Returns: (pattern, pattern_range, optional_type)
3528
+ - pattern: The parsed pattern (may be typed if annotation present)
3529
+ - pattern_range: The full range including any type annotation
3530
+ - optional_type: The type if annotation was present, None otherwise */
3531
+ bindingPatternWithOptType:
3532
+ | headBindingPattern
3533
+ { // Simple pattern without type annotation
3534
+ $1, $1.Range, None }
3535
+
3536
+ | headBindingPattern COLON topTypeWithTypeConstraints
3537
+ { // Pattern with type annotation (e.g., x: int)
3538
+ let ty, arity = $3
3539
+ let mWhole = unionRanges $1.Range ty.Range
3540
+ // Create a typed pattern node
3541
+ let typedPat = SynPat.Typed($1, ty, mWhole)
3542
+ typedPat, mWhole, Some ty }
3543
+
3544
+ | headBindingPattern COLON error
3545
+ { // Error recovery: incomplete type annotation (e.g., "let x:" with no type)
3546
+ let mColon = rhs parseState 2
3547
+ let mWhole = unionRanges $1.Range mColon
3548
+ // Create a typed pattern with an error type for recovery
3549
+ let errorType = SynType.FromParseError(mColon.EndRange)
3550
+ let typedPat = SynPat.Typed($1, errorType, mWhole)
3551
+ typedPat, mWhole, Some errorType }
3552
+
3553
+ /* Common rule for computation expression binding patterns
3554
+ Handles the pattern part of let!, use!, and! bindings with consistent
3555
+ type annotation support and language feature checking.
3556
+
3557
+ Returns: (pattern, pattern_range, isInline, isMutable, optional_type) */
3558
+ ceBindingCore:
3559
+ | opt_inline opt_mutable bindingPatternWithOptType
3560
+ { let pat, mPat, tyOpt = $3
3561
+ let isInline = Option.isSome $1
3562
+ let isMutable = Option.isSome $2
3563
+
3564
+ // For CE bindings, check language feature if type annotation is present
3565
+ // This ensures that typed let!/use!/and! bindings are only allowed when
3566
+ // the AllowTypedLetUseAndBang feature is enabled
3567
+ match tyOpt with
3568
+ | Some ty ->
3569
+ parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
3570
+ | None -> ()
3571
+
3572
+ pat, mPat, isInline, isMutable, tyOpt }
3573
+
3523
3574
opt_simplePatterns:
3524
3575
| simplePatterns
3525
3576
{ Some $1 }
@@ -4099,54 +4150,45 @@ recover:
4099
4150
{ debugPrint("recovering via EOF"); false }
4100
4151
4101
4152
moreBinders:
4102
- | AND_BANG headBindingPattern EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
4103
- { let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
4153
+ /* Refactored and! bindings to use unified ceBindingCore
4154
+ This ensures consistent handling of patterns and type annotations
4155
+ across all computation expression bindings */
4156
+ | AND_BANG ceBindingCore EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
4157
+ { // Handle and! bindings with unified pattern parsing
4158
+ let pat, mPat, isInline, isMutable, tyOpt = $2
4159
+
4160
+ // and! bindings don't support inline or mutable modifiers
4161
+ if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4162
+ if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4163
+
4104
4164
let mEquals = rhs parseState 3
4105
4165
let m = unionRanges (rhs parseState 1) $4.Range
4166
+ // Debug point should span the entire binding: from AND_BANG through the expression
4167
+ let spBind = DebugPointAtBinding.Yes(m)
4106
4168
let mIn = rhs parseState 5
4107
4169
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
4108
- SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $6 }
4109
-
4110
- | AND_BANG headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
4111
- { // Handle type annotations on patterns in and! bindings
4112
- // Example: and! y: string = asyncString()
4113
- let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 6)
4114
- let pat =
4115
- match $3 with
4116
- | None -> $2
4117
- | Some (_, SynReturnInfo((ty, _), _)) ->
4118
- parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
4119
- SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
4120
- let mEquals = rhs parseState 4
4121
- let m = unionRanges (rhs parseState 1) $5.Range
4122
- let mIn = rhs parseState 6
4123
- let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
4124
- SynExprAndBang(spBind, false, true, pat, $5, m, trivia) :: $7 }
4125
-
4126
- | OAND_BANG headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
4127
- { let report, mIn, _ = $5
4170
+
4171
+ // Note: For and!, we always use isRecursive=false and isUse=true
4172
+ SynExprAndBang(spBind, false, true, pat, $4, m, trivia) :: $6 }
4173
+
4174
+ | OAND_BANG ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
4175
+ { // Offside-sensitive version of and! binding
4176
+ let pat, mPat, isInline, isMutable, tyOpt = $2
4177
+
4178
+ // and! bindings don't support inline or mutable modifiers
4179
+ if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4180
+ if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4181
+
4182
+ let report, mIn, _ = $5
4128
4183
report "and!" (rhs parseState 1) // report unterminated error
4129
- let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
4130
4184
let mEquals = rhs parseState 3
4131
4185
let m = unionRanges (rhs parseState 1) $4.Range
4186
+ // Debug point should span the entire binding: from OAND_BANG through the expression
4187
+ let spBind = DebugPointAtBinding.Yes(m)
4132
4188
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
4133
- SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $7 }
4134
-
4135
- | OAND_BANG headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
4136
- { // Handle type annotations on patterns in and! bindings (offside-sensitive version)
4137
- let report, mIn, _ = $6
4138
- report "and!" (rhs parseState 1) // report unterminated error
4139
- let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $5.Range)
4140
- let pat =
4141
- match $3 with
4142
- | None -> $2
4143
- | Some (_, SynReturnInfo((ty, _), _)) ->
4144
- parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
4145
- SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
4146
- let mEquals = rhs parseState 4
4147
- let m = unionRanges (rhs parseState 1) $5.Range
4148
- let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
4149
- SynExprAndBang(spBind, false, true, pat, $5, m, trivia) :: $8 }
4189
+
4190
+ // Note: For and!, we always use isRecursive=false and isUse=true
4191
+ SynExprAndBang(spBind, false, true, pat, $4, m, trivia) :: $7 }
4150
4192
4151
4193
| %prec prec_no_more_attr_bindings
4152
4194
{ [] }
@@ -4502,69 +4544,65 @@ declExpr:
4502
4544
SynExpr.Typed($2, ty, m)
4503
4545
SynExpr.YieldOrReturnFrom(($1, not $1), expr, (unionRanges (rhs parseState 1) $2.Range), trivia) }
4504
4546
4505
- | BINDER headBindingPattern EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4506
- { (* This rule handles the 'use!' and 'let!' binding expressions in computation expressions *)
4507
- (* The BINDER token represents keywords like 'use!' or 'let!' *)
4508
- (* headBindingPattern represents patterns in the binding:
4509
- - Underscore ('_') patterns are preserved as SynPat.Wild
4510
- - Named patterns ('__') are represented as SynPat.Named
4511
- - Identifiers (like 'value') are represented as SynPat.LongIdent *)
4512
- let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5)
4547
+ /* Refactored let! and use! bindings to use unified ceBindingCore
4548
+ This ensures consistent handling across all computation expression bindings
4549
+ while maintaining backward compatibility */
4550
+ | BINDER ceBindingCore EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4551
+ { // Handle let! and use! bindings with unified pattern parsing
4552
+ let pat, mPat, isInline, isMutable, tyOpt = $2
4553
+
4554
+ // let! and use! bindings don't support inline or mutable modifiers
4555
+ if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4556
+ if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4557
+
4513
4558
let mEquals = rhs parseState 3
4514
4559
let m = unionRanges (rhs parseState 1) $8.Range
4560
+ // Debug point should span from BINDER through the expression (but not the continuation)
4561
+ let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
4515
4562
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4516
- SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m, trivia) }
4517
-
4518
- | OBINDER headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4519
- { let report, mIn, _ = $5
4563
+
4564
+ // $1 contains the actual keyword ("let!" or "use!")
4565
+ let isUse = ($1 = "use")
4566
+ SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, $7, $8, m, trivia) }
4567
+
4568
+ | OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4569
+ { // Offside-sensitive version of let!/use! binding
4570
+ let pat, mPat, isInline, isMutable, tyOpt = $2
4571
+
4572
+ // let! and use! bindings don't support inline or mutable modifiers
4573
+ if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4574
+ if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4575
+
4576
+ let report, mIn, _ = $5
4520
4577
report (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error
4521
- let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
4522
4578
let mEquals = rhs parseState 3
4523
4579
let m = unionRanges (rhs parseState 1) $8.Range
4580
+ // Debug point should span from OBINDER through the expression (but not the continuation)
4581
+ let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
4524
4582
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4525
- SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m, trivia) }
4526
-
4527
- | BINDER headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4528
- { // Handle type annotations on patterns in let!/use! bindings
4529
- // Examples: let! x: int = async { return 1 }
4530
- // use! _: IDisposable = async { return new MemoryStream() }
4531
- let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 7)
4532
- let pat =
4533
- match $3 with
4534
- | None -> $2
4535
- | Some (_, SynReturnInfo((ty, _), _)) ->
4536
- SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
4537
- parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang pat.Range
4538
- let mEquals = rhs parseState 4
4539
- let m = unionRanges (rhs parseState 1) $9.Range
4540
- let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4541
- SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, pat, $5, $8, $9, m, trivia) }
4542
-
4543
- | OBINDER headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4544
- { // Handle type annotations on patterns in let!/use! bindings (offside-sensitive version)
4545
- // This rule maintains consistent handling of binding constructs across different syntactic contexts
4546
- let report, mIn, _ = $6
4547
- report (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error
4548
- let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $5.Range)
4549
- let pat =
4550
- match $3 with
4551
- | None -> $2
4552
- | Some (_, SynReturnInfo((ty, _), _)) ->
4553
- SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
4554
- parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang pat.Range
4555
- let mEquals = rhs parseState 4
4556
- let m = unionRanges (rhs parseState 1) $9.Range
4557
- let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4558
- SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, pat, $5, $8, $9, m, trivia) }
4559
-
4560
- | OBINDER headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
4561
- { // error recovery that allows intellisense when writing incomplete computation expressions
4583
+
4584
+ let isUse = ($1 = "use")
4585
+ SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, $7, $8, m, trivia) }
4586
+
4587
+ | OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
4588
+ { // Error recovery for incomplete let!/use! bindings
4589
+ // Allows intellisense to work when writing incomplete computation expressions
4590
+ let pat, mPat, isInline, isMutable, tyOpt = $2
4591
+
4592
+ // Error checking for invalid modifiers
4593
+ if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4594
+ if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
4595
+
4596
+ // Debug point should span from OBINDER through the expression
4562
4597
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
4563
4598
let mEquals = rhs parseState 3
4564
4599
let mAll = unionRanges (rhs parseState 1) (rhs parseState 7)
4565
4600
let m = $4.Range.EndRange // zero-width range
4566
4601
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4567
- SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, [], SynExpr.ImplicitZero m, mAll, trivia) }
4602
+
4603
+ let isUse = ($1 = "use")
4604
+ // Use ImplicitZero as the continuation expression for error recovery
4605
+ SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, [], SynExpr.ImplicitZero m, mAll, trivia) }
4568
4606
4569
4607
| DO_BANG typedSequentialExpr IN opt_OBLOCKSEP typedSequentialExprBlock %prec expr_let
4570
4608
{ let spBind = DebugPointAtBinding.NoneAtDo
0 commit comments