jhipster / prettier-java

Prettier Java Plugin
http://www.jhipster.tech/prettier-java/
Apache License 2.0
1.08k stars 103 forks source link

feat: JDK 22 unnamed variables & patterns #620

Closed jtkiesel closed 10 months ago

jtkiesel commented 10 months ago

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 commented 10 months ago

@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.

clementdessoude commented 10 months ago

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

jtkiesel commented 10 months ago

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.