From 99613cc797ff6e15177937c5f9f4c57b9640a687 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 30 Aug 2026 12:22:36 +0200 Subject: [PATCH] Fix comment + LINQ: validate type arguments in ThenBy-chain. --- ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs | 7 +++++-- .../CSharp/Transforms/IntroduceQueryExpressions.cs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index f81bc7b50..b1e678cd1 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -1336,8 +1336,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver // Apply the merged top-level nullability: Debug.Assert(topLevelNullability.HasValue); - // (Roslyn has a different approach in MergeOrRemoveCandidates, but to me that just looked - // like an overly complicated way of achieving the same thing.) + // Roslyn has a different approach in MergeOrRemoveCandidates, which can + // differ in behavior when there's both lower+upper bounds + // -- e.g. `static void M(T x, Action a)` called with `M("s", (object? o) => {}))` + // is inferred as `T = object?` by Roslyn, but `T = object` by us. + // To match Roslyn exactly, we'd need to handle the topLevelNullability per-candidate. for (int i = 0; i < candidateTypes.Count; i++) { candidateTypes[i] = candidateTypes[i].ChangeNullability(topLevelNullability.Value); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index 5714d6abe..c9c5064f2 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -348,7 +348,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms static bool IsComplexQuery(MemberReferenceExpression mre) { - return ((mre.Target is InvocationExpression && mre.Parent is InvocationExpression) || mre.Parent?.Parent is QueryClause); + return (mre.Target is InvocationExpression && mre.Parent is InvocationExpression) || mre.Parent?.Parent is QueryClause; } QueryFromClause MakeFromClause(ParameterDeclaration parameter, Expression body) @@ -414,6 +414,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return false; if (parameter.Name != expectedParameterName) return false; + if (mre.TypeArguments.Count > 0) + return false; if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending") return !IsNullConditional(mre.Target);