diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
index 477074c8e..41ee16cf0 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
@@ -36,6 +36,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
currentMethod = context.DecompiledMember as IMethod;
}
+ protected void Uninitialize()
+ {
+ currentTypeDefinition = null;
+ currentMethod = null;
+ }
+
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
ITypeDefinition oldType = currentTypeDefinition;
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
index b73ab839b..ae4775303 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
@@ -37,6 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// Represents a position immediately before nextNode.
/// nextNode is either an ExpressionStatement in a BlockStatement, or an initializer in a for-loop.
///
+ [DebuggerDisplay("level = {level}, nextNode = {nextNode}")]
struct InsertionPoint
{
///
@@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
+ [DebuggerDisplay("VariableToDeclare(Name={Name})")]
class VariableToDeclare
{
public readonly IType Type;
@@ -87,9 +89,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public int SourceOrder;
public InsertionPoint InsertionPoint;
-
- public bool RemovedDueToCollision;
-
+
+ public VariableToDeclare ReplacementDueToCollision;
+ public bool RemovedDueToCollision => ReplacementDueToCollision != null;
+
public VariableToDeclare(IType type, string name, bool defaultInitialization, InsertionPoint insertionPoint, int sourceOrder)
{
this.Type = type;
@@ -102,22 +105,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
readonly Dictionary variableDict = new Dictionary();
- TransformContext context;
-
public void Run(AstNode rootNode, TransformContext context)
{
try {
- this.context = context;
+ variableDict.Clear();
EnsureExpressionStatementsAreValid(rootNode);
FindInsertionPoints(rootNode, 0);
ResolveCollisions();
- InsertVariableDeclarations();
+ InsertVariableDeclarations(context);
} finally {
variableDict.Clear();
- this.context = null;
}
}
+ ///
+ /// Analyze the input AST (containing undeclared variables)
+ /// for where those variables would be declared by this transform.
+ /// Analysis does not modify the AST.
+ ///
+ public void Analyze(AstNode rootNode)
+ {
+ variableDict.Clear();
+ FindInsertionPoints(rootNode, 0);
+ ResolveCollisions();
+ }
+
+ ///
+ /// Get the position where the declaration for the variable will be inserted.
+ ///
+ public AstNode GetDeclarationPoint(ILVariable variable)
+ {
+ VariableToDeclare v = variableDict[variable];
+ while (v.ReplacementDueToCollision != null) {
+ v = v.ReplacementDueToCollision;
+ }
+ return v.InsertionPoint.nextNode;
+ }
+
+ public void ClearAnalysisResults()
+ {
+ variableDict.Clear();
+ }
+
#region EnsureExpressionStatementsAreValid
void EnsureExpressionStatementsAreValid(AstNode rootNode)
{
@@ -297,7 +326,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Debug.Assert(point1.level == point2.level);
if (point1.nextNode.Parent == point2.nextNode.Parent) {
// We found a collision!
- prev.RemovedDueToCollision = true;
+ prev.ReplacementDueToCollision = v;
// Continue checking other entries in multiDict against the new position of `v`.
if (prev.SourceOrder < v.SourceOrder) {
// If we switch v's insertion point to prev's insertion point,
@@ -318,7 +347,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
- void InsertVariableDeclarations()
+ void InsertVariableDeclarations(TransformContext context)
{
var replacements = new List>();
foreach (var p in variableDict) {
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
index e55335c21..f30226665 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
@@ -35,13 +35,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
///
public sealed class PatternStatementTransform : ContextTrackingVisitor, IAstTransform
{
+ readonly DeclareVariables declareVariables = new DeclareVariables();
TransformContext context;
public void Run(AstNode rootNode, TransformContext context)
{
- this.context = context;
- base.Initialize(context);
- rootNode.AcceptVisitor(this);
+ if (this.context != null)
+ throw new InvalidOperationException("Reentrancy in PatternStatementTransform.Run?");
+ try {
+ this.context = context;
+ base.Initialize(context);
+ declareVariables.Analyze(rootNode);
+ rootNode.AcceptVisitor(this);
+ } finally {
+ this.context = null;
+ base.Uninitialize();
+ declareVariables.ClearAnalysisResults();
+ }
}
#region Visitor Overrides
@@ -434,7 +444,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return foreachStatement;
}
- static bool VariableCanBeDeclaredInLoop(IL.ILVariable itemVar, WhileStatement loop)
+ bool VariableCanBeDeclaredInLoop(IL.ILVariable itemVar, WhileStatement loop)
{
if (itemVar == null || !(itemVar.Kind == IL.VariableKind.Local || itemVar.Kind == IL.VariableKind.StackSlot)) {
// only locals/temporaries can be converted into foreach loop variable
@@ -457,7 +467,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// captured variables cannot be declared in the loop unless the loop is their capture scope
return false;
}
- return true;
+
+ AstNode declPoint = declareVariables.GetDeclarationPoint(itemVar);
+ return declPoint.Ancestors.Contains(loop);
}
static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop)
diff --git a/ICSharpCode.Decompiler/Tests/TestCases/Pretty/Loops.cs b/ICSharpCode.Decompiler/Tests/TestCases/Pretty/Loops.cs
index 9c5c73b60..31f562821 100644
--- a/ICSharpCode.Decompiler/Tests/TestCases/Pretty/Loops.cs
+++ b/ICSharpCode.Decompiler/Tests/TestCases/Pretty/Loops.cs
@@ -197,5 +197,42 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("End of method");
}
+
+ public void DoubleForEachWithSameVariable(IEnumerable enumerable)
+ {
+ foreach (string current in enumerable) {
+ current.ToLower();
+ }
+ foreach (string current in enumerable) {
+ current.ToLower();
+ }
+ }
+
+ static void ForeachExceptForNameCollision(IEnumerable inputs)
+ {
+ Console.WriteLine("ForeachWithNameCollision");
+ int input;
+ using (var enumerator = inputs.GetEnumerator()) {
+ while (enumerator.MoveNext()) {
+ input = enumerator.Current;
+ Console.WriteLine(input);
+ }
+ }
+ input = 1;
+ Console.WriteLine(input);
+ }
+
+ static void ForeachExceptForContinuedUse(IEnumerable inputs)
+ {
+ Console.WriteLine("ForeachExceptForContinuedUse");
+ int input = 0;
+ using (var enumerator = inputs.GetEnumerator()) {
+ while (enumerator.MoveNext()) {
+ input = enumerator.Current;
+ Console.WriteLine(input);
+ }
+ }
+ Console.WriteLine("Last: " + input);
+ }
}
}