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 34 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 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
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
44 changes: 38 additions & 6 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -4107,6 +4107,22 @@ moreBinders:
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $6 }

| AND_BANG headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point we should probably try to unify let and let! parsing rules and check whether it's a ! version or not in the type checker. As the first step it should be at least parse it in a unified manner and then create different tree nodes conditionally. But ideally it should be something like isComputed: bool flag that the type checker would inspect.

@edgarfgp Do you think it's feasible to unify the rules as part of this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's feasible to unify the rules as part of this PR?

Agreed. I think as part of this PR I can try and I can try and unify the parsing rules for let, let! and and!.

Could you also cover error cases in the grammar, please?

Yeah I can add syntax tree test that show the parsing errors.

cc @T-Gro any objections ?

{ // Handle type annotations on patterns in and! bindings
// Example: and! y: string = asyncString()
let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 6)
let pat =
match $3 with
| None -> $2
| Some (_, SynReturnInfo((ty, _), _)) ->
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
let mEquals = rhs parseState 4
let m = unionRanges (rhs parseState 1) $5.Range
let mIn = rhs parseState 6
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = Some mIn }
SynExprAndBang(spBind, false, true, pat, $5, m, trivia) :: $7 }

| OAND_BANG headBindingPattern EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
{ let report, mIn, _ = $5
report "and!" (rhs parseState 1) // report unterminated error
Expand All @@ -4116,6 +4132,22 @@ moreBinders:
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
SynExprAndBang(spBind, $1, true, $2, $4, m, trivia) :: $7 }

| OAND_BANG headBindingPattern opt_topReturnTypeWithTypeConstraints EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
{ // Handle type annotations on patterns in and! bindings (offside-sensitive version)
let report, mIn, _ = $6
report "and!" (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, _), _)) ->
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
let mEquals = rhs parseState 4
let m = unionRanges (rhs parseState 1) $5.Range
let trivia = { AndBangKeyword = rhs parseState 1; EqualsRange = mEquals; InKeyword = mIn }
SynExprAndBang(spBind, false, true, pat, $5, m, trivia) :: $8 }

| %prec prec_no_more_attr_bindings
{ [] }

Expand Down Expand Up @@ -4429,7 +4461,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 +4471,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 +4488,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,7 +4498,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.YieldOrReturnFrom(($1, not $1), expr, (unionRanges (rhs parseState 1) $2.Range), trivia) }

Expand Down Expand Up @@ -4502,7 +4534,7 @@ declExpr:
| None -> $2
| Some (_, SynReturnInfo((ty, _), _)) ->
SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang pat.Range
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang 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 }
Expand All @@ -4519,7 +4551,7 @@ declExpr:
| None -> $2
| Some (_, SynReturnInfo((ty, _), _)) ->
SynPat.Typed($2, ty, unionRanges $2.Range ty.Range)
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetOrUseBang pat.Range
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang 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 }
Expand Down
Loading
Loading