Skip to content

Commit b5a9e9f

Browse files
committed
Unify mkLetExpression
1 parent 03805f5 commit b5a9e9f

File tree

3 files changed

+164
-79
lines changed

3 files changed

+164
-79
lines changed

src/Compiler/SyntaxTree/ParseHelpers.fs

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -867,41 +867,21 @@ let mkClassMemberLocalBindings
867867

868868
SynMemberDefn.LetBindings(decls, isStatic, isRec, mWhole)
869869

870-
let mkLocalBindings (mWhole, BindingSetPreAttrs(_, isRec, isUse, declsPreAttrs, _), mIn, body: SynExpr) =
871-
let ignoredFreeAttrs, decls = declsPreAttrs [] None
870+
/// Creates a SynExprAndBang node for and! bindings in computation expressions
871+
let mkAndBang (mKeyword: range, pat: SynPat, rhs: SynExpr, mWhole: range, mEquals: range, mIn: range option) =
872+
// Calculate debug point range: from keyword through the rhs expression
873+
let spBind = DebugPointAtBinding.Yes(unionRanges mKeyword rhs.Range)
872874

873-
let mWhole =
874-
match decls with
875-
| SynBinding(xmlDoc = xmlDoc) :: _ -> unionRangeWithXmlDoc xmlDoc mWhole
876-
| _ -> mWhole
877-
878-
if not (isNil ignoredFreeAttrs) then
879-
warning (Error(FSComp.SR.parsAttributesIgnored (), mWhole))
880-
881-
let mIn =
882-
mIn
883-
|> Option.bind (fun (mIn: range) ->
884-
if Position.posEq mIn.Start body.Range.Start then
885-
None
886-
else
887-
Some mIn)
888-
889-
let mLetOrUse =
890-
match decls with
891-
| SynBinding(trivia = trivia) :: _ -> trivia.LeadingKeyword.Range
892-
| _ -> range0
893-
894-
SynExpr.LetOrUse(
895-
isRec,
896-
isUse,
897-
decls,
898-
body,
899-
mWhole,
875+
// Create trivia
876+
let trivia: SynExprAndBangTrivia =
900877
{
901-
LetOrUseKeyword = mLetOrUse
878+
AndBangKeyword = mKeyword
879+
EqualsRange = mEquals
902880
InKeyword = mIn
903881
}
904-
)
882+
883+
// For and!, isUse is always true, isFromSource is always true
884+
SynExprAndBang(spBind, false, true, pat, rhs, mWhole, trivia)
905885

