Skip to content

[JEP441] Add support for switch pattern matching#4375

Merged
jlerbsc merged 7 commits intojavaparser:masterfrom
johannescoetzee:johannes/switch-patterns
Apr 19, 2024
Merged

[JEP441] Add support for switch pattern matching#4375
jlerbsc merged 7 commits intojavaparser:masterfrom
johannescoetzee:johannes/switch-patterns

Conversation

@johannescoetzee
Copy link
Copy Markdown
Collaborator

Switch pattern matching changes introduced in https://openjdk.org/jeps/441

This PR adds support for switch pattern matching with guards and resolves #4343 using the approach discussed in #4361 and partially implemented in #4357.

In particular, it adds support for the new CasePattern [Guard] syntax in

SwitchLabel:
  case CaseConstant {, CaseConstant}
  case null [, default]
  case CasePattern [Guard]    // <- New support
  default

CasePattern:
  Pattern

Guard:
  when Expression

The main changes in this PR are:

  • Syntax support in the parser
  • Syntax support in the concrete syntax model
  • Addition of an optional SwitchEntry.guard field which contains the guard expression if any
  • Type solver checks switch pattern labels to find variable declarations
  • Fixes for the PrettyPrinter and LexicalPreservingPrinter
  • Some unit tests for all of the above.


String serialized = serialize(cu, false);

assertEquals("{\"!\":\"com.github.javaparser.ast.CompilationUnit\",\"range\":{\"beginLine\":1,\"beginColumn\":1,\"endLine\":1,\"endColumn\":23},\"tokenRange\":{\"beginToken\":{\"kind\":19,\"text\":\"class\"},\"endToken\":{\"kind\":0,\"text\":\"\"}},\"imports\":[],\"types\":[{\"!\":\"com.github.javaparser.ast.body.ClassOrInterfaceDeclaration\",\"range\":{\"beginLine\":1,\"beginColumn\":1,\"endLine\":1,\"endColumn\":23},\"tokenRange\":{\"beginToken\":{\"kind\":19,\"text\":\"class\"},\"endToken\":{\"kind\":103,\"text\":\"}\"}},\"extendedTypes\":[],\"implementedTypes\":[],\"isInterface\":\"false\",\"permittedTypes\":[],\"typeParameters\":[],\"members\":[{\"!\":\"com.github.javaparser.ast.body.FieldDeclaration\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":22},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"java\"},\"endToken\":{\"kind\":106,\"text\":\";\"}},\"modifiers\":[],\"variables\":[{\"!\":\"com.github.javaparser.ast.body.VariableDeclarator\",\"range\":{\"beginLine\":1,\"beginColumn\":21,\"endLine\":1,\"endColumn\":21},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"y\"},\"endToken\":{\"kind\":97,\"text\":\"y\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":21,\"endLine\":1,\"endColumn\":21},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"y\"},\"endToken\":{\"kind\":97,\"text\":\"y\"}},\"identifier\":\"y\"},\"type\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":19},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"java\"},\"endToken\":{\"kind\":97,\"text\":\"Y\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":19,\"endLine\":1,\"endColumn\":19},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"Y\"},\"endToken\":{\"kind\":97,\"text\":\"Y\"}},\"identifier\":\"Y\"},\"scope\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":17},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"java\"},\"endToken\":{\"kind\":97,\"text\":\"util\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":14,\"endLine\":1,\"endColumn\":17},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"util\"},\"endToken\":{\"kind\":97,\"text\":\"util\"}},\"identifier\":\"util\"},\"scope\":{\"!\":\"com.github.javaparser.ast.type.ClassOrInterfaceType\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":12},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"java\"},\"endToken\":{\"kind\":97,\"text\":\"java\"}},\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":9,\"endLine\":1,\"endColumn\":12},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"java\"},\"endToken\":{\"kind\":97,\"text\":\"java\"}},\"identifier\":\"java\"},\"annotations\":[]},\"annotations\":[]},\"annotations\":[]}}],\"annotations\":[]}],\"modifiers\":[],\"name\":{\"!\":\"com.github.javaparser.ast.expr.SimpleName\",\"range\":{\"beginLine\":1,\"beginColumn\":7,\"endLine\":1,\"endColumn\":7},\"tokenRange\":{\"beginToken\":{\"kind\":97,\"text\":\"X\"},\"endToken\":{\"kind\":97,\"text\":\"X\"}},\"identifier\":\"X\"},\"annotations\":[]}]}", serialized);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes here are just some token kind values which changed due to the addition of the WHEN token

