Skip to content

Commit

Permalink
Merge pull request #2461 from icsharpcode/pattern-matching
Browse files Browse the repository at this point in the history
Pattern matching
  • Loading branch information
siegfriedpammer authored Oct 4, 2021
2 parents 105cdfd + 6290e2f commit 1f92b44
Show file tree
Hide file tree
Showing 28 changed files with 1,125 additions and 155 deletions.
2 changes: 2 additions & 0 deletions ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,15 @@ public static List<string> GetPreprocessorSymbols(CompilerOptions flags)
if (flags.HasFlag(CompilerOptions.UseRoslyn2_10_0)
|| flags.HasFlag(CompilerOptions.UseRoslynLatest))
{
preprocessorSymbols.Add("ROSLYN2");
preprocessorSymbols.Add("CS70");
preprocessorSymbols.Add("CS71");
preprocessorSymbols.Add("CS72");
preprocessorSymbols.Add("VB15");
}
if (flags.HasFlag(CompilerOptions.UseRoslynLatest))
{
preprocessorSymbols.Add("ROSLYN3");
preprocessorSymbols.Add("CS73");
preprocessorSymbols.Add("CS80");
preprocessorSymbols.Add("VB16");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
<Compile Include="TestCases\Correctness\DeconstructionTests.cs" />
<Compile Include="TestCases\Correctness\DynamicTests.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<Compile Include="TestCases\Pretty\PatternMatching.cs" />
<None Include="TestCases\Pretty\CovariantReturns.cs" />
<Compile Include="TestCases\VBPretty\VBPropertiesTest.cs" />
<None Include="TestCases\ILPretty\Issue2260SwitchString.cs" />
Expand Down
8 changes: 7 additions & 1 deletion ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ public void OutVariables([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOp
RunForLibrary(cscOptions: cscOptions);
}

[Test]
public void PatternMatching([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}

[Test]
public void InitializerTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
Expand Down Expand Up @@ -480,7 +486,7 @@ public void ConstantsTests([ValueSource(nameof(defaultOptions))] CompilerOptions
[Test]
public void Issue1080([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp6));
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ public static void NumberBasic(decimal? a, decimal? b)
{
Console.WriteLine();
}
#if ROSLYN
#if ROSLYN2
// Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b'
if (!(a == b))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class PatternMatching
public class OutVariables
{
public static void OutVarInShortCircuit(Dictionary<int, string> d)
{
Expand Down
248 changes: 248 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
using System;

namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class PatternMatching
{
public void SimpleTypePattern(object x)
{
if (x is string value)
{
Console.WriteLine(value);
}
}

public void TypePatternWithShortcircuit(object x)
{
Use(F() && x is string text && text.Contains("a"));
if (F() && x is string text2 && text2.Contains("a"))
{
Console.WriteLine(text2);
}
}

public void TypePatternWithShortcircuitAnd(object x)
{
if (x is string text && text.Contains("a"))
{
Console.WriteLine(text);
}
else
{
Console.WriteLine();
}
}

public void TypePatternWithShortcircuitOr(object x)
{
if (!(x is string text) || text.Contains("a"))
{
Console.WriteLine();
}
else
{
Console.WriteLine(text);
}
}

public void TypePatternWithShortcircuitOr2(object x)
{
if (F() || !(x is string value))
{
Console.WriteLine();
}
else
{
Console.WriteLine(value);
}
}

public void TypePatternValueTypesCondition(object x)
{
if (x is int num)
{
Console.WriteLine("Integer: " + num);
}
else
{
Console.WriteLine("else");
}
}

public void TypePatternValueTypesCondition2()
{
if (GetObject() is int num)
{
Console.WriteLine("Integer: " + num);
}
else
{
Console.WriteLine("else");
}
}

public void TypePatternValueTypesWithShortcircuitAnd(object x)
{
if (x is int num && num.GetHashCode() > 0)
{
Console.WriteLine("Positive integer: " + num);
}
else
{
Console.WriteLine("else");
}
}

public void TypePatternValueTypesWithShortcircuitOr(object x)
{
if (!(x is int value) || value.GetHashCode() > 0)
{
Console.WriteLine();
}
else
{
Console.WriteLine(value);
}
}

#if ROSLYN3 || OPT
// Roslyn 2.x generates a complex infeasible path in debug builds, which RemoveInfeasiblePathTransform
// currently cannot handle. Because this would increase the complexity of that transform, we ignore
// this case.
public void TypePatternValueTypesWithShortcircuitOr2(object x)
{
if (F() || !(x is int value))
{
Console.WriteLine();
}
else
{
Console.WriteLine(value);
}
}
#endif

public void TypePatternGenerics<T>(object x)
{
if (x is T val)
{
Console.WriteLine(val.GetType().FullName);
}
else
{
Console.WriteLine("not a " + typeof(T).FullName);
}
}

public void TypePatternGenericRefType<T>(object x) where T : class
{
if (x is T val)
{
Console.WriteLine(val.GetType().FullName);
}
else
{
Console.WriteLine("not a " + typeof(T).FullName);
}
}

public void TypePatternGenericValType<T>(object x) where T : struct
{
if (x is T val)
{
Console.WriteLine(val.GetType().FullName);
}
else
{
Console.WriteLine("not a " + typeof(T).FullName);
}
}

public void TypePatternValueTypesWithShortcircuitAndMultiUse(object x)
{
if (x is int num && num.GetHashCode() > 0 && num % 2 == 0)
{
Console.WriteLine("Positive integer: " + num);
}
else
{
Console.WriteLine("else");
}
}

public void TypePatternValueTypesWithShortcircuitAndMultiUse2(object x)
{
if ((x is int num && num.GetHashCode() > 0 && num % 2 == 0) || F())
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("else");
}
}

public void TypePatternValueTypesWithShortcircuitAndMultiUse3(object x)
{
if (F() || (x is int num && num.GetHashCode() > 0 && num % 2 == 0))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("else");
}
}

public void TypePatternValueTypes()
{
Use(F() && GetObject() is int num && num.GetHashCode() > 0 && num % 2 == 0);
}

public static void NotTypePatternVariableUsedOutsideTrueBranch(object x)
{
string text = x as string;
if (text != null && text.Length > 5)
{
Console.WriteLine("pattern matches");
}
if (text != null && text.Length > 10)
{
Console.WriteLine("other use!");
}
}

public static void NotTypePatternBecauseVarIsNotDefAssignedInCaseOfFallthrough(object x)
{
#if OPT
string obj = x as string;
if (obj == null)
{
Console.WriteLine("pattern doesn't match");
}
Console.WriteLine(obj == null);
#else
string text = x as string;
if (text == null)
{
Console.WriteLine("pattern doesn't match");
}
Console.WriteLine(text == null);
#endif
}

private bool F()
{
return true;
}

private object GetObject()
{
throw new NotImplementedException();
}

private void Use(bool x)
{
}
}
}
6 changes: 0 additions & 6 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,15 +320,9 @@ public void EmptyIf()
if (!F(1))
{
}
if (F(2) && F(3))
{
}
if (F(4) || F(5))
{
}
if (F(0) && F(1) && !F(2) && (F(3) || F(4)))
{
}
E();
}
#endif
Expand Down
4 changes: 3 additions & 1 deletion ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,18 @@ public static List<IILTransform> GetILTransforms()
new SplitVariables(),
new ILInlining(),
new InlineReturnTransform(), // must run before DetectPinnedRegions
new RemoveInfeasiblePathTransform(),
new DetectPinnedRegions(), // must run after inlining but before non-critical control flow transforms
new YieldReturnDecompiler(), // must run after inlining but before loop detection
new AsyncAwaitDecompiler(), // must run after inlining but before loop detection
new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection
new DetectExitPoints(),
new LdLocaDupInitObjTransform(),
new EarlyExpressionTransforms(),
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
// RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...)
// is already collapsed into stloc(V, ...).
new RemoveDeadVariableInit(),
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
new ControlFlowSimplification(), //split variables may enable new branch to leave inlining
new DynamicCallSiteTransform(),
new SwitchDetection(),
Expand All @@ -119,6 +120,7 @@ public static List<IILTransform> GetILTransforms()
},
// re-run DetectExitPoints after loop detection
new DetectExitPoints(),
new PatternMatchingTransform(), // must run after LoopDetection and before ConditionDetection
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
new ConditionDetection(),
Expand Down
Loading

0 comments on commit 1f92b44

Please sign in to comment.