Browse Source

Refactor ForeachStatement to support deconstruction

pull/2119/head
Siegfried Pammer 5 years ago
parent
commit
c9f5e5dd33
  1. 24
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs
  4. 110
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  5. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs
  6. 7
      ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs
  7. 23
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForeachStatement.cs
  8. 47
      ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs
  9. 8
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  10. 2
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs

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

@ -17,10 +17,20 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
public static class DeconstructionExt
{
public static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K key, out V value)
{
key = pair.Key;
value = pair.Value;
}
}
internal class DeconstructionTests internal class DeconstructionTests
{ {
[StructLayout(LayoutKind.Sequential, Size = 1)] [StructLayout(LayoutKind.Sequential, Size = 1)]
@ -241,5 +251,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
(Get(0).IntField, Get(1).IntField) = GetSource<int, int>(); (Get(0).IntField, Get(1).IntField) = GetSource<int, int>();
} }
public void DeconstructDictionaryForEach(Dictionary<string, int> dictionary)
{
foreach (var (str, num2) in dictionary) {
Console.WriteLine(str + ": " + num2);
}
}
public void DeconstructTupleListForEach(List<(string, int)> tuples)
{
foreach (var (str, num) in tuples) {
Console.WriteLine(str + ": " + num);
}
}
} }
} }

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1562,7 +1562,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Space(policy.SpacesWithinForeachParentheses); Space(policy.SpacesWithinForeachParentheses);
foreachStatement.VariableType.AcceptVisitor(this); foreachStatement.VariableType.AcceptVisitor(this);
Space(); Space();
WriteIdentifier(foreachStatement.VariableNameToken); foreachStatement.VariableDesignation.AcceptVisitor(this);
Space();
WriteKeyword(ForeachStatement.InKeywordRole); WriteKeyword(ForeachStatement.InKeywordRole);
Space(); Space();
foreachStatement.InExpression.AcceptVisitor(this); foreachStatement.InExpression.AcceptVisitor(this);

2
ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs

@ -269,7 +269,7 @@ namespace ICSharpCode.Decompiler.CSharp
StartSequencePoint(foreachStatement); StartSequencePoint(foreachStatement);
AddToSequencePoint(foreachInfo.GetCurrentCall); AddToSequencePoint(foreachInfo.GetCurrentCall);
EndSequencePoint(foreachStatement.VariableType.StartLocation, foreachStatement.VariableNameToken.EndLocation); EndSequencePoint(foreachStatement.VariableType.StartLocation, foreachStatement.VariableDesignation.EndLocation);
VisitAsSequencePoint(foreachStatement.EmbeddedStatement); VisitAsSequencePoint(foreachStatement.EmbeddedStatement);
} }