906886
let mkDefnBindings (mWhole, BindingSetPreAttrs(_, isRec, isUse, declsPreAttrs, _bindingSetRange), attrs, vis, attrsm) =
907887
if isUse then
@@ -1073,3 +1053,74 @@ let leadingKeywordIsAbstract =
10731053
| SynLeadingKeyword.StaticAbstract _
10741054
| SynLeadingKeyword.StaticAbstractMember _ -> true
10751055
| _ -> false
1056+
1057+
1058+
/// Unified helper for creating let/let!/use/use! expressions
1059+
/// Creates either SynExpr.LetOrUse or SynExpr.LetOrUseBang based on isBang parameter
1060+
/// Handles all four cases: 'let', 'let!', 'use', and 'use!'
1061+
let mkLetExpression
1062+
(
1063+
isBang: bool,
1064+
mKeyword: range,
1065+
mIn: Option<range>,
1066+
mWhole: range,
1067+
body: SynExpr,
1068+
// For regular let/use: binding information
1069+
bindingInfo: (bool * BindingSet) option,
1070+
// For let!/use!: pattern, rhs, andBangs, equals range, and isUse flag
1071+
bangInfo: (SynPat * SynExpr * SynExprAndBang list * range option * bool) option
1072+
) =
1073+
if isBang then
1074+
match bangInfo with
1075+
| Some (pat, rhs, andBangs, mEquals, isUse) ->
1076+
// Create let! or use! expression
1077+
let spBind = DebugPointAtBinding.Yes(unionRanges mKeyword rhs.Range)
1078+
let trivia: SynExprLetOrUseBangTrivia =
1079+
{
1080+
LetOrUseBangKeyword = mKeyword
1081+
EqualsRange = mEquals
1082+
}
1083+
// isFromSource is true for user-written code
1084+
SynExpr.LetOrUseBang(spBind, isUse, true, pat, rhs, andBangs, body, mWhole, trivia)
1085+
| None ->
1086+
failwith "mkLetExpression: bangInfo required for let!/use! expressions"
1087+
else
1088+
match bindingInfo with
1089+
| Some (isRec, BindingSetPreAttrs(_, _, isUse, declsPreAttrs, _)) ->
1090+
// Create regular let or use expression
1091+
let ignoredFreeAttrs, decls = declsPreAttrs [] None
1092+
1093+
let mWhole' =
1094+
match decls with
1095+
| SynBinding(xmlDoc = xmlDoc) :: _ -> unionRangeWithXmlDoc xmlDoc mWhole
1096+
| _ -> mWhole
1097+
1098+
if not (isNil ignoredFreeAttrs) then
1099+
warning (Error(FSComp.SR.parsAttributesIgnored (), mWhole'))
1100+
1101+
let mIn' =
1102+
mIn
1103+
|> Option.bind (fun (mIn: range) ->
1104+
if Position.posEq mIn.Start body.Range.Start then
1105+
None
1106+
else
1107+
Some mIn)
1108+
1109+
let mLetOrUse =
1110+
match decls with
1111+
| SynBinding(trivia = trivia) :: _ -> trivia.LeadingKeyword.Range
1112+
| _ -> range0
1113+
1114+
SynExpr.LetOrUse(
1115+
isRec,
1116+
isUse, // Pass through the isUse flag from binding info
1117+
decls,
1118+
body,
1119+
mWhole',
1120+
{
1121+
LetOrUseKeyword = mLetOrUse
1122+
InKeyword = mIn'
1123+
}
1124+
)
1125+
| None ->
1126+
failwith "mkLetExpression: bindingInfo required for regular let/use expressions"

src/Compiler/SyntaxTree/ParseHelpers.fsi

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,19 @@ val mkClassMemberLocalBindings:
196196
isStatic: bool * initialRangeOpt: range option * attrs: SynAttributes * vis: SynAccess option * BindingSet ->
197197
SynMemberDefn
198198

199-
val mkLocalBindings: mWhole: range * BindingSet * mIn: range option * body: SynExpr -> SynExpr
199+
/// Creates either SynExpr.LetOrUse or SynExpr.LetOrUseBang based on isBang parameter
200+
/// Handles all four cases: 'let', 'let!', 'use', and 'use!'
201+
val mkLetExpression:
202+
isBang: bool *
203+
mKeyword: range *
204+
mIn: range option *
205+
mWhole: range *
206+
body: SynExpr *
207+
bindingInfo: (bool * BindingSet) option *
208+
bangInfo: (SynPat * SynExpr * SynExprAndBang list * range option * bool) option -> SynExpr
209+
210+
val mkAndBang:
211+
mKeyword: range * pat: SynPat * rhs: SynExpr * mWhole: range * mEquals: range * mIn: range option -> SynExprAndBang
200212

201213
val mkDefnBindings:
202214
mWhole: range * BindingSet * attrs: SynAttributes * vis: SynAccess option * attrsm: range -> SynModuleDecl list

src/Compiler/pars.fsy

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4131,12 +4131,15 @@ sequentialExpr:
41314131
SynExpr.Sequential(DebugPointAtSequential.SuppressNeither, false, $1, $4, unionRanges $1.Range $4.Range, trivia) }
41324132

41334133
| hardwhiteLetBindings %prec prec_args_error
4134-
{ let hwlb, m, mIn = $1
4135-
let mLetKwd, isUse = match hwlb with (BindingSetPreAttrs(m, _, isUse, _, _)) -> m, isUse
4134+
{ let bindingSet, m, mIn = $1
4135+
let mLetKwd, isUse = match bindingSet with (BindingSetPreAttrs(m, _, isUse, _, _)) -> m, isUse
41364136
let usedKeyword = if isUse then "use" else "let"
41374137
reportParseErrorAt mLetKwd (FSComp.SR.parsExpectedExpressionAfterLet(usedKeyword, usedKeyword))
41384138
let fauxRange = m.EndRange // zero width range at end of m
4139-
mkLocalBindings (m, hwlb, mIn, arbExpr ("seqExpr", fauxRange)) }
4139+
// Extract isRec from BindingSetPreAttrs
4140+
let isRec = match bindingSet with BindingSetPreAttrs(_, isRec, _, _, _) -> isRec
4141+
// Use mkLetExpression for both let and use expressions
4142+
mkLetExpression(false, mLetKwd, mIn, m, arbExpr ("seqExpr", fauxRange), Some(isRec, bindingSet), None) }
41404143

41414144
/* Use this as the last terminal when performing error recovery */
41424145
/* The contract for using this is that (a) if EOF occurs then the */
@@ -4161,15 +4164,12 @@ moreBinders:
41614164
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
41624165
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
41634166

4167+
let mKeyword = rhs parseState 1
41644168
let mEquals = rhs parseState 3
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)
4168-
let mIn = rhs parseState 5
4169-
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
4169+
let m = unionRanges mKeyword $4.Range
4170+
let mIn = Some(rhs parseState 5)
41704171

