diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 0c0da7afd447245dd1af46541368c9b3b93026c6..2373642d487453fe16ddba5a3f51bf95a15a79bf 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -7027,15 +7027,6 @@ internal class CSharpResources { } } - /// - /// Looks up a localized string similar to Deconstruction is not supported for an 'out' argument.. - /// - internal static string ERR_OutVarDeconstructionIsNotSupported { - get { - return ResourceManager.GetString("ERR_OutVarDeconstructionIsNotSupported", resourceCulture); - } - } - /// /// Looks up a localized string similar to '{0}' cannot define overloaded methods that differ only on ref and out. /// @@ -9331,6 +9322,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to The syntax 'var (...)' as an lvalue is reserved.. + /// + internal static string ERR_VarInvocationLvalueReserved { + get { + return ResourceManager.GetString("ERR_VarInvocationLvalueReserved", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}': virtual or abstract members cannot be private. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 562014a4122632a1119c376021e6c9ff8d0d32a7..8044e03270a25c6ff0aba136d2d7d1634c6d454d 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4947,8 +4947,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Invalid instrumentation kind: {0} - - Deconstruction is not supported for an 'out' argument. + + The syntax 'var (...)' as an lvalue is reserved. Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8577a12e69e16bce2b659df63e0f995c8206ee96..bfcd3ea40bedd6a924d7fab150be4db16e80546f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1432,7 +1432,7 @@ internal enum ErrorCode ERR_ImplicitlyTypedOutVariableUsedInTheSameArgumentList = 8196, ERR_TypeInferenceFailedForImplicitlyTypedOutVariable = 8197, ERR_ExpressionTreeContainsOutVariable = 8198, - ERR_OutVarDeconstructionIsNotSupported = 8199, + ERR_VarInvocationLvalueReserved = 8199, ERR_ExpressionVariableInConstructorOrFieldInitializer = 8200, #endregion diagnostics for out var } diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 7e89193e78cea07112ba349d683eb85474186bf5..f0f8de618549455d86c66f49dc44b01b8f2c04f7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -3390,6 +3390,7 @@ private ExpressionSyntax ParsePossibleRefExpression() var expression = this.ParseExpressionCore(); if (refKeyword != default(SyntaxToken)) { + expression = CheckValidLvalue(expression); expression = _syntaxFactory.RefExpression(refKeyword, expression); } @@ -4842,6 +4843,7 @@ private static bool CanReuseVariableDeclarator(CSharp.Syntax.VariableDeclaratorS var init = this.ParseVariableInitializer(isLocal && !isConst); if (refKeyword != null) { + init = CheckValidLvalue(init); init = _syntaxFactory.RefExpression(refKeyword, init); } @@ -9248,6 +9250,11 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) newPrecedence = GetPrecedence(opKind); var opToken = this.EatToken(); var operand = this.ParseSubExpression(newPrecedence); + if (SyntaxFacts.IsIncrementOrDecrementOperator(opToken.Kind)) + { + operand = CheckValidLvalue(operand); + } + leftOperand = _syntaxFactory.PrefixUnaryExpression(opKind, opToken, operand); } else if (IsAwaitExpression()) @@ -9366,6 +9373,7 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) opToken = AddTrailingSkippedSyntax(opToken, refToken); } + leftOperand = CheckValidLvalue(leftOperand); leftOperand = _syntaxFactory.AssignmentExpression(opKind, leftOperand, opToken, this.ParseSubExpression(newPrecedence)); } else @@ -9564,6 +9572,7 @@ private ExpressionSyntax ParsePostFixExpression(ExpressionSyntax expr) case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: + expr = CheckValidLvalue(expr); expr = _syntaxFactory.PostfixUnaryExpression(SyntaxFacts.GetPostfixUnaryExpression(tk), expr, this.EatToken()); break; @@ -9843,14 +9852,9 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) { expression = this.ParseSubExpression(Precedence.Expression); - // See if the expression is an invocation that could also be successfully parsed and interpreted - // as a deconstruction target. I.e. something like "var (x, y)" or "var (x, (y, z))". - // We should report an error in this case because we plan to support deconstruction for out arguments - // in the future. We need to ensure that we do not successfully interpret this as an invocation of a - // ref-returning method named var with two, or more arguments. - if (IsDeconstructionCompatibleArgument(refOrOutKeyword, expression)) + if (refOrOutKeyword != null) { - expression = this.AddError(expression, ErrorCode.ERR_OutVarDeconstructionIsNotSupported); + expression = CheckValidLvalue(expression); } } } @@ -9858,60 +9862,35 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) return _syntaxFactory.Argument(nameColon, refOrOutKeyword, expression); } - private static bool IsDeconstructionCompatibleArgument(SyntaxToken refOrOutKeyword, ExpressionSyntax expression) + private ExpressionSyntax CheckValidLvalue(ExpressionSyntax expression) { - if (refOrOutKeyword?.Kind == SyntaxKind.OutKeyword && - expression.Kind == SyntaxKind.InvocationExpression) + return IsDeconstructionCompatibleArgument(expression) + ? this.AddError(expression, ErrorCode.ERR_VarInvocationLvalueReserved) + : expression; + } + + /// + /// See if the expression is an invocation of a method named 'var', + /// I.e. something like "var(x, y)" or "var(x, (y, z))" or "var(1)". + /// We report an error when such an invocation is used in a certain syntactic contexts that + /// will require an lvalue because we may elect to support deconstruction + /// in the future. We need to ensure that we do not successfully interpret this as an invocation of a + /// ref-returning method named var. + /// + private static bool IsDeconstructionCompatibleArgument(ExpressionSyntax expression) + { + if (expression.Kind == SyntaxKind.InvocationExpression) { var invocation = (InvocationExpressionSyntax)expression; ExpressionSyntax invocationTarget = invocation.Expression; return invocationTarget.Kind == SyntaxKind.IdentifierName && - ((IdentifierNameSyntax)invocationTarget).Identifier.IsVar() && - invocation.ArgumentList.Arguments.Count > 1 && - !expression.GetDiagnostics().Contains(info => info.Severity == DiagnosticSeverity.Error) && - IsDeconstructionCompatibleArgumentList(invocation.ArgumentList.Arguments); + ((IdentifierNameSyntax)invocationTarget).Identifier.IsVar(); } return false; } - private static bool IsDeconstructionCompatibleArgumentList(SeparatedSyntaxList arguments) - { - int count = arguments.Count; - - for (int i = 0; i < count; i++) - { - ArgumentSyntax argument = arguments[i]; - - if (argument.NameColon != null || argument.RefOrOutKeyword != null) - { - return false; - } - - switch (argument.Expression.Kind) - { - case SyntaxKind.IdentifierName: - // Identifier is compatible - break; - - case SyntaxKind.TupleExpression: - // Tuple is compatible if its argument list is compatible - if (!IsDeconstructionCompatibleArgumentList(((TupleExpressionSyntax)argument.Expression).Arguments)) - { - return false; - } - break; - - default: - // Nothing else can be compatible. - return false; - } - } - - return true; - } - private bool IsPossibleOutVarDeclaration() { var tk = this.CurrentToken.Kind; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 6828630b3ab2119f414ce97c3a71d9500285c23a..c82373229b7aeee4402dff614e0f48131eb9d3f3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -414,6 +414,18 @@ public static SyntaxKind GetPostfixUnaryExpression(SyntaxKind token) } } + internal static bool IsIncrementOrDecrementOperator(SyntaxKind token) + { + switch (token) + { + case SyntaxKind.PlusPlusToken: + case SyntaxKind.MinusMinusToken: + return true; + default: + return false; + } + } + public static bool IsUnaryOperatorDeclarationToken(SyntaxKind token) { return IsPrefixUnaryExpressionOperatorToken(token) || diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs index 28a567adf1ec78dff634f8a9f9563f75053f3e0c..df486d57af5f90eb2415408474b62897a4a3484b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs @@ -1982,15 +1982,24 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( + // (6,9): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var (int x1, x2) = (1, 2); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (int x1, x2)").WithLocation(6, 9), // (6,14): error CS1525: Invalid expression term 'int' // var (int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 14), // (6,18): error CS1003: Syntax error, ',' expected // var (int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_SyntaxError, "x1").WithArguments(",", "").WithLocation(6, 18), + // (7,9): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var (var x3, x4) = (1, 2); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (var x3, x4)").WithLocation(7, 9), // (7,18): error CS1003: Syntax error, ',' expected // var (var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_SyntaxError, "x3").WithArguments(",", "").WithLocation(7, 18), + // (8,9): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var (x5, var (x6, x7)) = (1, (2, 3)); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x5, var (x6, x7))").WithLocation(8, 9), // (6,18): error CS0103: The name 'x1' does not exist in the current context // var (int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 18), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs index fa5a18a04f2810c1f758e50a1a5e1e232fd91718..4304ef36023a02413f5ee35c332bb08e06e205df 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs @@ -78,9 +78,9 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (6,19): error CS8199: Deconstruction is not supported for an 'out' argument. + // (6,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. // Test1(out var (x1, x2)); - Diagnostic(ErrorCode.ERR_OutVarDeconstructionIsNotSupported, "var (x1, x2)").WithLocation(6, 19), + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, x2)").WithLocation(6, 19), // (6,24): error CS0103: The name 'x1' does not exist in the current context // Test1(out var (x1, x2)); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 24), @@ -277,9 +277,9 @@ static object Test1(out int x) options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (11,19): error CS8199: Deconstruction is not supported for an 'out' argument. + // (11,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. // Test1(out var (x1, (x2, x3))); - Diagnostic(ErrorCode.ERR_OutVarDeconstructionIsNotSupported, "var (x1, (x2, x3))").WithLocation(11, 19), + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, (x2, x3))").WithLocation(11, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) @@ -318,12 +318,13 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (8,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(out var (x1)); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1)").WithLocation(8, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } @@ -358,12 +359,13 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (9,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(out var (x1, x2: x2)); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, x2: x2)").WithLocation(9, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } @@ -398,12 +400,13 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (9,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(out var (ref x1, x2)); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (ref x1, x2)").WithLocation(9, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } @@ -438,12 +441,13 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (9,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(out var (x1, (x2))); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, (x2))").WithLocation(9, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } @@ -478,12 +482,13 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (9,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(out var ((x1), x2)); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var ((x1), x2)").WithLocation(9, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } @@ -511,9 +516,9 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6)); compilation.VerifyDiagnostics( - // (6,19): error CS8199: Deconstruction is not supported for an 'out' argument. + // (6,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. // Test1(out var (x1, x2)); - Diagnostic(ErrorCode.ERR_OutVarDeconstructionIsNotSupported, "var (x1, x2)").WithLocation(6, 19), + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, x2)").WithLocation(6, 19), // (6,24): error CS0103: The name 'x1' does not exist in the current context // Test1(out var (x1, x2)); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 24), @@ -605,12 +610,13 @@ static object Test1(ref int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (9,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(ref var (x1, x2)); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, x2)").WithLocation(9, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } @@ -731,12 +737,13 @@ static object Test1(out int x) options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( + // (11,19): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // Test1(out var (x1, (a: x2, b: x3))); + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x1, (a: x2, b: x3))").WithLocation(11, 19), // (4,24): warning CS0649: Field 'Cls.F1' is never assigned to, and will always have its default value 0 // private static int F1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F1").WithArguments("Cls.F1", "0").WithLocation(4, 24) ); - - CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs index 6b6484068354984d911a5966b6aebcac47d4e3dc..46925da1685b0534db34ef9f6eaf69762f615387 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs @@ -1805,5 +1805,61 @@ struct ValueTuple CreateCompilationWithMscorlib(source).VerifyDiagnostics( ); } + + public void NoDeconstructionAsLvalue() + { + var source = +@" +class C +{ + void M() + { + var (x, y) = e; // ok, deconstruction declaration + var(x, y); // ok, invocation + int x = var(x, y); // ok, invocation + var(x, y) += e; // error 1 + var(x, y)++; // error 2 + ++var(x, y); // error 3 + M(out var(x, y)); // error 4 + M(ref var(x, y)); // error 5 + return ref var(x, y); // error 6 + ref int x = ref var(x, y); // error 7 + var (x, 1) = e; // error 8 + } +}"; + ParseAndValidate(source, + // (9,9): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var(x, y) += e; // error 1 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(9, 9), + // (10,9): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var(x, y)++; // error 2 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(10, 9), + // (11,11): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // ++var(x, y); // error 3 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(11, 11), + // (12,15): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // M(out var(x, y)); // error 4 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(12, 15), + // (13,15): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // M(ref var(x, y)); // error 5 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(13, 15), + // (14,20): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // return ref var(x, y); // error 6 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(14, 20), + // (15,25): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // ref int x = ref var(x, y); // error 7 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x, y)").WithLocation(15, 25), + // (16,9): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var (x, 1) = e; // error 8 + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var (x, 1)").WithLocation(16, 9) + ); + } + + public static void ParseAndValidate(string text, params DiagnosticDescription[] expectedErrors) + { + var parsedTree = ParseWithRoundTripCheck(text); + var actualErrors = parsedTree.GetDiagnostics(); + actualErrors.Verify(expectedErrors); + } } }