Skip to content

Commit d2d38e3

Browse files
authored
Parser: recover on missing union case fields (#17452)
* Parser: recover on missing union case fields * Add missing error * Add another test * Release notes * Update xlf
1 parent 3e2db28 commit d2d38e3

30 files changed

+522
-87
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Added
1010

1111
* Support for nullable reference types ([PR #15181](https://github.com/dotnet/fsharp/pull/15181))
12+
* Parser: recover on missing union case fields ([#17452](https://github.com/dotnet/fsharp/pull/17452))
1213
* Sink: report function domain type ([PR #17470](https://github.com/dotnet/fsharp/pull/17470))
1314

1415
### Changed

src/Compiler/FSComp.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1762,7 +1762,7 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [<Struct>] di
17621762
3860,chkStaticMembersOnObjectExpressions,"Object expressions cannot implement interfaces with static abstract members or declare static members."
17631763
3861,chkTailCallAttrOnNonRec,"The TailCall attribute should only be applied to recursive functions."
17641764
3862,parsStaticMemberImcompleteSyntax,"Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration."
1765-
3863,parsExpectingField,"Expecting record field"
1765+
3863,parsExpectingRecordField,"Expecting record field"
17661766
3864,tooManyMethodsInDotNetTypeWritingAssembly,"The type '%s' has too many methods. Found: '%d', maximum: '%d'"
17671767
3865,parsOnlySimplePatternsAreAllowedInConstructors,"Only simple patterns are allowed in primary constructors"
17681768
3866,chkStaticAbstractInterfaceMembers,"A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.%s)."
@@ -1775,3 +1775,4 @@ featureParsedHashDirectiveArgumentNonString,"# directives with non-quoted string
17751775
3869,featureParsedHashDirectiveUnexpectedInteger,"Unexpected integer literal '%d'."
17761776
3869,featureParsedHashDirectiveUnexpectedIdentifier,"Unexpected identifier '%s'."
17771777
featureEmptyBodiedComputationExpressions,"Support for computation expressions with empty bodies: builder {{ }}"
1778+
3870,parsExpectingUnionCaseField,"Expecting union case field"

src/Compiler/pars.fsy

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,7 +2471,7 @@ braceFieldDeclList:
24712471
{ [] }
24722472

24732473
| LBRACE rbrace
2474-
{ errorR (Error(FSComp.SR.parsExpectingField(), rhs parseState 2))
2474+
{ errorR (Error(FSComp.SR.parsExpectingRecordField (), rhs parseState 2))
24752475
[] }
24762476

24772477
anonRecdType:
@@ -2711,17 +2711,26 @@ attrUnionCaseDecl:
27112711
{ fun (xmlDoc, mBar) -> mkSynUnionCase $1 $2 (SynIdent(mkSynId mBar.EndRange "", None)) (SynUnionCaseKind.Fields []) mBar (xmlDoc, mBar) |> Choice2Of2 }
27122712

27132713
| opt_attributes opt_access unionCaseName OF unionCaseRepr
2714-
{ mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.Fields $5) (rhs2 parseState 1 5) >> Choice2Of2 }
2714+
{ let mId = rhs parseState 3
2715+
let fields, mFields = $5
2716+
let mWhole = unionRanges mId mFields
2717+
mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.Fields fields) mWhole >> Choice2Of2 }
27152718

27162719
| opt_attributes opt_access unionCaseName unionCaseRepr
27172720
{ errorR (Error(FSComp.SR.parsMissingKeyword("of"), rhs2 parseState 3 4))
2718-
mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.Fields $4) (rhs2 parseState 1 4) >> Choice2Of2 }
2721+
let mAttributes = rhs parseState 1
2722+
let fields, mFields = $4
2723+
let mWhole = unionRanges mAttributes mFields
2724+
mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.Fields fields) mWhole >> Choice2Of2 }
27192725

27202726
| opt_attributes opt_access OF unionCaseRepr
2721-
{ let mOf = rhs parseState 3
2727+
{ let mAttributes = rhs parseState 1
2728+
let mOf = rhs parseState 3
27222729
let mId = mOf.StartRange
2730+
let fields, mFields = $4
2731+
let mWhole = unionRanges mAttributes mFields
27232732
errorR (Error(FSComp.SR.parsMissingUnionCaseName(), mOf))
2724-
mkSynUnionCase $1 $2 (SynIdent(mkSynId mId "", None)) (SynUnionCaseKind.Fields $4) (rhs2 parseState 1 4) >> Choice2Of2 }
2733+
mkSynUnionCase $1 $2 (SynIdent(mkSynId mId "", None)) (SynUnionCaseKind.Fields fields) mWhole >> Choice2Of2 }
27252734

27262735
| opt_attributes opt_access OF recover
27272736
{ let mOf = rhs parseState 3
@@ -2733,13 +2742,18 @@ attrUnionCaseDecl:
27332742
{ mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.Fields []) (rhs2 parseState 1 4) >> Choice2Of2 }
27342743

27352744
| opt_attributes opt_access unionCaseName COLON topType
2736-
{ if parseState.LexBuffer.ReportLibraryOnlyFeatures then libraryOnlyWarning(lhs parseState)
2737-
mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.FullType $5) (rhs2 parseState 1 5) >> Choice2Of2 }
2745+
{ let mAttributes = rhs parseState 1
2746+
let fullType, _ = $5
2747+
let mWhole = unionRanges mAttributes fullType.Range
2748+
if parseState.LexBuffer.ReportLibraryOnlyFeatures then libraryOnlyWarning(lhs parseState)
2749+
mkSynUnionCase $1 $2 $3 (SynUnionCaseKind.FullType $5) mWhole >> Choice2Of2 }
27382750

27392751
| opt_attributes opt_access unionCaseName EQUALS atomicExpr
27402752
{ if Option.isSome $2 then errorR(Error(FSComp.SR.parsEnumFieldsCannotHaveVisibilityDeclarations(), rhs parseState 2))
27412753
let mEquals = rhs parseState 4
2742-
let mDecl = rhs2 parseState 1 5
2754+
let mAttributes = rhs parseState 1
2755+
let expr, _ = $5
2756+
let mDecl = unionRanges mAttributes expr.Range
27432757
(fun (xmlDoc, mBar) ->
27442758
let trivia: SynEnumCaseTrivia = { BarRange = Some mBar; EqualsRange = mEquals }
27452759
let mDecl = unionRangeWithXmlDoc xmlDoc mDecl
@@ -2797,16 +2811,20 @@ firstUnionCaseDeclOfMany:
27972811

27982812
firstUnionCaseDecl:
27992813
| ident OF unionCaseRepr
2800-
{ let trivia: SynUnionCaseTrivia = { BarRange = None }
2814+
{ let fields, mFields = $3
2815+
let trivia: SynUnionCaseTrivia = { BarRange = None }
28012816
let xmlDoc = grabXmlDoc (parseState, [], 1)
2802-
let mDecl = rhs2 parseState 1 3 |> unionRangeWithXmlDoc xmlDoc
2803-
Choice2Of2(SynUnionCase([], SynIdent($1, None), SynUnionCaseKind.Fields $3, xmlDoc, None, mDecl, trivia)) }
2817+
let mId = rhs parseState 1
2818+
let mDecl = unionRanges mId mFields |> unionRangeWithXmlDoc xmlDoc
2819+
Choice2Of2(SynUnionCase([], SynIdent($1, None), SynUnionCaseKind.Fields fields, xmlDoc, None, mDecl, trivia)) }
28042820

2805-
| unionCaseName COLON topType
2821+
| unionCaseName COLON topType
28062822
{ if parseState.LexBuffer.ReportLibraryOnlyFeatures then libraryOnlyWarning(lhs parseState)
28072823
let trivia: SynUnionCaseTrivia = { BarRange = None }
28082824
let xmlDoc = grabXmlDoc (parseState, [], 1)
2809-
let mDecl = rhs2 parseState 1 3 |> unionRangeWithXmlDoc xmlDoc
2825+
let mId = rhs parseState 1
2826+
let fullType, _ = $3
2827+
let mDecl = unionRanges mId fullType.Range |> unionRangeWithXmlDoc xmlDoc
28102828
Choice2Of2(SynUnionCase([], $1, SynUnionCaseKind.FullType $3, xmlDoc, None, mDecl, trivia)) }
28112829

28122830
| ident OF recover
@@ -2818,18 +2836,21 @@ firstUnionCaseDecl:
28182836
| OF unionCaseRepr
28192837
{ let mOf = rhs parseState 1
28202838
let mId = mOf.StartRange
2839+
let fields, mFields = $2
28212840
errorR (Error(FSComp.SR.parsMissingUnionCaseName(), mOf))
28222841
let id = SynIdent(mkSynId mId "", None)
28232842
let trivia: SynUnionCaseTrivia = { BarRange = None }
28242843
let xmlDoc = grabXmlDoc (parseState, [], 1)
2825-
let mDecl = rhs2 parseState 1 2 |> unionRangeWithXmlDoc xmlDoc
2826-
Choice2Of2(SynUnionCase([], id, SynUnionCaseKind.Fields $2, xmlDoc, None, mDecl, trivia)) }
2844+
let mDecl = unionRanges mOf mFields |> unionRangeWithXmlDoc xmlDoc
2845+
Choice2Of2(SynUnionCase([], id, SynUnionCaseKind.Fields fields, xmlDoc, None, mDecl, trivia)) }
28272846

28282847
| ident EQUALS atomicExpr opt_OBLOCKSEP
2829-
{ let mEquals = rhs parseState 2
2848+
{ let mId = rhs parseState 1
2849+
let mEquals = rhs parseState 2
28302850
let trivia: SynEnumCaseTrivia = { BarRange = None; EqualsRange = mEquals }
28312851
let xmlDoc = grabXmlDoc (parseState, [], 1)
2832-
let mDecl = rhs2 parseState 1 3 |> unionRangeWithXmlDoc xmlDoc
2852+
let expr, _ = $3
2853+
let mDecl = unionRanges mId expr.Range |> unionRangeWithXmlDoc xmlDoc
28332854
Choice1Of2(SynEnumCase([], SynIdent($1, None), fst $3, xmlDoc, mDecl, trivia)) }
28342855

28352856
| ident EQUALS recover opt_OBLOCKSEP
@@ -2843,10 +2864,26 @@ firstUnionCaseDecl:
28432864

28442865
unionCaseReprElements:
28452866
| unionCaseReprElement STAR unionCaseReprElements
2846-
{ $1 :: $3 }
2867+
{ let mField = rhs parseState 1
2868+
let fields, mFields = $3
2869+
$1 :: fields, unionRanges mField mFields }
2870+
2871+
| unionCaseReprElement STAR recover
2872+
{ let mStar = rhs parseState 2
2873+
let ty = SynType.FromParseError mStar.EndRange
2874+
let field = mkSynAnonField (ty, PreXmlDoc.Empty)
2875+
[$1; field], rhs2 parseState 1 2 }
2876+
2877+
| STAR unionCaseReprElements
2878+
{ let mStar = rhs parseState 1
2879+
errorR (Error(FSComp.SR.parsExpectingUnionCaseField (), rhs parseState 1))
2880+
let fields, mFields = $2
2881+
let ty = SynType.FromParseError mStar.StartRange
2882+
let field = mkSynAnonField (ty, PreXmlDoc.Empty)
2883+
field :: fields, unionRanges mStar mFields }
28472884

28482885
| unionCaseReprElement %prec prec_toptuptyptail_prefix
2849-
{ [$1] }
2886+
{ [$1], rhs parseState 1 }
28502887

28512888
unionCaseReprElement:
28522889
| ident COLON appTypeNullableInParens
@@ -2872,7 +2909,7 @@ unionCaseReprElement:
28722909
unionCaseRepr:
28732910
| braceFieldDeclList
28742911
{ errorR(Deprecated(FSComp.SR.parsConsiderUsingSeparateRecordType(), lhs parseState))
2875-
$1 }
2912+
$1, rhs parseState 1 }
28762913

28772914
| unionCaseReprElements
28782915
{ $1 }
@@ -2948,7 +2985,8 @@ exconIntro:
29482985
{ SynUnionCase([], SynIdent($1, None), SynUnionCaseKind.Fields [], PreXmlDoc.Empty, None, lhs parseState, { BarRange = None }) }
29492986

29502987
| ident OF unionCaseRepr
2951-
{ SynUnionCase([], SynIdent($1, None), SynUnionCaseKind.Fields $3, PreXmlDoc.Empty, None, lhs parseState, { BarRange = None }) }
2988+
{ let fields, _ = $3
2989+
SynUnionCase([], SynIdent($1, None), SynUnionCaseKind.Fields fields, PreXmlDoc.Empty, None, lhs parseState, { BarRange = None }) }
29522990

29532991
| ident OF recover
29542992
{ SynUnionCase([], SynIdent($1, None), SynUnionCaseKind.Fields [], PreXmlDoc.Empty, None, lhs parseState, { BarRange = None }) }

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.ja.xlf

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)