Browse Source

Fix #2124: Unwrap nullables when printing constant values.

pull/2126/head
Siegfried Pammer 5 years ago
parent
commit
3bac7e1d4e
  1. 2
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 58
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs
  3. 55
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

2
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -425,7 +425,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -425,7 +425,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void OptionalArguments([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]

58
ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs

@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class OptionalArguments : List<int>
{
public enum MyEnum
{
A,
B
}
public OptionalArguments(string name, int a = 5)
{
@ -170,5 +176,57 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -170,5 +176,57 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
throw null;
}
public static void Definition_Enum(MyEnum p = MyEnum.A)
{
}
public static void Definition_Enum_OutOfRangeDefault(MyEnum p = (MyEnum)(-1))
{
}
public static void Definition_NullableEnum(MyEnum? p = MyEnum.A)
{
}
public static void Definition_NullableEnum_OutOfRangeDefault(MyEnum? p = (MyEnum)(-1))
{
}
public static void Definition_Int(int p = 0)
{
}
public static void Definition_NullableInt(int? p = 0)
{
}
public static void Definition_Int100(int p = 100)
{
}
public static void Definition_NullableInt100(int? p = 100)
{
}
#if CS90
public static void Definition_NInt(nint p = 100)
{
}
public static void Definition_NullableNInt(nint? p = 100)
{
}
#endif
}
}

55
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -792,35 +792,38 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -792,35 +792,38 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
expr.Initializer = new ArrayInitializerExpression(arr.Select(e => ConvertConstantValue(elementType, e.Type, e.Value)));
return expr;
} else if (type.Kind == TypeKind.Enum) {
return ConvertEnumValue(type, (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false));
} else {
if (IsSpecialConstant(type, constantValue, out var expr))
IType underlyingType = NullableType.GetUnderlyingType(type);
if (underlyingType.Kind == TypeKind.Enum) {
return ConvertEnumValue(underlyingType, (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false));
} else {
if (IsSpecialConstant(underlyingType, constantValue, out var expr))
return expr;
if (underlyingType.IsKnownType(KnownTypeCode.Double) || underlyingType.IsKnownType(KnownTypeCode.Single))
return ConvertFloatingPointLiteral(underlyingType, constantValue);
IType literalType = underlyingType;
bool integerTypeMismatch = underlyingType.IsCSharpSmallIntegerType() || underlyingType.IsCSharpNativeIntegerType();
if (integerTypeMismatch) {
// C# does not have integer literals of small integer types,
// use `int` literal instead.
// It also doesn't have native integer literals, those also use `int` (or `uint` for `nuint`).
bool unsigned = underlyingType.Kind == TypeKind.NUInt;
constantValue = CSharpPrimitiveCast.Cast(unsigned ? TypeCode.UInt32 : TypeCode.Int32, constantValue, false);
var compilation = resolver?.Compilation ?? expectedType.GetDefinition()?.Compilation;
literalType = compilation?.FindType(unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32);
}
LiteralFormat format = LiteralFormat.None;
if (PrintIntegralValuesAsHex) {
format = LiteralFormat.HexadecimalNumber;
}
expr = new PrimitiveExpression(constantValue, format);
if (AddResolveResultAnnotations && literalType != null)
expr.AddAnnotation(new ConstantResolveResult(literalType, constantValue));
if (integerTypeMismatch && !type.Equals(expectedType)) {
expr = new CastExpression(ConvertType(type), expr);
}
return expr;
if (type.IsKnownType(KnownTypeCode.Double) || type.IsKnownType(KnownTypeCode.Single))
return ConvertFloatingPointLiteral(type, constantValue);
IType literalType = type;
bool integerTypeMismatch = type.IsCSharpSmallIntegerType() || type.IsCSharpNativeIntegerType();
if (integerTypeMismatch) {
// C# does not have integer literals of small integer types,
// use `int` literal instead.
// It also doesn't have native integer literals, those also use `int` (or `uint` for `nuint`).
bool unsigned = type.Kind == TypeKind.NUInt;
constantValue = CSharpPrimitiveCast.Cast(unsigned ? TypeCode.UInt32 : TypeCode.Int32, constantValue, false);
var compilation = resolver?.Compilation ?? expectedType.GetDefinition()?.Compilation;
literalType = compilation?.FindType(unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32);
}
LiteralFormat format = LiteralFormat.None;
if (PrintIntegralValuesAsHex) {
format = LiteralFormat.HexadecimalNumber;
}
expr = new PrimitiveExpression(constantValue, format);
if (AddResolveResultAnnotations && literalType != null)
expr.AddAnnotation(new ConstantResolveResult(literalType, constantValue));
if (integerTypeMismatch && !type.Equals(expectedType)) {
expr = new CastExpression(ConvertType(type), expr);
}
return expr;
}
}

Loading…
Cancel
Save