diff --git a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java index 18ec6f50..9a29a9dd 100644 --- a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java +++ b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java @@ -494,15 +494,9 @@ private String recipeDescriptor(JCTree.JCClassDecl classDecl, String defaultDisp private void maybeRemoveImports(Map> importsByTemplate, StringBuilder recipe, JCTree.JCMethodDecl beforeTemplate, JCTree.JCMethodDecl afterTemplate) { Set beforeImports = getBeforeImportsAsStrings(importsByTemplate, beforeTemplate); - Set afterImports = getImportsAsStrings(importsByTemplate, afterTemplate); - for (String anImport : beforeImports) { - if (anImport.startsWith("java.lang.")) { - continue; - } - if (beforeImports.contains(anImport) && !afterImports.contains(anImport)) { - recipe.append(" maybeRemoveImport(\"").append(anImport).append("\");\n"); - } - } + beforeImports.removeAll(getImportsAsStrings(importsByTemplate, afterTemplate)); + beforeImports.removeIf(i -> i.startsWith("java.lang.")); + beforeImports.forEach(anImport -> recipe.append(" maybeRemoveImport(\"").append(anImport).append("\");\n")); } private Set getBeforeImportsAsStrings(Map> importsByTemplate, JCTree.JCMethodDecl templateMethod) { @@ -663,7 +657,7 @@ private String toLambda(JCTree.JCMethodDecl method) { StringJoiner joiner = new StringJoiner(", ", "(", ")"); for (JCTree.JCVariableDecl parameter : method.getParameters()) { - String paramType = parameter.getType().type.tsym.getQualifiedName().toString(); + String paramType = parameter.getType().type.toString(); if (!getBoxedPrimitive(paramType).equals(paramType)) { paramType = "@Primitive " + getBoxedPrimitive(paramType); } else if (paramType.startsWith("java.lang.")) { @@ -726,18 +720,17 @@ private TemplateDescriptor validate(Context context, JCCompilationUnit cu) { return null; } - boolean valid = true; for (JCTree member : classDecl.getMembers()) { if (member instanceof JCTree.JCMethodDecl && !beforeTemplates.contains(member) && member != afterTemplate) { for (JCTree.JCAnnotation annotation : getTemplateAnnotations(((JCTree.JCMethodDecl) member), UNSUPPORTED_ANNOTATIONS::contains)) { printNoteOnce("@" + annotation.annotationType + " is currently not supported", classDecl.sym); - valid = false; + return null; } } } // resolve so that we can inspect the template body - valid &= resolve(context, cu); + boolean valid = resolve(context, cu); if (valid) { for (JCTree.JCMethodDecl template : beforeTemplates) { valid &= validateTemplateMethod(template); @@ -748,27 +741,29 @@ private TemplateDescriptor validate(Context context, JCCompilationUnit cu) { } private boolean validateTemplateMethod(JCTree.JCMethodDecl template) { - // TODO Additional Refaster features https://github.com/openrewrite/rewrite-templating/issues/47 - boolean valid = true; for (JCTree.JCAnnotation annotation : getTemplateAnnotations(template, UNSUPPORTED_ANNOTATIONS::contains)) { printNoteOnce("@" + annotation.annotationType + " is currently not supported", classDecl.sym); - valid = false; + return false; } for (JCTree.JCVariableDecl parameter : template.getParameters()) { for (JCTree.JCAnnotation annotation : getTemplateAnnotations(parameter, UNSUPPORTED_ANNOTATIONS::contains)) { printNoteOnce("@" + annotation.annotationType + " is currently not supported", classDecl.sym); - valid = false; + return false; } if (parameter.vartype instanceof ParameterizedTypeTree || parameter.vartype.type instanceof Type.TypeVar) { printNoteOnce("Generics are currently not supported", classDecl.sym); - valid = false; + return false; } } if (template.restype instanceof ParameterizedTypeTree || template.restype.type instanceof Type.TypeVar) { printNoteOnce("Generics are currently not supported", classDecl.sym); - valid = false; + return false; + } + if (template.body.stats.get(0) instanceof JCTree.JCIf) { + printNoteOnce("If statements are currently not supported", classDecl.sym); + return false; } - valid &= new TreeScanner() { + return new TreeScanner() { boolean valid = true; boolean validate(JCTree tree) { @@ -778,14 +773,14 @@ boolean validate(JCTree tree) { @Override public void visitIdent(JCTree.JCIdent jcIdent) { - if (jcIdent.sym != null + if (valid + && jcIdent.sym != null && jcIdent.sym.packge().getQualifiedName().contentEquals("com.google.errorprone.refaster")) { printNoteOnce(jcIdent.type.tsym.getQualifiedName() + " is currently not supported", classDecl.sym); valid = false; } } }.validate(template.getBody()); - return valid; } public void beforeTemplate(JCTree.JCMethodDecl method) { @@ -811,17 +806,16 @@ private boolean resolve(Context context, JCCompilationUnit cu) { } return true; } - } - private final Set printedMessages = new HashSet<>(); + private final Map printedMessages = new TreeMap<>(); /** * @param message The message to print * @param symbol The symbol to attach the message to; printed as clickable link to file */ private void printNoteOnce(String message, Symbol.ClassSymbol symbol) { - if (printedMessages.add(message)) { + if (printedMessages.compute(message, (k, v) -> v == null ? 1 : v + 1) == 1) { processingEnv.getMessager().printMessage(Kind.NOTE, message, symbol); } } diff --git a/src/main/java/org/openrewrite/java/template/processor/TemplateProcessor.java b/src/main/java/org/openrewrite/java/template/processor/TemplateProcessor.java index 73fd6504..ce51a9c9 100644 --- a/src/main/java/org/openrewrite/java/template/processor/TemplateProcessor.java +++ b/src/main/java/org/openrewrite/java/template/processor/TemplateProcessor.java @@ -208,7 +208,7 @@ public void visitIdent(JCTree.JCIdent ident) { for (JCTree.JCVariableDecl parameter : parameters) { if (parameter.type.tsym instanceof Symbol.ClassSymbol) { String paramType = parameter.type.tsym.getQualifiedName().toString(); - if (!paramType.startsWith("java.lang")) { + if (!paramType.startsWith("java.lang") && !"Array".equals(paramType)) { out.write("import " + paramType + ";\n"); } } diff --git a/src/main/java/org/openrewrite/java/template/processor/TypeAwareProcessor.java b/src/main/java/org/openrewrite/java/template/processor/TypeAwareProcessor.java index f78392df..e35b6f95 100644 --- a/src/main/java/org/openrewrite/java/template/processor/TypeAwareProcessor.java +++ b/src/main/java/org/openrewrite/java/template/processor/TypeAwareProcessor.java @@ -34,7 +34,6 @@ import java.lang.reflect.Proxy; public abstract class TypeAwareProcessor extends AbstractProcessor { - protected ProcessingEnvironment processingEnv; protected JavacProcessingEnvironment javacProcessingEnv; protected Trees trees; diff --git a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java index 710dfc2f..559e2a5c 100644 --- a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java +++ b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java @@ -35,6 +35,7 @@ class RefasterTemplateProcessorTest { @ParameterizedTest @ValueSource(strings = { + "Arrays", "MethodThrows", "NestedPreconditions", "ParameterReuse", diff --git a/src/test/resources/refaster/Arrays.java b/src/test/resources/refaster/Arrays.java new file mode 100644 index 00000000..fb1e3939 --- /dev/null +++ b/src/test/resources/refaster/Arrays.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package foo; + +import com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.BeforeTemplate; + +public class Arrays { + @BeforeTemplate + String before(String[] strings) { + return String.join(", ", strings); + } + + @AfterTemplate + String after(String[] strings) { + return String.join(":", strings); + } +} diff --git a/src/test/resources/refaster/ArraysRecipe.java b/src/test/resources/refaster/ArraysRecipe.java new file mode 100644 index 00000000..0f690e16 --- /dev/null +++ b/src/test/resources/refaster/ArraysRecipe.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package foo; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.lang.NonNullApi; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.search.*; +import org.openrewrite.java.template.Primitive; +import org.openrewrite.java.template.Semantics; +import org.openrewrite.java.template.function.*; +import org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor; +import org.openrewrite.java.tree.*; + +import java.util.*; + +import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.*; + +@SuppressWarnings("all") +@NonNullApi +public class ArraysRecipe extends Recipe { + + public ArraysRecipe() { + } + + @Override + public String getDisplayName() { + return "Refaster template `Arrays`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic class Arrays {\n \n @BeforeTemplate()\n String before(String[] strings) {\n return String.join(\", \", strings);\n }\n \n @AfterTemplate()\n String after(String[] strings) {\n return String.join(\":\", strings);\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate before = Semantics.expression(this, "before", (String[] strings) -> String.join(", ", strings)).build(); + final JavaTemplate after = Semantics.expression(this, "after", (String[] strings) -> String.join(":", strings)).build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = before.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + new UsesMethod<>("java.lang.String join(..)"), + javaVisitor + ); + } +}