From b8cfe9e4b1adf54604717d284b51c63bcb1f419c Mon Sep 17 00:00:00 2001 From: Sebastien Lebreton Date: Mon, 20 Jul 2026 01:23:15 +0200 Subject: [PATCH] Fix #3894: keep lambda parameter typing consistent An anonymous parameter type cannot be named, so the original lambda must have used implicit parameters throughout. Emit the whole parameter list implicitly instead of mixing implicit and explicit declarations. Assisted-by: Copilot:gpt-5.6-sol:GitHub Copilot CLI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 86d2918e-5a24-48b4-9a86-41d331ec3720 --- .../TestCases/Pretty/AnonymousTypes.cs | 13 +++++++++++++ .../CSharp/ExpressionBuilder.cs | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AnonymousTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AnonymousTypes.cs index 1c85f92f9b..32483e92cf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AnonymousTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AnonymousTypes.cs @@ -174,5 +174,18 @@ private static Action MakeAction(T template) { return null; } +#if ROSLYN + private static TValue GetOrCreate(TKey key, Func factory) + { + return factory(key, 1); + } + + private static int MixedLambdaParameters() + { + return GetOrCreate(new { + Value = 1 + }, (item, context) => item.Value + context); + } +#endif } } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 398181075e..5656f1c2b9 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2669,6 +2669,8 @@ IType GetTaskType(IType resultType) IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) { var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index!.Value); + var result = new List(parameters.Count); + bool anyAnonymousType = false; int i = 0; foreach (var parameter in parameters) { @@ -2684,10 +2686,21 @@ IEnumerable MakeParameters(IReadOnlyList param pd.Name = "P_" + i; } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) - pd.Type = null; - yield return pd; + anyAnonymousType = true; + result.Add(pd); i++; } + // An anonymous type cannot be named, so a lambda with such a parameter must be implicitly typed. + // C# also requires all lambda parameters to use the same form (CS0748). Drop every type when the + // remaining parameter syntax permits it; otherwise keep the converted declarations as a + // best-effort fallback for an unrepresentable signature. + if (anyAnonymousType && result.All(p => p.ParameterModifier == ReferenceKind.None + && !p.IsParams && !p.IsScopedRef && p.Attributes.Count == 0 && p.DefaultExpression is null)) + { + foreach (var pd in result) + pd.Type = null; + } + return result; } protected internal override TranslatedExpression VisitBlockContainer(BlockContainer container, TranslationContext context)