Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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."
3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type."
3524,parsExpectingExpressionInTuple,"Expecting expression"
9 changes: 9 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTreeOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 18 additions & 0 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Neočekávaný token v definici typu. Za typem {0} se očekává =.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<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>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Unerwartetes Token in Typdefinition. Nach Typ "{0}" wurde "=" erwartet.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">Dieser Memberzugriff ist mehrdeutig. Setzen Sie Klammern um die Objekterstellung, z. B. "(new SomeType(args)). MemberName“</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Token inesperado en la definición de tipo. Se esperaba "=" después del tipo "{0}".</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<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>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Jeton inattendu dans la définition de type. Signe '=' attendu après le type '{0}'.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<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>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Token imprevisto nella definizione del tipo. Dopo il tipo '{0}' è previsto '='.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">L'accesso ai membri è ambiguo. Utilizzare le parentesi intorno alla creazione oggetto, ad esempio “(New SomeType (args)). MemberName”</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">型定義に予期しないトークンがあります。型 '{0}' の後には '=' が必要です。</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">このメンバーへのアクセスはあいまいです。オブジェクト作成の前後にはかっこを使用してください。例: '(new SomeType(args)).MemberName'</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">형식 정의에 예기치 않은 토큰이 있습니다. '{0}' 형식 뒤에 '='가 필요합니다.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">이 구성원 액세스가 모호합니다. 개체 생성 주위에 괄호를 사용하세요. 예: '(새로운 SomeType(인수)).MemberName'</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Nieoczekiwany token w definicji typu. Oczekiwano znaku „=” po typie „{0}”.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">Dostęp tego elementu członkowskiego jest niejednoznaczny. W celu utworzenia obiektu użyj nawiasów, na przykład „(nowy SomeType(args)).MemberName”</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Token inesperado na definição de tipo. Esperava-se '=' após o tipo '{0}'.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">Este acesso de membro é ambíguo. Use parênteses em torno da criação do objeto, por exemplo, '(new SomeType(args)).MemberName''.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Неожиданный токен в определении типа. После типа "{0}" ожидается "=".</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">Неоднозначный доступ к этому элементу. Заключите операцию создания объекта в круглые скобки, например (new Объект(аргументы)).Элемент</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">Tür tanımında beklenmeyen belirteç var. '{0}' türünden sonra '=' bekleniyordu.</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">Bu üye erişimi belirsiz. Lütfen nesne oluşturma etrafında parantez kullanın, örneğin '(yeni SomeType (args)).MemberName’</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">类型定义中出现意外标记。类型“{0}”后应为 "="。</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">此成员访问权限不明确。请在对象创建周围使用括号,例如 “(new SomeType(args)).MemberName”</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@
<target state="translated">型別定義中出現非預期的權杖。類型 '{0}' 之後應該要有 '='。</target>
<note />
</trans-unit>
<trans-unit id="parsExpectingExpressionInTuple">
<source>Expecting expression</source>
<target state="new">Expecting expression</target>
<note />
</trans-unit>
<trans-unit id="parsNewExprMemberAccess">
<source>This member access is ambiguous. Please use parentheses around the object creation, e.g. '(new SomeType(args)).MemberName'</source>
<target state="translated">此成員存取不明確。請在物件建立前後加上括弧,例如「(new SomeType(args)).MemberName」</target>
Expand Down
7 changes: 7 additions & 0 deletions tests/service/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -449,6 +454,8 @@ let coreLibAssemblyName =
"mscorlib"
#endif

let getRange (e: SynExpr) = e.Range

let assertRange
(expectedStartLine: int, expectedStartColumn: int)
(expectedEndLine: int, expectedEndColumn: int)
Expand Down
135 changes: 135 additions & 0 deletions tests/service/ParserTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
open FSharp.Compiler.Service.Tests.Common
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
open FsUnit
open NUnit.Framework

[<Test>]
Expand Down Expand Up @@ -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))

[<Test>]
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"

[<Test>]
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"

[<Test>]
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"


[<Test>]
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"

[<Test>]
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"

[<Test>]
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"