Skip to content

Question about switch statements and pattern representation #4361

Closed
@johannescoetzee

Description

@johannescoetzee

Related to #4343 and #4217

Java 21 extends patterns in 2 ways that affect patterns directly (relevant syntax shown with some alternatives omitted for brevity):

Patterns in switch statments can now include guard

SwitchLabel:
  case CaseConstant { , CaseConstant }
  case CasePattern [ Guard ]
  case null [ , default ]

CasePattern:
  Pattern

Guard:
  when Expression

Record Patterns

Pattern:
  TypePattern
  RecordPattern

TypePattern:
  LocalVariableDeclaration

RecordPattern:
  ReferenceType ( [ PatternList ] )

PatternList : 
  Pattern { , Pattern }

Current state

Patterns: JavaParser currently only supports TypePatterns in instanceof expressions and represents these with a PatternExpr.

Switch labels: Switch labels are represented as a list of Expressions in the corresponding SwitchEntry

Proposed changes

1. Switch patterns

Since switch labels are currently represented as a single Expression per label, I think the best solution in this case is to add a

class SwitchPattern {
	Pattern pattern; // Using Pattern here, since this could also be a record pattern
	Expression guard;
}

class to group these. The other option would be to add an Optional<Expression> guard to all patterns and then have it be empty
for all non-switch patterns.

Another syntax change to consider is the case null [ , default ] case. Currently, we can tell if a SwitchEntry is the default
case by checking if the label list is empty, but with the option of having a shared null/default case, this would no longer be
true. The best solution I can think of for this is to add an isDefault field to SwitchEntry that will always be set in the
empty label case (meaning you can use either the empty label list or isDefault to determine whether the entry is a default),
and will also be set in the shared null/default case (in which case the empty label list check will return an incorrect result).

The other option I considered is to add some sort of DefaultExpr as a placeholder, but I think that causes more trouble than it's worth.

2. Record patterns

This is the trickier change of the two. The ideal case would be to split PatternExpr into 2 separate classes: TypePatternExpr and
RecordPatternExpr, both implementing a PatternExpr interface/base class for generics. This would be a rather large change for
anyone currently using PatternExpr, so an alternative could be to keep PatternExpr as the representation for TypePattern and to
use something like Pattern as a common base for that and RecordPatternExpr.

An option that avoids having two pattern classes for the different types would be to use PatternExpr for both types of patterns.
To do this, we could add a NodeList<PatternExpr> patterns field to PatternExpr, which would be empty if the pattern is a TypePattern
and set to the patterns in the PatternList otherwise. We would then also have to change the name field of PatternExpr to
Optional<SimpleName> and set it to empty if the PatternExpr is a RecordPattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions