Skip to content

Commit

Permalink
Parser: recover on unfinished 'as' patterns (dotnet#16404)
Browse files Browse the repository at this point in the history
* Parser: recover on unfinished 'as' patterns

* Baselines

* More tests

* More recovery

* More tests

* Remove unused rule

* Baselines

* Restore the rule

* Update baseline

* Update baseline
  • Loading branch information
auduchinok authored Dec 12, 2023
1 parent 3c531f2 commit e795c9b
Show file tree
Hide file tree
Showing 32 changed files with 400 additions and 35 deletions.
28 changes: 25 additions & 3 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -3241,10 +3241,16 @@ localBinding:

| opt_inline opt_mutable bindingPattern opt_topReturnTypeWithTypeConstraints recover
{ if not $5 then reportParseErrorAt (rhs parseState 5) (FSComp.SR.parsUnexpectedEndOfFileDefinition())
let bindingPat, mBindLhs = $3
let optReturnType = $4
let mWhole = rhs2 parseState 1 (match optReturnType with None -> 3 | _ -> 4)
let mWhole =
let mStart = rhs parseState 1
let mEnd =
match optReturnType with
| None -> bindingPat.Range
| Some (_, returnInfo) -> returnInfo.Range
unionRanges mStart mEnd
let mRhs = mWhole.EndRange // zero-width range at end of last good token
let bindingPat, mBindLhs = $3
let localBindingBuilder =
(fun xmlDoc attrs vis (leadingKeyword: SynLeadingKeyword) ->
let spBind = DebugPointAtBinding.Yes(unionRanges leadingKeyword.Range mRhs)
Expand Down Expand Up @@ -3404,7 +3410,7 @@ constant:

bindingPattern:
| headBindingPattern
{ $1, rhs parseState 1 }
{ $1, $1.Range }

// Subset of patterns allowed to be used in implicit ctors.
// For a better error recovery we could replace these rules with the actual SynPat parsing
Expand Down Expand Up @@ -3477,6 +3483,17 @@ headBindingPattern:
| headBindingPattern AS constrPattern
{ SynPat.As($1, $3, rhs2 parseState 1 3) }

| headBindingPattern AS recover
{ let mAs = rhs parseState 2
let pat2 = SynPat.Wild(mAs.EndRange)
SynPat.As($1, pat2, rhs2 parseState 1 2) }

| headBindingPattern AS
{ let mAs = rhs parseState 2
reportParseErrorAt mAs (FSComp.SR.parsExpectingPattern ())
let pat2 = SynPat.Wild(mAs.EndRange)
SynPat.As($1, pat2, rhs2 parseState 1 2) }

| headBindingPattern BAR headBindingPattern
{ let mBar = rhs parseState 2
SynPat.Or($1, $3, rhs2 parseState 1 3, { BarRange = mBar }) }
Expand Down Expand Up @@ -3751,6 +3768,11 @@ parenPattern:
| parenPattern AS constrPattern
{ SynPat.As($1, $3, rhs2 parseState 1 3) }

| parenPattern AS recover
{ let mAs = rhs parseState 2
let pat2 = SynPat.Wild(mAs.EndRange)
SynPat.As($1, pat2, rhs2 parseState 1 2) }

| parenPattern BAR parenPattern
{ let mBar = rhs parseState 2
SynPat.Or($1, $3, rhs2 parseState 1 3, { BarRange = mBar }) }
Expand Down
52 changes: 38 additions & 14 deletions tests/service/PatternMatchCompilationTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -611,16 +611,17 @@ let x as () = y
let z as
"""
dumpDiagnostics checkResults |> shouldEqual [
"(10,9--10,10): Unexpected symbol ',' in binding";
"(11,9--11,10): Unexpected symbol ':' in binding";
"(12,9--12,11): Unexpected symbol '::' in binding";
"(13,9--13,10): Unexpected symbol '&' in binding";
"(14,9--14,10): Unexpected symbol '|' in binding";
"(10,6--10,8): Expecting pattern";
"(11,6--11,8): Expecting pattern";
"(12,6--12,8): Expecting pattern";
"(13,6--13,8): Expecting pattern";
"(14,6--14,8): Expecting pattern";
"(15,13--15,14): Unexpected symbol '=' in pattern. Expected ')' or other token.";
"(15,9--15,10): 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.";
"(17,16--17,17): Unexpected identifier in pattern. Expected '(' or other token.";
"(20,0--20,0): Incomplete structured construct at or before this point in binding";
"(19,6--19,8): Expecting pattern";
"(20,0--20,0): Incomplete structured construct at or before this point in binding. Expected '=' or other token.";
"(3,13--3,17): This expression was expected to have type\u001d 'int' \u001dbut here has type\u001d 'bool'";
"(3,4--3,10): Incomplete pattern matches on this expression. For example, the value '0' may indicate a case not covered by the pattern(s).";
"(4,16--4,17): This expression was expected to have type\u001d 'bool' \u001dbut here has type\u001d 'int'";
Expand All @@ -629,6 +630,11 @@ let z as
"(6,9--6,15): This runtime coercion or type test from type\u001d 'a \u001d to \u001d int \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.";
"(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,15--10,16): This expression was expected to have type\u001d ''a * 'b' \u001dbut here has type\u001d 'int'";
"(11,11--11,12): The type 'k' is not defined.";
"(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,12): The two sides of this 'or' pattern bind different sets of variables";
"(18,14--18,15): The value or constructor 'y' is not defined."
]

Expand Down Expand Up @@ -943,16 +949,17 @@ let :? x as () = y
let :? z as
"""
dumpDiagnostics checkResults |> shouldEqual [
"(10,12--10,13): Unexpected symbol ',' in binding";
"(11,12--11,13): Unexpected symbol ':' in binding";
"(12,12--12,14): Unexpected symbol '::' in binding";
"(13,12--13,13): Unexpected symbol '&' in binding";
"(14,12--14,13): Unexpected symbol '|' in binding";
"(10,9--10,11): Expecting pattern";
"(11,9--11,11): Expecting pattern";
"(12,9--12,11): Expecting pattern";
"(13,9--13,11): Expecting pattern";
"(14,9--14,11): Expecting pattern";
"(15,16--15,17): Unexpected symbol '=' in pattern. Expected ')' or other token.";
"(15,12--15,13): 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.";
"(17,19--17,20): Unexpected identifier in pattern. Expected '(' or other token.";
"(20,0--20,0): Incomplete structured construct at or before this point in binding";
"(19,9--19,11): Expecting pattern";
"(20,0--20,0): Incomplete structured construct at or before this point in binding. Expected '=' or other token.";
"(3,7--3,8): The type 'a' is not defined.";
"(3,4--3,8): 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.";
"(4,7--4,8): The type 'b' is not defined.";
Expand All @@ -967,10 +974,26 @@ let :? z as
"(8,4--8,8): 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.";
"(9,7--9,8): The type 'g' is not defined.";
"(9,4--9,8): 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.";
"(10,7--10,8): The type 'h' is not defined.";
"(10,4--10,8): 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.";
"(10,7--10,8): The type 'h' is not defined.";
"(10,4--10,8): 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.";
"(11,7--11,8): The type 'j' is not defined.";
"(11,4--11,8): 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.";
"(12,7--12,8): The type 'l' is not defined.";
"(12,4--12,8): 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.";
"(13,7--13,8): The type 'n' is not defined.";
"(13,4--13,8): 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.";
"(14,7--14,8): The type 'p' is not defined.";
"(14,4--14,8): 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.";
"(15,7--15,8): The type 'r' is not defined.";
"(15,4--15,8): 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.";
"(17,7--17,8): The type 'v' is not defined.";
"(17,4--17,8): 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.";
"(18,7--18,8): The type 'x' is not defined.";
"(18,4--18,8): 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."
"(18,4--18,8): 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.";
"(19,7--19,8): The type 'z' is not defined.";
"(19,4--19,8): 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."
]

[<Test>]
Expand Down Expand Up @@ -1207,7 +1230,8 @@ let y as ?z = 8
()
"""
dumpDiagnostics checkResults |> shouldEqual [
"(7,9--7,11): Unexpected symbol '[<' in binding"
"(7,6--7,8): Expecting pattern";
"(7,9--7,11): Unexpected symbol '[<' in binding. Expected '=' or other token."
"(8,4--8,11): This is not a valid pattern"
"(8,4--8,16): Incomplete pattern matches on this expression."
"(9,9--9,16): This is not a valid pattern"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ImplFile
SynArgInfo ([], false, None)), None),
Named (SynIdent (this, None), false, None, (4,12--4,16)),
None, ArbitraryAfterError ("memberCore2", (4,16--4,16)),
(4,12--4,19), NoneAtInvisible,
(4,12--4,16), NoneAtInvisible,
{ LeadingKeyword = Member (4,5--4,11)
InlineKeyword = None
EqualsRange = None }), (4,5--4,16))], [], (3,2--3,9),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ImplFile
SynArgInfo ([], false, None)), None),
Named (SynIdent (this, None), false, None, (4,12--4,16)),
None, ArbitraryAfterError ("memberCore1", (4,16--4,16)),
(4,12--5,5), NoneAtInvisible,
(4,12--4,16), NoneAtInvisible,
{ LeadingKeyword = Member (4,5--4,11)
InlineKeyword = None
EqualsRange = None }), (4,5--4,16));
Expand Down
2 changes: 1 addition & 1 deletion tests/service/data/SyntaxTree/Member/Member 03.fs.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ ImplFile
(SynIdent (this, None), false, None, (5,17--5,21)),
None,
ArbitraryAfterError ("memberCore1", (5,21--5,21)),
(5,4--6,4), NoneAtInvisible,
(5,4--5,21), NoneAtInvisible,
{ LeadingKeyword = Member (5,10--5,16)
InlineKeyword = None
EqualsRange = None }), (5,4--5,21));
Expand Down
2 changes: 1 addition & 1 deletion tests/service/data/SyntaxTree/Member/Member 07.fs.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ ImplFile
SynArgInfo ([], false, None)), None),
Named
(SynIdent (this, None), false, None, (5,17--5,21)),
None, Const (Int32 2, (5,25--5,26)), (5,4--5,24),
None, Const (Int32 2, (5,25--5,26)), (5,4--5,21),
NoneAtInvisible,
{ LeadingKeyword = Member (5,10--5,16)
InlineKeyword = None
Expand Down
2 changes: 1 addition & 1 deletion tests/service/data/SyntaxTree/Member/Member 12.fs.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ImplFile
(SynIdent (this, None), false, None, (4,11--4,15)),
None,
ArbitraryAfterError ("memberCore2", (4,15--4,15)),
(4,11--6,1), NoneAtInvisible,
(4,11--4,15), NoneAtInvisible,
{ LeadingKeyword = Member (4,4--4,10)
InlineKeyword = None
EqualsRange = None }), (4,4--4,15))], (4,4--4,15)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ImplFile
(false,
SynLongIdent
([Boolean; parse], [(2,27--2,28)], [None; None]), None,
(2,20--2,33)), (2,4--2,17), Yes (2,0--2,33),
(2,20--2,33)), (2,5--2,16), Yes (2,0--2,33),
{ LeadingKeyword = Let (2,0--2,3)
InlineKeyword = None
EqualsRange = Some (2,18--2,19) })], (2,0--2,33))],
Expand Down
4 changes: 4 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 01.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

match Some 1 with
| _ as _ -> ()
22 changes: 22 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 01.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/As 01.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,17),
App
(NonAtomic, false, Ident Some, Const (Int32 1, (3,11--3,12)),
(3,6--3,12)),
[SynMatchClause
(As (Wild (4,2--4,3), Wild (4,7--4,8), (4,2--4,8)), 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,13--3,17) }), (3,0--4,14))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--4,14), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
4 changes: 4 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 02.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

match Some 1 with
| _ as -> ()
24 changes: 24 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 02.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/As 02.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Match
(Yes (3,0--3,17),
App
(NonAtomic, false, Ident Some, Const (Int32 1, (3,11--3,12)),
(3,6--3,12)),
[SynMatchClause
(As (Wild (4,2--4,3), Wild (4,6--4,6), (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,13--3,17) }), (3,0--4,12))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--4,12), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,7)-(4,9) parse error Unexpected symbol '->' in pattern
3 changes: 3 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 03.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let _ as _ = ()
21 changes: 21 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 03.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/As 03.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
As (Wild (3,4--3,5), Wild (3,9--3,10), (3,4--3,10)), None,
Const (Unit, (3,13--3,15)), (3,4--3,10), Yes (3,0--3,15),
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,11--3,12) })], (3,0--3,15))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--3,15), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
3 changes: 3 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 04.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let _ as = ()
23 changes: 23 additions & 0 deletions tests/service/data/SyntaxTree/Pattern/As 04.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ImplFile
(ParsedImplFileInput
("/root/Pattern/As 04.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
As (Wild (3,4--3,5), Wild (3,8--3,8), (3,4--3,8)), None,
Const (Unit, (3,11--3,13)), (3,4--3,8), Yes (3,0--3,13),
{ LeadingKeyword = Let (3,0--3,3)
InlineKeyword = None
EqualsRange = Some (3,9--3,10) })], (3,0--3,13))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--3,13), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

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

match Some 1 with
| _ as
Loading

0 comments on commit e795c9b

Please sign in to comment.