diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs index c4a0133e3..4e861fa4a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs @@ -165,6 +165,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static MyInt? StaticNMy { get; set; } } + private class DeeplyNestedSource + { + public void Deconstruct(out T top, out InnerOuterDeconstructable middle) + { + top = default; + middle = default; + } + } + + [StructLayout(LayoutKind.Sequential, Size = 1)] + private struct InnerDeconstructable + { + public void Deconstruct(out int x, out int y) + { + x = 0; + y = 0; + } + } + + [StructLayout(LayoutKind.Sequential, Size = 1)] + private struct InnerOuterDeconstructable + { + public void Deconstruct(out int x, out InnerDeconstructable y) + { + x = 0; + y = default; + } + } + + private class NestedSource + { + public void Deconstruct(out T outer, out InnerDeconstructable inner) + { + outer = default; + inner = default; + } + } + + private class NestedSourceInnerFirst + { + public void Deconstruct(out InnerDeconstructable inner, out T outer) + { + inner = default; + outer = default; + } + } + + private (string, string) tupleField; + private DeconstructionSource GetSource() { return null; @@ -220,6 +269,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return null; } + private NestedSource GetNestedSource() + { + return null; + } + + private NestedSourceInnerFirst GetNestedSourceInnerFirst() + { + return null; + } + + private DeeplyNestedSource GetDeeplyNestedSource() + { + return null; + } + public void LocalVariable_NoConversion_Custom() { var (myInt3, myInt4) = GetSource(); @@ -1023,5 +1087,178 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty var (num2, text2) = structSource; Console.WriteLine(num2 + text2 + structSource.Dummy); } + + public void DeconstructInIfElse(bool flag) + { + if (flag) + { + var (value, value2) = GetSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + } + else + { + var (value3, value4) = GetTuple(); + Console.WriteLine(value3); + Console.WriteLine(value4); + } + } + + public void DeconstructInsideSwitchCase(int selector) + { + switch (selector) + { + case 1: + var (value3, value4) = GetSource(); + Console.WriteLine(value3); + Console.WriteLine(value4); + break; + case 2: + var (value, value2) = GetTuple(); + Console.WriteLine(value); + Console.WriteLine(value2); + break; + default: + Console.WriteLine("default"); + break; + } + } + + public void DeconstructInsideTry() + { + try + { + var (value, value2) = GetSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + } + catch + { + Console.WriteLine("oops"); + } + } + + public void DeconstructInWhileLoop_Tuple() + { + while (true) + { + var (value, value2) = GetTuple(); + Console.WriteLine(value); + Console.WriteLine(value2); + } + } + + public void DeconstructThreeTupleListForEach(List<(string, int, double)> tuples) + { + foreach (var (text, num, num2) in tuples) + { + Console.WriteLine(text + ": " + num + ", " + num2); + } + } + + public void IndexerSource_Tuple(Dictionary dict, int key) + { + var (value, value2) = dict[key]; + Console.WriteLine(value); + Console.WriteLine(value2); + } + + public void Mixed_LocalAndField_Custom() + { + string value; + (value, Get(0).StringField) = GetSource(); + Console.WriteLine(value); + } + + public void Mixed_LocalAndField_Tuple() + { + string value; + (value, Get(0).StringField) = GetTuple(); + Console.WriteLine(value); + } + + public void Nested_InnerDiscardFirst_Custom() + { + var (value, (_, value2)) = GetNestedSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + } + + public void Nested_InnerDiscardLast_Custom() + { + var (value, (value2, _)) = GetNestedSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + } + + public void Nested_NestedAtPosition0_Custom() + { + var ((value, value2), value3) = GetNestedSourceInnerFirst(); + Console.WriteLine(value); + Console.WriteLine(value2); + Console.WriteLine(value3); + } + + public void Nested_NoConversion_Custom() + { + var (value, (value2, value3)) = GetNestedSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + Console.WriteLine(value3); + } + + public void Nested_PropertyTargets_Custom() + { + (Get(0).String, (Get(1).Int, Get(2).Int)) = GetNestedSource(); + } + + public void Nested_ThreeLevels_Custom() + { + var (value, (value2, (value3, value4))) = GetDeeplyNestedSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + Console.WriteLine(value3); + Console.WriteLine(value4); + } + + public void Property_NoConversion_ReverseOrderFetches_Custom() + { + (Get(1).My, Get(0).NMy) = GetSource(); + } + + public void Property_NoConversion_ReverseOrderFetches_Tuple() + { + (Get(1).My, Get(0).NMy) = GetTuple(); + } + + public void StaticProperty_NoConversion_Custom() + { + (AssignmentTargets.StaticNMy, AssignmentTargets.StaticMy) = GetSource(); + } + + public void StaticProperty_NoConversion_Tuple() + { + (AssignmentTargets.StaticNMy, AssignmentTargets.StaticMy) = GetTuple(); + } + + public void TupleFieldSource() + { + var (value, value2) = tupleField; + Console.WriteLine(value); + Console.WriteLine(value2); + } + + // #4059: csc reuses the same out-slot temporaries for both calls, and hands them to the + // second one in the opposite order, so neither deconstruction is recognized. + //public void TwoBackToBackDeconstructs_Custom() + //{ + // var (value, value2) = GetSource(); + // Console.WriteLine(value); + // Console.WriteLine(value2); + // var (value3, value4) = GetSource(); + // Console.WriteLine(value3); + // Console.WriteLine(value4); + //} + } }