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
#if CS80 #if CS80
public void RecursivePattern_Type(object x) 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 else
{ {
@ -375,6 +375,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("not Test"); 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 #endif
private bool F() private bool F()
{ {

28
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

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

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

@ -432,12 +432,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
if (string.IsNullOrEmpty(proposedName)) if (string.IsNullOrEmpty(proposedName))
{ {
var proposedNameForStores = variable.StoreInstructions.OfType<StLoc>() var proposedNameForStores = new HashSet<string>();
.Select(expr => GetNameFromInstruction(expr.Value)) foreach (var store in variable.StoreInstructions)
.Except(currentLowerCaseTypeOrMemberNames).ToList(); {
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) if (proposedNameForStores.Count == 1)
{ {
proposedName = proposedNameForStores[0]; proposedName = proposedNameForStores.Single();
} }
} }
if (string.IsNullOrEmpty(proposedName)) if (string.IsNullOrEmpty(proposedName))

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

@ -182,13 +182,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == container) 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; 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 // if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037 // br IL_0037
@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (MatchInstruction.IsPatternMatch(condition, out var operand)) if (MatchInstruction.IsPatternMatch(condition, out var operand))
{ {
if (!PropertyOrFieldAccess(operand, out var target)) if (!PropertyOrFieldAccess(operand, out var target, out _))
{ {
return; return;
} }
@ -220,31 +220,61 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent) 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 { if (operand is CallInstruction {
Method: { Method: {
SymbolKind: SymbolKind.Accessor, SymbolKind: SymbolKind.Accessor,
AccessorKind: MethodSemanticsAttributes.Getter AccessorKind: MethodSemanticsAttributes.Getter,
AccessorOwner: { } _member
}, },
Arguments: [var _target] Arguments: [var _target]
}) })
{ {
target = _target; target = _target;
member = _member;
return true; return true;
} }
else if (operand.MatchLdFld(out target, out _)) else if (operand.MatchLdFld(out target, out var field))
{ {
member = field;
return true; return true;
} }
else else
{ {
member = null;
return false; return false;
} }
} }
@ -379,7 +409,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// the pattern matching machinery to an offset belonging to an instruction in the then-block. // the pattern matching machinery to an offset belonging to an instruction in the then-block.
unboxBlock.SetILRange(unboxBlock.Instructions[0]); unboxBlock.SetILRange(unboxBlock.Instructions[0]);
storeToV.Variable.Kind = VariableKind.PatternLocal; storeToV.Variable.Kind = VariableKind.PatternLocal;
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, unboxBlock, falseInst, context); DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, unboxBlock, falseInst, context, ref cfg);
return true; return true;
} }

Loading…
Cancel
Save