Skip to content

Commit a7617f9

Browse files
authored
Parser: recover on missing items in tuple expression (#13352)
* Parser: recover on missing first item in tuple expression * Fix longer tuples, add test * Add another test * Fix perfect formatting * add an extra line break in a line that has less than 120 chars * add an extra space after an capitalized pattern * Add another test * Another test * Recover on missing last item
1 parent 5a5a5f6 commit a7617f9

19 files changed

+238
-1
lines changed

src/Compiler/FSComp.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1644,4 +1644,5 @@ reprStateMachineInvalidForm,"The state machine has an unexpected form"
16441644
3520,invalidXmlDocPosition,"XML comment is not placed on a valid language element."
16451645
3521,tcInvalidMemberDeclNameMissingOrHasParen,"Invalid member declaration. The name of the member is missing or has parentheses."
16461646
3522,tcAnonRecdDuplicateFieldId,"The field '%s' appears multiple times in this record expression."
1647-
3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type."
1647+
3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type."
1648+
3524,parsExpectingExpressionInTuple,"Expecting expression"

src/Compiler/SyntaxTree/SyntaxTreeOps.fs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,3 +985,12 @@ let mkDynamicArgExpr expr =
985985
SynExpr.Const(con, con.Range ident.idRange)
986986
| SynExpr.Paren (expr = e) -> e
987987
| e -> e
988+
989+
let rec normalizeTupleExpr exprs commas : SynExpr list * range list =
990+
match exprs with
991+
| SynExpr.Tuple (false, innerExprs, innerCommas, _) :: rest ->
992+
let innerExprs, innerCommas =
993+
normalizeTupleExpr (List.rev innerExprs) (List.rev innerCommas)
994+
995+
innerExprs @ rest, innerCommas @ commas
996+
| _ -> exprs, commas

src/Compiler/SyntaxTree/SyntaxTreeOps.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,5 @@ val (|SynPipeRight3|_|): SynExpr -> (SynExpr * SynExpr * SynExpr * SynExpr) opti
340340
val prependIdentInLongIdentWithTrivia: ident: SynIdent -> dotm: range -> lid: SynLongIdent -> SynLongIdent
341341

342342
val mkDynamicArgExpr: expr: SynExpr -> SynExpr
343+
344+
val normalizeTupleExpr: exprs: SynExpr list -> commas: range list -> SynExpr list * range List

src/Compiler/pars.fsy

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3831,6 +3831,13 @@ declExpr:
38313831

38323832
| tupleExpr %prec expr_tuple
38333833
{ let exprs, commas = $1
3834+
let exprs, commas =
3835+
// Nested non-struct tuple is only possible during error recovery,
3836+
// in other situations there are intermediate nodes.
3837+
match exprs with
3838+
| SynExpr.Tuple(false, _, _, _) :: _ -> normalizeTupleExpr exprs commas
3839+
| _ -> exprs, commas
3840+
38343841
SynExpr.Tuple (false, List.rev exprs, List.rev commas, (commas.Head, exprs) ||> unionRangeWithListBy (fun e -> e.Range) ) }
38353842

38363843
| declExpr JOIN_IN declExpr
@@ -4187,6 +4194,17 @@ tupleExpr:
41874194
| declExpr COMMA declExpr
41884195
{ [$3 ; $1], [rhs parseState 2] }
41894196

4197+
| COMMA declExpr
4198+
{ let commaRange = rhs parseState 1
4199+
reportParseErrorAt commaRange (FSComp.SR.parsExpectingExpressionInTuple())
4200+
[$2; arbExpr ("tupleExpr3", commaRange.StartRange)], [commaRange] }
4201+
4202+
| COMMA ends_coming_soon_or_recover
4203+
{ if not $2 then reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsExpectedExpressionAfterToken())
4204+
let commaRange = rhs parseState 1
4205+
let zeroWidthAtNextToken = (rhs parseState 2).StartRange
4206+
[(arbExpr("tupleExpr4", zeroWidthAtNextToken)); arbExpr ("tupleExpr5", commaRange.StartRange)], [commaRange] }
4207+
41904208
minusExpr:
41914209
| MINUS minusExpr %prec expr_prefix_plus_minus
41924210
{ mkSynPrefix (rhs parseState 1) (unionRanges (rhs parseState 1) $2.Range) "~-" $2 }

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@
517517
<target state="translated">Neočekávaný token v definici typu. Za typem {0} se očekává =.</target>
518518
<note />
519519
</trans-unit>
520+
<trans-unit id="parsExpectingExpressionInTuple">
521+
<source>Expecting expression</source>
522+
<target state="new">Expecting expression</target>
523+
<note />
524+
</trans-unit>
520525
<trans-unit id="parsNewExprMemberAccess">
521526
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
522527
<target state="translated">Tento přístup člena je nejednoznačný. Při vytváření objektu použijte závorky, např. (new SomeType(args)).MemberName'</target>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@
517517
<target state="translated">Unerwartetes Token in Typdefinition. Nach Typ "{0}" wurde "=" erwartet.</target>
518518
<note />
519519
</trans-unit>
520+
<trans-unit id="parsExpectingExpressionInTuple">
521+
<source>Expecting expression</source>
522+
<target state="new">Expecting expression</target>
523+
<note />
524+
</trans-unit>
520525
<trans-unit id="parsNewExprMemberAccess">
521526
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
522527
<target state="translated">Dieser Memberzugriff ist mehrdeutig. Setzen Sie Klammern um die Objekterstellung, z. B. "(new SomeType(args)). MemberName“</target>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@
517517
<target state="translated">Token inesperado en la definición de tipo. Se esperaba "=" después del tipo "{0}".</target>
518518
<note />
519519
</trans-unit>
520+
<trans-unit id="parsExpectingExpressionInTuple">
521+
<source>Expecting expression</source>
522+
<target state="new">Expecting expression</target>
523+
<note />
524+
</trans-unit>
520525
<trans-unit id="parsNewExprMemberAccess">
521526
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
522527
<target state="translated">Este acceso de miembro es ambiguo. Use paréntesis alrededor de la creación del objeto, por ejemplo, '(nuevo AlgúnTipo(args)).NombreMiembro'</target>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@
517517
<target state="translated">Jeton inattendu dans la définition de type. Signe '=' attendu après le type '{0}'.</target>
518518
<note />
519519
</trans-unit>
520+
<trans-unit id="parsExpectingExpressionInTuple">
521+
<source>Expecting expression</source>
522+
<target state="new">Expecting expression</target>
523+
<note />
524+
</trans-unit>
520525
<trans-unit id="parsNewExprMemberAccess">
521526
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
522527
<target state="translated">L’accès à ce membre est ambigu. Utilisez des parenthèses autour de la création de l’objet, par exemple' (New SomeType (args)). MemberName</target>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@
517517
<target state="translated">Token imprevisto nella definizione del tipo. Dopo il tipo '{0}' è previsto '='.</target>
518518
<note />
519519
</trans-unit>
520+
<trans-unit id="parsExpectingExpressionInTuple">
521+
<source>Expecting expression</source>
522+
<target state="new">Expecting expression</target>
523+
<note />
524+
</trans-unit>
520525
<trans-unit id="parsNewExprMemberAccess">
521526
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
522527
<target state="translated">L'accesso ai membri è ambiguo. Utilizzare le parentesi intorno alla creazione oggetto, ad esempio “(New SomeType (args)). MemberName”</target>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@
517517
<target state="translated">型定義に予期しないトークンがあります。型 '{0}' の後には '=' が必要です。</target>
518518
<note />
519519
</trans-unit>
520+
<trans-unit id="parsExpectingExpressionInTuple">
521+
<source>Expecting expression</source>
522+
<target state="new">Expecting expression</target>
523+
<note />
524+
</trans-unit>
520525
<trans-unit id="parsNewExprMemberAccess">
521526
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
522527
<target state="translated">このメンバーへのアクセスはあいまいです。オブジェクト作成の前後にはかっこを使用してください。例: '(new SomeType(args)).MemberName'</target>

0 commit comments

Comments
 (0)