Skip to content

Parser: recover on unfinished cons patterns #17231

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

Merged
merged 4 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
### Added

* Generate new `Equals` overload to avoid boxing for structural comparison ([PR #16857](https://github.com/dotnet/fsharp/pull/16857))
* Parser: better recovery for unfinished patterns ([PR #17231](https://github.com/dotnet/fsharp/pull/17231))

### Changed
* Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173))
Expand Down
22 changes: 22 additions & 0 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -3454,6 +3454,17 @@ headBindingPattern:
{ let mColonColon = rhs parseState 2
SynPat.ListCons($1, $3, rhs2 parseState 1 3, { ColonColonRange = mColonColon }) }

| headBindingPattern COLON_COLON recover
{ let mColonColon = rhs parseState 2
let pat2 = SynPat.Wild(mColonColon.EndRange)
SynPat.ListCons($1, pat2, rhs2 parseState 1 2, { ColonColonRange = mColonColon }) }

| headBindingPattern COLON_COLON
{ let mColonColon = rhs parseState 2
reportParseErrorAt mColonColon (FSComp.SR.parsExpectingPattern ())
let pat2 = SynPat.Wild(mColonColon.EndRange)
SynPat.ListCons($1, pat2, rhs2 parseState 1 2, { ColonColonRange = mColonColon }) }

| tuplePatternElements %prec pat_tuple
{ let pats, commas = $1
let pats, commas = normalizeTuplePat pats commas
Expand Down Expand Up @@ -3760,6 +3771,17 @@ parenPattern:
{ let mColonColon = rhs parseState 2
SynPat.ListCons($1, $3, rhs2 parseState 1 3, { ColonColonRange = mColonColon }) }

| parenPattern COLON_COLON recover
{ let mColonColon = rhs parseState 2
let pat2 = SynPat.Wild(mColonColon.EndRange)
SynPat.ListCons($1, pat2, rhs2 parseState 1 2, { ColonColonRange = mColonColon }) }

| parenPattern COLON_COLON
{ let mColonColon = rhs parseState 2
reportParseErrorAt mColonColon (FSComp.SR.parsExpectingPattern ())
let pat2 = SynPat.Wild(mColonColon.EndRange)
SynPat.ListCons($1, pat2, rhs2 parseState 1 2, { ColonColonRange = mColonColon }) }

| constrPattern { $1 }

tupleParenPatternElements:
Expand Down
8 changes: 6 additions & 2 deletions tests/service/PatternMatchCompilationTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ let z as =
"(10,7--10,9): Unexpected keyword 'as' in binding";
"(10,5--10,6): Expecting pattern";
"(11,10--11,12): Unexpected keyword 'as' in binding. Expected '=' or other token.";
"(12,9--12,11): Unexpected keyword 'as' in binding";
"(12,6--12,8): Expecting pattern";
"(13,8--13,10): Unexpected keyword 'as' in binding";
"(14,8--14,10): Unexpected keyword 'as' in binding";
"(15,8--15,10): Unexpected keyword 'as' in pattern. Expected ')' or other token.";
Expand All @@ -776,6 +776,8 @@ let z as =
"(8,29--8,30): This expression was expected to have type\u001d 'unit' \u001dbut here has type\u001d 'int'";
"(9,26--9,27): This expression was expected to have type\u001d 'unit' \u001dbut here has type\u001d 'int'";
"(10,14--10,15): This expression was expected to have type\u001d ''a * 'b' \u001dbut here has type\u001d 'int'";
"(12,16--12,18): This expression was expected to have type\u001d ''a list' \u001dbut here has type\u001d 'int'";
"(12,4--12,13): Incomplete pattern matches on this expression. For example, the value '[]' may indicate a case not covered by the pattern(s).";
"(15,4--15,5): The pattern discriminator 'r' is not defined.";
"(15,4--15,12): Incomplete pattern matches on this expression."
]
Expand Down Expand Up @@ -1176,7 +1178,7 @@ let as :? z =
"(10,7--10,9): Unexpected keyword 'as' in binding";
"(10,5--10,6): Expecting pattern";
"(11,10--11,12): Unexpected keyword 'as' in binding. Expected '=' or other token.";
"(12,9--12,11): Unexpected keyword 'as' in binding";
"(12,6--12,8): Expecting pattern";
"(13,8--13,10): Unexpected keyword 'as' in binding";
"(14,8--14,10): Unexpected keyword 'as' in binding";
"(15,13--15,15): Unexpected keyword 'as' in pattern. Expected '(' or other token.";
Expand Down Expand Up @@ -1204,6 +1206,8 @@ let as :? z =
"(9,22--9,26): The type 'unit' does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion.";
"(10,13--10,14): The type 'i' is not defined.";
"(10,10--10,14): The type ''a * 'b' does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion.";
"(12,15--12,16): The type 'm' is not defined.";
"(12,12--12,16): The type ''a list' does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion.";
"(16,4--16,5): The pattern discriminator 't' is not defined.";
"(16,14--16,15): The type 'u' is not defined.";
"(16,11--16,15): This runtime coercion or type test from type\u001d 'a \u001d to \u001d 'b \u001dinvolves an indeterminate type based on information prior to this program point. Runtime type tests are not allowed on some types. Further type annotations are needed."
Expand Down
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 01.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

match () with
| _ :: _ -> ()

()
22 changes: 22 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 01.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/Cons 01.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(ListCons
(Wild (4,2--4,3), Wild (4,7--4,8), (4,2--4,8),
{ ColonColonRange = (4,4--4,6) }), None,
Const (Unit, (4,12--4,14)), (4,2--4,14), Yes,
{ ArrowRange = Some (4,9--4,11)
BarRange = Some (4,0--4,1) })], (3,0--4,14),
{ MatchKeyword = (3,0--3,5)
WithKeyword = (3,9--3,13) }), (3,0--4,14));
Expr (Const (Unit, (6,0--6,2)), (6,0--6,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 02.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

match () with
| _ :: -> ()

()
24 changes: 24 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 02.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/Cons 02.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(ListCons
(Wild (4,2--4,3), Wild (4,6--4,6), (4,2--4,6),
{ ColonColonRange = (4,4--4,6) }), None,
Const (Unit, (4,10--4,12)), (4,2--4,12), Yes,
{ ArrowRange = Some (4,7--4,9)
BarRange = Some (4,0--4,1) })], (3,0--4,12),
{ MatchKeyword = (3,0--3,5)
WithKeyword = (3,9--3,13) }), (3,0--4,12));
Expr (Const (Unit, (6,0--6,2)), (6,0--6,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,4)-(4,6) parse error Expecting pattern
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 03.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

match () with
| _ ::

()
25 changes: 25 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 03.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/Cons 03.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(ListCons
(Wild (4,2--4,3), Wild (4,6--4,6), (4,2--4,6),
{ ColonColonRange = (4,4--4,6) }), None,
ArbitraryAfterError ("patternClauses2", (4,6--4,6)),
(4,2--4,6), Yes, { ArrowRange = None
BarRange = Some (4,0--4,1) })],
(3,0--4,6), { MatchKeyword = (3,0--3,5)
WithKeyword = (3,9--3,13) }), (3,0--4,6));
Expr (Const (Unit, (6,0--6,2)), (6,0--6,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,4)-(4,6) parse error Expecting pattern
(6,0)-(6,1) parse error Incomplete structured construct at or before this point in pattern matching. Expected '->' or other token.
7 changes: 7 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 04.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

match () with
| _ ::
| _ -> ()

()
26 changes: 26 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/Cons 04.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/Cons 04.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(Or
(ListCons
(Wild (4,2--4,3), Wild (4,6--4,6), (4,2--4,6),
{ ColonColonRange = (4,4--4,6) }), Wild (5,2--5,3),
(4,2--5,3), { BarRange = (5,0--5,1) }), None,
Const (Unit, (5,7--5,9)), (4,2--5,9), Yes,
{ ArrowRange = Some (5,4--5,6)
BarRange = Some (4,0--4,1) })], (3,0--5,9),
{ MatchKeyword = (3,0--3,5)
WithKeyword = (3,9--3,13) }), (3,0--5,9));
Expr (Const (Unit, (7,0--7,2)), (7,0--7,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--7,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,4)-(4,6) parse error Expecting pattern
Loading