4171-
// Note: For and!, we always use isRecursive=false and isUse=true
4172-
SynExprAndBang(spBind, false, true, pat, $4, m, trivia) :: $6 }
4172+
mkAndBang(mKeyword, pat, $4, m, mEquals, mIn) :: $6 }
41734173

41744174
| OAND_BANG ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
41754175
{ // Offside-sensitive version of and! binding
@@ -4181,47 +4181,69 @@ moreBinders:
41814181

41824182
let report, mIn, _ = $5
41834183
report "and!" (rhs parseState 1) // report unterminated error
4184+
let mKeyword = rhs parseState 1
41844185
let mEquals = rhs parseState 3
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)
4188-
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
4186+
let m = unionRanges mKeyword $4.Range
41894187

4190-
// Note: For and!, we always use isRecursive=false and isUse=true
4191-
SynExprAndBang(spBind, false, true, pat, $4, m, trivia) :: $7 }
4188+
mkAndBang(mKeyword, pat, $4, m, mEquals, mIn) :: $7 }
41924189

41934190
| %prec prec_no_more_attr_bindings
41944191
{ [] }
41954192

41964193
declExpr:
41974194
| defnBindings IN typedSequentialExpr %prec expr_let
41984195
{ let mIn = rhs parseState 2 |> Some
4199-
mkLocalBindings (unionRanges (rhs2 parseState 1 2) $3.Range, $1, mIn, $3) }
4196+
let mWhole = unionRanges (rhs2 parseState 1 2) $3.Range
4197+
let bindingSet = $1
4198+
// Extract isRec and mKeyword from BindingSetPreAttrs
4199+
let mKeyword, isRec = match bindingSet with BindingSetPreAttrs(m, isRec, _, _, _) -> m, isRec
4200+
// Use mkLetExpression for both let and use expressions
4201+
mkLetExpression(false, mKeyword, mIn, mWhole, $3, Some(isRec, bindingSet), None) }
42004202

