From 7c6f7fea0567af4a24a9c3c8df24a507c80892a0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 5 Jan 2025 20:22:44 +0100 Subject: [PATCH] Make sure that there is only one applicable implicit conversion when dealing with switch, otherwise use an explicit cast. --- .../CSharp/ExpressionBuilder.cs | 83 +++++++++++++++---- .../CSharp/StatementBuilder.cs | 2 +- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 9f53f9c89..2b32f842b 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3900,38 +3900,93 @@ namespace ICSharpCode.Decompiler.CSharp } } - internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool allowImplicitConversion) + internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool isExpressionContext) { TranslatedExpression value; IType type; + // prepare expression and expected type if (inst.Value is StringToInt strToInt) { - value = Translate(strToInt.Argument) - .ConvertTo( - strToInt.ExpectedType, - this, - allowImplicitConversion: allowImplicitConversion - ); - type = compilation.FindType(KnownTypeCode.String); + value = Translate(strToInt.Argument); + type = strToInt.ExpectedType ?? compilation.FindType(KnownTypeCode.String); } else { strToInt = null; value = Translate(inst.Value); - type = value.Type; - if (inst.Type != null) + type = inst.Type ?? value.Type; + } + + // find and unwrap the input type + IType inputType = value.Type; + if (value.Expression is CastExpression && value.ResolveResult is ConversionResolveResult crr) + { + inputType = crr.Input.Type; + } + inputType = NullableType.GetUnderlyingType(inputType).GetEnumUnderlyingType(); + + // check input/underlying type for compatibility + bool allowImplicitConversion; + if (IsCompatibleWithSwitch(inputType) || (strToInt != null && inputType.Equals(type))) + { + allowImplicitConversion = !isExpressionContext; + } + else + { + var applicableImplicitConversionOperators = inputType.GetMethods(IsCompatibleImplicitConversionOperator).ToArray(); + switch (applicableImplicitConversionOperators.Length) { - value = value.ConvertTo(inst.Type, this, allowImplicitConversion: allowImplicitConversion); - type = inst.Type; + case 0: + allowImplicitConversion = !isExpressionContext; + break; + case 1: + allowImplicitConversion = !isExpressionContext; + // TODO validate + break; + default: + allowImplicitConversion = false; + break; } } - return (value, type, strToInt); + + value = value.ConvertTo(type, this, allowImplicitConversion: allowImplicitConversion); + + var caseType = strToInt != null + ? compilation.FindType(KnownTypeCode.String) + : type; + + return (value, caseType, strToInt); + + static bool IsCompatibleWithSwitch(IType type) + { + return type.IsKnownType(KnownTypeCode.SByte) + || type.IsKnownType(KnownTypeCode.Byte) + || type.IsKnownType(KnownTypeCode.Int16) + || type.IsKnownType(KnownTypeCode.UInt16) + || type.IsKnownType(KnownTypeCode.Int32) + || type.IsKnownType(KnownTypeCode.UInt32) + || type.IsKnownType(KnownTypeCode.Int64) + || type.IsKnownType(KnownTypeCode.UInt64) + || type.IsKnownType(KnownTypeCode.Char) + || type.IsKnownType(KnownTypeCode.String); + } + + bool IsCompatibleImplicitConversionOperator(IMethod operatorMethod) + { + if (!operatorMethod.IsOperator) + return false; + if (operatorMethod.Name != "op_Implicit") + return false; + if (operatorMethod.Parameters.Count != 1) + return false; + return IsCompatibleWithSwitch(operatorMethod.ReturnType); + } } protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context) { // switch-expression does not support implicit conversions - var (value, type, strToInt) = TranslateSwitchValue(inst, allowImplicitConversion: false); + var (value, type, strToInt) = TranslateSwitchValue(inst, true); IL.SwitchSection defaultSection = inst.GetDefaultSection(); SwitchExpression switchExpr = new SwitchExpression(); diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index a806e8bb7..094a51355 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.CSharp var oldCaseLabelMapping = caseLabelMapping; caseLabelMapping = new Dictionary(); - var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, allowImplicitConversion: true); + var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, false); IL.SwitchSection defaultSection = inst.GetDefaultSection();