mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
Pin the deconstruction shapes the transform has to keep matching
Twenty shapes that no fixture covered: nesting at depth three and at position zero, inner discards on either side, property and no-conversion targets, and deconstruction inside try, switch, if/else and while. All but one already decompile correctly - they are checked in so a future change to DeconstructionTransform cannot silently drop them. The one that does not is left commented out with a pointer to #4059 rather than as a red test: two back-to-back deconstructions share their out-slot temporaries, and neither is recognized. #4059 Assisted-by: Claude:claude-opus-5[1m]:Claude Code
This commit is contained in:
committed by
Siegfried Pammer
parent
27b45a4458
commit
a980639f99
@@ -165,6 +165,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
|
||||
public static MyInt? StaticNMy { get; set; }
|
||||
}
|
||||
|
||||
private class DeeplyNestedSource<T>
|
||||
{
|
||||
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<T>
|
||||
{
|
||||
public void Deconstruct(out T outer, out InnerDeconstructable inner)
|
||||
{
|
||||
outer = default;
|
||||
inner = default;
|
||||
}
|
||||
}
|
||||
|
||||
private class NestedSourceInnerFirst<T>
|
||||
{
|
||||
public void Deconstruct(out InnerDeconstructable inner, out T outer)
|
||||
{
|
||||
inner = default;
|
||||
outer = default;
|
||||
}
|
||||
}
|
||||
|
||||
private (string, string) tupleField;
|
||||
|
||||
private DeconstructionSource<T, T2> GetSource<T, T2>()
|
||||
{
|
||||
return null;
|
||||
@@ -220,6 +269,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
|
||||
return null;
|
||||
}
|
||||
|
||||
private NestedSource<T> GetNestedSource<T>()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private NestedSourceInnerFirst<T> GetNestedSourceInnerFirst<T>()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private DeeplyNestedSource<T> GetDeeplyNestedSource<T>()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void LocalVariable_NoConversion_Custom()
|
||||
{
|
||||
var (myInt3, myInt4) = GetSource<MyInt?, MyInt>();
|
||||
@@ -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<string, string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var (value3, value4) = GetTuple<string, string>();
|
||||
Console.WriteLine(value3);
|
||||
Console.WriteLine(value4);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeconstructInsideSwitchCase(int selector)
|
||||
{
|
||||
switch (selector)
|
||||
{
|
||||
case 1:
|
||||
var (value3, value4) = GetSource<string, string>();
|
||||
Console.WriteLine(value3);
|
||||
Console.WriteLine(value4);
|
||||
break;
|
||||
case 2:
|
||||
var (value, value2) = GetTuple<string, string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("default");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeconstructInsideTry()
|
||||
{
|
||||
try
|
||||
{
|
||||
var (value, value2) = GetSource<string, string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("oops");
|
||||
}
|
||||
}
|
||||
|
||||
public void DeconstructInWhileLoop_Tuple()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var (value, value2) = GetTuple<string, string>();
|
||||
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<int, (string, int)> 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<string, string>();
|
||||
Console.WriteLine(value);
|
||||
}
|
||||
|
||||
public void Mixed_LocalAndField_Tuple()
|
||||
{
|
||||
string value;
|
||||
(value, Get(0).StringField) = GetTuple<string, string>();
|
||||
Console.WriteLine(value);
|
||||
}
|
||||
|
||||
public void Nested_InnerDiscardFirst_Custom()
|
||||
{
|
||||
var (value, (_, value2)) = GetNestedSource<string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
}
|
||||
|
||||
public void Nested_InnerDiscardLast_Custom()
|
||||
{
|
||||
var (value, (value2, _)) = GetNestedSource<string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
}
|
||||
|
||||
public void Nested_NestedAtPosition0_Custom()
|
||||
{
|
||||
var ((value, value2), value3) = GetNestedSourceInnerFirst<string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
Console.WriteLine(value3);
|
||||
}
|
||||
|
||||
public void Nested_NoConversion_Custom()
|
||||
{
|
||||
var (value, (value2, value3)) = GetNestedSource<string>();
|
||||
Console.WriteLine(value);
|
||||
Console.WriteLine(value2);
|
||||
Console.WriteLine(value3);
|
||||
}
|
||||
|
||||
public void Nested_PropertyTargets_Custom()
|
||||
{
|
||||
(Get(0).String, (Get(1).Int, Get(2).Int)) = GetNestedSource<string>();
|
||||
}
|
||||
|
||||
public void Nested_ThreeLevels_Custom()
|
||||
{
|
||||
var (value, (value2, (value3, value4))) = GetDeeplyNestedSource<string>();
|
||||
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<MyInt, MyInt?>();
|
||||
}
|
||||
|
||||
public void Property_NoConversion_ReverseOrderFetches_Tuple()
|
||||
{
|
||||
(Get(1).My, Get(0).NMy) = GetTuple<MyInt, MyInt?>();
|
||||
}
|
||||
|
||||
public void StaticProperty_NoConversion_Custom()
|
||||
{
|
||||
(AssignmentTargets.StaticNMy, AssignmentTargets.StaticMy) = GetSource<MyInt?, MyInt>();
|
||||
}
|
||||
|
||||
public void StaticProperty_NoConversion_Tuple()
|
||||
{
|
||||
(AssignmentTargets.StaticNMy, AssignmentTargets.StaticMy) = GetTuple<MyInt?, MyInt>();
|
||||
}
|
||||
|
||||
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<string, string>();
|
||||
// Console.WriteLine(value);
|
||||
// Console.WriteLine(value2);
|
||||
// var (value3, value4) = GetSource<string, string>();
|
||||
// Console.WriteLine(value3);
|
||||
// Console.WriteLine(value4);
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user