Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 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
5bde641
Merge branch 'dotnet:main' into main
edgarfgp Jul 26, 2025
0f7c23c
Merge branch 'dotnet:main' into main
edgarfgp Jul 29, 2025
6a6843c
Merge branch 'dotnet:main' into main
edgarfgp Aug 4, 2025
d11dd4a
Merge branch 'dotnet:main' into main
edgarfgp Aug 4, 2025
30fc9a2
Merge branch 'dotnet:main' into main
edgarfgp Aug 5, 2025
815c51b
Merge branch 'dotnet:main' into main
edgarfgp Aug 11, 2025
7536249
Merge branch 'dotnet:main' into main
edgarfgp Aug 12, 2025
5b1a5aa
Merge branch 'dotnet:main' into main
edgarfgp Aug 27, 2025
c98ede2
Allow comma as union cases name fields separator
edgarfgp Aug 31, 2025
c8377fe
Allow comma as union cases name fields separator
edgarfgp Aug 31, 2025
874bd82
Merge branch 'allow-comma-as-name-field-separator' of github.com:edga…
edgarfgp Sep 2, 2025
492f4ab
more tests
edgarfgp Sep 2, 2025
bc24900
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 2, 2025
51002cb
format
edgarfgp Sep 2, 2025
91bf314
more tests
edgarfgp Sep 3, 2025
9962fa9
more tests
edgarfgp Sep 3, 2025
20b4240
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 3, 2025
94bade0
Merge branch 'allow-comma-as-name-field-separator' of github.com:edga…
edgarfgp Sep 3, 2025
a8db7b7
more tests
edgarfgp Sep 3, 2025
afd33aa
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 3, 2025
baa070d
More tests
edgarfgp Sep 3, 2025
f0877ff
Merge branch 'allow-comma-as-name-field-separator' of github.com:edga…
edgarfgp Sep 3, 2025
bd65332
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 4, 2025
3c3eada
more tests
edgarfgp Sep 4, 2025
dce0b2e
Merge branch 'allow-comma-as-name-field-separator' of github.com:edga…
edgarfgp Sep 4, 2025
2537bcf
more tests
edgarfgp Sep 5, 2025
0cac8f6
Merge branch 'dotnet:main' into main
edgarfgp Sep 5, 2025
d4c67dc
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 5, 2025
ca6c03e
more tests
edgarfgp Sep 5, 2025
95f5778
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 8, 2025
a8fe750
reduce diff
edgarfgp Sep 10, 2025
2b6b79e
factor rules to make it easier to review
edgarfgp Sep 10, 2025
56b0be6
one more test
edgarfgp Sep 11, 2025
efdea46
more tests
edgarfgp Sep 11, 2025
83a96f6
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 11, 2025
200ee49
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 16, 2025
2217f5a
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 18, 2025
33801bc
Merge branch 'main' into allow-comma-as-name-field-separator
edgarfgp Sep 21, 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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Add support for `when 'T : Enum` library-only static optimization constraint. ([PR #18546](https://github.com/dotnet/fsharp/pull/18546))
* Add support for tail calls in computation expressions ([PR #18804](https://github.com/dotnet/fsharp/pull/18804))
* Add `--typecheck-only` flag support for F# Interactive (FSI) scripts to type-check without execution. ([Issue #18686](https://github.com/dotnet/fsharp/issues/18686))
* Allow comma as union case name field separator. ([PR #18833](https://github.com/dotnet/fsharp/pull/18833))
* Diagnostics: add extended data for 'No constructors' error ([PR #18863](https://github.com/dotnet/fsharp/pull/18863))
* FSharpType.Format: support top-level prefix generic types style. ([PR #18897](https://github.com/dotnet/fsharp/pull/18897))
* FCS: allow getting captured types ([PR $18878](https://github.com/dotnet/fsharp/pull/18878))
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* 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)))
* 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))
* Support tail calls in computation expressions ([PR #18804](https://github.com/dotnet/fsharp/pull/18804))
* Allow comma as union case name field separator. ([PR #18833](https://github.com/dotnet/fsharp/pull/18833))

### Fixed

Expand Down
9 changes: 8 additions & 1 deletion src/Compiler/Checking/CheckRecordSyntaxHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,14 @@ let GroupUpdatesToNestedFields (fields: ((Ident list * Ident) * SynExpr option)
/// Expands a long identifier into nested copy-and-update expressions.
///
/// `{ x with A.B = 0; A.C = "" }` becomes `{ x with A = { x.A with B = 0 }; A = { x.A with C = "" } }`
let TransformAstForNestedUpdates (cenv: TcFileState) (env: TcEnv) overallTy (lid: LongIdent) exprBeingAssigned withExpr =
let TransformAstForNestedUpdates
(cenv: TcFileState)
(env: TcEnv)
overallTy
(lid: LongIdent)
exprBeingAssigned
(withExpr: SynExpr * BlockSeparator)
=
let recdExprCopyInfo ids withExpr id =
let upToId origSepRng id lidwd =
let rec buildLid res (id: Ident) =
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1813,4 +1813,6 @@ featureAllowLetOrUseBangTypeAnnotationWithoutParens,"Allow let! and use! type an
3876,lexWarnDirectivesMustMatch,"There is another %s for this warning already in line %d."
3877,lexLineDirectiveMappingIsNotUnique,"The file '%s' was also pointed to in a line directive in '%s'. Proper warn directive application may not be possible."
3878,tcAttributeIsNotValidForUnionCaseWithFields,"This attribute is not valid for use on union cases with fields."
3879,parsInconsistentSeparators,"Inconsistent separators in pattern. Use either all commas or all semicolons, but not both."
featureReturnFromFinal,"Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder."
featureAllowCommaAsUnionCaseNamedFieldSeparator,"Allow comma as union case name field separator."
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ type LanguageFeature =
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowTypedLetUseAndBang
| ReturnFromFinal
| AllowCommaAsUnionCaseNamedFieldSeparator

/// LanguageVersion management
type LanguageVersion(versionText) =
Expand Down Expand Up @@ -245,6 +246,7 @@ type LanguageVersion(versionText) =

// F# preview (still preview in 10.0)
LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work
LanguageFeature.AllowCommaAsUnionCaseNamedFieldSeparator, previewVersion
]

static let defaultLanguageVersion = LanguageVersion("default")
Expand Down Expand Up @@ -415,6 +417,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions -> FSComp.SR.featureErrorOnInvalidDeclsInTypeDefinitions ()
| LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()
| LanguageFeature.ReturnFromFinal -> FSComp.SR.featureReturnFromFinal ()
| LanguageFeature.AllowCommaAsUnionCaseNamedFieldSeparator -> FSComp.SR.featureAllowCommaAsUnionCaseNamedFieldSeparator ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type LanguageFeature =
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowTypedLetUseAndBang
| ReturnFromFinal
| AllowCommaAsUnionCaseNamedFieldSeparator

/// LanguageVersion management
type LanguageVersion =
Expand Down
100 changes: 97 additions & 3 deletions src/Compiler/SyntaxTree/ParseHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,9 +1065,103 @@ let leadingKeywordIsAbstract =
| SynLeadingKeyword.StaticAbstractMember _ -> true
| _ -> false

/// Unified helper for creating let/let!/use/use! expressions
/// Creates SynExpr.LetOrUse based on isBang parameter
/// Handles all four cases: 'let', 'let!', 'use', and 'use!'
let mkNamePatPairFieldFromId (id: Ident) (mEq: range option) (pat: SynPat) (sep: BlockSeparator option) : NamePatPairField =
let m = unionRanges id.idRange pat.Range
let lid = SynLongIdent([ id ], [], [ None ])
NamePatPairField(lid, mEq, m, pat, sep)

let mkNamePatPairFieldFromLid (lid: SynLongIdent) (mEq: range option) (pat: SynPat) (sep: BlockSeparator option) : NamePatPairField =
let m = unionRanges lid.Range pat.Range
NamePatPairField(lid, mEq, m, pat, sep)

let mkMissingNamePatPairFieldFromId (id: Ident) (sep: BlockSeparator option) : NamePatPairField =
let m = id.idRange
let lid = SynLongIdent([ id ], [], [ None ])
let bindPat = SynPat.Named(SynIdent(id, None), false, None, m)
let patErr = patFromParseError bindPat
NamePatPairField(lid, None, m, patErr, sep)

let private mkVarPatFromIdent (id: Ident) =
SynPat.Named(SynIdent(id, None), false, None, id.idRange)

let mkTuplePatFromTrailingIdents (leadingPat: SynPat) (pairs: (BlockSeparator * Ident) list) : SynPat * range =
match List.tryFrontAndBack pairs with
| None -> leadingPat, leadingPat.Range
| Some(front, (lastSep, lastId)) ->
let trailingPats = front |> List.map (fun (_, id) -> mkVarPatFromIdent id)
let lastPat = mkVarPatFromIdent lastId
let pats = leadingPat :: (trailingPats @ [ lastPat ])

let commaRanges =
(front |> List.map (fun (sep, _) -> sep.Range)) @ [ lastSep.Range ]

let mTuple = unionRanges leadingPat.Range lastId.idRange
SynPat.Tuple(false, pats, commaRanges, mTuple), mTuple

let mkTuplePatFromTrailingPatterns (leadingPat: SynPat) (pairs: (BlockSeparator * SynPat) list) : SynPat * range =
match List.tryFrontAndBack pairs with
| None -> leadingPat, leadingPat.Range
| Some(front, (lastSep, lastPat)) ->
let trailingPats = front |> List.map (fun (_, p) -> p)
let pats = leadingPat :: (trailingPats @ [ lastPat ])

let commaRanges =
(front |> List.map (fun (sep, _) -> sep.Range)) @ [ lastSep.Range ]

let mTuple = unionRanges leadingPat.Range lastPat.Range
SynPat.Tuple(false, pats, commaRanges, mTuple), mTuple

let tryFoldTuplePat (mEq1: range option) (pat1: SynPat) (pairsAsPatterns: (BlockSeparator * SynPat) list) : (SynPat * range) option =
match mEq1, pat1, pairsAsPatterns with
| Some _, SynPat.FromParseError _, _ -> None
| Some _, _, [] -> None
| Some _, _, _ -> Some(mkTuplePatFromTrailingPatterns pat1 pairsAsPatterns)
| None, _, _ -> None

let tryFoldTuplePatFromTrailingIdents
(mEq1: range option)
(pat1: SynPat)
(pairsAsIdents: (BlockSeparator * Ident) list)
: (SynPat * range) option =
match mEq1, pat1, pairsAsIdents with
| Some _, SynPat.FromParseError _, _ -> None
| Some _, _, [] -> None
| Some _, _, _ -> Some(mkTuplePatFromTrailingIdents pat1 pairsAsIdents)
| None, _, _ -> None

let reportInconsistentSeparatorsForNamePatPairs (fields: NamePatPairField list) =
// Map BlockSeparator to a kind: semicolon=0, comma=1, offside=None
let kindOf sep =
match sep with
| BlockSeparator.Semicolon _ -> Some 0
| BlockSeparator.Comma _ -> Some 1
| BlockSeparator.Offside _ -> None

// Find the first concrete kind (semicolon/comma) and return it together with the remaining fields to scan
let rec firstKind rest =
match rest with
| [] -> None
| NamePatPairField(blockSeparator = Some sep) :: tail ->
match kindOf sep with
| Some k -> Some(k, tail)
| None -> firstKind tail
| _ :: tail -> firstKind tail

match firstKind fields with
| None -> ()
| Some(k0, rest) ->
// Scan for the first conflicting separator and report once
let rec scan xs =
match xs with
| [] -> ()
| NamePatPairField(blockSeparator = Some sep) :: tail ->
match kindOf sep with
| Some k when k <> k0 -> reportParseErrorAt sep.Range (FSComp.SR.parsInconsistentSeparators ())
| _ -> scan tail
| _ :: tail -> scan tail

scan rest

let mkLetExpression
(
isBang: bool,
Expand Down
31 changes: 31 additions & 0 deletions src/Compiler/SyntaxTree/ParseHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,35 @@ val mkSynField:
leadingKeyword: SynLeadingKeyword option ->
SynField

val mkNamePatPairFieldFromId:
id: Ident -> mEq: range option -> pat: SynPat -> sep: BlockSeparator option -> NamePatPairField

val mkNamePatPairFieldFromLid:
lid: SynLongIdent -> mEq: range option -> pat: SynPat -> sep: BlockSeparator option -> NamePatPairField

val mkMissingNamePatPairFieldFromId: id: Ident -> sep: BlockSeparator option -> NamePatPairField

/// Build a tuple pattern by folding trailing identifiers (with their comma separators)
/// into a leading pattern. No Paren node is added; the commas are captured via commaRanges.
/// Returns the synthesized tuple pattern and its overall range.
val mkTuplePatFromTrailingIdents: leadingPat: SynPat -> pairs: (BlockSeparator * Ident) list -> SynPat * range

/// Build a tuple pattern by folding trailing patterns (with their comma separators)
/// into a leading pattern. No Paren node is added; the commas are captured via commaRanges.
/// Returns the synthesized tuple pattern and its overall range.
val mkTuplePatFromTrailingPatterns: leadingPat: SynPat -> pairs: (BlockSeparator * SynPat) list -> SynPat * range

/// Try to fold trailing patterns into a tuple for a named field under recovery.
/// Returns Some(tuplePat, mTuple) when mEq is present, pat1 is not FromParseError, and there is at least one trailing element.
val tryFoldTuplePat:
mEq1: range option -> pat1: SynPat -> pairsAsPatterns: (BlockSeparator * SynPat) list -> (SynPat * range) option

/// Try to fold trailing identifiers into a tuple for a named field under recovery.
/// Returns Some(tuplePat, mTuple) when mEq is present, pat1 is not FromParseError, and there is at least one trailing ident.
val tryFoldTuplePatFromTrailingIdents:
mEq1: range option -> pat1: SynPat -> pairsAsIdents: (BlockSeparator * Ident) list -> (SynPat * range) option

/// Reports an error when named-field patterns mix separators within the same list.
val reportInconsistentSeparatorsForNamePatPairs: fields: NamePatPairField list -> unit

val leadingKeywordIsAbstract: SynLeadingKeyword -> bool
Loading
Loading