From 14b6cd13415ec17a3fa573088dba9df2fdbfb494 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Sat, 30 Sep 2023 08:49:20 -0700 Subject: [PATCH] Split statement and expression templates, move code from rewrite-java --- build.gradle.kts | 5 +- .../openrewrite/java/template/Matcher.java | 23 + .../openrewrite/java/template/Matches.java | 26 + .../openrewrite/java/template/NotMatches.java | 26 + .../openrewrite/java/template/Primitive.java | 26 + .../template/RefasterTemplateProcessor.java | 949 ------------------ .../openrewrite/java/template/Semantics.java | 100 ++ .../java/template/TemplateProcessor.java | 482 --------- .../java/template/function/Expr0.java | 5 + .../java/template/function/Expr1.java | 5 + .../java/template/function/Expr10.java | 5 + .../java/template/function/Expr2.java | 5 + .../java/template/function/Expr3.java | 5 + .../java/template/function/Expr4.java | 5 + .../java/template/function/Expr5.java | 5 + .../java/template/function/Expr6.java | 5 + .../java/template/function/Expr7.java | 5 + .../java/template/function/Expr8.java | 5 + .../java/template/function/Expr9.java | 5 + .../java/template/function/Stat0.java | 5 + .../java/template/function/Stat1.java | 5 + .../java/template/function/Stat10.java | 5 + .../java/template/function/Stat2.java | 5 + .../java/template/function/Stat3.java | 5 + .../java/template/function/Stat4.java | 5 + .../java/template/function/Stat5.java | 5 + .../java/template/function/Stat6.java | 5 + .../java/template/function/Stat7.java | 5 + .../java/template/function/Stat8.java | 5 + .../java/template/function/Stat9.java | 5 + .../internal/AbstractRefasterJavaVisitor.java | 84 ++ .../template/internal/PatternBuilder.java | 26 + .../java/template/package-info.java | 19 + .../javax.annotation.processing.Processor | 4 +- .../RefasterTemplateProcessorTest.java | 4 +- .../java/template/TemplateProcessorTest.java | 2 + .../resources/recipes/MatchingRecipes.java | 25 +- .../recipes/MultipleDereferencesRecipes.java | 28 +- .../recipes/NestedPreconditionsRecipe.java | 26 +- .../recipes/ShouldAddImportsRecipes.java | 48 +- .../ShouldSupportNestedClassesRecipes.java | 27 +- .../recipes/UseStringIsEmptyRecipe.java | 15 +- 42 files changed, 549 insertions(+), 1506 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/template/Matcher.java create mode 100644 src/main/java/org/openrewrite/java/template/Matches.java create mode 100644 src/main/java/org/openrewrite/java/template/NotMatches.java create mode 100644 src/main/java/org/openrewrite/java/template/Primitive.java delete mode 100644 src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java create mode 100644 src/main/java/org/openrewrite/java/template/Semantics.java delete mode 100644 src/main/java/org/openrewrite/java/template/TemplateProcessor.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr0.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr1.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr10.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr2.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr3.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr4.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr5.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr6.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr7.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr8.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Expr9.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat0.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat1.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat10.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat2.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat3.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat4.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat5.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat6.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat7.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat8.java create mode 100644 src/main/java/org/openrewrite/java/template/function/Stat9.java create mode 100644 src/main/java/org/openrewrite/java/template/internal/AbstractRefasterJavaVisitor.java create mode 100644 src/main/java/org/openrewrite/java/template/internal/PatternBuilder.java create mode 100644 src/main/java/org/openrewrite/java/template/package-info.java diff --git a/build.gradle.kts b/build.gradle.kts index 33db5d00..0f42f078 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -77,13 +77,16 @@ dependencies { compileOnly(files(tools)) compileOnly("org.jetbrains:annotations:24.0.+") + compileOnly("org.projectlombok:lombok:latest.release") + annotationProcessor("org.projectlombok:lombok:latest.release") + compileOnly("org.openrewrite:rewrite-java:latest.release") + // Needed for annotation processing tests testImplementation(files(tools)) testImplementation("org.openrewrite:rewrite-java:latest.integration") testImplementation("org.slf4j:slf4j-api:latest.release") testImplementation("com.google.testing.compile:compile-testing:latest.release") - // Needed to run tests testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release") testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release") diff --git a/src/main/java/org/openrewrite/java/template/Matcher.java b/src/main/java/org/openrewrite/java/template/Matcher.java new file mode 100644 index 00000000..66ae66ef --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/Matcher.java @@ -0,0 +1,23 @@ +/* + * 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 org.openrewrite.java.template; + +import org.openrewrite.java.tree.J; + +@FunctionalInterface +public interface Matcher { + boolean matches(T t); +} diff --git a/src/main/java/org/openrewrite/java/template/Matches.java b/src/main/java/org/openrewrite/java/template/Matches.java new file mode 100644 index 00000000..cb37b168 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/Matches.java @@ -0,0 +1,26 @@ +/* + * 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 org.openrewrite.java.template; + +import org.openrewrite.java.tree.Expression; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +public @interface Matches { + Class> value(); +} diff --git a/src/main/java/org/openrewrite/java/template/NotMatches.java b/src/main/java/org/openrewrite/java/template/NotMatches.java new file mode 100644 index 00000000..ab5f9b9c --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/NotMatches.java @@ -0,0 +1,26 @@ +/* + * 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 org.openrewrite.java.template; + +import org.openrewrite.java.tree.Expression; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +public @interface NotMatches { + Class> value(); +} diff --git a/src/main/java/org/openrewrite/java/template/Primitive.java b/src/main/java/org/openrewrite/java/template/Primitive.java new file mode 100644 index 00000000..c8df4f20 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/Primitive.java @@ -0,0 +1,26 @@ +/* + * 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 org.openrewrite.java.template; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.SOURCE) +public @interface Primitive { +} diff --git a/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java b/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java deleted file mode 100644 index f5e4136b..00000000 --- a/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java +++ /dev/null @@ -1,949 +0,0 @@ -/* - * 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 org.openrewrite.java.template; - -import com.sun.source.tree.*; -import com.sun.source.util.TreePath; -import com.sun.source.util.Trees; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; -import com.sun.tools.javac.tree.TreeMaker; -import com.sun.tools.javac.tree.TreeScanner; -import com.sun.tools.javac.util.Context; -import org.jetbrains.annotations.Nullable; -import org.openrewrite.java.template.internal.*; -import org.openrewrite.java.template.internal.permit.Parent; -import sun.misc.Unsafe; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.ProcessingEnvironment; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.NestingKind; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic.Kind; -import javax.tools.JavaFileObject; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.Writer; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.util.Collections.singletonList; -import static org.openrewrite.java.template.RefasterTemplateProcessor.AFTER_TEMPLATE; -import static org.openrewrite.java.template.RefasterTemplateProcessor.BEFORE_TEMPLATE; - -/** - * For steps to debug this annotation processor, see - * this blog post. - */ -@SupportedAnnotationTypes({BEFORE_TEMPLATE, AFTER_TEMPLATE}) -public class RefasterTemplateProcessor extends AbstractProcessor { - static final String BEFORE_TEMPLATE = "com.google.errorprone.refaster.annotation.BeforeTemplate"; - static final String AFTER_TEMPLATE = "com.google.errorprone.refaster.annotation.AfterTemplate"; - static Set UNSUPPORTED_ANNOTATIONS = Stream.of( - "com.google.errorprone.refaster.annotation.AlsoNegation", - "com.google.errorprone.refaster.annotation.AllowCodeBetweenLines", - "com.google.errorprone.refaster.annotation.Matches", - "com.google.errorprone.refaster.annotation.MayOptionallyUse", - "com.google.errorprone.refaster.annotation.NoAutoboxing", - "com.google.errorprone.refaster.annotation.NotMatches", - "com.google.errorprone.refaster.annotation.OfKind", - "com.google.errorprone.refaster.annotation.Placeholder", - "com.google.errorprone.refaster.annotation.Repeated", - "com.google.errorprone.refaster.annotation.UseImportPolicy", - "com.google.errorprone.annotations.DoNotCall" - ).collect(Collectors.toSet()); - static final String PRIMITIVE_ANNOTATION = "@Primitive"; - static final Map PRIMITIVE_TYPE_MAP = new HashMap<>(); - - static { - PRIMITIVE_TYPE_MAP.put(boolean.class.getName(), Boolean.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(byte.class.getName(), Byte.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(char.class.getName(), Character.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(short.class.getName(), Short.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(int.class.getName(), Integer.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(long.class.getName(), Long.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(float.class.getName(), Float.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(double.class.getName(), Double.class.getSimpleName()); - PRIMITIVE_TYPE_MAP.put(void.class.getName(), Void.class.getSimpleName()); - } - - static Set DO_AFTER_VISIT = Stream.of( - "new org.openrewrite.java.cleanup.UnnecessaryParenthesesVisitor()", - "new org.openrewrite.java.ShortenFullyQualifiedTypeReferences().getVisitor()" - ).collect(Collectors.toCollection(LinkedHashSet::new)); - - static Set INLINE_VISIT = Stream.of( - "new org.openrewrite.java.cleanup.SimplifyBooleanExpressionVisitor()" - ).collect(Collectors.toCollection(LinkedHashSet::new)); - - static ClassValue> LST_TYPE_MAP = new ClassValue>() { - @Override - protected List computeValue(Class type) { - if (JCTree.JCUnary.class.isAssignableFrom(type)) { - return singletonList("J.Unary"); - } else if (JCTree.JCBinary.class.isAssignableFrom(type)) { - return singletonList("J.Binary"); - } else if (JCTree.JCMethodInvocation.class.isAssignableFrom(type)) { - return singletonList("J.MethodInvocation"); - } else if (JCTree.JCFieldAccess.class.isAssignableFrom(type)) { - return Arrays.asList("J.FieldAccess", "J.Identifier"); - } else if (JCTree.JCExpression.class.isAssignableFrom(type)) { - // catch all for expressions - return singletonList("Expression"); - } else if (JCTree.JCStatement.class.isAssignableFrom(type)) { - // catch all for statements - return singletonList("Statement"); - } - throw new IllegalArgumentException(type.toString()); - } - }; - - private ProcessingEnvironment processingEnv; - private JavacProcessingEnvironment javacProcessingEnv; - private Trees trees; - - /** - * We just return the latest version of whatever JDK we run on. Stupid? Yeah, but it's either that - * or warnings on all versions but 1. - */ - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - @Override - public synchronized void init(ProcessingEnvironment processingEnv) { - super.init(processingEnv); - this.processingEnv = processingEnv; - this.javacProcessingEnv = getJavacProcessingEnvironment(processingEnv); - if (javacProcessingEnv == null) { - return; - } - trees = Trees.instance(javacProcessingEnv); - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - for (Element element : roundEnv.getRootElements()) { - JCCompilationUnit jcCompilationUnit = toUnit(element); - if (jcCompilationUnit != null) { - maybeGenerateTemplateSources(jcCompilationUnit); - } - } - - return true; - } - - void maybeGenerateTemplateSources(JCCompilationUnit cu) { - Context context = javacProcessingEnv.getContext(); - - new TreeScanner() { - final Map> imports = new HashMap<>(); - final Map> staticImports = new HashMap<>(); - final Map recipes = new LinkedHashMap<>(); - - @Override - public void visitClassDef(JCTree.JCClassDecl classDecl) { - super.visitClassDef(classDecl); - - TemplateDescriptor descriptor = getTemplateDescriptor(classDecl, context, cu); - if (descriptor != null) { - - TreeMaker treeMaker = TreeMaker.instance(context).forToplevel(cu); - List membersWithoutConstructor = classDecl.getMembers().stream() - .filter(m -> !(m instanceof JCTree.JCMethodDecl) || !((JCTree.JCMethodDecl) m).name.contentEquals("")) - .collect(Collectors.toList()); - JCTree.JCClassDecl copy = treeMaker.ClassDef(classDecl.mods, classDecl.name, classDecl.typarams, classDecl.extending, classDecl.implementing, com.sun.tools.javac.util.List.from(membersWithoutConstructor)); - - processingEnv.getMessager().printMessage(Kind.NOTE, "Generating template for " + descriptor.classDecl.getSimpleName()); - - String templateName = classDecl.sym.fullname.toString().substring(classDecl.sym.packge().fullname.length() + 1); - String templateFqn = classDecl.sym.fullname.toString() + "Recipe"; - String templateCode = copy.toString().trim(); - String displayName = cu.docComments.getComment(classDecl) != null ? cu.docComments.getComment(classDecl).getText().trim() : "Refaster template `" + templateName + '`'; - if (displayName.endsWith(".")) { - displayName = displayName.substring(0, displayName.length() - 1); - } - - for (JCTree.JCMethodDecl template : descriptor.beforeTemplates) { - for (Symbol anImport : ImportDetector.imports(template)) { - if (anImport instanceof Symbol.ClassSymbol) { - imports.computeIfAbsent(template, k -> new TreeSet<>()) - .add(anImport.getQualifiedName().toString().replace('$', '.')); - } else if (anImport instanceof Symbol.VarSymbol || anImport instanceof Symbol.MethodSymbol) { - staticImports.computeIfAbsent(template, k -> new TreeSet<>()) - .add(anImport.owner.getQualifiedName().toString().replace('$', '.') + '.' + anImport.flatName().toString()); - } else { - throw new AssertionError(anImport.getClass()); - } - } - } - for (Symbol anImport : ImportDetector.imports(descriptor.afterTemplate)) { - if (anImport instanceof Symbol.ClassSymbol) { - imports.computeIfAbsent(descriptor.afterTemplate, k -> new TreeSet<>()) - .add(anImport.getQualifiedName().toString().replace('$', '.')); - } else if (anImport instanceof Symbol.VarSymbol || anImport instanceof Symbol.MethodSymbol) { - staticImports.computeIfAbsent(descriptor.afterTemplate, k -> new TreeSet<>()) - .add(anImport.owner.getQualifiedName().toString().replace('$', '.') + '.' + anImport.flatName().toString()); - } else { - throw new AssertionError(anImport.getClass()); - } - } - - for (Set imports : imports.values()) { - imports.removeIf(i -> "java.lang".equals(i.substring(0, i.lastIndexOf('.')))); - imports.remove(BEFORE_TEMPLATE); - imports.remove(AFTER_TEMPLATE); - } - - Map befores = new LinkedHashMap<>(); - for (JCTree.JCMethodDecl templ : descriptor.beforeTemplates) { - String name = templ.getName().toString(); - if (befores.containsKey(name)) { - String base = name; - for (int i = 0; ; i++) { - name = base + i; - if (!befores.containsKey(name)) { - break; - } - } - } - befores.put(name, templ); - } - String after = descriptor.afterTemplate.getName().toString(); - - StringBuilder recipe = new StringBuilder(); - String recipeName = templateFqn.substring(templateFqn.lastIndexOf('.') + 1); - String modifiers = classDecl.getModifiers().getFlags().stream().map(Modifier::toString).collect(Collectors.joining(" ")); - recipe.append(modifiers + " class " + recipeName + " extends Recipe {\n"); - recipe.append("\n"); - recipe.append(" @Override\n"); - recipe.append(" public String getDisplayName() {\n"); - recipe.append(" return \"" + escape(displayName) + "\";\n"); - recipe.append(" }\n"); - recipe.append("\n"); - recipe.append(" @Override\n"); - recipe.append(" public String getDescription() {\n"); - recipe.append(" return \"Recipe created for the following Refaster template:\\n```java\\n" + escape(templateCode) + "\\n```\\n.\";\n"); - recipe.append(" }\n"); - recipe.append("\n"); - recipe.append(" @Override\n"); - recipe.append(" public TreeVisitor getVisitor() {\n"); - recipe.append(" JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() {\n"); - recipe.append("\n"); - for (Map.Entry entry : befores.entrySet()) { - recipe.append(" Supplier " + entry.getKey() + " = memoize(() -> JavaTemplate.compile(this, \"" + entry.getKey() + "\", " - + toLambda(entry.getValue()) + ").build());\n"); - recipe.append("\n"); - } - recipe.append(" Supplier " + after + " = memoize(() -> JavaTemplate.compile(this, \"" + after + "\", " - + toLambda(descriptor.afterTemplate) + ").build());\n"); - recipe.append("\n"); - - List lstTypes = LST_TYPE_MAP.get(getType(descriptor.beforeTemplates.get(0))); - String parameters = parameters(descriptor); - for (String lstType : lstTypes) { - String methodSuffix = lstType.startsWith("J.") ? lstType.substring(2) : lstType; - recipe.append(" @Override\n"); - recipe.append(" public J visit" + methodSuffix + "(" + lstType + " elem, ExecutionContext ctx) {\n"); - if (lstType.equals("Statement")) { - recipe.append(" if (elem instanceof J.Block) {;\n"); - recipe.append(" // FIXME workaround\n"); - recipe.append(" return elem;\n"); - recipe.append(" }\n"); - } - - recipe.append(" JavaTemplate.Matcher matcher;\n"); - for (Map.Entry entry : befores.entrySet()) { - recipe.append(" if (" + "(matcher = matcher(" + entry.getKey() + ", getCursor())).find()" + ") {\n"); - com.sun.tools.javac.util.List jcVariableDecls = entry.getValue().getParameters(); - for (int i = 0; i < jcVariableDecls.size(); i++) { - JCTree.JCVariableDecl param = jcVariableDecls.get(i); - com.sun.tools.javac.util.List annotations = param.getModifiers().getAnnotations(); - for (int j = 0; j < annotations.size(); j++) { - JCTree.JCAnnotation jcAnnotation = annotations.get(j); - String annotationType = jcAnnotation.attribute.type.tsym.getQualifiedName().toString(); - if (annotationType.equals("org.openrewrite.java.template.NotMatches")) { - String matcher = ((Type.ClassType) jcAnnotation.attribute.getValue().values.get(0).snd.getValue()).tsym.getQualifiedName().toString(); - recipe.append(" if (new " + matcher + "().matches((Expression) matcher.parameter(" + i + "))) {\n"); - recipe.append(" return super.visit" + methodSuffix + "(elem, ctx);\n"); - recipe.append(" }\n"); - } else if (annotationType.equals("org.openrewrite.java.template.Matches")) { - String matcher = ((Type.ClassType) jcAnnotation.attribute.getValue().values.get(0).snd.getValue()).tsym.getQualifiedName().toString(); - recipe.append(" if (!new " + matcher + "().matches((Expression) matcher.parameter(" + i + "))) {\n"); - recipe.append(" return super.visit" + methodSuffix + "(elem, ctx);\n"); - recipe.append(" }\n"); - } - } - } - Set beforeImports = imports.entrySet().stream() - .filter(e -> entry.getValue().equals(e.getKey())) - .map(Map.Entry::getValue) - .flatMap(Set::stream) - .collect(Collectors.toSet()); - Set afterImports = imports.entrySet().stream() - .filter(e -> descriptor.afterTemplate == e.getKey()) - .map(Map.Entry::getValue) - .flatMap(Set::stream) - .collect(Collectors.toSet()); - for (String import_ : imports.values().stream().flatMap(Set::stream).collect(Collectors.toSet())) { - if (import_.startsWith("java.lang.")) { - continue; - } - if (beforeImports.contains(import_) && afterImports.contains(import_)) { - } else if (beforeImports.contains(import_)) { - recipe.append(" maybeRemoveImport(\"" + import_ + "\");\n"); - } - } - beforeImports = staticImports.entrySet().stream() - .filter(e -> entry.getValue().equals(e.getKey())) - .map(Map.Entry::getValue) - .flatMap(Set::stream) - .collect(Collectors.toSet()); - afterImports = staticImports.entrySet().stream() - .filter(e -> descriptor.afterTemplate == e.getKey()) - .map(Map.Entry::getValue) - .flatMap(Set::stream) - .collect(Collectors.toSet()); - for (String import_ : staticImports.values().stream().flatMap(Set::stream).collect(Collectors.toSet())) { - int dot = import_.lastIndexOf('.'); - if (import_.startsWith("java.lang.")) { - continue; - } - if (beforeImports.contains(import_) && afterImports.contains(import_)) { - } else if (beforeImports.contains(import_)) { - recipe.append(" maybeRemoveImport(\"" + import_ + "\");\n"); - } - } - if (parameters.isEmpty()) { - recipe.append(" return embed(apply(" + after + ", getCursor(), elem.getCoordinates().replace()), getCursor(), ctx);\n"); - } else { - recipe.append(" return embed(\n"); - recipe.append(" apply(" + after + ", getCursor(), elem.getCoordinates().replace(), " + parameters + "),\n"); - recipe.append(" getCursor(),\n"); - recipe.append(" ctx\n"); - recipe.append(" );\n"); - } - recipe.append(" }\n"); - } - recipe.append(" return super.visit" + methodSuffix + "(elem, ctx);\n"); - recipe.append(" }\n"); - recipe.append("\n"); - } - recipe.append(" };\n"); - - String preconditions = generatePreconditions(descriptor.beforeTemplates, imports, 16); - if (preconditions == null) { - recipe.append(" return javaVisitor;\n"); - } else { - recipe.append(" return Preconditions.check(\n"); - recipe.append(" " + preconditions + ",\n"); - recipe.append(" javaVisitor\n"); - recipe.append(" );\n"); - } - recipe.append(" }\n"); - recipe.append("}\n"); - recipes.put(recipeName, recipe.toString()); - } - - if (classDecl.sym != null && classDecl.sym.getNestingKind() == NestingKind.TOP_LEVEL && !recipes.isEmpty()) { - boolean outerClassRequired = descriptor == null; - try { - String inputOuterFQN = outerClassRequired ? classDecl.sym.fullname.toString() : descriptor.classDecl.sym.fullname.toString(); - String className = inputOuterFQN + (outerClassRequired ? "Recipes" : "Recipe"); - JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(className); - try (Writer out = new BufferedWriter(builderFile.openWriter())) { - out.write("package " + classDecl.sym.packge().toString() + ";\n"); - out.write("\n"); - out.write("import org.openrewrite.ExecutionContext;\n"); - out.write("import org.openrewrite.Preconditions;\n"); - out.write("import org.openrewrite.Recipe;\n"); - out.write("import org.openrewrite.TreeVisitor;\n"); - out.write("import org.openrewrite.java.JavaTemplate;\n"); - out.write("import org.openrewrite.java.JavaVisitor;\n"); - out.write("import org.openrewrite.java.internal.template.AbstractRefasterJavaVisitor;\n"); - out.write("import org.openrewrite.java.search.*;\n"); - out.write("import org.openrewrite.java.template.Primitive;\n"); - out.write("import org.openrewrite.java.tree.*;\n"); - out.write("\n"); - out.write("import java.util.function.Supplier;\n"); - if (outerClassRequired) { - out.write("\n"); - out.write("import java.util.Arrays;\n"); - out.write("import java.util.List;\n"); - } - - out.write("\n"); - - if (!imports.isEmpty()) { - for (String anImport : imports.values().stream().flatMap(Set::stream).collect(Collectors.toSet())) { - out.write("import " + anImport + ";\n"); - } - out.write("\n"); - } - if (!staticImports.isEmpty()) { - for (String anImport : staticImports.values().stream().flatMap(Set::stream).collect(Collectors.toSet())) { - out.write("import static " + anImport + ";\n"); - } - out.write("\n"); - } - - if (outerClassRequired) { - String outerClassName = className.substring(className.lastIndexOf('.') + 1); - out.write("public final class " + outerClassName + " extends Recipe {\n"); - - String simpleInputOuterFQN = inputOuterFQN.substring(inputOuterFQN.lastIndexOf('.') + 1); - out.write("\n" + - " @Override\n" + - " public String getDisplayName() {\n" + - " return \"`" + simpleInputOuterFQN + "` Refaster recipes\";\n" + - " }\n" + - "\n" + - " @Override\n" + - " public String getDescription() {\n" + - " return \"Refaster template recipes for `" + inputOuterFQN + "`.\";\n" + - " }\n" + - "\n\n"); - String recipesAsList = recipes.keySet().stream() - .map(r -> " new " + r.substring(r.lastIndexOf('.') + 1, r.length()) + "()") - .collect(Collectors.joining(",\n")); - out.write( - " @Override\n" + - " public List getRecipeList() {\n" + - " return Arrays.asList(\n" + - recipesAsList + '\n' + - " );\n" + - " }\n"); - - - for (String r : recipes.values()) { - out.write(r.replaceAll("(?m)^(.+)$", " $1")); - out.write('\n'); - } - out.write("}\n"); - } else { - for (String r : recipes.values()) { - out.write(r); - out.write('\n'); - } - } - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - /** - * Generate the minimal precondition that would allow to match each before template individually. - * - * @param beforeTemplates - * @param imports - * @param i - * @return the precondition or null if no precondition is required - */ - private String generatePreconditions( - List beforeTemplates, - Map> imports, int indent) { - Map> preconditions = new LinkedHashMap<>(); - for (JCTree.JCMethodDecl beforeTemplate : beforeTemplates) { - Set usesVisitors = new LinkedHashSet<>(); - - Set localImports = imports.getOrDefault(beforeTemplate, Collections.emptySet()); - for (String import_ : localImports) { - usesVisitors.add("new UsesType<>(\"" + import_ + "\", true)"); - } - List usedMethods = UsedMethodDetector.usedMethods(beforeTemplate); - for (Symbol.MethodSymbol method : usedMethods) { - usesVisitors.add("new UsesMethod<>(\"" + method.owner.getQualifiedName().toString() + ' ' + method.name.toString() + "(..)\")"); - } - - preconditions.put(beforeTemplate, usesVisitors); - } - - if (preconditions.size() == 1) { - return joinPreconditions(preconditions.values().iterator().next(), "and", indent + 4); - } else if (preconditions.size() > 1) { - Set common = new LinkedHashSet<>(); - for (String dep : preconditions.values().iterator().next()) { - if (preconditions.values().stream().allMatch(v -> v.contains(dep))) { - common.add(dep); - } - } - common.forEach(dep -> preconditions.values().forEach(v -> v.remove(dep))); - preconditions.values().removeIf(Collection::isEmpty); - - if (common.isEmpty()) { - return joinPreconditions(preconditions.values().stream().map(v -> joinPreconditions(v, "and", indent + 4)).collect(Collectors.toList()), "or", indent + 4); - } else { - if (!preconditions.isEmpty()) { - String uniqueConditions = joinPreconditions(preconditions.values().stream().map(v -> joinPreconditions(v, "and", indent + 12)).collect(Collectors.toList()), "or", indent + 8); - common.add(uniqueConditions); - } - return joinPreconditions(common, "and", indent + 4); - } - } - return null; - } - - private String joinPreconditions(Collection preconditions, String op, int indent) { - if (preconditions.isEmpty()) { - return null; - } else if (preconditions.size() == 1) { - return preconditions.iterator().next(); - } - char[] indentChars = new char[indent]; - Arrays.fill(indentChars, ' '); - String indentStr = new String(indentChars); - return "Preconditions." + op + "(\n" + indentStr + String.join(",\n" + indentStr, preconditions) + "\n" + indentStr.substring(0, indent - 4) + ')'; - } - }.scan(cu); - } - - private static String lambdaCastType(Class type, JCTree.JCMethodDecl method) { - if (type == JCTree.JCMethodInvocation.class - && method.getBody().getStatements().last() instanceof JCTree.JCExpressionStatement - && !(method.getReturnType().type instanceof Type.JCVoidType)) { - return ""; - } - int paramCount = method.params.size(); - boolean asFunction = !(method.restype.type instanceof Type.JCVoidType) && JCTree.JCExpression.class.isAssignableFrom(type); - StringJoiner joiner = new StringJoiner(", ", "<", ">").setEmptyValue(""); - for (int i = 0; i < (asFunction ? paramCount + 1 : paramCount); i++) { - joiner.add("?"); - } - return "(JavaTemplate." + (asFunction ? 'F' : 'P') + paramCount + joiner + ") "; - } - - private String escape(String string) { - return string.replace("\"", "\\\"").replaceAll("\\R", "\\\\n"); - } - - private String parameters(TemplateDescriptor descriptor) { - List afterParams = new ArrayList<>(); - new TreeScanner() { - @Override - public void scan(JCTree jcTree) { - if (jcTree instanceof JCTree.JCIdent) { - JCTree.JCIdent jcIdent = (JCTree.JCIdent) jcTree; - if (jcIdent.sym instanceof Symbol.VarSymbol - && jcIdent.sym.owner instanceof Symbol.MethodSymbol - && ((Symbol.MethodSymbol) jcIdent.sym.owner).params.contains(jcIdent.sym)) { - afterParams.add(((Symbol.MethodSymbol) jcIdent.sym.owner).params.indexOf(jcIdent.sym)); - } - } - super.scan(jcTree); - } - }.scan(descriptor.afterTemplate.body); - - StringJoiner joiner = new StringJoiner(", "); - for (Integer param : afterParams) { - joiner.add("matcher.parameter(" + param + ")"); - } - return joiner.toString(); - } - - private Class getType(JCTree.JCMethodDecl method) { - JCTree.JCStatement statement = method.getBody().getStatements().get(0); - Class type = statement.getClass(); - if (statement instanceof JCTree.JCReturn) { - type = ((JCTree.JCReturn) statement).expr.getClass(); - } else if (statement instanceof JCTree.JCExpressionStatement) { - type = ((JCTree.JCExpressionStatement) statement).expr.getClass(); - } - return type; - } - - private String toLambda(JCTree.JCMethodDecl method) { - StringBuilder builder = new StringBuilder(); - - // for now excluding assignment expressions and prefix and postfix -- and ++ - Set> expressionStatementTypes = Stream.of( - JCTree.JCMethodInvocation.class, - JCTree.JCNewClass.class).collect(Collectors.toSet()); - - Class type = getType(method); - if (expressionStatementTypes.contains(type)) { - builder.append(lambdaCastType(type, method)); - } - - StringJoiner joiner = new StringJoiner(", ", "(", ")"); - for (JCTree.JCVariableDecl parameter : method.getParameters()) { - String paramType = parameter.getType().type.tsym.getQualifiedName().toString(); - if (PRIMITIVE_TYPE_MAP.containsKey(paramType)) { - paramType = PRIMITIVE_ANNOTATION + ' ' + PRIMITIVE_TYPE_MAP.get(paramType); - } else if (paramType.startsWith("java.lang.")) { - paramType = paramType.substring("java.lang.".length()); - } - joiner.add(paramType + " " + parameter.getName()); - } - builder.append(joiner); - builder.append(" -> "); - - JCTree.JCStatement statement = method.getBody().getStatements().get(0); - if (statement instanceof JCTree.JCReturn) { - builder.append(FQNPretty.toString(((JCTree.JCReturn) statement).getExpression())); - } else if (statement instanceof JCTree.JCThrow) { - String string = FQNPretty.toString(statement); - builder.append("{ ").append(string).append(" }"); - } else { - String string = FQNPretty.toString(statement); - builder.append(string); - } - return builder.toString(); - } - - @Nullable - private TemplateDescriptor getTemplateDescriptor(JCTree.JCClassDecl tree, Context context, JCCompilationUnit cu) { - TemplateDescriptor result = new TemplateDescriptor(tree); - for (JCTree member : tree.getMembers()) { - if (member instanceof JCTree.JCMethodDecl) { - JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) member; - List annotations = getTemplateAnnotations(method, BEFORE_TEMPLATE::equals); - if (!annotations.isEmpty()) { - result.beforeTemplate(method); - } - annotations = getTemplateAnnotations(method, AFTER_TEMPLATE::equals); - if (!annotations.isEmpty()) { - result.afterTemplate(method); - } - } - } - return result.validate(context, cu); - } - - class TemplateDescriptor { - final JCTree.JCClassDecl classDecl; - final List beforeTemplates = new ArrayList<>(); - JCTree.JCMethodDecl afterTemplate; - - public TemplateDescriptor(JCTree.JCClassDecl classDecl) { - this.classDecl = classDecl; - } - - @Nullable - private TemplateDescriptor validate(Context context, JCCompilationUnit cu) { - if (beforeTemplates.isEmpty() || afterTemplate == null) { - 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)) { - processingEnv.getMessager().printMessage(Kind.NOTE, "The @" + annotation.annotationType + " is currently not supported", ((JCTree.JCMethodDecl) member).sym); - valid = false; - } - } - } - - // resolve so that we can inspect the template body - valid &= resolve(context, cu); - if (valid) { - for (JCTree.JCMethodDecl template : beforeTemplates) { - valid &= validateTemplateMethod(template); - } - valid &= validateTemplateMethod(afterTemplate); - } - return valid ? this : null; - } - - private boolean validateTemplateMethod(JCTree.JCMethodDecl template) { - boolean valid = true; - // TODO: support all Refaster method-level annotations - for (JCTree.JCAnnotation annotation : getTemplateAnnotations(template, UNSUPPORTED_ANNOTATIONS::contains)) { - processingEnv.getMessager().printMessage(Kind.NOTE, "The @" + annotation.annotationType + " is currently not supported", template.sym); - valid = false; - } - // TODO: support all Refaster parameter-level annotations - for (JCTree.JCVariableDecl parameter : template.getParameters()) { - for (JCTree.JCAnnotation annotation : getTemplateAnnotations(parameter, UNSUPPORTED_ANNOTATIONS::contains)) { - processingEnv.getMessager().printMessage(Kind.NOTE, "The @" + annotation.annotationType + " annotation is currently not supported", template.sym); - valid = false; - } - if (parameter.vartype instanceof ParameterizedTypeTree || parameter.vartype.type instanceof Type.TypeVar) { - processingEnv.getMessager().printMessage(Kind.NOTE, "Generics are currently not supported", template.sym); - valid = false; - } - } - if (template.restype instanceof ParameterizedTypeTree || template.restype.type instanceof Type.TypeVar) { - processingEnv.getMessager().printMessage(Kind.NOTE, "Generics are currently not supported", template.sym); - valid = false; - } - valid &= new TreeScanner() { - boolean valid = true; - - boolean validate(JCTree tree) { - scan(tree); - return valid; - } - - @Override - public void visitIdent(JCTree.JCIdent jcIdent) { - if (jcIdent.sym != null - && jcIdent.sym.packge().getQualifiedName().contentEquals("com.google.errorprone.refaster")) { - processingEnv.getMessager().printMessage(Kind.NOTE, jcIdent.type.tsym.getQualifiedName() + " is not supported", template.sym); - valid = false; - } - } - }.validate(template.getBody()); - return valid; - } - - public void beforeTemplate(JCTree.JCMethodDecl method) { - beforeTemplates.add(method); - } - - public void afterTemplate(JCTree.JCMethodDecl method) { - afterTemplate = method; - } - - private boolean resolve(Context context, JCCompilationUnit cu) { - try { - JavacResolution res = new JavacResolution(context); - beforeTemplates.replaceAll(key -> { - Map resolved = res.resolveAll(context, cu, singletonList(key)); - return (JCTree.JCMethodDecl) resolved.get(key); - }); - Map resolved = res.resolveAll(context, cu, singletonList(afterTemplate)); - afterTemplate = (JCTree.JCMethodDecl) resolved.get(afterTemplate); - } catch (Throwable t) { - processingEnv.getMessager().printMessage(Kind.WARNING, "Had trouble type attributing the template."); - return false; - } - return true; - } - - } - - private static List getTemplateAnnotations(MethodTree method, Predicate typePredicate) { - List result = new ArrayList<>(); - for (AnnotationTree annotation : method.getModifiers().getAnnotations()) { - Tree type = annotation.getAnnotationType(); - if (type.getKind() == Tree.Kind.IDENTIFIER && ((JCTree.JCIdent) type).sym != null - && typePredicate.test(((JCTree.JCIdent) type).sym.getQualifiedName().toString())) { - result.add((JCTree.JCAnnotation) annotation); - } else if (type.getKind() == Tree.Kind.IDENTIFIER && ((JCTree.JCAnnotation) annotation).attribute != null - && ((JCTree.JCAnnotation) annotation).attribute.type instanceof Type.ClassType - && ((JCTree.JCAnnotation) annotation).attribute.type.tsym != null - && typePredicate.test(((JCTree.JCAnnotation) annotation).attribute.type.tsym.getQualifiedName().toString())) { - result.add((JCTree.JCAnnotation) annotation); - } else if (type.getKind() == Tree.Kind.MEMBER_SELECT && type instanceof JCTree.JCFieldAccess - && ((JCTree.JCFieldAccess) type).sym != null - && typePredicate.test(((JCTree.JCFieldAccess) type).sym.getQualifiedName().toString())) { - result.add((JCTree.JCAnnotation) annotation); - } - } - return result; - } - - private static List getTemplateAnnotations(VariableTree parameter, Predicate typePredicate) { - List result = new ArrayList<>(); - for (AnnotationTree annotation : parameter.getModifiers().getAnnotations()) { - Tree type = annotation.getAnnotationType(); - if (type.getKind() == Tree.Kind.IDENTIFIER - && ((JCTree.JCIdent) type).sym != null - && typePredicate.test(((JCTree.JCIdent) type).sym.getQualifiedName().toString())) { - result.add((JCTree.JCAnnotation) annotation); - } else if (type.getKind() == Tree.Kind.MEMBER_SELECT && type instanceof JCTree.JCFieldAccess - && ((JCTree.JCFieldAccess) type).sym != null - && typePredicate.test(((JCTree.JCFieldAccess) type).sym.getQualifiedName().toString())) { - result.add((JCTree.JCAnnotation) annotation); - } - } - return result; - } - - private JCCompilationUnit toUnit(Element element) { - TreePath path = null; - if (trees != null) { - try { - path = trees.getPath(element); - } catch (NullPointerException ignore) { - // Happens if a package-info.java doesn't contain a package declaration. - // We can safely ignore those, since they do not need any processing - } - } - if (path == null) { - return null; - } - - return (JCCompilationUnit) path.getCompilationUnit(); - } - - /** - * This class casts the given processing environment to a JavacProcessingEnvironment. In case of - * gradle incremental compilation, the delegate ProcessingEnvironment of the gradle wrapper is returned. - */ - public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) { - addOpens(); - if (procEnv instanceof JavacProcessingEnvironment) { - return (JavacProcessingEnvironment) procEnv; - } - - // try to find a "delegate" field in the object, and use this to try to obtain a JavacProcessingEnvironment - for (Class procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { - Object delegate = tryGetDelegateField(procEnvClass, procEnv); - if (delegate == null) { - delegate = tryGetProxyDelegateToField(procEnv); - } - if (delegate == null) { - delegate = tryGetProcessingEnvField(procEnvClass, procEnv); - } - - if (delegate != null) { - return getJavacProcessingEnvironment(delegate); - } - // delegate field was not found, try on superclass - } - - processingEnv.getMessager().printMessage(Kind.WARNING, "Can't get the delegate of the gradle " + - "IncrementalProcessingEnvironment. " + - "OpenRewrite's template processor won't work."); - return null; - } - - @SuppressWarnings({"DataFlowIssue", "JavaReflectionInvocation"}) - private static void addOpens() { - Class cModule; - try { - cModule = Class.forName("java.lang.Module"); - } catch (ClassNotFoundException e) { - return; //jdk8-; this is not needed. - } - - Unsafe unsafe = getUnsafe(); - Object jdkCompilerModule = getJdkCompilerModule(); - Object ownModule = getOwnModule(); - String[] allPkgs = { - "com.sun.tools.javac.code", - "com.sun.tools.javac.comp", - "com.sun.tools.javac.file", - "com.sun.tools.javac.main", - "com.sun.tools.javac.model", - "com.sun.tools.javac.parser", - "com.sun.tools.javac.processing", - "com.sun.tools.javac.tree", - "com.sun.tools.javac.util", - "com.sun.tools.javac.jvm", - }; - - try { - Method m = cModule.getDeclaredMethod("implAddOpens", String.class, cModule); - long firstFieldOffset = getFirstFieldOffset(unsafe); - unsafe.putBooleanVolatile(m, firstFieldOffset, true); - for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule); - } catch (Exception ignore) { - } - } - - private static long getFirstFieldOffset(Unsafe unsafe) { - try { - return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first")); - } catch (NoSuchFieldException e) { - // can't happen. - throw new RuntimeException(e); - } catch (SecurityException e) { - // can't happen - throw new RuntimeException(e); - } - } - - private static Unsafe getUnsafe() { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(null); - } catch (Exception e) { - return null; - } - } - - private static Object getOwnModule() { - try { - Method m = Permit.getMethod(Class.class, "getModule"); - return m.invoke(RefasterTemplateProcessor.class); - } catch (Exception e) { - return null; - } - } - - private static Object getJdkCompilerModule() { - // call public api: ModuleLayer.boot().findModule("jdk.compiler").get(); - // but use reflection because we don't want this code to crash on jdk1.7 and below. - // In that case, none of this stuff was needed in the first place, so we just exit via - // the catch block and do nothing. - try { - Class cModuleLayer = Class.forName("java.lang.ModuleLayer"); - Method mBoot = cModuleLayer.getDeclaredMethod("boot"); - Object bootLayer = mBoot.invoke(null); - Class cOptional = Class.forName("java.util.Optional"); - Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class); - Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler"); - return cOptional.getDeclaredMethod("get").invoke(oCompilerO); - } catch (Exception e) { - return null; - } - } - - /** - * Gradle incremental processing - */ - private Object tryGetDelegateField(Class delegateClass, Object instance) { - try { - return Permit.getField(delegateClass, "delegate").get(instance); - } catch (Exception e) { - return null; - } - } - - /** - * Kotlin incremental processing - */ - private Object tryGetProcessingEnvField(Class delegateClass, Object instance) { - try { - return Permit.getField(delegateClass, "processingEnv").get(instance); - } catch (Exception e) { - return null; - } - } - - /** - * IntelliJ >= 2020.3 - */ - private Object tryGetProxyDelegateToField(Object instance) { - try { - InvocationHandler handler = Proxy.getInvocationHandler(instance); - return Permit.getField(handler.getClass(), "val$delegateTo").get(handler); - } catch (Exception e) { - return null; - } - } -} diff --git a/src/main/java/org/openrewrite/java/template/Semantics.java b/src/main/java/org/openrewrite/java/template/Semantics.java new file mode 100644 index 00000000..b2639569 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/Semantics.java @@ -0,0 +1,100 @@ +package org.openrewrite.java.template; + +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.template.function.*; +import org.openrewrite.java.template.internal.PatternBuilder; + +@SuppressWarnings("unused") +public class Semantics { + private Semantics() { + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr0 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr1 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr2 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr3 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr4 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr5 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr6 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr7 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr8 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr9 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder expression(JavaVisitor owner, String name, Expr10 f) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat0 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat1 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat2 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat3 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat4 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat5 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat6 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat7 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat8 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat9 p) { + return new PatternBuilder(name).build(owner); + } + + public static JavaTemplate.Builder statement(JavaVisitor owner, String name, Stat10 p) { + return new PatternBuilder(name).build(owner); + } +} diff --git a/src/main/java/org/openrewrite/java/template/TemplateProcessor.java b/src/main/java/org/openrewrite/java/template/TemplateProcessor.java deleted file mode 100644 index e91975f7..00000000 --- a/src/main/java/org/openrewrite/java/template/TemplateProcessor.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright 2022 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 org.openrewrite.java.template; - -import com.sun.source.tree.Tree; -import com.sun.source.tree.VariableTree; -import com.sun.source.util.TreePath; -import com.sun.source.util.TreePathScanner; -import com.sun.source.util.Trees; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; -import com.sun.tools.javac.tree.TreeScanner; -import com.sun.tools.javac.util.Context; -import org.openrewrite.java.template.internal.ClasspathJarNameDetector; -import org.openrewrite.java.template.internal.ImportDetector; -import org.openrewrite.java.template.internal.JavacResolution; -import org.openrewrite.java.template.internal.Permit; -import org.openrewrite.java.template.internal.permit.Parent; -import sun.misc.Unsafe; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.ProcessingEnvironment; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic.Kind; -import javax.tools.JavaFileObject; -import java.io.*; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.Collections.*; - -/** - * For steps to debug this annotation processor, see - * this blog post. - */ -@SupportedAnnotationTypes("*") -public class TemplateProcessor extends AbstractProcessor { - private static final String PRIMITIVE_ANNOTATION = "org.openrewrite.java.template.Primitive"; - private static final Map PRIMITIVE_TYPE_MAP = new HashMap<>(); - - static { - PRIMITIVE_TYPE_MAP.put(Boolean.class.getName(), boolean.class.getName()); - PRIMITIVE_TYPE_MAP.put(Byte.class.getName(), byte.class.getName()); - PRIMITIVE_TYPE_MAP.put(Character.class.getName(), char.class.getName()); - PRIMITIVE_TYPE_MAP.put(Short.class.getName(), short.class.getName()); - PRIMITIVE_TYPE_MAP.put(Integer.class.getName(), int.class.getName()); - PRIMITIVE_TYPE_MAP.put(Long.class.getName(), long.class.getName()); - PRIMITIVE_TYPE_MAP.put(Float.class.getName(), float.class.getName()); - PRIMITIVE_TYPE_MAP.put(Double.class.getName(), double.class.getName()); - PRIMITIVE_TYPE_MAP.put(Void.class.getName(), void.class.getName()); - } - - private ProcessingEnvironment processingEnv; - private JavacProcessingEnvironment javacProcessingEnv; - private Trees trees; - - private final String javaFileContent; - - public TemplateProcessor(String javaFileContent) { - this.javaFileContent = javaFileContent; - } - - public TemplateProcessor() { - this(null); - } - - /** - * We just return the latest version of whatever JDK we run on. Stupid? Yeah, but it's either that - * or warnings on all versions but 1. - */ - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - @Override - public synchronized void init(ProcessingEnvironment processingEnv) { - super.init(processingEnv); - this.processingEnv = processingEnv; - this.javacProcessingEnv = getJavacProcessingEnvironment(processingEnv); - if (javacProcessingEnv == null) { - return; - } - trees = Trees.instance(javacProcessingEnv); - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - for (Element element : roundEnv.getRootElements()) { - JCCompilationUnit jcCompilationUnit = toUnit(element); - if (jcCompilationUnit != null) { - maybeGenerateTemplateSources(jcCompilationUnit); - } - } - - return true; - } - - void maybeGenerateTemplateSources(JCCompilationUnit cu) { - Context context = javacProcessingEnv.getContext(); - JavacResolution res = new JavacResolution(context); - - new TreeScanner() { - @Override - public void visitApply(JCTree.JCMethodInvocation tree) { - JCTree.JCExpression jcSelect = tree.getMethodSelect(); - String name = jcSelect instanceof JCTree.JCFieldAccess ? - ((JCTree.JCFieldAccess) jcSelect).name.toString() : - ((JCTree.JCIdent) jcSelect).getName().toString(); - - if ("compile".equals(name) && tree.getArguments().size() == 3) { - JCTree.JCMethodInvocation resolvedMethod; - Map resolved; - try { - resolved = res.resolveAll(context, cu, singletonList(tree)); - resolvedMethod = (JCTree.JCMethodInvocation) resolved.get(tree); - } catch (Throwable t) { - processingEnv.getMessager().printMessage(Kind.WARNING, "Had trouble type attributing the template."); - return; - } - - JCTree.JCExpression arg2 = tree.getArguments().get(2); - if (isOfClassType(resolvedMethod.type, "org.openrewrite.java.JavaTemplate.Builder") && - (arg2 instanceof JCTree.JCLambda || arg2 instanceof JCTree.JCTypeCast && ((JCTree.JCTypeCast) arg2).getExpression() instanceof JCTree.JCLambda)) { - - JCTree.JCLambda template = arg2 instanceof JCTree.JCLambda ? (JCTree.JCLambda) arg2 : (JCTree.JCLambda) ((JCTree.JCTypeCast) arg2).getExpression(); - - NavigableMap parameterPositions; - List parameters; - if (template.getParameters().isEmpty()) { - parameterPositions = emptyNavigableMap(); - parameters = emptyList(); - } else { - parameterPositions = new TreeMap<>(); - Map parameterResolution = res.resolveAll(context, cu, template.getParameters()); - parameters = new ArrayList<>(template.getParameters().size()); - for (VariableTree p : template.getParameters()) { - parameters.add((JCTree.JCVariableDecl) parameterResolution.get((JCTree) p)); - } - JCTree.JCLambda resolvedTemplate = (JCTree.JCLambda) parameterResolution.get(template); - - new TreeScanner() { - @Override - public void visitIdent(JCTree.JCIdent ident) { - for (JCTree.JCVariableDecl parameter : parameters) { - if (parameter.sym == ident.sym) { - parameterPositions.put(ident.getStartPosition(), parameter); - } - } - } - }.scan(resolvedTemplate.getBody()); - } - - try (InputStream inputStream = javaFileContent == null ? - cu.getSourceFile().openInputStream() : new ByteArrayInputStream(javaFileContent.getBytes())) { - //noinspection ResultOfMethodCallIgnored - inputStream.skip(template.getBody().getStartPosition()); - - byte[] templateSourceBytes = new byte[template.getBody().getEndPosition(cu.endPositions) - template.getBody().getStartPosition()]; - - //noinspection ResultOfMethodCallIgnored - inputStream.read(templateSourceBytes); - - String templateSource = new String(templateSourceBytes); - templateSource = templateSource.replace("\"", "\\\""); - - for (Map.Entry paramPos : parameterPositions.descendingMap().entrySet()) { - JCTree.JCVariableDecl param = paramPos.getValue(); - String type = param.type.toString(); - for (JCTree.JCAnnotation annotation : param.getModifiers().getAnnotations()) { - if (annotation.type.tsym.getQualifiedName().contentEquals(PRIMITIVE_ANNOTATION)) { - type = PRIMITIVE_TYPE_MAP.get(param.type.toString()); - // don't generate the annotation into the source code - param.mods.annotations = com.sun.tools.javac.util.List.filter(param.mods.annotations, annotation); - } - } - templateSource = templateSource.substring(0, paramPos.getKey() - template.getBody().getStartPosition()) + - "#{any(" + type + ")}" + - templateSource.substring((paramPos.getKey() - template.getBody().getStartPosition()) + - param.name.length()); - } - - JCTree.JCLiteral templateName = (JCTree.JCLiteral) tree.getArguments().get(1); - if (templateName.value == null) { - processingEnv.getMessager().printMessage(Kind.WARNING, "Can't compile a template with a null name."); - return; - } - - // this could be a visitor in the case that the visitor is in its own file or - // named inner class, or a recipe if the visitor is defined in an anonymous class - JCTree.JCClassDecl classDecl = cursor(cu, template).stream() - .filter(JCTree.JCClassDecl.class::isInstance) - .map(JCTree.JCClassDecl.class::cast) - .reduce((next, acc) -> next) - .orElseThrow(() -> new IllegalStateException("Expected to find an enclosing class")); - - String templateFqn; - - if (isOfClassType(classDecl.type, "org.openrewrite.java.JavaVisitor")) { - templateFqn = classDecl.sym.fullname.toString() + "_" + templateName.getValue().toString(); - } else { - JCTree.JCNewClass visitorClass = cursor(cu, template).stream() - .filter(JCTree.JCNewClass.class::isInstance) - .map(JCTree.JCNewClass.class::cast) - .reduce((next, acc) -> next) - .orElse(null); - - JCTree.JCNewClass resolvedVisitorClass = (JCTree.JCNewClass) resolved.get(visitorClass); - - if (resolvedVisitorClass != null && isOfClassType(resolvedVisitorClass.clazz.type, "org.openrewrite.java.JavaVisitor")) { - templateFqn = ((Symbol.ClassSymbol) resolvedVisitorClass.type.tsym).flatname.toString() + "_" + - templateName.getValue().toString(); - } else { - processingEnv.getMessager().printMessage(Kind.WARNING, "Can't compile a template outside of a visitor or recipe."); - return; - } - } - - JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(templateFqn); - try (Writer out = new BufferedWriter(builderFile.openWriter())) { - out.write("package " + classDecl.sym.packge().toString() + ";\n"); - out.write("import org.openrewrite.java.*;\n"); - - - for (JCTree.JCVariableDecl parameter : parameters) { - if (parameter.type.tsym instanceof Symbol.ClassSymbol) { - String paramType = parameter.type.tsym.getQualifiedName().toString(); - if (!paramType.startsWith("java.lang")) { - out.write("import " + paramType + ";\n"); - } - } - } - - out.write("\n"); - out.write("public class " + templateFqn.substring(templateFqn.lastIndexOf('.') + 1) + " {\n"); - out.write(" public static JavaTemplate.Builder getTemplate(JavaVisitor visitor) {\n"); - out.write(" return JavaTemplate\n"); - out.write(" .builder(\"" + templateSource + "\")"); - - List imports = ImportDetector.imports(resolved.get(template)); - String classpath = ClasspathJarNameDetector.classpathFor(resolved.get(template), imports); - if (!classpath.isEmpty()) { - out.write("\n .javaParser(JavaParser.fromJavaVersion().classpath(" + - classpath + "))"); - } - - for (Symbol anImport : imports) { - if (anImport instanceof Symbol.ClassSymbol && !anImport.getQualifiedName().toString().startsWith("java.lang.")) { - out.write("\n .imports(\"" + ((Symbol.ClassSymbol) anImport).fullname.toString().replace('$', '.') + "\")"); - } else if (anImport instanceof Symbol.VarSymbol || anImport instanceof Symbol.MethodSymbol) { - out.write("\n .staticImports(\"" + anImport.owner.getQualifiedName().toString().replace('$', '.') + '.' + anImport.flatName().toString() + "\")"); - } - } - - out.write(";\n"); - out.write(" }\n"); - out.write("}\n"); - out.flush(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - super.visitApply(tree); - } - }.scan(cu); - } - - private boolean isOfClassType(Type type, String fqn) { - return type instanceof Type.ClassType && (((Symbol.ClassSymbol) type.tsym) - .fullname.contentEquals(fqn) || isOfClassType(((Type.ClassType) type).supertype_field, fqn)); - } - - private Stack cursor(JCCompilationUnit cu, Tree t) { - AtomicReference> matching = new AtomicReference<>(); - new TreePathScanner, Stack>() { - @Override - public Stack scan(Tree tree, Stack parent) { - Stack cursor = new Stack<>(); - cursor.addAll(parent); - cursor.push(tree); - if (tree == t) { - matching.set(cursor); - return cursor; - } - return super.scan(tree, cursor); - } - }.scan(cu, new Stack<>()); - return matching.get(); - } - - private JCCompilationUnit toUnit(Element element) { - TreePath path = null; - if (trees != null) { - try { - path = trees.getPath(element); - } catch (NullPointerException ignore) { - // Happens if a package-info.java doesn't contain a package declaration. - // We can safely ignore those, since they do not need any processing - } - } - if (path == null) { - return null; - } - - return (JCCompilationUnit) path.getCompilationUnit(); - } - - /** - * This class casts the given processing environment to a JavacProcessingEnvironment. In case of - * gradle incremental compilation, the delegate ProcessingEnvironment of the gradle wrapper is returned. - */ - public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) { - addOpens(); - if (procEnv instanceof JavacProcessingEnvironment) { - return (JavacProcessingEnvironment) procEnv; - } - - // try to find a "delegate" field in the object, and use this to try to obtain a JavacProcessingEnvironment - for (Class procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { - Object delegate = tryGetDelegateField(procEnvClass, procEnv); - if (delegate == null) { - delegate = tryGetProxyDelegateToField(procEnv); - } - if (delegate == null) { - delegate = tryGetProcessingEnvField(procEnvClass, procEnv); - } - - if (delegate != null) { - return getJavacProcessingEnvironment(delegate); - } - // delegate field was not found, try on superclass - } - - processingEnv.getMessager().printMessage(Kind.WARNING, "Can't get the delegate of the gradle " + - "IncrementalProcessingEnvironment. " + - "OpenRewrite's template processor won't work."); - return null; - } - - private static void addOpens() { - Class cModule; - try { - cModule = Class.forName("java.lang.Module"); - } catch (ClassNotFoundException e) { - return; //jdk8-; this is not needed. - } - - Unsafe unsafe = getUnsafe(); - Object jdkCompilerModule = getJdkCompilerModule(); - Object ownModule = getOwnModule(); - String[] allPkgs = { - "com.sun.tools.javac.code", - "com.sun.tools.javac.comp", - "com.sun.tools.javac.file", - "com.sun.tools.javac.main", - "com.sun.tools.javac.model", - "com.sun.tools.javac.parser", - "com.sun.tools.javac.processing", - "com.sun.tools.javac.tree", - "com.sun.tools.javac.util", - "com.sun.tools.javac.jvm", - }; - - try { - Method m = cModule.getDeclaredMethod("implAddOpens", String.class, cModule); - long firstFieldOffset = getFirstFieldOffset(unsafe); - unsafe.putBooleanVolatile(m, firstFieldOffset, true); - for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule); - } catch (Exception ignore) { - } - } - - private static long getFirstFieldOffset(Unsafe unsafe) { - try { - return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first")); - } catch (NoSuchFieldException e) { - // can't happen. - throw new RuntimeException(e); - } catch (SecurityException e) { - // can't happen - throw new RuntimeException(e); - } - } - - private static Unsafe getUnsafe() { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(null); - } catch (Exception e) { - return null; - } - } - - private static Object getOwnModule() { - try { - Method m = Permit.getMethod(Class.class, "getModule"); - return m.invoke(TemplateProcessor.class); - } catch (Exception e) { - return null; - } - } - - private static Object getJdkCompilerModule() { - // call public api: ModuleLayer.boot().findModule("jdk.compiler").get(); - // but use reflection because we don't want this code to crash on jdk1.7 and below. - // In that case, none of this stuff was needed in the first place, so we just exit via - // the catch block and do nothing. - try { - Class cModuleLayer = Class.forName("java.lang.ModuleLayer"); - Method mBoot = cModuleLayer.getDeclaredMethod("boot"); - Object bootLayer = mBoot.invoke(null); - Class cOptional = Class.forName("java.util.Optional"); - Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class); - Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler"); - return cOptional.getDeclaredMethod("get").invoke(oCompilerO); - } catch (Exception e) { - return null; - } - } - - /** - * Gradle incremental processing - */ - private Object tryGetDelegateField(Class delegateClass, Object instance) { - try { - return Permit.getField(delegateClass, "delegate").get(instance); - } catch (Exception e) { - return null; - } - } - - /** - * Kotlin incremental processing - */ - private Object tryGetProcessingEnvField(Class delegateClass, Object instance) { - try { - return Permit.getField(delegateClass, "processingEnv").get(instance); - } catch (Exception e) { - return null; - } - } - - /** - * IntelliJ >= 2020.3 - */ - private Object tryGetProxyDelegateToField(Object instance) { - try { - InvocationHandler handler = Proxy.getInvocationHandler(instance); - return Permit.getField(handler.getClass(), "val$delegateTo").get(handler); - } catch (Exception e) { - return null; - } - } -} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr0.java b/src/main/java/org/openrewrite/java/template/function/Expr0.java new file mode 100644 index 00000000..16ca95cd --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr0.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr0 { + R accept() throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr1.java b/src/main/java/org/openrewrite/java/template/function/Expr1.java new file mode 100644 index 00000000..084f5105 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr1.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr1 { + R accept(P1 p1) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr10.java b/src/main/java/org/openrewrite/java/template/function/Expr10.java new file mode 100644 index 00000000..1d5c6e5a --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr10.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr10 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr2.java b/src/main/java/org/openrewrite/java/template/function/Expr2.java new file mode 100644 index 00000000..234290dd --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr2.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr2 { + R accept(P1 p1, P2 p2) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr3.java b/src/main/java/org/openrewrite/java/template/function/Expr3.java new file mode 100644 index 00000000..6f9c3e46 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr3.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr3 { + R accept(P1 p1, P2 p2, P3 p3) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr4.java b/src/main/java/org/openrewrite/java/template/function/Expr4.java new file mode 100644 index 00000000..61b81ceb --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr4.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr4 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr5.java b/src/main/java/org/openrewrite/java/template/function/Expr5.java new file mode 100644 index 00000000..1c1232ba --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr5.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr5 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr6.java b/src/main/java/org/openrewrite/java/template/function/Expr6.java new file mode 100644 index 00000000..e63924d7 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr6.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr6 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr7.java b/src/main/java/org/openrewrite/java/template/function/Expr7.java new file mode 100644 index 00000000..caa15178 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr7.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr7 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr8.java b/src/main/java/org/openrewrite/java/template/function/Expr8.java new file mode 100644 index 00000000..c2bf60dc --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr8.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr8 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Expr9.java b/src/main/java/org/openrewrite/java/template/function/Expr9.java new file mode 100644 index 00000000..a859c601 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Expr9.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Expr9 { + R accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat0.java b/src/main/java/org/openrewrite/java/template/function/Stat0.java new file mode 100644 index 00000000..c8ed8a30 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat0.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat0 { + void accept() throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat1.java b/src/main/java/org/openrewrite/java/template/function/Stat1.java new file mode 100644 index 00000000..ecba9be3 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat1.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat1 { + void accept(P1 p1) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat10.java b/src/main/java/org/openrewrite/java/template/function/Stat10.java new file mode 100644 index 00000000..8bd6fa10 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat10.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat10 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat2.java b/src/main/java/org/openrewrite/java/template/function/Stat2.java new file mode 100644 index 00000000..a9525556 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat2.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat2 { + void accept(P1 p1, P2 p2) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat3.java b/src/main/java/org/openrewrite/java/template/function/Stat3.java new file mode 100644 index 00000000..ea497522 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat3.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat3 { + void accept(P1 p1, P2 p2, P3 p3) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat4.java b/src/main/java/org/openrewrite/java/template/function/Stat4.java new file mode 100644 index 00000000..1a455c6f --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat4.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat4 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat5.java b/src/main/java/org/openrewrite/java/template/function/Stat5.java new file mode 100644 index 00000000..88c91aeb --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat5.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat5 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat6.java b/src/main/java/org/openrewrite/java/template/function/Stat6.java new file mode 100644 index 00000000..625bf47e --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat6.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat6 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat7.java b/src/main/java/org/openrewrite/java/template/function/Stat7.java new file mode 100644 index 00000000..97977d84 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat7.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat7 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat8.java b/src/main/java/org/openrewrite/java/template/function/Stat8.java new file mode 100644 index 00000000..1aa3160e --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat8.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat8 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/function/Stat9.java b/src/main/java/org/openrewrite/java/template/function/Stat9.java new file mode 100644 index 00000000..48b10c12 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/function/Stat9.java @@ -0,0 +1,5 @@ +package org.openrewrite.java.template.function; + +public interface Stat9 { + void accept(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) throws Exception; +} diff --git a/src/main/java/org/openrewrite/java/template/internal/AbstractRefasterJavaVisitor.java b/src/main/java/org/openrewrite/java/template/internal/AbstractRefasterJavaVisitor.java new file mode 100644 index 00000000..39f51618 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/internal/AbstractRefasterJavaVisitor.java @@ -0,0 +1,84 @@ +/* + * 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 org.openrewrite.java.template.internal; + +import org.openrewrite.Cursor; +import org.openrewrite.ExecutionContext; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.ShortenFullyQualifiedTypeReferences; +import org.openrewrite.java.cleanup.SimplifyBooleanExpressionVisitor; +import org.openrewrite.java.cleanup.UnnecessaryParenthesesVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaCoordinates; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +@SuppressWarnings("unused") +public abstract class AbstractRefasterJavaVisitor extends JavaVisitor { + + protected final Supplier memoize(Supplier delegate) { + AtomicReference value = new AtomicReference<>(); + return () -> { + T val = value.get(); + if (val == null) { + val = value.updateAndGet(cur -> cur == null ? Objects.requireNonNull(delegate.get()) : cur); + } + return val; + }; + } + + protected final JavaTemplate.Matcher matcher(Supplier template, Cursor cursor) { + return template.get().matcher(cursor); + } + + protected final J apply(Supplier template, Cursor cursor, JavaCoordinates coordinates, Object... parameters) { + return template.get().apply(cursor, coordinates, parameters); + } + + @Deprecated + // to be removed as soon as annotation processor generates required options + protected J embed(J j, Cursor cursor, ExecutionContext ctx) { + return embed(j, cursor, ctx, EmbeddingOption.values()); + } + + @SuppressWarnings({"DataFlowIssue", "SameParameterValue"}) + protected J embed(J j, Cursor cursor, ExecutionContext ctx, EmbeddingOption... options) { + EnumSet optionsSet = options.length > 0 ? EnumSet.copyOf(Arrays.asList(options)) : + EnumSet.noneOf(EmbeddingOption.class); + + TreeVisitor visitor; + if (optionsSet.contains(EmbeddingOption.REMOVE_PARENS) && !getAfterVisit().contains(visitor = new UnnecessaryParenthesesVisitor<>())) { + doAfterVisit(visitor); + } + if (optionsSet.contains(EmbeddingOption.SHORTEN_NAMES)) { + doAfterVisit(ShortenFullyQualifiedTypeReferences.modifyOnly(j)); + } + if (optionsSet.contains(EmbeddingOption.SIMPLIFY_BOOLEANS)) { + j = new SimplifyBooleanExpressionVisitor().visitNonNull(j, ctx, cursor.getParentOrThrow()); + } + return j; + } + + protected enum EmbeddingOption { + SHORTEN_NAMES, SIMPLIFY_BOOLEANS, REMOVE_PARENS; + } +} diff --git a/src/main/java/org/openrewrite/java/template/internal/PatternBuilder.java b/src/main/java/org/openrewrite/java/template/internal/PatternBuilder.java new file mode 100644 index 00000000..f12eab23 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/internal/PatternBuilder.java @@ -0,0 +1,26 @@ +package org.openrewrite.java.template.internal; + +import lombok.Value; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +@Value +@SuppressWarnings("unused") +public class PatternBuilder { + String name; + + public JavaTemplate.Builder build(JavaVisitor owner) { + try { + Class templateClass = Class.forName(owner.getClass().getName() + "_" + name, true, + owner.getClass().getClassLoader()); + Method getTemplate = templateClass.getDeclaredMethod("getTemplate", JavaVisitor.class); + return (JavaTemplate.Builder) getTemplate.invoke(null, owner); + } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | + IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/org/openrewrite/java/template/package-info.java b/src/main/java/org/openrewrite/java/template/package-info.java new file mode 100644 index 00000000..1d435666 --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2020 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. + */ +@NonNullApi +package org.openrewrite.java.template; + +import org.openrewrite.internal.lang.NonNullApi; diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/src/main/resources/META-INF/services/javax.annotation.processing.Processor index 321d6e01..12769398 100644 --- a/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1,2 +1,2 @@ -org.openrewrite.java.template.RefasterTemplateProcessor -org.openrewrite.java.template.TemplateProcessor +org.openrewrite.java.template.processor.RefasterTemplateProcessor +org.openrewrite.java.template.processor.TemplateProcessor diff --git a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java index e1845f44..f527038d 100644 --- a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java +++ b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java @@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.openrewrite.java.template.processor.RefasterTemplateProcessor; import java.io.File; import java.net.URL; @@ -78,7 +79,8 @@ static Collection classpath() { fileForClass(com.sun.tools.javac.tree.JCTree.class), fileForClass(org.openrewrite.Recipe.class), fileForClass(org.openrewrite.java.JavaTemplate.class), - fileForClass(org.slf4j.Logger.class) + fileForClass(org.slf4j.Logger.class), + fileForClass(Primitive.class) ); } diff --git a/src/test/java/org/openrewrite/java/template/TemplateProcessorTest.java b/src/test/java/org/openrewrite/java/template/TemplateProcessorTest.java index e5d4eae3..b3995de2 100644 --- a/src/test/java/org/openrewrite/java/template/TemplateProcessorTest.java +++ b/src/test/java/org/openrewrite/java/template/TemplateProcessorTest.java @@ -19,6 +19,8 @@ import com.google.testing.compile.JavaFileObjects; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.openrewrite.java.template.processor.RefasterTemplateProcessor; +import org.openrewrite.java.template.processor.TemplateProcessor; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; diff --git a/src/test/resources/recipes/MatchingRecipes.java b/src/test/resources/recipes/MatchingRecipes.java index 3862faae..be64197f 100644 --- a/src/test/resources/recipes/MatchingRecipes.java +++ b/src/test/resources/recipes/MatchingRecipes.java @@ -4,11 +4,14 @@ 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.internal.template.AbstractRefasterJavaVisitor; 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.function.Supplier; @@ -29,13 +32,14 @@ public String getDescription() { return "Refaster template recipes for `foo.Matching`."; } - @Override public List getRecipeList() { return Arrays.asList( new StringIsEmptyRecipe() ); } + + @NonNullApi public static class StringIsEmptyRecipe extends Recipe { @Override @@ -51,12 +55,9 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (JavaTemplate.F2) (@Primitive Integer i, String s) -> s.substring(i).isEmpty()).build()); - - Supplier before2 = memoize(() -> JavaTemplate.compile(this, "before2", (JavaTemplate.F2) (@Primitive Integer i, String s) -> s.substring(i).isEmpty()).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (String s) -> s != null && s.length() == 0).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (@Primitive Integer i, String s) -> s.substring(i).isEmpty()).build()); + final Supplier before2 = memoize(() -> Semantics.expression(this, "before2", (@Primitive Integer i, String s) -> s.substring(i).isEmpty()).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> s != null && s.length() == 0).build()); @Override public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { @@ -86,8 +87,12 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( - Preconditions.and(new UsesMethod<>("java.lang.String isEmpty(..)"), new UsesMethod<>("java.lang.String substring(..)")), - javaVisitor); + Preconditions.and( + new UsesMethod<>("java.lang.String isEmpty(..)"), + new UsesMethod<>("java.lang.String substring(..)") + ), + javaVisitor + ); } } diff --git a/src/test/resources/recipes/MultipleDereferencesRecipes.java b/src/test/resources/recipes/MultipleDereferencesRecipes.java index 35e59fbe..a208ee2f 100644 --- a/src/test/resources/recipes/MultipleDereferencesRecipes.java +++ b/src/test/resources/recipes/MultipleDereferencesRecipes.java @@ -4,11 +4,14 @@ 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.internal.template.AbstractRefasterJavaVisitor; 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.function.Supplier; @@ -32,7 +35,6 @@ public String getDescription() { return "Refaster template recipes for `foo.MultipleDereferences`."; } - @Override public List getRecipeList() { return Arrays.asList( @@ -41,6 +43,8 @@ public List getRecipeList() { new EqualsItselfRecipe() ); } + + @NonNullApi public static class VoidTypeRecipe extends Recipe { @Override @@ -56,10 +60,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (JavaTemplate.P1) (java.nio.file.Path p) -> java.nio.file.Files.delete(p)).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (JavaTemplate.P1) (java.nio.file.Path p) -> java.nio.file.Files.delete(p)).build()); + final Supplier before = memoize(() -> Semantics.statement(this, "before", (java.nio.file.Path p) -> java.nio.file.Files.delete(p)).build()); + final Supplier after = memoize(() -> Semantics.statement(this, "after", (java.nio.file.Path p) -> java.nio.file.Files.delete(p)).build()); @Override public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { @@ -87,6 +89,7 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { } } + @NonNullApi public static class StringIsEmptyRecipe extends Recipe { @Override @@ -102,10 +105,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (JavaTemplate.F1) (String s) -> s.isEmpty()).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (String s) -> s != null && s.length() == 0).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (String s) -> s.isEmpty()).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> s != null && s.length() == 0).build()); @Override public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { @@ -128,6 +129,7 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { } } + @NonNullApi public static class EqualsItselfRecipe extends Recipe { @Override @@ -143,10 +145,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (Object o) -> o == o).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (Object o) -> true).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (Object o) -> o == o).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (Object o) -> true).build()); @Override public J visitBinary(J.Binary elem, ExecutionContext ctx) { diff --git a/src/test/resources/recipes/NestedPreconditionsRecipe.java b/src/test/resources/recipes/NestedPreconditionsRecipe.java index f5318caf..921ee0e3 100644 --- a/src/test/resources/recipes/NestedPreconditionsRecipe.java +++ b/src/test/resources/recipes/NestedPreconditionsRecipe.java @@ -4,11 +4,14 @@ 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.internal.template.AbstractRefasterJavaVisitor; 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.function.Supplier; @@ -18,6 +21,7 @@ import java.util.HashMap; import java.util.Hashtable; +@NonNullApi public class NestedPreconditionsRecipe extends Recipe { @Override @@ -33,12 +37,9 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier hashMap = memoize(() -> JavaTemplate.compile(this, "hashMap", (JavaTemplate.F1) (@Primitive Integer size) -> new java.util.HashMap(size)).build()); - - Supplier linkedHashMap = memoize(() -> JavaTemplate.compile(this, "linkedHashMap", (JavaTemplate.F1) (@Primitive Integer size) -> new java.util.LinkedHashMap(size)).build()); - - Supplier hashtable = memoize(() -> JavaTemplate.compile(this, "hashtable", (JavaTemplate.F1) (@Primitive Integer size) -> new java.util.Hashtable(size)).build()); + final Supplier hashMap = memoize(() -> Semantics.expression(this, "hashMap", (@Primitive Integer size) -> new java.util.HashMap(size)).build()); + final Supplier linkedHashMap = memoize(() -> Semantics.expression(this, "linkedHashMap", (@Primitive Integer size) -> new java.util.LinkedHashMap(size)).build()); + final Supplier hashtable = memoize(() -> Semantics.expression(this, "hashtable", (@Primitive Integer size) -> new java.util.Hashtable(size)).build()); @Override public J visitExpression(Expression elem, ExecutionContext ctx) { @@ -64,7 +65,14 @@ public J visitExpression(Expression elem, ExecutionContext ctx) { }; return Preconditions.check( - Preconditions.and(new UsesType<>("java.util.Map", true), Preconditions.or(new UsesType<>("java.util.HashMap", true), new UsesType<>("java.util.LinkedHashMap", true))), - javaVisitor); + Preconditions.and( + new UsesType<>("java.util.Map", true), + Preconditions.or( + new UsesType<>("java.util.HashMap", true), + new UsesType<>("java.util.LinkedHashMap", true) + ) + ), + javaVisitor + ); } } diff --git a/src/test/resources/recipes/ShouldAddImportsRecipes.java b/src/test/resources/recipes/ShouldAddImportsRecipes.java index b6b1282a..2f0c0eab 100644 --- a/src/test/resources/recipes/ShouldAddImportsRecipes.java +++ b/src/test/resources/recipes/ShouldAddImportsRecipes.java @@ -4,11 +4,14 @@ 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.internal.template.AbstractRefasterJavaVisitor; 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.function.Supplier; @@ -32,7 +35,6 @@ public String getDescription() { return "Refaster template recipes for `foo.ShouldAddImports`."; } - @Override public List getRecipeList() { return Arrays.asList( @@ -41,6 +43,8 @@ public List getRecipeList() { new StaticImportObjectsHashRecipe() ); } + + @NonNullApi public static class StringValueOfRecipe extends Recipe { @Override @@ -56,10 +60,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (JavaTemplate.F1) (String s) -> String.valueOf(s)).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (JavaTemplate.F1) (String s) -> java.util.Objects.toString(s)).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (String s) -> String.valueOf(s)).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> java.util.Objects.toString(s)).build()); @Override public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { @@ -77,10 +79,12 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( new UsesMethod<>("java.lang.String valueOf(..)"), - javaVisitor); + javaVisitor + ); } } + @NonNullApi public static class ObjectsEqualsRecipe extends Recipe { @Override @@ -96,12 +100,9 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier equals = memoize(() -> JavaTemplate.compile(this, "equals", (JavaTemplate.F2) (@Primitive Integer a, @Primitive Integer b) -> java.util.Objects.equals(a, b)).build()); - - Supplier compareZero = memoize(() -> JavaTemplate.compile(this, "compareZero", (@Primitive Integer a, @Primitive Integer b) -> Integer.compare(a, b) == 0).build()); - - Supplier isis = memoize(() -> JavaTemplate.compile(this, "isis", (@Primitive Integer a, @Primitive Integer b) -> a == b).build()); + final Supplier equals = memoize(() -> Semantics.expression(this, "equals", (@Primitive Integer a, @Primitive Integer b) -> java.util.Objects.equals(a, b)).build()); + final Supplier compareZero = memoize(() -> Semantics.expression(this, "compareZero", (@Primitive Integer a, @Primitive Integer b) -> Integer.compare(a, b) == 0).build()); + final Supplier isis = memoize(() -> Semantics.expression(this, "isis", (@Primitive Integer a, @Primitive Integer b) -> a == b).build()); @Override public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { @@ -126,11 +127,19 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( - Preconditions.or(Preconditions.and(new UsesType<>("java.util.Objects", true), new UsesMethod<>("java.util.Objects equals(..)")), new UsesMethod<>("java.lang.Integer compare(..)")), - javaVisitor); + Preconditions.or( + Preconditions.and( + new UsesType<>("java.util.Objects", true), + new UsesMethod<>("java.util.Objects equals(..)") + ), + new UsesMethod<>("java.lang.Integer compare(..)") + ), + javaVisitor + ); } } + @NonNullApi public static class StaticImportObjectsHashRecipe extends Recipe { @Override @@ -146,10 +155,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (JavaTemplate.F1) (String s) -> hash(s)).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (JavaTemplate.F1) (String s) -> s.hashCode()).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (String s) -> hash(s)).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> s.hashCode()).build()); @Override public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { @@ -168,7 +175,8 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( new UsesMethod<>("java.util.Objects hash(..)"), - javaVisitor); + javaVisitor + ); } } diff --git a/src/test/resources/recipes/ShouldSupportNestedClassesRecipes.java b/src/test/resources/recipes/ShouldSupportNestedClassesRecipes.java index 4c40ed5b..e786706d 100644 --- a/src/test/resources/recipes/ShouldSupportNestedClassesRecipes.java +++ b/src/test/resources/recipes/ShouldSupportNestedClassesRecipes.java @@ -4,11 +4,14 @@ 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.internal.template.AbstractRefasterJavaVisitor; 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.function.Supplier; @@ -29,7 +32,6 @@ public String getDescription() { return "Refaster template recipes for `foo.ShouldSupportNestedClasses`."; } - @Override public List getRecipeList() { return Arrays.asList( @@ -37,6 +39,8 @@ public List getRecipeList() { new AnotherClassRecipe() ); } + + @NonNullApi public static class NestedClassRecipe extends Recipe { @Override @@ -52,10 +56,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (String s) -> s.length() > 0).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (String s) -> !s.isEmpty()).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (String s) -> s.length() > 0).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> !s.isEmpty()).build()); @Override public J visitBinary(J.Binary elem, ExecutionContext ctx) { @@ -73,10 +75,12 @@ public J visitBinary(J.Binary elem, ExecutionContext ctx) { }; return Preconditions.check( new UsesMethod<>("java.lang.String length(..)"), - javaVisitor); + javaVisitor + ); } } + @NonNullApi static class AnotherClassRecipe extends Recipe { @Override @@ -92,10 +96,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (String s) -> s.length() == 0).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (JavaTemplate.F1) (String s) -> s.isEmpty()).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (String s) -> s.length() == 0).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> s.isEmpty()).build()); @Override public J visitBinary(J.Binary elem, ExecutionContext ctx) { @@ -113,7 +115,8 @@ public J visitBinary(J.Binary elem, ExecutionContext ctx) { }; return Preconditions.check( new UsesMethod<>("java.lang.String length(..)"), - javaVisitor); + javaVisitor + ); } } diff --git a/src/test/resources/recipes/UseStringIsEmptyRecipe.java b/src/test/resources/recipes/UseStringIsEmptyRecipe.java index 7c01f64a..bf370874 100644 --- a/src/test/resources/recipes/UseStringIsEmptyRecipe.java +++ b/src/test/resources/recipes/UseStringIsEmptyRecipe.java @@ -4,16 +4,20 @@ 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.internal.template.AbstractRefasterJavaVisitor; 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.function.Supplier; +@NonNullApi public class UseStringIsEmptyRecipe extends Recipe { @Override @@ -29,10 +33,8 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { - - Supplier before = memoize(() -> JavaTemplate.compile(this, "before", (String s) -> s.length() > 0).build()); - - Supplier after = memoize(() -> JavaTemplate.compile(this, "after", (String s) -> !s.isEmpty()).build()); + final Supplier before = memoize(() -> Semantics.expression(this, "before", (String s) -> s.length() > 0).build()); + final Supplier after = memoize(() -> Semantics.expression(this, "after", (String s) -> !s.isEmpty()).build()); @Override public J visitBinary(J.Binary elem, ExecutionContext ctx) { @@ -50,6 +52,7 @@ public J visitBinary(J.Binary elem, ExecutionContext ctx) { }; return Preconditions.check( new UsesMethod<>("java.lang.String length(..)"), - javaVisitor); + javaVisitor + ); } }