Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: JDK 22 unnamed variables & patterns #620

Conversation

jtkiesel
Copy link
Contributor

What changed with this PR:

JDK 22 unnamed variables & patterns, and switch labels with multiple patterns, are now supported.

Example

Input

class T {
  static int count(Iterable<Order> orders) {
    int total = 0;
    for (Order _ : orders)  // Unnamed variable
      total++;
    return total;
  }

  void simpleForLoop() {
    for (int i = 0, _ = sideEffect(); i < 10; i++) {}
  }

  void assignment() {
    while (q.size() >= 3) {
      var x = q.remove();
      var y = q.remove();
      var _ = q.remove();  // Unnamed variable
    }
  }

  void multipleAssignment() {
    while (q.size() >= 3) {
      var x = q.remove();
      var _ = q.remove();  // Unnamed variable
      var _ = q.remove();  // Unnamed variable
    }
  }

  void catchClause() {
    try {
      int i = Integer.parseInt(s);
    } catch (NumberFormatException _) {  // Unnamed variable
      System.out.println("Bad number: " + s);
    }
  }

  void multipleCatchClauses() {
    try {}
    catch (Exception _) {}  // Unnamed variable
    catch (Throwable _) {}  // Unnamed variable
  }

  void tryWithResources() {
    try (var _ = ScopedContext.acquire()) {  // Unnamed variable
    }
  }

  void lambda() {
    stream.collect(Collectors.toMap(String::toUpperCase, _ -> "NODATA"));  // Unnamed variable
  }

  void switchTypePattern() {
    switch (ball) {
      case RedBall _   -> process(ball);     // Unnamed pattern variable
      case BlueBall _  -> process(ball);     // Unnamed pattern variable
      case GreenBall _ -> stopProcessing();  // Unnamed pattern variable
    }
  }

  void switchRecordPattern() {
    switch (box) {
      case Box(RedBall _)   -> processBox(box);   // Unnamed pattern variable
      case Box(BlueBall _)  -> processBox(box);   // Unnamed pattern variable
      case Box(GreenBall _) -> stopProcessing();  // Unnamed pattern variable
      case Box(var _)       -> pickAnotherBox();  // Unnamed pattern variable
    }
  }

  void multipleSwitchPatterns() {
    switch (box) {
      case Box(RedBall _), Box(BlueBall _) -> processBox(box);
      case Box(GreenBall _)                -> stopProcessing();
      case Box(var _)                      -> pickAnotherBox();
    }
  }

  void multipleSwitchPatternsWithGuard() {
    switch (box) {
      case Box(RedBall _), Box(BlueBall _) when x == 42 -> processBox(b);
    }
  }

  void instanceofExpressions() {
    if (r instanceof ColoredPoint(Point(int x, int y), _)) {}
    if (r instanceof ColoredPoint(_, Color c)) {}
    if (r instanceof ColoredPoint(Point(int x, _), _)) {}
  }

  void switchLabelWithUnnamedPattern() {
    switch (box) {
      case Box(RedBall _), Box(BlueBall _) -> processBox(box);
      case Box(GreenBall _)                -> stopProcessing();
      case Box(_)                          -> pickAnotherBox();
    }
  }

  int wrappingMultipleSwitchPatterns() {
    return switch ("") {
      case LongTypeName longVariableName, LongTypeName longVariableName -> 0;
      case LongTypeName longVariableName, LongTypeName longVariableName, LongTypeName longVariableName -> 0;
      case MyRecord(A a), MyRecord(B b) -> 0;
      case MyRecord(A a), MyRecord(B b) when true -> 0;
      case MyRecord(LongTypeName longVariableName, LongTypeName longVariableName), MyRecord(LongTypeName longVariableName, LongTypeName longVariableName) -> 0;
      case MyRecord(LongTypeName longVariableName, LongTypeName longVariableName), MyRecord(LongTypeName longVariableName, LongTypeName longVariableName) when this.longVariableName > longVariableName && this.longVariableName > longVariableName -> longMethodName(longVariableName, longVariableName, longVariableName, longVariableName);
    };
  }
}

Output

class T {

  static int count(Iterable<Order> orders) {
    int total = 0;
    for (Order _ : orders) total++; // Unnamed variable
    return total;
  }

  void simpleForLoop() {
    for (int i = 0, _ = sideEffect(); i < 10; i++) {}
  }

  void assignment() {
    while (q.size() >= 3) {
      var x = q.remove();
      var y = q.remove();
      var _ = q.remove(); // Unnamed variable
    }
  }

  void multipleAssignment() {
    while (q.size() >= 3) {
      var x = q.remove();
      var _ = q.remove(); // Unnamed variable
      var _ = q.remove(); // Unnamed variable
    }
  }

  void catchClause() {
    try {
      int i = Integer.parseInt(s);
    } catch (NumberFormatException _) { // Unnamed variable
      System.out.println("Bad number: " + s);
    }
  }

  void multipleCatchClauses() {
    try {} catch (Exception _) {} catch (Throwable _) {} // Unnamed variable // Unnamed variable
  }

  void tryWithResources() {
    try (var _ = ScopedContext.acquire()) {} // Unnamed variable
  }

  void lambda() {
    stream.collect(Collectors.toMap(String::toUpperCase, _ -> "NODATA")); // Unnamed variable
  }