110
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -495,12 +495,9 @@ namespace ICSharpCode.Decompiler.CSharp
if (enumeratorVar2 != enumeratorVar) if (enumeratorVar2 != enumeratorVar)
return null; return null;
// Detect which foreach-variable transformation is necessary/possible. // Detect which foreach-variable transformation is necessary/possible.
var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, conditionInst, out var singleGetter, out var foreachVariable); var transformation = DetectGetCurrentTransformation(container, body, loopContainer, enumeratorVar, conditionInst, out var singleGetter, out var foreachVariable);
if (transformation == RequiredGetCurrentTransformation.NoForeach) if (transformation == RequiredGetCurrentTransformation.NoForeach)
return null; return null;
// The existing foreach variable, if found, can only be used in the loop container.
if (foreachVariable != null && !(foreachVariable.CaptureScope == null || foreachVariable.CaptureScope == loopContainer))
return null;
// Extract in-expression // Extract in-expression
var collectionExpr = m.Get<Expression>("collection").Single(); var collectionExpr = m.Get<Expression>("collection").Single();
// Special case: foreach (var item in this) is decompiled as foreach (var item in base) // Special case: foreach (var item in this) is decompiled as foreach (var item in base)
@ -537,6 +534,8 @@ namespace ICSharpCode.Decompiler.CSharp
break; break;
} }
VariableDesignation designation = null;
// Handle the required foreach-variable transformation: // Handle the required foreach-variable transformation:
switch (transformation) { switch (transformation) {
case RequiredGetCurrentTransformation.UseExistingVariable: case RequiredGetCurrentTransformation.UseExistingVariable:
@ -566,7 +565,18 @@ namespace ICSharpCode.Decompiler.CSharp
body.Instructions.Insert(0, new StLoc(localCopyVariable, new LdLoc(foreachVariable))); body.Instructions.Insert(0, new StLoc(localCopyVariable, new LdLoc(foreachVariable)));
body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
break; break;
case RequiredGetCurrentTransformation.Deconstruction:
useVar = true;
designation = TranslateForeachDeconstructionDesignation((DeconstructInstruction)body.Instructions[0]);
break;
}
if (designation == null) {
designation = new SingleVariableDesignation { Identifier = foreachVariable.Name };
// Add the variable annotation for highlighting
designation.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
} }
// Convert the modified body to C# AST: // Convert the modified body to C# AST:
var whileLoop = (WhileStatement)ConvertAsBlock(container).First(); var whileLoop = (WhileStatement)ConvertAsBlock(container).First();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach(); BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
@ -586,12 +596,10 @@ namespace ICSharpCode.Decompiler.CSharp
// Construct the foreach loop. // Construct the foreach loop.
var foreachStmt = new ForeachStatement { var foreachStmt = new ForeachStatement {
VariableType = useVar ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type), VariableType = useVar ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type),
VariableName = foreachVariable.Name, VariableDesignation = designation,
InExpression = collectionExpr.Detach(), InExpression = collectionExpr.Detach(),
EmbeddedStatement = foreachBody EmbeddedStatement = foreachBody
}; };
// Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
foreachStmt.AddAnnotation(new ForeachAnnotation(inst.ResourceExpression, conditionInst, singleGetter)); foreachStmt.AddAnnotation(new ForeachAnnotation(inst.ResourceExpression, conditionInst, singleGetter));
foreachStmt.CopyAnnotationsFrom(whileLoop); foreachStmt.CopyAnnotationsFrom(whileLoop);
// If there was an optional return statement, return it as well. // If there was an optional return statement, return it as well.
@ -606,6 +614,37 @@ namespace ICSharpCode.Decompiler.CSharp
return foreachStmt; return foreachStmt;
} }
VariableDesignation TranslateForeachDeconstructionDesignation(DeconstructInstruction inst)
{
var assignments = inst.Assignments.Instructions;
int assignmentPos = 0;
return ConstructDesignation(inst.Pattern);
VariableDesignation ConstructDesignation(MatchInstruction matchInstruction)
{
var designations = new ParenthesizedVariableDesignation();
foreach (var subPattern in matchInstruction.SubPatterns.Cast<MatchInstruction>()) {
if (subPattern.IsVar) {
var designation = new SingleVariableDesignation();
if (subPattern.HasDesignator) {
ILVariable v = ((StLoc)assignments[assignmentPos]).Variable;
v.Kind = VariableKind.ForeachLocal;
designation.Identifier = v.Name;
designation.AddAnnotation(new ILVariableResolveResult(v));
assignmentPos++;
} else {
designation.Identifier = "_";
}
designations.VariableDesignations.Add(designation);
} else {
designations.VariableDesignations.Add(ConstructDesignation(subPattern));
}
}
return designations;
}
}
static bool EqualErasedType(IType a, IType b) static bool EqualErasedType(IType a, IType b)
{ {
return NormalizeTypeVisitor.TypeErasure.EquivalentTypes(a, b); return NormalizeTypeVisitor.TypeErasure.EquivalentTypes(a, b);
@ -694,7 +733,12 @@ namespace ICSharpCode.Decompiler.CSharp
/// ... (ldloca copy) ... /// ... (ldloca copy) ...
/// </code> /// </code>
/// </summary> /// </summary>
IntroduceNewVariableAndLocalCopy IntroduceNewVariableAndLocalCopy,
/// <summary>
/// call get_Current() is the tested operand of a deconstruct instruction.
/// and the deconstruct instruction is the first statement in the loop body.
/// </summary>
Deconstruction,
} }
/// <summary> /// <summary>
@ -707,11 +751,14 @@ namespace ICSharpCode.Decompiler.CSharp
/// <param name="singleGetter">Returns the call instruction invoking Current's getter.</param> /// <param name="singleGetter">Returns the call instruction invoking Current's getter.</param>
/// <param name="foreachVariable">Returns the the foreach variable, if a suitable was found. This variable is only assigned once and its assignment is the first statement in <paramref name="loopBody"/>.</param> /// <param name="foreachVariable">Returns the the foreach variable, if a suitable was found. This variable is only assigned once and its assignment is the first statement in <paramref name="loopBody"/>.</param>
/// <returns><see cref="RequiredGetCurrentTransformation"/> for details.</returns> /// <returns><see cref="RequiredGetCurrentTransformation"/> for details.</returns>
RequiredGetCurrentTransformation DetectGetCurrentTransformation(BlockContainer usingContainer, Block loopBody, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out ILVariable foreachVariable) RequiredGetCurrentTransformation DetectGetCurrentTransformation(BlockContainer usingContainer, Block loopBody, BlockContainer loopContainer, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out ILVariable foreachVariable)
{ {
singleGetter = null; singleGetter = null;
foreachVariable = null; foreachVariable = null;
var loads = (enumerator.LoadInstructions.OfType<ILInstruction>().Concat(enumerator.AddressInstructions.OfType<ILInstruction>())).Where(ld => !ld.IsDescendantOf(moveNextUsage)).ToArray(); var loads = enumerator.LoadInstructions.OfType<ILInstruction>()
.Concat(enumerator.AddressInstructions.OfType<ILInstruction>())
.Where(ld => !ld.IsDescendantOf(moveNextUsage))
.ToArray();
// enumerator is used in multiple locations or not in conjunction with get_Current // enumerator is used in multiple locations or not in conjunction with get_Current
// => no foreach // => no foreach
if (loads.Length != 1 || !ParentIsCurrentGetter(loads[0])) if (loads.Length != 1 || !ParentIsCurrentGetter(loads[0]))
@ -719,8 +766,17 @@ namespace ICSharpCode.Decompiler.CSharp
singleGetter = (CallInstruction)loads[0].Parent; singleGetter = (CallInstruction)loads[0].Parent;
// singleGetter is not part of the first instruction in body or cannot be uninlined // singleGetter is not part of the first instruction in body or cannot be uninlined
// => no foreach // => no foreach
if (!(singleGetter.IsDescendantOf(loopBody.Instructions[0]) && ILInlining.CanUninline(singleGetter, loopBody.Instructions[0]))) if (!(singleGetter.IsDescendantOf(loopBody.Instructions[0])
&& ILInlining.CanUninline(singleGetter, loopBody.Instructions[0])))
{
return RequiredGetCurrentTransformation.NoForeach; return RequiredGetCurrentTransformation.NoForeach;
}
if (loopBody.Instructions[0] is DeconstructInstruction deconstruction
&& singleGetter == deconstruction.Pattern.TestedOperand
&& CanBeDeconstructedInForeach(deconstruction, usingContainer, loopContainer))
{
return RequiredGetCurrentTransformation.Deconstruction;
}
ILInstruction inst = singleGetter; ILInstruction inst = singleGetter;
// in some cases, i.e. foreach variable with explicit type different from the collection-item-type, // in some cases, i.e. foreach variable with explicit type different from the collection-item-type,
// the result of call get_Current is casted. // the result of call get_Current is casted.
@ -729,7 +785,7 @@ namespace ICSharpCode.Decompiler.CSharp
// One variable was found. // One variable was found.
if (inst.Parent is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) { if (inst.Parent is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) {
// Must be a plain assignment expression and variable must only be used in 'body' + only assigned once. // Must be a plain assignment expression and variable must only be used in 'body' + only assigned once.
if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer)) { if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer, loopContainer)) {
foreachVariable = stloc.Variable; foreachVariable = stloc.Variable;
return RequiredGetCurrentTransformation.UseExistingVariable; return RequiredGetCurrentTransformation.UseExistingVariable;
} }
@ -743,13 +799,39 @@ namespace ICSharpCode.Decompiler.CSharp
return RequiredGetCurrentTransformation.IntroduceNewVariable; return RequiredGetCurrentTransformation.IntroduceNewVariable;
} }
bool CanBeDeconstructedInForeach(DeconstructInstruction deconstruction, BlockContainer usingContainer, BlockContainer loopContainer)
{
if (deconstruction.Init.Count > 0)
return false;
if (deconstruction.Conversions.Instructions.Count > 0)
return false;
var operandType = deconstruction.Pattern.TestedOperand.InferType(this.typeSystem);
var expectedType = deconstruction.Pattern.Variable.Type;
if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(operandType, expectedType))
return false;
foreach (var item in deconstruction.Assignments.Instructions) {
if (!item.MatchStLoc(out var v, out var value))
return false;
expectedType = ((LdLoc)value).Variable.Type;
if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(v.Type, expectedType))
return false;
if (!(v.Kind == VariableKind.StackSlot || v.Kind == VariableKind.Local))
return false;
if (!VariableIsOnlyUsedInBlock((StLoc)item, usingContainer, loopContainer))
return false;
if (!(v.CaptureScope == null || v.CaptureScope == usingContainer))
return false;
}
return true;
}
/// <summary> /// <summary>
/// Determines whether storeInst.Variable is only assigned once and used only inside <paramref name="usingContainer"/>. /// Determines whether storeInst.Variable is only assigned once and used only inside <paramref name="usingContainer"/>.
/// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions, /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions,
/// or as target of ldobj. /// or as target of ldobj.
/// (This only applies to value types.) /// (This only applies to value types.)
/// </summary> /// </summary>
bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer) bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer, BlockContainer loopContainer)
{ {
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
return false; return false;
@ -757,6 +839,8 @@ namespace ICSharpCode.Decompiler.CSharp
return false; return false;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst)) if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false; return false;
if (!(storeInst.Variable.CaptureScope == null || storeInst.Variable.CaptureScope == loopContainer))
return false;
return true; return true;
bool AddressUseAllowed(LdLoca la) bool AddressUseAllowed(LdLoca la)

