Skip to content
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

Parser: recover on unfinished isConst patterns #17232

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +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))
* Parser: better recovery for unfinished patterns ([PR #17231](https://github.com/dotnet/fsharp/pull/17231), [PR #17232](https://github.com/dotnet/fsharp/pull/17232)))

### Changed
* Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173))
Expand Down
24 changes: 20 additions & 4 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ let parse_error_rich = Some(fun (ctxt: ParseErrorContext<_>) ->
%type <SynExpr> declExprBlock
%type <SynPat> headBindingPattern
%type <SynType> atomTypeNonAtomicDeprecated
%type <SynType> atomTypeOrAnonRecdType
psfinaki marked this conversation as resolved.
Show resolved Hide resolved
%type <SynExpr> atomicExprAfterType
%type <SynExpr> typedSequentialExprBlock
%type <SynExpr * bool> atomicExpr
Expand Down Expand Up @@ -3454,7 +3455,7 @@ headBindingPattern:
{ let mColonColon = rhs parseState 2
SynPat.ListCons($1, $3, rhs2 parseState 1 3, { ColonColonRange = mColonColon }) }

| headBindingPattern COLON_COLON recover
| headBindingPattern COLON_COLON ends_coming_soon_or_recover
{ let mColonColon = rhs parseState 2
let pat2 = SynPat.Wild(mColonColon.EndRange)
SynPat.ListCons($1, pat2, rhs2 parseState 1 2, { ColonColonRange = mColonColon }) }
Expand Down Expand Up @@ -3583,7 +3584,20 @@ constrPattern:
SynPat.LongIdent(lid, None, None, args, vis, m) }

| COLON_QMARK atomTypeOrAnonRecdType %prec pat_isinst
{ SynPat.IsInst($2, lhs parseState) }
{ let m = unionRanges (rhs parseState 1) $2.Range
SynPat.IsInst($2, m) }

| COLON_QMARK ends_coming_soon_or_recover %prec pat_isinst
{ let mColon = rhs parseState 1
auduchinok marked this conversation as resolved.
Show resolved Hide resolved
reportParseErrorAt mColon (FSComp.SR.parsExpectingPattern ())
let ty = SynType.FromParseError(mColon.EndRange)
SynPat.IsInst(ty, mColon) }

| COLON_QMARK %prec pat_isinst
{ let mColon = rhs parseState 1
let ty = SynType.FromParseError(mColon.EndRange)
reportParseErrorAt mColon (FSComp.SR.parsExpectingType ())
SynPat.IsInst(ty, mColon) }

| atomicPattern
{ $1 }
Expand Down Expand Up @@ -3758,8 +3772,9 @@ parenPattern:
{ let mLhs = lhs parseState
SynPat.Typed($1, $3, mLhs) }

| parenPattern COLON recover
| parenPattern COLON ends_coming_soon_or_recover
{ let mColon = rhs parseState 2
reportParseErrorAt mColon (FSComp.SR.parsExpectingPattern ())
let ty = SynType.FromParseError(mColon.EndRange)
SynPat.Typed($1, ty, unionRanges $1.Range mColon) }

Expand All @@ -3771,8 +3786,9 @@ parenPattern:
{ let mColonColon = rhs parseState 2
SynPat.ListCons($1, $3, rhs2 parseState 1 3, { ColonColonRange = mColonColon }) }

| parenPattern COLON_COLON recover
| parenPattern COLON_COLON ends_coming_soon_or_recover
{ 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 }) }

Expand Down
8 changes: 6 additions & 2 deletions tests/service/PatternMatchCompilationTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ let z as =
"(11,10--11,12): Unexpected keyword 'as' in binding. Expected '=' or other token.";
"(12,6--12,8): Expecting pattern";
"(13,8--13,10): Unexpected keyword 'as' in binding";
"(14,8--14,10): Unexpected keyword 'as' in binding";
"(14,6--14,7): Expecting pattern";
"(15,8--15,10): Unexpected keyword 'as' in pattern. Expected ')' or other token.";
"(15,6--15,7): Unmatched '('";
"(16,0--16,3): Unexpected syntax or possible incorrect indentation: this token is offside of context started at position (15:1). Try indenting this further.\u001dTo continue using non-conforming indentation, pass the '--strict-indentation-' flag to the compiler, or set the language version to F# 7.";
Expand All @@ -778,6 +778,7 @@ let z as =
"(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).";
"(14,4--14,7): The two sides of this 'or' pattern bind different sets of variables";
"(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 @@ -1180,7 +1181,7 @@ let as :? z =
"(11,10--11,12): Unexpected keyword 'as' in binding. Expected '=' or other token.";
"(12,6--12,8): Expecting pattern";
"(13,8--13,10): Unexpected keyword 'as' in binding";
"(14,8--14,10): Unexpected keyword 'as' in binding";
"(14,6--14,7): Expecting pattern";
"(15,13--15,15): Unexpected keyword 'as' in pattern. Expected '(' or other token.";
"(16,8--16,10): Unexpected keyword 'as' in pattern. Expected ')' or other token.";
"(16,6--16,7): Unmatched '('";
Expand Down Expand Up @@ -1208,6 +1209,9 @@ let as :? z =
"(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.";
"(14,4--14,7): The two sides of this 'or' pattern bind different sets of variables";
"(14,14--14,15): The type 'q' is not defined.";
"(14,11--14,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.";
"(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/IsInst 01.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

match () with
| :? T -> ()

()
21 changes: 21 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 01.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/IsInst 01.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(IsInst
(LongIdent (SynLongIdent ([T], [], [None])), (4,2--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 []))
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 02.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

match () with
| :? T

()
23 changes: 23 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 02.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/IsInst 02.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(IsInst
(LongIdent (SynLongIdent ([T], [], [None])), (4,2--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 []))

(6,0)-(6,1) parse error Incomplete structured construct at or before this point in pattern matching. Expected '->' or other token.
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 03.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/IsInst 03.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/IsInst 03.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(IsInst (FromParseError (4,4--4,4), (4,2--4,4)), None,
Const (Unit, (4,8--4,10)), (4,2--4,10), Yes,
{ ArrowRange = Some (4,5--4,7)
BarRange = Some (4,0--4,1) })], (3,0--4,10),
{ MatchKeyword = (3,0--3,5)
WithKeyword = (3,9--3,13) }), (3,0--4,10));
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,2)-(4,4) parse error Expecting type
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 04.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

match () with
| :?

()
23 changes: 23 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 04.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/IsInst 04.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(IsInst (FromParseError (4,4--4,4), (4,2--4,4)), None,
ArbitraryAfterError ("patternClauses2", (4,4--4,4)),
(4,2--4,4), Yes, { ArrowRange = None
BarRange = Some (4,0--4,1) })],
(3,0--4,4), { MatchKeyword = (3,0--3,5)
WithKeyword = (3,9--3,13) }), (3,0--4,4));
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,2)-(4,4) parse error Expecting type
(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/IsInst 05.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Module

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

()
24 changes: 24 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/IsInst 05.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/IsInst 05.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,13), Const (Unit, (3,6--3,8)),
[SynMatchClause
(Or
(IsInst (FromParseError (4,4--4,4), (4,2--4,4)),
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,2)-(4,4) parse error Expecting type
Loading