Skip to content

Commit 4f7f156

Browse files
authored
Parser: recover on missing items in tuple patterns (#13985)
1 parent 58023aa commit 4f7f156

20 files changed

+363
-23
lines changed

src/Compiler/FSComp.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,3 +1653,5 @@ reprStateMachineInvalidForm,"The state machine has an unexpected form"
16531653
3536,tcUsingInterfaceWithStaticAbstractMethodAsType,"'%s' is normally used as a type constraint in generic code, e.g. \"'T when ISomeInterface<'T>\" or \"let f (x: #ISomeInterface<_>)\". See https://aka.ms/fsharp-iwsams for guidance. You can disable this warning by using '#nowarn \"3536\"' or '--nowarn:3536'."
16541654
3537,tcTraitHasMultipleSupportTypes,"The trait '%s' invoked by this call has multiple support types. This invocation syntax is not permitted for such traits. See https://aka.ms/fsharp-srtp for guidance."
16551655
3545,tcMissingRequiredMembers,"The following required properties have to be initalized:%s"
1656+
3546,parsExpectingPatternInTuple,"Expecting pattern"
1657+
3547,parsExpectedPatternAfterToken,"Expected a pattern after this point"

src/Compiler/SyntaxTree/SyntaxTreeOps.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,13 @@ let rec normalizeTupleExpr exprs commas : SynExpr list * range list =
10311031
innerExprs @ rest, innerCommas @ commas
10321032
| _ -> exprs, commas
10331033

1034+
let rec normalizeTuplePat pats : SynPat list =
1035+
match pats with
1036+
| SynPat.Tuple (false, innerPats, _) :: rest ->
1037+
let innerExprs = normalizeTuplePat (List.rev innerPats)
1038+
innerExprs @ rest
1039+
| _ -> pats
1040+
10341041
/// Remove all members that were captures as SynMemberDefn.GetSetMember
10351042
let rec desugarGetSetMembers (memberDefns: SynMemberDefns) =
10361043
memberDefns

src/Compiler/SyntaxTree/SyntaxTreeOps.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ val mkDynamicArgExpr: expr: SynExpr -> SynExpr
350350

351351
val normalizeTupleExpr: exprs: SynExpr list -> commas: range list -> SynExpr list * range List
352352

353+
val normalizeTuplePat: pats: SynPat list -> SynPat list
354+
353355
val desugarGetSetMembers: memberDefns: SynMemberDefns -> SynMemberDefns
354356

355357
val getTypeFromTuplePath: path: SynTupleTypeSegment list -> SynType list

src/Compiler/pars.fsy

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3126,21 +3126,48 @@ headBindingPattern:
31263126
| headBindingPattern COLON_COLON headBindingPattern
31273127
{ SynPat.LongIdent (SynLongIdent(mkSynCaseName (rhs parseState 2) opNameCons, [], [ Some (IdentTrivia.OriginalNotation "::") ]), None, None, SynArgPats.Pats [SynPat.Tuple (false, [$1;$3], rhs2 parseState 1 3)], None, lhs parseState) }
31283128

3129-
| tuplePatternElements %prec pat_tuple
3130-
{ SynPat.Tuple(false, List.rev $1, lhs parseState) }
3129+
| tuplePatternElements %prec pat_tuple
3130+
{ let pats = normalizeTuplePat $1
3131+
let m = (rhs parseState 1, pats) ||> unionRangeWithListBy (fun p -> p.Range)
3132+
SynPat.Tuple(false, List.rev pats, m) }
31313133

31323134
| conjPatternElements %prec pat_conj
31333135
{ SynPat.Ands(List.rev $1, lhs parseState) }
31343136

31353137
| constrPattern
31363138
{ $1 }
31373139

3138-
tuplePatternElements:
3139-
| tuplePatternElements COMMA headBindingPattern
3140+
tuplePatternElements:
3141+
| tuplePatternElements COMMA headBindingPattern
31403142
{ $3 :: $1 }
31413143

3142-
| headBindingPattern COMMA headBindingPattern
3143-
{ $3 :: $1 :: [] }
3144+
| headBindingPattern COMMA headBindingPattern
3145+
{ [$3; $1] }
3146+
3147+
| tuplePatternElements COMMA ends_coming_soon_or_recover
3148+
{ let commaRange = rhs parseState 2
3149+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingPatternInTuple ())
3150+
let pat2 = SynPat.Wild(commaRange.EndRange)
3151+
pat2 :: $1 }
3152+
3153+
| headBindingPattern COMMA ends_coming_soon_or_recover
3154+
{ let commaRange = rhs parseState 2
3155+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingPatternInTuple ())
3156+
let pat2 = SynPat.Wild(commaRange.EndRange)
3157+
[pat2; $1] }
3158+
3159+
| COMMA headBindingPattern
3160+
{ let commaRange = rhs parseState 1
3161+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingPatternInTuple ())
3162+
let pat1 = SynPat.Wild(commaRange.StartRange)
3163+
[$2; pat1] }
3164+
3165+
| COMMA ends_coming_soon_or_recover
3166+
{ let commaRange = rhs parseState 1
3167+
if not $2 then reportParseErrorAt commaRange (FSComp.SR.parsExpectedPatternAfterToken ())
3168+
let pat1 = SynPat.Wild(commaRange.StartRange)
3169+
let pat2 = SynPat.Wild(commaRange.EndRange)
3170+
[pat2; pat1] }
31443171

31453172
conjPatternElements:
31463173
| conjPatternElements AMP headBindingPattern
@@ -3351,8 +3378,10 @@ parenPattern:
33513378
{ let mBar = rhs parseState 2
33523379
SynPat.Or($1, $3, rhs2 parseState 1 3, { BarRange = mBar }) }
33533380

3354-
| tupleParenPatternElements
3355-
{ SynPat.Tuple(false, List.rev $1, lhs parseState) }
3381+
| tupleParenPatternElements
3382+
{ let pats = normalizeTuplePat $1
3383+
let m = (rhs parseState 1, pats) ||> unionRangeWithListBy (fun p -> p.Range)
3384+
SynPat.Tuple(false, List.rev pats, m) }
33563385

33573386
| conjParenPatternElements
33583387
{ SynPat.Ands(List.rev $1, rhs2 parseState 1 3) }
@@ -3371,11 +3400,36 @@ parenPattern:
33713400
| constrPattern { $1 }
33723401

33733402
tupleParenPatternElements:
3374-
| tupleParenPatternElements COMMA parenPattern
3403+
| tupleParenPatternElements COMMA parenPattern
33753404
{ $3 :: $1 }
33763405

3377-
| parenPattern COMMA parenPattern
3378-
{ $3 :: $1 :: [] }
3406+
| parenPattern COMMA parenPattern
3407+
{ [$3; $1] }
3408+
3409+
| tupleParenPatternElements COMMA ends_coming_soon_or_recover
3410+
{ let commaRange = rhs parseState 2
3411+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingPatternInTuple())
3412+
let pat2 = SynPat.Wild(commaRange.EndRange)
3413+
pat2 :: $1 }
3414+
3415+
| parenPattern COMMA ends_coming_soon_or_recover
3416+
{ let commaRange = rhs parseState 2
3417+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingPatternInTuple())
3418+
let pat2 = SynPat.Wild(commaRange.EndRange)
3419+
[pat2; $1] }
3420+
3421+
| COMMA parenPattern
3422+
{ let commaRange = rhs parseState 1
3423+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingPatternInTuple())
3424+
let pat1 = SynPat.Wild(commaRange.StartRange)
3425+
[$2; pat1] }
3426+
3427+
| COMMA ends_coming_soon_or_recover
3428+
{ let commaRange = rhs parseState 1
3429+
if not $2 then reportParseErrorAt commaRange (FSComp.SR.parsExpectedPatternAfterToken ())
3430+
let pat1 = SynPat.Wild(commaRange.StartRange)
3431+
let pat2 = SynPat.Wild(commaRange.EndRange)
3432+
[pat2; pat1] }
33793433