4
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs

@ -31,8 +31,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
public VariableDesignation Designation { public VariableDesignation Designation {
get { return GetChildByRole(VariableDesignation.VariableDesignationRole); } get { return GetChildByRole(Roles.VariableDesignationRole); }
set { SetChildByRole(VariableDesignation.VariableDesignationRole, value); } set { SetChildByRole(Roles.VariableDesignationRole, value); }
} }
public override void AcceptVisitor(IAstVisitor visitor) public override void AcceptVisitor(IAstVisitor visitor)

7
ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs

@ -46,11 +46,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly Role<VariableInitializer> Variable = new Role<VariableInitializer> ("Variable", VariableInitializer.Null); public static readonly Role<VariableInitializer> Variable = new Role<VariableInitializer> ("Variable", VariableInitializer.Null);
public static readonly Role<Statement> EmbeddedStatement = new Role<Statement> ("EmbeddedStatement", Statement.Null); public static readonly Role<Statement> EmbeddedStatement = new Role<Statement> ("EmbeddedStatement", Statement.Null);
public readonly static Role<EntityDeclaration> TypeMemberRole = new Role<EntityDeclaration> ("TypeMember"); public readonly static Role<EntityDeclaration> TypeMemberRole = new Role<EntityDeclaration> ("TypeMember");
public static readonly Role<VariableDesignation> VariableDesignationRole = new Role<VariableDesignation>("VariableDesignation", VariableDesignation.Null);
// public static readonly TokenRole Keyword = new TokenRole ("Keyword", CSharpTokenNode.Null); // public static readonly TokenRole Keyword = new TokenRole ("Keyword", CSharpTokenNode.Null);
// public static readonly TokenRole InKeyword = new TokenRole ("InKeyword", CSharpTokenNode.Null); // public static readonly TokenRole InKeyword = new TokenRole ("InKeyword", CSharpTokenNode.Null);
// some pre defined constants for most used punctuation // some pre defined constants for most used punctuation
public static readonly TokenRole LPar = new TokenRole ("("); public static readonly TokenRole LPar = new TokenRole ("(");
public static readonly TokenRole RPar = new TokenRole (")"); public static readonly TokenRole RPar = new TokenRole (")");

