diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs index 494bf801b..1fc53d916 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs @@ -551,6 +551,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } + // Same, but the escaping element is in the first position. Every leaf of the + // wrongly nested node precedes the assigned ones there, so the retry that demotes + // it has to be reached before the pattern is judged to start mid-way. + public void LocalVariable_TupleInner_FirstElementUsedOutside() + { + var (tuple2, value) = GetTuple<(int, int), int>(); + Console.WriteLine(tuple2.Item1); + Console.WriteLine(value); + } + public void ForEach_Nested_TupleInner() { foreach (var (value, (value2, value3)) in GetList<(int, (int, int))>()) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs index 4e1eeaa0e..63324af82 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs @@ -210,22 +210,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms // the forwarding fixup in MatchAssignments. if (!anyAssignments && !(rootCall != null && rootCall.NestedCalls.Any(c => c != null))) return false; - // first tuple element may not be discarded, - // otherwise we would run this transform on a suffix of the actual pattern. - if (deconstructionResults[0] == null) - return false; // A nested tuple designation only holds if the pattern consumed every read of // its temporary; a remaining read means the value escapes the designation. // Retry with the variable as a plain designator leaf, which restores the flat - // deconstruction the escaping read needs. + // deconstruction the escaping read needs. This has to be decided before the + // leaf check below: every leaf of a wrongly nested first element precedes the + // assigned ones, so that check would report the pattern as starting mid-way + // and give up on a designation the retry can still make work. var escaped = EscapedTupleNodes(); - if (escaped == null) + if (escaped != null) { - endPos = pos; - return true; + doNotNest ??= new HashSet(); + doNotNest.UnionWith(escaped); + continue; } - doNotNest ??= new HashSet(); - doNotNest.UnionWith(escaped); + // first tuple element may not be discarded, + // otherwise we would run this transform on a suffix of the actual pattern. + if (deconstructionResults[0] == null) + return false; + endPos = pos; + return true; } List? EscapedTupleNodes()