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
} }
} }
internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool allowImplicitConversion) internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool isExpressionContext)
{ {
TranslatedExpression value; TranslatedExpression value;
IType type; IType type;
// prepare expression and expected type
if (inst.Value is StringToInt strToInt) if (inst.Value is StringToInt strToInt)
{ {
value = Translate(strToInt.Argument) value = Translate(strToInt.Argument);
.ConvertTo( type = strToInt.ExpectedType ?? compilation.FindType(KnownTypeCode.String);
strToInt.ExpectedType,
this,
allowImplicitConversion: allowImplicitConversion
);
type = compilation.FindType(KnownTypeCode.String);
} }
else else
{ {
strToInt = null; strToInt = null;
value = Translate(inst.Value); value = Translate(inst.Value);
type = value.Type; type = inst.Type ?? value.Type;
if (inst.Type != null) }
// 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); case 0:
type = inst.Type; 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) protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context)
{ {
// switch-expression does not support implicit conversions // 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(); IL.SwitchSection defaultSection = inst.GetDefaultSection();
SwitchExpression switchExpr = new SwitchExpression(); SwitchExpression switchExpr = new SwitchExpression();

2
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.CSharp
var oldCaseLabelMapping = caseLabelMapping; var oldCaseLabelMapping = caseLabelMapping;
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>(); 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(); IL.SwitchSection defaultSection = inst.GetDefaultSection();

Loading…
Cancel
Save