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 744d041341e..01d53a00185 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -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)) * Parser: recover on empty match clause ([PR #17233](https://github.com/dotnet/fsharp/pull/17233)) ### Changed diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 96dccbc1353..12a252edca9 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -168,6 +168,7 @@ let parse_error_rich = Some(fun (ctxt: ParseErrorContext<_>) -> %type declExprBlock %type headBindingPattern %type atomTypeNonAtomicDeprecated +%type atomTypeOrAnonRecdType %type atomicExprAfterType %type typedSequentialExprBlock %type atomicExpr @@ -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 }) } @@ -3583,7 +3584,21 @@ 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 recover %prec pat_isinst + { let mColon = rhs parseState 1 + if not $2 then + 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 } @@ -3760,6 +3775,8 @@ parenPattern: | parenPattern COLON recover { let mColon = rhs parseState 2 + if not $3 then + reportParseErrorAt mColon (FSComp.SR.parsExpectingPattern ()) let ty = SynType.FromParseError(mColon.EndRange) SynPat.Typed($1, ty, unionRanges $1.Range mColon) } @@ -3773,6 +3790,8 @@ parenPattern: | parenPattern COLON_COLON recover { let mColonColon = rhs parseState 2 + if not $3 then + reportParseErrorAt mColonColon (FSComp.SR.parsExpectingPattern ()) let pat2 = SynPat.Wild(mColonColon.EndRange) SynPat.ListCons($1, pat2, rhs2 parseState 1 2, { ColonColonRange = mColonColon }) } diff --git a/tests/service/PatternMatchCompilationTests.fs b/tests/service/PatternMatchCompilationTests.fs index 5d0645483c7..236f62172d6 100644 --- a/tests/service/PatternMatchCompilationTests.fs +++ b/tests/service/PatternMatchCompilationTests.fs @@ -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."; @@ -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." ] @@ -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 '('"; @@ -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." diff --git a/tests/service/data/SyntaxTree/MatchClause/Missing pat 05.fs b/tests/service/data/SyntaxTree/MatchClause/Missing pat 05.fs new file mode 100644 index 00000000000..34b8bb6fb33 --- /dev/null +++ b/tests/service/data/SyntaxTree/MatchClause/Missing pat 05.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| + +() \ No newline at end of file diff --git a/tests/service/data/SyntaxTree/MatchClause/Missing pat 05.fs.bsl b/tests/service/data/SyntaxTree/MatchClause/Missing pat 05.fs.bsl new file mode 100644 index 00000000000..e9693065f21 --- /dev/null +++ b/tests/service/data/SyntaxTree/MatchClause/Missing pat 05.fs.bsl @@ -0,0 +1,18 @@ +ImplFile + (ParsedImplFileInput + ("/root/MatchClause/Missing pat 05.fs", false, QualifiedNameOfFile Module, + [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Expr + (Match + (Yes (3,0--3,13), Const (Unit, (3,6--3,8)), [], (3,0--4,1), + { MatchKeyword = (3,0--3,5) + WithKeyword = (3,9--3,13) }), (3,0--4,1)); + 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 expression diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 01.fs b/tests/service/data/SyntaxTree/Pattern/IsInst 01.fs new file mode 100644 index 00000000000..c8d91e018b0 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 01.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| :? T -> () + +() diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 01.fs.bsl b/tests/service/data/SyntaxTree/Pattern/IsInst 01.fs.bsl new file mode 100644 index 00000000000..3b690e54512 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 01.fs.bsl @@ -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 [])) diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 02.fs b/tests/service/data/SyntaxTree/Pattern/IsInst 02.fs new file mode 100644 index 00000000000..c51d9658d56 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 02.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| :? T + +() diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 02.fs.bsl b/tests/service/data/SyntaxTree/Pattern/IsInst 02.fs.bsl new file mode 100644 index 00000000000..dd1e86fabe4 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 02.fs.bsl @@ -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. diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 03.fs b/tests/service/data/SyntaxTree/Pattern/IsInst 03.fs new file mode 100644 index 00000000000..1497d452b01 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 03.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| :? -> () + +() diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 03.fs.bsl b/tests/service/data/SyntaxTree/Pattern/IsInst 03.fs.bsl new file mode 100644 index 00000000000..320a8c97f75 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 03.fs.bsl @@ -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 diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 04.fs b/tests/service/data/SyntaxTree/Pattern/IsInst 04.fs new file mode 100644 index 00000000000..27391569dca --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 04.fs @@ -0,0 +1,6 @@ +module Module + +match () with +| :? + +() diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 04.fs.bsl b/tests/service/data/SyntaxTree/Pattern/IsInst 04.fs.bsl new file mode 100644 index 00000000000..1cf58c17dbc --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 04.fs.bsl @@ -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. diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 05.fs b/tests/service/data/SyntaxTree/Pattern/IsInst 05.fs new file mode 100644 index 00000000000..5e7445b6b28 --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 05.fs @@ -0,0 +1,7 @@ +module Module + +match () with +| :? +| _ -> () + +() diff --git a/tests/service/data/SyntaxTree/Pattern/IsInst 05.fs.bsl b/tests/service/data/SyntaxTree/Pattern/IsInst 05.fs.bsl new file mode 100644 index 00000000000..136e844c2ef --- /dev/null +++ b/tests/service/data/SyntaxTree/Pattern/IsInst 05.fs.bsl @@ -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