diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs index b9fa0b055..110db46f9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs @@ -10,9 +10,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private int total; + // Roslyn 4.14 puts no ParamArrayAttribute on the lambda's own method, so for a named + // delegate type the 'params' cannot be recovered there; newer compilers record it. public ParamsAction ParamsStatementBody() { +#if ROSLYN5 || !EXPECTED_OUTPUT return (params int[] xs) => { +#else + return (int[] xs) => { +#endif total += xs.Length; Console.WriteLine(xs.Length); }; @@ -20,7 +26,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public ParamsAction ParamsExpressionBody() { +#if ROSLYN5 || !EXPECTED_OUTPUT return (params int[] xs) => Console.WriteLine(xs.Length); +#else + return (int[] xs) => Console.WriteLine(xs.Length); +#endif } public OptionalFunc OptionalStatementBody() diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLambdaOptionalAndParamsParameters.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLambdaOptionalAndParamsParameters.Expected.cs index 988cf2535..c2394285a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLambdaOptionalAndParamsParameters.Expected.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLambdaOptionalAndParamsParameters.Expected.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { @@ -13,7 +12,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly public ParamsAction ParamsLambda() { - return ([ParamArray] int[] xs) => { + return (int[] xs) => { total += xs.Length; Console.WriteLine(xs.Length); }; @@ -21,7 +20,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly public OptionalFunc OptionalLambda() { - return ([Optional][DefaultParameterValue(5)] int x) => x * 2; + return (int x) => x * 2; } } } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 6cae69619..18f1208cf 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2543,13 +2543,16 @@ namespace ICSharpCode.Decompiler.CSharp AnonymousMethodExpression ame = new AnonymousMethodExpression(); ame.IsAsync = function.IsAsync; ame.Parameters.AddRange(MakeParameters(function.Parameters, function)); - // 'params' and parameter default values are part of the delegate's signature, and it - // is the delegate's Invoke method that call sites bind against. An anonymous function's - // own method does not reliably carry them - Roslyn 4.14 emits no ParamArrayAttribute - // there - so take them from Invoke, keeping declaration and call sites consistent. - var invokeParameters = delegateType.GetDelegateInvokeMethod()?.Parameters; - if (invokeParameters?.Count == ame.Parameters.Count) - { + if (delegateType.IsAnonymousDelegate() + && delegateType.GetDelegateInvokeMethod()?.Parameters is { } invokeParameters + && invokeParameters.Count == ame.Parameters.Count) + { + // A compiler-synthesized delegate type exists only because the lambda declared + // 'params' or a parameter default value, and the lambda's own declaration is the + // only way to spell that type. Roslyn 4.14 does not put a ParamArrayAttribute on + // the lambda's method, so take both from the delegate's Invoke method, which + // always carries them. Named delegate types are left alone: there the lambda's + // own metadata decides, and a plain parameter list is valid either way. int parameterIndex = 0; foreach (var pd in ame.Parameters) { @@ -2611,29 +2614,14 @@ namespace ICSharpCode.Decompiler.CSharp if (!settings.LambdaOptionalAndParamsParameters || ame.Parameters.Any(p => p.Type is null)) { // 'params' and parameter default values are only legal on the explicitly typed - // parameter list of a lambda, and only since C# 12. Elsewhere, downgrade them to - // their underlying metadata attributes so the information is not lost. + // parameter list of a lambda, and only since C# 12. There is no other spelling: + // [ParamArray] cannot be written explicitly in any language version, and attributes + // on lambda parameters need C# 10. Drop them; the delegate's Invoke method still + // carries them at every call site. foreach (var p in ame.Parameters) { - if (p.IsParams) - { - p.IsParams = false; - p.Attributes.Add(new AttributeSection(new Syntax.Attribute { - Type = astBuilder.ConvertAttributeType(compilation.FindType(KnownAttribute.ParamArray)) - })); - } - if (p.DefaultExpression is { } defaultValue) - { - defaultValue.Detach(); - p.Attributes.Add(new AttributeSection(new Syntax.Attribute { - Type = astBuilder.ConvertAttributeType(compilation.FindType(KnownAttribute.Optional)) - })); - var defaultParameterValue = new Syntax.Attribute { - Type = astBuilder.ConvertAttributeType(compilation.FindType(KnownAttribute.DefaultParameterValue)) - }; - defaultParameterValue.Arguments.Add(defaultValue); - p.Attributes.Add(new AttributeSection(defaultParameterValue)); - } + p.IsParams = false; + p.DefaultExpression = null; } } bool isLambda = false; diff --git a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs index edf305c5a..753f317db 100644 --- a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs +++ b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs @@ -144,13 +144,6 @@ namespace ICSharpCode.Decompiler.CSharp { HandleAttributes(param.GetAttributes()); CollectNamespacesForTypeReference(param.Type); - if (param.IsOptional) - { - // ExpressionBuilder.TranslateFunction may downgrade a default value - // on an anonymous-function parameter to [Optional] and - // [DefaultParameterValue(...)]; keep their namespace in the superset. - namespaces.Add(KnownAttribute.DefaultParameterValue.GetTypeName().Namespace); - } } HandleTypeParameters(partMethod.TypeParameters); HandleOverrides(part.GetMethodImplementations(module.metadata), module);