diff --git a/docs/features/wildcards.work.md b/docs/features/wildcards.work.md index 49f6c5f600704..83bc4cf2e84eb 100644 --- a/docs/features/wildcards.work.md +++ b/docs/features/wildcards.work.md @@ -5,26 +5,28 @@ work items remaining for wildcards - [ ] Gather together a specification document - [ ] Language behavior (e.g. [this](https://github.com/dotnet/roslyn/issues/14862) and [this](https://github.com/dotnet/roslyn/issues/14794) and [this](https://github.com/dotnet/roslyn/issues/14832)) - [ ] [SemanticModel behavior](https://gist.github.com/gafter/ab10e413efe3a066209cbf14cb874988) (see also [here](https://gist.github.com/gafter/37305d619bd04511f4f66b86f6f2d3a5)) - - [ ] Warnings (for non-wildcard expression variables declared but not used) - - [ ] Debugging + - [ ] Warnings (for expression variables declared but not used) + - [x] Debugging (value discarded is not visible in debugger) ### Compiler - [x] Syntax model changes -- [ ] Symbol changes -- [ ] Parsing for the short-form wildcard pattern `_` -- [ ] Implement binding of wildcards - - [ ] In a pattern - - [ ] In a deconstruction declaration - - [ ] In a deconstruction assignment expression +- [x] Symbol changes +- [x] Parsing for the short-form wildcard pattern `_` +- [x] Implement binding of wildcards + - [x] In a pattern + - [x] In a deconstruction declaration + - [x] In a deconstruction assignment expression - [ ] In an out argument (in every argument context) - - [ ] Both the long form `var _` and the short form `_` + - [x] Both the long form `var _` and the short form `_` +- [x] Type inference for wildcards in each context - [ ] Implement semantic model changes -- [ ] Type inference for wildcards in each context -- [ ] Implement lowering of wildcards - - [ ] In a pattern - - [ ] In a deconstruction declaration - - [ ] In a deconstruction assignment expression - - [ ] In an out argument (in every argument context) + - [ ] `GetTypeInfo` of a wildcard expression `_` should be the type of the discarded value + - [ ] `GetSymbolInfo` of a wildcard expression `_` should be an `IDiscardedSymbol` +- [x] Implement lowering of wildcards + - [x] In a pattern + - [x] In a deconstruction declaration + - [x] In a deconstruction assignment expression + - [ ] In an out argument (in each argument context) ### Testing - [ ] Symbol tests diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index 12097a8252a8e..becfe63df7a3e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -16,47 +16,48 @@ namespace Microsoft.CodeAnalysis.CSharp /// internal partial class Binder { - /// - /// Only handles assignment-only or declaration-only deconstructions at this point. - /// Issue https://github.com/dotnet/roslyn/issues/15050 tracks allowing mixed deconstructions - /// private BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, DiagnosticBag diagnostics) { var left = node.Left; var right = node.Right; - - bool isDeclaration = node.IsDeconstructionDeclaration(); - Debug.Assert(isDeclaration || !ContainsDeclarations(left)); - return BindDeconstruction(node, left, right, diagnostics, isDeclaration); - } - - internal BoundDeconstructionAssignmentOperator BindDeconstruction( - CSharpSyntaxNode node, - ExpressionSyntax left, - ExpressionSyntax right, - DiagnosticBag diagnostics, - bool isDeclaration, - BoundDeconstructValuePlaceholder rightPlaceholder = null) - { - DeconstructionVariable locals = BindDeconstructionVariables(left, isDeclaration, diagnostics); - Debug.Assert(locals.HasNestedVariables); - var result = BindDeconstructionAssignment(node, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder); - FreeDeconstructionVariables(locals.NestedVariables); - return result; - } - - private static bool ContainsDeclarations(ExpressionSyntax expression) - { - switch (expression.Kind()) + DeclarationExpressionSyntax declaration = null; + ExpressionSyntax expression = null; + var result = BindDeconstruction(node, left, right, diagnostics, ref declaration, ref expression); + if (declaration != null) { - case SyntaxKind.DeclarationExpression: - return true; - case SyntaxKind.TupleExpression: - var tuple = (TupleExpressionSyntax)expression; - return tuple.Arguments.Any(a => ContainsDeclarations(a.Expression)); - default: - return false; + // only allowed at the top level, or in a for loop + switch (node.Parent?.Kind()) + { + case null: + case SyntaxKind.ExpressionStatement: + if (expression != null) + { + // We only allow assignment-only or declaration-only deconstructions at this point. + // Issue https://github.com/dotnet/roslyn/issues/15050 tracks allowing mixed deconstructions. + // For now we give an error when you mix. + Error(diagnostics, ErrorCode.ERR_MixedDeconstructionUnsupported, left); + } + break; + case SyntaxKind.ForStatement: + if (((ForStatementSyntax)node.Parent).Initializers.Contains(node)) + { + if (expression != null) + { + Error(diagnostics, ErrorCode.ERR_MixedDeconstructionUnsupported, left); + } + } + else + { + Error(diagnostics, ErrorCode.ERR_DeclarationExpressionNotPermitted, declaration); + } + break; + default: + Error(diagnostics, ErrorCode.ERR_DeclarationExpressionNotPermitted, declaration); + break; + } } + + return result; } private static void FreeDeconstructionVariables(ArrayBuilder variables) @@ -122,8 +123,8 @@ private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment( constructionStepsOpt); TypeSymbol returnType = hasErrors ? - CreateErrorType() : - constructionStepsOpt.Last().OutputPlaceholder.Type; + CreateErrorType() : + constructionStepsOpt.Last().OutputPlaceholder.Type; var deconstructions = deconstructionSteps.ToImmutableAndFree(); var conversions = conversionSteps.ToImmutableAndFree(); @@ -257,29 +258,37 @@ private void SetInferredTypes(ArrayBuilder variables, Im var variable = variables[i]; if (!variable.HasNestedVariables) { - switch (variable.Single.Kind) + var pending = variable.Single; + if ((object)pending.Type != null) { - case BoundKind.DeconstructionVariablePendingInference: - { - var pending = (DeconstructionVariablePendingInference)variable.Single; - BoundExpression local = pending.SetInferredType(foundTypes[i], this, diagnostics); - variables[i] = new DeconstructionVariable(local, local.Syntax); - } - break; - case BoundKind.DiscardedExpression: - { - var pending = (BoundDiscardedExpression)variable.Single; - if ((object)pending.Type == null) - { - variables[i] = new DeconstructionVariable(pending.SetInferredType(foundTypes[i]), pending.Syntax); - } - } - break; + continue; } + + variables[i] = new DeconstructionVariable(SetInferredType(pending, foundTypes[i], diagnostics), pending.Syntax); } } } + private BoundExpression SetInferredType(BoundExpression expression, TypeSymbol type, DiagnosticBag diagnostics) + { + switch (expression.Kind) + { + case BoundKind.DeconstructionVariablePendingInference: + { + var pending = (DeconstructionVariablePendingInference)expression; + return pending.SetInferredType(type, this, diagnostics); + } + case BoundKind.DiscardExpression: + { + var pending = (BoundDiscardExpression)expression; + Debug.Assert((object)pending.Type == null); + return pending.SetInferredType(type); + } + default: + throw ExceptionUtilities.UnexpectedValue(expression.Kind); + } + } + /// /// Find any deconstruction locals that are still pending inference and fail their inference. /// @@ -301,8 +310,8 @@ private void FailRemainingInferences(ArrayBuilder variab BoundExpression local = ((DeconstructionVariablePendingInference)variable.Single).FailInference(this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); break; - case BoundKind.DiscardedExpression: - var pending = (BoundDiscardedExpression)variable.Single; + case BoundKind.DiscardExpression: + var pending = (BoundDiscardExpression)variable.Single; if ((object)pending.Type == null) { Error(diagnostics, ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, pending.Syntax, "_"); @@ -634,20 +643,53 @@ private BoundBadExpression MissingDeconstruct(BoundExpression receiver, CSharpSy } /// - /// Returns bound variables in a tree. - /// For variables that are being declared, it makes locals (or fields in global statement). - /// If the type is unknown, a deconstruction variable pending inference is used instead (which will be replaced with a local or field later). - /// For expressions that don't declare variables, simply binds them and verify they are assignable to. - /// + /// Bind a deconstruction assignment. + /// + /// The deconstruction operation + /// The left (tuple) operand + /// The right (deconstrucible) operand + /// Where to report diagnostics + /// A variable set to the first variable declaration found in the left + /// A variable set to the first expression in the left that isn't a declaration or discard + /// + /// + internal BoundDeconstructionAssignmentOperator BindDeconstruction( + CSharpSyntaxNode deconstruction, + ExpressionSyntax left, + ExpressionSyntax right, + DiagnosticBag diagnostics, + ref DeclarationExpressionSyntax declaration, + ref ExpressionSyntax expression, + BoundDeconstructValuePlaceholder rightPlaceholder = null) + { + DeconstructionVariable locals = BindDeconstructionVariables(left, diagnostics, ref declaration, ref expression); + Debug.Assert(locals.HasNestedVariables); + var result = BindDeconstructionAssignment(deconstruction, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder); + FreeDeconstructionVariables(locals.NestedVariables); + return result; + } + + /// + /// Prepares locals (or fields in global statement) and lvalue expressions corresponding to the variables of the declaration. + /// The locals/fields/lvalues are kept in a tree which captures the nesting of variables. + /// Each local or field is either a simple local or field access (when its type is known) or a deconstruction variable pending inference. /// The caller is responsible for releasing the nested ArrayBuilders. /// - private DeconstructionVariable BindDeconstructionVariables(ExpressionSyntax node, bool isDeclaration, DiagnosticBag diagnostics) + private DeconstructionVariable BindDeconstructionVariables( + ExpressionSyntax node, + DiagnosticBag diagnostics, + ref DeclarationExpressionSyntax declaration, + ref ExpressionSyntax expression) { switch (node.Kind()) { case SyntaxKind.DeclarationExpression: { var component = (DeclarationExpressionSyntax)node; + if (declaration == null) + { + declaration = component; + } bool isVar; bool isConst = false; @@ -660,7 +702,7 @@ private DeconstructionVariable BindDeconstructionVariables(ExpressionSyntax node Error(diagnostics, ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, component.Designation); } - return BindDeconstructionVariables(declType, component.Type, component.Designation, diagnostics); + return BindDeconstructionVariables(declType, component.Designation, diagnostics); } case SyntaxKind.TupleExpression: { @@ -668,27 +710,30 @@ private DeconstructionVariable BindDeconstructionVariables(ExpressionSyntax node var builder = ArrayBuilder.GetInstance(component.Arguments.Count); foreach (var arg in component.Arguments) { - builder.Add(BindDeconstructionVariables(arg.Expression, isDeclaration, diagnostics)); + if (arg.NameColon != null) + { + Error(diagnostics, ErrorCode.ERR_TupleElementNamesInDeconstruction, arg.NameColon); + } + + builder.Add(BindDeconstructionVariables(arg.Expression, diagnostics, ref declaration, ref expression)); } return new DeconstructionVariable(builder, node); } default: + var boundVariable = BindExpression(node, diagnostics, invoked: false, indexed: false); + var checkedVariable = CheckValue(boundVariable, BindValueKind.Assignment, diagnostics); + if (expression == null && checkedVariable.Kind != BoundKind.DiscardExpression) { - var boundVariable = BindExpression(node, diagnostics, invoked: false, indexed: false); - if (isDeclaration && boundVariable.Kind != BoundKind.DiscardedExpression ) - { - Error(diagnostics, ErrorCode.ERR_MixedDeconstructionDisallowed, node, node); - } - var checkedVariable = CheckValue(boundVariable, BindValueKind.Assignment, diagnostics); - return new DeconstructionVariable(checkedVariable, node); + expression = node; } + + return new DeconstructionVariable(checkedVariable, node); } } private DeconstructionVariable BindDeconstructionVariables( TypeSymbol declType, - TypeSyntax typeSyntax, VariableDesignationSyntax node, DiagnosticBag diagnostics) { @@ -697,12 +742,12 @@ private DeconstructionVariable BindDeconstructionVariables( case SyntaxKind.SingleVariableDesignation: { var single = (SingleVariableDesignationSyntax)node; - return new DeconstructionVariable(BindDeconstructionVariable(declType, typeSyntax, single, diagnostics), node); + return new DeconstructionVariable(BindDeconstructionVariable(declType, single, diagnostics), node); } - case SyntaxKind.DiscardedDesignation: + case SyntaxKind.DiscardDesignation: { - var discarded = (DiscardedDesignationSyntax)node; - return new DeconstructionVariable(new BoundDiscardedExpression(discarded, declType), node); + var discarded = (DiscardDesignationSyntax)node; + return new DeconstructionVariable(BindDiscardExpression(discarded, declType), node); } case SyntaxKind.ParenthesizedVariableDesignation: { @@ -710,7 +755,7 @@ private DeconstructionVariable BindDeconstructionVariables( var builder = ArrayBuilder.GetInstance(); foreach (var n in tuple.Variables) { - builder.Add(BindDeconstructionVariables(declType, typeSyntax, n, diagnostics)); + builder.Add(BindDeconstructionVariables(declType, n, diagnostics)); } return new DeconstructionVariable(builder, node); } @@ -719,6 +764,13 @@ private DeconstructionVariable BindDeconstructionVariables( } } + private static BoundDiscardExpression BindDiscardExpression( + DiscardDesignationSyntax designation, + TypeSymbol declType) + { + return new BoundDiscardExpression(designation, declType); + } + /// /// In embedded statements, returns a BoundLocal when the type was explicit. /// In global statements, returns a BoundFieldAccess when the type was explicit. @@ -726,7 +778,6 @@ private DeconstructionVariable BindDeconstructionVariables( /// private BoundExpression BindDeconstructionVariable( TypeSymbol declType, - TypeSyntax typeSyntax, SingleVariableDesignationSyntax designation, DiagnosticBag diagnostics) { @@ -742,7 +793,7 @@ private BoundExpression BindDeconstructionVariable( if ((object)declType != null) { - CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax); + CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, designation); return new BoundLocal(designation, localSymbol, constantValueOpt: null, type: declType, hasErrors: hasErrors); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e1889b269e6ae..58d61f9b7818f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -647,6 +647,9 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic CreateErrorType("ref")); } + case SyntaxKind.DeclarationExpression: + return BindDeclarationExpression((DeclarationExpressionSyntax)node, diagnostics); + default: // NOTE: We could probably throw an exception here, but it's conceivable // that a non-parser syntax tree could reach this point with an unexpected @@ -703,6 +706,62 @@ private static bool IsThrowExpressionInProperContext(ThrowExpressionSyntax node) } } + // Bind a declaration expression where it isn't permitted. + private BoundExpression BindDeclarationExpression(DeclarationExpressionSyntax node, DiagnosticBag diagnostics) + { + // This is an error, as declaration expressions are handled specially in every context in which + // they are permitted. So we have a context in which they are *not* permitted. Nevertheless, we + // bind it and then give one nice message. + + bool isVar; + bool isConst = false; + AliasSymbol alias; + TypeSymbol declType = BindVariableType(node.Designation, diagnostics, node.Type, ref isConst, out isVar, out alias); + Error(diagnostics, ErrorCode.ERR_DeclarationExpressionNotPermitted, node); + return BindDeclarationVariables(declType, node.Designation, diagnostics); + } + + /// + /// Bind a declaration variable where it isn't permitted. The caller is expected to produce a diagnostic. + /// + private BoundExpression BindDeclarationVariables(TypeSymbol declType, VariableDesignationSyntax node, DiagnosticBag diagnostics) + { + declType = declType ?? CreateErrorType("var"); + switch (node.Kind()) + { + case SyntaxKind.SingleVariableDesignation: + { + var single = (SingleVariableDesignationSyntax)node; + var result = BindDeconstructionVariable(declType, single, diagnostics); + return result; + } + case SyntaxKind.DiscardDesignation: + { + var discarded = (DiscardDesignationSyntax)node; + return BindDiscardExpression(discarded, declType); + } + case SyntaxKind.ParenthesizedVariableDesignation: + { + var tuple = (ParenthesizedVariableDesignationSyntax)node; + var builder = ArrayBuilder.GetInstance(tuple.Variables.Count); + foreach (var n in tuple.Variables) + { + builder.Add(BindDeclarationVariables(declType, n, diagnostics)); + } + var subExpressions = builder.ToImmutableAndFree(); + var tupleType = TupleTypeSymbol.Create( + null, + subExpressions.SelectAsArray(e => e.Type), + default(ImmutableArray), + default(ImmutableArray), + Compilation); + return new BoundTupleLiteral(node, default(ImmutableArray), subExpressions, tupleType); + } + default: + throw ExceptionUtilities.UnexpectedValue(node.Kind()); + } + } + private BoundExpression BindTupleExpression(TupleExpressionSyntax node, DiagnosticBag diagnostics) { SeparatedSyntaxList arguments = node.Arguments; @@ -1144,7 +1203,7 @@ private BoundExpression BindIdentifier( { if (node.IsKind(SyntaxKind.IdentifierName) && FallBackOnDiscard((IdentifierNameSyntax)node, diagnostics)) { - return new BoundDiscardedExpression(node, type: null); + return new BoundDiscardExpression(node, type: null); } // Otherwise, the simple-name is undefined and a compile-time error occurs. @@ -2127,7 +2186,7 @@ private BoundExpression BindOutVariableArgument(DeclarationExpressionSyntax decl VariableDesignationSyntax designation = declarationExpression.Designation; switch (designation.Kind()) { - case SyntaxKind.DiscardedDesignation: + case SyntaxKind.DiscardDesignation: { bool isVar; bool isConst = false; @@ -2135,7 +2194,7 @@ private BoundExpression BindOutVariableArgument(DeclarationExpressionSyntax decl TypeSymbol declType = BindVariableType(designation, diagnostics, typeSyntax, ref isConst, out isVar, out alias); Debug.Assert(isVar == ((object)declType == null)); - return new BoundDiscardedExpression((DiscardedDesignationSyntax)designation, declType); + return new BoundDiscardExpression((DiscardDesignationSyntax)designation, declType); } case SyntaxKind.SingleVariableDesignation: return BindOutVariableDeclarationArgument(declarationExpression, diagnostics); @@ -2402,11 +2461,11 @@ private void CoerceArguments( TypeSymbol parameterType = GetCorrespondingParameterType(ref result, parameters, arg); arguments[arg] = ((OutDeconstructVarPendingInference)argument).SetInferredType(parameterType, success: true); } - else if (argument.Kind == BoundKind.DiscardedExpression && !argument.HasExpressionType()) + else if (argument.Kind == BoundKind.DiscardExpression && !argument.HasExpressionType()) { TypeSymbol parameterType = GetCorrespondingParameterType(ref result, parameters, arg); Debug.Assert((object)parameterType != null); - arguments[arg] = ((BoundDiscardedExpression)argument).SetInferredType(parameterType); + arguments[arg] = ((BoundDiscardExpression)argument).SetInferredType(parameterType); } } } @@ -6234,9 +6293,9 @@ private BoundExpression ConvertToArrayIndex(BoundExpression index, SyntaxNode no { return ((OutVariablePendingInference)index).FailInference(this, diagnostics); } - else if (index.Kind == BoundKind.DiscardedExpression && !index.HasExpressionType()) + else if (index.Kind == BoundKind.DiscardExpression && !index.HasExpressionType()) { - return ((BoundDiscardedExpression)index).FailInference(this, diagnostics); + return ((BoundDiscardExpression)index).FailInference(this, diagnostics); } var result = diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index a23495d573511..2b9042d77537a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -116,7 +116,7 @@ internal BoundExpression MakeInvocationExpression( var analyzedArguments = AnalyzedArguments.GetInstance(); Debug.Assert(!args.Any(e => e.Kind == BoundKind.OutVariablePendingInference || e.Kind == BoundKind.OutDeconstructVarPendingInference || - e.Kind == BoundKind.DiscardedExpression && !e.HasExpressionType())); + e.Kind == BoundKind.DiscardExpression && !e.HasExpressionType())); analyzedArguments.Arguments.AddRange(args); BoundExpression result = BindInvocationExpression( node, node, methodName, boundExpression, analyzedArguments, diagnostics, queryClause, @@ -339,7 +339,7 @@ private ImmutableArray BuildArgumentsForDynamicInvocation(Analy Debug.Assert(arguments.Arguments[i].Kind != BoundKind.OutDeconstructVarPendingInference); if (arguments.Arguments[i].Kind == BoundKind.OutVariablePendingInference || - arguments.Arguments[i].Kind == BoundKind.DiscardedExpression && !arguments.Arguments[i].HasExpressionType()) + arguments.Arguments[i].Kind == BoundKind.DiscardExpression && !arguments.Arguments[i].HasExpressionType()) { var builder = ArrayBuilder.GetInstance(arguments.Arguments.Count); builder.AddRange(arguments.Arguments); @@ -352,9 +352,9 @@ private ImmutableArray BuildArgumentsForDynamicInvocation(Analy { builder[i] = ((OutVariablePendingInference)argument).FailInference(this, diagnostics); } - else if (argument.Kind == BoundKind.DiscardedExpression && !argument.HasExpressionType()) + else if (argument.Kind == BoundKind.DiscardExpression && !argument.HasExpressionType()) { - builder[i] = ((BoundDiscardedExpression)argument).FailInference(this, diagnostics); + builder[i] = ((BoundDiscardExpression)argument).FailInference(this, diagnostics); } i++; @@ -1192,7 +1192,7 @@ private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedA break; } case BoundKind.OutVariablePendingInference: - case BoundKind.DiscardedExpression: + case BoundKind.DiscardExpression: { if (argument.HasExpressionType()) { @@ -1230,15 +1230,15 @@ private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedA newArguments[i] = ((OutVariablePendingInference)argument).SetInferredType(candidateType, null); } } - else if (argument.Kind == BoundKind.DiscardedExpression) + else if (argument.Kind == BoundKind.DiscardExpression) { if ((object)candidateType == null) { - newArguments[i] = ((BoundDiscardedExpression)argument).FailInference(this, null); + newArguments[i] = ((BoundDiscardExpression)argument).FailInference(this, null); } else { - newArguments[i] = ((BoundDiscardedExpression)argument).SetInferredType(candidateType); + newArguments[i] = ((BoundDiscardExpression)argument).SetInferredType(candidateType); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 530bc250fe984..5087ea379466b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -280,7 +280,7 @@ private BoundPattern BindDeclarationPattern( { case SyntaxKind.SingleVariableDesignation: break; - case SyntaxKind.DiscardedDesignation: + case SyntaxKind.DiscardDesignation: return new BoundDeclarationPattern(node, null, boundDeclType, isVar, hasErrors); default: throw ExceptionUtilities.UnexpectedValue(node.Designation.Kind()); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index c06786850b85a..f51a42f8af135 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -591,7 +591,7 @@ private TypeSymbol BindVariableType(CSharpSyntaxNode declarationNode, Diagnostic declarationNode is VariableDesignationSyntax || declarationNode.Kind() == SyntaxKind.VariableDeclaration || declarationNode.Kind() == SyntaxKind.DeclarationExpression || - declarationNode.Kind() == SyntaxKind.DiscardedDesignation); + declarationNode.Kind() == SyntaxKind.DiscardDesignation); // If the type is "var" then suppress errors when binding it. "var" might be a legal type // or it might not; if it is not then we do not want to report an error. If it is, then @@ -1713,15 +1713,15 @@ private BoundExpression BindAssignment(AssignmentExpressionSyntax node, Diagnost var op1 = BindValue(node.Left, diagnostics, BindValueKind.Assignment); // , BIND_MEMBERSET); var op2 = BindValue(node.Right, diagnostics, BindValueKind.RValue); // , BIND_RVALUEREQUIRED); - if (op1.Kind == BoundKind.DiscardedExpression) + if (op1.Kind == BoundKind.DiscardExpression) { - op1 = InferTypeForDiscard((BoundDiscardedExpression)op1, op2, diagnostics); + op1 = InferTypeForDiscard((BoundDiscardExpression)op1, op2, diagnostics); } return BindAssignment(node, op1, op2, diagnostics); } - private BoundExpression InferTypeForDiscard(BoundDiscardedExpression op1, BoundExpression op2, DiagnosticBag diagnostics) + private BoundExpression InferTypeForDiscard(BoundDiscardExpression op1, BoundExpression op2, DiagnosticBag diagnostics) { if (op2.Type == null) { @@ -1865,7 +1865,7 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind Debug.Assert(valueKind == BindValueKind.RefOrOut); return expr; - case BoundKind.DiscardedExpression: + case BoundKind.DiscardExpression: Debug.Assert(valueKind == BindValueKind.Assignment || valueKind == BindValueKind.RefOrOut); return expr; } diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs index 3ebf5124af1da..3829ea1db8fb4 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp { internal abstract class ExpressionVariableFinder : CSharpSyntaxWalker where TFieldOrLocalSymbol : Symbol { - private ArrayBuilder _localsBuilder; + private ArrayBuilder _variablesBuilder; private SyntaxNode _nodeToBind; protected void FindExpressionVariables( @@ -21,8 +21,8 @@ protected void FindExpressionVariables( { Debug.Assert(node != null); - ArrayBuilder save = _localsBuilder; - _localsBuilder = builder; + ArrayBuilder save = _variablesBuilder; + _variablesBuilder = builder; #if DEBUG // These are all of the kinds of nodes we should need to handle in this class. @@ -55,7 +55,7 @@ protected void FindExpressionVariables( VisitNodeToBind(node); - _localsBuilder = save; + _variablesBuilder = save; } public override void VisitVariableDeclarator(VariableDeclaratorSyntax node) @@ -84,15 +84,15 @@ protected void FindExpressionVariables( SeparatedSyntaxList nodes) { Debug.Assert(nodes.Count > 0); - ArrayBuilder save = _localsBuilder; - _localsBuilder = builder; + ArrayBuilder save = _variablesBuilder; + _variablesBuilder = builder; foreach (var n in nodes) { VisitNodeToBind(n); } - _localsBuilder = save; + _variablesBuilder = save; } public override void VisitEqualsValueClause(EqualsValueClauseSyntax node) @@ -196,7 +196,7 @@ public override void VisitDeclarationPattern(DeclarationPatternSyntax node) var variable = MakePatternVariable(node, _nodeToBind); if ((object)variable != null) { - _localsBuilder.Add(variable); + _variablesBuilder.Add(variable); } base.VisitDeclarationPattern(node); @@ -259,18 +259,18 @@ public override void VisitBinaryExpression(BinaryExpressionSyntax node) public override void VisitDeclarationExpression(DeclarationExpressionSyntax node) { - var argumentSyntax = (ArgumentSyntax)node?.Parent; - var argumentListSyntax = (BaseArgumentListSyntax)argumentSyntax?.Parent; - var variable = MakeOutVariable(node, argumentListSyntax, _nodeToBind); + var argumentSyntax = (ArgumentSyntax)node.Parent; + var argumentListSyntax = argumentSyntax.Parent as BaseArgumentListSyntax; + var variable = MakeDeclarationExpressionVariable(node, argumentListSyntax, _nodeToBind); if ((object)variable != null) { - _localsBuilder.Add(variable); + _variablesBuilder.Add(variable); } } public override void VisitAssignmentExpression(AssignmentExpressionSyntax node) { - if (node.Left.IsKind(SyntaxKind.TupleExpression) || node.Left.IsKind(SyntaxKind.DeclarationExpression)) + if (node.IsDeconstruction()) { CollectVariablesFromDeconstruction(node.Left, node); } @@ -283,14 +283,14 @@ public override void VisitAssignmentExpression(AssignmentExpressionSyntax node) } private void CollectVariablesFromDeconstruction( - ExpressionSyntax declaration, + ExpressionSyntax possibleTupleDeclaration, AssignmentExpressionSyntax deconstruction) { - switch (declaration.Kind()) + switch (possibleTupleDeclaration.Kind()) { case SyntaxKind.TupleExpression: { - var tuple = (TupleExpressionSyntax)declaration; + var tuple = (TupleExpressionSyntax)possibleTupleDeclaration; foreach (var arg in tuple.Arguments) { CollectVariablesFromDeconstruction(arg.Expression, deconstruction); @@ -299,13 +299,15 @@ private void CollectVariablesFromDeconstruction( } case SyntaxKind.DeclarationExpression: { - var declarationExpression = (DeclarationExpressionSyntax)declaration; + var declarationExpression = (DeclarationExpressionSyntax)possibleTupleDeclaration; CollectVariablesFromDeconstruction(declarationExpression.Designation, declarationExpression.Type, deconstruction); break; } default: - Visit(declaration); - break; + { + Visit(possibleTupleDeclaration); + break; + } } } @@ -322,7 +324,7 @@ private void CollectVariablesFromDeconstruction( var variable = MakeDeconstructionVariable(closestTypeSyntax, single, deconstruction); if ((object)variable != null) { - _localsBuilder.Add(variable); + _variablesBuilder.Add(variable); } break; } @@ -335,15 +337,24 @@ private void CollectVariablesFromDeconstruction( } break; } - case SyntaxKind.DiscardedDesignation: + case SyntaxKind.DiscardDesignation: break; default: throw ExceptionUtilities.UnexpectedValue(designation.Kind()); } } - protected abstract TFieldOrLocalSymbol MakeOutVariable(DeclarationExpressionSyntax node, BaseArgumentListSyntax argumentListSyntax, SyntaxNode nodeToBind); + /// + /// Make a variable for a declaration expression other than a deconstruction left-hand-side. The only + /// other legal place for a declaration expression today is an out variable declaration; this method + /// handles that and the error cases as well. + /// + protected abstract TFieldOrLocalSymbol MakeDeclarationExpressionVariable(DeclarationExpressionSyntax node, BaseArgumentListSyntax argumentListSyntax, SyntaxNode nodeToBind); + /// + /// Make a variable for a declaration expression appearing as one of the declared variables of the left-hand-side + /// of a deconstruction assignment. + /// protected abstract TFieldOrLocalSymbol MakeDeconstructionVariable( TypeSyntax closestTypeSyntax, SingleVariableDesignationSyntax designation, @@ -404,7 +415,7 @@ protected override LocalSymbol MakePatternVariable(DeclarationPatternSyntax node var designation = node.Designation as SingleVariableDesignationSyntax; if (designation == null) { - Debug.Assert(node.Designation.Kind() == SyntaxKind.DiscardedDesignation); + Debug.Assert(node.Designation.Kind() == SyntaxKind.DiscardDesignation); return null; } @@ -426,7 +437,7 @@ protected override LocalSymbol MakePatternVariable(DeclarationPatternSyntax node nodeToBind: nodeToBind, forbiddenZone: null); } - protected override LocalSymbol MakeOutVariable(DeclarationExpressionSyntax node, BaseArgumentListSyntax argumentListSyntax, SyntaxNode nodeToBind) + protected override LocalSymbol MakeDeclarationExpressionVariable(DeclarationExpressionSyntax node, BaseArgumentListSyntax argumentListSyntax, SyntaxNode nodeToBind) { NamedTypeSymbol container = _scopeBinder.ContainingType; var designation = node.Designation as SingleVariableDesignationSyntax; @@ -532,7 +543,7 @@ protected override Symbol MakePatternVariable(DeclarationPatternSyntax node, Syn _containingFieldOpt, nodeToBind); } - protected override Symbol MakeOutVariable(DeclarationExpressionSyntax node, BaseArgumentListSyntax argumentListSyntax, SyntaxNode nodeToBind) + protected override Symbol MakeDeclarationExpressionVariable(DeclarationExpressionSyntax node, BaseArgumentListSyntax argumentListSyntax, SyntaxNode nodeToBind) { var designation = (SingleVariableDesignationSyntax)node.Designation; return GlobalExpressionVariable.Create( diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 00a696e94b48c..b209f91cb2637 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -99,7 +99,9 @@ internal void CollectLocalsFromDeconstruction( case SyntaxKind.IdentifierName: break; default: - throw ExceptionUtilities.UnexpectedValue(declaration.Kind()); + // In broken code, we can have an arbitrary expression here. Collect its expression variables. + ExpressionVariableFinder.FindExpressionVariables(this, locals, declaration); + break; } } @@ -136,7 +138,7 @@ internal void CollectLocalsFromDeconstruction( } break; } - case SyntaxKind.DiscardedDesignation: + case SyntaxKind.DiscardDesignation: break; default: throw ExceptionUtilities.UnexpectedValue(designation.Kind()); @@ -166,13 +168,16 @@ internal override BoundStatement BindForEachDeconstruction(DiagnosticBag diagnos ExpressionSyntax variables = ((ForEachVariableStatementSyntax)_syntax).Variable; var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, inferredType ?? CreateErrorType("var")); + DeclarationExpressionSyntax declaration = null; + ExpressionSyntax expression = null; BoundDeconstructionAssignmentOperator deconstruction = BindDeconstruction( variables, variables, right: null, diagnostics: diagnostics, - isDeclaration: true, - rightPlaceholder: valuePlaceholder); + rightPlaceholder: valuePlaceholder, + declaration: ref declaration, + expression: ref expression); return new BoundExpressionStatement(_syntax, deconstruction); } @@ -233,16 +238,36 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, iterationVariableType = inferredType ?? CreateErrorType("var"); var variables = node.Variable; - var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, iterationVariableType); - BoundDeconstructionAssignmentOperator deconstruction = BindDeconstruction( - variables, - variables, - right: null, - diagnostics: diagnostics, - isDeclaration: true, - rightPlaceholder: valuePlaceholder); - - deconstructStep = new BoundForEachDeconstructStep(variables, deconstruction, valuePlaceholder); + if (variables.IsDeconstructionLeft()) + { + var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, iterationVariableType); + DeclarationExpressionSyntax declaration = null; + ExpressionSyntax expression = null; + BoundDeconstructionAssignmentOperator deconstruction = BindDeconstruction( + variables, + variables, + right: null, + diagnostics: diagnostics, + rightPlaceholder: valuePlaceholder, + declaration: ref declaration, + expression: ref expression); + + if (expression != null) + { + // error: must declare foreach loop iteration variables. + Error(diagnostics, ErrorCode.ERR_MustDeclareForeachIteration, variables); + hasErrors = true; + } + + deconstructStep = new BoundForEachDeconstructStep(variables, deconstruction, valuePlaceholder); + } + else if (!node.HasErrors) + { + // error: must declare foreach loop iteration variables. + Error(diagnostics, ErrorCode.ERR_MustDeclareForeachIteration, variables); + hasErrors = true; + } + boundIterationVariableType = new BoundTypeExpression(variables, aliasOpt: null, type: iterationVariableType); break; } @@ -252,7 +277,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); - hasErrors = hasErrors || iterationVariableType.IsErrorType(); + hasErrors = hasErrors || boundIterationVariableType.HasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts). if (hasErrors) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index d424c113a8779..502d97a271a4b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1795,7 +1795,7 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy var nodeKind = node.Kind; if (nodeKind == BoundKind.OutVariablePendingInference || nodeKind == BoundKind.OutDeconstructVarPendingInference || - (nodeKind == BoundKind.DiscardedExpression && !node.HasExpressionType())) + (nodeKind == BoundKind.DiscardExpression && !node.HasExpressionType())) { // Neither conversion from expression is better when the argument is an implicitly-typed out variable declaration. okToDowngradeToNeither = false; @@ -2975,7 +2975,7 @@ private Conversion CheckArgumentForApplicability( var argType = argument.Type; if (argument.Kind == BoundKind.OutVariablePendingInference || argument.Kind == BoundKind.OutDeconstructVarPendingInference || - (argument.Kind == BoundKind.DiscardedExpression && (object)argType == null)) + (argument.Kind == BoundKind.DiscardExpression && (object)argType == null)) { Debug.Assert(argRefKind != RefKind.None); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs index e1c1efa0b75ce..231a0ab76b132 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs @@ -924,7 +924,7 @@ private static void ReportBadArgumentError( if (!argument.HasExpressionType() && argument.Kind != BoundKind.OutDeconstructVarPendingInference && argument.Kind != BoundKind.OutVariablePendingInference && - argument.Kind != BoundKind.DiscardedExpression) + argument.Kind != BoundKind.DiscardExpression) { // If the problem is that a lambda isn't convertible to the given type, also report why. // The argument and parameter type might match, but may not have same in/out modifiers @@ -973,7 +973,7 @@ private static void ReportBadArgumentError( { Debug.Assert(argument.Kind != BoundKind.OutDeconstructVarPendingInference); Debug.Assert(argument.Kind != BoundKind.OutVariablePendingInference); - Debug.Assert(argument.Kind != BoundKind.DiscardedExpression || argument.HasExpressionType()); + Debug.Assert(argument.Kind != BoundKind.DiscardExpression || argument.HasExpressionType()); Debug.Assert(argument.Display != null); if (arguments.IsExtensionMethodThisArgument(arg)) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardedExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs similarity index 83% rename from src/Compilers/CSharp/Portable/BoundTree/BoundDiscardedExpression.cs rename to src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs index 4b870b42150cb..dc47e4d10878d 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardedExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs @@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.CSharp { - internal partial class BoundDiscardedExpression + internal partial class BoundDiscardExpression { public BoundExpression SetInferredType(TypeSymbol type) { @@ -13,7 +13,7 @@ public BoundExpression SetInferredType(TypeSymbol type) return this.Update(type); } - public BoundDiscardedExpression FailInference(Binder binder, DiagnosticBag diagnosticsOpt) + public BoundDiscardExpression FailInference(Binder binder, DiagnosticBag diagnosticsOpt) { if (diagnosticsOpt != null) { diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 86a5dd6fbbc34..6754910945792 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -419,11 +419,6 @@ - - - - - @@ -1584,9 +1579,9 @@ - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index eb55475f163d2..7a3283678fbf1 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -1037,21 +1037,6 @@ public override TResult Accept(OperationVisitor OperationKind.None; - - public override void Accept(OperationVisitor visitor) - { - visitor.VisitNoneOperation(this); - } - - public override TResult Accept(OperationVisitor visitor, TArgument argument) - { - return visitor.VisitNoneOperation(this, argument); - } - } - internal partial class BoundCompoundAssignmentOperator : ICompoundAssignmentExpression { BinaryOperationKind ICompoundAssignmentExpression.BinaryOperationKind => Expression.DeriveBinaryOperationKind(this.Operator.Kind); @@ -3024,12 +3009,12 @@ public override TResult Accept(OperationVisitor - + @@ -498,7 +498,7 @@ - + @@ -751,6 +751,7 @@ + diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 7de5d8d87ff92..23a9c016d4f31 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -3103,6 +3103,15 @@ internal static string ERR_DecConstError { } } + /// + /// Looks up a localized string similar to A declaration is not allowed in this context.. + /// + internal static string ERR_DeclarationExpressionNotPermitted { + get { + return ResourceManager.GetString("ERR_DeclarationExpressionNotPermitted", resourceCulture); + } + } + /// /// Looks up a localized string similar to Deconstruction 'var (...)' form disallows a specific type for 'var'.. /// @@ -6137,11 +6146,11 @@ internal static string ERR_MissingTypeInSource { } /// - /// Looks up a localized string similar to Cannot reference {0} in a deconstruction declaration. + /// Looks up a localized string similar to A deconstruction cannot mix declarations and expressions on the left-hand-side.. /// - internal static string ERR_MixedDeconstructionDisallowed { + internal static string ERR_MixedDeconstructionUnsupported { get { - return ResourceManager.GetString("ERR_MixedDeconstructionDisallowed", resourceCulture); + return ResourceManager.GetString("ERR_MixedDeconstructionUnsupported", resourceCulture); } } @@ -6199,6 +6208,15 @@ internal static string ERR_MultiTypeInDeclaration { } } + /// + /// Looks up a localized string similar to A foreach loop must declare its iteration variables.. + /// + internal static string ERR_MustDeclareForeachIteration { + get { + return ResourceManager.GetString("ERR_MustDeclareForeachIteration", resourceCulture); + } + } + /// /// Looks up a localized string similar to In order for '{0}' to be applicable as a short circuit operator, its declaring type '{1}' must define operator true and operator false. /// @@ -8863,6 +8881,15 @@ internal static string ERR_TupleElementNamesAttributeMissing { } } + /// + /// Looks up a localized string similar to Tuple element names are not permitted on the left of a deconstruction.. + /// + internal static string ERR_TupleElementNamesInDeconstruction { + get { + return ResourceManager.GetString("ERR_TupleElementNamesInDeconstruction", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tuple element name '{0}' is only allowed at position {1}.. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 99995ac5870de..62117c4be2005 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2105,9 +2105,6 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot assign {0} to an implicitly-typed variable - - Cannot reference {0} in a deconstruction declaration - Implicitly-typed variables must be initialized @@ -4981,4 +4978,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A throw expression is not allowed in this context. + + A deconstruction cannot mix declarations and expressions on the left-hand-side. + + + A declaration is not allowed in this context. + + + A foreach loop must declare its iteration variables. + + + Tuple element names are not permitted on the left of a deconstruction. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index e03639e301a63..b08e21a743d50 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -306,10 +306,6 @@ private void EmitExpressionCore(BoundExpression expression, bool used) EmitThrowExpression((BoundThrowExpression)expression, used); break; - case BoundKind.Void: - Debug.Assert(!used); - break; - default: // Code gen should not be invoked if there are errors. Debug.Assert(expression.Kind != BoundKind.BadExpression); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 38cbd6abfc5bc..73826a18cba97 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1386,7 +1386,6 @@ internal enum ErrorCode ERR_PartialMethodInconsistentTupleNames = 8142, ERR_ExpressionTreeContainsTupleLiteral = 8143, ERR_ExpressionTreeContainsTupleConversion = 8144, - #endregion tuple diagnostics introduced in C# 7 #region diagnostics for ref locals and ref returns introduced in C# 7 @@ -1426,14 +1425,19 @@ internal enum ErrorCode ERR_RefReturningCallAndAwait = 8178, #endregion diagnostics for ref locals and ref returns introduced in C# 7 + #region stragglers for C# 7 ERR_PredefinedValueTupleTypeNotFound = 8179, ERR_SemiOrLBraceOrArrowExpected = 8180, ERR_NewWithTupleTypeSyntax = 8181, ERR_PredefinedValueTupleTypeMustBeStruct = 8182, ERR_DiscardTypeInferenceFailed = 8183, - ERR_MixedDeconstructionDisallowed = 8184, + ERR_MixedDeconstructionUnsupported = 8184, + ERR_DeclarationExpressionNotPermitted = 8185, + ERR_MustDeclareForeachIteration = 8186, + ERR_TupleElementNamesInDeconstruction = 8187, + #endregion stragglers for C# 7 - // Available = 8185-8195 + // Available = 8188-8195 #region diagnostics for out var ERR_ImplicitlyTypedOutVariableUsedInTheSameArgumentList = 8196, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 1f2a6d8994c76..db433a2c5b01e 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -8,6 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp { internal enum MessageID { + None = 0, MessageBase = 1200, IDS_SK_METHOD = MessageBase + 2000, IDS_SK_TYPE = MessageBase + 2001, diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index 3cd0f369ed93f..c3134eaf7fddb 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -2691,12 +2691,7 @@ public sealed override BoundNode VisitDeconstructionVariablePendingInference(Dec throw ExceptionUtilities.Unreachable; } - public override BoundNode VisitVoid(BoundVoid node) - { - return null; - } - - public override BoundNode VisitDiscardedExpression(BoundDiscardedExpression node) + public override BoundNode VisitDiscardExpression(BoundDiscardExpression node) { return null; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index e0a707ad06714..cd6b2c615abda 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -195,7 +195,7 @@ private BoundExpression MakeStaticAssignmentOperator(SyntaxNode syntax, BoundExp refKind: refKind); } - case BoundKind.DiscardedExpression: + case BoundKind.DiscardExpression: { return rewrittenRight; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs index e3c9a2257281f..882a323217d0a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs @@ -102,7 +102,7 @@ private void ApplyAssignments(BoundDeconstructionAssignmentOperator node, ArrayB int numAssignments = node.AssignmentSteps.Length; for (int i = 0; i < numAssignments; i++) { - if (lhsTargets[i].Kind == BoundKind.DiscardedExpression) + if (lhsTargets[i].Kind == BoundKind.DiscardExpression) { // skip assignment step for discards continue; @@ -158,7 +158,7 @@ private ImmutableArray GetAssignmentTargetsAndSideEffects(Immut foreach (var variable in variables) { - if (variable.Kind == BoundKind.DiscardedExpression) + if (variable.Kind == BoundKind.DiscardExpression) { assignmentTargets.Add(variable); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs index fe8c8614d3faa..bf5c3255ea278 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs @@ -68,14 +68,14 @@ private BoundExpression MakeIsConstantPattern(BoundConstantPattern loweredPatter private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern loweredPattern, BoundExpression loweredInput) { - Debug.Assert(((object)loweredPattern.Variable == null && loweredPattern.VariableAccess.Kind == BoundKind.DiscardedExpression) || + Debug.Assert(((object)loweredPattern.Variable == null && loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) || loweredPattern.Variable.GetTypeOrReturnType() == loweredPattern.DeclaredType.Type); if (loweredPattern.IsVar) { var result = _factory.Literal(true); - if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardedExpression) + if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) { return result; } @@ -86,10 +86,10 @@ private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern lowered return _factory.MakeSequence(assignment, result); } - if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardedExpression) + if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) { LocalSymbol temp; - BoundLocal discard = _factory.MakeTempForDiscard((BoundDiscardedExpression)loweredPattern.VariableAccess, out temp); + BoundLocal discard = _factory.MakeTempForDiscard((BoundDiscardExpression)loweredPattern.VariableAccess, out temp); return _factory.Sequence(ImmutableArray.Create(temp), sideEffects: ImmutableArray.Empty, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs index 014dfe7e65b00..74ecc489890d0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs @@ -672,7 +672,7 @@ internal LoweredDynamicOperation MakeDynamicOperation( /// private ImmutableArray MakeTempsForDiscardArguments(ref ImmutableArray loweredArguments) { - int discardCount = loweredArguments.Count(a => a.Kind == BoundKind.DiscardedExpression); + int discardCount = loweredArguments.Count(a => a.Kind == BoundKind.DiscardExpression); if (discardCount == 0) { diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 5f82aff7f9ff5..67b32fafba97c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -1275,7 +1275,7 @@ internal BoundStatement NoOp(NoOpStatementFlavor noOpStatementFlavor) return new BoundNoOpStatement(Syntax, noOpStatementFlavor); } - internal BoundLocal MakeTempForDiscard(BoundDiscardedExpression node, ArrayBuilder temps) + internal BoundLocal MakeTempForDiscard(BoundDiscardExpression node, ArrayBuilder temps) { LocalSymbol temp; BoundLocal result = MakeTempForDiscard(node, out temp); @@ -1283,7 +1283,7 @@ internal BoundLocal MakeTempForDiscard(BoundDiscardedExpression node, ArrayBuild return result; } - internal BoundLocal MakeTempForDiscard(BoundDiscardedExpression node, out LocalSymbol temp) + internal BoundLocal MakeTempForDiscard(BoundDiscardExpression node, out LocalSymbol temp) { temp = new SynthesizedLocal(this.CurrentMethod, node.Type, SynthesizedLocalKind.LoweringTemp); @@ -1292,12 +1292,12 @@ internal BoundLocal MakeTempForDiscard(BoundDiscardedExpression node, out LocalS internal ImmutableArray MakeTempsForDiscardArguments(ImmutableArray arguments, ArrayBuilder builder) { - var discardsCount = arguments.Count(a => a.Kind == BoundKind.DiscardedExpression); + var discardsCount = arguments.Count(a => a.Kind == BoundKind.DiscardExpression); if (discardsCount != 0) { arguments = arguments.SelectAsArray( - (arg, t) => arg.Kind == BoundKind.DiscardedExpression ? t.Item1.MakeTempForDiscard((BoundDiscardedExpression)arg, t.Item2) : arg, + (arg, t) => arg.Kind == BoundKind.DiscardExpression ? t.Item1.MakeTempForDiscard((BoundDiscardExpression)arg, t.Item2) : arg, ValueTuple.Create(this, builder)); } diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 8297206437946..ab6b2c6522ad6 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -1773,6 +1773,11 @@ private void SkipBadMemberListTokens(ref GreenNode previousNode) { bool done = false; + // always consume at least one token. + var token = this.EatToken(); + token = this.AddError(token, ErrorCode.ERR_InvalidMemberDecl, token.Text); + tokens.Add(token); + while (!done) { SyntaxKind kind = this.CurrentToken.Kind; @@ -1813,13 +1818,7 @@ private void SkipBadMemberListTokens(ref GreenNode previousNode) break; } - var token = this.EatToken(); - if (tokens.Count == 0) - { - token = this.AddError(token, ErrorCode.ERR_InvalidMemberDecl, token.Text); - } - - tokens.Add(token); + tokens.Add(this.EatToken()); } previousNode = AddTrailingSkippedSyntax((CSharpSyntaxNode)previousNode, tokens.ToListNode()); @@ -2407,16 +2406,6 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatement(SyntaxKind par } } - if (acceptStatement) - { - var deconstruction = ParseDeconstructionDeclarationAssignment(); - if (deconstruction != null) - { - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.GlobalStatement(_syntaxFactory.ExpressionStatement(deconstruction, semicolon)); - } - } - // Everything that's left -- methods, fields, properties, // indexers, and non-conversion operators -- starts with a type // (possibly void). Parse that. @@ -3990,6 +3979,12 @@ private void ParseParameterList( attributes.Clear(); modifiers.Clear(); parameter = this.ParseParameter(attributes, modifiers, allowThisKeyword, allowDefaults, allowAttributes); + if (parameter.IsMissing && this.IsPossibleParameter(allowThisKeyword)) + { + // ensure we always consume tokens + parameter = AddTrailingSkippedSyntax(parameter, this.EatToken()); + } + nodes.Add(parameter); hasParams = modifiers.Any((int)SyntaxKind.ParamsKeyword); hasArgList = parameter.Identifier.Kind == SyntaxKind.ArgListKeyword; @@ -5326,6 +5321,8 @@ private enum NameOptions InExpression = 1 << 0, // Used to influence parser ambiguity around "<" and generics vs. expressions. Used in ParseSimpleName. InTypeList = 1 << 1, // Allows attributes to appear within the generic type argument list. Used during ParseInstantiation. PossiblePattern = 1 << 2, // Used to influence parser ambiguity around "<" and generics vs. expressions on the right of 'is' + AfterIsOrCaseOrOutOrTupleComma = 1 << 3, + FirstElementOfPossibleTupleLiteral = 1 << 4, } /// @@ -5346,6 +5343,17 @@ private bool IsTrueIdentifier() return false; } + /// + /// True if the given token is not really some contextual keyword. + /// This method is for use in executable code, as it treats `partial` as an identifier. + /// + private bool IsTrueIdentifier(SyntaxToken token) + { + return + token.Kind == SyntaxKind.IdentifierToken && + !(this.IsInQuery && IsTokenQueryContextualKeyword(token)); + } + private IdentifierNameSyntax ParseIdentifierName() { if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.IdentifierName) @@ -5558,58 +5566,77 @@ private enum ScanTypeArgumentListKind private ScanTypeArgumentListKind ScanTypeArgumentList(NameOptions options) { - if (this.CurrentToken.Kind == SyntaxKind.LessThanToken) + if (this.CurrentToken.Kind != SyntaxKind.LessThanToken) { - if ((options & NameOptions.InExpression) != 0) - { - // Scan for a type argument list. If we think it's a type argument list - // then assume it is unless we see specific tokens following it. - if (this.ScanPossibleTypeArgumentList()) - { - switch (this.CurrentToken.Kind) - { - case SyntaxKind.IdentifierToken: - // we allow 'G x' as a pattern-matching operation. - return ((options & NameOptions.PossiblePattern) != 0) - ? ScanTypeArgumentListKind.DefiniteTypeArgumentList - : ScanTypeArgumentListKind.PossibleTypeArgumentList; + return ScanTypeArgumentListKind.NotTypeArgumentList; + } - case SyntaxKind.OpenParenToken: - case SyntaxKind.CloseParenToken: - case SyntaxKind.CloseBracketToken: - case SyntaxKind.ColonToken: - case SyntaxKind.SemicolonToken: - case SyntaxKind.CommaToken: - case SyntaxKind.DotToken: - case SyntaxKind.QuestionToken: - case SyntaxKind.EqualsEqualsToken: - case SyntaxKind.ExclamationEqualsToken: - // These tokens are from 7.5.4.2 Grammar Ambiguities - return ScanTypeArgumentListKind.DefiniteTypeArgumentList; - - case SyntaxKind.AmpersandAmpersandToken: // e.g. `e is A && e` - case SyntaxKind.BarBarToken: // e.g. `e is A || e` - case SyntaxKind.CaretToken: // e.g. `e is A ^ e` - case SyntaxKind.BarToken: // e.g. `e is A | e` - case SyntaxKind.AmpersandToken: // e.g. `e is A & e` - case SyntaxKind.OpenBracketToken: // e.g. `e is A[]` - case SyntaxKind.CloseBraceToken: // e.g. `new { X = e is A }` - case SyntaxKind.EndOfFileToken: // e.g. `e is A` in isolation - // These tokens are not from 7.5.4.2 Grammar Ambiguities - return ScanTypeArgumentListKind.DefiniteTypeArgumentList; + if ((options & NameOptions.InExpression) == 0) + { + return ScanTypeArgumentListKind.DefiniteTypeArgumentList; + } - default: - return ScanTypeArgumentListKind.PossibleTypeArgumentList; - } + if (!this.ScanPossibleTypeArgumentList()) + { + return ScanTypeArgumentListKind.NotTypeArgumentList; + } + + // Scan for a type argument list. If we think it's a type argument list + // then assume it is unless we see specific tokens following it. + switch (this.CurrentToken.Kind) + { + case SyntaxKind.IdentifierToken: + // C#7: In certain contexts, we treat *identifier* as a disambiguating token. Those + // contexts are where the sequence of tokens being disambiguated is immediately preceded by one + // of the keywords is, case, or out, or arises while parsing the first element of a tuple literal + // (in which case the tokens are preceded by `(` and the identifier is followed by a `,`) or a + // subsequent element of a tuple literal. + // Note that we treat query contextual keywords (which appear here as identifiers) as disambiguating tokens as well. + if ((options & NameOptions.AfterIsOrCaseOrOutOrTupleComma) != 0 || + (options & NameOptions.FirstElementOfPossibleTupleLiteral) != 0 && this.PeekToken(1).Kind == SyntaxKind.CommaToken) + { + // we allow 'G x' as a pattern-matching operation and a declaration expression in a tuple. + return ScanTypeArgumentListKind.DefiniteTypeArgumentList; } - } - else - { + + return ScanTypeArgumentListKind.PossibleTypeArgumentList; + + case SyntaxKind.OpenParenToken: + case SyntaxKind.CloseParenToken: + case SyntaxKind.CloseBracketToken: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.ColonToken: + case SyntaxKind.SemicolonToken: + case SyntaxKind.CommaToken: + case SyntaxKind.DotToken: + case SyntaxKind.QuestionToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.BarToken: + case SyntaxKind.CaretToken: + // These tokens are from 7.5.4.2 Grammar Ambiguities + return ScanTypeArgumentListKind.DefiniteTypeArgumentList; + + case SyntaxKind.AmpersandAmpersandToken: // e.g. `e is A && e` + case SyntaxKind.BarBarToken: // e.g. `e is A || e` + case SyntaxKind.AmpersandToken: // e.g. `e is A & e` + case SyntaxKind.OpenBracketToken: // e.g. `e is A[]` + case SyntaxKind.LessThanToken: // e.g. `e is A < C` + case SyntaxKind.GreaterThanToken: // e.g. `e is A > C` + case SyntaxKind.LessThanEqualsToken: // e.g. `e is A <= C` + case SyntaxKind.GreaterThanEqualsToken: // e.g. `e is A >= C` + case SyntaxKind.IsKeyword: // e.g. `e is A is bool` + case SyntaxKind.AsKeyword: // e.g. `e is A as bool` + // These tokens are added to 7.5.4.2 Grammar Ambiguities in C#7 + return ScanTypeArgumentListKind.DefiniteTypeArgumentList; + + case SyntaxKind.EndOfFileToken: // e.g. `e is A` + // This is useful for parsing expressions in isolation return ScanTypeArgumentListKind.DefiniteTypeArgumentList; - } - } - return ScanTypeArgumentListKind.NotTypeArgumentList; + default: + return ScanTypeArgumentListKind.PossibleTypeArgumentList; + } } private bool ScanPossibleTypeArgumentList() @@ -6159,22 +6186,19 @@ private ScanTypeFlags ScanType(out SyntaxToken lastTokenOfType) return result; } - // Finally, check for array types and nullables. + // Finally, check for array types. while (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken) { this.EatToken(); - if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) + while (this.CurrentToken.Kind == SyntaxKind.CommaToken) { - while (this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - this.EatToken(); - } + this.EatToken(); + } - if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) - { - lastTokenOfType = null; - return ScanTypeFlags.NotType; - } + if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) + { + lastTokenOfType = null; + return ScanTypeFlags.NotType; } lastTokenOfType = this.EatToken(); @@ -6310,12 +6334,10 @@ private ScanTypeFlags ScanNonArrayType(out SyntaxToken lastTokenOfType) /// private ScanTypeFlags ScanTupleType(out SyntaxToken lastTokenOfType) { - bool mustBeType = false; - var tupleElementType = ScanType(out lastTokenOfType); if (tupleElementType != ScanTypeFlags.NotType) { - if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) + if (IsTrueIdentifier()) { lastTokenOfType = this.EatToken(); } @@ -6332,12 +6354,8 @@ private ScanTypeFlags ScanTupleType(out SyntaxToken lastTokenOfType) lastTokenOfType = this.EatToken(); return ScanTypeFlags.NotType; } - else if (tupleElementType == ScanTypeFlags.MustBeType) - { - mustBeType = true; - } - if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) + if (IsTrueIdentifier()) { lastTokenOfType = this.EatToken(); } @@ -6347,18 +6365,11 @@ private ScanTypeFlags ScanTupleType(out SyntaxToken lastTokenOfType) if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken) { lastTokenOfType = this.EatToken(); - - if (mustBeType) - { - return ScanTypeFlags.MustBeType; - } - else - { - return ScanTypeFlags.TupleType; - } + return ScanTypeFlags.TupleType; } } } + // Can't be a type! lastTokenOfType = null; return ScanTypeFlags.NotType; @@ -6425,9 +6436,12 @@ private enum ParseTypeMode { Normal, Parameter, - Pattern, + AfterIsOrCase, + AfterOut, + AfterTupleComma, AsExpression, - ArrayCreation + ArrayCreation, + FirstElementOfPossibleTupleLiteral } private TypeSyntax ParseType( @@ -6450,11 +6464,35 @@ private TypeSyntax ParseTypeCore( ParseTypeMode mode, bool expectSizes) { - var isOrAs = mode == ParseTypeMode.AsExpression || mode == ParseTypeMode.Pattern; - var type = this.ParseUnderlyingType(mode == ParseTypeMode.Parameter, - mode == ParseTypeMode.Pattern ? NameOptions.InExpression | NameOptions.PossiblePattern : NameOptions.None); + var isOrAs = mode == ParseTypeMode.AsExpression || mode == ParseTypeMode.AfterIsOrCase; + NameOptions nameOptions; + switch (mode) + { + case ParseTypeMode.AfterIsOrCase: + nameOptions = NameOptions.InExpression | NameOptions.AfterIsOrCaseOrOutOrTupleComma | NameOptions.PossiblePattern; + break; + case ParseTypeMode.AfterOut: + case ParseTypeMode.AfterTupleComma: + nameOptions = NameOptions.InExpression | NameOptions.AfterIsOrCaseOrOutOrTupleComma; + break; + case ParseTypeMode.FirstElementOfPossibleTupleLiteral: + nameOptions = NameOptions.InExpression | NameOptions.FirstElementOfPossibleTupleLiteral; + break; + case ParseTypeMode.ArrayCreation: + case ParseTypeMode.AsExpression: + case ParseTypeMode.Normal: + case ParseTypeMode.Parameter: + nameOptions = NameOptions.None; + break; + default: + throw ExceptionUtilities.UnexpectedValue(mode); + } - if (this.CurrentToken.Kind == SyntaxKind.QuestionToken) + var type = this.ParseUnderlyingType(parentIsParameter: mode == ParseTypeMode.Parameter, options: nameOptions); + + if (this.CurrentToken.Kind == SyntaxKind.QuestionToken && + // we do not permit nullable types in a declaration pattern + (mode != ParseTypeMode.AfterIsOrCase || !IsTrueIdentifier(this.PeekToken(1)))) { var resetPoint = this.GetResetPoint(); try @@ -6479,7 +6517,21 @@ private TypeSyntax ParseTypeCore( } // Check for pointer types (only if pType is NOT an array type) - type = this.ParsePointerTypeMods(type); + switch (mode) + { + case ParseTypeMode.AfterIsOrCase: + case ParseTypeMode.AfterTupleComma: + case ParseTypeMode.FirstElementOfPossibleTupleLiteral: + // these contexts do not permit a pointer type. + break; + case ParseTypeMode.Normal: + case ParseTypeMode.Parameter: + case ParseTypeMode.AfterOut: + case ParseTypeMode.ArrayCreation: + case ParseTypeMode.AsExpression: + type = this.ParsePointerTypeMods(type); + break; + } // Now check for arrays. if (this.IsPossibleRankAndDimensionSpecifier()) @@ -6649,7 +6701,7 @@ private TupleElementSyntax ParseTupleElement() var type = ParseType(); IdentifierNameSyntax name = null; - if (CurrentToken.Kind == SyntaxKind.IdentifierToken) + if (this.IsTrueIdentifier()) { name = ParseIdentifierName(); } @@ -6678,7 +6730,7 @@ private TypeSyntax ParseUnderlyingType(bool parentIsParameter, NameOptions optio return _syntaxFactory.PredefinedType(token); } - else if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) + else if (IsTrueIdentifier()) { return this.ParseQualifiedName(options); } @@ -6850,8 +6902,9 @@ private StatementSyntax ParseStatementNoDeclaration(bool allowAnyExpression) case SyntaxKind.DoKeyword: return this.ParseDoStatement(); case SyntaxKind.ForKeyword: - case SyntaxKind.ForEachKeyword: return this.ParseForOrForEachStatement(); + case SyntaxKind.ForEachKeyword: + return this.ParseForEachStatement(); case SyntaxKind.GotoKeyword: return this.ParseGotoStatement(); case SyntaxKind.IfKeyword: @@ -6936,7 +6989,10 @@ private bool IsPossibleLocalDeclarationStatement(bool allowAnyExpression) var tk = this.CurrentToken.Kind; if (tk == SyntaxKind.RefKeyword || - (SyntaxFacts.IsPredefinedType(tk) && this.PeekToken(1).Kind != SyntaxKind.DotToken) || IsDeclarationModifier(tk)) + IsDeclarationModifier(tk) || // treat `static int x = 2;` as a local variable declaration + (SyntaxFacts.IsPredefinedType(tk) && + this.PeekToken(1).Kind != SyntaxKind.DotToken && // e.g. `int.Parse()` is an expression + this.PeekToken(1).Kind != SyntaxKind.OpenParenToken)) // e.g. `int (x, y)` is an error decl expression { return true; } @@ -6954,12 +7010,6 @@ private bool IsPossibleLocalDeclarationStatement(bool allowAnyExpression) return typedIdentifier.Value; } - bool deconstruction = IsPossibleDeconstructionDeclaration(); - if (deconstruction) - { - return true; - } - var resetPoint = this.GetResetPoint(); try { @@ -6968,7 +7018,7 @@ private bool IsPossibleLocalDeclarationStatement(bool allowAnyExpression) // We could always return true for st == AliasQualName in addition to MustBeType on the first line, however, we want it to return false in the case where // CurrentToken.Kind != SyntaxKind.Identifier so that error cases, like: A::N(), are not parsed as variable declarations and instead are parsed as A.N() where we can give // a better error message saying "did you meant to use a '.'?" - if (st == ScanTypeFlags.MustBeType && this.CurrentToken.Kind != SyntaxKind.DotToken) + if (st == ScanTypeFlags.MustBeType && this.CurrentToken.Kind != SyntaxKind.DotToken && this.CurrentToken.Kind != SyntaxKind.OpenParenToken) { return true; } @@ -7256,9 +7306,9 @@ private bool IsPossibleNewExpression() /// false if it definitely can't be, /// null if we need to scan further to find out. /// - private static bool? IsPossibleTypedIdentifierStart(SyntaxToken current, SyntaxToken next, bool allowThisKeyword) + private bool? IsPossibleTypedIdentifierStart(SyntaxToken current, SyntaxToken next, bool allowThisKeyword) { - if (current.Kind == SyntaxKind.IdentifierToken) + if (IsTrueIdentifier(current)) { switch (next.Kind) { @@ -7274,7 +7324,8 @@ private bool IsPossibleNewExpression() case SyntaxKind.OpenParenToken: if (current.IsVar()) { - // potential deconstruction-declaration + // potentially either a tuple type in a local declaration (true), or + // a tuple lvalue in a deconstruction assignment (false). return null; } else @@ -7283,7 +7334,7 @@ private bool IsPossibleNewExpression() } case SyntaxKind.IdentifierToken: - return true; + return IsTrueIdentifier(next); case SyntaxKind.ThisKeyword: return allowThisKeyword; @@ -7525,8 +7576,7 @@ private StatementSyntax ParseEmbeddedStatement(bool complexCheck) } // An "embedded" statement is simply a statement that is not a labelled - // statement or a declaration statement. Parse a normal statement and post- - // check for the error case. + // statement or a declaration statement. switch (statement.Kind) { case SyntaxKind.LabeledStatement: @@ -7534,19 +7584,6 @@ private StatementSyntax ParseEmbeddedStatement(bool complexCheck) case SyntaxKind.LocalFunctionStatement: statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt); break; - case SyntaxKind.ExpressionStatement: - // Deconstruction-declaration is only allowed as top-level statement - // see https://github.com/dotnet/roslyn/issues/15049 - var expression = ((ExpressionStatementSyntax)statement).Expression; - if (expression.Kind == SyntaxKind.SimpleAssignmentExpression) - { - var assignment = (AssignmentExpressionSyntax)expression; - if (assignment.Left.IsDeconstructionDeclarationLeft()) - { - statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt); - } - } - break; } return statement; @@ -7797,8 +7834,6 @@ private bool IsEndOfDoWhileExpression() private StatementSyntax ParseForOrForEachStatement() { - Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword || this.CurrentToken.Kind == SyntaxKind.ForEachKeyword); - // Check if the user wrote the following accidentally: // // for (SomeType t in @@ -7812,28 +7847,22 @@ private StatementSyntax ParseForOrForEachStatement() var resetPoint = this.GetResetPoint(); try { - if (this.CurrentToken.Kind == SyntaxKind.ForKeyword) + Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword); + this.EatToken(); + if (this.EatToken().Kind == SyntaxKind.OpenParenToken && + this.ScanType() != ScanTypeFlags.NotType && + this.EatToken().Kind == SyntaxKind.IdentifierToken && + this.EatToken().Kind == SyntaxKind.InKeyword) { - this.EatToken(); - if (this.EatToken().Kind == SyntaxKind.OpenParenToken && - this.ScanType() != ScanTypeFlags.NotType && - this.EatToken().Kind == SyntaxKind.IdentifierToken && - this.EatToken().Kind == SyntaxKind.InKeyword) - { - // Looks like a foreach statement. Parse it that way instead - this.Reset(ref resetPoint); - return this.ParseForEachStatement(); - } - else - { - // Normal for statement. - this.Reset(ref resetPoint); - return this.ParseForStatement(); - } + // Looks like a foreach statement. Parse it that way instead + this.Reset(ref resetPoint); + return this.ParseForEachStatement(); } else { - return this.ParseForEachStatement(); + // Normal for statement. + this.Reset(ref resetPoint); + return this.ParseForStatement(); } } finally @@ -7859,40 +7888,28 @@ private ForStatementSyntax ParseForStatement() { // Here can be either a declaration or an expression statement list. Scan // for a declaration first. - AssignmentExpressionSyntax deconstruction = null; VariableDeclarationSyntax decl = null; bool isDeclaration = false; - bool isDeconstruction = false; if (this.CurrentToken.Kind == SyntaxKind.RefKeyword) { isDeclaration = true; } else { - deconstruction = TryParseDeconstructionDeclarationAssignment(); - - if (deconstruction == null) - { - isDeclaration = !this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: false) && - this.ScanType() != ScanTypeFlags.NotType && - this.IsTrueIdentifier(); + isDeclaration = !this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: false) && + this.ScanType() != ScanTypeFlags.NotType && + this.IsTrueIdentifier(); - this.Reset(ref resetPoint); - } - else - { - initializers.Add(deconstruction); - isDeconstruction = true; - } + this.Reset(ref resetPoint); } if (isDeclaration) { decl = ParseVariableDeclaration(); } - else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken && !isDeconstruction) + else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { - // Not a type followed by an identifier, and not a deconstruction-declaration, so it must be an expression list. + // Not a type followed by an identifier, so it must be an expression list. this.ParseForStatementExpressionList(ref openParen, initializers); } @@ -8000,33 +8017,59 @@ private CommonForEachStatementSyntax ParseForEachStatement() } var openParen = this.EatToken(SyntaxKind.OpenParenToken); + var variable = ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureTuples, permitTupleDesignation: true); + var @in = this.EatToken(SyntaxKind.InKeyword, ErrorCode.ERR_InExpected); + if (!IsValidForeachVariable(variable)) + { + @in = this.AddError(@in, ErrorCode.ERR_BadForeachDecl); + } - var deconstruction = TryParseDeconstructionDeclaration(SyntaxKind.InKeyword); + var expression = this.ParseExpressionCore(); + var closeParen = this.EatToken(SyntaxKind.CloseParenToken); + var statement = this.ParseEmbeddedStatement(true); - TypeSyntax type = null; - SyntaxToken name = null; - if (deconstruction == null) + var decl = variable as DeclarationExpressionSyntax; + if (decl != null && decl.designation.Kind != SyntaxKind.ParenthesizedVariableDesignation) { - type = this.ParseType(); - if (this.CurrentToken.Kind == SyntaxKind.InKeyword) - { - name = this.ParseIdentifierToken(); - name = this.AddError(name, ErrorCode.ERR_BadForeachDecl); - } - else + // if we see a foreach declaration that isn't a deconstruction, we use the old form of foreach syntax node. + SyntaxToken identifier; + switch (decl.designation.Kind) { - name = this.ParseIdentifierToken(); + case SyntaxKind.SingleVariableDesignation: + identifier = ((SingleVariableDesignationSyntax)decl.designation).identifier; + break; + case SyntaxKind.DiscardDesignation: + // revert the identifier from its contextual underscore back to an identifier. + var discard = ((DiscardDesignationSyntax)decl.designation).underscoreToken; + Debug.Assert(discard.Kind == SyntaxKind.UnderscoreToken); + identifier = SyntaxToken.WithValue(SyntaxKind.IdentifierToken, discard.LeadingTrivia.Node, discard.Text, discard.ValueText, discard.TrailingTrivia.Node); + break; + default: + throw ExceptionUtilities.UnexpectedValue(decl.designation.Kind); } + + return _syntaxFactory.ForEachStatement(@foreach, openParen, decl.Type, identifier, @in, expression, closeParen, statement); } - var @in = this.EatToken(SyntaxKind.InKeyword, ErrorCode.ERR_InExpected); - var expression = this.ParseExpressionCore(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var statement = this.ParseEmbeddedStatement(true); + return _syntaxFactory.ForEachVariableStatement(@foreach, openParen, variable, @in, expression, closeParen, statement); + } - return (deconstruction == null) - ? (CommonForEachStatementSyntax)_syntaxFactory.ForEachStatement(@foreach, openParen, type, name, @in, expression, closeParen, statement) - : (CommonForEachStatementSyntax)_syntaxFactory.ForEachVariableStatement(@foreach, openParen, deconstruction, @in, expression, closeParen, statement); + private static bool IsValidForeachVariable(ExpressionSyntax variable) + { + switch (variable.Kind) + { + case SyntaxKind.DeclarationExpression: + // e.g. `foreach (var (x, y) in e)` + return true; + case SyntaxKind.TupleExpression: + // e.g. `foreach ((var x, var y) in e)` + return true; + case SyntaxKind.IdentifierName: + // e.g. `foreach (_ in e)` + return ((IdentifierNameSyntax)variable).Identifier.ContextualKind == SyntaxKind.UnderscoreToken; + default: + return false; + } } private GotoStatementSyntax ParseGotoStatement() @@ -8419,18 +8462,10 @@ private LabeledStatementSyntax ParseLabeledStatement() } /// - /// Parses any kind of local declaration statement: local variable, local function, or deconstruction declaration. + /// Parses any kind of local declaration statement: local variable or local function. /// private StatementSyntax ParseLocalDeclarationStatement() { - var deconstruction = TryParseDeconstructionDeclarationAssignment(); - if (deconstruction != null) - { - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - var result = _syntaxFactory.ExpressionStatement(deconstruction, semicolon); - return result; - } - var mods = _pool.Allocate(); this.ParseDeclarationModifiers(mods); @@ -8482,200 +8517,7 @@ private StatementSyntax ParseLocalDeclarationStatement() } } - /// - /// Returns null and resets the pointer if this does not look like a deconstruction-declaration after all. - /// - private AssignmentExpressionSyntax TryParseDeconstructionDeclarationAssignment() - { - if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken - || CurrentToken.IsVarOrPredefinedType() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken) - { - var resetPoint = this.GetResetPoint(); - - try - { - var deconstruction = ParseDeconstructionDeclarationAssignment(); - if (deconstruction == null) - { - this.Reset(ref resetPoint); - return null; - } - else - { - return deconstruction; - } - - } - finally - { - this.Release(ref resetPoint); - } - } - - return null; - } - - /// - /// Returns null and resets the pointer if this does not look like a deconstruction-declaration after all. - /// - private ExpressionSyntax TryParseDeconstructionDeclaration(SyntaxKind nextExpectedKind) - { - if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken - || CurrentToken.IsVarOrPredefinedType() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken) - { - var resetPoint = this.GetResetPoint(); - - try - { - var deconstruction = ParseDeconstructionDeclarationVariables(true); - if (deconstruction == null || CurrentToken.Kind != nextExpectedKind) - { - this.Reset(ref resetPoint); - return null; - } - else - { - return deconstruction; - } - } - finally - { - this.Release(ref resetPoint); - } - } - - return null; - } - - /// - /// Parses a deconstruction-declaration, which can appear in a local-declaration statement or a for statement. - /// Returns null if this does not look like a deconstruction-declaration after all (equal sign missing). - /// - /// The syntax is either var form: `var (deconstruction-declaration, ...) = expression` or list form `(deconstruction-declaration, ...) = expression`. - /// Cannot return null, except at the top-level. - /// - private AssignmentExpressionSyntax ParseDeconstructionDeclarationAssignment() - { - var component = TryParseDeconstructionDeclaration(SyntaxKind.EqualsToken); - if (component == null) return null; - var equalsToken = this.EatToken(SyntaxKind.EqualsToken); - var value = this.ParseExpressionCore(); - return _syntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, component, equalsToken, value); - } - - /// - /// Parses a deconstruction-declaration, which can appear in a local-declaration statement or a for statement. - /// Returns null if this does not look like a deconstruction-declaration after all (equal sign missing). - /// - /// The syntax is either var form: `var (deconstruction-declaration, ...) = expression` or list form `(deconstruction-declaration, ...) = expression`. - /// Cannot return null, except at the top-level. - /// - /// Specifies whether to parse the terminal form of a deconstruction-declaration (which can't appear at the top-level). - private ExpressionSyntax ParseDeconstructionDeclarationVariables(bool topLevel = false) - { - if (topLevel && - !(CurrentToken.IsVarOrPredefinedType() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken || this.CurrentToken.Kind == SyntaxKind.OpenParenToken)) - { - return null; - } - - // the two forms of component are - // (1) type designator - // (2) ( decl-expr, ... ) - ExpressionSyntax result; - if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) - { - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var listOfDeclarations = _pool.AllocateSeparated(); - while (true) - { - listOfDeclarations.Add(_syntaxFactory.Argument(nameColon: null, refOrOutKeyword: null, expression: ParseDeconstructionDeclarationVariables())); - if (this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - listOfDeclarations.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - } - else - { - break; - } - } - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - result = _syntaxFactory.TupleExpression(openParen, listOfDeclarations, closeParen); - _pool.Free(listOfDeclarations); - } - else - { - if (this.CurrentToken.ContextualKind == SyntaxKind.UnderscoreToken && - (this.PeekToken(1).Kind == SyntaxKind.CommaToken || this.PeekToken(1).Kind == SyntaxKind.CloseParenToken)) - { - return this.ParseIdentifierName(); - } - - TypeSyntax type; - bool reportMissingType = false; - if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && - (this.PeekToken(1).Kind == SyntaxKind.CommaToken || this.PeekToken(1).Kind == SyntaxKind.CloseParenToken)) - { - var missingType = CreateMissingIdentifierToken(); - type = _syntaxFactory.IdentifierName(missingType); - reportMissingType = true; - } - else - { - type = ParseType(); - } - - VariableDesignationSyntax designation = ParseDeconstructionDesignation(topLevel); - if (reportMissingType) - { - designation = this.AddError(designation, ErrorCode.ERR_TypeExpected); - } - - result = _syntaxFactory.DeclarationExpression(type, designation); - } - - return - topLevel ? (TypeFoundInDeconstructionDeclarationVariables(result) ? CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples) : null) : result; - } - - /// - /// Checks if we can find at least one type in the deconstruction variables - /// - private static bool TypeFoundInDeconstructionDeclarationVariables(ExpressionSyntax node) - { - switch (node.Kind) - { - case SyntaxKind.TupleExpression: - { - var syntax = (TupleExpressionSyntax)node; - if (syntax.Arguments.Count <= 1) return false; // don't count 1ples - for (int i = 0; i < syntax.Arguments.Count; i++) - { - if (TypeFoundInDeconstructionDeclarationVariables(syntax.Arguments[i].Expression)) return true; - } - - return false; - } - case SyntaxKind.DeclarationExpression: - { - var syntax = (DeclarationExpressionSyntax)node; - if (syntax.Type.IsMissing || syntax.Designation.IsMissing) return false; - if (syntax.Designation.Kind == SyntaxKind.ParenthesizedVariableDesignation) - { - return (syntax.Type.Kind == SyntaxKind.IdentifierName) && ((IdentifierNameSyntax)syntax.Type).Identifier.IsVar() - || syntax.Type.Kind == SyntaxKind.PredefinedType; - } - - return true; - } - case SyntaxKind.IdentifierName: - return false; - default: - throw ExceptionUtilities.UnexpectedValue(node.Kind); - } - } - - private VariableDesignationSyntax ParseDeconstructionDesignation(bool topLevel = false) + private VariableDesignationSyntax ParseDesignation() { // the two forms of designation are // (1) identifier @@ -8687,7 +8529,7 @@ private VariableDesignationSyntax ParseDeconstructionDesignation(bool topLevel = var listOfDesignations = _pool.AllocateSeparated(); while (true) { - listOfDesignations.Add(ParseDeconstructionDesignation()); + listOfDesignations.Add(ParseDesignation()); if (this.CurrentToken.Kind == SyntaxKind.CommaToken) { listOfDesignations.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); @@ -8718,7 +8560,7 @@ private VariableDesignationSyntax ParseSimpleDesignation() if (CurrentToken.ContextualKind == SyntaxKind.UnderscoreToken) { var underscore = this.EatContextualToken(SyntaxKind.UnderscoreToken); - return _syntaxFactory.DiscardedDesignation(underscore); + return _syntaxFactory.DiscardDesignation(underscore); } else { @@ -8727,33 +8569,6 @@ private VariableDesignationSyntax ParseSimpleDesignation() } } - /// - /// Check ahead for a deconstruction declaration. This requires at least one good-looking variable and the presence of an equals sign. - /// Doesn't move the cursor. - /// - private bool IsPossibleDeconstructionDeclaration() - { - if (CurrentToken.IsVarOrPredefinedType() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken || - this.CurrentToken.Kind == SyntaxKind.OpenParenToken) - { - var resetPoint = this.GetResetPoint(); - try - { - var assignment = ParseDeconstructionDeclarationAssignment(); - return assignment != null && assignment.operatorToken.Kind == SyntaxKind.EqualsToken; - } - finally - { - this.Reset(ref resetPoint); - this.Release(ref resetPoint); - } - } - else - { - return false; - } - } - private WhenClauseSyntax ParseWhenClause() { if (this.CurrentToken.ContextualKind != SyntaxKind.WhenKeyword) @@ -9403,6 +9218,10 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) return (precedence <= Precedence.Coalescing) ? result : this.AddError(result, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk)); } + else if (this.IsPossibleDeconstructionLeft(precedence)) + { + leftOperand = ParseDeclarationExpression(ParseTypeMode.Normal, MessageID.IDS_FeatureTuples); + } else { // Not a unary operator - get a primary expression. @@ -9528,6 +9347,18 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) return leftOperand; } + private ExpressionSyntax ParseDeclarationExpression(ParseTypeMode mode, MessageID feature) + { + TypeSyntax type = this.ParseType(mode); + var designation = ParseDesignation(); + if (feature != MessageID.None) + { + designation = CheckFeatureAvailability(designation, feature); + } + + return _syntaxFactory.DeclarationExpression(type, designation); + } + private ExpressionSyntax ParseThrowExpression() { var throwToken = this.EatToken(SyntaxKind.ThrowKeyword); @@ -9597,6 +9428,10 @@ private ExpressionSyntax ParseTerm(Precedence precedence) { expr = this.ParseLambdaExpression(); } + else if (this.IsPossibleDeconstructionLeft(precedence)) + { + expr = ParseDeclarationExpression(ParseTypeMode.Normal, MessageID.IDS_FeatureTuples); + } else { expr = this.ParseAliasQualifiedName(NameOptions.InExpression); @@ -9670,6 +9505,74 @@ private ExpressionSyntax ParseTerm(Precedence precedence) return this.ParsePostFixExpression(expr); } + /// + /// Returns true if... + /// 1. The precedence is less than or equal to Assignment, and + /// 2. The current token is the identifier var or a predefined type, and + /// 3. it is followed by (, and + /// 4. that ( begins a valid parenthesized designation, and + /// 5. the token following that designation is = + /// + private bool IsPossibleDeconstructionLeft(Precedence precedence) + { + if (precedence > Precedence.Assignment || !(this.CurrentToken.IsVar() || IsPredefinedType(this.CurrentToken.Kind))) + { + return false; + } + + var resetPoint = this.GetResetPoint(); + try + { + this.EatToken(); // `var` + return + this.CurrentToken.Kind == SyntaxKind.OpenParenToken && ScanDesignator() && + this.CurrentToken.Kind == SyntaxKind.EqualsToken; + } + finally + { + // Restore current token index + this.Reset(ref resetPoint); + this.Release(ref resetPoint); + } + } + + private bool ScanDesignator() + { + switch (this.CurrentToken.Kind) + { + case SyntaxKind.IdentifierToken: + if (!IsTrueIdentifier()) + { + goto default; + } + + this.EatToken(); // eat the identifier + return true; + case SyntaxKind.OpenParenToken: + while (true) + { + this.EatToken(); // eat the open paren or comma + if (!ScanDesignator()) + { + return false; + } + + switch (this.CurrentToken.Kind) + { + case SyntaxKind.CommaToken: + continue; + case SyntaxKind.CloseParenToken: + this.EatToken(); // eat the close paren + return true; + default: + return false; + } + } + default: + return false; + } + } + private bool IsPossibleLambdaExpression(Precedence precedence) { if (precedence <= Precedence.Lambda && this.PeekToken(1).Kind == SyntaxKind.EqualsGreaterThanToken) @@ -9838,8 +9741,22 @@ private void ParseArgumentList( SyntaxKind openKind, SyntaxKind closeKind) { + Debug.Assert(openKind == SyntaxKind.OpenParenToken || openKind == SyntaxKind.OpenBracketToken); + Debug.Assert(closeKind == SyntaxKind.CloseParenToken || closeKind == SyntaxKind.CloseBracketToken); + Debug.Assert((openKind == SyntaxKind.OpenParenToken) == (closeKind == SyntaxKind.CloseParenToken)); bool isIndexer = openKind == SyntaxKind.OpenBracketToken; - var open = this.EatToken(openKind); + + if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken || + this.CurrentToken.Kind == SyntaxKind.OpenBracketToken) + { + // convert `[` into `(` or vice versa for error recovery + openToken = this.EatTokenAsKind(openKind); + } + else + { + openToken = this.EatToken(openKind); + } + var saveTerm = _termState; _termState |= TerminatorState.IsEndOfArgumentList; @@ -9862,7 +9779,9 @@ private void ParseArgumentList( // additional arguments while (true) { - if (this.CurrentToken.Kind == closeKind || this.CurrentToken.Kind == SyntaxKind.SemicolonToken) + if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken || + this.CurrentToken.Kind == SyntaxKind.CloseBracketToken || + this.CurrentToken.Kind == SyntaxKind.SemicolonToken) { break; } @@ -9872,13 +9791,13 @@ private void ParseArgumentList( list.Add(this.ParseArgumentExpression(isIndexer)); continue; } - else if (this.SkipBadArgumentListTokens(ref open, list, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort) + else if (this.SkipBadArgumentListTokens(ref openToken, list, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort) { break; } } } - else if (this.SkipBadArgumentListTokens(ref open, list, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue) + else if (this.SkipBadArgumentListTokens(ref openToken, list, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue) { goto tryAgain; } @@ -9898,8 +9817,17 @@ private void ParseArgumentList( _termState = saveTerm; - openToken = open; - closeToken = this.EatToken(closeKind); + if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken || + this.CurrentToken.Kind == SyntaxKind.CloseBracketToken) + { + // convert `]` into `)` or vice versa for error recovery + closeToken = this.EatTokenAsKind(closeKind); + } + else + { + closeToken = this.EatToken(closeKind); + } + arguments = list.ToList(); } finally @@ -9971,22 +9899,14 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) // However, we actually do support ref indexing of indexed properties in COM interop // scenarios, and when indexing an object of static type "dynamic". So we enforce // that the ref/out of the argument must match the parameter when binding the argument list. - if (refOrOutKeyword?.Kind == SyntaxKind.OutKeyword && IsPossibleOutVarDeclaration()) - { - TypeSyntax typeSyntax = ParseType(); - var designation = CheckFeatureAvailability(ParseSimpleDesignation(), MessageID.IDS_FeatureOutVar); - expression = _syntaxFactory.DeclarationExpression( - typeSyntax, - designation); - } - else - { - expression = this.ParseSubExpression(Precedence.Expression); - if (refOrOutKeyword != null) - { - expression = CheckValidLvalue(expression); - } + expression = (refOrOutKeyword?.Kind == SyntaxKind.OutKeyword) + ? ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureOutVar, permitTupleDesignation: false) + : ParseSubExpression(Precedence.Expression); + + if (refOrOutKeyword != null && expression.Kind != SyntaxKind.DeclarationExpression) + { + expression = CheckValidLvalue(expression); } } @@ -10091,7 +10011,7 @@ private RefTypeExpressionSyntax ParseRefTypeExpression() { var keyword = this.EatToken(); var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expr = this.ParseSubExpression(0); + var expr = this.ParseSubExpression(Precedence.Expression); var closeParen = this.EatToken(SyntaxKind.CloseParenToken); return _syntaxFactory.RefTypeExpression(keyword, openParen, expr, closeParen); @@ -10156,8 +10076,7 @@ private bool ScanParenthesizedImplicitlyTypedLambda(Precedence precedence) } // case 2: ( x ) => - if (this.PeekToken(1).Kind == SyntaxKind.IdentifierToken - && (!this.IsInQuery || !IsTokenQueryContextualKeyword(this.PeekToken(1))) + if (IsTrueIdentifier(this.PeekToken(1)) && this.PeekToken(2).Kind == SyntaxKind.CloseParenToken && this.PeekToken(3).Kind == SyntaxKind.EqualsGreaterThanToken) { @@ -10192,8 +10111,10 @@ private bool ScanExplicitlyTypedLambda(Precedence precedence) var resetPoint = this.GetResetPoint(); try { + bool foundParameterModifier = false; + // do we have the following: - // case 1: ( T x , + // case 1: ( T x , ... ) => // case 2: ( T x ) => // case 3: ( out T x, // case 4: ( ref T x, @@ -10202,55 +10123,62 @@ private bool ScanExplicitlyTypedLambda(Precedence precedence) // // if so then parse it as a lambda - // Advance past the open paren. - this.EatToken(); + // Note: in the first two cases, we cannot distinguish a lambda from a tuple expression + // containing declaration expressions, so we scan forwards to the `=>` so we know for sure. - // Eat 'out' or 'ref' for cases [3, 6] - if (this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword) + while (true) { + // Advance past the open paren or comma. this.EatToken(); - } - // NOTE: if we see "out" or ref" and part of cases 3,4,5,6 followed by EOF, we'll parse as a lambda. - if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken) - { - return true; - } + // Eat 'out' or 'ref' for cases [3, 6]. Even though not allowed in a lambda, + // we treat `params` similarly for better error recovery. + switch (this.CurrentToken.Kind) + { + case SyntaxKind.RefKeyword: + case SyntaxKind.OutKeyword: + case SyntaxKind.ParamsKeyword: + this.EatToken(); + foundParameterModifier = true; + break; + } - // NOTE: advances CurrentToken - if (this.ScanType() == ScanTypeFlags.NotType) - { - return false; - } + if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken) + { + return foundParameterModifier; + } - if (this.CurrentToken.Kind == SyntaxKind.EndOfFileToken) - { - return true; - } + // NOTE: advances CurrentToken + if (this.ScanType() == ScanTypeFlags.NotType) + { + return false; + } - if (!this.IsTrueIdentifier()) - { - return false; - } + if (this.IsTrueIdentifier()) + { + // eat the identifier + this.EatToken(); + } - switch (this.PeekToken(1).Kind) - { - case SyntaxKind.EndOfFileToken: - case SyntaxKind.CommaToken: - return true; + switch (this.CurrentToken.Kind) + { + case SyntaxKind.EndOfFileToken: + return foundParameterModifier; - case SyntaxKind.CloseParenToken: - switch (this.PeekToken(2).Kind) - { - case SyntaxKind.EndOfFileToken: - case SyntaxKind.EqualsGreaterThanToken: + case SyntaxKind.CommaToken: + if (foundParameterModifier) + { return true; + } - default: - return false; - } - default: - return false; + continue; + + case SyntaxKind.CloseParenToken: + return this.PeekToken(1).Kind == SyntaxKind.EqualsGreaterThanToken; + + default: + return false; + } } } finally @@ -10300,7 +10228,7 @@ private ExpressionSyntax ParseCastOrParenExpressionOrLambdaOrTuple(Precedence pr { this.Reset(ref resetPoint); var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expression = this.ParseSubExpression(Precedence.Expression); + var expression = this.ParseExpressionOrDeclaration(ParseTypeMode.FirstElementOfPossibleTupleLiteral, feature: 0, permitTupleDesignation: true); // ( , must be a tuple if (this.CurrentToken.Kind == SyntaxKind.CommaToken) @@ -10310,11 +10238,10 @@ private ExpressionSyntax ParseCastOrParenExpressionOrLambdaOrTuple(Precedence pr } // ( name: - if (expression.Kind == SyntaxKind.IdentifierName && - this.CurrentToken.Kind == SyntaxKind.ColonToken) + if (expression.Kind == SyntaxKind.IdentifierName && this.CurrentToken.Kind == SyntaxKind.ColonToken) { var nameColon = _syntaxFactory.NameColon((IdentifierNameSyntax)expression, EatToken()); - expression = ParseSubExpression(0); + expression = this.ParseExpressionOrDeclaration(ParseTypeMode.FirstElementOfPossibleTupleLiteral, feature: 0, permitTupleDesignation: true); var firstArg = _syntaxFactory.Argument(nameColon, refOrOutKeyword: default(SyntaxToken), expression: expression); return ParseTupleExpressionTail(openParen, firstArg); @@ -10344,13 +10271,11 @@ private TupleExpressionSyntax ParseTupleExpressionTail(SyntaxToken openParen, Ar ArgumentSyntax arg; - var expression = ParseSubExpression(0); - if (expression.Kind == SyntaxKind.IdentifierName && - this.CurrentToken.Kind == SyntaxKind.ColonToken) + var expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); + if (expression.Kind == SyntaxKind.IdentifierName && this.CurrentToken.Kind == SyntaxKind.ColonToken) { var nameColon = _syntaxFactory.NameColon((IdentifierNameSyntax)expression, EatToken()); - expression = ParseSubExpression(0); - + expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); arg = _syntaxFactory.Argument(nameColon, refOrOutKeyword: default(SyntaxToken), expression: expression); } else @@ -10370,7 +10295,6 @@ private TupleExpressionSyntax ParseTupleExpressionTail(SyntaxToken openParen, Ar } result = CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples); - return result; } finally diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index 94238a285c99b..b7048205b6748 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -23,7 +23,7 @@ private CSharpSyntaxNode ParseTypeOrPattern() case SyntaxKind.SemicolonToken: case SyntaxKind.CommaToken: // HACK: for error recovery, we prefer a (missing) type. - return this.ParseType(ParseTypeMode.Pattern); + return this.ParseType(ParseTypeMode.AfterIsOrCase); default: // attempt to disambiguate. break; @@ -37,7 +37,7 @@ private CSharpSyntaxNode ParseTypeOrPattern() var resetPoint = this.GetResetPoint(); try { - TypeSyntax type = this.ParseType(ParseTypeMode.Pattern); + TypeSyntax type = this.ParseType(ParseTypeMode.AfterIsOrCase); tk = this.CurrentToken.ContextualKind; if (!type.IsMissing) @@ -102,6 +102,148 @@ private PatternSyntax ParsePattern() return _syntaxFactory.ConstantPattern((ExpressionSyntax)node); } + // + // Parse an expression where a declaration expression would be permitted. This is suitable for use after + // the `out` keyword in an argument list, or in the elements of a tuple literal (because they may + // be on the left-hand-side of a deconstruction). The first element of a tuple is handled slightly + // differently, as we check for the comma before concluding that the identifier should cause a + // disambiguation. For example, for the input `(A < B , C > D)`, we treat this as a tuple with + // two elements, because if we considered the `A` to be a type, it wouldn't be a tuple at + // all. Since we don't have such a thing as a one-element tuple (even for deconstruction), the + // absence of the comma after the `D` means we don't treat the `D` as contributing to the + // disambiguation of the expression/type. More formally, ... + // + // If a sequence of tokens can be parsed(in context) as a* simple-name* (§7.6.3), *member-access* (§7.6.5), + // or* pointer-member-access* (§18.5.2) ending with a* type-argument-list* (§4.4.1), the token immediately + // following the closing `>` token is examined, to see if it is + // - One of `( ) ] } : ; , . ? == != | ^ && || & [`; or + // - One of the relational operators `< > <= >= is as`; or + // - A contextual query keyword appearing inside a query expression; or + // - In certain contexts, we treat *identifier* as a disambiguating token.Those contexts are where the + // sequence of tokens being disambiguated is immediately preceded by one of the keywords `is`, `case` + // or `out`, or arises while parsing the first element of a tuple literal(in which case the tokens are + // preceded by `(` or `:` and the identifier is followed by a `,`) or a subsequent element of a tuple literal. + // + // If the following token is among this list, or an identifier in such a context, then the *type-argument-list* is + // retained as part of the *simple-name*, *member-access* or *pointer-member-access* and any other possible parse + // of the sequence of tokens is discarded.Otherwise, the *type-argument-list* is not considered to be part of the + // *simple-name*, *member-access* or *pointer-member-access*, even if there is no other possible parse of the + // sequence of tokens.Note that these rules are not applied when parsing a *type-argument-list* in a *namespace-or-type-name* (§3.8). + // + // See also ScanTypeArgumentList where these disambiguation rules are encoded. + // + private ExpressionSyntax ParseExpressionOrDeclaration(ParseTypeMode mode, MessageID feature, bool permitTupleDesignation) + { + return IsPossibleDeclarationExpression(mode, permitTupleDesignation) + ? this.ParseDeclarationExpression(mode, feature) + : this.ParseSubExpression(Precedence.Expression); + } + + private bool IsPossibleDeclarationExpression(ParseTypeMode mode, bool permitTupleDesignation) + { + if (this.IsInAsync && this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword) + { + // can't be a declaration expression. + return false; + } + + var resetPoint = this.GetResetPoint(); + try + { + bool typeIsVar = IsVarType(); + SyntaxToken lastTokenOfType; + switch (ScanType(out lastTokenOfType)) + { + case ScanTypeFlags.PointerOrMultiplication: + if (mode == ParseTypeMode.FirstElementOfPossibleTupleLiteral || mode == ParseTypeMode.AfterTupleComma) + { + // Tuples cannot contain pointer types because pointers may not be generic type arguments. + return false; + } + break; + + case ScanTypeFlags.NotType: + return false; + } + + // check for a designation + if (!ScanDesignation(permitTupleDesignation && (typeIsVar || IsPredefinedType(lastTokenOfType.Kind)))) + { + return false; + } + + return mode != ParseTypeMode.FirstElementOfPossibleTupleLiteral || this.CurrentToken.Kind == SyntaxKind.CommaToken; + } + finally + { + this.Reset(ref resetPoint); + this.Release(ref resetPoint); + } + } + + /// + /// Is the following set of tokens, interpreted as a type, the type `var`? + /// + private bool IsVarType() + { + if (!this.CurrentToken.IsVar()) + { + return false; + } + + switch (this.PeekToken(1).Kind) + { + case SyntaxKind.DotToken: + case SyntaxKind.ColonColonToken: + case SyntaxKind.OpenBracketToken: + case SyntaxKind.AsteriskToken: + case SyntaxKind.QuestionToken: + case SyntaxKind.LessThanToken: + return false; + default: + return true; + } + } + + private bool ScanDesignation(bool permitTuple) + { + switch (this.CurrentToken.Kind) + { + default: + return false; + case SyntaxKind.IdentifierToken: + var result = this.IsTrueIdentifier(); + this.EatToken(); + return result; + case SyntaxKind.OpenParenToken: + if (!permitTuple) + { + return false; + } + + bool sawComma = false; + while (true) + { + this.EatToken(); // consume the `(` or `,` + if (!ScanDesignation(permitTuple: true)) + { + return false; + } + switch (this.CurrentToken.Kind) + { + case SyntaxKind.CloseParenToken: + this.EatToken(); + return sawComma; + case SyntaxKind.CommaToken: + sawComma = true; + continue; + default: + return false; + } + } + } + } + // Priority is the ExpressionSyntax. It might return ExpressionSyntax which might be a constant pattern such as 'case 3:' // All constant expressions are converted to the constant pattern in the switch binder if it is a match statement. // It is used for parsing patterns in the switch cases. It never returns constant pattern! @@ -117,7 +259,7 @@ private CSharpSyntaxNode ParseExpressionOrPattern(bool whenIsKeyword) var resetPoint = this.GetResetPoint(); try { - TypeSyntax type = this.ParseType(ParseTypeMode.Pattern); + TypeSyntax type = this.ParseType(ParseTypeMode.AfterIsOrCase); if (!type.IsMissing) { // X.Y.Z id @@ -147,6 +289,7 @@ private CSharpSyntaxNode ParseExpressionOrPattern(bool whenIsKeyword) // But it still might be a pattern such as (operand is 3) or (operand is nameof(x)) node = this.ParseSubExpression(Precedence.Expression); } + return node; } } diff --git a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs index b6430e8b2f201..f55e18caa6aea 100644 --- a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs @@ -474,6 +474,22 @@ protected SyntaxToken EatToken(SyntaxKind kind) return CreateMissingToken(kind, this.CurrentToken.Kind, reportError: true); } + // Consume a token if it is the right kind. Otherwise skip a token and replace it with one of the correct kind. + protected SyntaxToken EatTokenAsKind(SyntaxKind expected) + { + Debug.Assert(SyntaxFacts.IsAnyToken(expected)); + + var ct = this.CurrentToken; + if (ct.Kind == expected) + { + MoveToNextToken(); + return ct; + } + + var replacement = CreateMissingToken(expected, this.CurrentToken.Kind, reportError: true); + return AddTrailingSkippedSyntax(replacement, this.EatToken()); + } + private SyntaxToken CreateMissingToken(SyntaxKind expected, SyntaxKind actual, bool reportError) { // should we eat the current ParseToken's leading trivia? diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index e8dc19fb0da0d..b5145749ae8f0 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -47,10 +47,10 @@ Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax.WithDesignation(Mi Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax.WithType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken tildeToken, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax.WithExpressionBody(Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax.UnderscoreToken.get -> Microsoft.CodeAnalysis.SyntaxToken -Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax.WithUnderscoreToken(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax.UnderscoreToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax.WithUnderscoreToken(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken forEachKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax variable, Microsoft.CodeAnalysis.SyntaxToken inKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax.Variable.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax @@ -161,7 +161,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.CasePatternSwitchLabel = 9009 -> Micros Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConstantPattern = 9002 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.DeclarationExpression = 9040 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.DeclarationPattern = 9000 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind -Microsoft.CodeAnalysis.CSharp.SyntaxKind.DiscardedDesignation = 9014 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.DiscardDesignation = 9014 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.ForEachVariableStatement = 8929 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.IsPatternExpression = 8657 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.LocalFunctionStatement = 8830 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind @@ -188,7 +188,7 @@ override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCasePatternSwit override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDeclarationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode -override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDiscardedDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDiscardDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitForEachVariableStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitLocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode @@ -214,8 +214,8 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationExpressionSyntax.Accept override Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax.ExpressionBody.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax -override Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void -override Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax.CloseParenToken.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax override Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax.ForEachKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken @@ -276,8 +276,8 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DestructorDeclaration(Microso static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DestructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DestructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken tildeToken, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DestructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken tildeToken, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DestructorDeclarationSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DiscardedDesignation() -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DiscardedDesignation(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DiscardDesignation() -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DiscardDesignation(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ForEachVariableStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax variable, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ForEachVariableStatement(Microsoft.CodeAnalysis.SyntaxToken forEachKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax variable, Microsoft.CodeAnalysis.SyntaxToken inKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax @@ -307,7 +307,7 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCasePatternSwitch virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeclarationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax node) -> void -virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDiscardedDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDiscardDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitForEachVariableStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax node) -> void @@ -324,7 +324,7 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCasePatt virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeclarationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax node) -> TResult -virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDiscardedDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardedDesignationSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDiscardDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardDesignationSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitForEachVariableStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax node) -> TResult diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index c0b15675d591b..9ace2067bc4d8 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -198,7 +198,7 @@ public override void VisitLocal(ILocalSymbol symbol) } } - public override void VisitDiscarded(IDiscardedSymbol symbol) + public override void VisitDiscard(IDiscardSymbol symbol) { if (format.LocalOptions.IncludesOption(SymbolDisplayLocalOptions.IncludeType)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/DiscardedSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/DiscardSymbol.cs similarity index 67% rename from src/Compilers/CSharp/Portable/Symbols/DiscardedSymbol.cs rename to src/Compilers/CSharp/Portable/Symbols/DiscardSymbol.cs index 67c98830ce629..42e2f8a99e023 100644 --- a/src/Compilers/CSharp/Portable/Symbols/DiscardedSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/DiscardSymbol.cs @@ -5,17 +5,17 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal class DiscardedSymbol : Symbol, IDiscardedSymbol + internal class DiscardSymbol : Symbol, IDiscardSymbol { private readonly TypeSymbol _type; - public DiscardedSymbol(TypeSymbol type) + public DiscardSymbol(TypeSymbol type) { Debug.Assert((object)type != null); _type = type; } - ITypeSymbol IDiscardedSymbol.Type => _type; + ITypeSymbol IDiscardSymbol.Type => _type; public TypeSymbol Type => _type; public override Symbol ContainingSymbol => null; @@ -28,18 +28,13 @@ public DiscardedSymbol(TypeSymbol type) public override bool IsSealed => false; public override bool IsStatic => false; public override bool IsVirtual => false; - public override SymbolKind Kind => SymbolKind.Discarded; + public override SymbolKind Kind => SymbolKind.Discard; public override ImmutableArray Locations => ImmutableArray.Empty; internal override ObsoleteAttributeData ObsoleteAttributeData => null; - internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument a) => visitor.VisitDiscarded(this, a); - public override void Accept(SymbolVisitor visitor) => visitor.VisitDiscarded(this); - public override TResult Accept(SymbolVisitor visitor) => visitor.VisitDiscarded(this); - public override void Accept(CSharpSymbolVisitor visitor) => visitor.VisitDiscarded(this); - public override TResult Accept(CSharpSymbolVisitor visitor) => visitor.VisitDiscarded(this); - - // Need to figure out the correct behavior for the following methods. - // Tracked by https://github.com/dotnet/roslyn/issues/15449 - //public override string GetDocumentationCommentId() => TODO; - //public override string GetDocumentationCommentXml(CultureInfo preferredCulture, bool expandIncludes, CancellationToken cancellationToken) => TODO; + internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument a) => visitor.VisitDiscard(this, a); + public override void Accept(SymbolVisitor visitor) => visitor.VisitDiscard(this); + public override TResult Accept(SymbolVisitor visitor) => visitor.VisitDiscard(this); + public override void Accept(CSharpSymbolVisitor visitor) => visitor.VisitDiscard(this); + public override TResult Accept(CSharpSymbolVisitor visitor) => visitor.VisitDiscard(this); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index a8a068e97675c..1f488088e6baa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -658,7 +658,10 @@ protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) { case SyntaxKind.SimpleAssignmentExpression: var assignment = (AssignmentExpressionSyntax)_deconstruction; - _nodeBinder.BindDeconstruction(assignment, assignment.Left, assignment.Right, diagnostics, isDeclaration: true); + Debug.Assert(assignment.IsDeconstruction()); + DeclarationExpressionSyntax declaration = null; + ExpressionSyntax expression = null; + _nodeBinder.BindDeconstruction(assignment, assignment.Left, assignment.Right, diagnostics, ref declaration, ref expression); break; case SyntaxKind.ForEachVariableStatement: @@ -670,9 +673,8 @@ protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) throw ExceptionUtilities.UnexpectedValue(_deconstruction.Kind()); } - TypeSymbol result = this._type; - Debug.Assert((object)result != null); - return result; + Debug.Assert((object)this._type != null); + return this._type; } internal override SyntaxNode ForbiddenZone @@ -682,7 +684,7 @@ internal override SyntaxNode ForbiddenZone switch (_deconstruction.Kind()) { case SyntaxKind.SimpleAssignmentExpression: - return ((AssignmentExpressionSyntax)_deconstruction).Right; + return _deconstruction; case SyntaxKind.ForEachVariableStatement: // There is no forbidden zone for a foreach statement, because the @@ -752,9 +754,29 @@ protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) break; } - Debug.Assert((object)this._type != null); + if ((object)this._type == null) + { + AssertNoOutOrPatternVariable(); + SetType(_nodeBinder.CreateErrorType("var")); + } + return this._type; } + + [Conditional("DEBUG")] + private void AssertNoOutOrPatternVariable() + { + var parent = this._typeSyntax.Parent; + + if (parent?.Kind() == SyntaxKind.DeclarationExpression && ((DeclarationExpressionSyntax)parent).IsOutVarDeclaration()) + { + Debug.Assert(false); + } + else if (parent?.Kind() == SyntaxKind.DeclarationPattern) + { + Debug.Assert(false); + } + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index b1ecd27a13721..a92700a2a7b5b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -465,7 +465,7 @@ public bool CanBeReferencedByName case SymbolKind.Assembly: case SymbolKind.DynamicType: case SymbolKind.NetModule: - case SymbolKind.Discarded: + case SymbolKind.Discard: return false; default: diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor.cs index 881f9fd1ff115..3cb79a3065c5e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor.cs @@ -38,7 +38,7 @@ public virtual void VisitDynamicType(DynamicTypeSymbol symbol) DefaultVisit(symbol); } - public virtual void VisitDiscarded(DiscardedSymbol symbol) + public virtual void VisitDiscard(DiscardSymbol symbol) { DefaultVisit(symbol); } diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`1.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`1.cs index d331c1698b94b..1ac5bc61312fa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`1.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`1.cs @@ -38,7 +38,7 @@ public virtual TResult VisitDynamicType(DynamicTypeSymbol symbol) return DefaultVisit(symbol); } - public virtual TResult VisitDiscarded(DiscardedSymbol symbol) + public virtual TResult VisitDiscard(DiscardSymbol symbol) { return DefaultVisit(symbol); } diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`2.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`2.cs index a6096e3005798..4ed2cc4600954 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`2.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolVisitor`2.cs @@ -154,13 +154,13 @@ public virtual TResult VisitDynamicType(DynamicTypeSymbol symbol, TArgument argu } /// - /// Called when visiting a ; Override this with specific + /// Called when visiting a ; Override this with specific /// implementation; Calling if it's not overridden /// /// The visited symbol /// Additional argument /// - public virtual TResult VisitDiscarded(DiscardedSymbol symbol, TArgument argument) + public virtual TResult VisitDiscard(DiscardSymbol symbol, TArgument argument) { return DefaultVisit(symbol, argument); } diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs new file mode 100644 index 0000000000000..57b762b76a7da --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax +{ + internal abstract partial class TypeSyntax + { + public bool IsVar + { + get + { + var ts = this as IdentifierNameSyntax; + return ts != null && ts.Identifier.ToString() == "var"; + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 88e4777a1e663..272d8a937014d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1714,7 +1714,7 @@ - + @@ -1851,8 +1851,8 @@ - - + + @@ -2118,8 +2118,14 @@ - - + + + The variable(s) of the loop. In correct code this is a tuple + literal, declaration expression with a tuple designator, or + a wildcard syntax in the form of a simple identifier. In broken + code it could be something else. + + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs index 9d41b0ed65329..4b92c4b2ff624 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs @@ -207,54 +207,24 @@ internal static SyntaxNode SkipParens(this SyntaxNode expression) } /// - /// Is this expression composed only of declaration expressions and discards nested in tuple expressions? + /// Returns true if the expression on the left-hand-side of an assignment causes the assignment to be a deconstruction. /// - private static bool IsDeconstructionDeclarationLeft(this ExpressionSyntax self) + internal static bool IsDeconstructionLeft(this ExpressionSyntax node) { - switch (self.Kind()) - { - case SyntaxKind.DeclarationExpression: - return true; - case SyntaxKind.TupleExpression: - var tuple = (TupleExpressionSyntax)self; - return tuple.Arguments.All(a => IsDeconstructionDeclarationLeft(a.Expression)); - case SyntaxKind.IdentifierName: - // Underscore is the only expression that is not clearly a declaration that we tolerate for now - var identifier = (IdentifierNameSyntax)self; - return identifier.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken; - default: - return false; - } - } - - /// - /// Returns true if the expression is composed only of nested tuple, declaration expressions and discards. - /// - internal static bool IsDeconstructionDeclarationLeft(this Syntax.InternalSyntax.ExpressionSyntax node) - { - switch (node.Kind) + switch (node.Kind()) { case SyntaxKind.TupleExpression: - var arguments = ((Syntax.InternalSyntax.TupleExpressionSyntax)node).Arguments; - for (int i = 0; i < arguments.Count; i++) - { - if (!IsDeconstructionDeclarationLeft(arguments[i].Expression)) return false; - } - return true; case SyntaxKind.DeclarationExpression: - return true; - case SyntaxKind.IdentifierName: - // Underscore is the only expression that is not clearly a declaration that we tolerate for now - return node.RawContextualKind == (int)SyntaxKind.UnderscoreToken; + return ((DeclarationExpressionSyntax)node).Designation.Kind() == SyntaxKind.ParenthesizedVariableDesignation; default: return false; } } - internal static bool IsDeconstructionDeclaration(this AssignmentExpressionSyntax self) + internal static bool IsDeconstruction(this AssignmentExpressionSyntax self) { - return self.Left.IsDeconstructionDeclarationLeft(); + return self.Left.IsDeconstructionLeft(); } private static bool IsInContextWhichNeedsDynamicAttribute(CSharpSyntaxNode node) @@ -406,5 +376,12 @@ internal static CSharpSyntaxNode GetContainingDeconstruction(this ExpressionSynt } } } + + internal static bool IsOutVarDeclaration(this DeclarationExpressionSyntax p) + { + return p.Designation.Kind() == SyntaxKind.SingleVariableDesignation + && p.Parent?.Kind() == SyntaxKind.Argument + && ((ArgumentSyntax)p.Parent).RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword; + } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index daf0e6944f132..55677ff230ebe 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -552,7 +552,7 @@ public enum SyntaxKind : ushort ConstantPattern = 9002, CasePatternSwitchLabel = 9009, WhenClause = 9013, - DiscardedDesignation = 9014, + DiscardDesignation = 9014, // Kinds between 9000 and 9039 are "reserved" for pattern matching. // Please start with 9040 if you add more kinds below. diff --git a/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs index 3ba6e13589d6b..8cdb0e5c92a96 100644 --- a/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs @@ -8,8 +8,7 @@ public bool IsVar { get { - var ts = this.Green as InternalSyntax.IdentifierNameSyntax; - return ts != null && ts.Identifier.ToString() == "var"; + return ((InternalSyntax.TypeSyntax)this.Green).IsVar; } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs index fe597aa237403..6998d78a30be4 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs @@ -1151,15 +1151,9 @@ public void Deconstruct(out int a, out int b) var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,33): error CS1003: Syntax error, '=>' expected + // (6,19): error CS8185: A declaration is not allowed in this context. // var z = ((var x, int y) = new C()); - Diagnostic(ErrorCode.ERR_SyntaxError, "=").WithArguments("=>", "=").WithLocation(6, 33), - // (6,33): error CS1525: Invalid expression term '=' - // var z = ((var x, int y) = new C()); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(6, 33), - // (6,19): error CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code - // var z = ((var x, int y) = new C()); - Diagnostic(ErrorCode.ERR_TypeVarNotFound, "var").WithLocation(6, 19) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var x").WithLocation(6, 19) ); } @@ -1214,15 +1208,15 @@ public void M3() // (7,22): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. // (int x, var (err1, y)) = (0, new C()); Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "err1").WithArguments("System.ArgIterator").WithLocation(7, 22), - // (8,10): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. + // (8,22): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. // (ArgIterator err2, var err3) = M2(); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "ArgIterator").WithArguments("System.ArgIterator").WithLocation(8, 10), + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "err2").WithArguments("System.ArgIterator").WithLocation(8, 22), // (8,32): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. // (ArgIterator err2, var err3) = M2(); Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "err3").WithArguments("System.ArgIterator").WithLocation(8, 32), - // (9,19): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. + // (9,31): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. // foreach ((ArgIterator err4, var err5) in new[] { M2() }) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "ArgIterator").WithArguments("System.ArgIterator").WithLocation(9, 19), + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "err4").WithArguments("System.ArgIterator").WithLocation(9, 31), // (9,41): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or lambda expressions. // foreach ((ArgIterator err4, var err5) in new[] { M2() }) Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "err5").WithArguments("System.ArgIterator").WithLocation(9, 41), @@ -1260,15 +1254,9 @@ public void Deconstruct(out int a, out int b) var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (7,10): error CS1031: Type expected + // (7,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. // (x, int y) = new C(); - Diagnostic(ErrorCode.ERR_TypeExpected, "x").WithLocation(7, 10), - // (7,10): error CS0128: A local variable or function named 'x' is already defined in this scope - // (x, int y) = new C(); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x").WithArguments("x").WithLocation(7, 10), - // (6,13): warning CS0168: The variable 'x' is declared but never used - // int x; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "x").WithArguments("x").WithLocation(6, 13) + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(x, int y)").WithLocation(7, 9) ); } @@ -1287,42 +1275,12 @@ public static void Main() var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,9): error CS8124: Tuple must contain at least two elements. - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_TupleTooFewElements, "(Alice: var ").WithLocation(6, 9), - // (6,21): error CS1026: ) expected - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_CloseParenExpected, "x").WithLocation(6, 21), - // (6,21): error CS1002: ; expected - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_SemicolonExpected, "x").WithLocation(6, 21), - // (6,22): error CS1002: ; expected - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 22), - // (6,22): error CS1513: } expected - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_RbraceExpected, ",").WithLocation(6, 22), - // (6,34): error CS1002: ; expected - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 34), - // (6,34): error CS1513: } expected - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 34), - // (6,36): error CS1525: Invalid expression term '=' - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(6, 36), - // (6,17): error CS0103: The name 'var' does not exist in the current context - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(6, 17), - // (6,21): error CS0103: The name 'x' does not exist in the current context - // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(6, 21), - // (6,24): warning CS0164: This label has not been referenced + // (6,10): error CS8187: Tuple element names are not permitted on the left of a deconstruction. // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.WRN_UnreferencedLabel, "Bob").WithLocation(6, 24), - // (6,33): warning CS0168: The variable 'y' is declared but never used + Diagnostic(ErrorCode.ERR_TupleElementNamesInDeconstruction, "Alice:").WithLocation(6, 10), + // (6,24): error CS8187: Tuple element names are not permitted on the left of a deconstruction. // (Alice: var x, Bob: int y) = (1, 2); - Diagnostic(ErrorCode.WRN_UnreferencedVar, "y").WithArguments("y").WithLocation(6, 33) + Diagnostic(ErrorCode.ERR_TupleElementNamesInDeconstruction, "Bob:").WithLocation(6, 24) ); } @@ -2431,6 +2389,11 @@ private static void VerifyModelForDeconstructionLocal(SemanticModel model, Singl VerifyModelForDeconstruction(model, decl, LocalDeclarationKind.RegularVariable, references); } + private static void VerifyModelForLocal(SemanticModel model, SingleVariableDesignationSyntax decl, LocalDeclarationKind kind, params IdentifierNameSyntax[] references) + { + VerifyModelForDeconstruction(model, decl, kind, references); + } + private static void VerifyModelForDeconstructionForeach(SemanticModel model, SingleVariableDesignationSyntax decl, params IdentifierNameSyntax[] references) { VerifyModelForDeconstruction(model, decl, LocalDeclarationKind.ForEachIterationVariable, references); @@ -2497,9 +2460,9 @@ private static SingleVariableDesignationSyntax GetDeconstructionVariable(SyntaxT return tree.GetRoot().DescendantNodes().OfType().Where(d => d.Identifier.ValueText == name).Single(); } - private static IEnumerable GetDiscardDesignations(SyntaxTree tree) + private static IEnumerable GetDiscardDesignations(SyntaxTree tree) { - return tree.GetRoot().DescendantNodes().OfType(); + return tree.GetRoot().DescendantNodes().OfType(); } private static IEnumerable GetDiscardIdentifiers(SyntaxTree tree) @@ -2720,40 +2683,47 @@ static void Main() } [Fact] - public void ForWithBadInitializersCannotParse() + public void ForWithVarDeconstructInitializersCanParse() { string source = @" +using System; class C { static void Main() { - for (var (x1, x2) = (1, 2), x1 = 0; ; ) + int x3; + for (var (x1, x2) = (1, 2), x3 = 3; true; ) { + Console.WriteLine(x1); + Console.WriteLine(x2); + Console.WriteLine(x3); + break; } } } "; - var comp = CreateCompilationWithMscorlib(source, references: s_valueTupleRefs); + Action validator = (ModuleSymbol module) => + { + var sourceModule = (SourceModuleSymbol)module; + var compilation = sourceModule.DeclaringCompilation; + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForDeconstructionLocal(model, x2, x2Ref); + }; + + var comp = CompileAndVerify(source, expectedOutput: @"1 +2 +3", additionalRefs: s_valueTupleRefs, sourceSymbolValidator: validator); comp.VerifyDiagnostics( - // (6,35): error CS1002: ; expected - // for (var (x1, x2) = (1, 2), x1 = 0; ; ) - Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 35), - // (6,35): error CS1525: Invalid expression term ',' - // for (var (x1, x2) = (1, 2), x1 = 0; ; ) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(6, 35), - // (6,35): error CS1002: ; expected - // for (var (x1, x2) = (1, 2), x1 = 0; ; ) - Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 35), - // (6,35): error CS1525: Invalid expression term ',' - // for (var (x1, x2) = (1, 2), x1 = 0; ; ) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(6, 35), - // (6,43): error CS1026: ) expected - // for (var (x1, x2) = (1, 2), x1 = 0; ; ) - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(6, 43), - // (6,47): error CS1513: } expected - // for (var (x1, x2) = (1, 2), x1 = 0; ; ) - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 47) + // this is permitted now, as it is just an assignment expression ); } @@ -4220,9 +4190,9 @@ public void DeconstructionInCSharp6Script() var comp = CreateCompilationWithMscorlib45(source, parseOptions: TestOptions.Script.WithLanguageVersion(LanguageVersion.CSharp6), options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( - // (2,1): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. + // (2,5): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. // var (x, y) = (1, 2); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "var (x, y)").WithArguments("tuples", "7").WithLocation(2, 1), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(x, y)").WithArguments("tuples", "7").WithLocation(2, 5), // (2,14): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(1, 2)").WithArguments("tuples", "7").WithLocation(2, 14) @@ -4836,7 +4806,7 @@ static void Main() var model = comp.GetSemanticModel(tree); var discard = GetDiscardIdentifiers(tree).First(); - var symbol = (IDiscardedSymbol)model.GetSymbolInfo(discard).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 + var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 //Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString()); } @@ -4858,9 +4828,9 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( - // (7,10): error CS8184: Cannot reference _ in a deconstruction declaration + // (7,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. // (_, var x) = (1, 2); - Diagnostic(ErrorCode.ERR_MixedDeconstructionDisallowed, "_").WithArguments("_").WithLocation(7, 10) + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(_, var x)").WithLocation(7, 9) ); var tree = comp.SyntaxTrees.First(); @@ -4910,15 +4880,9 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( - // (7,10): error CS1031: Type expected + // (7,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. // (i, var x) = (1, 2); - Diagnostic(ErrorCode.ERR_TypeExpected, "i").WithLocation(7, 10), - // (7,10): error CS0128: A local variable or function named 'i' is already defined in this scope - // (i, var x) = (1, 2); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "i").WithArguments("i").WithLocation(7, 10), - // (6,13): warning CS0168: The variable 'i' is declared but never used - // int i; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "i").WithArguments("i").WithLocation(6, 13) + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(i, var x)").WithLocation(7, 9) ); } @@ -5039,7 +5003,7 @@ static void Main() var discard3 = GetDiscardIdentifiers(tree).First(); Assert.Equal("(_, var x)", discard3.Parent.Parent.ToString()); - var symbol3 = (IDiscardedSymbol)model.GetSymbolInfo(discard3).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 + var symbol3 = (IDiscardSymbol)model.GetSymbolInfo(discard3).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 //Assert.Equal("System.Int32", symbol3.Type.ToTestDisplayString()); } @@ -5056,23 +5020,26 @@ static void Main() } } "; - var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); + Action validator = (ModuleSymbol module) => + { + var sourceModule = (SourceModuleSymbol)module; + var compilation = sourceModule.DeclaringCompilation; + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var refs = GetReferences(tree, "_"); + Assert.Equal(2, refs.Count()); + model.GetTypeInfo(refs.ElementAt(0)); // Assert.Equal("int", model.GetTypeInfo(refs.ElementAt(0)).Type.ToDisplayString()); + model.GetTypeInfo(refs.ElementAt(1)); // Assert.Equal("string", model.GetTypeInfo(refs.ElementAt(1)).Type.ToDisplayString()); + + var tuple = (TupleExpressionSyntax)refs.ElementAt(0).Parent.Parent; + Assert.Equal("(_, _)", tuple.ToString()); + Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(tuple).Type.ToTestDisplayString()); + }; + + var comp = CompileAndVerify(source, expectedOutput: @"2", additionalRefs: s_valueTupleRefs, sourceSymbolValidator: validator); comp.VerifyDiagnostics( - // (6,25): error CS1001: Identifier expected - // foreach ((_, _) in new[] { (1, "hello") }) { System.Console.Write("2"); } - Diagnostic(ErrorCode.ERR_IdentifierExpected, "in").WithLocation(6, 25), - // (6,25): error CS0230: Type and identifier are both required in a foreach statement - // foreach ((_, _) in new[] { (1, "hello") }) { System.Console.Write("2"); } - Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 25), - // (6,19): error CS0246: The type or namespace name '_' could not be found (are you missing a using directive or an assembly reference?) - // foreach ((_, _) in new[] { (1, "hello") }) { System.Console.Write("2"); } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "_").WithArguments("_").WithLocation(6, 19), - // (6,22): error CS0246: The type or namespace name '_' could not be found (are you missing a using directive or an assembly reference?) - // foreach ((_, _) in new[] { (1, "hello") }) { System.Console.Write("2"); } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "_").WithArguments("_").WithLocation(6, 22), - // (6,9): error CS0030: Cannot convert type '(int, string)' to '(_, _)' - // foreach ((_, _) in new[] { (1, "hello") }) { System.Console.Write("2"); } - Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("(int, string)", "(_, _)").WithLocation(6, 9) + // this is permitted now, as it is just an assignment expression ); } @@ -5097,12 +5064,12 @@ static void Main() "; var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( - // (11,30): error CS8184: Cannot reference _ in a deconstruction declaration - // foreach ((var y, _) in new[] { (1, "hello") }) { System.Console.Write("4"); } // error - Diagnostic(ErrorCode.ERR_MixedDeconstructionDisallowed, "_").WithArguments("_").WithLocation(11, 30), // (11,30): error CS0029: Cannot implicitly convert type 'string' to 'int' // foreach ((var y, _) in new[] { (1, "hello") }) { System.Console.Write("4"); } // error Diagnostic(ErrorCode.ERR_NoImplicitConv, "_").WithArguments("string", "int").WithLocation(11, 30), + // (11,22): error CS8186: A foreach loop must declare its iteration variables. + // foreach ((var y, _) in new[] { (1, "hello") }) { System.Console.Write("4"); } // error + Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(var y, _)").WithLocation(11, 22), // (10,17): warning CS0168: The variable '_' is declared but never used // int _; Diagnostic(ErrorCode.WRN_UnreferencedVar, "_").WithArguments("_").WithLocation(10, 17) @@ -5242,18 +5209,18 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); // mixing declaration and expressions isn't supported yet comp.VerifyDiagnostics( - // (6,17): error CS1031: Type expected + // (6,17): error CS0841: Cannot use local variable 'x' before it is declared // (var x, x) = (1, 2); - Diagnostic(ErrorCode.ERR_TypeExpected, "x").WithLocation(6, 17), - // (7,10): error CS1031: Type expected - // (y, var y) = (1, 2); - Diagnostic(ErrorCode.ERR_TypeExpected, "y").WithLocation(7, 10), - // (6,17): error CS0128: A local variable or function named 'x' is already defined in this scope + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x").WithArguments("x").WithLocation(6, 17), + // (6,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. // (var x, x) = (1, 2); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x").WithArguments("x").WithLocation(6, 17), - // (7,17): error CS0128: A local variable or function named 'y' is already defined in this scope + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(var x, x)").WithLocation(6, 9), + // (7,10): error CS0841: Cannot use local variable 'y' before it is declared // (y, var y) = (1, 2); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "y").WithArguments("y").WithLocation(7, 17) + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "y").WithArguments("y").WithLocation(7, 10), + // (7,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. + // (y, var y) = (1, 2); + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(y, var y)").WithLocation(7, 9) ); } @@ -5508,16 +5475,12 @@ static System.Collections.Generic.IEnumerable M() "; var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( - // (6,20): error CS1001: Identifier expected - // foreach (_ in M()) - Diagnostic(ErrorCode.ERR_IdentifierExpected, "in").WithLocation(6, 20), - // (6,20): error CS0230: Type and identifier are both required in a foreach statement + // (6,18): error CS8186: A foreach loop must declare its iteration variables. // foreach (_ in M()) - Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 20), - // (6,18): error CS0246: The type or namespace name '_' could not be found (are you missing a using directive or an assembly reference?) - // foreach (_ in M()) - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "_").WithArguments("_").WithLocation(6, 18) + Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "_").WithLocation(6, 18) ); + // TODO: test SemanticModel.GetTypeInfo on the wildcard here. + // see https://github.com/dotnet/roslyn/issues/15450 } [Fact] @@ -5546,5 +5509,330 @@ static void Main() Diagnostic(ErrorCode.WRN_UnreferencedVar, "_").WithArguments("_").WithLocation(6, 13) ); } + + [Fact] + public void MixedDeconstruction_01() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + var t = (1, 2); + var x = (int x1, int x2) = t; + System.Console.WriteLine(x1); + System.Console.WriteLine(x2); + } +}"; + var compilation = CreateCompilationWithMscorlib(source, options: TestOptions.DebugExe, references: s_valueTupleRefs); + compilation.VerifyDiagnostics( + // (7,18): error CS8185: A declaration is not allowed in this context. + // var x = (int x1, int x2) = t; + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(7, 18) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForDeconstructionLocal(model, x2, x2Ref); + } + + [Fact] + public void MixedDeconstruction_02() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + var t = (1, 2); + int z; + (int x1, z) = t; + System.Console.WriteLine(x1); + } +}"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (8,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. + // (int x1, z) = t; + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(int x1, z)").WithLocation(8, 9) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + } + + [Fact] + public void MixedDeconstruction_03() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + var t = (1, 2); + int z; + for ((int x1, z) = t; ; ) + { + System.Console.WriteLine(x1); + } + } +}"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (8,14): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side. + // for ((int x1, z) = t; ; ) + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(int x1, z)").WithLocation(8, 14) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + } + + [Fact] + public void MixedDeconstruction_04() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + var t = (1, 2); + for (; ; (int x1, int x2) = t) + { + System.Console.WriteLine(x1); + System.Console.WriteLine(x2); + } + } +}"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (7,19): error CS8185: A declaration is not allowed in this context. + // for (; ; (int x1, int x2) = t) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(7, 19), + // (9,38): error CS0165: Use of unassigned local variable 'x1' + // System.Console.WriteLine(x1); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(9, 38), + // (10,38): error CS0165: Use of unassigned local variable 'x2' + // System.Console.WriteLine(x2); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x2").WithArguments("x2").WithLocation(10, 38) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForDeconstructionLocal(model, x2, x2Ref); + } + + [Fact] + public void MixedDeconstruction_05() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) + { + System.Console.WriteLine(x1); + System.Console.WriteLine(x2); + } + } + static int _M; + static ref int M(out int x) { x = 2; return ref _M; } +}"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (6,34): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "args is var x2").WithLocation(6, 34), + // (6,34): error CS0029: Cannot implicitly convert type 'int' to 'bool' + // foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) + Diagnostic(ErrorCode.ERR_NoImplicitConv, "args is var x2").WithArguments("int", "bool").WithLocation(6, 34), + // (6,18): error CS8186: A foreach loop must declare its iteration variables. + // foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) + Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(M(out var x1), args is var x2, _)").WithLocation(6, 18) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + Assert.Equal("int", model.GetTypeInfo(x1Ref).Type.ToDisplayString()); + + model = compilation.GetSemanticModel(tree); + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + Assert.Equal("string[]", model.GetTypeInfo(x2Ref).Type.ToDisplayString()); + + VerifyModelForLocal(model, x1, LocalDeclarationKind.RegularVariable, x1Ref); + VerifyModelForLocal(model, x2, LocalDeclarationKind.PatternVariable, x2Ref); + } + + [Fact] + public void ForeachIntoExpression() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + foreach (M(out var x1) in new[] { 1, 2, 3 }) + { + System.Console.WriteLine(x1); + } + } + static int _M; + static ref int M(out int x) { x = 2; return ref _M; } +}"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (6,32): error CS0230: Type and identifier are both required in a foreach statement + // foreach (M(out var x1) in new[] { 1, 2, 3 }) + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 32) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + Assert.Equal("int", model.GetTypeInfo(x1Ref).Type.ToDisplayString()); + + VerifyModelForLocal(model, x1, LocalDeclarationKind.RegularVariable, x1Ref); + } + + [Fact] + public void MixedDeconstruction_06() + { + string source = @" +class Program +{ + static void Main(string[] args) + { + foreach (M1(M2(out var x1, args is var x2), x1, x2) in new[] {1, 2, 3}) + { + System.Console.WriteLine(x1); + System.Console.WriteLine(x2); + } + } + + static int _M; + static ref int M1(int m2, int x, string[] y) { return ref _M; } + static int M2(out int x, bool b) => x = 2; +}"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (6,61): error CS0230: Type and identifier are both required in a foreach statement + // foreach (M1(M2(out var x1, args is var x2), x1, x2) in new[] {1, 2, 3}) + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 61) + ); + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReferences(tree, "x1"); + Assert.Equal("int", model.GetTypeInfo(x1Ref.First()).Type.ToDisplayString()); + + model = compilation.GetSemanticModel(tree); + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReferences(tree, "x2"); + Assert.Equal("string[]", model.GetTypeInfo(x2Ref.First()).Type.ToDisplayString()); + + VerifyModelForLocal(model, x1, LocalDeclarationKind.RegularVariable, x1Ref.ToArray()); + VerifyModelForLocal(model, x2, LocalDeclarationKind.PatternVariable, x2Ref.ToArray()); + } + + [Fact] + public void IncompleteDeclarationIsSeenAsTupleLiteral() + { + string source = @" +class C +{ + static void Main() + { + (int x1, string x2); + System.Console.WriteLine(x1); + System.Console.WriteLine(x2); + } +} +"; + + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + compilation.VerifyDiagnostics( + // (6,10): error CS8184: A declaration is not allowed in this context. + // (int x1, string x2); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(6, 10), + // (6,18): error CS8184: A declaration is not allowed in this context. + // (int x1, string x2); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "string x2").WithLocation(6, 18), + // (6,9): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement + // (int x1, string x2); + Diagnostic(ErrorCode.ERR_IllegalStatement, "(int x1, string x2)").WithLocation(6, 9), + // (6,14): error CS0165: Use of unassigned local variable 'x1' + // (int x1, string x2); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(6, 14), + // (6,25): error CS0165: Use of unassigned local variable 'x2' + // (int x1, string x2); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x2").WithArguments("x2").WithLocation(6, 25) + ); + + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + Assert.Equal("int", model.GetTypeInfo(x1Ref).Type.ToDisplayString()); + + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + Assert.Equal("string", model.GetTypeInfo(x2Ref).Type.ToDisplayString()); + + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + VerifyModelForDeconstructionLocal(model, x2, x2Ref); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/15614")] + void InvokeVarForLvalueInParens() + { + var source = @" +class Program +{ + public static void Main() + { + (var(x, y)) = 10; + System.Console.WriteLine(z); + } + static int x = 1, y = 2, z = 3; + static ref int var(int x, int y) + { + return ref z; + } +}"; + var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "10"); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 9f42f68741260..f714c534606b6 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -11022,12 +11022,15 @@ static void Main() // (6,47): error CS1525: Invalid expression term 'int' // System.Console.WriteLine(nameof((int, int))); Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 47), - // (7,55): error CS1003: Syntax error, '=>' expected + // (7,42): error CS8185: A declaration is not allowed in this context. // System.Console.WriteLine(nameof((int a, int b))); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(7, 55), - // (7,55): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(7, 42), + // (7,49): error CS8185: A declaration is not allowed in this context. // System.Console.WriteLine(nameof((int a, int b))); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(7, 55) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(7, 49), + // (7,41): error CS8081: Expression does not have a name. + // System.Console.WriteLine(nameof((int a, int b))); + Diagnostic(ErrorCode.ERR_ExpressionHasNoName, "(int a, int b)").WithLocation(7, 41) ); } @@ -13263,15 +13266,21 @@ void Match(object o) var comp = CreateCompilationWithMscorlib(source); comp.VerifyDiagnostics( - // (6,32): error CS1003: Syntax error, '=>' expected + // (6,19): error CS8185: A declaration is not allowed in this context. + // if (o is (int a, int b)) { } + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(6, 19), + // (6,26): error CS8185: A declaration is not allowed in this context. + // if (o is (int a, int b)) { } + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(6, 26), + // (6,18): error CS0150: A constant value is expected // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(6, 32), - // (6,32): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_ConstantExpected, "(int a, int b)").WithLocation(6, 18), + // (6,23): error CS0165: Use of unassigned local variable 'a' // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 32), - // (6,18): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(6, 23), + // (6,30): error CS0165: Use of unassigned local variable 'b' // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(int a, int b)").WithArguments("lambda expression", "object").WithLocation(6, 18) + Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(6, 30) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs index 6d400dcb40ad7..9700a6d991d8b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs @@ -32,7 +32,7 @@ public void DiagnosticAnalyzerAllInOne() syntaxKindsMissing.Add(SyntaxKind.ParenthesizedVariableDesignation); syntaxKindsMissing.Add(SyntaxKind.ForEachVariableStatement); syntaxKindsMissing.Add(SyntaxKind.DeclarationExpression); - syntaxKindsMissing.Add(SyntaxKind.DiscardedDesignation); + syntaxKindsMissing.Add(SyntaxKind.DiscardDesignation); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); CreateCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { analyzer }); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs index f450e2bfa7b5e..2c1a860d8c28a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs @@ -600,9 +600,12 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,24): error CS1525: Invalid expression term '.' + // (6,11): error CS1525: Invalid expression term 'int' // ((int, string)).ToString(); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ".").WithArguments(".").WithLocation(6, 24) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 11), + // (6,16): error CS1525: Invalid expression term 'string' + // ((int, string)).ToString(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "string").WithArguments("string").WithLocation(6, 16) ); } @@ -1322,9 +1325,9 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular6); comp.VerifyDiagnostics( - // (6,9): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. + // (6,13): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. // var (x1, x2) = Pair.Create(1, 2); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "var (x1, x2)").WithArguments("tuples", "7").WithLocation(6, 9), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(x1, x2)").WithArguments("tuples", "7").WithLocation(6, 13), // (7,9): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. // (int x3, int x4) = Pair.Create(1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x3, int x4)").WithArguments("tuples", "7").WithLocation(7, 9), @@ -1416,27 +1419,6 @@ static void Main() ); } - [Fact] - public void IncompleteDeclarationIsSeenAsTupleType() - { - string source = @" -class C -{ - static void Main() - { - (int x1, string x2); - } -} -"; - - var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); - comp.VerifyDiagnostics( - // (6,28): error CS1001: Identifier expected - // (int x1, string x2); - Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(6, 28) - ); - } - [Fact] public void UseBeforeDeclared() { @@ -2428,7 +2410,7 @@ static void Test(int arg1, (byte, byte) arg2) } [Fact] - public void DeclarationCannotBeEmbedded() + public void DeconstructionMayBeEmbedded() { var source = @" class C1 @@ -2442,9 +2424,8 @@ void M() "; var comp = CreateCompilationWithMscorlib(source, references: s_valueTupleRefs); comp.VerifyDiagnostics( - // (7,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var (x, y) = (1, 2); - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var (x, y) = (1, 2);").WithLocation(7, 13) + // this is no longer considered a declaration statement, + // but rather is an assignment expression. So no error. ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs index 4a6a8de715083..ba14092de4163 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs @@ -1149,10 +1149,11 @@ static void Main() CreateCompilationWithMscorlib(source).VerifyDiagnostics( // (6,18): error CS1547: Keyword 'void' cannot be used in this context // foreach (void element in new int[1]) - Diagnostic(ErrorCode.ERR_NoVoidHere, "void"), + Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(6, 18), // (6,9): error CS0030: Cannot convert type 'int' to 'void' // foreach (void element in new int[1]) - Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("int", "void")); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("int", "void").WithLocation(6, 9) + ); } [WorkItem(545123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545123")] @@ -1870,25 +1871,37 @@ public static void Main(string [] args) } "; var boundNode = GetBoundForEachStatement(text, - // (6,16): error CS1001: Identifier expected - Diagnostic(ErrorCode.ERR_IdentifierExpected, ";"), + // (6,13): error CS1525: Invalid expression term 'int' + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 13), // (6,16): error CS1515: 'in' expected - Diagnostic(ErrorCode.ERR_InExpected, ";"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_InExpected, ";").WithLocation(6, 16), + // (6,16): error CS0230: Type and identifier are both required in a foreach statement + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_BadForeachDecl, ";").WithLocation(6, 16), // (6,16): error CS1525: Invalid expression term ';' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 16), // (6,16): error CS1026: ) expected - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(6, 16), // (6,28): error CS1002: ; expected - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 28), // (6,28): error CS1513: } expected - Diagnostic(ErrorCode.ERR_RbraceExpected, ")"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 28), // (6,18): error CS0103: The name 'i' does not exist in the current context - Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i").WithLocation(6, 18), // (6,18): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement - Diagnostic(ErrorCode.ERR_IllegalStatement, "i < 5"), + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_IllegalStatement, "i < 5").WithLocation(6, 18), // (6,25): error CS0103: The name 'i' does not exist in the current context - Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i")); - + // foreach(int; i < 5; i++) + Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i").WithLocation(6, 25) + ); Assert.Null(boundNode.EnumeratorInfoOpt); } @@ -3003,7 +3016,9 @@ private static BoundForEachStatement GetBoundForEachStatement(string text, param comp.VerifyDiagnostics(diagnostics); - var syntaxNode = (ForEachStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.ForEachStatement).AsNode(); + var syntaxNode = + (CommonForEachStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.ForEachStatement).AsNode() ?? + (CommonForEachStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.ForEachVariableStatement).AsNode(); var treeModel = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree); var memberModel = treeModel.GetMemberModel(syntaxNode); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 3581895bdfab9..9898c048d8c94 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -1577,49 +1577,40 @@ static void Main(string[] args) } "; VerifyDiagnostics(source, - // (6,17): error CS1002: ; expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_SemicolonExpected, "operator").WithLocation(6, 17), - // (6,17): error CS1513: } expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_RbraceExpected, "operator").WithLocation(6, 17), - // (6,36): error CS1026: ) expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_CloseParenExpected, "left").WithLocation(6, 36), - // (6,36): error CS1002: ; expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_SemicolonExpected, "left").WithLocation(6, 36), - // (6,40): error CS1002: ; expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 40), - // (6,40): error CS1513: } expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_RbraceExpected, ",").WithLocation(6, 40), - // (6,55): error CS1002: ; expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 55), - // (6,55): error CS1513: } expected - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 55), - // (6,9): error CS0119: 'Program' is a type, which is not valid in the given context - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_BadSKunknown, "Program").WithArguments("Program", "type").WithLocation(6, 9), - // (6,28): error CS0119: 'Program' is a type, which is not valid in the given context - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_BadSKunknown, "Program").WithArguments("Program", "type").WithLocation(6, 28), - // (6,28): error CS0119: 'Program' is a type, which is not valid in the given context - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_BadSKunknown, "Program").WithArguments("Program", "type").WithLocation(6, 28), - // (6,36): error CS0103: The name 'left' does not exist in the current context - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.ERR_NameNotInContext, "left").WithArguments("left").WithLocation(6, 36), - // (8,20): error CS0103: The name 'left' does not exist in the current context - // return left; - Diagnostic(ErrorCode.ERR_NameNotInContext, "left").WithArguments("left").WithLocation(8, 20), - // (6,50): warning CS0168: The variable 'right' is declared but never used - // Program operator +(Program left, Program right) - Diagnostic(ErrorCode.WRN_UnreferencedVar, "right").WithArguments("right").WithLocation(6, 50) - ); + // (6,17): error CS1002: ; expected + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "operator").WithLocation(6, 17), + // (6,17): error CS1513: } expected + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_RbraceExpected, "operator").WithLocation(6, 17), + // (6,56): error CS1002: ; expected + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 56), + // (6,9): error CS0119: 'Program' is a type, which is not valid in the given context + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_BadSKunknown, "Program").WithArguments("Program", "type").WithLocation(6, 9), + // (6,28): error CS8184: A declaration is not allowed in this context. + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "Program left").WithLocation(6, 28), + // (6,42): error CS8184: A declaration is not allowed in this context. + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "Program right").WithLocation(6, 42), + // (6,27): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(Program left, Program right)").WithArguments("System.ValueTuple`2").WithLocation(6, 27), + // (6,26): error CS0023: Operator '+' cannot be applied to operand of type '(Program, Program)' + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_BadUnaryOp, "+(Program left, Program right)").WithArguments("+", "(Program, Program)").WithLocation(6, 26), + // (8,13): error CS0127: Since 'Program.Main(string[])' returns void, a return keyword must not be followed by an object expression + // return left; + Diagnostic(ErrorCode.ERR_RetNoObjectRequired, "return").WithArguments("Program.Main(string[])").WithLocation(8, 13), + // (6,36): error CS0165: Use of unassigned local variable 'left' + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_UseDefViolation, "left").WithArguments("left").WithLocation(6, 36), + // (6,50): error CS0165: Use of unassigned local variable 'right' + // Program operator +(Program left, Program right) + Diagnostic(ErrorCode.ERR_UseDefViolation, "right").WithArguments("right").WithLocation(6, 50) + ); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs index 9b491eeea29a3..7b4403990f34c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs @@ -125,27 +125,30 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (6,35): error CS1003: Syntax error, '=>' expected + // (6,20): error CS8184: A declaration is not allowed in this context. // Test1(out (var x1, var x2)); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(6, 35), - // (6,35): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var x1").WithLocation(6, 20), + // (6,28): error CS8184: A declaration is not allowed in this context. // Test1(out (var x1, var x2)); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 35), - // (6,20): error CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var x2").WithLocation(6, 28), + // (6,19): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // Test1(out (var x1, var x2)); - Diagnostic(ErrorCode.ERR_TypeVarNotFound, "var").WithLocation(6, 20), - // (6,28): error CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(var x1, var x2)").WithArguments("System.ValueTuple`2").WithLocation(6, 19), + // (6,19): error CS1510: A ref or out value must be an assignable variable // Test1(out (var x1, var x2)); - Diagnostic(ErrorCode.ERR_TypeVarNotFound, "var").WithLocation(6, 28), - // (7,34): error CS0103: The name 'x1' does not exist in the current context - // System.Console.WriteLine(x1); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(7, 34), - // (8,34): error CS0103: The name 'x2' does not exist in the current context - // System.Console.WriteLine(x2); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(8, 34) + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(var x1, var x2)").WithLocation(6, 19) ); - Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree); + + var x1Decl = GetDeclaration(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForOutVarWithoutDataFlow(model, x1Decl, x1Ref); + + var x2Decl = GetDeclaration(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForOutVarWithoutDataFlow(model, x2Decl, x2Ref); } [Fact] @@ -172,21 +175,36 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (6,36): error CS1003: Syntax error, '=>' expected + // (6,20): error CS8184: A declaration is not allowed in this context. // Test1(out (int x1, long x2)); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(6, 36), - // (6,36): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(6, 20), + // (6,28): error CS8184: A declaration is not allowed in this context. // Test1(out (int x1, long x2)); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 36), - // (7,34): error CS0103: The name 'x1' does not exist in the current context - // System.Console.WriteLine(x1); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(7, 34), - // (8,34): error CS0103: The name 'x2' does not exist in the current context - // System.Console.WriteLine(x2); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(8, 34) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "long x2").WithLocation(6, 28), + // (6,19): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported + // Test1(out (int x1, long x2)); + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(int x1, long x2)").WithArguments("System.ValueTuple`2").WithLocation(6, 19), + // (6,19): error CS1510: A ref or out value must be an assignable variable + // Test1(out (int x1, long x2)); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(int x1, long x2)").WithLocation(6, 19), + // (6,24): error CS0165: Use of unassigned local variable 'x1' + // Test1(out (int x1, long x2)); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(6, 24), + // (6,33): error CS0165: Use of unassigned local variable 'x2' + // Test1(out (int x1, long x2)); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x2").WithArguments("x2").WithLocation(6, 33) ); - Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree); + + var x1Decl = GetDeclaration(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForOutVarWithoutDataFlow(model, x1Decl, x1Ref); + + var x2Decl = GetDeclaration(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForOutVarWithoutDataFlow(model, x2Decl, x2Ref); } [Fact] @@ -214,33 +232,49 @@ static object Test1(out int x) var compilation = CreateCompilationWithMscorlib(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (6,46): error CS1001: Identifier expected - // Test1(out (int x1, (long x2, byte x3))); - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(6, 46), - // (6,47): error CS1003: Syntax error, '=>' expected + // (6,20): error CS8184: A declaration is not allowed in this context. // Test1(out (int x1, (long x2, byte x3))); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(6, 47), - // (6,47): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(6, 20), + // (6,29): error CS8184: A declaration is not allowed in this context. // Test1(out (int x1, (long x2, byte x3))); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 47), - // (6,28): error CS8137: Cannot define a class or member that utilizes tuples because the compiler required type 'System.Runtime.CompilerServices.TupleElementNamesAttribute' cannot be found. Are you missing a reference? + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "long x2").WithLocation(6, 29), + // (6,38): error CS8184: A declaration is not allowed in this context. // Test1(out (int x1, (long x2, byte x3))); - Diagnostic(ErrorCode.ERR_TupleElementNamesAttributeMissing, "(long x2, byte x3)").WithArguments("System.Runtime.CompilerServices.TupleElementNamesAttribute").WithLocation(6, 28), + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "byte x3").WithLocation(6, 38), // (6,28): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // Test1(out (int x1, (long x2, byte x3))); Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(long x2, byte x3)").WithArguments("System.ValueTuple`2").WithLocation(6, 28), - // (7,34): error CS0103: The name 'x1' does not exist in the current context - // System.Console.WriteLine(x1); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(7, 34), - // (8,34): error CS0103: The name 'x2' does not exist in the current context - // System.Console.WriteLine(x2); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(8, 34), - // (9,34): error CS0103: The name 'x3' does not exist in the current context - // System.Console.WriteLine(x3); - Diagnostic(ErrorCode.ERR_NameNotInContext, "x3").WithArguments("x3").WithLocation(9, 34) + // (6,19): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported + // Test1(out (int x1, (long x2, byte x3))); + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(int x1, (long x2, byte x3))").WithArguments("System.ValueTuple`2").WithLocation(6, 19), + // (6,19): error CS1510: A ref or out value must be an assignable variable + // Test1(out (int x1, (long x2, byte x3))); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(int x1, (long x2, byte x3))").WithLocation(6, 19), + // (6,24): error CS0165: Use of unassigned local variable 'x1' + // Test1(out (int x1, (long x2, byte x3))); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(6, 24), + // (6,34): error CS0165: Use of unassigned local variable 'x2' + // Test1(out (int x1, (long x2, byte x3))); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x2").WithArguments("x2").WithLocation(6, 34), + // (6,43): error CS0165: Use of unassigned local variable 'x3' + // Test1(out (int x1, (long x2, byte x3))); + Diagnostic(ErrorCode.ERR_UseDefViolation, "x3").WithArguments("x3").WithLocation(6, 43) ); - Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree); + + var x1Decl = GetDeclaration(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForOutVarWithoutDataFlow(model, x1Decl, x1Ref); + + var x2Decl = GetDeclaration(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForOutVarWithoutDataFlow(model, x2Decl, x2Ref); + + var x3Decl = GetDeclaration(tree, "x3"); + var x3Ref = GetReference(tree, "x3"); + VerifyModelForOutVarWithoutDataFlow(model, x3Decl, x3Ref); } [Fact] @@ -734,6 +768,17 @@ private static IEnumerable GetReferences(SyntaxTree tree, return tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == name); } + private static IEnumerable GetDeclarations(SyntaxTree tree, string name) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(p => p.Identifier().ValueText == name); + } + + private static DeclarationExpressionSyntax GetDeclaration(SyntaxTree tree, string name) + { + return GetDeclarations(tree, name).Single(); + } + private static DeclarationExpressionSyntax GetOutVarDeclaration(SyntaxTree tree, string name) { return GetOutVarDeclarations(tree, name).Single(); @@ -742,12 +787,12 @@ private static DeclarationExpressionSyntax GetOutVarDeclaration(SyntaxTree tree, private static IEnumerable GetOutVarDeclarations(SyntaxTree tree, string name) { return tree.GetRoot().DescendantNodes().OfType() - .Where(p => IsOutVarDeclaration(p) && p.Identifier().ValueText == name); + .Where(p => p.IsOutVarDeclaration() && p.Identifier().ValueText == name); } - private static IEnumerable GetDiscardDesignations(SyntaxTree tree) + private static IEnumerable GetDiscardDesignations(SyntaxTree tree) { - return tree.GetRoot().DescendantNodes().OfType(); + return tree.GetRoot().DescendantNodes().OfType(); } private static IEnumerable GetDiscardIdentifiers(SyntaxTree tree) @@ -755,17 +800,10 @@ private static IEnumerable GetDiscardIdentifiers(SyntaxTre return tree.GetRoot().DescendantNodes().OfType().Where(i => i.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken); } - private static bool IsOutVarDeclaration(DeclarationExpressionSyntax p) - { - return p.Designation.Kind() == SyntaxKind.SingleVariableDesignation - && p.Parent.Kind() == SyntaxKind.Argument - && ((ArgumentSyntax)p.Parent).RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword; - } - private static IEnumerable GetOutVarDeclarations(SyntaxTree tree) { return tree.GetRoot().DescendantNodes().OfType() - .Where(p => IsOutVarDeclaration(p)); + .Where(p => p.IsOutVarDeclaration()); } [Fact] @@ -997,7 +1035,7 @@ private static void VerifyNotAnOutLocal(SemanticModel model, IdentifierNameSynta var local = (SourceLocalSymbol)symbol; var parent = local.IdentifierToken.Parent; - Assert.Empty(parent.Ancestors().OfType().Where(e => IsOutVarDeclaration(e))); + Assert.Empty(parent.Ancestors().OfType().Where(e => e.IsOutVarDeclaration())); if (parent.Kind() == SyntaxKind.VariableDeclarator) { @@ -8991,9 +9029,6 @@ static bool TakeOutParam(object y, out object x) var compilation = CreateCompilationWithMscorlib45(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (11,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var (d, dd) = (TakeOutParam(true, out var x1), x1); - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var (d, dd) = (TakeOutParam(true, out var x1), x1);").WithLocation(11, 13), // (13,9): error CS0103: The name 'x1' does not exist in the current context // x1++; Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(13, 9) @@ -18057,24 +18092,9 @@ public static void Main() // (10,17): error CS1003: Syntax error, '[' expected // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(10, 17), - // (10,18): error CS1525: Invalid expression term 'out' - // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(10, 18), - // (10,18): error CS1026: ) expected - // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator - Diagnostic(ErrorCode.ERR_CloseParenExpected, "out").WithLocation(10, 18), - // (10,18): error CS1003: Syntax error, ',' expected - // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator - Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",", "out").WithLocation(10, 18), // (10,28): error CS1003: Syntax error, ']' expected // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(10, 28), - // (10,28): error CS1002: ; expected - // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(10, 28), - // (10,28): error CS1513: } expected - // int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(10, 28), // (7,13): error CS0501: 'b(out var)' must declare a body because it is not marked abstract, extern, or partial // int b(out var x2) = null; // parsed as a local function with syntax error Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "b").WithArguments("b(out var)").WithLocation(7, 13), @@ -18570,6 +18590,7 @@ static bool TakeOutParam(object y, out object x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_CloseParenExpected }; @@ -18930,6 +18951,7 @@ static bool TakeOutParam(object y, out bool x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_UseDefViolation }; @@ -19094,6 +19116,7 @@ static bool TakeOutParam(object y, out bool x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_CloseParenExpected }; @@ -19286,6 +19309,7 @@ static bool TakeOutParam(T y, out T x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_ImplicitlyTypedVariableMultipleDeclarator, (int)ErrorCode.ERR_FixedMustInit, @@ -19321,7 +19345,7 @@ static bool TakeOutParam(T y, out T x) // (87,17): warning CS0219: The variable 'y12' is assigned but its value is never used // var y12 = 12; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y12").WithArguments("y12").WithLocation(87, 17), - // (99,54): error CS0128: A local variable named 'x14' is already defined in this scope + // (99,54): error CS0128: A local variable or function named 'x14' is already defined in this scope // TakeOutParam(2, out var x14), Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(99, 54) ); @@ -19445,10 +19469,10 @@ static bool TakeOutParam(T y, out T x) }; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (12,58): error CS0128: A local variable named 'x1' is already defined in this scope + // (12,58): error CS0128: A local variable or function named 'x1' is already defined in this scope // using (var d,x1(Dummy(TakeOutParam(true, out var x1), x1))) Diagnostic(ErrorCode.ERR_LocalDuplicate, "x1").WithArguments("x1").WithLocation(12, 58), - // (20,73): error CS0128: A local variable named 'x2' is already defined in this scope + // (20,73): error CS0128: A local variable or function named 'x2' is already defined in this scope // using (System.IDisposable d,x2(Dummy(TakeOutParam(true, out var x2), x2))) Diagnostic(ErrorCode.ERR_LocalDuplicate, "x2").WithArguments("x2").WithLocation(20, 73) ); @@ -19532,6 +19556,7 @@ static bool TakeOutParam(T y, out T x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_FixedMustInit, (int)ErrorCode.ERR_UseDefViolation, @@ -19699,6 +19724,7 @@ static bool TakeOutParam(object y, out bool x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe.WithAllowUnsafe(true), parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_FixedMustInit, (int)ErrorCode.ERR_UseDefViolation @@ -19732,7 +19758,7 @@ static bool TakeOutParam(object y, out bool x) // (87,17): warning CS0219: The variable 'y12' is assigned but its value is never used // var y12 = 12; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y12").WithArguments("y12").WithLocation(87, 17), - // (99,55): error CS0128: A local variable named 'x14' is already defined in this scope + // (99,55): error CS0128: A local variable or function named 'x14' is already defined in this scope // TakeOutParam(2, out var x14), Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(99, 55) ); @@ -19864,6 +19890,7 @@ static bool TakeOutParam(object y, out bool x) var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe.WithAllowUnsafe(true), parseOptions: TestOptions.Regular); int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_FixedMustInit, (int)ErrorCode.ERR_UseDefViolation, @@ -20318,24 +20345,12 @@ public static void Main() // (8,28): error CS1003: Syntax error, '[' expected // fixed bool d[2], Test3 (out var x3); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(8, 28), - // (8,29): error CS1525: Invalid expression term 'out' - // fixed bool d[2], Test3 (out var x3); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(8, 29), - // (8,29): error CS1026: ) expected - // fixed bool d[2], Test3 (out var x3); - Diagnostic(ErrorCode.ERR_CloseParenExpected, "out").WithLocation(8, 29), - // (8,29): error CS1003: Syntax error, ',' expected - // fixed bool d[2], Test3 (out var x3); - Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",", "out").WithLocation(8, 29), // (8,39): error CS1003: Syntax error, ']' expected // fixed bool d[2], Test3 (out var x3); Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(8, 39), - // (8,39): error CS1003: Syntax error, ',' expected + // (8,33): error CS8185: A declaration is not allowed in this context. // fixed bool d[2], Test3 (out var x3); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",", ")").WithLocation(8, 39), - // (8,28): error CS7092: A fixed buffer may only have one dimension. - // fixed bool d[2], Test3 (out var x3); - Diagnostic(ErrorCode.ERR_FixedBufferTooManyDimensions, "(out var x3").WithLocation(8, 28) + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var x3").WithLocation(8, 33) ); var tree = compilation.SyntaxTrees.Single(); @@ -26442,21 +26457,9 @@ public static bool TakeOutParam(T y, out T x) // (3,10): error CS1003: Syntax error, '[' expected // bool a, b(out var x1); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,11): error CS1525: Invalid expression term 'out' - // bool a, b(out var x1); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(3, 11), - // (3,11): error CS1026: ) expected - // bool a, b(out var x1); - Diagnostic(ErrorCode.ERR_CloseParenExpected, "out").WithLocation(3, 11), - // (3,11): error CS1003: Syntax error, ',' expected - // bool a, b(out var x1); - Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",", "out").WithLocation(3, 11), // (3,21): error CS1003: Syntax error, ']' expected // bool a, b(out var x1); Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 21), - // (3,21): error CS1003: Syntax error, ',' expected - // bool a, b(out var x1); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",", ")").WithLocation(3, 21), // (3,19): error CS8197: Cannot infer the type of implicitly-typed out variable 'x1'. // bool a, b(out var x1); Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedOutVariable, "x1").WithArguments("x1").WithLocation(3, 19) @@ -26489,12 +26492,6 @@ public static bool TakeOutParam(T y, out T x) // (3,10): error CS1003: Syntax error, '[' expected // bool a, b(out var x1); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,11): error CS1525: Invalid expression term 'out' - // bool a, b(out var x1); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(3, 11), - // (3,11): error CS1003: Syntax error, ',' expected - // bool a, b(out var x1); - Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",", "out").WithLocation(3, 11), // (3,21): error CS1003: Syntax error, ']' expected // bool a, b(out var x1); Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 21), @@ -26544,13 +26541,13 @@ public static bool TakeOutParam(T y, out T x) compilation.VerifyDiagnostics( // (3,10): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // bool a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1))").WithLocation(3, 10), + Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1)").WithLocation(3, 10), // (3,10): error CS1003: Syntax error, '[' expected // bool a, b(H.TakeOutParam(1, out var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,41): error CS1003: Syntax error, ']' expected + // (3,40): error CS1003: Syntax error, ']' expected // bool a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 41), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 40), // (2,1): warning CS0164: This label has not been referenced // label: Diagnostic(ErrorCode.WRN_UnreferencedLabel, "label").WithLocation(2, 1) @@ -26579,13 +26576,13 @@ public static bool TakeOutParam(T y, out T x) compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( // (3,10): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // bool a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1))").WithLocation(3, 10), + Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1)").WithLocation(3, 10), // (3,10): error CS1003: Syntax error, '[' expected // bool a, b(H.TakeOutParam(1, out var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,41): error CS1003: Syntax error, ']' expected + // (3,40): error CS1003: Syntax error, ']' expected // bool a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 41), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 40), // (8,13): error CS0103: The name 'x1' does not exist in the current context // H.Dummy(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(8, 13) @@ -26632,13 +26629,13 @@ public static bool TakeOutParam(T y, out T x) compilation.VerifyDiagnostics( // (3,25): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // event System.Action a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1))").WithLocation(3, 25), + Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1)").WithLocation(3, 25), // (3,25): error CS1003: Syntax error, '[' expected // event System.Action a, b(H.TakeOutParam(1, out var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 25), - // (3,56): error CS1003: Syntax error, ']' expected + // (3,55): error CS1003: Syntax error, ']' expected // event System.Action a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 56) + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 55) ); var tree = compilation.SyntaxTrees.Single(); @@ -26664,13 +26661,13 @@ public static bool TakeOutParam(T y, out T x) compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( // (3,25): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // event System.Action a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1))").WithLocation(3, 25), + Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.TakeOutParam(1, out var x1)").WithLocation(3, 25), // (3,25): error CS1003: Syntax error, '[' expected // event System.Action a, b(H.TakeOutParam(1, out var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 25), - // (3,56): error CS1003: Syntax error, ']' expected + // (3,55): error CS1003: Syntax error, ']' expected // event System.Action a, b(H.TakeOutParam(1, out var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 56), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 55), // (8,13): error CS0103: The name 'x1' does not exist in the current context // H.Dummy(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(8, 13) @@ -27614,7 +27611,7 @@ static void Main() //Assert.Equal("", model.GetTypeInfo(declaration2).Type.ToTestDisplayString()); // https://github.com/dotnet/roslyn/issues/15450 var discard3 = GetDiscardIdentifiers(tree).First(); - var symbol = (IDiscardedSymbol)model.GetSymbolInfo(discard3).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 + var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard3).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 //Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString()); comp.VerifyIL("C.Main()", @" @@ -27691,7 +27688,7 @@ static void Main() //Assert.Equal("System.Int32", model.GetTypeInfo(declaration2).Type.ToTestDisplayString()); // https://github.com/dotnet/roslyn/issues/15450 var discard3 = GetDiscardIdentifiers(tree).First(); - var symbol = (IDiscardedSymbol)model.GetSymbolInfo(discard3).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 + var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard3).Symbol; // returns null https://github.com/dotnet/roslyn/issues/15450 //Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString()); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTestBase.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTestBase.cs index e7f3e9474322d..baae92fec289c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTestBase.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTestBase.cs @@ -31,9 +31,9 @@ protected IEnumerable GetPatternDeclarations(Sy return tree.GetRoot().DescendantNodes().OfType().Where(p => p.Parent.Kind() == SyntaxKind.DeclarationPattern); } - protected static IEnumerable GetDiscardDesignations(SyntaxTree tree) + protected static IEnumerable GetDiscardDesignations(SyntaxTree tree) { - return tree.GetRoot().DescendantNodes().OfType(); + return tree.GetRoot().DescendantNodes().OfType(); } protected static IdentifierNameSyntax GetReference(SyntaxTree tree, string name) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Global.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Global.cs index b947f77035c9f..d0f8cd158c4a4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Global.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Global.cs @@ -5704,13 +5704,13 @@ class H compilation.VerifyDiagnostics( // (3,10): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // bool a, b("5948" is var x1); - Diagnostic(ErrorCode.ERR_BadVarDecl, @"(""5948"" is var x1)").WithLocation(3, 10), + Diagnostic(ErrorCode.ERR_BadVarDecl, @"(""5948"" is var x1").WithLocation(3, 10), // (3,10): error CS1003: Syntax error, '[' expected // bool a, b("5948" is var x1); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,28): error CS1003: Syntax error, ']' expected + // (3,27): error CS1003: Syntax error, ']' expected // bool a, b("5948" is var x1); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 28) + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 27) ); var tree = compilation.SyntaxTrees.Single(); @@ -5737,13 +5737,13 @@ class H compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( // (3,10): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // bool a, b("5948" is var x1); - Diagnostic(ErrorCode.ERR_BadVarDecl, @"(""5948"" is var x1)").WithLocation(3, 10), + Diagnostic(ErrorCode.ERR_BadVarDecl, @"(""5948"" is var x1").WithLocation(3, 10), // (3,10): error CS1003: Syntax error, '[' expected // bool a, b("5948" is var x1); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,28): error CS1003: Syntax error, ']' expected + // (3,27): error CS1003: Syntax error, ']' expected // bool a, b("5948" is var x1); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 28), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 27), // (8,13): error CS0103: The name 'x1' does not exist in the current context // H.Dummy(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(8, 13) @@ -5785,13 +5785,13 @@ class H compilation.VerifyDiagnostics( // (3,10): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // bool a, b((1 is var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "((1 is var x1))").WithLocation(3, 10), + Diagnostic(ErrorCode.ERR_BadVarDecl, "((1 is var x1)").WithLocation(3, 10), // (3,10): error CS1003: Syntax error, '[' expected // bool a, b((1 is var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,25): error CS1003: Syntax error, ']' expected + // (3,24): error CS1003: Syntax error, ']' expected // bool a, b((1 is var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 25), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 24), // (2,1): warning CS0164: This label has not been referenced // label: Diagnostic(ErrorCode.WRN_UnreferencedLabel, "label").WithLocation(2, 1) @@ -5820,13 +5820,13 @@ class H compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( // (3,10): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // bool a, b((1 is var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "((1 is var x1))").WithLocation(3, 10), + Diagnostic(ErrorCode.ERR_BadVarDecl, "((1 is var x1)").WithLocation(3, 10), // (3,10): error CS1003: Syntax error, '[' expected // bool a, b((1 is var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 10), - // (3,25): error CS1003: Syntax error, ']' expected + // (3,24): error CS1003: Syntax error, ']' expected // bool a, b((1 is var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 25), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 24), // (8,13): error CS0103: The name 'x1' does not exist in the current context // H.Dummy(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(8, 13) @@ -5868,13 +5868,13 @@ class H compilation.VerifyDiagnostics( // (3,25): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // event System.Action a, b(H.Dummy(1 is var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.Dummy(1 is var x1))").WithLocation(3, 25), + Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.Dummy(1 is var x1)").WithLocation(3, 25), // (3,25): error CS1003: Syntax error, '[' expected // event System.Action a, b(H.Dummy(1 is var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 25), - // (3,47): error CS1003: Syntax error, ']' expected + // (3,46): error CS1003: Syntax error, ']' expected // event System.Action a, b(H.Dummy(1 is var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 47) + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 46) ); var tree = compilation.SyntaxTrees.Single(); @@ -5901,13 +5901,13 @@ class H compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( // (3,25): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) // event System.Action a, b(H.Dummy(1 is var x1)); - Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.Dummy(1 is var x1))").WithLocation(3, 25), + Diagnostic(ErrorCode.ERR_BadVarDecl, "(H.Dummy(1 is var x1)").WithLocation(3, 25), // (3,25): error CS1003: Syntax error, '[' expected // event System.Action a, b(H.Dummy(1 is var x1)); Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(3, 25), - // (3,47): error CS1003: Syntax error, ']' expected + // (3,46): error CS1003: Syntax error, ']' expected // event System.Action a, b(H.Dummy(1 is var x1)); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(3, 47), + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(3, 46), // (8,13): error CS0103: The name 'x1' does not exist in the current context // H.Dummy(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(8, 13) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Scope.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Scope.cs index ca63fa96b011c..9ccc3add59837 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Scope.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Scope.cs @@ -6811,9 +6811,6 @@ void Test1() var compilation = CreateCompilationWithMscorlib45(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); compilation.VerifyDiagnostics( - // (11,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var (d, dd) = ((true is var x1), x1); - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var (d, dd) = ((true is var x1), x1);").WithLocation(11, 13), // (13,9): error CS0103: The name 'x1' does not exist in the current context // x1++; Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(13, 9) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs index 732d1dc9d3888..74d7d9172b96a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs @@ -1477,12 +1477,6 @@ static void M(object o) // (21,24): error CS1525: Invalid expression term 'int' // case (int, int): Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(21, 24), - // (22,32): error CS1003: Syntax error, '=>' expected - // case (int x, int y): - Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>", ":").WithLocation(22, 32), - // (22,32): error CS1525: Invalid expression term ':' - // case (int x, int y): - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ":").WithArguments(":").WithLocation(22, 32), // (23,19): error CS1525: Invalid expression term 'int' // case (int, int) z: Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(23, 19), @@ -1498,9 +1492,15 @@ static void M(object o) // (23,31): error CS1002: ; expected // case (int, int) z: Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(23, 31), - // (24,33): error CS1003: Syntax error, '=>' expected + // (24,33): error CS1003: Syntax error, ':' expected + // case (int a, int b) c: + Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments(":", "").WithLocation(24, 33), + // (24,35): error CS1525: Invalid expression term 'case' // case (int a, int b) c: - Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments("=>", "").WithLocation(24, 33), + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("case").WithLocation(24, 35), + // (24,35): error CS1002: ; expected + // case (int a, int b) c: + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(24, 35), // (25,19): error CS1525: Invalid expression term 'long' // case (long, long) d: Diagnostic(ErrorCode.ERR_InvalidExprTerm, "long").WithArguments("long").WithLocation(25, 19), @@ -1540,12 +1540,6 @@ static void M(object o) // (43,28): error CS1525: Invalid expression term 'int' // if (o is (int, int)) {} Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(43, 28), - // (44,36): error CS1003: Syntax error, '=>' expected - // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(44, 36), - // (44,36): error CS1525: Invalid expression term ')' - // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(44, 36), // (45,23): error CS1525: Invalid expression term 'int' // if (o is (int, int) z)) {} Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(45, 23), @@ -1561,15 +1555,15 @@ static void M(object o) // (45,34): error CS1513: } expected // if (o is (int, int) z)) {} Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(45, 34), - // (46,37): error CS1003: Syntax error, '=>' expected + // (46,37): error CS1026: ) expected // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments("=>", "").WithLocation(46, 37), - // (50,54): error CS1003: Syntax error, '=>' expected - // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("=>", ")").WithLocation(50, 54), - // (50,54): error CS1525: Invalid expression term ')' - // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(50, 54), + Diagnostic(ErrorCode.ERR_CloseParenExpected, "c").WithLocation(46, 37), + // (46,38): error CS1002: ; expected + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(46, 38), + // (46,38): error CS1513: } expected + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(46, 38), // (51,51): error CS1026: ) expected // if (o is (System.Int32, System.Int32) z)) {} Diagnostic(ErrorCode.ERR_CloseParenExpected, "z").WithLocation(51, 51), @@ -1579,21 +1573,39 @@ static void M(object o) // (51,52): error CS1513: } expected // if (o is (System.Int32, System.Int32) z)) {} Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(51, 52), - // (52,55): error CS1003: Syntax error, '=>' expected + // (52,55): error CS1026: ) expected + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_CloseParenExpected, "c").WithLocation(52, 55), + // (52,56): error CS1002: ; expected + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(52, 56), + // (52,56): error CS1513: } expected // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments("=>", "").WithLocation(52, 55), + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(52, 56), // (21,18): error CS0150: A constant value is expected // case (int, int): Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(21, 18), - // (22,18): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + // (22,19): error CS8184: A declaration is not allowed in this context. + // case (int x, int y): + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x").WithLocation(22, 19), + // (22,26): error CS8184: A declaration is not allowed in this context. + // case (int x, int y): + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int y").WithLocation(22, 26), + // (22,18): error CS0150: A constant value is expected // case (int x, int y): - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(int x, int y)").WithArguments("lambda expression", "object").WithLocation(22, 18), + Diagnostic(ErrorCode.ERR_ConstantExpected, "(int x, int y)").WithLocation(22, 18), // (23,18): error CS0150: A constant value is expected // case (int, int) z: Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(23, 18), - // (24,18): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + // (24,19): error CS8184: A declaration is not allowed in this context. // case (int a, int b) c: - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(int a, int b) c").WithArguments("lambda expression", "object").WithLocation(24, 18), + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(24, 19), + // (24,26): error CS8184: A declaration is not allowed in this context. + // case (int a, int b) c: + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(24, 26), + // (24,18): error CS0150: A constant value is expected + // case (int a, int b) c: + Diagnostic(ErrorCode.ERR_ConstantExpected, "(int a, int b)").WithLocation(24, 18), // (25,18): error CS0150: A constant value is expected // case (long, long) d: Diagnostic(ErrorCode.ERR_ConstantExpected, "(long, long)").WithLocation(25, 18), @@ -1618,27 +1630,48 @@ static void M(object o) // (43,22): error CS0150: A constant value is expected // if (o is (int, int)) {} Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(43, 22), - // (44,22): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + // (44,23): error CS8184: A declaration is not allowed in this context. + // if (o is (int x, int y)) {} + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x").WithLocation(44, 23), + // (44,30): error CS8184: A declaration is not allowed in this context. + // if (o is (int x, int y)) {} + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int y").WithLocation(44, 30), + // (44,22): error CS0150: A constant value is expected // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(int x, int y)").WithArguments("lambda expression", "object").WithLocation(44, 22), + Diagnostic(ErrorCode.ERR_ConstantExpected, "(int x, int y)").WithLocation(44, 22), // (45,22): error CS0150: A constant value is expected // if (o is (int, int) z)) {} Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(45, 22), // (45,33): error CS0103: The name 'z' does not exist in the current context // if (o is (int, int) z)) {} Diagnostic(ErrorCode.ERR_NameNotInContext, "z").WithArguments("z").WithLocation(45, 33), - // (46,22): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + // (46,23): error CS8184: A declaration is not allowed in this context. // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(int a, int b) c").WithArguments("lambda expression", "object").WithLocation(46, 22), + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(46, 23), + // (46,30): error CS8184: A declaration is not allowed in this context. + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(46, 30), + // (46,22): error CS0150: A constant value is expected + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_ConstantExpected, "(int a, int b)").WithLocation(46, 22), + // (46,37): error CS0103: The name 'c' does not exist in the current context + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_NameNotInContext, "c").WithArguments("c").WithLocation(46, 37), // (49,23): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(49, 23), // (49,37): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(49, 37), - // (50,22): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + // (50,23): error CS8184: A declaration is not allowed in this context. + // if (o is (System.Int32 x, System.Int32 y)) {} + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 x").WithLocation(50, 23), + // (50,39): error CS8184: A declaration is not allowed in this context. + // if (o is (System.Int32 x, System.Int32 y)) {} + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 y").WithLocation(50, 39), + // (50,22): error CS0150: A constant value is expected // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(System.Int32 x, System.Int32 y)").WithArguments("lambda expression", "object").WithLocation(50, 22), + Diagnostic(ErrorCode.ERR_ConstantExpected, "(System.Int32 x, System.Int32 y)").WithLocation(50, 22), // (51,23): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32) z)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(51, 23), @@ -1648,15 +1681,30 @@ static void M(object o) // (51,51): error CS0103: The name 'z' does not exist in the current context // if (o is (System.Int32, System.Int32) z)) {} Diagnostic(ErrorCode.ERR_NameNotInContext, "z").WithArguments("z").WithLocation(51, 51), - // (52,22): error CS1660: Cannot convert lambda expression to type 'object' because it is not a delegate type + // (52,23): error CS8184: A declaration is not allowed in this context. // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(System.Int32 a, System.Int32 b) c").WithArguments("lambda expression", "object").WithLocation(52, 22), + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 a").WithLocation(52, 23), + // (52,39): error CS8184: A declaration is not allowed in this context. + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 b").WithLocation(52, 39), + // (52,22): error CS0150: A constant value is expected + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_ConstantExpected, "(System.Int32 a, System.Int32 b)").WithLocation(52, 22), + // (52,55): error CS0103: The name 'c' does not exist in the current context + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_NameNotInContext, "c").WithArguments("c").WithLocation(52, 55), // (23,13): error CS0163: Control cannot fall through from one case label ('case (int, int) ') to another // case (int, int) z: Diagnostic(ErrorCode.ERR_SwitchFallThrough, "case (int, int) ").WithArguments("case (int, int) ").WithLocation(23, 13), + // (24,13): error CS0163: Control cannot fall through from one case label ('case (int a, int b) ') to another + // case (int a, int b) c: + Diagnostic(ErrorCode.ERR_SwitchFallThrough, "case (int a, int b) ").WithArguments("case (int a, int b) ").WithLocation(24, 13), // (23,29): warning CS0164: This label has not been referenced // case (int, int) z: Diagnostic(ErrorCode.WRN_UnreferencedLabel, "z").WithLocation(23, 29), + // (24,33): warning CS0164: This label has not been referenced + // case (int a, int b) c: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "c").WithLocation(24, 33), // (25,31): warning CS0164: This label has not been referenced // case (long, long) d: Diagnostic(ErrorCode.WRN_UnreferencedLabel, "d").WithLocation(25, 31), @@ -1671,7 +1719,31 @@ static void M(object o) Diagnostic(ErrorCode.WRN_UnreferencedLabel, "z").WithLocation(37, 47), // (39,47): warning CS0164: This label has not been referenced // case (System.Int64, System.Int64) d: - Diagnostic(ErrorCode.WRN_UnreferencedLabel, "d").WithLocation(39, 47) + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "d").WithLocation(39, 47), + // (44,27): error CS0165: Use of unassigned local variable 'x' + // if (o is (int x, int y)) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(44, 27), + // (44,34): error CS0165: Use of unassigned local variable 'y' + // if (o is (int x, int y)) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(44, 34), + // (46,27): error CS0165: Use of unassigned local variable 'a' + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(46, 27), + // (46,34): error CS0165: Use of unassigned local variable 'b' + // if (o is (int a, int b) c) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(46, 34), + // (50,36): error CS0165: Use of unassigned local variable 'x' + // if (o is (System.Int32 x, System.Int32 y)) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(50, 36), + // (50,52): error CS0165: Use of unassigned local variable 'y' + // if (o is (System.Int32 x, System.Int32 y)) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(50, 52), + // (52,36): error CS0165: Use of unassigned local variable 'a' + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(52, 36), + // (52,52): error CS0165: Use of unassigned local variable 'b' + // if (o is (System.Int32 a, System.Int32 b) c) {} + Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(52, 52) ); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FieldTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FieldTests.cs index f1102e11be9e4..f8310b76c3ff8 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FieldTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/FieldTests.cs @@ -392,7 +392,8 @@ protected virtual void Finalize const () { } Diagnostic(ErrorCode.ERR_FieldCantHaveVoidType, "void").WithLocation(5, 23), // (5,28): warning CS0649: Field 'A.Finalize' is never assigned to, and will always have its default value // protected virtual void Finalize const () { } - Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Finalize").WithArguments("A.Finalize", "").WithLocation(5, 28)); + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Finalize").WithArguments("A.Finalize", "").WithLocation(5, 28) + ); } [WorkItem(543791, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543791")] diff --git a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj index 65c67a9319f70..8f59cb40a7dce 100644 --- a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj +++ b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj @@ -87,6 +87,7 @@ + @@ -149,4 +150,4 @@ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs index 1dede6d0d30b0..f4dac9c616087 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs @@ -1719,26 +1719,14 @@ public void BadConstAwaitInvocationExpressionStatementInSyncContext() { N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "await"); } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); - N(SyntaxKind.BracketedArgumentList); + N(SyntaxKind.IdentifierToken, "foo"); + M(SyntaxKind.BracketedArgumentList); { M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.ParenthesizedExpression); - { - N(SyntaxKind.OpenParenToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - N(SyntaxKind.CloseParenToken); - } - } M(SyntaxKind.CloseBracketToken); } } @@ -1748,6 +1736,7 @@ public void BadConstAwaitInvocationExpressionStatementInSyncContext() N(SyntaxKind.CloseBraceToken); } } + EOF(); } [Fact] @@ -1785,18 +1774,6 @@ public void BadStaticAwaitInvocationExpressionStatementInSyncContext() N(SyntaxKind.BracketedArgumentList); { M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.ParenthesizedExpression); - { - N(SyntaxKind.OpenParenToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - N(SyntaxKind.CloseParenToken); - } - } M(SyntaxKind.CloseBracketToken); } } @@ -1834,24 +1811,19 @@ public void BadLocalDeclarationAndAwaitExpressionInSyncContext() { N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "await"); } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "foo"); N(SyntaxKind.BracketedArgumentList); { M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); + M(SyntaxKind.Argument); { - N(SyntaxKind.ParenthesizedExpression); + M(SyntaxKind.IdentifierName); { - N(SyntaxKind.OpenParenToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - M(SyntaxKind.CloseParenToken); + M(SyntaxKind.IdentifierToken); } } N(SyntaxKind.CloseBracketToken); @@ -1863,6 +1835,7 @@ public void BadLocalDeclarationAndAwaitExpressionInSyncContext() N(SyntaxKind.CloseBraceToken); } } + EOF(); } [Fact] @@ -1874,7 +1847,6 @@ public void BadStatementInSyncContext() int x = 2; } "); - N(SyntaxKind.ParenthesizedLambdaExpression); { N(SyntaxKind.ParameterList); @@ -1892,24 +1864,19 @@ public void BadStatementInSyncContext() { N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "await"); } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "foo"); N(SyntaxKind.BracketedArgumentList); { M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); + M(SyntaxKind.Argument); { - N(SyntaxKind.ParenthesizedExpression); + M(SyntaxKind.IdentifierName); { - N(SyntaxKind.OpenParenToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - M(SyntaxKind.CloseParenToken); + M(SyntaxKind.IdentifierToken); } } N(SyntaxKind.CloseBracketToken); @@ -1928,13 +1895,13 @@ public void BadStatementInSyncContext() } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "x"); N(SyntaxKind.EqualsValueClause); { N(SyntaxKind.EqualsToken); N(SyntaxKind.NumericLiteralExpression); { - N(SyntaxKind.NumericLiteralToken); + N(SyntaxKind.NumericLiteralToken, "2"); } } } @@ -1944,6 +1911,7 @@ public void BadStatementInSyncContext() N(SyntaxKind.CloseBraceToken); } } + EOF(); } #endregion AwaitExpressionStatementInSyncContext diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs new file mode 100644 index 0000000000000..3f2591705dc89 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs @@ -0,0 +1,1182 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Parsing +{ + [CompilerTrait(CompilerFeature.Tuples)] + public class DeclarationExpressionTests : ParsingTests + { + [Fact] + public void NullaboutOutDeclaration() + { + UsingStatement("M(out int? x);"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void NullableTypeTest_01() + { + UsingStatement("if (e is int?) {}"); + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NullableTypeTest_02() + { + UsingStatement("if (e is int ? true : false) {}"); + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.FalseLiteralExpression); + { + N(SyntaxKind.FalseKeyword); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NullableTypeTest_03() + { + UsingStatement("if (e is int? x) {}", + // (1,16): error CS1003: Syntax error, ':' expected + // if (e is int? x) {} + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":", ")").WithLocation(1, 16), + // (1,16): error CS1525: Invalid expression term ')' + // if (e is int? x) {} + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 16) + ); + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NullableTypeTest_04() + { + UsingStatement("if (e is int x ? true : false) {}"); + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.FalseLiteralExpression); + { + N(SyntaxKind.FalseKeyword); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void UnderscoreInOldForeach_01() + { + UsingStatement("foreach (int _ in e) {}"); + N(SyntaxKind.ForEachStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "_"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void UnderscoreInOldForeach_02() + { + UsingStatement("foreach (var _ in e) {}"); + N(SyntaxKind.ForEachStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.IdentifierToken, "_"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_01() + { + UsingStatement("foreach ((var x, var y) in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_02() + { + UsingStatement("foreach ((int x, int y) in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_03() + { + UsingStatement("foreach ((int x, int y) v in e) {}"); + N(SyntaxKind.ForEachStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.IdentifierToken, "v"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_04() + { + // there are semantic, not syntax errors + UsingStatement("foreach ((1, 2) in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_05() + { + UsingStatement("foreach (var (x, y) in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_06() + { + UsingStatement("foreach ((int x, var (y, z)) in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "z"); + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_07() + { + // there are semantic but not syntax errors here. + UsingStatement("foreach ((var (x, y), z) in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_08() + { + UsingStatement("foreach (x in e) {}", + // (1,12): error CS0230: Type and identifier are both required in a foreach statement + // foreach (x in e) {} + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(1, 12) + ); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_09() + { + UsingStatement("foreach (_ in e) {}"); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void NewForeach_10() + { + UsingStatement("foreach (a.b in e) {}", + // (1,14): error CS0230: Type and identifier are both required in a foreach statement + // foreach (a.b in e) {} + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(1, 14) + ); + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TupleOnTheLeft() + { + UsingStatement("(1, 2) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void OutTuple_01() + { + UsingStatement("M(out (1, 2));"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void OutTuple_02() + { + UsingStatement("M(out (x, y));"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void OutTuple_03() + { + UsingStatement("M(out (1, 2).Field);"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Field"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void OutTuple_04() + { + // there are semantic but not syntax errors here. + UsingStatement("M(out (int x, int y));"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void OutTuple_05() + { + // there are semantic but not syntax errors here. + UsingStatement("M(out (var x, var y));"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void NamedTupleOnTheLeft() + { + UsingStatement("(x: 1, y: 2) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void InvokeMethodNamedVar() + { + UsingStatement("var(1, 2) = e;", + // (1,1): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var(1, 2) = e; + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(1, 2)").WithLocation(1, 1) + ); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs index 7ad4b17ac250d..e154845c5535c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs @@ -1797,7 +1797,7 @@ void Foo() { N(SyntaxKind.IntKeyword); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -1812,7 +1812,7 @@ void Foo() { N(SyntaxKind.IdentifierToken, "var"); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -1830,12 +1830,12 @@ void Foo() N(SyntaxKind.ParenthesizedVariableDesignation); { N(SyntaxKind.OpenParenToken); - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } N(SyntaxKind.CommaToken); - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2071,7 +2071,6 @@ struct ValueTuple public ValueTuple(T1 item1, T2 item2) { this.Item1 = item1; this.Item2 = item2; } } }"; - // the duplicate reporting of CS8136 below is due to open issue https://github.com/dotnet/roslyn/issues/12905 CreateCompilationWithMscorlib(source).VerifyDiagnostics( // (6,18): error CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // int (x1, x2) = (1, 2); @@ -2086,6 +2085,7 @@ public void BadTupleElementTypeInDeconstruction02() @" class C { + int x2, x3; void M() { (int x1, x2) = (1, 2); @@ -2102,12 +2102,12 @@ struct ValueTuple } }"; CreateCompilationWithMscorlib(source).VerifyDiagnostics( - // (6,18): error CS1031: Type expected + // (7,9): error CS8183: A deconstruction cannot mix declarations and expressions on the left-hand-side. // (int x1, x2) = (1, 2); - Diagnostic(ErrorCode.ERR_TypeExpected, "x2").WithLocation(6, 18), - // (7,10): error CS1031: Type expected + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(int x1, x2)").WithLocation(7, 9), + // (8,9): error CS8183: A deconstruction cannot mix declarations and expressions on the left-hand-side. // (x3, int x4) = (1, 2); - Diagnostic(ErrorCode.ERR_TypeExpected, "x3").WithLocation(7, 10) + Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(x3, int x4)").WithLocation(8, 9) ); } @@ -2225,7 +2225,7 @@ public void DiscardsInDeconstruction_01() N(SyntaxKind.IdentifierToken, "x"); } N(SyntaxKind.CommaToken); - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2299,7 +2299,7 @@ public void DiscardsInDeconstruction_02() { N(SyntaxKind.IdentifierToken, "var"); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2364,7 +2364,7 @@ public void DiscardsInOut_01() { N(SyntaxKind.IdentifierToken, "var"); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2424,7 +2424,7 @@ public void DiscardsInOut_02() { N(SyntaxKind.IntKeyword); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2481,7 +2481,7 @@ public void DiscardsInPattern_01() { N(SyntaxKind.IntKeyword); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2540,7 +2540,7 @@ public void DiscardsInPattern_02() { N(SyntaxKind.IdentifierToken, "var"); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2603,7 +2603,7 @@ public void DiscardsInPattern_03() { N(SyntaxKind.IntKeyword); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2668,7 +2668,7 @@ public void DiscardsInPattern_04() { N(SyntaxKind.IdentifierToken, "var"); } - N(SyntaxKind.DiscardedDesignation); + N(SyntaxKind.DiscardDesignation); { N(SyntaxKind.UnderscoreToken); } @@ -2691,11 +2691,436 @@ public void DiscardsInPattern_04() EOF(); } - public static void ParseAndValidate(string text, params DiagnosticDescription[] expectedErrors) + [Fact] + public void BadTypeForDeconstruct_00() + { + UsingStatement(@"var (x, y) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_01() + { + UsingStatement(@"var::var (x, y) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_02() + { + UsingStatement(@"var.var (x, y) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_03() + { + UsingStatement(@"var (x, y) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "var"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_04() + { + UsingStatement(@"var[] (x, y) = e;", + // (1,5): error CS0443: Syntax error; value expected + // var[] (x, y) = e; + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(1, 5) + ); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.Argument); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_05() + { + UsingStatement(@"var* (x, y) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MultiplyExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.AsteriskToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_06() { - var parsedTree = ParseWithRoundTripCheck(text); - var actualErrors = parsedTree.GetDiagnostics(); - actualErrors.Verify(expectedErrors); + UsingStatement(@"var? (x, y) = e;", + // (1,16): error CS1003: Syntax error, ':' expected + // var? (x, y) = e; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(":", ";").WithLocation(1, 16), + // (1,16): error CS1525: Invalid expression term ';' + // var? (x, y) = e; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 16) + ); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void BadTypeForDeconstruct_07() + { + UsingStatement(@"var?.var (x, y) = e;"); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs index 79200b22ba77d..002cb29e0ad0a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs @@ -543,54 +543,39 @@ void Foo() { public void HangingLambdaParsing_Bug14167() { var tree = UsingNode(@"(int a, int b Main();"); - N(SyntaxKind.ParenthesizedLambdaExpression); + N(SyntaxKind.TupleExpression); { - N(SyntaxKind.ParameterList); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.Parameter); + N(SyntaxKind.DeclarationExpression); { N(SyntaxKind.PredefinedType); { N(SyntaxKind.IntKeyword); } - N(SyntaxKind.IdentifierToken, "a"); - } - N(SyntaxKind.CommaToken); - N(SyntaxKind.Parameter); - { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.SingleVariableDesignation); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierToken, "a"); } - N(SyntaxKind.IdentifierToken, "b"); } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Parameter); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.IdentifierToken, "Main"); + N(SyntaxKind.IntKeyword); } - M(SyntaxKind.IdentifierToken); - } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Parameter); - { - N(SyntaxKind.TupleType); + N(SyntaxKind.SingleVariableDesignation); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierToken, "b"); } - M(SyntaxKind.IdentifierToken); } - M(SyntaxKind.CloseParenToken); - } - M(SyntaxKind.EqualsGreaterThanToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); } + M(SyntaxKind.CloseParenToken); } EOF(); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs index e13eacab9b2b1..e212e540d8280 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs @@ -49,7 +49,7 @@ void M6() N(SyntaxKind.ClassDeclaration); { N(SyntaxKind.ClassKeyword); - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); N(SyntaxKind.MethodDeclaration); { @@ -78,8 +78,8 @@ void M6() { N(SyntaxKind.IdentifierToken, "L"); } - M(SyntaxKind.SemicolonToken); } + M(SyntaxKind.SemicolonToken); } N(SyntaxKind.ExpressionStatement); { @@ -94,8 +94,8 @@ void M6() { M(SyntaxKind.IdentifierToken); } - M(SyntaxKind.SemicolonToken); } + M(SyntaxKind.SemicolonToken); } N(SyntaxKind.CloseBraceToken); } @@ -126,7 +126,9 @@ void M6() { N(SyntaxKind.LessThanToken); M(SyntaxKind.TypeParameter); - M(SyntaxKind.IdentifierToken); + { + M(SyntaxKind.IdentifierToken); + } M(SyntaxKind.GreaterThanToken); } M(SyntaxKind.ParameterList); @@ -169,7 +171,9 @@ void M6() { N(SyntaxKind.LessThanToken); M(SyntaxKind.TypeParameter); - M(SyntaxKind.IdentifierToken); + { + M(SyntaxKind.IdentifierToken); + } M(SyntaxKind.GreaterThanToken); } M(SyntaxKind.ParameterList); @@ -208,26 +212,14 @@ void M6() N(SyntaxKind.VariableDeclarator); { N(SyntaxKind.IdentifierToken, "L"); - } - N(SyntaxKind.BracketedArgumentList); - { - M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); + M(SyntaxKind.BracketedArgumentList); { - N(SyntaxKind.ParenthesizedExpression); - { - N(SyntaxKind.OpenParenToken); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - M(SyntaxKind.CloseParenToken); - } + M(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); } - M(SyntaxKind.CloseBracketToken); } - M(SyntaxKind.SemicolonToken); } + M(SyntaxKind.SemicolonToken); } N(SyntaxKind.CloseBraceToken); } @@ -252,14 +244,14 @@ void M6() N(SyntaxKind.PredefinedType); { N(SyntaxKind.IntKeyword); - N(SyntaxKind.IdentifierToken, "L"); - N(SyntaxKind.ParameterList); - { - N(SyntaxKind.OpenParenToken); - M(SyntaxKind.CloseParenToken); - } - M(SyntaxKind.SemicolonToken); } + N(SyntaxKind.IdentifierToken, "L"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); } N(SyntaxKind.CloseBraceToken); } @@ -300,8 +292,11 @@ void M6() N(SyntaxKind.CloseBraceToken); } } + N(SyntaxKind.CloseBraceToken); } + N(SyntaxKind.EndOfFileToken); } + EOF(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index c7b64fecb88c7..8a4b55c0b920a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -254,12 +254,13 @@ public static int Main() } "; ParseAndValidate(test, - // (7,22): error CS1001: Identifier expected - // foreach (int in myarray) // CS0230 - Diagnostic(ErrorCode.ERR_IdentifierExpected, "in"), - // (7,22): error CS0230: Type and identifier are both required in a foreach statement - // foreach (int in myarray) // CS0230 - Diagnostic(ErrorCode.ERR_BadForeachDecl, "in")); + // (7,18): error CS1525: Invalid expression term 'int' + // foreach (int in myarray) // CS0230 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(7, 18), + // (7,22): error CS0230: Type and identifier are both required in a foreach statement + // foreach (int in myarray) // CS0230 + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(7, 22) + ); } [Fact] @@ -277,12 +278,10 @@ static void Main(string[] args) } "; ParseAndValidate(test, - // (7,20): error CS1001: Identifier expected - // foreach (x in myarray) { }// Invalid - Diagnostic(ErrorCode.ERR_IdentifierExpected, "in"), - // (7,20): error CS0230: Type and identifier are both required in a foreach statement - // foreach (x in myarray) { }// Invalid - Diagnostic(ErrorCode.ERR_BadForeachDecl, "in")); + // (7,20): error CS0230: Type and identifier are both required in a foreach statement + // foreach (x in myarray) { }// Invalid + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in") + ); } [Fact] @@ -302,12 +301,13 @@ public struct st { } "; ParseAndValidate(test, - // (7,23): error CS1001: Identifier expected - // foreach (st[] in myarray) { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, "in"), - // (7,23): error CS0230: Type and identifier are both required in a foreach statement - // foreach (st[] in myarray) { } - Diagnostic(ErrorCode.ERR_BadForeachDecl, "in")); + // (7,21): error CS0443: Syntax error; value expected + // foreach (st[] in myarray) { } + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(7, 21), + // (7,23): error CS0230: Type and identifier are both required in a foreach statement + // foreach (st[] in myarray) { } + Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(7, 23) + ); } [Fact] @@ -1314,15 +1314,10 @@ public static void Main() { "; ParseAndValidate(test, - // (8,15): error CS1003: Syntax error, ']' expected - // a[); - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")"), - // (8,15): error CS1002: ; expected - // a[); - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"), - // (8,15): error CS1513: } expected - // a[); - Diagnostic(ErrorCode.ERR_RbraceExpected, ")")); + // (8,15): error CS1003: Syntax error, ']' expected + // a[); + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(8, 15) + ); } [Fact] @@ -2181,46 +2176,46 @@ public static A operator () } }"; ParseAndValidate(test, TestOptions.Regular, - // (4,19): error CS1553: Declaration is not valid; use '+ operator (...' instead - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(4, 19), - // (4,23): error CS1003: Syntax error, 'operator' expected - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator", "explicit").WithLocation(4, 23), - // (4,23): error CS1019: Overloadable unary operator expected - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(4, 23), - // (4,32): error CS1003: Syntax error, '(' expected - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(4, 32), - // (4,32): error CS1041: Identifier expected; 'operator' is a keyword - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(4, 32), - // (4,41): error CS8096: Tuple must contain at least two elements. - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_TupleTooFewElements, "()").WithLocation(4, 41), - // (4,43): error CS1001: Identifier expected - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 43), - // (4,43): error CS1003: Syntax error, ',' expected - // public static int explicit operator () - Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",", "{").WithLocation(4, 43), - // (6,18): error CS1026: ) expected - // return 0; - Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(6, 18), - // (6,18): error CS1002: ; expected - // return 0; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 18), - // (8,30): error CS1037: Overloadable operator expected - // public static A operator () - Diagnostic(ErrorCode.ERR_OvlOperatorExpected, "(").WithLocation(8, 30), - // (8,31): error CS1003: Syntax error, '(' expected - // public static A operator () - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("(", ")").WithLocation(8, 31), - // (12,1): error CS1022: Type or namespace definition, or end-of-file expected - // } - Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(12, 1) -); + // (4,19): error CS1553: Declaration is not valid; use '+ operator (...' instead + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(4, 19), + // (4,23): error CS1003: Syntax error, 'operator' expected + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator", "explicit").WithLocation(4, 23), + // (4,23): error CS1019: Overloadable unary operator expected + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(4, 23), + // (4,32): error CS1003: Syntax error, '(' expected + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(4, 32), + // (4,32): error CS1041: Identifier expected; 'operator' is a keyword + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(4, 32), + // (4,41): error CS8124: Tuple must contain at least two elements. + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_TupleTooFewElements, "()").WithLocation(4, 41), + // (4,43): error CS1001: Identifier expected + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 43), + // (4,43): error CS1003: Syntax error, ',' expected + // public static int explicit operator () + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",", "{").WithLocation(4, 43), + // (6,18): error CS1026: ) expected + // return 0; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(6, 18), + // (6,18): error CS1002: ; expected + // return 0; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 18), + // (8,30): error CS1037: Overloadable operator expected + // public static A operator () + Diagnostic(ErrorCode.ERR_OvlOperatorExpected, "(").WithLocation(8, 30), + // (8,31): error CS1003: Syntax error, '(' expected + // public static A operator () + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("(", ")").WithLocation(8, 31), + // (12,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(12, 1) + ); } [Fact] @@ -2919,15 +2914,16 @@ static void Main() "; ParseAndValidate(test, - // (6,18): error CS1031: Type expected - // foreach (1) - Diagnostic(ErrorCode.ERR_TypeExpected, "1"), - // (6,18): error CS1001: Identifier expected - // foreach (1) - Diagnostic(ErrorCode.ERR_IdentifierExpected, "1"), - // (6,18): error CS1515: 'in' expected - // foreach (1) - Diagnostic(ErrorCode.ERR_InExpected, "1")); + // (6,19): error CS1515: 'in' expected + // foreach (1) + Diagnostic(ErrorCode.ERR_InExpected, ")").WithLocation(6, 19), + // (6,19): error CS0230: Type and identifier are both required in a foreach statement + // foreach (1) + Diagnostic(ErrorCode.ERR_BadForeachDecl, ")").WithLocation(6, 19), + // (6,19): error CS1525: Invalid expression term ')' + // foreach (1) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 19) + ); } [WorkItem(906503, "DevDiv/Personal")] @@ -3387,18 +3383,16 @@ class C "; // Extra errors ParseAndValidate(test, - // (4,26): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) - // event System.Action E(); - Diagnostic(ErrorCode.ERR_BadVarDecl, "()"), - // (4,26): error CS1003: Syntax error, '[' expected - // event System.Action E(); - Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "("), - // (4,27): error CS1525: Invalid expression term ')' - // event System.Action E(); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")"), - // (4,28): error CS1003: Syntax error, ']' expected - // event System.Action E(); - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";")); + // (4,26): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) + // event System.Action E(); + Diagnostic(ErrorCode.ERR_BadVarDecl, "(").WithLocation(4, 26), + // (4,26): error CS1003: Syntax error, '[' expected + // event System.Action E(); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(4, 26), + // (4,27): error CS1003: Syntax error, ']' expected + // event System.Action E(); + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(4, 27) + ); } [Fact] @@ -3586,40 +3580,40 @@ public static int Main () "; ParseAndValidate(test, TestOptions.Regular, - // (3,19): error CS1553: Declaration is not valid; use '+ operator (...' instead - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(3, 19), - // (3,23): error CS1003: Syntax error, 'operator' expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator", "implicit").WithLocation(3, 23), - // (3,23): error CS1019: Overloadable unary operator expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(3, 23), - // (3,32): error CS1003: Syntax error, '(' expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(3, 32), - // (3,32): error CS1041: Identifier expected; 'operator' is a keyword - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(3, 32), - // (3,41): error CS8096: Tuple must contain at least two elements. - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_TupleTooFewElements, "(foo f)").WithLocation(3, 41), - // (3,49): error CS1001: Identifier expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(3, 49), - // (3,49): error CS1003: Syntax error, ',' expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",", "{").WithLocation(3, 49), - // (3,61): error CS1026: ) expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_CloseParenExpected, "}").WithLocation(3, 61), - // (3,61): error CS1002: ; expected - // public static int implicit operator (foo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(3, 61), - // (4,1): error CS1022: Type or namespace definition, or end-of-file expected - // } - Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1) -); + // (3,19): error CS1553: Declaration is not valid; use '+ operator (...' instead + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(3, 19), + // (3,23): error CS1003: Syntax error, 'operator' expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator", "implicit").WithLocation(3, 23), + // (3,23): error CS1019: Overloadable unary operator expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(3, 23), + // (3,32): error CS1003: Syntax error, '(' expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(3, 32), + // (3,32): error CS1041: Identifier expected; 'operator' is a keyword + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(3, 32), + // (3,41): error CS8124: Tuple must contain at least two elements. + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_TupleTooFewElements, "(foo f)").WithLocation(3, 41), + // (3,49): error CS1001: Identifier expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(3, 49), + // (3,49): error CS1003: Syntax error, ',' expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",", "{").WithLocation(3, 49), + // (3,61): error CS1026: ) expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_CloseParenExpected, "}").WithLocation(3, 61), + // (3,61): error CS1002: ; expected + // public static int implicit operator (foo f) { return 6; } // Error + Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(3, 61), + // (4,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1) + ); } [Fact, WorkItem(535933, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/535933")] // ? @@ -3676,7 +3670,7 @@ public static int Main () // (4,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1) - ); + ); } [Fact(), WorkItem(526995, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/526995")] @@ -5198,30 +5192,5 @@ public static void Main(string[] args) } #endregion - - #region "Helpers" - - public static void ParseAndValidate(string text, params DiagnosticDescription[] expectedErrors) - { - var parsedTree = ParseWithRoundTripCheck(text); - var actualErrors = parsedTree.GetDiagnostics(); - actualErrors.Verify(expectedErrors); - } - - public static void ParseAndValidate(string text, CSharpParseOptions options, params DiagnosticDescription[] expectedErrors) - { - var parsedTree = ParseWithRoundTripCheck(text, options: options); - var actualErrors = parsedTree.GetDiagnostics(); - actualErrors.Verify(expectedErrors); - } - - public static void ParseAndValidateFirst(string text, DiagnosticDescription expectedFirstError) - { - var parsedTree = ParseWithRoundTripCheck(text); - var actualErrors = parsedTree.GetDiagnostics(); - actualErrors.Take(1).Verify(expectedFirstError); - } - - #endregion } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs index 4bd186e14746b..745f146f4cfb3 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs @@ -4115,8 +4115,12 @@ public void TestCloseParenAfterDoWhileExpressionIndexerStart() Assert.NotNull(ms.Body); Assert.Equal(1, ms.Body.Statements.Count); Assert.Equal(SyntaxKind.DoStatement, ms.Body.Statements[0].Kind()); - Assert.Equal(1, file.Errors().Length); - Assert.Equal((int)ErrorCode.ERR_SyntaxError, file.Errors()[0].Code); + file.Errors().Verify( + // error CS1003: Syntax error, ']' expected + Diagnostic(ErrorCode.ERR_SyntaxError).WithArguments("]", ")").WithLocation(1, 1), + // error CS1026: ) expected + Diagnostic(ErrorCode.ERR_CloseParenExpected).WithLocation(1, 1) + ); } [Fact] @@ -4281,8 +4285,12 @@ public void TestCloseParenAfterForStatementIncrementerStart() Assert.NotNull(ms.Body); Assert.Equal(1, ms.Body.Statements.Count); Assert.Equal(SyntaxKind.ForStatement, ms.Body.Statements[0].Kind()); - Assert.Equal(1, file.Errors().Length); - Assert.Equal((int)ErrorCode.ERR_SyntaxError, file.Errors()[0].Code); + file.Errors().Verify( + // error CS1003: Syntax error, ']' expected + Diagnostic(ErrorCode.ERR_SyntaxError).WithArguments("]", ")").WithLocation(1, 1), + // error CS1026: ) expected + Diagnostic(ErrorCode.ERR_CloseParenExpected).WithLocation(1, 1) + ); } [Fact] @@ -5300,13 +5308,13 @@ public void TestSemicolonAfterLambdaParameter() Assert.NotNull(ds.Declaration.Variables[0].Initializer); Assert.NotEqual(SyntaxKind.None, ds.Declaration.Variables[0].Initializer.EqualsToken.Kind()); Assert.NotNull(ds.Declaration.Variables[0].Initializer.Value); - Assert.Equal(SyntaxKind.ParenthesizedLambdaExpression, ds.Declaration.Variables[0].Initializer.Value.Kind()); - Assert.Equal(5, file.Errors().Length); - Assert.Equal((int)ErrorCode.ERR_TypeExpected, file.Errors()[0].Code); - Assert.Equal((int)ErrorCode.ERR_IdentifierExpected, file.Errors()[1].Code); - Assert.Equal((int)ErrorCode.ERR_CloseParenExpected, file.Errors()[2].Code); - Assert.Equal((int)ErrorCode.ERR_SyntaxError, file.Errors()[3].Code); - Assert.Equal((int)ErrorCode.ERR_InvalidExprTerm, file.Errors()[4].Code); + Assert.Equal(SyntaxKind.TupleExpression, ds.Declaration.Variables[0].Initializer.Value.Kind()); + file.Errors().Verify( + // error CS1525: Invalid expression term ';' + Diagnostic(ErrorCode.ERR_InvalidExprTerm).WithArguments(";").WithLocation(1, 1), + // error CS1026: ) expected + Diagnostic(ErrorCode.ERR_CloseParenExpected).WithLocation(1, 1) + ); } [Fact] @@ -5391,14 +5399,15 @@ public void TestStatementAfterLambdaParameter() Assert.NotNull(ds.Declaration.Variables[0].Initializer); Assert.NotEqual(SyntaxKind.None, ds.Declaration.Variables[0].Initializer.EqualsToken.Kind()); Assert.NotNull(ds.Declaration.Variables[0].Initializer.Value); - Assert.Equal(SyntaxKind.ParenthesizedLambdaExpression, ds.Declaration.Variables[0].Initializer.Value.Kind()); - Assert.Equal(6, file.Errors().Length); - Assert.Equal((int)ErrorCode.ERR_TypeExpected, file.Errors()[0].Code); - Assert.Equal((int)ErrorCode.ERR_IdentifierExpected, file.Errors()[1].Code); - Assert.Equal((int)ErrorCode.ERR_CloseParenExpected, file.Errors()[2].Code); - Assert.Equal((int)ErrorCode.ERR_SyntaxError, file.Errors()[3].Code); - Assert.Equal((int)ErrorCode.ERR_InvalidExprTerm, file.Errors()[4].Code); - Assert.Equal((int)ErrorCode.ERR_SemicolonExpected, file.Errors()[5].Code); + Assert.Equal(SyntaxKind.TupleExpression, ds.Declaration.Variables[0].Initializer.Value.Kind()); + file.Errors().Verify( + // error CS1525: Invalid expression term 'while' + Diagnostic(ErrorCode.ERR_InvalidExprTerm).WithArguments("while").WithLocation(1, 1), + // error CS1026: ) expected + Diagnostic(ErrorCode.ERR_CloseParenExpected).WithLocation(1, 1), + // error CS1002: ; expected + Diagnostic(ErrorCode.ERR_SemicolonExpected).WithLocation(1, 1) + ); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs index fbefc5513c305..9203a177e56eb 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using Xunit; using System.Linq; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -13,6 +14,27 @@ public abstract class ParsingTests : CSharpTestBase { private IEnumerator _treeEnumerator; + public static void ParseAndValidate(string text, params DiagnosticDescription[] expectedErrors) + { + var parsedTree = ParseWithRoundTripCheck(text); + var actualErrors = parsedTree.GetDiagnostics(); + actualErrors.Verify(expectedErrors); + } + + public static void ParseAndValidate(string text, CSharpParseOptions options, params DiagnosticDescription[] expectedErrors) + { + var parsedTree = ParseWithRoundTripCheck(text, options: options); + var actualErrors = parsedTree.GetDiagnostics(); + actualErrors.Verify(expectedErrors); + } + + public static void ParseAndValidateFirst(string text, DiagnosticDescription expectedFirstError) + { + var parsedTree = ParseWithRoundTripCheck(text); + var actualErrors = parsedTree.GetDiagnostics(); + actualErrors.Take(1).Verify(expectedFirstError); + } + protected virtual SyntaxTree ParseTree(string text, CSharpParseOptions options) => SyntaxFactory.ParseSyntaxTree(text, options); public CompilationUnitSyntax ParseFile(string text, CSharpParseOptions parseOptions = null) => @@ -24,6 +46,16 @@ internal CompilationUnitSyntax ParseFileExperimental(string text, MessageID feat protected virtual CSharpSyntaxNode ParseNode(string text, CSharpParseOptions options) => ParseTree(text, options).GetCompilationUnitRoot(); + internal void UsingStatement(string text, params DiagnosticDescription[] expectedErrors) + { + var node = SyntaxFactory.ParseStatement(text); + // we validate the text roundtrips + Assert.Equal(text, node.ToFullString()); + var actualErrors = node.GetDiagnostics(); + actualErrors.Verify(expectedErrors); + UsingNode(node); + } + /// /// Parses given string and initializes a depth-first preorder enumerator. /// @@ -142,13 +174,19 @@ private static IEnumerable EnumerateNodes(CSharpSyntaxNode no [Conditional("PARSING_TESTS_DUMP")] private static void Print(SyntaxNodeOrToken node) { - if (node.Kind() == SyntaxKind.IdentifierToken && !node.IsMissing) - { - Debug.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), node.ToString()); - } - else + switch (node.Kind()) { - Debug.WriteLine("{0}(SyntaxKind.{1});", node.IsMissing ? "M" : "N", node.Kind()); + case SyntaxKind.IdentifierToken: + case SyntaxKind.NumericLiteralToken: + if (node.IsMissing) + { + goto default; + } + Debug.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), node.ToString()); + break; + default: + Debug.WriteLine("{0}(SyntaxKind.{1});", node.IsMissing ? "M" : "N", node.Kind()); + break; } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs index 088be4e4ea8a6..1cbddda687ac8 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs @@ -292,5 +292,245 @@ public void IsPatternPrecedence_3() SyntaxFactory.ParseExpression("A is B < C, D > [ ] E").GetDiagnostics().Verify(); SyntaxFactory.ParseExpression("A < B > C").GetDiagnostics().Verify(); } + + [Fact] + public void QueryContextualPatternVariable_01() + { + SyntaxFactory.ParseExpression("from s in a where s is string where s.Length > 1 select s").GetDiagnostics().Verify(); + SyntaxFactory.ParseExpression("M(out int? x)").GetDiagnostics().Verify(); + } + + [Fact] + public void TypeDisambiguation_01() + { + UsingStatement(@" + var r = from s in a + where s is X // should disambiguate as a type here + where M(s) + select s as X;"); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "r"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.QueryExpression); + { + N(SyntaxKind.FromClause); + { + N(SyntaxKind.FromKeyword); + N(SyntaxKind.IdentifierToken, "s"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.QueryBody); + { + N(SyntaxKind.WhereClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "s"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + N(SyntaxKind.WhereClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "s"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.SelectClause); + { + N(SyntaxKind.SelectKeyword); + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "s"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void TypeDisambiguation_02() + { + UsingStatement(@" + var r = a is X // should disambiguate as a type here + is bool;"); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "r"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.BoolKeyword); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void TypeDisambiguation_03() + { + UsingStatement(@" + var r = a is X // should disambiguate as a type here + > Z;"); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "r"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Z"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/SeparatedSyntaxListParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/SeparatedSyntaxListParsingTests.cs index ba8cd9e95830d..0ae2ee57d487a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/SeparatedSyntaxListParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SeparatedSyntaxListParsingTests.cs @@ -218,14 +218,14 @@ void CheckTypeArguments2() N(SyntaxKind.ClassDeclaration); { N(SyntaxKind.ClassKeyword); - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); N(SyntaxKind.IncompleteMember); { N(SyntaxKind.NewKeyword); N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); @@ -250,7 +250,7 @@ void CheckTypeArguments2() N(SyntaxKind.NewKeyword); N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); @@ -281,13 +281,13 @@ void CheckTypeArguments2() { N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); @@ -303,7 +303,7 @@ void CheckTypeArguments2() } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "a1"); } } N(SyntaxKind.SemicolonToken); @@ -314,13 +314,13 @@ void CheckTypeArguments2() { N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "A"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); @@ -336,7 +336,7 @@ void CheckTypeArguments2() } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "a1"); } } N(SyntaxKind.SemicolonToken); @@ -351,49 +351,47 @@ void CheckTypeArguments2() } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "a1"); N(SyntaxKind.EqualsValueClause); { N(SyntaxKind.EqualsToken); N(SyntaxKind.TypeOfExpression); { N(SyntaxKind.TypeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.GenericName); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.TypeArgumentList); { - N(SyntaxKind.IdentifierToken); - N(SyntaxKind.TypeArgumentList); + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); { - N(SyntaxKind.LessThanToken); - N(SyntaxKind.GenericName); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.TypeArgumentList); { - N(SyntaxKind.IdentifierToken); - N(SyntaxKind.TypeArgumentList); + N(SyntaxKind.LessThanToken); + N(SyntaxKind.OmittedTypeArgument); { - N(SyntaxKind.LessThanToken); - N(SyntaxKind.OmittedTypeArgument); - { - N(SyntaxKind.OmittedTypeArgumentToken); - } - N(SyntaxKind.CommaToken); - N(SyntaxKind.OmittedTypeArgument); - { - N(SyntaxKind.OmittedTypeArgumentToken); - } - N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.OmittedTypeArgumentToken); } + N(SyntaxKind.CommaToken); + N(SyntaxKind.OmittedTypeArgument); + { + N(SyntaxKind.OmittedTypeArgumentToken); + } + N(SyntaxKind.GreaterThanToken); } - N(SyntaxKind.CommaToken); - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.GreaterThanToken); } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); } - N(SyntaxKind.CloseParenToken); } + N(SyntaxKind.CloseParenToken); } } } @@ -410,7 +408,7 @@ void CheckTypeArguments2() } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "a2"); N(SyntaxKind.EqualsValueClause); { N(SyntaxKind.EqualsToken); @@ -418,7 +416,7 @@ void CheckTypeArguments2() { N(SyntaxKind.GenericName); { - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "Swap"); N(SyntaxKind.TypeArgumentList); { N(SyntaxKind.LessThanToken); @@ -460,18 +458,18 @@ void CheckTypeArguments2() N(SyntaxKind.ClassDeclaration); { N(SyntaxKind.ClassKeyword); - N(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "M"); N(SyntaxKind.TypeParameterList); { N(SyntaxKind.LessThanToken); - N(SyntaxKind.TypeParameter); + M(SyntaxKind.TypeParameter); { - N(SyntaxKind.IdentifierToken); + M(SyntaxKind.IdentifierToken); } N(SyntaxKind.CommaToken); - N(SyntaxKind.TypeParameter); + M(SyntaxKind.TypeParameter); { - N(SyntaxKind.IdentifierToken); + M(SyntaxKind.IdentifierToken); } N(SyntaxKind.GreaterThanToken); } @@ -480,6 +478,7 @@ void CheckTypeArguments2() } N(SyntaxKind.EndOfFileToken); } + EOF(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ValueTupleTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ValueTupleTests.cs index 84312dd0dc57c..c52862187d7fd 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ValueTupleTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ValueTupleTests.cs @@ -514,17 +514,5 @@ class Program Diagnostic(ErrorCode.WRN_LowercaseEllSuffix, "l").WithLocation(4, 21) ); } - - #region "Helpers" - - public static void ParseAndValidate(string text, params DiagnosticDescription[] expectedErrors) - { - var parsedTree = ParseWithRoundTripCheck(text); - var actualErrors = parsedTree.GetDiagnostics(); - actualErrors.Verify(expectedErrors); - } - - #endregion "Helpers" - } } diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 93cc50f8f7277..dc98822a6375d 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -28,7 +28,7 @@ - + @@ -798,4 +798,4 @@ - + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 2caa4a267c1e9..40e303269b929 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -83,8 +83,8 @@ Microsoft.CodeAnalysis.Emit.EmitOptions.WithInstrumentationKinds(System.Collecti Microsoft.CodeAnalysis.Emit.InstrumentationKind Microsoft.CodeAnalysis.Emit.InstrumentationKind.None = 0 -> Microsoft.CodeAnalysis.Emit.InstrumentationKind Microsoft.CodeAnalysis.Emit.InstrumentationKind.TestCoverage = 1 -> Microsoft.CodeAnalysis.Emit.InstrumentationKind -Microsoft.CodeAnalysis.IDiscardedSymbol -Microsoft.CodeAnalysis.IDiscardedSymbol.Type.get -> Microsoft.CodeAnalysis.ITypeSymbol +Microsoft.CodeAnalysis.IDiscardSymbol +Microsoft.CodeAnalysis.IDiscardSymbol.Type.get -> Microsoft.CodeAnalysis.ITypeSymbol Microsoft.CodeAnalysis.IMethodSymbol.ReturnsByRef.get -> bool Microsoft.CodeAnalysis.INamedTypeSymbol.TupleElementNames.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.INamedTypeSymbol.TupleElementTypes.get -> System.Collections.Immutable.ImmutableArray @@ -717,7 +717,7 @@ Microsoft.CodeAnalysis.SymbolDisplayFormat.RemoveGenericsOptions(Microsoft.CodeA Microsoft.CodeAnalysis.SymbolDisplayFormat.RemoveLocalOptions(Microsoft.CodeAnalysis.SymbolDisplayLocalOptions options) -> Microsoft.CodeAnalysis.SymbolDisplayFormat Microsoft.CodeAnalysis.SymbolDisplayFormat.RemoveMiscellaneousOptions(Microsoft.CodeAnalysis.SymbolDisplayMiscellaneousOptions options) -> Microsoft.CodeAnalysis.SymbolDisplayFormat Microsoft.CodeAnalysis.SymbolDisplayMemberOptions.IncludeRef = 128 -> Microsoft.CodeAnalysis.SymbolDisplayMemberOptions -Microsoft.CodeAnalysis.SymbolKind.Discarded = 19 -> Microsoft.CodeAnalysis.SymbolKind +Microsoft.CodeAnalysis.SymbolKind.Discard = 19 -> Microsoft.CodeAnalysis.SymbolKind Microsoft.CodeAnalysis.Text.SourceText.CanBeEmbedded.get -> bool Microsoft.CodeAnalysis.Text.SourceText.GetChecksum() -> System.Collections.Immutable.ImmutableArray abstract Microsoft.CodeAnalysis.CompilationOptions.Language.get -> string @@ -985,8 +985,8 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitWhileUntilLoopStatement(Microsoft.CodeAnalysis.Semantics.IWhileUntilLoopStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitWithStatement(Microsoft.CodeAnalysis.Semantics.IWithStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation, TArgument argument) -> TResult -virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitDiscarded(Microsoft.CodeAnalysis.IDiscardedSymbol symbol) -> void -virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitDiscarded(Microsoft.CodeAnalysis.IDiscardedSymbol symbol) -> TResult +virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitDiscard(Microsoft.CodeAnalysis.IDiscardSymbol symbol) -> void +virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitDiscard(Microsoft.CodeAnalysis.IDiscardSymbol symbol) -> TResult virtual Microsoft.CodeAnalysis.SyntaxNode.ChildThatContainsPosition(int position) -> Microsoft.CodeAnalysis.SyntaxNodeOrToken virtual Microsoft.CodeAnalysis.SyntaxNode.SerializeTo(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> void virtual Microsoft.CodeAnalysis.SyntaxNode.ToFullString() -> string diff --git a/src/Compilers/Core/Portable/Symbols/IDiscardedSymbol.cs b/src/Compilers/Core/Portable/Symbols/IDiscardSymbol.cs similarity index 92% rename from src/Compilers/Core/Portable/Symbols/IDiscardedSymbol.cs rename to src/Compilers/Core/Portable/Symbols/IDiscardSymbol.cs index c850a942fc05e..99921ac05bb5c 100644 --- a/src/Compilers/Core/Portable/Symbols/IDiscardedSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IDiscardSymbol.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis /// A symbol representing a discarded value, e.g. a symbol in the result of /// GetSymbolInfo for `_` in `M(out _)` or `(x, _) = e`. /// - public interface IDiscardedSymbol : ISymbol + public interface IDiscardSymbol : ISymbol { /// /// The type of the discarded value. diff --git a/src/Compilers/Core/Portable/Symbols/SymbolKind.cs b/src/Compilers/Core/Portable/Symbols/SymbolKind.cs index d9a643859d144..2fd95ac737602 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolKind.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolKind.cs @@ -105,6 +105,6 @@ public enum SymbolKind /// /// Symbol represents a value that is discarded, e.g. in M(out _) /// - Discarded = 19, + Discard = 19, } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs index 8dfc555ab7728..035740138828b 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs @@ -28,7 +28,7 @@ public virtual void VisitAssembly(IAssemblySymbol symbol) DefaultVisit(symbol); } - public virtual void VisitDiscarded(IDiscardedSymbol symbol) + public virtual void VisitDiscard(IDiscardSymbol symbol) { DefaultVisit(symbol); } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs index 5a3dd9c732b87..3d0049e376069 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs @@ -31,7 +31,7 @@ public virtual TResult VisitAssembly(IAssemblySymbol symbol) return DefaultVisit(symbol); } - public virtual TResult VisitDiscarded(IDiscardedSymbol symbol) + public virtual TResult VisitDiscard(IDiscardSymbol symbol) { return DefaultVisit(symbol); } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 63e0ba890ae9f..8f92e5b7e58bf 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -8951,8 +8951,8 @@ void M() await Local<$$"; await VerifyAnyItemExistsAsync(markup); - } - + } + [WorkItem(14127, "https://github.com/dotnet/roslyn/issues/14127")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TupleTypeAtMemberLevel1() diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 03400c70ab845..8d503c417bb47 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -36,7 +36,7 @@ public async Task DiagnosticAnalyzerDriverAllInOne() syntaxKindsMissing.Add(SyntaxKind.ParenthesizedVariableDesignation); syntaxKindsMissing.Add(SyntaxKind.ForEachVariableStatement); syntaxKindsMissing.Add(SyntaxKind.DeclarationExpression); - syntaxKindsMissing.Add(SyntaxKind.DiscardedDesignation); + syntaxKindsMissing.Add(SyntaxKind.DiscardDesignation); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); using (var workspace = await TestWorkspace.CreateCSharpAsync(source, TestOptions.Regular)) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs index 5cd0940a8192a..68c6b00f61bc3 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs @@ -7246,7 +7246,7 @@ public void M1() parseOptions: TestOptions.Regular); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + [Fact/*(Skip = "https://github.com/dotnet/roslyn/issues/15508")*/, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WorkItem(14136, "https://github.com/dotnet/roslyn/issues/14136")] public async Task TestDeconstruction3() { @@ -7269,7 +7269,7 @@ public void M1() (int x, (int, int)) = Method(); } - private (int x, (int, int)) Method() + private object Method() { throw new NotImplementedException(); } @@ -7277,7 +7277,7 @@ public void M1() parseOptions: TestOptions.Regular); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + [Fact/*(Skip = "https://github.com/dotnet/roslyn/issues/15508")*/, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WorkItem(14136, "https://github.com/dotnet/roslyn/issues/14136")] public async Task TestDeconstruction4() { @@ -7300,7 +7300,7 @@ public void M1() (int x, int) = Method(); } - private (int x, int) Method() + private object Method() { throw new NotImplementedException(); } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index a4f1595c15716..ea6ee3770398c 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -2570,7 +2570,7 @@ await AssertSmartIndentAsync( expectedIndentation: 8); } - [WpfFact, Trait(Traits.Feature, Traits.Features.SmartIndent)] + [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/15813"), Trait(Traits.Feature, Traits.Features.SmartIndent)] public async Task DontCreateIndentOperationForBrokenBracketedArgumentList() { var code = @" @@ -2583,7 +2583,8 @@ static void M() } } "; - + // Need to confirm expected behavior after discard/deconstruction parsing changes + // https://github.com/dotnet/roslyn/issues/15813 await AssertSmartIndentAsync( code, indentationLine: 6, diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index 2402708b8a884..cda9d235db5cc 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -301,19 +301,12 @@ private static CSharpSyntaxNode Parse( var statementSyntax = expr.ParseStatement(statementDiagnostics); Debug.Assert((statementSyntax == null) || !statementDiagnostics.HasAnyErrors()); statementDiagnostics.Free(); - - // Prefer to parse expression statements (except deconstruction-declarations) as expressions. - // Once https://github.com/dotnet/roslyn/issues/15049 is fixed, we should parse d-declarations as expressions. var isExpressionStatement = statementSyntax.IsKind(SyntaxKind.ExpressionStatement); - var isDeconstructionDeclaration = isExpressionStatement && - IsDeconstructionDeclaration((ExpressionStatementSyntax)statementSyntax); - - if (statementSyntax != null && (!isExpressionStatement || isDeconstructionDeclaration)) + if (statementSyntax != null && !isExpressionStatement) { formatSpecifiers = null; - if (statementSyntax.IsKind(SyntaxKind.LocalDeclarationStatement) || - isDeconstructionDeclaration) + if (statementSyntax.IsKind(SyntaxKind.LocalDeclarationStatement)) { return statementSyntax; } @@ -326,15 +319,6 @@ private static CSharpSyntaxNode Parse( return expr.ParseExpression(diagnostics, allowFormatSpecifiers: true, formatSpecifiers: out formatSpecifiers); } - private static bool IsDeconstructionDeclaration(ExpressionStatementSyntax expressionStatement) - { - if (!expressionStatement.Expression.IsKind(SyntaxKind.SimpleAssignmentExpression)) - { - return false; - } - return ((AssignmentExpressionSyntax)expressionStatement.Expression).IsDeconstructionDeclaration(); - } - internal override CompileResult CompileAssignment( string target, string expr, diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/MayHaveSideEffectsVisitor.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/MayHaveSideEffectsVisitor.cs index 9d82aa4874d42..1ae0eefe87af6 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/MayHaveSideEffectsVisitor.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/MayHaveSideEffectsVisitor.cs @@ -27,6 +27,11 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) return this.SetMayHaveSideEffects(); } + public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node) + { + return this.SetMayHaveSideEffects(); + } + // Calls are treated as having side effects, but properties and // indexers are not. (Since this visitor is run on the bound tree // before lowering, properties are not represented as calls.) diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs index 4f539615fa5ad..9fa935e9dd354 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs @@ -112,14 +112,14 @@ void Test() Assert.Empty(missingAssemblyIdentities); Assert.Equal(DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult, resultProperties.Flags); - Assert.Equal(default(DkmEvaluationResultCategory), resultProperties.Category); // Not Data + Assert.Equal(DkmEvaluationResultCategory.Data, resultProperties.Category); // Data, because it is an expression Assert.Equal(default(DkmEvaluationResultAccessType), resultProperties.AccessType); Assert.Equal(default(DkmEvaluationResultStorageType), resultProperties.StorageType); Assert.Equal(default(DkmEvaluationResultTypeModifierFlags), resultProperties.ModifierFlags); testData.GetMethodData("<>x.<>m0(C)").VerifyIL(@" { - // Code size 105 (0x69) + // Code size 112 (0x70) .maxstack 4 .locals init (System.Guid V_0, int V_1, @@ -156,7 +156,10 @@ .locals init (System.Guid V_0, IL_0061: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" IL_0066: ldloc.2 IL_0067: stind.ref - IL_0068: ret + IL_0068: ldloc.1 + IL_0069: ldloc.2 + IL_006a: newobj ""System.ValueTuple..ctor(int, string)"" + IL_006f: ret }"); }); } @@ -196,14 +199,14 @@ void Test() Assert.Empty(missingAssemblyIdentities); Assert.Equal(DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult, resultProperties.Flags); - Assert.Equal(default(DkmEvaluationResultCategory), resultProperties.Category); // Not Data + Assert.Equal(DkmEvaluationResultCategory.Data, resultProperties.Category); // Data, because it is an expression Assert.Equal(default(DkmEvaluationResultAccessType), resultProperties.AccessType); Assert.Equal(default(DkmEvaluationResultStorageType), resultProperties.StorageType); Assert.Equal(default(DkmEvaluationResultTypeModifierFlags), resultProperties.ModifierFlags); testData.GetMethodData("<>x.<>m0(C)").VerifyIL(@" { - // Code size 63 (0x3f) + // Code size 70 (0x46) .maxstack 4 .locals init (System.Guid V_0, int V_1, @@ -228,8 +231,12 @@ .locals init (System.Guid V_0, IL_0037: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" IL_003c: ldloc.2 IL_003d: stind.ref - IL_003e: ret -}"); + IL_003e: ldloc.1 + IL_003f: ldloc.2 + IL_0040: newobj ""System.ValueTuple..ctor(int, string)"" + IL_0045: ret +} +"); }); } diff --git a/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs index 7b6d0f9f4bcce..7a111b5acc5c6 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -1082,7 +1082,8 @@ public static bool IsPossibleLambdaParameterModifierContext( // consider this a lambda if this is a location where the // lambda's type would be inferred because of a delegate // or Expression type. - if (token.Parent.IsKind(SyntaxKind.ParenthesizedExpression)) + if (token.Parent.IsKind(SyntaxKind.ParenthesizedExpression) || + token.Parent.IsKind(SyntaxKind.TupleExpression)) { return true; } diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs index 8c9dea1c71dc1..cec34b2de4acc 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -2018,6 +2018,10 @@ private bool TryGetTupleTypesAndNames( { AddTypeAndName((TupleExpressionSyntax)expr, elementTypesBuilder, elementNamesBuilder); } + else + { + return false; + } } if (elementTypesBuilder.Contains(null))