From 4a2afbb7236f8c061a953562650d222edaaa09db Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 10 Aug 2026 18:56:00 +0200 Subject: [PATCH] Walk indexer accesses when looking for a null-conditional source A query source can be reached through an indexer as well as through a member access or a call: `holder?[0].Where(...).Select(...)` puts an IndexerExpression between the LINQ call and the `?.`. The receiver walk stopped there, so query syntax was still introduced over a source the conditional access had lifted to a nullable value type, and the output failed to compile with CS1936 - the same way as the case that was reported, one node kind further along. IndexerExpression.Target is nullable where MemberReferenceExpression's and InvocationExpression's are not, so only that arm needs to match on the target. Assisted-by: Claude:claude-opus-5[1m]:Claude Code --- .../TestCases/Pretty/QueryExpressions.cs | 9 +++++++++ .../CSharp/Transforms/IntroduceQueryExpressions.cs | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs index 160ef4a65..0870735b1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs @@ -48,6 +48,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public Maybe Value; +#if CS60 + public Maybe this[int index] => default(Maybe); +#endif + public Func> Factory() { return () => default(Maybe); @@ -237,6 +241,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return holder?.Factory()().Where((int value) => value > 0).Select((int value) => value.ToString()); } + + public Maybe? NullConditionalIndexerQuery(MaybeHolder holder) + { + return holder?[0].Where((int value) => value > 0).Select((int value) => value.ToString()); + } #endif public static IEnumerable Issue1310a(bool test) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index d33e41884..4745061e3 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -380,7 +380,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms bool IsNullConditional(Expression target) => target switch { UnaryOperatorExpression { Operator: UnaryOperatorType.NullConditional } => true, MemberReferenceExpression member => IsNullConditional(member.Target), - InvocationExpression { Target: { } invocationTarget } => IsNullConditional(invocationTarget), + InvocationExpression invocation => IsNullConditional(invocation.Target), + IndexerExpression { Target: { } indexerTarget } => IsNullConditional(indexerTarget), _ => false };