diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md index c4496adae3c..99c251d76c2 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md @@ -9,7 +9,8 @@ ### Added * Support for nullable reference types ([PR #15181](https://github.com/dotnet/fsharp/pull/15181)) -* Parser: recover on missing union case fields ([#17452](https://github.com/dotnet/fsharp/pull/17452)) +* Parser: recover on missing union case fields (PR [#17452](https://github.com/dotnet/fsharp/pull/17452)) +* Parser: recover on missing union case field types (PR [#17455](https://github.com/dotnet/fsharp/pull/17455)) * Sink: report function domain type ([PR #17470](https://github.com/dotnet/fsharp/pull/17470)) ### Changed diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 24e876bd6a2..0088142917f 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -168,6 +168,7 @@ let parse_error_rich = Some(fun (ctxt: ParseErrorContext<_>) -> %type argExpr %type declExprBlock %type headBindingPattern +%type appTypeNullableInParens %type atomTypeNonAtomicDeprecated %type atomicExprAfterType %type typedSequentialExprBlock @@ -2883,27 +2884,35 @@ unionCaseReprElements: field :: fields, unionRanges mStar mFields } | unionCaseReprElement %prec prec_toptuptyptail_prefix - { [$1], rhs parseState 1 } + { [$1], $1.Range } unionCaseReprElement: | ident COLON appTypeNullableInParens - { let xmlDoc = grabXmlDoc(parseState, [], 1) - let mWhole = rhs2 parseState 1 3 |> unionRangeWithXmlDoc xmlDoc + { let xmlDoc = grabXmlDoc (parseState, [], 1) + let mId = rhs parseState 1 + let mWhole = unionRanges mId $3.Range |> unionRangeWithXmlDoc xmlDoc mkSynNamedField ($1, $3, xmlDoc, mWhole) } + | ident COLON recover + { let xmlDoc = grabXmlDoc (parseState, [], 1) + let mColon = rhs parseState 2 + let ty = SynType.FromParseError mColon.EndRange + let mWhole = rhs2 parseState 1 2 |> unionRangeWithXmlDoc xmlDoc + mkSynNamedField ($1, ty, xmlDoc, mWhole) } + | appTypeNullableInParens - { let xmlDoc = grabXmlDoc(parseState, [], 1) + { let xmlDoc = grabXmlDoc (parseState, [], 1) mkSynAnonField ($1, xmlDoc) } | ident COLON invalidUseOfAppTypeFunction - { let xmlDoc = grabXmlDoc(parseState, [], 1) + { let xmlDoc = grabXmlDoc (parseState, [], 1) let mWhole = rhs2 parseState 1 3 |> unionRangeWithXmlDoc xmlDoc - reportParseErrorAt ($3 : SynType).Range (FSComp.SR.tcUnexpectedFunTypeInUnionCaseField()) + reportParseErrorAt ($3: SynType).Range (FSComp.SR.tcUnexpectedFunTypeInUnionCaseField ()) mkSynNamedField ($1, $3, xmlDoc, mWhole) } | invalidUseOfAppTypeFunction - { let xmlDoc = grabXmlDoc(parseState, [], 1) - reportParseErrorAt ($1 : SynType).Range (FSComp.SR.tcUnexpectedFunTypeInUnionCaseField()) + { let xmlDoc = grabXmlDoc (parseState, [], 1) + reportParseErrorAt ($1: SynType).Range (FSComp.SR.tcUnexpectedFunTypeInUnionCaseField ()) mkSynAnonField ($1, xmlDoc) } unionCaseRepr: @@ -5981,11 +5990,13 @@ invalidUseOfAppTypeFunction: { let mArrow = rhs parseState 2 let m = unionRanges (rhs2 parseState 1 2) $3.Range SynType.Fun($1, $3, m, { ArrowRange = mArrow }) } + | appTypeWithoutNull RARROW recover { let mArrow = rhs parseState 2 let ty = SynType.FromParseError(mArrow.EndRange) let m = rhs2 parseState 1 2 SynType.Fun($1, ty, m, { ArrowRange = mArrow }) } + | appTypeWithoutNull RARROW RARROW invalidUseOfAppTypeFunction { let mArrow1 = rhs parseState 2 let mArrow2 = rhs parseState 3 @@ -5994,6 +6005,7 @@ invalidUseOfAppTypeFunction: let m1 = unionRanges $1.Range $4.Range let m2 = unionRanges mArrow2 $4.Range SynType.Fun($1, SynType.Fun(ty, $4, m2, { ArrowRange = mArrow2 }), m1, { ArrowRange = mArrow1 }) } + | appTypeWithoutNull RARROW appTypeWithoutNull { let mArrow = rhs parseState 2 let m = rhs2 parseState 1 3 diff --git a/tests/service/data/SyntaxTree/Type/Union - Field 01.fs b/tests/service/data/SyntaxTree/Type/Union - Field 01.fs new file mode 100644 index 00000000000..2f91c36c262 --- /dev/null +++ b/tests/service/data/SyntaxTree/Type/Union - Field 01.fs @@ -0,0 +1,4 @@ +module Module + +type U = + | A of i: int diff --git a/tests/service/data/SyntaxTree/Type/Union - Field 01.fs.bsl b/tests/service/data/SyntaxTree/Type/Union - Field 01.fs.bsl new file mode 100644 index 00000000000..bb78f6b332f --- /dev/null +++ b/tests/service/data/SyntaxTree/Type/Union - Field 01.fs.bsl @@ -0,0 +1,35 @@ +ImplFile + (ParsedImplFileInput + ("/root/Type/Union - Field 01.fs", false, QualifiedNameOfFile Module, [], + [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [U], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + Simple + (Union + (None, + [SynUnionCase + ([], SynIdent (A, None), + Fields + [SynField + ([], false, Some i, + LongIdent (SynLongIdent ([int], [], [None])), + false, + PreXmlDoc ((4,11), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,11--4,17), { LeadingKeyword = None + MutableKeyword = None })], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,6--4,17), { BarRange = Some (4,4--4,5) })], + (4,4--4,17)), (4,4--4,17)), [], None, (3,5--4,17), + { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,7--3,8) + WithKeyword = None })], (3,0--4,17))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,17), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) diff --git a/tests/service/data/SyntaxTree/Type/Union - Field 02.fs b/tests/service/data/SyntaxTree/Type/Union - Field 02.fs new file mode 100644 index 00000000000..2539d6ed648 --- /dev/null +++ b/tests/service/data/SyntaxTree/Type/Union - Field 02.fs @@ -0,0 +1,4 @@ +module Module + +type U = + | A of i: diff --git a/tests/service/data/SyntaxTree/Type/Union - Field 02.fs.bsl b/tests/service/data/SyntaxTree/Type/Union - Field 02.fs.bsl new file mode 100644 index 00000000000..4d3b58b4440 --- /dev/null +++ b/tests/service/data/SyntaxTree/Type/Union - Field 02.fs.bsl @@ -0,0 +1,36 @@ +ImplFile + (ParsedImplFileInput + ("/root/Type/Union - Field 02.fs", false, QualifiedNameOfFile Module, [], + [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [U], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + Simple + (Union + (None, + [SynUnionCase + ([], SynIdent (A, None), + Fields + [SynField + ([], false, Some i, FromParseError (4,13--4,13), + false, + PreXmlDoc ((4,11), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,11--4,13), { LeadingKeyword = None + MutableKeyword = None })], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,6--4,13), { BarRange = Some (4,4--4,5) })], + (4,4--4,13)), (4,4--4,13)), [], None, (3,5--4,13), + { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,7--3,8) + WithKeyword = None })], (3,0--4,13))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,13), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(5,0)-(5,0) parse error Incomplete structured construct at or before this point in union case diff --git a/tests/service/data/SyntaxTree/Type/Union - Field 03.fs b/tests/service/data/SyntaxTree/Type/Union - Field 03.fs new file mode 100644 index 00000000000..7c8092dcf64 --- /dev/null +++ b/tests/service/data/SyntaxTree/Type/Union - Field 03.fs @@ -0,0 +1,4 @@ +module Module + +type U = + | A of i: * int diff --git a/tests/service/data/SyntaxTree/Type/Union - Field 03.fs.bsl b/tests/service/data/SyntaxTree/Type/Union - Field 03.fs.bsl new file mode 100644 index 00000000000..378ecc0220d --- /dev/null +++ b/tests/service/data/SyntaxTree/Type/Union - Field 03.fs.bsl @@ -0,0 +1,43 @@ +ImplFile + (ParsedImplFileInput + ("/root/Type/Union - Field 03.fs", false, QualifiedNameOfFile Module, [], + [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [U], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + Simple + (Union + (None, + [SynUnionCase + ([], SynIdent (A, None), + Fields + [SynField + ([], false, Some i, FromParseError (4,13--4,13), + false, + PreXmlDoc ((4,11), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,11--4,13), { LeadingKeyword = None + MutableKeyword = None }); + SynField + ([], false, None, + LongIdent (SynLongIdent ([int], [], [None])), + false, + PreXmlDoc ((4,16), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,16--4,19), { LeadingKeyword = None + MutableKeyword = None })], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,6--4,19), { BarRange = Some (4,4--4,5) })], + (4,4--4,19)), (4,4--4,19)), [], None, (3,5--4,19), + { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,7--3,8) + WithKeyword = None })], (3,0--4,19))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,19), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(4,14)-(4,15) parse error Unexpected symbol '*' in union case