diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 3cad8590d7..0b3d9b1f09 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1644,4 +1644,5 @@ reprStateMachineInvalidForm,"The state machine has an unexpected form" 3520,invalidXmlDocPosition,"XML comment is not placed on a valid language element." 3521,tcInvalidMemberDeclNameMissingOrHasParen,"Invalid member declaration. The name of the member is missing or has parentheses." 3522,tcAnonRecdDuplicateFieldId,"The field '%s' appears multiple times in this record expression." -3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type." \ No newline at end of file +3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type." +3524,parsExpectingExpressionInTuple,"Expecting expression" \ No newline at end of file diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index 3af3910f0c..af767c5ac6 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -985,3 +985,12 @@ let mkDynamicArgExpr expr = SynExpr.Const(con, con.Range ident.idRange) | SynExpr.Paren (expr = e) -> e | e -> e + +let rec normalizeTupleExpr exprs commas : SynExpr list * range list = + match exprs with + | SynExpr.Tuple (false, innerExprs, innerCommas, _) :: rest -> + let innerExprs, innerCommas = + normalizeTupleExpr (List.rev innerExprs) (List.rev innerCommas) + + innerExprs @ rest, innerCommas @ commas + | _ -> exprs, commas diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi index dfff0392bc..3003554655 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi @@ -340,3 +340,5 @@ val (|SynPipeRight3|_|): SynExpr -> (SynExpr * SynExpr * SynExpr * SynExpr) opti val prependIdentInLongIdentWithTrivia: ident: SynIdent -> dotm: range -> lid: SynLongIdent -> SynLongIdent val mkDynamicArgExpr: expr: SynExpr -> SynExpr + +val normalizeTupleExpr: exprs: SynExpr list -> commas: range list -> SynExpr list * range List diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 0269b1b64f..8db997e9a0 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -4028,6 +4028,13 @@ declExpr: | tupleExpr %prec expr_tuple { let exprs, commas = $1 + let exprs, commas = + // Nested non-struct tuple is only possible during error recovery, + // in other situations there are intermediate nodes. + match exprs with + | SynExpr.Tuple(false, _, _, _) :: _ -> normalizeTupleExpr exprs commas + | _ -> exprs, commas + SynExpr.Tuple (false, List.rev exprs, List.rev commas, (commas.Head, exprs) ||> unionRangeWithListBy (fun e -> e.Range) ) } | declExpr JOIN_IN declExpr @@ -4384,6 +4391,17 @@ tupleExpr: | declExpr COMMA declExpr { [$3 ; $1], [rhs parseState 2] } + | COMMA declExpr + { let commaRange = rhs parseState 1 + reportParseErrorAt commaRange (FSComp.SR.parsExpectingExpressionInTuple()) + [$2; arbExpr ("tupleExpr3", commaRange.StartRange)], [commaRange] } + + | COMMA ends_coming_soon_or_recover + { if not $2 then reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsExpectedExpressionAfterToken()) + let commaRange = rhs parseState 1 + let zeroWidthAtNextToken = (rhs parseState 2).StartRange + [(arbExpr("tupleExpr4", zeroWidthAtNextToken)); arbExpr ("tupleExpr5", commaRange.StartRange)], [commaRange] } + minusExpr: | MINUS minusExpr %prec expr_prefix_plus_minus { mkSynPrefix (rhs parseState 1) (unionRanges (rhs parseState 1) $2.Range) "~-" $2 } diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 95f1966a33..5daa15e157 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -517,6 +517,11 @@ Neočekávaný token v definici typu. Za typem {0} se očekává =. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Tento přístup člena je nejednoznačný. Při vytváření objektu použijte závorky, např. (new SomeType(args)).MemberName' diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index e164dba0f0..bf26b598af 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -517,6 +517,11 @@ Unerwartetes Token in Typdefinition. Nach Typ "{0}" wurde "=" erwartet. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Dieser Memberzugriff ist mehrdeutig. Setzen Sie Klammern um die Objekterstellung, z. B. "(new SomeType(args)). MemberName“ diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index ea1ecde3db..f11ff17b39 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -517,6 +517,11 @@ Token inesperado en la definición de tipo. Se esperaba "=" después del tipo "{0}". + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Este acceso de miembro es ambiguo. Use paréntesis alrededor de la creación del objeto, por ejemplo, '(nuevo AlgúnTipo(args)).NombreMiembro' diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 648ef9169d..73e9906b74 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -517,6 +517,11 @@ Jeton inattendu dans la définition de type. Signe '=' attendu après le type '{0}'. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' 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 diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 1a76a536e1..40599faa5e 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -517,6 +517,11 @@ Token imprevisto nella definizione del tipo. Dopo il tipo '{0}' è previsto '='. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' L'accesso ai membri è ambiguo. Utilizzare le parentesi intorno alla creazione oggetto, ad esempio “(New SomeType (args)). MemberName” diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 6da3c202ac..5da834773d 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -517,6 +517,11 @@ 型定義に予期しないトークンがあります。型 '{0}' の後には '=' が必要です。 + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' このメンバーへのアクセスはあいまいです。オブジェクト作成の前後にはかっこを使用してください。例: '(new SomeType(args)).MemberName' diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 72daec9999..85ff4e40d4 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -517,6 +517,11 @@ 형식 정의에 예기치 않은 토큰이 있습니다. '{0}' 형식 뒤에 '='가 필요합니다. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' 이 구성원 액세스가 모호합니다. 개체 생성 주위에 괄호를 사용하세요. 예: '(새로운 SomeType(인수)).MemberName' diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 386aacfbc9..5185ed19e8 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -517,6 +517,11 @@ Nieoczekiwany token w definicji typu. Oczekiwano znaku „=” po typie „{0}”. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Dostęp tego elementu członkowskiego jest niejednoznaczny. W celu utworzenia obiektu użyj nawiasów, na przykład „(nowy SomeType(args)).MemberName” diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 3b581e58c9..1fff3a0b12 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -517,6 +517,11 @@ Token inesperado na definição de tipo. Esperava-se '=' após o tipo '{0}'. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Este acesso de membro é ambíguo. Use parênteses em torno da criação do objeto, por exemplo, '(new SomeType(args)).MemberName''. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 3288b85bc5..029d14dc67 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -517,6 +517,11 @@ Неожиданный токен в определении типа. После типа "{0}" ожидается "=". + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Неоднозначный доступ к этому элементу. Заключите операцию создания объекта в круглые скобки, например (new Объект(аргументы)).Элемент diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index cc11f361ba..2e4179f51f 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -517,6 +517,11 @@ Tür tanımında beklenmeyen belirteç var. '{0}' türünden sonra '=' bekleniyordu. + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' Bu üye erişimi belirsiz. Lütfen nesne oluşturma etrafında parantez kullanın, örneğin '(yeni SomeType (args)).MemberName’ diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 13a2e6d730..fa70d217b2 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -517,6 +517,11 @@ 类型定义中出现意外标记。类型“{0}”后应为 "="。 + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' 此成员访问权限不明确。请在对象创建周围使用括号,例如 “(new SomeType(args)).MemberName” diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 314dff132d..b8aea86c7c 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -517,6 +517,11 @@ 型別定義中出現非預期的權杖。類型 '{0}' 之後應該要有 '='。 + + Expecting expression + Expecting expression + + This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName' 此成員存取不明確。請在物件建立前後加上括弧,例如「(new SomeType(args)).MemberName」 diff --git a/tests/service/Common.fs b/tests/service/Common.fs index efc7e11508..b683dd9dce 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -226,6 +226,11 @@ let getSingleExprInModule (input: ParsedInput) = | SynModuleDecl.Expr (expr, _) -> expr | _ -> failwith "Unexpected expression" +let getSingleParenInnerExpr expr = + match expr with + | SynModuleDecl.Expr(SynExpr.Paren(expr, _, _, _), _) -> expr + | _ -> failwith "Unexpected tree" + let parseSourceCodeAndGetModule (source: string) = parseSourceCode ("test.fsx", source) |> getSingleModuleLikeDecl @@ -449,6 +454,8 @@ let coreLibAssemblyName = "mscorlib" #endif +let getRange (e: SynExpr) = e.Range + let assertRange (expectedStartLine: int, expectedStartColumn: int) (expectedEndLine: int, expectedEndColumn: int) diff --git a/tests/service/ParserTests.fs b/tests/service/ParserTests.fs index bbfb623aef..f493fc7681 100644 --- a/tests/service/ParserTests.fs +++ b/tests/service/ParserTests.fs @@ -3,6 +3,7 @@ open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.Syntax open FSharp.Compiler.Text +open FsUnit open NUnit.Framework [] @@ -187,3 +188,137 @@ let f (x, | SynPat.FromParseError (SynPat.Paren (SynPat.FromParseError (SynPat.Wild _, _), _), _) -> () | _ -> failwith "Unexpected tree" | _ -> failwith "Unexpected tree" + +let assertIsBefore (f: _ -> range) (a, b) = + let r1 = f a + let r2 = f b + Position.posGeq r2.Start r1.End |> shouldEqual true + +let checkExprOrder exprs = + exprs + |> List.pairwise + |> List.iter (assertIsBefore getRange) + +let checkRangeCountAndOrder commas = + commas + |> List.iter (fun (commas, length) -> + List.length commas |> shouldEqual length + + commas + |> List.pairwise + |> List.iter (assertIsBefore id)) + +[] +let ``Expr - Tuple 01`` () = + let parseResults = getParseResults """ +(,) +(,,) +(,,,) +""" + let exprs = getSingleModuleMemberDecls parseResults |> List.map getSingleParenInnerExpr + match exprs with + | [ SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e11; SynExpr.ArbitraryAfterError _ as e12], c1, _) + SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e21; SynExpr.ArbitraryAfterError _ as e22; SynExpr.ArbitraryAfterError _ as e23], c2, _) + SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e31; SynExpr.ArbitraryAfterError _ as e32; SynExpr.ArbitraryAfterError _ as e33; SynExpr.ArbitraryAfterError _ as e34], c3, _) ] -> + [ e11; e12; e21; e22; e23; e31; e32; e33; e34 ] |> checkExprOrder + [ c1, 1; c2, 2; c3, 3 ] |> checkRangeCountAndOrder + + | _ -> failwith "Unexpected tree" + +[] +let ``Expr - Tuple 02`` () = + let parseResults = getParseResults """ +(1,) +(,1) +(1,1) +""" + let exprs = getSingleModuleMemberDecls parseResults |> List.map getSingleParenInnerExpr + match exprs with + | [ SynExpr.Tuple(_, [SynExpr.Const _ as e11; SynExpr.ArbitraryAfterError _ as e12], c1, _) + SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e21; SynExpr.Const _ as e22], c2, _) + SynExpr.Tuple(_, [SynExpr.Const _ as e31; SynExpr.Const _ as e32], c3, _) ] -> + [ e11; e12; e21; e22; e31; e32 ] |> checkExprOrder + [ c1, 1; c2, 1; c3, 1 ] |> checkRangeCountAndOrder + + | _ -> failwith "Unexpected tree" + +[] +let ``Expr - Tuple 03`` () = + let parseResults = getParseResults """ +(1,,) +(,1,) +(,,1) + +(1,1,) +(,1,1) +(1,,1) + +(1,1,1) +""" + let exprs = getSingleModuleMemberDecls parseResults |> List.map getSingleParenInnerExpr + match exprs with + | [ SynExpr.Tuple(_, [SynExpr.Const _ as e11; SynExpr.ArbitraryAfterError _ as e12; SynExpr.ArbitraryAfterError _ as e13], c1, _) + SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e21; SynExpr.Const _ as e22; SynExpr.ArbitraryAfterError _ as e23], c2, _) + SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e31; SynExpr.ArbitraryAfterError _ as e32; SynExpr.Const _ as e33], c3, _) + + SynExpr.Tuple(_, [SynExpr.Const _ as e41; SynExpr.Const _ as e42; SynExpr.ArbitraryAfterError _ as e43], c4, _) + SynExpr.Tuple(_, [SynExpr.ArbitraryAfterError _ as e51; SynExpr.Const _ as e52; SynExpr.Const _ as e53], c5, _) + SynExpr.Tuple(_, [SynExpr.Const _ as e61; SynExpr.ArbitraryAfterError _ as e62; SynExpr.Const _ as e63], c6, _) + + SynExpr.Tuple(_, [SynExpr.Const _ as e71; SynExpr.Const _ as e72; SynExpr.Const _ as e73], c7, _) ] -> + [ e11; e12; e13; e21; e22; e23; e31; e32; e33 + e41; e42; e43; e51; e52; e53; e61; e62; e63 + e71; e72; e73 ] + |> checkExprOrder + + [ c1, 2; c2, 2; c3, 2 + c4, 2; c5, 2; c6, 2 + c7, 2 ] + |> checkRangeCountAndOrder + + | _ -> failwith "Unexpected tree" + + +[] +let ``Expr - Tuple 04`` () = + let parseResults = getParseResults """ +(,1,,2,3,,4,) +""" + let exprs = getSingleModuleMemberDecls parseResults |> List.map getSingleParenInnerExpr + match exprs with + | [ SynExpr.Tuple(_, [ SynExpr.ArbitraryAfterError _ as e1 + SynExpr.Const _ as e2 + SynExpr.ArbitraryAfterError _ as e3 + SynExpr.Const _ as e4 + SynExpr.Const _ as e5 + SynExpr.ArbitraryAfterError _ as e6 + SynExpr.Const _ as e7 + SynExpr.ArbitraryAfterError _ as e8 ], c, _) ] -> + [ e1; e2; e3; e4; e5; e6; e7; e8 ] + |> checkExprOrder + + [ c, 7 ] |> checkRangeCountAndOrder + + | _ -> failwith "Unexpected tree" + +[] +let ``Expr - Tuple 05`` () = + let parseResults = getParseResults """ +(1, +""" + match getSingleModuleMemberDecls parseResults |> List.map getSingleParenInnerExpr with + | [ SynExpr.FromParseError(SynExpr.Tuple(_, [SynExpr.Const _; SynExpr.ArbitraryAfterError _], _, _), _) ] -> () + | _ -> failwith "Unexpected tree" + +[] +let ``Expr - Tuple 06`` () = + let parseResults = getParseResults """ +(1,,,2) +""" + let synExprs = getSingleModuleMemberDecls parseResults |> List.map getSingleParenInnerExpr + match synExprs with + | [ SynExpr.Tuple(_, [ SynExpr.Const _ + SynExpr.ArbitraryAfterError _ + SynExpr.ArbitraryAfterError _ + SynExpr.Const _ ], _, _) ] -> () + | _ -> failwith "Unexpected tree"