42014203
| defnBindings IN error %prec expr_let
42024204
{ let mIn = rhs parseState 2 |> Some
4203-
mkLocalBindings (rhs2 parseState 1 2, $1, mIn, arbExpr ("declExpr1", (rhs parseState 3))) }
4205+
let mWhole = rhs2 parseState 1 2
4206+
let bindingSet = $1
4207+
// Extract isRec and mKeyword from BindingSetPreAttrs
4208+
let mKeyword, isRec = match bindingSet with BindingSetPreAttrs(m, isRec, _, _, _) -> m, isRec
4209+
// Use mkLetExpression for both let and use expressions
4210+
mkLetExpression(false, mKeyword, mIn, mWhole, arbExpr ("declExpr1", (rhs parseState 3)), Some(isRec, bindingSet), None) }
42044211
/*
42054212
FSComp.SR.parsNoMatchingInForLet() -- leave this in for now - it's an unused error string
42064213
*/
42074214

42084215
| hardwhiteLetBindings typedSequentialExprBlock %prec expr_let
4209-
{ let hwlb, m, mIn = $1
4210-
mkLocalBindings (unionRanges m $2.Range, hwlb, mIn, $2) }
4216+
{ let bindingSet, m, mIn = $1
4217+
let mWhole = unionRanges m $2.Range
4218+
// Extract isRec and mKeyword from BindingSetPreAttrs
4219+
let mKeyword, isRec = match bindingSet with BindingSetPreAttrs(m, isRec, _, _, _) -> m, isRec
4220+
// Use mkLetExpression for both let and use expressions
4221+
mkLetExpression(false, mKeyword, mIn, mWhole, $2, Some(isRec, bindingSet), None) }
42114222

42124223
| hardwhiteLetBindings error %prec expr_let
4213-
{ let hwlb, m, mIn = $1
4214-
reportParseErrorAt (match hwlb with (BindingSetPreAttrs(m, _, _, _, _)) -> m) (FSComp.SR.parsErrorInReturnForLetIncorrectIndentation())
4215-
mkLocalBindings (m, hwlb, mIn, arbExpr ("declExpr2", (rhs parseState 2))) }
4224+
{ let bindingSet, m, mIn = $1
4225+
reportParseErrorAt (match bindingSet with (BindingSetPreAttrs(m, _, _, _, _)) -> m) (FSComp.SR.parsErrorInReturnForLetIncorrectIndentation())
4226+
// Extract isRec and mKeyword from BindingSetPreAttrs
4227+
let mKeyword, isRec = match bindingSet with BindingSetPreAttrs(m, isRec, _, _, _) -> m, isRec
4228+
// Use mkLetExpression for both let and use expressions
4229+
mkLetExpression(false, mKeyword, mIn, m, arbExpr ("declExpr2", (rhs parseState 2)), Some(isRec, bindingSet), None) }
42164230

42174231
| hardwhiteLetBindings OBLOCKSEP typedSequentialExprBlock %prec expr_let
4218-
{ let hwlb, m, mIn = $1
4219-
mkLocalBindings (unionRanges m $3.Range, hwlb, mIn, $3) }
4232+
{ let bindingSet, m, mIn = $1
4233+
let mWhole = unionRanges m $3.Range
4234+
// Extract isRec and mKeyword from BindingSetPreAttrs
4235+
let mKeyword, isRec = match bindingSet with BindingSetPreAttrs(m, isRec, _, _, _) -> m, isRec
4236+
// Use mkLetExpression for both let and use expressions
4237+
mkLetExpression(false, mKeyword, mIn, mWhole, $3, Some(isRec, bindingSet), None) }
42204238

