diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index a1df5da38..bde7abd16 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -430,19 +430,45 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return false; } + var blockContainer = loop.Annotation(); + if (!itemVar.IsSingleDefinition) { - // foreach variable cannot be assigned to - return false; + // foreach variable cannot be assigned to. + // As a special case, we accept taking the address for a method call, + // but only if the call is the only use, so that any mutation by the call + // cannot be observed. + if (!AddressUsedForSingleCall(itemVar, blockContainer)) { + return false; + } } - if (itemVar.CaptureScope != null && itemVar.CaptureScope != loop.Annotation()) { + if (itemVar.CaptureScope != null && itemVar.CaptureScope != blockContainer) { // captured variables cannot be declared in the loop unless the loop is their capture scope return false; } return true; } + + static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop) + { + if (v.StoreCount == 1 && v.AddressCount == 1 && v.LoadCount == 0 && v.Type.IsReferenceType == false) { + if (v.AddressInstructions[0].Parent is IL.Call call + && v.AddressInstructions[0].ChildIndex == 0 + && !call.Method.IsStatic) { + // used as this pointer for a method call + // this is OK iff the call is not within a nested loop + for (var node = call.Parent; node != null; node = node.Parent) { + if (node == loop) + return true; + else if (node is IL.BlockContainer) + break; + } + } + } + return false; + } #endregion - + #region foreach (non-generic) ExpressionStatement getEnumeratorPattern = new ExpressionStatement( new AssignmentExpression( diff --git a/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs index 0478992b9..9a399d95d 100644 --- a/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs @@ -103,7 +103,7 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } - [Test, Ignore("Not fully working yet")] + [Test] public void Loops([ValueSource("defaultOptions")] CompilerOptions cscOptions) { Run(cscOptions: cscOptions);