rewrite icon indicating copy to clipboard operation
rewrite copied to clipboard

Support switch expressions in Java 21; currently fails with "is not print idempotent"

Open fprochazka opened this issue 1 year ago • 3 comments

What version of OpenRewrite are you using?

I am using

  • JDK Temurin 21.0.1
  • OpenRewrite 8.27.1
  • Maven 3.9.5
  • Maven/Gradle plugin 5.32.1
  • logging module 2.7.2
  • static-analysis module 1.8.1
  • testing-frameworks module 2.10.1

How are you running OpenRewrite?

I am using the Maven plugin, and my project is a multi-module project

pom.xml
  <plugin>
      <groupId>org.openrewrite.maven</groupId>
      <artifactId>rewrite-maven-plugin</artifactId>
      <version>${maven-rewrite-plugin.version}</version>
      <dependencies>
          <dependency>
              <groupId>com.shipmonk.tooling</groupId>
              <artifactId>shipmonk-tooling-openrewrite</artifactId>
              <version>${project.version}</version>
          </dependency>
      </dependencies>
      <configuration>
          <configLocation>${shipmonk-framework.basedir}/src/openrewrite/rewrite.yml</configLocation>
          <activeRecipes>
              <recipe>com.shipmonk.Cleanup</recipe>
              <recipe>org.openrewrite.java.logging.slf4j.ParameterizedLogging</recipe>
              <recipe>com.shipmonk.tooling.openrewrite.cleanup.ThrowsFormattingRecipe</recipe>
              <recipe>com.shipmonk.tooling.openrewrite.cleanup.MethodDeclarationParametersWrappingRecipe</recipe>
          </activeRecipes>
          <checkstyleDetectionEnabled>false</checkstyleDetectionEnabled>
          <activeStyles>
              <style>com.shipmonk.Style</style>
              <style>com.shipmonk.tooling.openrewrite.style.ShipMonkCheckstyle</style>
          </activeStyles>
          <failOnInvalidActiveRecipes>true</failOnInvalidActiveRecipes>
          <skipMavenParsing>true</skipMavenParsing>
          <exclusions>
            <!-- relative from git root dir -->
            <exclusion>**/.data</exclusion>
            <exclusion>**/.idea</exclusion>
            <exclusion>**/.cache</exclusion>
            <exclusion>**/.mvn</exclusion>
            <exclusion>**/docs</exclusion>
            <exclusion>**/docker</exclusion>
            <exclusion>**/*.iml</exclusion>
            <exclusion>**/*.json</exclusion>
            <exclusion>**/*.xml</exclusion>
          </exclusions>
      </configuration>
  </plugin>
rewrite.yml
---
type: specs.openrewrite.org/v1beta/recipe
name: com.shipmonk.Cleanup
recipeList:
  - org.openrewrite.java.OrderImports:
      removeUnused: false
  - org.openrewrite.java.RemoveObjectsIsNull: { }
  - org.openrewrite.java.format.OperatorWrap: { }
  - org.openrewrite.java.format.TypecastParenPad: { }
  - org.openrewrite.java.format.BlankLines: { }
  - org.openrewrite.java.format.EmptyNewlineAtEndOfFile: { }
  - org.openrewrite.java.format.SingleLineComments: { }
  - org.openrewrite.java.logging.slf4j.LoggersNamedForEnclosingClass: { }
  - org.openrewrite.java.logging.slf4j.Slf4jLogShouldBeConstant: { }
  - org.openrewrite.staticanalysis.DefaultComesLast: { }
  - org.openrewrite.staticanalysis.ModifierOrder: { }
  - org.openrewrite.staticanalysis.StringLiteralEquality: { }
  - org.openrewrite.staticanalysis.CompareEnumsWithEqualityOperator: { }
  - org.openrewrite.staticanalysis.EqualsAvoidsNull: { }
  - com.shipmonk.tooling.openrewrite.cleanup.ThrowsFormattingRecipe: { }
  - com.shipmonk.tooling.openrewrite.cleanup.MethodDeclarationParametersWrappingRecipe: { }

---
type: specs.openrewrite.org/v1beta/style
name: com.shipmonk.Style
styleConfigs:
  - org.openrewrite.java.style.ImportLayoutStyle:
      classCountToUseStarImport: 9999
      nameCountToUseStarImport: 9999
      layout:
        - import all other imports
        - <blank line>
        - import jakarta.*
        - import javax.*
        - import java.*
        - <blank line>
        - import static all other imports
  - org.openrewrite.java.style.TabsAndIndentsStyle:
      useTabCharacter: false
      tabSize: 4
      indentSize: 4
      continuationIndent: 4
      indentsRelativeToExpressionStart: false
      methodDeclarationParameters:
        alignWhenMultiple: false

(The problem persists even if I disable my own recipes)

What is the full stack trace of any errors you encountered?

It doesn't like some of the switch statements, but only some and I don't see why. I was able to obtain these details by adding --errors to the mvn command

[WARNING] There were problems parsing relative-path/NotificationResultHandlingIntegrationManager.java
[WARNING] java.lang.IllegalStateException: relative-path/NotificationResultHandlingIntegrationManager.java is not print idempotent. 
diff --git a/absolute-path/NotificationResultHandlingIntegrationManager.java b/absolute-path/NotificationResultHandlingIntegrationManager.java
index d316ac8..9fcbe95 100644
--- a/absolute-path/NotificationResultHandlingIntegrationManager.java
+++ b/absolute-path/NotificationResultHandlingIntegrationManager.java
@@ -33,7 +33,7 @@ 
 
         for (PostSendActionResult operation : postSendHookOperations) {
             switch (operation) {
-                case PostSendActionResult.DocumentAsyncRequestCreation documentAsyncRequestCreation -> {
+                defaultcase PostSendActionResult.DocumentAsyncRequestCreation documentAsyncRequestCreation -> {
                     entityManager.persist(documentAsyncRequestCreation.documentAsyncRequest());
                 }
             }

  org.openrewrite.Parser.requirePrintEqualsInput(Parser.java:52)

fprochazka avatar Jun 01 '24 13:06 fprochazka

I think the issue is that OpenRewrite does not yet fully support Java's pattern matching for switch expressions and statements as previewed starting with Java 17 and then released with Java 21. This needs to be fixed in the parser (and printer).

knutwannheden avatar Jun 02 '24 15:06 knutwannheden

@timtebeek did this one fall through the cracks? I stumbled upon an old Quarkus issue complaining about it and this is a pattern that becomes more and more common.

In our case, the example was:

          return switch(rand) {
-            case String s -> s;
-            case Object o -> String.valueOf(o);
+            defaultcase String s -> s;
+            defaultcase Object o -> String.valueOf(o);
         };

gsmet avatar Aug 21 '24 15:08 gsmet

hi @gsmet Thanks for the tag! Indeed we haven't yet built in support for switch expressions into our Java 21 parser. Definitely should though; especially as we're eyeing a Java 21 upgrade internally too, which will make us feel this pain too.

timtebeek avatar Aug 21 '24 16:08 timtebeek

Full blown support for switch pattern matching is now available:

  • https://github.com/openrewrite/rewrite/pull/4661

Record pattern matching is print idempotent, I'll work on full blown support next week

Laurens-W avatar Jan 17 '25 13:01 Laurens-W

Awesome, thanks!

gsmet avatar Jan 17 '25 13:01 gsmet