Browse Source

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
pull/3969/head
Siegfried Pammer 1 month ago
parent
commit
4a2afbb723
  1. 9
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

9
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs

@ -48,6 +48,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
public Maybe<int> Value; public Maybe<int> Value;
#if CS60
public Maybe<int> this[int index] => default(Maybe<int>);
#endif
public Func<Maybe<int>> Factory() public Func<Maybe<int>> Factory()
{ {
return () => default(Maybe<int>); return () => default(Maybe<int>);
@ -237,6 +241,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
return holder?.Factory()().Where((int value) => value > 0).Select((int value) => value.ToString()); return holder?.Factory()().Where((int value) => value > 0).Select((int value) => value.ToString());
} }
public Maybe<string>? NullConditionalIndexerQuery(MaybeHolder holder)
{
return holder?[0].Where((int value) => value > 0).Select((int value) => value.ToString());
}
#endif #endif
public static IEnumerable<char> Issue1310a(bool test) public static IEnumerable<char> Issue1310a(bool test)

3
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -380,7 +380,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
bool IsNullConditional(Expression target) => target switch { bool IsNullConditional(Expression target) => target switch {
UnaryOperatorExpression { Operator: UnaryOperatorType.NullConditional } => true, UnaryOperatorExpression { Operator: UnaryOperatorType.NullConditional } => true,
MemberReferenceExpression member => IsNullConditional(member.Target), MemberReferenceExpression member => IsNullConditional(member.Target),
InvocationExpression { Target: { } invocationTarget } => IsNullConditional(invocationTarget), InvocationExpression invocation => IsNullConditional(invocation.Target),
IndexerExpression { Target: { } indexerTarget } => IsNullConditional(indexerTarget),
_ => false _ => false
}; };

Loading…
Cancel
Save