From 07107ce5cb6323609b7a3881f60304e68e4a50a7 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Fri, 12 May 2023 15:58:07 +0200 Subject: [PATCH] Add some sanity checks If a template has multiple before templates, uses any unsupported Refaster annotations, or any of the static methods of the `Refaster` class, no recipe will be generated. Issue: #5 --- build.gradle.kts | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 61574 bytes gradlew | 4 +- .../template/RefasterTemplateProcessor.java | 188 +++++++++++++----- 4 files changed, 137 insertions(+), 56 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 597aaf06..db20e99b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,6 +64,7 @@ val tools = compiler.get().metadata.installationPath.file("lib/tools.jar") dependencies { compileOnly(files(tools)) + compileOnly("org.jetbrains:annotations:24.0.+") } tasks.withType { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..943f0cbfa754578e88a3dae77fce6e3dea56edbf 100644 GIT binary patch delta 5107 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 I6Xdi10eY*&F8}}l delta 5094 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 diff --git a/gradlew b/gradlew index 79a61d42..65dcd68d 100755 --- a/gradlew +++ b/gradlew @@ -144,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac diff --git a/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java b/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java index 7b5754b8..85d42514 100644 --- a/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java +++ b/src/main/java/org/openrewrite/java/template/RefasterTemplateProcessor.java @@ -15,7 +15,7 @@ */ package org.openrewrite.java.template; -import com.sun.source.tree.Tree; +import com.sun.source.tree.*; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import com.sun.tools.javac.code.Symbol; @@ -25,6 +25,7 @@ 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.ImportDetector; import org.openrewrite.java.template.internal.JavacResolution; import org.openrewrite.java.template.internal.Permit; @@ -38,7 +39,6 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeKind; import javax.tools.Diagnostic.Kind; import javax.tools.JavaFileObject; import java.io.IOException; @@ -48,6 +48,7 @@ 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; @@ -61,8 +62,20 @@ */ @SupportedAnnotationTypes({BEFORE_TEMPLATE, AFTER_TEMPLATE}) public class RefasterTemplateProcessor extends AbstractProcessor { - public static final String BEFORE_TEMPLATE = "com.google.errorprone.refaster.annotation.BeforeTemplate"; - public static final String AFTER_TEMPLATE = "com.google.errorprone.refaster.annotation.AfterTemplate"; + 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" + ).collect(Collectors.toSet()); static final String PRIMITIVE_ANNOTATION = "@Primitive"; static final Map PRIMITIVE_TYPE_MAP = new HashMap<>(); @@ -122,12 +135,6 @@ public synchronized void init(ProcessingEnvironment processingEnv) { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { -// TypeElement beforeTemplateType = processingEnv.getElementUtils().getTypeElement("com.google.errorprone.refaster.annotation.BeforeTemplate"); -// TypeElement afterTemplateType = processingEnv.getElementUtils().getTypeElement("com.google.errorprone.refaster.annotation.AfterTemplate"); -// roundEnv.getElementsAnnotatedWith(beforeTemplateType).forEach(e -> { -// processingEnv.getMessager().printMessage(Kind.NOTE, "Found @BeforeTemplate: " + e); -// }); - for (Element element : roundEnv.getRootElements()) { JCCompilationUnit jcCompilationUnit = toUnit(element); if (jcCompilationUnit != null) { @@ -145,14 +152,8 @@ void maybeGenerateTemplateSources(JCCompilationUnit cu) { @Override public void visitClassDef(JCTree.JCClassDecl tree) { super.visitClassDef(tree); - TemplateDescriptor descriptor = getTemplateDescriptor(tree, context); + TemplateDescriptor descriptor = getTemplateDescriptor(tree, context, cu); if (descriptor != null) { - try { - descriptor.resolve(context, cu); - } catch (Throwable t) { - processingEnv.getMessager().printMessage(Kind.WARNING, "Had trouble type attributing the template."); - return; - } TreeMaker treeMaker = TreeMaker.instance(context).forToplevel(cu); List membersWithoutConstructor = tree.getMembers().stream() @@ -214,11 +215,11 @@ public void visitClassDef(JCTree.JCClassDecl tree) { out.write(" public TreeVisitor getVisitor() {\n"); out.write(" return new JavaVisitor() {\n"); out.write(" final JavaTemplate before0 = JavaTemplate.compile(this, \"" - + descriptor.beforeTemplates.get(0).getName().toString() + "\", " - + toLambda(descriptor.beforeTemplates.get(0)) + ").build();\n"); + + descriptor.beforeTemplates.get(0).getName().toString() + "\", " + + toLambda(descriptor.beforeTemplates.get(0)) + ").build();\n"); out.write(" final JavaTemplate after = JavaTemplate.compile(this, \"" - + descriptor.afterTemplate.getName().toString() + "\", " - + toLambda(descriptor.afterTemplate) + ").build();\n"); + + descriptor.afterTemplate.getName().toString() + "\", " + + toLambda(descriptor.afterTemplate) + ").build();\n"); out.write("\n"); String lstType = LST_TYPE_MAP.get(getType(descriptor.beforeTemplates.get(0))); @@ -340,39 +341,94 @@ private String toLambda(JCTree.JCMethodDecl method) { return builder.toString(); } - private TemplateDescriptor getTemplateDescriptor(JCTree.JCClassDecl tree, Context context) { + @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); - if (annotations.stream().anyMatch(a -> a.sym.getQualifiedName().toString().equals(BEFORE_TEMPLATE))) { + List annotations = getTemplateAnnotations(method, BEFORE_TEMPLATE::equals); + if (!annotations.isEmpty()) { result.beforeTemplate(method); - } else if (annotations.stream().anyMatch(a -> a.sym.getQualifiedName().toString().equals(AFTER_TEMPLATE))) { + } + annotations = getTemplateAnnotations(method, AFTER_TEMPLATE::equals); + if (!annotations.isEmpty()) { result.afterTemplate(method); } } } - return result.validate(); + return result.validate(context, cu); } - static class TemplateDescriptor { - private final JCTree.JCClassDecl classDecl; - List beforeTemplates; + class TemplateDescriptor { + final JCTree.JCClassDecl classDecl; + final List beforeTemplates = new ArrayList<>(); JCTree.JCMethodDecl afterTemplate; public TemplateDescriptor(JCTree.JCClassDecl classDecl) { this.classDecl = classDecl; } - private TemplateDescriptor validate() { - return beforeTemplates != null && afterTemplate != null ? this : null; + @Nullable + private TemplateDescriptor validate(Context context, JCCompilationUnit cu) { + if (beforeTemplates.isEmpty() || afterTemplate == null) { + return null; + } + + boolean valid = true; + // TODO: support multiple before templates + if (beforeTemplates.size() > 1) { + beforeTemplates.forEach(t -> { + processingEnv.getMessager().printMessage(Kind.NOTE, "Only one @BeforeTemplate annotated method is supported at this time", t.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; } - public void beforeTemplate(JCTree.JCMethodDecl method) { - if (beforeTemplates == null) { - beforeTemplates = new ArrayList<>(); + 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.type.tsym.getQualifiedName() + " 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.type.tsym.getQualifiedName() + " annotation is currently not supported", parameter.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); } @@ -380,29 +436,52 @@ public void afterTemplate(JCTree.JCMethodDecl method) { afterTemplate = method; } - public void resolve(Context context, JCCompilationUnit cu) { - 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); + 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; } - public boolean isExpression() { - return afterTemplate.getReturnType().type.getKind() != TypeKind.VOID; + } + + 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.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(JCTree.JCMethodDecl method) { - return method.getModifiers().getAnnotations().stream() - .filter(a -> a.getTag() == JCTree.Tag.ANNOTATION) - .map(JCTree.JCAnnotation::getAnnotationType) - .filter(a -> a.getKind() == Tree.Kind.IDENTIFIER) - .map(JCTree.JCIdent.class::cast) - .filter(i -> i.sym != null && i.sym.getQualifiedName().toString().startsWith("com.google.errorprone.refaster.annotation.")) - .collect(Collectors.toList()); + 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 && 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 + && typePredicate.test(((JCTree.JCFieldAccess) type).sym.getQualifiedName().toString())) { + result.add((JCTree.JCAnnotation) annotation); + } + } + return result; } private JCCompilationUnit toUnit(Element element) { @@ -449,11 +528,12 @@ public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) } processingEnv.getMessager().printMessage(Kind.WARNING, "Can't get the delegate of the gradle " + - "IncrementalProcessingEnvironment. " + - "OpenRewrite's template processor won't work."); + "IncrementalProcessingEnvironment. " + + "OpenRewrite's template processor won't work."); return null; } + @SuppressWarnings({"DataFlowIssue", "JavaReflectionInvocation"}) private static void addOpens() { Class cModule; try {