Browse Source

Add null and not null patterns for nullable value types

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
8e63d92886
  1. 96
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 38
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

96
ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs

@ -503,6 +503,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void RecursivePattern_NullableIntField_Null(object obj)
{
if (obj is X { NullableIntField: null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableIntField_NotNull(object obj)
{
if (obj is X { NullableIntField: not null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableIntField_Var(object obj) public void RecursivePattern_NullableIntField_Var(object obj)
{ {
@ -527,6 +550,31 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("not Test"); Console.WriteLine("not Test");
} }
} }
public void RecursivePattern_NullableIntProp_Null(object obj)
{
if (obj is X { NullableIntProp: null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableIntProp_NotNull(object obj)
{
if (obj is X { NullableIntProp: not null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableIntProp_Var(object obj) public void RecursivePattern_NullableIntProp_Var(object obj)
{ {
if (obj is X { NullableIntProp: var nullableIntProp }) if (obj is X { NullableIntProp: var nullableIntProp })
@ -551,6 +599,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void RecursivePattern_NullableCustomStructField_Null(object obj)
{
if (obj is X { NullableCustomStructField: null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableCustomStructField_NotNull(object obj)
{
if (obj is X { NullableCustomStructField: not null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableCustomStructField_Var(object obj) public void RecursivePattern_NullableCustomStructField_Var(object obj)
{ {
if (obj is X { NullableCustomStructField: var nullableCustomStructField, Obj: null }) if (obj is X { NullableCustomStructField: var nullableCustomStructField, Obj: null })
@ -575,6 +647,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void RecursivePattern_NullableCustomStructProp_Null(object obj)
{
if (obj is X { NullableCustomStructProp: null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableCustomStructProp_NotNull(object obj)
{
if (obj is X { NullableCustomStructProp: not null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableCustomStructProp_Var(object obj) public void RecursivePattern_NullableCustomStructProp_Var(object obj)
{ {
if (obj is X { NullableCustomStructProp: var nullableCustomStructProp, Obj: null }) if (obj is X { NullableCustomStructProp: var nullableCustomStructProp, Obj: null })

38
ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

@ -347,6 +347,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
private static ILInstruction? MatchNullableHasValueCheckPattern(Block block, MatchInstruction varPattern, private static ILInstruction? MatchNullableHasValueCheckPattern(Block block, MatchInstruction varPattern,
ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg) ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
{ {
if (!(varPattern.Variable.StoreCount == 1 && varPattern.Variable.LoadCount == 0))
{
return null;
}
if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst)) if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{ {
return null; return null;
@ -357,13 +361,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst)) if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst))
{ {
if (DetectExitPoints.CompatibleExitInstruction(trueInst, parentFalseInst))
{
if (!(varPattern.Variable.AddressCount == 1))
{
return null;
}
context.Step("Nullable.HasValue check -> null pattern", block);
varPattern.ReplaceWith(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull()));
block.Instructions.Clear();
block.Instructions.Add(falseInst);
return falseInst;
}
return null; return null;
} }
if (!(trueInst.MatchBranch(out var trueBlock) && trueBlock.Parent == block.Parent && trueBlock.IncomingEdgeCount == 1)) if (varPattern.Variable.AddressCount == 1)
{
context.Step("Nullable.HasValue check -> not null pattern", block);
varPattern.ReplaceWith(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull()));
block.Instructions.Clear();
block.Instructions.Add(trueInst);
return trueInst;
}
else if (varPattern.Variable.AddressCount != 2)
{ {
return null; return null;
} }
if (!(varPattern.Variable.StoreCount == 1 && varPattern.Variable.LoadCount == 0 && varPattern.Variable.AddressCount == 2)) if (!(trueInst.MatchBranch(out var trueBlock) && trueBlock.Parent == block.Parent && trueBlock.IncomingEdgeCount == 1))
{ {
return null; return null;
} }
@ -463,7 +488,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(loadTemp.MatchLdLoc(out var tempVar) && tempVar.IsSingleDefinition && tempVar.LoadCount == 1)) if (!(loadTemp.MatchLdLoc(out var tempVar) && tempVar.IsSingleDefinition && tempVar.LoadCount == 1))
return false; return false;
return block.Instructions[0].MatchStLoc(tempVar, out condition); if (!block.Instructions[0].MatchStLoc(tempVar, out condition))
return false;
while (condition.MatchLogicNot(out var arg))
{
condition = arg;
ExtensionMethods.Swap(ref trueInst, ref falseInst);
}
return true;
default: default:
condition = null; condition = null;
trueInst = null; trueInst = null;

Loading…
Cancel
Save