Browse Source

Make sure that there is only one applicable implicit conversion when dealing with switch, otherwise use an explicit cast.

pull/3362/head
Siegfried Pammer 4 months ago
parent
commit
7c6f7fea05
  1. 83
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

83
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -3900,38 +3900,93 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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();

2
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.CSharp
var oldCaseLabelMapping = caseLabelMapping;
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>();
var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, allowImplicitConversion: true);
var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, false);
IL.SwitchSection defaultSection = inst.GetDefaultSection();

Loading…
Cancel
Save