Browse Source

Add support for var sub patterns.

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
51a8eb28f1
  1. 16
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 28
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 21
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  4. 46
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -318,9 +318,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -318,9 +318,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#if CS80
public void RecursivePattern_Type(object x)
{
if (x is X { C: string text })
if (x is X { C: string c })
{
Console.WriteLine("Test " + text);
Console.WriteLine("Test " + c);
}
else
{
@ -375,6 +375,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -375,6 +375,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("not Test");
}
}
public void RecursivePattern_MultipleConstantsMixedWithVar(object x)
{
if (x is X { A: 42, C: var c, B: "Hello" })
{
Console.WriteLine("Test " + c);
}
else
{
Console.WriteLine("not Test");
}
}
#endif
private bool F()
{

28
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -4558,16 +4558,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -4558,16 +4558,21 @@ namespace ICSharpCode.Decompiler.CSharp
switch (pattern)
{
case MatchInstruction matchInstruction:
if (!matchInstruction.CheckType)
throw new NotImplementedException();
if (matchInstruction.IsDeconstructCall)
throw new NotImplementedException();
if (matchInstruction.IsDeconstructTuple)
throw new NotImplementedException();
if (matchInstruction.SubPatterns.Count > 0)
if (matchInstruction.SubPatterns.Count > 0 || (matchInstruction.CheckNotNull && !matchInstruction.CheckType))
{
RecursivePatternExpression recursivePatternExpression = new();
recursivePatternExpression.Type = ConvertType(matchInstruction.Variable.Type);
if (matchInstruction.CheckType)
{
recursivePatternExpression.Type = ConvertType(matchInstruction.Variable.Type);
}
else
{
Debug.Assert(matchInstruction.CheckNotNull);
}
foreach (var subPattern in matchInstruction.SubPatterns)
{
if (!MatchInstruction.IsPatternMatch(subPattern, out var testedOperand))
@ -4602,17 +4607,28 @@ namespace ICSharpCode.Decompiler.CSharp @@ -4602,17 +4607,28 @@ namespace ICSharpCode.Decompiler.CSharp
}
return recursivePatternExpression.WithILInstruction(matchInstruction);
}
else if (matchInstruction.HasDesignator)
else if (matchInstruction.HasDesignator || !matchInstruction.CheckType)
{
SingleVariableDesignation designator = new SingleVariableDesignation { Identifier = matchInstruction.Variable.Name };
designator.AddAnnotation(new ILVariableResolveResult(matchInstruction.Variable));
AstType type;
if (matchInstruction.CheckType)
{
type = ConvertType(matchInstruction.Variable.Type);
}
else
{
Debug.Assert(matchInstruction.IsVar);
type = new SimpleType("var");
}
return new DeclarationExpression {
Type = ConvertType(matchInstruction.Variable.Type),
Type = type,
Designation = designator
}.WithILInstruction(matchInstruction);
}
else
{
Debug.Assert(matchInstruction.CheckType);
return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type))
.WithILInstruction(matchInstruction);
}

21
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -432,12 +432,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -432,12 +432,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (string.IsNullOrEmpty(proposedName))
{
var proposedNameForStores = variable.StoreInstructions.OfType<StLoc>()
.Select(expr => GetNameFromInstruction(expr.Value))
.Except(currentLowerCaseTypeOrMemberNames).ToList();
var proposedNameForStores = new HashSet<string>();
foreach (var store in variable.StoreInstructions)
{
if (store is StLoc stloc)
{
var name = GetNameFromInstruction(stloc.Value);
if (!currentLowerCaseTypeOrMemberNames.Contains(name))
proposedNameForStores.Add(name);
}
else if (store is MatchInstruction match && match.SlotInfo == MatchInstruction.SubPatternsSlot)
{
var name = GetNameFromInstruction(match.TestedOperand);
if (!currentLowerCaseTypeOrMemberNames.Contains(name))
proposedNameForStores.Add(name);
}
}
if (proposedNameForStores.Count == 1)
{
proposedName = proposedNameForStores[0];
proposedName = proposedNameForStores.Single();
}
}
if (string.IsNullOrEmpty(proposedName))

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

@ -182,13 +182,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -182,13 +182,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == container)
{
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueBlock, falseInst, context);
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueBlock, falseInst, context, ref cfg);
}
return true;
}
private void DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context)
private void DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
{
// if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037
@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (MatchInstruction.IsPatternMatch(condition, out var operand))
{
if (!PropertyOrFieldAccess(operand, out var target))
if (!PropertyOrFieldAccess(operand, out var target, out _))
{
return;
}
@ -220,31 +220,61 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -220,31 +220,61 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent)
{
DetectPropertySubPatterns(parentPattern, trueBlock, falseInst, context);
DetectPropertySubPatterns(parentPattern, trueBlock, falseInst, context, ref cfg);
}
}
}
else if (block.Instructions[0].MatchStLoc(out var targetVariable, out var operand))
{
if (!PropertyOrFieldAccess(operand, out var target, out var member))
{
return;
}
if (!target.MatchLdLocRef(parentPattern.Variable))
{
return;
}
if (!targetVariable.Type.Equals(member.ReturnType))
{
return;
}
if (!CheckAllUsesDominatedBy(targetVariable, (BlockContainer)block.Parent!, block, block.Instructions[0], null, context, ref cfg))
{
return;
}
context.Step("Property var pattern", block);
var varPattern = new MatchInstruction(targetVariable, operand)
.WithILRange(block.Instructions[0]);
parentPattern.SubPatterns.Add(varPattern);
block.Instructions.RemoveAt(0);
targetVariable.Kind = VariableKind.PatternLocal;
DetectPropertySubPatterns(parentPattern, block, parentFalseInst, context, ref cfg);
}
}
private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target)
private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member)
{
if (operand is CallInstruction {
Method: {
SymbolKind: SymbolKind.Accessor,
AccessorKind: MethodSemanticsAttributes.Getter
AccessorKind: MethodSemanticsAttributes.Getter,
AccessorOwner: { } _member
},
Arguments: [var _target]
})
{
target = _target;
member = _member;
return true;
}
else if (operand.MatchLdFld(out target, out _))
else if (operand.MatchLdFld(out target, out var field))
{
member = field;
return true;
}
else
{
member = null;
return false;
}
}
@ -379,7 +409,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -379,7 +409,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// the pattern matching machinery to an offset belonging to an instruction in the then-block.
unboxBlock.SetILRange(unboxBlock.Instructions[0]);
storeToV.Variable.Kind = VariableKind.PatternLocal;
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, unboxBlock, falseInst, context);
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, unboxBlock, falseInst, context, ref cfg);
return true;
}

Loading…
Cancel
Save