42214239
| hardwhiteLetBindings OBLOCKSEP error %prec expr_let
4222-
{ let hwlb, m, mIn = $1
4223-
//reportParseErrorAt (match hwlb with (BindingSetPreAttrs(m, _, _, _, _)) -> m) (FSComp.SR.parsErrorInReturnForLetIncorrectIndentation())
4224-
mkLocalBindings (unionRanges m (rhs parseState 3), hwlb, mIn, arbExpr ("declExpr3", (rhs parseState 3))) }
4240+
{ let bindingSet, m, mIn = $1
4241+
//reportParseErrorAt (match bindingSet with (BindingSetPreAttrs(m, _, _, _, _)) -> m) (FSComp.SR.parsErrorInReturnForLetIncorrectIndentation())
4242+
let mWhole = unionRanges m (rhs parseState 3)
4243+
// Extract isRec and mKeyword from BindingSetPreAttrs
4244+
let mKeyword, isRec = match bindingSet with BindingSetPreAttrs(m, isRec, _, _, _) -> m, isRec
4245+
// Use mkLetExpression for both let and use expressions
4246+
mkLetExpression(false, mKeyword, mIn, mWhole, arbExpr ("declExpr3", (rhs parseState 3)), Some(isRec, bindingSet), None) }
42254247

42264248
| hardwhiteDoBinding %prec expr_let
42274249
{ let (BindingSetPreAttrs(_, _, _, _, m)), e = $1
@@ -4555,15 +4577,15 @@ declExpr:
45554577
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
45564578
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
45574579

4558-
let mEquals = rhs parseState 3
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)
4562-
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4580+
let mKeyword = rhs parseState 1
4581+
let mEquals = Some(rhs parseState 3)
4582+
let m = unionRanges mKeyword $8.Range
45634583

4564-
// $1 contains the actual keyword ("let!" or "use!")
4584+
// $1 contains the actual keyword ("let" or "use")
45654585
let isUse = ($1 = "use")
4566-
SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, $7, $8, m, trivia) }
4586+
4587+
// Use mkLetExpression for both let! and use! bindings
4588+
mkLetExpression(true, mKeyword, None, m, $8, None, Some(pat, $4, $7, mEquals, isUse)) }
45674589

45684590
| OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
45694591
{ // Offside-sensitive version of let!/use! binding
@@ -4575,14 +4597,14 @@ declExpr:
45754597

45764598
let report, mIn, _ = $5
45774599
report (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error
4578-
let mEquals = rhs parseState 3
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)
4582-
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
4600+
let mKeyword = rhs parseState 1
4601+
let mEquals = Some(rhs parseState 3)
4602+
let m = unionRanges mKeyword $8.Range
45834603

45844604
let isUse = ($1 = "use")
4585-
SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, $7, $8, m, trivia) }
4605+
4606+
// Use mkLetExpression for both let! and use! bindings
4607+
mkLetExpression(true, mKeyword, None, m, $8, None, Some(pat, $4, $7, mEquals, isUse)) }
45864608

45874609
| OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
45884610
{ // Error recovery for incomplete let!/use! bindings
@@ -4593,16 +4615,16 @@ declExpr:
45934615
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
45944616
if isMutable then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
45954617

4596-
// Debug point should span from OBINDER through the expression
4597-
let spBind = DebugPointAtBinding.Yes(unionRanges (rhs parseState 1) $4.Range)
4598-
let mEquals = rhs parseState 3
4599-
let mAll = unionRanges (rhs parseState 1) (rhs parseState 7)
4618+
let mKeyword = rhs parseState 1
4619+
let mEquals = Some(rhs parseState 3)
4620+
let mAll = unionRanges mKeyword (rhs parseState 7)
46004621
let m = $4.Range.EndRange // zero-width range
4601-
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }
46024622

46034623
let isUse = ($1 = "use")
4624+
46044625
// Use ImplicitZero as the continuation expression for error recovery
4605-
SynExpr.LetOrUseBang(spBind, isUse, true, pat, $4, [], SynExpr.ImplicitZero m, mAll, trivia) }
4626+
// Use mkLetExpression for both let! and use! bindings
4627+
mkLetExpression(true, mKeyword, None, mAll, SynExpr.ImplicitZero m, None, Some(pat, $4, [], mEquals, isUse)) }
46064628

46074629
| DO_BANG typedSequentialExpr IN opt_OBLOCKSEP typedSequentialExprBlock %prec expr_let
46084630
{ let spBind = DebugPointAtBinding.NoneAtDo

0 commit comments

Comments
 (0)