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 'as' patterns #16404

Merged
merged 10 commits into from
Dec 12, 2023
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
28 changes: 25 additions & 3 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -3214,10 +3214,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 @@ -3377,7 +3383,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 @@ -3450,6 +3456,17 @@ headBindingPattern:
| headBindingPattern AS constrPattern
{ SynPat.As($1, $3, rhs2 parseState 1 3) }

| headBindingPattern AS recover
auduchinok marked this conversation as resolved.
Show resolved Hide resolved
{ 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 @@ -3724,6 +3741,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