Skip to content

Commit e7abd44

Browse files
authored
Allow typed bindings(and!) in CE without parentheses (#18682)
1 parent 30a9258 commit e7abd44

File tree

94 files changed

+2824
-140
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2824
-140
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.100.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Fix parsing errors using anonymous records and code quotations ([PR #18603](https://github.com/dotnet/fsharp/pull/18603))
1010
* Better error message for attribute targets. ([PR #18641](https://github.com/dotnet/fsharp/pull/18641))
1111
* Fixed: Allow `return`, `return!`, `yield`, `yield!` type annotations without parentheses ([PR #18533](https://github.com/dotnet/fsharp/pull/18533))
12-
* Allow `let!` and `use!` type annotations without requiring parentheses ([PR #18508](https://github.com/dotnet/fsharp/pull/18508))
12+
* 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)))
1313
* Fix find all references for F# exceptions ([PR #18565](https://github.com/dotnet/fsharp/pull/18565))
1414
* Shorthand lambda: fix completion for chained calls and analysis for unfinished expression ([PR #18560](https://github.com/dotnet/fsharp/pull/18560))
1515
* Completion: fix previous namespace considered opened [PR #18609](https://github.com/dotnet/fsharp/pull/18609)

docs/release-notes/.Language/preview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Warn when `unit` is passed to an `obj`-typed argument ([PR #18330](https://github.com/dotnet/fsharp/pull/18330))
99
* Fix parsing errors using anonymous records and units of measures ([PR #18543](https://github.com/dotnet/fsharp/pull/18543))
1010
* 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))
11-
* Allow `let!` and `use!` type annotations without requiring parentheses. ([PR #18508](https://github.com/dotnet/fsharp/pull/18508))
11+
* 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)))
1212
* Exception names are now validated for illegal characters using the same mechanism as types/modules/namespaces ([Issue #18763](https://github.com/dotnet/fsharp/issues/18763))
1313

1414
### Fixed

src/Compiler/Checking/Expressions/CheckComputationExpressions.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1794,7 +1794,7 @@ let rec TryTranslateComputationExpression
17941794
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.UseBangBindingValueDiscard
17951795

17961796
let supportsTypedLetOrUseBang =
1797-
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.AllowTypedLetOrUseBang
1797+
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.AllowTypedLetUseAndBang
17981798

17991799
// use! x = ...
18001800
// use! (x) = ...

src/Compiler/Facilities/LanguageFeatures.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ type LanguageFeature =
102102
| UseBangBindingValueDiscard
103103
| BetterAnonymousRecordParsing
104104
| ScopedNowarn
105-
| AllowTypedLetOrUseBang
105+
| AllowTypedLetUseAndBang
106106

107107
/// LanguageVersion management
108108
type LanguageVersion(versionText) =
@@ -235,7 +235,7 @@ type LanguageVersion(versionText) =
235235
LanguageFeature.UseBangBindingValueDiscard, languageVersion100
236236
LanguageFeature.BetterAnonymousRecordParsing, languageVersion100
237237
LanguageFeature.ScopedNowarn, languageVersion100
238-
LanguageFeature.AllowTypedLetOrUseBang, languageVersion100
238+
LanguageFeature.AllowTypedLetUseAndBang, languageVersion100
239239
LanguageFeature.UnmanagedConstraintCsharpInterop, languageVersion100
240240
LanguageFeature.AllowAccessModifiersToAutoPropertiesGettersAndSetters, languageVersion100
241241

@@ -408,7 +408,7 @@ type LanguageVersion(versionText) =
408408
| LanguageFeature.UseBangBindingValueDiscard -> FSComp.SR.featureUseBangBindingValueDiscard ()
409409
| LanguageFeature.BetterAnonymousRecordParsing -> FSComp.SR.featureBetterAnonymousRecordParsing ()
410410
| LanguageFeature.ScopedNowarn -> FSComp.SR.featureScopedNowarn ()
411-
| LanguageFeature.AllowTypedLetOrUseBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()
411+
| LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()
412412

413413
/// Get a version string associated with the given feature.
414414
static member GetFeatureVersionString feature =

src/Compiler/Facilities/LanguageFeatures.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ type LanguageFeature =
9393
| UseBangBindingValueDiscard
9494
| BetterAnonymousRecordParsing
9595
| ScopedNowarn
96-
| AllowTypedLetOrUseBang
96+
| AllowTypedLetUseAndBang
9797

9898
/// LanguageVersion management
9999
type LanguageVersion =

src/Compiler/SyntaxTree/ParseHelpers.fs

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -867,41 +867,19 @@ 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+
let spBind = DebugPointAtBinding.Yes(unionRanges mKeyword rhs.Range)
872873

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,
874+
let trivia: SynExprAndBangTrivia =
900875
{
901-
LetOrUseKeyword = mLetOrUse
876+
AndBangKeyword = mKeyword
877+
EqualsRange = mEquals
902878
InKeyword = mIn
903879
}
904-
)
880+
881+
// For and!, isUse is always true, isFromSource is always true
882+
SynExprAndBang(spBind, false, true, pat, rhs, mWhole, trivia)
905883

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

src/Compiler/SyntaxTree/ParseHelpers.fsi

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,20 @@ 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 ->
209+
SynExpr
210+
211+
val mkAndBang:
212+
mKeyword: range * pat: SynPat * rhs: SynExpr * mWhole: range * mEquals: range * mIn: range option -> SynExprAndBang
200213

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

0 commit comments

Comments
 (0)