23
ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForeachStatement.cs

@ -47,23 +47,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
get { return GetChildByRole (Roles.Type); } get { return GetChildByRole (Roles.Type); }
set { SetChildByRole (Roles.Type, value); } set { SetChildByRole (Roles.Type, value); }
} }
public string VariableName { public VariableDesignation VariableDesignation {
get { get { return GetChildByRole(Roles.VariableDesignationRole); }
return GetChildByRole (Roles.Identifier).Name; set { SetChildByRole(Roles.VariableDesignationRole, value); }
}
set {
SetChildByRole(Roles.Identifier, Identifier.Create (value));
}
}
public Identifier VariableNameToken {
get {
return GetChildByRole (Roles.Identifier);
}
set {
SetChildByRole(Roles.Identifier, value);
}
} }
public CSharpTokenNode InToken { public CSharpTokenNode InToken {
@ -102,7 +89,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{ {
ForeachStatement o = other as ForeachStatement; ForeachStatement o = other as ForeachStatement;
return o != null && this.VariableType.DoMatch(o.VariableType, match) && MatchString(this.VariableName, o.VariableName) return o != null && this.VariableType.DoMatch(o.VariableType, match) && this.VariableDesignation.DoMatch(o.VariableDesignation, match)
&& this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); && this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match);
} }
} }

47
ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs

@ -16,19 +16,47 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.DebugInfo;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public abstract class VariableDesignation : AstNode public abstract class VariableDesignation : AstNode
{ {
public static Role<VariableDesignation> VariableDesignationRole = new Role<VariableDesignation>("VariableDesignation"); public override NodeType NodeType => NodeType.Unknown;
#region Null
public new static readonly VariableDesignation Null = new NullVariableDesignation();
sealed class NullVariableDesignation : VariableDesignation
{
public override bool IsNull {
get {
return true;
}
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitNullNode(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitNullNode(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitNullNode(this, data);
}
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other == null || other.IsNull;
}
}
#endregion
} }
/// <summary> /// <summary>
@ -36,7 +64,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary> /// </summary>
public class SingleVariableDesignation : VariableDesignation public class SingleVariableDesignation : VariableDesignation
{ {
public override NodeType NodeType => NodeType.Unknown;
public string Identifier { public string Identifier {
get { return GetChildByRole(Roles.Identifier).Name; } get { return GetChildByRole(Roles.Identifier).Name; }
@ -80,15 +107,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
public AstNodeCollection<VariableDesignation> VariableDesignations { public AstNodeCollection<VariableDesignation> VariableDesignations {
get { return GetChildrenByRole(VariableDesignation.VariableDesignationRole); } get { return GetChildrenByRole(Roles.VariableDesignationRole); }
} }
public CSharpTokenNode RParToken { public CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); } get { return GetChildByRole(Roles.RPar); }
} }
public override NodeType NodeType => NodeType.Unknown;
public override void AcceptVisitor(IAstVisitor visitor) public override void AcceptVisitor(IAstVisitor visitor)
{ {
visitor.VisitParenthesizedVariableDesignation(this); visitor.VisitParenthesizedVariableDesignation(this);

8
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -337,14 +337,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
body.Statements.Add(statement.Detach()); body.Statements.Add(statement.Detach());
var foreachStmt = new ForeachStatement { var foreachStmt = new ForeachStatement {
VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type), VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type),
VariableName = itemVariable.Name, VariableDesignation = new SingleVariableDesignation { Identifier = itemVariable.Name },
InExpression = m.Get<IdentifierExpression>("arrayVariable").Single().Detach(), InExpression = m.Get<IdentifierExpression>("arrayVariable").Single().Detach(),
EmbeddedStatement = body EmbeddedStatement = body
}; };
foreachStmt.CopyAnnotationsFrom(forStatement); foreachStmt.CopyAnnotationsFrom(forStatement);
itemVariable.Kind = IL.VariableKind.ForeachLocal; itemVariable.Kind = IL.VariableKind.ForeachLocal;
// Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement). // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type)); foreachStmt.VariableDesignation.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
// TODO : add ForeachAnnotation // TODO : add ForeachAnnotation
forStatement.ReplaceWith(foreachStmt); forStatement.ReplaceWith(foreachStmt);
return foreachStmt; return foreachStmt;
@ -495,7 +495,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
body.Statements.Add(statement.Detach()); body.Statements.Add(statement.Detach());
var foreachStmt = new ForeachStatement { var foreachStmt = new ForeachStatement {
VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type), VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type),
VariableName = itemVariable.Name, VariableDesignation = new SingleVariableDesignation { Identifier = itemVariable.Name },
InExpression = m.Get<IdentifierExpression>("collection").Single().Detach(), InExpression = m.Get<IdentifierExpression>("collection").Single().Detach(),
EmbeddedStatement = body EmbeddedStatement = body
}; };
@ -504,7 +504,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
//foreachStmt.CopyAnnotationsFrom(forStatement); //foreachStmt.CopyAnnotationsFrom(forStatement);
itemVariable.Kind = IL.VariableKind.ForeachLocal; itemVariable.Kind = IL.VariableKind.ForeachLocal;
// Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement). // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type)); foreachStmt.VariableDesignation.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
// TODO : add ForeachAnnotation // TODO : add ForeachAnnotation
expressionStatement.ReplaceWith(foreachStmt); expressionStatement.ReplaceWith(foreachStmt);
return foreachStmt; return foreachStmt;

2
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -164,7 +164,7 @@ namespace ICSharpCode.Decompiler
if (node is Identifier && node.Parent != null) if (node is Identifier && node.Parent != null)
node = node.Parent; node = node.Parent;
if (node is ParameterDeclaration || node is VariableInitializer || node is CatchClause || node is ForeachStatement) { if (node is ParameterDeclaration || node is VariableInitializer || node is CatchClause || node is VariableDesignation) {
var variable = node.Annotation<ILVariableResolveResult>()?.Variable; var variable = node.Annotation<ILVariableResolveResult>()?.Variable;
if (variable != null) if (variable != null)
return variable; return variable;

Loading…
Cancel
Save