33803434
conjParenPatternElements:
33813435
| conjParenPatternElements AMP parenPattern

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@
592592
<target state="translated">Neočekávaný token v definici typu. Za typem {0} se očekává =.</target>
593593
<note />
594594
</trans-unit>
595+
<trans-unit id="parsExpectedPatternAfterToken">
596+
<source>Expected a pattern after this point</source>
597+
<target state="new">Expected a pattern after this point</target>
598+
<note />
599+
</trans-unit>
600+
<trans-unit id="parsExpectingPatternInTuple">
601+
<source>Expecting pattern</source>
602+
<target state="new">Expecting pattern</target>
603+
<note />
604+
</trans-unit>
595605
<trans-unit id="parsIncompleteTyparExpr1">
596606
<source>Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</source>
597607
<target state="new">Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</target>

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@
592592
<target state="translated">Unerwartetes Token in Typdefinition. Nach Typ "{0}" wurde "=" erwartet.</target>
593593
<note />
594594
</trans-unit>
595+
<trans-unit id="parsExpectedPatternAfterToken">
596+
<source>Expected a pattern after this point</source>
597+
<target state="new">Expected a pattern after this point</target>
598+
<note />
599+
</trans-unit>
600+
<trans-unit id="parsExpectingPatternInTuple">
601+
<source>Expecting pattern</source>
602+
<target state="new">Expecting pattern</target>
603+
<note />
604+
</trans-unit>
595605
<trans-unit id="parsIncompleteTyparExpr1">
596606
<source>Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</source>
597607
<target state="new">Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</target>

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@
592592
<target state="translated">Token inesperado en la definición de tipo. Se esperaba "=" después del tipo "{0}".</target>
593593
<note />
594594
</trans-unit>
595+
<trans-unit id="parsExpectedPatternAfterToken">
596+
<source>Expected a pattern after this point</source>
597+
<target state="new">Expected a pattern after this point</target>
598+
<note />
599+
</trans-unit>
600+
<trans-unit id="parsExpectingPatternInTuple">
601+
<source>Expecting pattern</source>
602+
<target state="new">Expecting pattern</target>
603+
<note />
604+
</trans-unit>
595605
<trans-unit id="parsIncompleteTyparExpr1">
596606
<source>Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</source>
597607
<target state="new">Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</target>

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@
592592
<target state="translated">Jeton inattendu dans la définition de type. Signe '=' attendu après le type '{0}'.</target>
593593
<note />
594594
</trans-unit>
595+
<trans-unit id="parsExpectedPatternAfterToken">
596+
<source>Expected a pattern after this point</source>
597+
<target state="new">Expected a pattern after this point</target>
598+
<note />
599+
</trans-unit>
600+
<trans-unit id="parsExpectingPatternInTuple">
601+
<source>Expecting pattern</source>
602+
<target state="new">Expecting pattern</target>
603+
<note />
604+
</trans-unit>
595605
<trans-unit id="parsIncompleteTyparExpr1">
596606
<source>Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</source>
597607
<target state="new">Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</target>

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@
592592
<target state="translated">Token imprevisto nella definizione del tipo. Dopo il tipo '{0}' è previsto '='.</target>
593593
<note />
594594
</trans-unit>
595+
<trans-unit id="parsExpectedPatternAfterToken">
596+
<source>Expected a pattern after this point</source>
597+
<target state="new">Expected a pattern after this point</target>
598+
<note />
599+
</trans-unit>
600+
<trans-unit id="parsExpectingPatternInTuple">
601+
<source>Expecting pattern</source>
602+
<target state="new">Expecting pattern</target>
603+
<note />
604+
</trans-unit>
595605
<trans-unit id="parsIncompleteTyparExpr1">
596606
<source>Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</source>
597607
<target state="new">Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</target>

src/Compiler/xlf/FSComp.txt.ja.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@
592592
<target state="translated">型定義に予期しないトークンがあります。型 '{0}' の後には '=' が必要です。</target>
593593
<note />
594594
</trans-unit>
595+
<trans-unit id="parsExpectedPatternAfterToken">
596+
<source>Expected a pattern after this point</source>
597+
<target state="new">Expected a pattern after this point</target>
598+
<note />
599+
</trans-unit>
600+
<trans-unit id="parsExpectingPatternInTuple">
601+
<source>Expecting pattern</source>
602+
<target state="new">Expecting pattern</target>
603+
<note />
604+
</trans-unit>
595605
<trans-unit id="parsIncompleteTyparExpr1">
596606
<source>Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</source>
597607
<target state="new">Incomplete character literal (example: 'Q') or qualified type invocation (example: 'T.Name)</target>

0 commit comments

Comments
 (0)