8000 [JEP441] Add support for switch pattern matching by johannescoetzee · Pull Request #4375 · javaparser/javaparser · GitHub
[go: up one dir, main page]

Skip to content

Conversation

johannescoetzee
Copy link
Contributor

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
Contributor 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
Contributor 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
Contributor 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

Copy link
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
Contributor 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
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
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
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
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
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
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.

< 6293 /task-lists>

@johannescoetzee
Copy link
Contributor 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
Collaborator
jlerbsc commented Apr 19, 2024

A job well done. Thanks for your time.

@pativa
Copy link
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
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
0