From 7a225afbdccac739186228c592afb46227b9d59e Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Thu, 23 May 2024 11:29:59 +0200 Subject: [PATCH 1/3] Parser: recover on unfinished cons patterns --- src/Compiler/pars.fsy | 22 ++++++++++++++++ .../data/SyntaxTree/Pattern/Cons 01.fs | 6 +++++ .../data/SyntaxTree/Pattern/Cons 01.fs.bsl | 22 ++++++++++++++++ .../data/SyntaxTree/Pattern/Cons 02.fs | 6 +++++ .../data/SyntaxTree/Pattern/Cons 02.fs.bsl | 24 +++++++++++++++++ .../data/SyntaxTree/Pattern/Cons 03.fs | 6 +++++ .../data/SyntaxTree/Pattern/Cons 03.fs.bsl | 25 ++++++++++++++++++ .../data/SyntaxTree/Pattern/Cons 04.fs | 7 +++++ .../data/SyntaxTree/Pattern/Cons 04.fs.bsl | 26 +++++++++++++++++++ 9 files changed, 144 insertions(+) create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 01.fs create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 01.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 02.fs create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 02.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 03.fs create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 03.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 04.fs create mode 100644 tests/service/data/SyntaxTree/Pattern/Cons 04.fs.bsl diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 96058711135..71547ba6d1b 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -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 @@ -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: diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 01.fs b/tests/service/data/SyntaxTree/Pattern/Cons 01.fs new file mode 100644 index 00000000000..1240aaa7f2c --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 01.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| _ :: _ -> () + +() diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 01.fs.bsl b/tests/service/data/SyntaxTree/Pattern/Cons 01.fs.bsl new file mode 100644 index 00000000000..c48f71c8ab2 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 01.fs.bsl @@ -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 [])) diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 02.fs b/tests/service/data/SyntaxTree/Pattern/Cons 02.fs new file mode 100644 index 00000000000..942db13cf52 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 02.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| _ :: -> () + +() diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 02.fs.bsl b/tests/service/data/SyntaxTree/Pattern/Cons 02.fs.bsl new file mode 100644 index 00000000000..a88741e666c --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 02.fs.bsl @@ -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 diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 03.fs b/tests/service/data/SyntaxTree/Pattern/Cons 03.fs new file mode 100644 index 00000000000..e42f6aaa9f6 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 03.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| _ :: + +() diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 03.fs.bsl b/tests/service/data/SyntaxTree/Pattern/Cons 03.fs.bsl new file mode 100644 index 00000000000..fda13199e97 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 03.fs.bsl @@ -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. diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 04.fs b/tests/service/data/SyntaxTree/Pattern/Cons 04.fs new file mode 100644 index 00000000000..6f487679eb8 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 04.fs @@ -0,0 +1,7 @@ +module Module + +match () with +| _ :: +| _ -> () + +() diff --git a/tests/service/data/SyntaxTree/Pattern/Cons 04.fs.bsl b/tests/service/data/SyntaxTree/Pattern/Cons 04.fs.bsl new file mode 100644 index 00000000000..90d727b5f30 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/Cons 04.fs.bsl @@ -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 From a8fb61cb6c7ecb142d49ede1e87c2622560c491d Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Thu, 23 May 2024 12:43:35 +0200 Subject: [PATCH 2/3] Update baselines --- tests/service/PatternMatchCompilationTests.fs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/service/PatternMatchCompilationTests.fs b/tests/service/PatternMatchCompilationTests.fs index 9b7fe1ff127..5d0645483c7 100644 --- a/tests/service/PatternMatchCompilationTests.fs +++ b/tests/service/PatternMatchCompilationTests.fs @@ -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."; @@ -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." ] @@ -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."; @@ -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." From 76c0d9cade23fb9b0d5c82e880246c292256aaf0 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Fri, 24 May 2024 00:30:57 +0200 Subject: [PATCH 3/3] Release notes --- docs/release-notes/.FSharp.Compiler.Service/8.0.400.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index 29dac7317ee..25b83ea049b 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -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))