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
public void LocalVariable_NoConversion() public void LocalVariable_NoConversion()
{ {
MyInt? myInt3; var (myInt3, x) = GetSource<MyInt?, MyInt>();
MyInt x;
(myInt3, x) = GetSource<MyInt?, MyInt>();
Console.WriteLine(myInt3); Console.WriteLine(myInt3);
Console.WriteLine(x); Console.WriteLine(x);
} }
public void LocalVariable_NoConversion_DiscardFirst() public void LocalVariable_NoConversion_DiscardFirst()
{ {
MyInt x; var (_, x, value) = GetSource<MyInt?, MyInt, int>();
int value;
(_, x, value) = GetSource<MyInt?, MyInt, int>();
Console.WriteLine(x); Console.WriteLine(x);
Console.WriteLine(value); Console.WriteLine(value);
} }
public void LocalVariable_NoConversion_DiscardLast() public void LocalVariable_NoConversion_DiscardLast()
{ {
MyInt? myInt3; var (myInt3, x, _) = GetSource<MyInt?, MyInt, int>();
MyInt x;
(myInt3, x, _) = GetSource<MyInt?, MyInt, int>();
Console.WriteLine(myInt3); Console.WriteLine(myInt3);
Console.WriteLine(x); Console.WriteLine(x);
} }
public void LocalVariable_NoConversion_DiscardSecond() public void LocalVariable_NoConversion_DiscardSecond()
{ {
MyInt? myInt3; var (myInt3, _, value) = GetSource<MyInt?, MyInt, int>();
int value;
(myInt3, _, value) = GetSource<MyInt?, MyInt, int>();
Console.WriteLine(myInt3); Console.WriteLine(myInt3);
Console.WriteLine(value); Console.WriteLine(value);
} }
public void LocalVariable_NoConversion_ReferenceTypes() public void LocalVariable_NoConversion_ReferenceTypes()
{ {
string value; var (value, value2) = GetSource<string, string>();
string value2;
(value, value2) = GetSource<string, string>();
Console.WriteLine(value); Console.WriteLine(value);
Console.WriteLine(value2); Console.WriteLine(value2);
} }
public void LocalVariable_IntToLongConversion() public void LocalVariable_IntToLongConversion()
{ {
int value; var (value, value2) = GetSource<int, int>();
long value2;
(value, value2) = GetSource<int, int>();
Console.WriteLine(value); Console.WriteLine(value);
Console.WriteLine(value2); Console.WriteLine(value2);
} }
public void LocalVariable_NoConversion_ComplexValue() public void LocalVariable_NoConversion_ComplexValue()
{ {
MyInt? myInt3; var (myInt3, x) = new DeconstructionSource<MyInt?, MyInt> {
MyInt x;
(myInt3, x) = new DeconstructionSource<MyInt?, MyInt> {
Dummy = 3 Dummy = 3
}; };
Console.WriteLine(myInt3); Console.WriteLine(myInt3);

7
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

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

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

@ -102,6 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public VariableToDeclare ReplacementDueToCollision; public VariableToDeclare ReplacementDueToCollision;
public bool InvolvedInCollision; public bool InvolvedInCollision;
public bool RemovedDueToCollision => ReplacementDueToCollision != null; public bool RemovedDueToCollision => ReplacementDueToCollision != null;
public bool DeclaredInDeconstruction;
public VariableToDeclare(ILVariable variable, bool defaultInitialization, InsertionPoint insertionPoint, IdentifierExpression firstUse, int sourceOrder) public VariableToDeclare(ILVariable variable, bool defaultInitialization, InsertionPoint insertionPoint, IdentifierExpression firstUse, int sourceOrder)
{ {
@ -126,6 +127,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
EnsureExpressionStatementsAreValid(rootNode); EnsureExpressionStatementsAreValid(rootNode);
FindInsertionPoints(rootNode, 0); FindInsertionPoints(rootNode, 0);
ResolveCollisions(); ResolveCollisions();
InsertDeconstructionVariableDeclarations();
InsertVariableDeclarations(context); InsertVariableDeclarations(context);
UpdateAnnotations(rootNode); UpdateAnnotations(rootNode);
} finally { } finally {
@ -133,7 +135,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
variableDict.Clear(); variableDict.Clear();
} }
} }
/// <summary> /// <summary>
/// Analyze the input AST (containing undeclared variables) /// Analyze the input AST (containing undeclared variables)
/// for where those variables would be declared by this transform. /// for where those variables would be declared by this transform.
@ -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) bool IsMatchingAssignment(VariableToDeclare v, out AssignmentExpression assignment)
{ {
assignment = v.InsertionPoint.nextNode as AssignmentExpression; assignment = v.InsertionPoint.nextNode as AssignmentExpression;
@ -454,7 +493,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
var replacements = new List<(AstNode, AstNode)>(); var replacements = new List<(AstNode, AstNode)>();
foreach (var (ilVariable, v) in variableDict) { foreach (var (ilVariable, v) in variableDict) {
if (v.RemovedDueToCollision) if (v.RemovedDueToCollision || v.DeclaredInDeconstruction)
continue; continue;
if (CombineDeclarationAndInitializer(v, context) && IsMatchingAssignment(v, out AssignmentExpression assignment)) { if (CombineDeclarationAndInitializer(v, context) && IsMatchingAssignment(v, out AssignmentExpression assignment)) {

Loading…
Cancel
Save