  void switchTypePattern() {
    switch (ball) {
      case RedBall _ -> process(ball); // Unnamed pattern variable
      case BlueBall _ -> process(ball); // Unnamed pattern variable
      case GreenBall _ -> stopProcessing(); // Unnamed pattern variable
    }
  }

  void switchRecordPattern() {
    switch (box) {
      case Box(RedBall _) -> processBox(box); // Unnamed pattern variable
      case Box(BlueBall _) -> processBox(box); // Unnamed pattern variable
      case Box(GreenBall _) -> stopProcessing(); // Unnamed pattern variable
      case Box(var _) -> pickAnotherBox(); // Unnamed pattern variable
    }
  }

  void multipleSwitchPatterns() {
    switch (box) {
      case Box(RedBall _), Box(BlueBall _) -> processBox(box);
      case Box(GreenBall _) -> stopProcessing();
      case Box(var _) -> pickAnotherBox();
    }
  }

  void multipleSwitchPatternsWithGuard() {
    switch (box) {
      case Box(RedBall _), Box(BlueBall _) when x == 42 -> processBox(b);
    }
  }

  void instanceofExpressions() {
    if (r instanceof ColoredPoint(Point(int x, int y), _)) {}
    if (r instanceof ColoredPoint(_, Color c)) {}
    if (r instanceof ColoredPoint(Point(int x, _), _)) {}
  }

  void switchLabelWithUnnamedPattern() {
    switch (box) {
      case Box(RedBall _), Box(BlueBall _) -> processBox(box);
      case Box(GreenBall _) -> stopProcessing();
      case Box(_) -> pickAnotherBox();
    }
  }

  int wrappingMultipleSwitchPatterns() {
    return switch ("") {
      case LongTypeName longVariableName, LongTypeName longVariableName -> 0;
      case
        LongTypeName longVariableName,
        LongTypeName longVariableName,
        LongTypeName longVariableName -> 0;
      case MyRecord(A a), MyRecord(B b) -> 0;
      case MyRecord(A a), MyRecord(B b) when true -> 0;
      case
        MyRecord(LongTypeName longVariableName, LongTypeName longVariableName),
        MyRecord(
          LongTypeName longVariableName,
          LongTypeName longVariableName
        ) -> 0;
      case
        MyRecord(LongTypeName longVariableName, LongTypeName longVariableName),
        MyRecord(LongTypeName longVariableName, LongTypeName longVariableName)
      when (
        this.longVariableName > longVariableName &&
        this.longVariableName > longVariableName
      ) -> longMethodName(
        longVariableName,
        longVariableName,
        longVariableName,
        longVariableName
      );
    };
  }
}

Relative issues or prs:

Closes #612

@jtkiesel
Copy link
Contributor Author

@clementdessoude Let me know what you think about the formatting choices I made. I went back and forth over a few different styles for formatting switch labels with multiple patterns, and ended up deciding to go with this one, but I'd certainly be open to a different style.

@jtkiesel jtkiesel force-pushed the feat/jdk-22-unnamed-variables-and-patterns branch from 8432aa8 to c56a8b8 Compare November 13, 2023 02:59
@clementdessoude
Copy link
Contributor

clementdessoude commented Nov 13, 2023

For this one, I think the review will have to wait for next weekend, as it is less trivial than #603 😅

@jtkiesel
Copy link
Contributor Author

No worries! Initially, I tried to keep my changes to a minimum, but realized that some of the spec deviations were causing a lot of trouble for me, so I ended up aligning things a bit more closely to the spec which helped keep the formatting code a lot simpler.

@jtkiesel jtkiesel force-pushed the feat/jdk-22-unnamed-variables-and-patterns branch from c56a8b8 to 9678640 Compare November 22, 2023 06:03
Comment on lines -519 to -541
// https://docs.oracle.com/javase/specs/jls/se16/html/jls-14.html#jls-Resource
// https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-Resource
$.RULE("resource", () => {
$.OR([
{
GATE: $.BACKTRACK($.resourceInit),
// Spec Deviation: extracted this alternative to "resourceInit"
// to enable backtracking.
ALT: () => $.SUBRULE($.resourceInit)
GATE: () => $.BACKTRACK_LOOKAHEAD($.isLocalVariableDeclaration),
ALT: () => $.SUBRULE($.localVariableDeclaration)
},
{ ALT: () => $.SUBRULE($.variableAccess) }
]);
});

// Spec Deviation: extracted from "resource"
$.RULE("resourceInit", () => {
$.MANY(() => {
$.SUBRULE($.variableModifier);
});
$.SUBRULE($.localVariableType);
$.CONSUME(t.Identifier);
$.CONSUME(t.Equals);
$.SUBRULE($.expression);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the update ! If I may, it would have been even better if you split the commit in two, one for handling the new JDK 22 unnamed variables & patterns feature, and one other for updating this: it is easier to review as it does not mix up two different things :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I probably should have made this change as the first commit, proving that no changes occurred after making this change, and then have my JDK 22 unnamed variables & patterns feature be the second commit, which would effectively leverage the change from the first. I'll keep this kind of thing in mind for future contributions!

@clementdessoude clementdessoude merged commit 904ce59 into jhipster:main Nov 26, 2023
6 checks passed
@jtkiesel jtkiesel deleted the feat/jdk-22-unnamed-variables-and-patterns branch November 26, 2023 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support JDK 22 multiple patterns in switch case label
2 participants