Proposal: Extended property patterns · Issue #4394 · dotnet/csharplang (original) (raw)

Extended property patterns

Summary

Allow property subpatterns to reference nested members.

Motivation

As discussed in #4114 when you want to match a child property, nesting another recursive pattern adds too much noise which will hurt readability with no real advantage.

Detailed design

Semantics

A pattern of the form { Property1.Property2: pattern } is exactly equivalent to { Property1: { Property2: pattern } }.

This will include the null check for Nullable<T> values as it is the case for the expanded form, so we only see the underlying type's members when we dot off of a property pattern.

Repeated member paths are allowed. Under the hood, such member accesses are simplified to be evaluated once.

Syntax

Currently a SubpatternSyntax uses NameColonSyntax which is not able to hold a chain of identifiers.

There are several avenues we could take:

  1. Accept a generic ExpressoinSyntax in place of the name
    This would particularly help with parsing and avoiding lookaheads, since patterns and expressions have a common parsing path, we can start with the pattern and if we get to a colon, we just adjust it as the name and continue to parse the actual pattern.
  2. Accept a generic NameSyntax in place of the name
    This seems to be the "correct" node to use, but is more expensive to parse. Also. if and when we introduce indexer patterns we probably want to relax property patterns to enable nesting those as well e.g. { Property1[0].Property2[1]: pattern }. In that case we'll need to go with option (1) for forward compatibility.
  3. Accept a new QualifiedNameColonSyntax in addition to NameColonSyntax
    This option doesn't quite help with anything other than keeping the API consistent.

In the first two options, we're able to hold a simple identifier so we might want to consider deprecating NameColon API.

Alternatives

We could use { P1?.P2: p } syntax to make the implicit null check more apparent. But then we still want to consider P1.P2 in case ?. doesn't apply e.g. for structs. Since patterns shouldn't throw in regular usage and we should emit the null-check anyways, that distinction would seem to be unnecessary.

Design meetings