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

Tolerate but ignore UseImportPolicy to cover additional recipes #53

Merged
merged 21 commits into from
Dec 27, 2023

Conversation

timtebeek
Copy link
Contributor

@timtebeek timtebeek commented Dec 23, 2023

What's your motivation?

Anything in particular you'd like reviewers to focus on?

We now skip templates that start with if ; no checks yet on only having a single statement, or not using while/for/...
We could track support for any of those separately in #47 if we think it's worthwhile to support.

Have you considered any alternatives or workarounds?

  • We could have generated lambdas that start with the FQN, but that diverges from what folks specified and would require a subsequent clean up.
  • We could have changed JavaTemplate to maybeAddImport on any .staticImports("org.mockito.Mockito.never") it receives. That likely might be desirable anyway, but can be picked up separately.

@timtebeek timtebeek added the enhancement New feature or request label Dec 23, 2023
@timtebeek timtebeek self-assigned this Dec 23, 2023
@knutwannheden
Copy link
Contributor

I am unsure about this. Ideally I would like to not use any imports in the template code and then let the qualified name shortening recipe remove any unnecessary qualified names. Do we have any examples where this doesn't work?

@knutwannheden
Copy link
Contributor

Supporting UseImportPolicy on top of that would be nice, but not very critical, IMHO. Possibly we would in the future want a code style to control specific methods to always import statically. That would then possibly be at odds with this annotation. But this is of course all very hypothetical.

@timtebeek
Copy link
Contributor Author

I've tried this out in rewrite-migrate-java, but from this recipe

package org.openrewrite.java.migrate.io;

import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;

import java.nio.file.Path;

import static java.nio.file.Files.exists;

public class FileExists {
    @BeforeTemplate
    boolean before(Path path) {
        return path.toFile().exists();
    }

    @AfterTemplate
    boolean after(Path path) {
        return exists(path);
    }
}
we end up with a failing test
package org.openrewrite.java.migrate.io;

import org.junit.jupiter.api.Test;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class FileExistsTest implements RewriteTest {
    @Test
    void fileExists() {
        rewriteRun(
          recipeSpec -> recipeSpec.recipe(new FileExistsRecipe()),
          //language=java
          java(
            """
              import java.nio.file.Path;
              class Test {
                  boolean test(Path path) {
                      return path.toFile().exists();
                  }
              }
              """,
            """
              import java.nio.file.Path;
              
              import static java.nio.file.Files.exists;
              
              class Test {
                  boolean test(Path path) {
                      return exists(path);
                  }
              }
              """
          )
        );
    }
}
diff --git a/Test.java b/Test.java
index c502b8f..e381ae5 100644
--- a/Test.java
+++ b/Test.java
@@ -1,7 +1,4 @@ 
 import java.nio.file.Path;
-
-import static java.nio.file.Files.exists;
-
 class Test {
     boolean test(Path path) {
         return exists(path);

Comment on lines +24 to +28
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface UseImportPolicy {
ImportPolicy value();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just to show we are able to handle/ignore this annotation; I don't plan to support it's individual values.

public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
JavaTemplate.Matcher matcher;
if ((matcher = before.matcher(getCursor())).find()) {
maybeAddImport("java.nio.file.Files", "exists");
Copy link
Contributor Author

@timtebeek timtebeek Dec 23, 2023

Choose a reason for hiding this comment

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

Note that this maybeAddImport is not necessary when we use Files.exists(path) in the @AfterTemplate instead of a static import there. But unfortunately in error-prone-support they use this style often, such as in MockitoRules.

@timtebeek
Copy link
Contributor Author

Found the source of the failure in

class CheckIndexConditional {
    @BeforeTemplate
    void before(int index, int size) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
    }

    @AfterTemplate
    void after(int index, int size) {
        checkIndex(index, size);
    }
}

The if conditional leads to a compilation error when we produce the lambda.

@timtebeek
Copy link
Contributor Author

With these changes we now go from supporting 94 refaster rule before methods in error-prone-support to covering 152 before methods. 🎉

@timtebeek timtebeek marked this pull request as ready for review December 24, 2023 11:49
@timtebeek timtebeek changed the title Support adding new static imports Support adding new static imports, arrays and ignore if statements Dec 24, 2023
@timtebeek timtebeek marked this pull request as draft December 25, 2023 12:05
@timtebeek timtebeek changed the title Support adding new static imports, arrays and ignore if statements Add new static imports Dec 27, 2023
@timtebeek
Copy link
Contributor Author

Checked main branch again after the latest changes; for this recipe

import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;

import java.nio.file.Path;

public class FileExists {
    @BeforeTemplate
    boolean before(Path path) {
        return path.toFile().exists();
    }

    @AfterTemplate
    boolean after(Path path) {
        return java.nio.file.Files.exists(path);
    }
}

we generate non-static imports on main

import org.junit.jupiter.api.Test;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class FileExistsTest implements RewriteTest {
    @Test
    void fileExists() {
        rewriteRun(
          recipeSpec -> recipeSpec.recipe(new FileExistsRecipe()),
          //language=java
          java(
            """
              import java.nio.file.Path;
              class Test {
                  boolean test(Path path) {
                      return path.toFile().exists();
                  }
              }
              """,
            """
              import java.nio.file.Files;
              import java.nio.file.Path;
              
              class Test {
                  boolean test(Path path) {
                      return Files.exists(path);
                  }
              }
              """
          )
        );
    }
}

@timtebeek
Copy link
Contributor Author

Closing this for now, as we don't yet have a method of conflict resolution if we were to aim for static imports.

@timtebeek timtebeek closed this Dec 27, 2023
@timtebeek
Copy link
Contributor Author

We do support these additional recipes with non-static imports, and uncovered and fixed a few more issues, so not much work lost.

@timtebeek timtebeek deleted the support-adding-new-static-imports branch December 27, 2023 14:31
@timtebeek timtebeek restored the support-adding-new-static-imports branch December 27, 2023 14:31
@timtebeek
Copy link
Contributor Author

Ah no wait; we should still tolerate UseImportPolicy, even if we ignore it's hints. 🙃

@timtebeek timtebeek reopened this Dec 27, 2023
@timtebeek timtebeek changed the title Add new static imports Tolerate but ignore UseImportPolicy to cover additional recipes Dec 27, 2023
@timtebeek timtebeek marked this pull request as ready for review December 27, 2023 14:37
@@ -501,22 +498,6 @@ private void maybeRemoveImports(Map<JCTree.JCMethodDecl, Set<String>> importsByT
beforeImports.forEach(anImport -> recipe.append(" maybeRemoveImport(\"").append(anImport).append("\");\n"));
}

private void maybeAddStaticImports(Map<JCTree.JCMethodDecl, Set<String>> importsByTemplate, StringBuilder recipe, JCTree.JCMethodDecl beforeTemplate, JCTree.JCMethodDecl afterTemplate) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we really already remove this code?

@timtebeek timtebeek merged commit 7993e10 into main Dec 27, 2023
1 check passed
@timtebeek timtebeek deleted the support-adding-new-static-imports branch December 27, 2023 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request refaster
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants