diff --git a/docs/features/deconstruction.md b/docs/features/deconstruction.md
index 93d4a303a1837..0a10068f2cb20 100644
--- a/docs/features/deconstruction.md
+++ b/docs/features/deconstruction.md
@@ -121,61 +121,63 @@ Note that tuples (`System.ValueTuple`) don't need to invoke Deconstruct.
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
- | local_variable_combo_declaration ';' // new
;
-local_variable_combo_declaration
- : local_variable_combo_declaration_lhs '=' expression
+local_variable_declaration
+ : local_variable_type local_variable_declarators
+ | deconstruction_declaration // new
+ ;
-local_variable_combo_declaration_lhs
- : 'var' '(' identifier_list ')'
- | '(' local_variable_list ')'
- ;
+deconstruction_declaration // new
+ : deconstruction_variables '=' expression
+ ;
-identifier_list
- : identifier ',' identifier
- | identifier_list ',' identifier
- ;
+deconstuction_variables
+ : '(' deconstuction_variables_nested (',' deconstuction_variables_nested)* ')'
+ | 'var' deconstruction_identifiers
+ ;
-local_variable_list
- : local_variable_type identifier ',' local_variable_type identifier
- | local_variable_list ',' local_variable_type identifier
- ;
+deconstuction_variables_nested // new
+ : deconstuction_variables
+ | type identifier
+ ;
+
+deconstruction_identifiers
+ : '(' deconstruction_identifiers_nested (',' deconstruction_identifiers_nested)* ')'
+ ;
+
+deconstruction_identifiers_nested // new
+ : deconstruction_identifiers
+ | identifier
+ ;
foreach_statement
: 'foreach' '(' local_variable_type identifier 'in' expression ')' embedded_statement
- | 'foreach' '(' local_variable_combo_declaration_lhs 'in' expression ')' embedded_statement // new
+ | 'foreach' '(' deconstruction_variables 'in' expression ')' embedded_statement // new
+ ;
+
+for_statement
+ : 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')' embedded_statement
;
for_initializer
: local_variable_declaration
- | local_variable_combo_declaration // new
+ | deconstruction_declaration // new
| statement_expression_list
;
let_clause
: 'let' identifier '=' expression
- | 'let' '(' identifier_list ')' '=' expression // new
+ | 'let' deconstruction_identifiers '=' expression // new
;
-from_clause // not sure
+from_clause
: 'from' type? identifier 'in' expression
- ;
-
-join_clause // not sure
- : 'join' type? identifier 'in' expression 'on' expression 'equals' expression
- ;
-
-join_into_clause // not sure
- : 'join' type? identifier 'in' expression 'on' expression 'equals' expression 'into' identifier
- ;
-
-constant_declarator // not sure
- : identifier '=' constant_expression
+ | 'from' deconstuction_variables 'in' expression // new
+ | 'from' deconstruction_identifiers 'in' expression // new
;
```
-Treat deconstruction of a tuple into new variables as a new kind of node (not AssignmentExpression).
It would pick up the behavior of each contexts where new variables can be declared (TODO: need to list). For instance, in LINQ, new variables go into a transparent identifiers.
It is seen as deconstructing into separate variables (we don't introduce transparent identifiers in contexts where they didn't exist previously).
diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
index 64b5cb82e0b92..1ef7fad2daa88 100644
--- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
+++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
@@ -760,6 +760,7 @@
+
@@ -854,6 +855,7 @@
+
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index bdd7cafaa8466..cdd7a78d41ece 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_DeconstructRequiresExpression {
}
}
+ ///
+ /// Looks up a localized string similar to Deconstruction must contain at least two variables..
+ ///
+ internal static string ERR_DeconstructTooFewElements {
+ get {
+ return ResourceManager.GetString("ERR_DeconstructTooFewElements", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Cannot deconstruct a tuple of '{0}' elements into '{1}' variables..
///
@@ -10493,6 +10502,15 @@ internal static string TypeArgumentCannotBeNull {
}
}
+ ///
+ /// Looks up a localized string similar to The type must be 'var'..
+ ///
+ internal static string TypeMustBeVar {
+ get {
+ return ResourceManager.GetString("TypeMustBeVar", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Use Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Literal to create numeric literal tokens..
///
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 74bf5c3edab53..7eb9c72232da4 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -4896,4 +4896,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Cannot deconstruct dynamic objects.
+
+ Deconstruction must contain at least two variables.
+
+
+ The type must be 'var'.
+
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 97eae9e0245da..959135d8b4027 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -1398,5 +1398,6 @@ internal enum ErrorCode
ERR_DeconstructRequiresExpression = 8210,
ERR_DeconstructWrongCardinality = 8211,
ERR_CannotDeconstructDynamic = 8212,
+ ERR_DeconstructTooFewElements = 8213,
}
}
diff --git a/src/Compilers/CSharp/Portable/GlobalSuppressions.cs b/src/Compilers/CSharp/Portable/GlobalSuppressions.cs
index 36201f1e58362..5bea60c95810f 100644
--- a/src/Compilers/CSharp/Portable/GlobalSuppressions.cs
+++ b/src/Compilers/CSharp/Portable/GlobalSuppressions.cs
@@ -10,4 +10,9 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax)~Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IncompleteMember(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax)~Microsoft.CodeAnalysis.CSharp.Syntax.IncompleteMemberSyntax")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ReturnStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax)~Microsoft.CodeAnalysis.CSharp.Syntax.ReturnStatementSyntax")]
-
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DeconstructionVariablesVar(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax)~Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionVariablesVarSyntax")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Microsoft.CodeAnalysis.SyntaxTokenList)~Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.VariableDeconstructionDeclarator(Microsoft.CodeAnalysis.SeparatedSyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax})~Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.VariableDeclaration(Microsoft.CodeAnalysis.SeparatedSyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclaratorSyntax})~Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax)~Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.EventFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax)~Microsoft.CodeAnalysis.CSharp.Syntax.EventFieldDeclarationSyntax")]
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
index 2370f52437f80..ad5839511da97 100644
--- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
+++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Text;
@@ -4373,7 +4374,7 @@ private MemberDeclarationSyntax ParseFixedSizeBufferDeclaration(
return _syntaxFactory.FieldDeclaration(
attributes, modifiers.ToTokenList(),
- _syntaxFactory.VariableDeclaration(type, variables),
+ _syntaxFactory.VariableDeclaration(type, variables, deconstruction: null),
semicolon);
}
finally
@@ -4520,7 +4521,7 @@ private FieldDeclarationSyntax ParseNormalFieldDeclaration(
return _syntaxFactory.FieldDeclaration(
attributes,
modifiers.ToTokenList(),
- _syntaxFactory.VariableDeclaration(type, variables),
+ _syntaxFactory.VariableDeclaration(type, variables, deconstruction: null),
semicolon);
}
finally
@@ -4567,7 +4568,7 @@ private MemberDeclarationSyntax ParseEventFieldDeclaration(
attributes,
modifiers.ToTokenList(),
eventToken,
- _syntaxFactory.VariableDeclaration(type, variables),
+ _syntaxFactory.VariableDeclaration(type, variables, deconstruction: null),
semicolon);
}
finally
@@ -5055,7 +5056,7 @@ private FieldDeclarationSyntax ParseConstantFieldDeclaration(SyntaxListBuilder in )
+ // or
+ // foreach ( in )
SyntaxToken @foreach;
@@ -7868,16 +7894,22 @@ private ForEachStatementSyntax ParseForEachStatement()
var openParen = this.EatToken(SyntaxKind.OpenParenToken);
- var type = this.ParseType(false);
- SyntaxToken name;
- if (this.CurrentToken.Kind == SyntaxKind.InKeyword)
- {
- name = this.ParseIdentifierToken();
- name = this.AddError(name, ErrorCode.ERR_BadForeachDecl);
- }
- else
+ var deconstruction = TryParseDeconstructionDeclaration(withEquals: false);
+
+ TypeSyntax type = null;
+ SyntaxToken name = null;
+ if (deconstruction == null)
{
- name = this.ParseIdentifierToken();
+ type = this.ParseType(false);
+ if (this.CurrentToken.Kind == SyntaxKind.InKeyword)
+ {
+ name = this.ParseIdentifierToken();
+ name = this.AddError(name, ErrorCode.ERR_BadForeachDecl);
+ }
+ else
+ {
+ name = this.ParseIdentifierToken();
+ }
}
var @in = this.EatToken(SyntaxKind.InKeyword, ErrorCode.ERR_InExpected);
@@ -7885,7 +7917,7 @@ private ForEachStatementSyntax ParseForEachStatement()
var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
var statement = this.ParseEmbeddedStatement(true);
- return _syntaxFactory.ForEachStatement(@foreach, openParen, type, name, @in, expression, closeParen, statement);
+ return _syntaxFactory.ForEachStatement(@foreach, openParen, type, name, deconstruction, @in, expression, closeParen, statement);
}
private GotoStatementSyntax ParseGotoStatement()
@@ -8284,11 +8316,22 @@ private LabeledStatementSyntax ParseLabeledStatement()
}
///
- /// Parses any kind of local declaration statement: local variable, local funcion, or let statement.
+ /// Parses any kind of local declaration statement: local variable, local function, or deconstruction declaration.
///
- ///
private StatementSyntax ParseLocalDeclarationStatement()
{
+ VariableDeclarationSyntax deconstruction = TryParseDeconstructionDeclaration(withEquals: true);
+ if (deconstruction != null)
+ {
+ var semicolon = this.EatToken(SyntaxKind.SemicolonToken);
+
+ return _syntaxFactory.LocalDeclarationStatement(
+ modifiers: default(SyntaxList),
+ refKeyword: null,
+ declaration: deconstruction,
+ semicolonToken: semicolon);
+ }
+
var mods = _pool.Allocate();
var variables = _pool.AllocateSeparated();
try
@@ -8323,7 +8366,7 @@ private StatementSyntax ParseLocalDeclarationStatement()
return _syntaxFactory.LocalDeclarationStatement(
mods.ToTokenList(),
refKeyword,
- _syntaxFactory.VariableDeclaration(type, variables),
+ _syntaxFactory.VariableDeclaration(type, variables, deconstruction: null),
semicolon);
}
finally
@@ -8333,6 +8376,283 @@ private StatementSyntax ParseLocalDeclarationStatement()
}
}
+ ///
+ /// Returns null and resets the pointer if this does not look like a deconstruction-declaration after all.
+ ///
+ private VariableDeclarationSyntax TryParseDeconstructionDeclaration(bool withEquals)
+ {
+ if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken
+ || (CurrentToken.IsVar() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken))
+ {
+ var resetPoint = this.GetResetPoint();
+
+ try
+ {
+ var deconstruction = ParseDeconstructionDeclaration(withEquals);
+ if (deconstruction == null || !DeconstructionVariableLooksGood(deconstruction, withEquals))
+ {
+ 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.
+ ///
+ /// Specifies whether to look for and consume the equals sign and the following expression.
+ /// Specifies whether to parse the terminal form of a deconstruction-declaration (which can't appear at the top-level).
+ private VariableDeclarationSyntax ParseDeconstructionDeclaration(bool withEquals, bool topLevel = true)
+ {
+ Debug.Assert(topLevel || !withEquals); // withEquals can only be set at the top-level
+
+ VariableDeclarationSyntax result;
+ if (this.CurrentToken.IsVar() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken)
+ {
+ // parses `var (...) = expression` form
+ result = ParseDeconstructionVarForm(withEquals);
+ }
+ else if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
+ {
+ // parses `(...) = expression` form
+ result = ParseDeconstructionList(withEquals, justIdentifiers: false);
+ }
+ else if (!topLevel)
+ {
+ // parses `type id` part
+ result = ParseDeconstructionTypeIdPart();
+ }
+ else
+ {
+ return null;
+ }
+
+ return CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples);
+ }
+
+ ///
+ /// Parses `(..., ..., ...)` where each part is deconstruction variables (such as `type id` or `var (..., ...)` or another list `(..., ..., ...)`).
+ /// Never returns null.
+ ///
+ private VariableDeclarationSyntax ParseDeconstructionList(bool withEquals, bool justIdentifiers)
+ {
+ var list = _pool.AllocateSeparated();
+
+ try
+ {
+ var openParen = this.EatToken(SyntaxKind.OpenParenToken);
+
+ if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
+ {
+ while (true)
+ {
+ VariableDeclarationSyntax variable;
+
+ if (justIdentifiers)
+ {
+ variable = ParseDeconstructionIdentifierOrIdentifiersParts(withEquals: false);
+ }
+ else
+ {
+ variable = ParseDeconstructionDeclaration(withEquals: false, topLevel: false);
+ }
+
+ Debug.Assert(variable != null);
+ list.Add(variable);
+
+ if (this.CurrentToken.Kind != SyntaxKind.CommaToken)
+ {
+ break;
+ }
+
+ var comma = this.EatToken(SyntaxKind.CommaToken);
+ list.AddSeparator(comma);
+ }
+ }
+
+ var closeParen = this.EatToken(SyntaxKind.CloseParenToken);
+
+ SyntaxToken equals = null;
+ ExpressionSyntax expression = null;
+ if (withEquals)
+ {
+ equals = this.EatToken(SyntaxKind.EqualsToken);
+ expression = this.ParseExpressionCore();
+ }
+
+ var deconstruction = _syntaxFactory.VariableDeconstructionDeclarator(openParen, list, closeParen, equals, expression);
+ var result = _syntaxFactory.VariableDeclaration(type: null, variables: default(SeparatedSyntaxList), deconstruction: deconstruction);
+
+ if (!result.ContainsDiagnostics && list.Count < 2)
+ {
+ result = this.AddError(result, ErrorCode.ERR_DeconstructTooFewElements);
+ }
+
+ return result;
+ }
+ finally
+ {
+ _pool.Free(list);
+ }
+ }
+
+ ///
+ /// Parses the var form of deconstruction. For instance, `var (x, y) = ...`
+ /// Never returns null.
+ ///
+ private VariableDeclarationSyntax ParseDeconstructionVarForm(bool withEquals)
+ {
+ Debug.Assert(this.CurrentToken.IsVar());
+
+ var varType = ParseType(parentIsParameter: false);
+ var identifiers = ParseDeconstructionList(withEquals, justIdentifiers: true);
+
+ return _syntaxFactory.VariableDeclaration(varType, default(SeparatedSyntaxList), identifiers.Deconstruction);
+ }
+
+ ///
+ /// Parses both `id` and `(id, ...)` in a deconstruction-declaration.
+ /// Never returns null.
+ ///
+ private VariableDeclarationSyntax ParseDeconstructionIdentifierOrIdentifiersParts(bool withEquals)
+ {
+ if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
+ {
+ return ParseDeconstructionList(withEquals: false, justIdentifiers: true);
+ }
+ else
+ {
+ return ParseDeconstructionIdPart();
+ }
+ }
+
+ ///
+ /// Parses an individual identifier in a deconstruction declaration. For instance, in `var (id, id) = ...`
+ /// Never returns null.
+ ///
+ private VariableDeclarationSyntax ParseDeconstructionIdPart()
+ {
+ var identifier = ParseIdentifierToken();
+ var declarator = _syntaxFactory.VariableDeclarator(identifier, null, null);
+ return _syntaxFactory.VariableDeclaration(
+ type: null,
+ variables: SyntaxFactory.SeparatedList(declarator),
+ deconstruction: null);
+ }
+
+ ///
+ /// Parses an individual type and identifier in a deconstruction declaration. For instance, in `(type id, type id) = ...`
+ /// Never returns null.
+ ///
+ private VariableDeclarationSyntax ParseDeconstructionTypeIdPart()
+ {
+ var type = ParseType(parentIsParameter: false);
+ var identifier = ParseIdentifierToken();
+
+ var declarator = _syntaxFactory.VariableDeclarator(identifier, null, null);
+ return _syntaxFactory.VariableDeclaration(
+ type: type,
+ variables: SyntaxFactory.SeparatedList(declarator),
+ deconstruction: null);
+ }
+
+ ///
+ /// 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.
+ /// PROTOTYPE(tuples) Can this be done without allocations?
+ ///
+ private bool IsPossibleDeconstructionDeclaration()
+ {
+ if ((this.CurrentToken.IsVar() && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken) ||
+ this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
+ {
+ var resetPoint = this.GetResetPoint();
+ try
+ {
+ // We don't need to parse the expression following the equals token
+ var variables = ParseDeconstructionDeclaration(withEquals: false);
+ var equalsToken = this.EatToken(SyntaxKind.EqualsToken);
+
+ // We just need the equals token and one other confirmation that this is a deconstruction syntax
+ return DeconstructionVariableLooksGood(variables, withEquals: false) && !equalsToken.IsMissing;
+ }
+ finally
+ {
+ this.Reset(ref resetPoint);
+ this.Release(ref resetPoint);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Returns true if one distinct clue is found that this is intended as deconstruction variables, and the equals sign is as expected.
+ ///
+ private static bool DeconstructionVariableLooksGood(VariableDeclarationSyntax node, bool withEquals = true)
+ {
+ if (node == null)
+ {
+ return false;
+ }
+
+ if (withEquals && node.Deconstruction != null && node.Deconstruction.EqualsToken.IsMissing)
+ {
+ return false;
+ }
+
+ if (node.Type != null)
+ {
+ if (node.Type.Kind == SyntaxKind.IdentifierName && ((IdentifierNameSyntax)node.Type).Identifier.IsVar()
+ && node.Deconstruction != null && !node.Deconstruction.OpenParenToken.IsMissing && !node.Deconstruction.CloseParenToken.IsMissing)
+ {
+ // `var (..., ....)` with var and both parens present
+ return true;
+ }
+
+ if (!node.Type.IsMissing && node.Variables.Count == 1 && !node.Variables[0].Identifier.IsMissing)
+ {
+ // `type id` with both type and id present
+ return true;
+ }
+ }
+ else
+ {
+ if (node.Variables.Count == 0 && node.Deconstruction != null)
+ {
+ // (..., ...) where one of the elements looks good
+ int count = node.Deconstruction.Variables.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (DeconstructionVariableLooksGood(node.Deconstruction.Variables[i], withEquals: false))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
private WhenClauseSyntax ParseWhenClauseOpt()
{
if (this.CurrentToken.ContextualKind != SyntaxKind.WhenKeyword)
@@ -8356,7 +8676,7 @@ private VariableDeclarationSyntax ParseVariableDeclaration()
LocalFunctionStatementSyntax localFunction;
ParseLocalDeclaration(variables, false, default(SyntaxList), default(SyntaxToken), out type, out localFunction);
Debug.Assert(localFunction == null);
- var result = _syntaxFactory.VariableDeclaration(type, variables);
+ var result = _syntaxFactory.VariableDeclaration(type, variables, deconstruction: null);
_pool.Free(variables);
return result;
}
diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
index 36114469e6090..92657a0fcec4a 100644
--- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
+++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
@@ -26,6 +26,8 @@ Microsoft.CodeAnalysis.CSharp.Syntax.DelegateDeclarationSyntax.WithRefKeyword(Mi
Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax.RefKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken equalsToken, Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax value) -> Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax.WithRefKeyword(Microsoft.CodeAnalysis.SyntaxToken refKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax.DeconstructionVariables.get -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax.WithDeconstructionVariables(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax deconstructionVariables) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForEachStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ForStatementSyntax.RefKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.ForStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken forKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax declaration, Microsoft.CodeAnalysis.SeparatedSyntaxList initializers, Microsoft.CodeAnalysis.SyntaxToken firstSemicolonToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken secondSemicolonToken, Microsoft.CodeAnalysis.SeparatedSyntaxList incrementors, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ForStatementSyntax.WithRefKeyword(Microsoft.CodeAnalysis.SyntaxToken refKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ForStatementSyntax
@@ -117,6 +119,20 @@ Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.Update(Microsoft.CodeAnalys
Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.WithCloseParenToken(Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.WithElements(Microsoft.CodeAnalysis.SeparatedSyntaxList elements) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.WithOpenParenToken(Microsoft.CodeAnalysis.SyntaxToken openParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax.Deconstruction.get -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax.WithDeconstruction(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax deconstruction) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.AddVariables(params Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.CloseParenToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.EqualsToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.OpenParenToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.Value.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.Variables.get -> Microsoft.CodeAnalysis.SeparatedSyntaxList
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.WithCloseParenToken(Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.WithEqualsToken(Microsoft.CodeAnalysis.SyntaxToken equalsToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.WithOpenParenToken(Microsoft.CodeAnalysis.SyntaxToken openParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.WithValue(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax value) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.WithVariables(Microsoft.CodeAnalysis.SeparatedSyntaxList variables) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.Condition.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken whenKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition) -> Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax
@@ -134,6 +150,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.ReplaceKeyword = 8439 -> Microsoft.Code
Microsoft.CodeAnalysis.CSharp.SyntaxKind.TupleElement = 8926 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.TupleExpression = 8927 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.TupleType = 8925 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
+Microsoft.CodeAnalysis.CSharp.SyntaxKind.VariableDeconstructionDeclarator = 8928 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhenClause = 9013 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
@@ -144,6 +161,7 @@ override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitOriginalExpress
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitTupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitTupleExpression(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitTupleType(Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
+override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitVariableDeconstructionDeclarator(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
override Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
override Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult
@@ -165,6 +183,8 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax.Accept(Micro
override Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult
override Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
override Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult
+override Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
+override Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult
override Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
override Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax declarationSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.ISymbol
@@ -201,6 +221,8 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleExpression(Microsoft.Cod
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleExpression(Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SeparatedSyntaxList arguments, Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleType(Microsoft.CodeAnalysis.SeparatedSyntaxList elements = default(Microsoft.CodeAnalysis.SeparatedSyntaxList)) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleType(Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SeparatedSyntaxList elements, Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
+static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.VariableDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax deconstructionDeclaration) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax
+static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.VariableDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax deconstructionDeclaration) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition = null) -> Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhenClause(Microsoft.CodeAnalysis.SyntaxToken whenKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition) -> Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax node) -> void
@@ -212,6 +234,7 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitOriginalExpressio
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleType(Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax node) -> void
+virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitVariableDeconstructionDeclarator(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax node) -> TResult
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> TResult
@@ -222,4 +245,5 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitOriginal
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax node) -> TResult
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax node) -> TResult
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleType(Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax node) -> TResult
+virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitVariableDeconstructionDeclarator(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax node) -> TResult
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax node) -> TResult
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs
new file mode 100644
index 0000000000000..5351f6f1735b7
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs
@@ -0,0 +1,12 @@
+// 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
+{
+ public sealed partial class ForEachStatementSyntax : StatementSyntax
+ {
+ public ForEachStatementSyntax Update(SyntaxToken forEachKeyword, SyntaxToken openParenToken, TypeSyntax type, SyntaxToken identifier, SyntaxToken inKeyword, ExpressionSyntax expression, SyntaxToken closeParenToken, StatementSyntax statement)
+ {
+ return Update(forEachKeyword, openParenToken, type, identifier, null, inKeyword, expression, closeParenToken, statement);
+ }
+ }
+}
diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
index d0c54bb560b91..8144a04bd29c0 100644
--- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
+++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
@@ -1809,11 +1809,29 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
@@ -2033,7 +2051,8 @@
-
+
+
@@ -2041,13 +2060,14 @@
-
-
+
+
Gets the identifier.
+
@@ -2057,6 +2077,7 @@
+
@@ -3933,7 +3954,7 @@
-