diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LambdaNaturalType.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LambdaNaturalType.cs index 722604e21..33c5ffac0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LambdaNaturalType.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LambdaNaturalType.cs @@ -74,12 +74,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness var optionalParameter = (int x = 5) => x * 2; Console.WriteLine("optional parameter: " + optionalParameter() + " " + optionalParameter(7)); -#if CS140 - // Roslyn 4.14 emits no ParamArrayAttribute on a lambda's method, so 'params' cannot - // be recovered there and the decompiled call would no longer bind in expanded form. var paramsParameter = (params int[] xs) => xs.Length; Console.WriteLine("params parameter: " + paramsParameter(1, 2, 3)); -#endif } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs index d4dec77ab..b9fa0b055 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LambdaOptionalAndParamsParameters.cs @@ -12,35 +12,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public ParamsAction ParamsStatementBody() { -#if CS140 return (params int[] xs) => { total += xs.Length; Console.WriteLine(xs.Length); }; -#elif EXPECTED_OUTPUT - // Roslyn 4.14 does not emit ParamArrayAttribute on the lambda's method, so the - // 'params' modifier cannot be recovered from metadata. - return delegate (int[] xs) { - total += xs.Length; - Console.WriteLine(xs.Length); - }; -#else - return (params int[] xs) => { - total += xs.Length; - Console.WriteLine(xs.Length); - }; -#endif } public ParamsAction ParamsExpressionBody() { -#if EXPECTED_OUTPUT && !CS140 - return delegate (int[] xs) { - Console.WriteLine(xs.Length); - }; -#else return (params int[] xs) => Console.WriteLine(xs.Length); -#endif } public OptionalFunc OptionalStatementBody() diff --git a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs index 402085354..207b92edb 100644 --- a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs @@ -104,16 +104,16 @@ namespace ICSharpCode.Decompiler.Tests CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }); - // Roslyn 4.14 does not emit ParamArrayAttribute on synthesized lambda methods, so tests - // pinning its round-trip need the current compiler only. - static readonly CompilerOptions[] roslynLatestOnlyOptions = Tester.SupportedOnCurrentPlatform(new[] + static readonly CompilerOptions[] roslyn4OrNewerOptions = Tester.SupportedOnCurrentPlatform(new[] { + CompilerOptions.UseRoslyn4_14_0, + CompilerOptions.Optimize | CompilerOptions.UseRoslyn4_14_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }); [Test] - public async Task NoLambdaOptionalAndParamsParameters([ValueSource(nameof(roslynLatestOnlyOptions))] CompilerOptions cscOptions) + public async Task NoLambdaOptionalAndParamsParameters([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp11_0) { FileScopedNamespaces = false diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 1291a087c..864cd7ccd 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2543,6 +2543,26 @@ 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) + { + int parameterIndex = 0; + foreach (var pd in ame.Parameters) + { + var invokeParameter = invokeParameters[parameterIndex++]; + pd.IsParams |= invokeParameter.IsParams; + if (pd.DefaultExpression is null + && astBuilder.ConvertParameter(invokeParameter).DefaultExpression is { } defaultValue) + { + defaultValue.Detach(); + pd.DefaultExpression = defaultValue; + } + } + } var builder = new StatementBuilder( typeSystem, this.decompilationContext,