diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
index b133ac0787..34f940d861 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
@@ -4946,5 +4946,21 @@ public void Issue1779(int value)
CustomStruct2 customStruct = GetStruct();
customStruct.IntProp += value;
}
+
+ public static string PreincrementWithMethodCall(int value)
+ {
+ return (++value).ToString();
+ }
+
+#if CS72
+ public static string PreincrementWithInParameter(int value)
+ {
+ PreincrementWithInParameter_Helper(++value);
+ return value.ToString();
+ }
+ public static void PreincrementWithInParameter_Helper(in int value)
+ {
+ }
+#endif
}
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
index b5530fb70d..1b6b1b44f0 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
@@ -54,7 +54,8 @@ void IStatementTransform.Run(Block block, int pos, StatementTransformContext con
if (context.Settings.IntroduceIncrementAndDecrement)
{
if (TransformPostIncDecOperatorWithInlineStore(block, pos)
- || TransformPostIncDecOperator(block, pos))
+ || TransformPostIncDecOperator(block, pos)
+ || TransformPreIncDecOperatorWithInlineStore(block, pos))
{
// again, new top-level stloc might need inlining:
context.RequestRerun();
@@ -844,6 +845,83 @@ bool IsDuplicatedAddressComputation(ILInstruction storeTarget, ILInstruction loa
}
}
+ ///
+ /// stloc l(stloc target(binary.add(ldloc target, ldc.i4 1)))
+ ///
+ bool TransformPreIncDecOperatorWithInlineStore(Block block, int pos)
+ {
+ var store = block.Instructions[pos];
+ if (!IsCompoundStore(store, out var targetType1, out var value1, context.TypeSystem))
+ {
+ return false;
+ }
+ if (!IsCompoundStore(value1, out var targetType2, out var value2, context.TypeSystem))
+ {
+ return false;
+ }
+ if (targetType1 != targetType2)
+ return false;
+ var targetType = targetType1;
+ var stloc_outer = store as StLoc;
+ var stloc_inner = value1 as StLoc;
+ LdLoc ldloc;
+ var binary = UnwrapSmallIntegerConv(value2, out var conv) as BinaryNumericInstruction;
+ if (binary != null && (binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1)))
+ {
+ if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
+ return false;
+
+ if (conv is not null)
+ {
+ var primitiveType = targetType.ToPrimitiveType();
+ if (primitiveType.GetSize() == conv.TargetType.GetSize() && primitiveType.GetSign() != conv.TargetType.GetSign())
+ targetType = SwapSign(targetType, context.TypeSystem);
+ }
+
+ if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings))
+ return false;
+ ldloc = binary.Left as LdLoc;
+ }
+ else if (value2 is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1)
+ {
+ if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement"))
+ return false;
+ if (operatorCall.IsLifted)
+ return false; // TODO: add tests and think about whether nullables need special considerations
+ ldloc = operatorCall.Arguments[0] as LdLoc;
+ }
+ else
+ {
+ return false;
+ }
+ if (stloc_outer == null)
+ return false;
+ if (stloc_inner == null)
+ return false;
+ if (ldloc == null)
+ return false;
+ if (!(stloc_outer.Variable.Kind == VariableKind.Local || stloc_outer.Variable.Kind == VariableKind.StackSlot))
+ return false;
+ if (!IsMatchingCompoundLoad(ldloc, stloc_inner, out var target, out var targetKind, out var finalizeMatch))
+ return false;
+ if (IsImplicitTruncation(stloc_outer.Value, stloc_outer.Variable.Type, context.TypeSystem))
+ return false;
+ context.Step(nameof(TransformPreIncDecOperatorWithInlineStore), store);
+ finalizeMatch?.Invoke(context);
+ if (binary != null)
+ {
+ block.Instructions[pos] = new StLoc(stloc_outer.Variable, new NumericCompoundAssign(
+ binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue));
+ }
+ else
+ {
+ Call operatorCall = (Call)value2;
+ block.Instructions[pos] = new StLoc(stloc_outer.Variable, new UserDefinedCompoundAssign(
+ operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, new LdcI4(1)));
+ }
+ return true;
+ }
+
///
/// stobj(target, binary.add(stloc l(ldobj(target)), ldc.i4 1))
/// where target is pure and does not use 'l', and the 'stloc l' does not truncate