EXPORTS(76),
PROVIDES(77),
TRANSITIVE(78),
LONG_LITERAL(79),
Copy link
Copy Markdown
Collaborator Author

@johannescoetzee johannescoetzee Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are generated


public static Kind valueOf(int kind) {
switch(kind) {
case 150:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are generated

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 18, 2024

Codecov Report

Attention: Patch coverage is 70.19868% with 45 lines in your changes are missing coverage. Please review.

Project coverage is 51.912%. Comparing base (9f532e5) to head (7df03e3).
Report is 2 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##            master     #4375       +/-   ##
=============================================
- Coverage   51.929%   51.912%   -0.018%     
=============================================
  Files          503       503               
  Lines        28369     28442       +73     
  Branches      4920      4934       +14     
=============================================
+ Hits         14732     14765       +33     
- Misses       11591     11625       +34     
- Partials      2046      2052        +6     
Flag Coverage Δ
AlsoSlowTests 51.912% <70.198%> (-0.018%) ⬇️
javaparser-core 51.912% <70.198%> (-0.018%) ⬇️
javaparser-symbol-solver 51.912% <70.198%> (-0.018%) ⬇️
jdk-10 51.909% <70.198%> (-0.018%) ⬇️
jdk-11 51.909% <70.198%> (-0.018%) ⬇️
jdk-12 51.909% <70.198%> (-0.018%) ⬇️
jdk-13 51.909% <70.198%> (-0.007%) ⬇️
jdk-14 51.909% <70.198%> (-0.007%) ⬇️
jdk-15 51.909% <70.198%> (-0.018%) ⬇️
jdk-16 51.909% <70.198%> (-0.018%) ⬇️
jdk-17 51.909% <70.198%> (-0.018%) ⬇️
jdk-18 51.909% <70.198%> (-0.018%) ⬇️
jdk-8 51.907% <70.198%> (-0.018%) ⬇️
jdk-9 51.898% <70.198%> (-0.018%) ⬇️
macos-latest 51.905% <70.198%> (-0.018%) ⬇️
ubuntu-latest 51.895% <70.198%> (-0.018%) ⬇️
windows-latest 51.891% <70.198%> (-0.018%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
...src/main/java/com/github/javaparser/JavaToken.java 92.841% <100.000%> (+0.032%) ⬆️
...rc/main/java/com/github/javaparser/TokenTypes.java 84.615% <ø> (ø)
...ub/javaparser/ast/observer/ObservableProperty.java 88.953% <100.000%> (+0.064%) ⬆️
...or/language_level_validations/Java21Validator.java 100.000% <100.000%> (ø)
...hub/javaparser/ast/visitor/VoidVisitorAdapter.java 99.043% <100.000%> (+0.002%) ⬆️
...thub/javaparser/metamodel/JavaParserMetaModel.java 99.462% <100.000%> (+0.002%) ⬆️
...hub/javaparser/metamodel/SwitchEntryMetaModel.java 100.000% <ø> (ø)
...github/javaparser/printer/ConcreteSyntaxModel.java 94.957% <100.000%> (ø)
...avaparser/printer/DefaultPrettyPrinterVisitor.java 92.891% <100.000%> (+0.017%) ⬆️
...r/language_level_validations/Java1_0Validator.java 96.000% <80.000%> (-1.143%) ⬇️
... and 11 more

... and 2 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f530956...7df03e3. Read the comment docs.

@johannescoetzee
Copy link
Copy Markdown
Collaborator Author

I just realized I forgot to update the validators. I'll update the PR with that probably this afternoon.

if (node == null) {
return false;
}
if (guard != null) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this and the counterpart?
Do you have a test case that tests the deletion of a node with a guard expression?

@Override
public Visitable visit(final InitializerDeclaration n, final Object arg) {
BlockStmt body = cloneNode(n.getBody(), arg);
NodeList<AnnotationExpr> annotations = cloneList(n.getAnnotations(), arg);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this relate to the subject of the PR?

@Override
public R visit(final SwitchEntry n, final A arg) {
R result;
if (n.getGuard().isPresent()) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this block!

concreteSyntaxModelByClass.put(ReturnStmt.class, sequence(comment(), token(GeneratedJavaParserConstants.RETURN), conditional(ObservableProperty.EXPRESSION, IS_PRESENT, sequence(space(), child(ObservableProperty.EXPRESSION))), semicolon()));
concreteSyntaxModelByClass.put(YieldStmt.class, sequence(comment(), token(YIELD), conditional(ObservableProperty.EXPRESSION, IS_PRESENT, sequence(space(), child(ObservableProperty.EXPRESSION))), semicolon()));
concreteSyntaxModelByClass.put(SwitchEntry.class, sequence(comment(), conditional(SWITCH_STATEMENT_ENTRY, FLAG, conditional(ObservableProperty.LABELS, IS_NOT_EMPTY, sequence(token(GeneratedJavaParserConstants.CASE), space(), list(ObservableProperty.LABELS), token(GeneratedJavaParserConstants.COLON)), sequence(token(GeneratedJavaParserConstants._DEFAULT), token(GeneratedJavaParserConstants.COLON))), conditional(ObservableProperty.LABELS, IS_NOT_EMPTY, conditional(ObservableProperty.DEFAULT, FLAG, sequence(token(GeneratedJavaParserConstants.CASE), space(), list(ObservableProperty.LABELS), comma(), space(), token(GeneratedJavaParserConstants._DEFAULT), space(), token(GeneratedJavaParserConstants.ARROW)), sequence(token(GeneratedJavaParserConstants.CASE), space(), list(ObservableProperty.LABELS), token(GeneratedJavaParserConstants.ARROW))), sequence(token(GeneratedJavaParserConstants._DEFAULT), space(), token(GeneratedJavaParserConstants.ARROW)))), newline(), indent(), list(ObservableProperty.STATEMENTS, newline(), none(), newline()), unindent()));
concreteSyntaxModelByClass.put(SwitchEntry.class, sequence(comment(), conditional(SWITCH_STATEMENT_ENTRY, FLAG, conditional(ObservableProperty.LABELS, IS_NOT_EMPTY, sequence(token(GeneratedJavaParserConstants.CASE), space(), list(ObservableProperty.LABELS), token(GeneratedJavaParserConstants.COLON)), sequence(token(GeneratedJavaParserConstants._DEFAULT), token(GeneratedJavaParserConstants.COLON))), conditional(ObservableProperty.LABELS, IS_NOT_EMPTY, conditional(ObservableProperty.DEFAULT, FLAG, sequence(token(GeneratedJavaParserConstants.CASE), space(), list(ObservableProperty.LABELS), comma(), space(), token(GeneratedJavaParserConstants._DEFAULT), space(), token(GeneratedJavaParserConstants.ARROW)), sequence(token(GeneratedJavaParserConstants.CASE), space(), list(ObservableProperty.LABELS), conditional(ObservableProperty.GUARDED_ENTRY, FLAG, sequence(space(), token(GeneratedJavaParserConstants.WHEN), space(), child(ObservableProperty.GUARD))), space(), token(GeneratedJavaParserConstants.ARROW))), sequence(token(GeneratedJavaParserConstants._DEFAULT), space(), token(GeneratedJavaParserConstants.ARROW)))), newline(), indent(), list(ObservableProperty.STATEMENTS, newline(), none(), newline()), unindent()));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that the GUARDED_ENTRY property is necessary. Can't we just use the GUARD property?

* be expected or not.
*/
@DerivedProperty
public boolean isGuardedEntry() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that the GUARDED_ENTRY property is necessary. Can't we just use the GUARD property?

@jlerbsc
Copy link
Copy Markdown
Collaborator

jlerbsc commented Apr 18, 2024

Great work. I've got a few questions, but it seems fine to me, with the possible exception of the derived property introduced for the Lexical Preserving Printer.

@johannescoetzee
Copy link
Copy Markdown
Collaborator Author

I've updated this PR with the following:

  • Removed the GUARDED_ENTRY property in favour of using the IS_PRESENT condition on the GUARD
  • Updated validators with tests
  • Added removal tests

@jlerbsc jlerbsc merged commit 62794f1 into javaparser:master Apr 19, 2024
@jlerbsc jlerbsc added this to the next release milestone Apr 19, 2024
@jlerbsc jlerbsc added the PR: Added A PR that introduces new behaviour (e.g. functionality, tests) label Apr 19, 2024
@jlerbsc
Copy link
Copy Markdown
Collaborator

jlerbsc commented Apr 19, 2024

A job well done. Thanks for your time.

@pativa
Copy link
Copy Markdown

pativa commented May 29, 2024

This looks great and should resolve an issue we are facing with javaparser!

Do you have any estimate as of when this will be released? 🙏

@jlerbsc
Copy link
Copy Markdown
Collaborator

jlerbsc commented May 29, 2024

This will be integrated into the next release, which will provide support for all Java 21 non-preview features

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: Added A PR that introduces new behaviour (e.g. functionality, tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pattern matching for switch - JEP 441

3 participants