diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 3ceeeaf60..39cdba538 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -373,6 +373,20 @@ namespace ICSharpCode.Decompiler.CSharp return HandleImplicitConversion(method, argumentList.Arguments[0]); } + if (settings.LiftNullables && method.Name == "GetValueOrDefault" + && method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT) + && method.DeclaringType.TypeArguments[0].IsKnownType(KnownTypeCode.Boolean) + && argumentList.Length == 0) + { + argumentList.CheckNoNamedOrOptionalArguments(); + return new BinaryOperatorExpression( + target.Expression, + BinaryOperatorType.Equality, + new PrimitiveExpression(true)) + .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, + argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); + } + var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref target, ref argumentList, CallTransformation.All, out IParameterizedMember foundMethod); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index f9a605012..3605d968d 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -1167,6 +1167,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } return base.VisitBinaryOperatorExpression(expr); } + + public override AstNode VisitUnaryOperatorExpression(UnaryOperatorExpression expr) + { + if (expr.Operator == UnaryOperatorType.Not && expr.Expression is BinaryOperatorExpression { Operator: BinaryOperatorType.Equality } binary) + { + binary.Operator = BinaryOperatorType.InEquality; + expr.ReplaceWith(binary.Detach()); + return VisitBinaryOperatorExpression(binary); + } + return base.VisitUnaryOperatorExpression(expr); + } #endregion #region C# 7.3 pattern based fixed (for value types) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 5385bbaae..b9b99ad42 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -96,8 +96,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; } else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None - && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp) - ) + && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp)) { // if (comp(x != 0)) ==> if (x) // comp(comp(...) != 0) => comp(...) @@ -107,6 +106,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.Left.AcceptVisitor(this); return; } + new NullableLiftingTransform(context).Run(inst); base.VisitComp(inst); if (inst.IsLifted) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 4e297b00d..3d517e463 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -85,6 +85,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + /// + /// VS2022.10 / Roslyn 4.10.0 adds an optimization that turns + /// a == 42 into a.GetValueOrDefault() == 42 without any HasValue check. + /// + public void Run(Comp comp) + { + if (!comp.IsLifted && comp.Kind.IsEqualityOrInequality()) + { + var left = comp.Left; + var right = comp.Right; + if (MatchGetValueOrDefault(left, out ILInstruction arg) + && right.MatchLdcI(out var value) && value != 0) + { + context.Step("comp(a.GetValueOrDefault() == const) -> comp.lifted(a == const)", comp); + comp.LiftingKind = ComparisonLiftingKind.CSharp; + comp.Left = new LdObj(arg, ((Call)left).Method.DeclaringType); + } + else if (MatchGetValueOrDefault(right, out arg) + && left.MatchLdcI(out value) && value != 0) + { + context.Step("comp(const == a.GetValueOrDefault()) -> comp.lifted(const == a)", comp); + comp.LiftingKind = ComparisonLiftingKind.CSharp; + comp.Right = new LdObj(arg, ((Call)right).Method.DeclaringType); + } + } + } + public bool RunStatements(Block block, int pos) { // e.g.: