Browse Source

Added support for `var (a, b, _) = ...;` syntax in deconstruction.

pull/2119/head
Siegfried Pammer 5 years ago
parent
commit
3390d3849d
  1. 28
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs
  2. 7
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  3. 43
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

28
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs

@ -149,63 +149,49 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -149,63 +149,49 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void LocalVariable_NoConversion()
{
MyInt? myInt3;
MyInt x;
(myInt3, x) = GetSource<MyInt?, MyInt>();
var (myInt3, x) = GetSource<MyInt?, MyInt>();
Console.WriteLine(myInt3);
Console.WriteLine(x);
}
public void LocalVariable_NoConversion_DiscardFirst()
{
MyInt x;
int value;
(_, x, value) = GetSource<MyInt?, MyInt, int>();
var (_, x, value) = GetSource<MyInt?, MyInt, int>();
Console.WriteLine(x);
Console.WriteLine(value);
}
public void LocalVariable_NoConversion_DiscardLast()
{
MyInt? myInt3;
MyInt x;
(myInt3, x, _) = GetSource<MyInt?, MyInt, int>();
var (myInt3, x, _) = GetSource<MyInt?, MyInt, int>();
Console.WriteLine(myInt3);
Console.WriteLine(x);
}
public void LocalVariable_NoConversion_DiscardSecond()
{
MyInt? myInt3;
int value;
(myInt3, _, value) = GetSource<MyInt?, MyInt, int>();
var (myInt3, _, value) = GetSource<MyInt?, MyInt, int>();
Console.WriteLine(myInt3);
Console.WriteLine(value);
}
public void LocalVariable_NoConversion_ReferenceTypes()
{
string value;
string value2;
(value, value2) = GetSource<string, string>();
var (value, value2) = GetSource<string, string>();
Console.WriteLine(value);
Console.WriteLine(value2);
}
public void LocalVariable_IntToLongConversion()
{
int value;
long value2;
(value, value2) = GetSource<int, int>();
var (value, value2) = GetSource<int, int>();
Console.WriteLine(value);
Console.WriteLine(value2);
}
public void LocalVariable_NoConversion_ComplexValue()
{
MyInt? myInt3;
MyInt x;
(myInt3, x) = new DeconstructionSource<MyInt?, MyInt> {
var (myInt3, x) = new DeconstructionSource<MyInt?, MyInt> {
Dummy = 3
};
Console.WriteLine(myInt3);

7
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -567,7 +567,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -567,7 +567,7 @@ namespace ICSharpCode.Decompiler.CSharp
break;
case RequiredGetCurrentTransformation.Deconstruction:
useVar = true;
designation = TranslateForeachDeconstructionDesignation((DeconstructInstruction)body.Instructions[0]);
designation = TranslateDeconstructionDesignation((DeconstructInstruction)body.Instructions[0], isForeach: true);
break;
}
@ -614,7 +614,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -614,7 +614,7 @@ namespace ICSharpCode.Decompiler.CSharp
return foreachStmt;
}
VariableDesignation TranslateForeachDeconstructionDesignation(DeconstructInstruction inst)
internal static VariableDesignation TranslateDeconstructionDesignation(DeconstructInstruction inst, bool isForeach)
{
var assignments = inst.Assignments.Instructions;
int assignmentPos = 0;
@ -629,7 +629,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -629,7 +629,8 @@ namespace ICSharpCode.Decompiler.CSharp
var designation = new SingleVariableDesignation();
if (subPattern.HasDesignator) {
ILVariable v = ((StLoc)assignments[assignmentPos]).Variable;
v.Kind = VariableKind.ForeachLocal;
if (isForeach)
v.Kind = VariableKind.ForeachLocal;
designation.Identifier = v.Name;
designation.AddAnnotation(new ILVariableResolveResult(v));
assignmentPos++;

43
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -102,6 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -102,6 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public VariableToDeclare ReplacementDueToCollision;
public bool InvolvedInCollision;
public bool RemovedDueToCollision => ReplacementDueToCollision != null;
public bool DeclaredInDeconstruction;
public VariableToDeclare(ILVariable variable, bool defaultInitialization, InsertionPoint insertionPoint, IdentifierExpression firstUse, int sourceOrder)
{
@ -126,6 +127,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -126,6 +127,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
EnsureExpressionStatementsAreValid(rootNode);
FindInsertionPoints(rootNode, 0);
ResolveCollisions();
InsertDeconstructionVariableDeclarations();
InsertVariableDeclarations(context);
UpdateAnnotations(rootNode);
} finally {
@ -133,7 +135,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -133,7 +135,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
variableDict.Clear();
}
}
/// <summary>
/// Analyze the input AST (containing undeclared variables)
/// for where those variables would be declared by this transform.
@ -425,6 +426,44 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -425,6 +426,44 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
private void InsertDeconstructionVariableDeclarations()
{
var usedVariables = new HashSet<ILVariable>();
foreach (var g in variableDict.Values.GroupBy(v => v.InsertionPoint.nextNode)) {
if (!(g.Key is ExpressionStatement { Expression: AssignmentExpression { Left: TupleExpression left, Operator: AssignmentOperatorType.Assign } assignment }))
continue;
usedVariables.Clear();
var deconstruct = assignment.Annotation<DeconstructInstruction>();
if (deconstruct == null || deconstruct.Init.Count > 0 || deconstruct.Conversions.Instructions.Count > 0)
continue;
if (!deconstruct.Assignments.Instructions.All(IsDeclarableVariable))
continue;
var designation = StatementBuilder.TranslateDeconstructionDesignation(deconstruct, isForeach: false);
left.ReplaceWith(new DeclarationExpression { Type = new SimpleType("var"), Designation = designation });
foreach (var v in usedVariables) {
variableDict[v].DeclaredInDeconstruction = true;
}
bool IsDeclarableVariable(ILInstruction inst)
{
if (!inst.MatchStLoc(out var v, out var value))
return false;
if (!g.Any(vd => vd.ILVariable == v))
return false;
if (!usedVariables.Add(v))
return false;
var expectedType = ((LdLoc)value).Variable.Type;
if (!v.Type.Equals(expectedType))
return false;
if (!(v.Kind == VariableKind.StackSlot || v.Kind == VariableKind.Local))
return false;
return true;
}
}
}
bool IsMatchingAssignment(VariableToDeclare v, out AssignmentExpression assignment)
{
assignment = v.InsertionPoint.nextNode as AssignmentExpression;
@ -454,7 +493,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -454,7 +493,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
var replacements = new List<(AstNode, AstNode)>();
foreach (var (ilVariable, v) in variableDict) {
if (v.RemovedDueToCollision)
if (v.RemovedDueToCollision || v.DeclaredInDeconstruction)
continue;
if (CombineDeclarationAndInitializer(v, context) && IsMatchingAssignment(v, out AssignmentExpression assignment)) {

Loading…
Cancel
Save