diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 7092f6085..c2dca9b37 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -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] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs index cf97f492f..cd6bfd019 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs @@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class OptionalArguments : List { + public enum MyEnum + { + A, + B + } + public OptionalArguments(string name, int a = 5) { @@ -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 } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 1f944a664..17e2c803a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -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; } }