Skip to content

Commit 38f966d

Browse files
authored
Parser: add recovery for unfinished match clauses (#10872)
* Diagnostics: add production rule index * Parser: fix recovery for unfinished match clause Fixes recovery for missing right hand sides: ``` match () with | x ``` * Add tests * Add recovery near to next clause * Restore indent in test data * Update desktop test baselines * Update FSharpQA test baselines * Fix recovered patterns ranges * Try to fix Desktop test suite * Update surface area * Better diagnostics * Try to fix Desktop test suite * Fix tests * Simplify test * Add more tests * Disable failing test
1 parent 1d7d97e commit 38f966d

File tree

16 files changed

+199
-92
lines changed

16 files changed

+199
-92
lines changed

src/fsharp/CompilerDiagnostics.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ let OutputPhasedErrorR (os: StringBuilder) (err: PhasedDiagnostic) (canSuggestNa
11691169
printfn " ----"
11701170
//printfn " state %d" state
11711171
for rp in rps do
1172-
printfn " non-terminal %+A: ... " (Parser.prodIdxToNonTerminal rp)
1172+
printfn " non-terminal %+A (idx %d): ... " (Parser.prodIdxToNonTerminal rp) rp
11731173
#endif
11741174

11751175
match ctxt.CurrentToken with

src/fsharp/SyntaxTree.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,11 @@ type SynArgPats =
10151015
pats: (Ident * SynPat) list *
10161016
range: range
10171017

1018+
member x.Patterns =
1019+
match x with
1020+
| Pats pats -> pats
1021+
| NamePatPairs (pats, _) -> pats |> List.map snd
1022+
10181023
[<NoEquality; NoComparison;RequireQualifiedAccess>]
10191024
type SynPat =
10201025

src/fsharp/SyntaxTree.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,8 @@ type SynArgPats =
11481148
pats: (Ident * SynPat) list *
11491149
range: range
11501150

1151+
member Patterns: SynPat list
1152+
11511153
/// Represents a syntax tree for an F# pattern
11521154
[<NoEquality; NoComparison; RequireQualifiedAccess>]
11531155
type SynPat =

src/fsharp/pars.fsy

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ let rangeOfLongIdent(lid:LongIdent) =
316316
%type <Ident> ident
317317
%type <SynType> typ typEOF
318318
%type <SynTypeDefnSig list> tyconSpfnList
319+
%type <SynArgPats * Range> atomicPatsOrNamePatPairs
320+
%type <SynPat list> atomicPatterns
319321
%type <Range * SynExpr> patternResult
320322
%type <SynExpr> declExpr
321323
%type <SynExpr> minusExpr
@@ -2337,13 +2339,6 @@ opt_explicitValTyparDecls:
23372339
|
23382340
{ SynValTyparDecls(None, true) }
23392341

2340-
opt_explicitValTyparDecls2:
2341-
| explicitValTyparDecls
2342-
{ Some $1 }
2343-
2344-
| /* EMPTY */
2345-
{ None }
2346-
23472342
/* Any tokens in this grammar must be added to the lex filter rule 'peekAdjacentTypars' */
23482343
/* See the F# specification "Lexical analysis of type applications and type parameter definitions" */
23492344
opt_typeConstraints:
@@ -3103,17 +3098,45 @@ namePatPair:
31033098
{ ($1, $3) }
31043099

31053100
constrPattern:
3106-
| atomicPatternLongIdent explicitValTyparDecls
3107-
{ let vis, lid = $1 in SynPat.LongIdent (lid, None, Some $2, SynArgPats.Pats [], vis, lhs parseState) }
3108-
3109-
| atomicPatternLongIdent opt_explicitValTyparDecls2 atomicPatsOrNamePatPairs %prec pat_app
3110-
{ let vis, lid = $1 in SynPat.LongIdent (lid, None, $2, $3, vis, lhs parseState) }
3111-
3112-
| atomicPatternLongIdent opt_explicitValTyparDecls2 HIGH_PRECEDENCE_PAREN_APP atomicPatsOrNamePatPairs
3113-
{ let vis, lid = $1 in SynPat.LongIdent (lid, None, $2, $4, vis, lhs parseState) }
3114-
3115-
| atomicPatternLongIdent opt_explicitValTyparDecls2 HIGH_PRECEDENCE_BRACK_APP atomicPatsOrNamePatPairs
3116-
{ let vis, lid = $1 in SynPat.LongIdent (lid, None, $2, $4, vis, lhs parseState) }
3101+
| atomicPatternLongIdent explicitValTyparDecls
3102+
{ let vis, lid = $1
3103+
SynPat.LongIdent (lid, None, Some $2, SynArgPats.Pats [], vis, lhs parseState) }
3104+
3105+
| atomicPatternLongIdent explicitValTyparDecls atomicPatsOrNamePatPairs %prec pat_app
3106+
{ let vis, lid = $1
3107+
let args, argsM = $3
3108+
let m = unionRanges (rhs2 parseState 1 2) argsM
3109+
SynPat.LongIdent (lid, None, Some $2, args, vis, m) }
3110+
3111+
| atomicPatternLongIdent explicitValTyparDecls HIGH_PRECEDENCE_PAREN_APP atomicPatsOrNamePatPairs
3112+
{ let vis, lid = $1
3113+
let args, argsM = $4
3114+
let m = unionRanges (rhs2 parseState 1 2) argsM
3115+
SynPat.LongIdent (lid, None, Some $2, args, vis, m) }
3116+
3117+
| atomicPatternLongIdent explicitValTyparDecls HIGH_PRECEDENCE_BRACK_APP atomicPatsOrNamePatPairs
3118+
{ let vis, lid = $1
3119+
let args, argsM = $4
3120+
let m = unionRanges (rhs2 parseState 1 2) argsM
3121+
SynPat.LongIdent (lid, None, Some $2, args, vis, m) }
3122+
3123+
| atomicPatternLongIdent atomicPatsOrNamePatPairs %prec pat_app
3124+
{ let vis, lid = $1
3125+
let args, argsM = $2
3126+
let m = unionRanges (rhs parseState 1) argsM
3127+
SynPat.LongIdent (lid, None, None, args, vis, m) }
3128+
3129+
| atomicPatternLongIdent HIGH_PRECEDENCE_PAREN_APP atomicPatsOrNamePatPairs
3130+
{ let vis, lid = $1
3131+
let args, argsM = $3
3132+
let m = unionRanges (rhs parseState 1) argsM
3133+
SynPat.LongIdent (lid, None, None, args, vis, m) }
3134+
3135+
| atomicPatternLongIdent HIGH_PRECEDENCE_BRACK_APP atomicPatsOrNamePatPairs
3136+
{ let vis, lid = $1
3137+
let args, argsM = $3
3138+
let m = unionRanges (rhs parseState 1) argsM
3139+
SynPat.LongIdent (lid, None, None, args, vis, m) }
31173140

31183141
| COLON_QMARK atomTypeOrAnonRecdType %prec pat_isinst
31193142
{ SynPat.IsInst($2, lhs parseState) }
@@ -3123,10 +3146,12 @@ constrPattern:
31233146

31243147
atomicPatsOrNamePatPairs:
31253148
| LPAREN namePatPairs rparen
3126-
{ SynArgPats.NamePatPairs $2 }
3149+
{ SynArgPats.NamePatPairs $2, snd $2 }
31273150

31283151
| atomicPatterns
3129-
{ SynArgPats.Pats $1 }
3152+
{ let mWhole = rhs parseState 1
3153+
let m = (mWhole.StartRange, $1) ||> unionRangeWithListBy (fun p -> p.Range)
3154+
SynArgPats.Pats $1, m }
31303155

31313156
atomicPatterns:
31323157
| atomicPattern atomicPatterns %prec pat_args
@@ -3970,6 +3995,12 @@ patternClauses:
39703995
let m = unionRanges resultExpr.Range patm
39713996
(SynMatchClause(pat, guard, (Some mArrow), resultExpr, m, DebugPointAtTarget.Yes) :: clauses), mLast }
39723997

3998+
| patternAndGuard error BAR patternClauses
3999+
{ let pat, guard, patm = $1
4000+
let clauses, mLast = $4
4001+
let m = guard |> Option.map (fun e -> unionRanges patm e.Range) |> Option.defaultValue patm
4002+
(SynMatchClause(pat, guard, None, arbExpr ("patternClauses1", m.EndRange), m, DebugPointAtTarget.Yes) :: clauses), mLast }
4003+
39734004
| patternAndGuard patternResult BAR error
39744005
{ let pat, guard, patm = $1
39754006
let mArrow, resultExpr = $2
@@ -3989,12 +4020,9 @@ patternClauses:
39894020
| patternAndGuard error
39904021
{ let pat, guard, patm = $1
39914022
let mLast = rhs parseState 2
3992-
let m =
3993-
match guard with
3994-
| Some e -> unionRanges patm e.Range
3995-
| _ -> patm
4023+
let m = guard |> Option.map (fun e -> unionRanges patm e.Range) |> Option.defaultValue patm
39964024
// silent recovery
3997-
[SynMatchClause(pat, guard, None, SynExpr.Const (SynConst.Unit, mLast.EndRange), m, DebugPointAtTarget.Yes)], mLast }
4025+
[SynMatchClause(pat, guard, None, arbExpr ("patternClauses2", m.EndRange), m, DebugPointAtTarget.Yes)], mLast }
39984026

39994027
patternGuard:
40004028
| WHEN declExpr

src/fsharp/service/ServiceParsedInputOps.fs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,11 @@ module ParsedInput =
908908
if isOnTheRightOfComma elements commas e then Some args else None
909909
| _ -> None
910910

911+
let (|SkipFromParseErrorPat|) pat =
912+
match pat with
913+
| SynPat.FromParseError(pat, _) -> pat
914+
| _ -> pat
915+
911916
let walker =
912917
{
913918
new SyntaxVisitorBase<_>() with
@@ -980,7 +985,8 @@ module ParsedInput =
980985

981986
member _.VisitBinding(_path, defaultTraverse, (SynBinding(headPat = headPat) as synBinding)) =
982987

983-
let visitParam = function
988+
let visitParam (SkipFromParseErrorPat pat) =
989+
match pat with
984990
| SynPat.Named (range = range)
985991
| SynPat.As (_, SynPat.Named (range = range), _) when rangeContainsPos range pos ->
986992
// parameter without type hint, no completion
@@ -999,12 +1005,13 @@ module ParsedInput =
9991005
| SynArgPats.Pats pats ->
10001006
pats |> List.tryPick (fun pat ->
10011007
match pat with
1008+
| SynPat.FromParseError(pat, _)
10021009
| SynPat.Paren(pat, _) ->
10031010
match pat with
10041011
| SynPat.Tuple(_, pats, _) ->
10051012
pats |> List.tryPick visitParam
10061013
| _ -> visitParam pat
1007-
| SynPat.Wild range when rangeContainsPos range pos ->
1014+
| SynPat.Wild range | SynPat.FromParseError (SynPat.Named _, range) when rangeContainsPos range pos ->
10081015
// let foo (x|
10091016
Some CompletionContext.Invalid
10101017
| _ -> visitParam pat

tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5239,6 +5239,29 @@ FSharp.Compiler.Syntax.DebugPointAtSwitch: Int32 GetHashCode(System.Collections.
52395239
FSharp.Compiler.Syntax.DebugPointAtSwitch: Int32 Tag
52405240
FSharp.Compiler.Syntax.DebugPointAtSwitch: Int32 get_Tag()
52415241
FSharp.Compiler.Syntax.DebugPointAtSwitch: System.String ToString()
5242+
FSharp.Compiler.Syntax.DebugPointAtTarget
5243+
FSharp.Compiler.Syntax.DebugPointAtTarget+Tags: Int32 No
5244+
FSharp.Compiler.Syntax.DebugPointAtTarget+Tags: Int32 Yes
5245+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean Equals(FSharp.Compiler.Syntax.DebugPointAtTarget)
5246+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean Equals(System.Object)
5247+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
5248+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean IsNo
5249+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean IsYes
5250+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean get_IsNo()
5251+
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean get_IsYes()
5252+
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget No
5253+
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget Yes
5254+
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget get_No()
5255+
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget get_Yes()
5256+
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget+Tags
5257+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 CompareTo(FSharp.Compiler.Syntax.DebugPointAtTarget)
5258+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 CompareTo(System.Object)
5259+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 CompareTo(System.Object, System.Collections.IComparer)
5260+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 GetHashCode()
5261+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 GetHashCode(System.Collections.IEqualityComparer)
5262+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 Tag
5263+
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 get_Tag()
5264+
FSharp.Compiler.Syntax.DebugPointAtTarget: System.String ToString()
52425265
FSharp.Compiler.Syntax.DebugPointAtTry
52435266
FSharp.Compiler.Syntax.DebugPointAtTry+Tags: Int32 Body
52445267
FSharp.Compiler.Syntax.DebugPointAtTry+Tags: Int32 No
@@ -5310,29 +5333,6 @@ FSharp.Compiler.Syntax.DebugPointAtWith: Int32 GetHashCode(System.Collections.IE
53105333
FSharp.Compiler.Syntax.DebugPointAtWith: Int32 Tag
53115334
FSharp.Compiler.Syntax.DebugPointAtWith: Int32 get_Tag()
53125335
FSharp.Compiler.Syntax.DebugPointAtWith: System.String ToString()
5313-
FSharp.Compiler.Syntax.DebugPointAtTarget
5314-
FSharp.Compiler.Syntax.DebugPointAtTarget+Tags: Int32 No
5315-
FSharp.Compiler.Syntax.DebugPointAtTarget+Tags: Int32 Yes
5316-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean Equals(FSharp.Compiler.Syntax.DebugPointAtTarget)
5317-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean Equals(System.Object)
5318-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
5319-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean IsNo
5320-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean IsYes
5321-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean get_IsNo()
5322-
FSharp.Compiler.Syntax.DebugPointAtTarget: Boolean get_IsYes()
5323-
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget No
5324-
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget Yes
5325-
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget get_No()
5326-
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget get_Yes()
5327-
FSharp.Compiler.Syntax.DebugPointAtTarget: FSharp.Compiler.Syntax.DebugPointAtTarget+Tags
5328-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 CompareTo(FSharp.Compiler.Syntax.DebugPointAtTarget)
5329-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 CompareTo(System.Object)
5330-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 CompareTo(System.Object, System.Collections.IComparer)
5331-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 GetHashCode()
5332-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 GetHashCode(System.Collections.IEqualityComparer)
5333-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 Tag
5334-
FSharp.Compiler.Syntax.DebugPointAtTarget: Int32 get_Tag()
5335-
FSharp.Compiler.Syntax.DebugPointAtTarget: System.String ToString()
53365336
FSharp.Compiler.Syntax.ExprAtomicFlag
53375337
FSharp.Compiler.Syntax.ExprAtomicFlag: FSharp.Compiler.Syntax.ExprAtomicFlag Atomic
53385338
FSharp.Compiler.Syntax.ExprAtomicFlag: FSharp.Compiler.Syntax.ExprAtomicFlag NonAtomic
@@ -5728,6 +5728,8 @@ FSharp.Compiler.Syntax.SynArgPats: FSharp.Compiler.Syntax.SynArgPats+Pats
57285728
FSharp.Compiler.Syntax.SynArgPats: FSharp.Compiler.Syntax.SynArgPats+Tags
57295729
FSharp.Compiler.Syntax.SynArgPats: Int32 Tag
57305730
FSharp.Compiler.Syntax.SynArgPats: Int32 get_Tag()
5731+
FSharp.Compiler.Syntax.SynArgPats: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynPat] Patterns
5732+
FSharp.Compiler.Syntax.SynArgPats: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynPat] get_Patterns()
57315733
FSharp.Compiler.Syntax.SynArgPats: System.String ToString()
57325734
FSharp.Compiler.Syntax.SynAttribute
57335735
FSharp.Compiler.Syntax.SynAttribute: Boolean AppliesToGetterAndSetter

tests/fsharp/typecheck/sigs/neg104.bsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ neg104.fs(20,23,20,24): parse error FS0010: Incomplete structured construct at o
77

88
neg104.fs(23,25,23,26): parse error FS0010: Incomplete structured construct at or before this point in expression
99

10-
neg104.fs(26,28,26,29): parse error FS0010: Incomplete structured construct at or before this point in pattern
10+
neg104.fs(26,28,26,29): parse error FS0010: Incomplete structured construct at or before this point in pattern matching. Expected '->' or other token.
1111

1212
neg104.fs(29,31,29,32): parse error FS0010: Incomplete structured construct at or before this point in pattern matching
1313

tests/fsharp/typecheck/sigs/neg104.vsbsl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ neg104.fs(20,23,20,24): parse error FS0010: Incomplete structured construct at o
77

88
neg104.fs(23,25,23,26): parse error FS0010: Incomplete structured construct at or before this point in expression
99

10-
neg104.fs(26,28,26,29): parse error FS0010: Incomplete structured construct at or before this point in pattern
10+
neg104.fs(26,28,26,29): parse error FS0010: Incomplete structured construct at or before this point in pattern matching. Expected '->' or other token.
1111

1212
neg104.fs(29,31,29,32): parse error FS0010: Incomplete structured construct at or before this point in pattern matching
1313

@@ -27,8 +27,6 @@ neg104.fs(20,9,20,22): typecheck error FS0025: Incomplete pattern matches on thi
2727

2828
neg104.fs(23,9,23,22): typecheck error FS0025: Incomplete pattern matches on this expression.
2929

30-
neg104.fs(26,9,26,22): typecheck error FS0025: Incomplete pattern matches on this expression.
31-
3230
neg104.fs(32,21,32,26): typecheck error FS0003: This value is not a function and cannot be applied.
3331

3432
neg104.fs(35,9,35,18): typecheck error FS0748: This construct may only be used within computation expressions. To return a value from an ordinary function simply write the expression without 'return'.

tests/fsharp/typecheck/sigs/neg80.vsbsl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,3 @@
22
neg80.fsx(79,5,79,6): parse error FS0010: Unexpected symbol '|' in pattern matching
33

44
neg80.fsx(79,5,79,6): parse error FS0010: Unexpected symbol '|' in pattern matching
5-
6-
neg80.fsx(79,6,79,6): typecheck error FS0001: All branches of a pattern match expression must return values implicitly convertible to the type of the first branch, which here is 'string'. This branch returns a value of type 'unit'.
7-
8-
neg80.fsx(76,11,76,13): typecheck error FS0025: Incomplete pattern matches on this expression. For example, the value 'Horizontal (_, _)' may indicate a case not covered by the pattern(s).

tests/fsharpqa/Source/Conformance/LexicalAnalysis/IdentifiersAndKeywords/E_KeywordIdent01.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#light
33

44
//<Expects id="FS0010" status="error">Unexpected keyword 'type' in binding</Expects>
5-
//<Expects id="FS0010" status="error">Unexpected keyword 'class' in pattern</Expects>
5+
//<Expects id="FS0010" status="error">Unexpected keyword 'class' in binding. Expected '=' or other token.</Expects>
66

77
let type = 2
88

0 commit comments

Comments
 (0)