changeset 38606:6b708cd90241

8032230: Enhance javax.a.p.RoundEnvironment after repeating annotations Reviewed-by: jjg
author darcy
date Wed, 25 May 2016 22:32:16 -0700
parents 72ca5671f3e6
children eb4a0b7e67a1
files langtools/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacRoundEnvironment.java langtools/test/tools/javac/processing/environment/round/TestElementsAnnotatedWith.java
diffstat 3 files changed, 378 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/langtools/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java	Wed May 25 11:33:56 2016 -0400
+++ b/langtools/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java	Wed May 25 22:32:16 2016 -0700
@@ -27,6 +27,8 @@
 
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
+import java.util.LinkedHashSet;
+import java.util.Collections;
 import java.util.Set;
 import java.lang.annotation.Annotation;
 
@@ -92,6 +94,38 @@
     Set<? extends Element> getElementsAnnotatedWith(TypeElement a);
 
     /**
+     * Returns the elements annotated with one or more of the given
+     * annotation types.
+     *
+     * @apiNote This method may be useful when processing repeating
+     * annotations by looking for an annotation type and its
+     * containing annotation type at the same time.
+     *
+     * @implSpec The default implementation of this method creates an
+     * empty result set, iterates over the annotations in the argument
+     * set calling {@link #getElementsAnnotatedWith(TypeElement)} on
+     * each annotation and adding those results to the result
+     * set. Finally, the contents of the result set are returned as an
+     * unmodifiable set.
+     *
+     * @param annotations  annotation types being requested
+     * @return the elements annotated with one or more of the given
+     * annotation types, or an empty set if there are none
+     * @throws IllegalArgumentException if the any elements of the
+     * argument set do not represent an annotation type
+     * @jls 9.6.3 Repeatable Annotation Types
+     * @since 9
+     */
+    default Set<? extends Element> getElementsAnnotatedWithAny(TypeElement... annotations){
+        // Use LinkedHashSet rather than HashSet for predictability
+        Set<Element> result = new LinkedHashSet<>();
+        for (TypeElement annotation : annotations) {
+            result.addAll(getElementsAnnotatedWith(annotation));
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    /**
      * Returns the elements annotated with the given annotation type.
      * The annotation may appear directly or be inherited.  Only
      * package elements and type elements <i>included</i> in this
@@ -110,4 +144,36 @@
      * represent an annotation type
      */
     Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a);
+
+    /**
+     * Returns the elements annotated with one or more of the given
+     * annotation types.
+     *
+     * @apiNote This method may be useful when processing repeating
+     * annotations by looking for an annotation type and its
+     * containing annotation type at the same time.
+     *
+     * @implSpec The default implementation of this method creates an
+     * empty result set, iterates over the annotations in the argument
+     * set calling {@link #getElementsAnnotatedWith(Class)} on
+     * each annotation and adding those results to the result
+     * set. Finally, the contents of the result set are returned as an
+     * unmodifiable set.
+     *
+     * @param annotations  annotation types being requested
+     * @return the elements annotated with one or more of the given
+     * annotation types, or an empty set if there are none
+     * @throws IllegalArgumentException if the any elements of the
+     * argument set do not represent an annotation type
+     * @jls 9.6.3 Repeatable Annotation Types
+     * @since 9
+     */
+    default Set<? extends Element> getElementsAnnotatedWithAny(Set<Class<? extends Annotation>> annotations){
+        // Use LinkedHashSet rather than HashSet for predictability
+        Set<Element> result = new LinkedHashSet<>();
+        for (Class<? extends Annotation> annotation : annotations) {
+            result.addAll(getElementsAnnotatedWith(annotation));
+        }
+        return Collections.unmodifiableSet(result);
+    }
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacRoundEnvironment.java	Wed May 25 11:33:56 2016 -0400
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacRoundEnvironment.java	Wed May 25 22:32:16 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -51,6 +51,7 @@
     private final boolean processingOver;
     private final boolean errorRaised;
     private final ProcessingEnvironment processingEnv;
+    private final Elements eltUtils;
 
     // Caller must pass in an immutable set
     private final Set<? extends Element> rootElements;
@@ -63,6 +64,7 @@
         this.errorRaised = errorRaised;
         this.rootElements = rootElements;
         this.processingEnv = processingEnv;
+        this.eltUtils = processingEnv.getElementUtils();
     }
 
     public String toString() {
@@ -100,9 +102,6 @@
         return rootElements;
     }
 
-    private static final String NOT_AN_ANNOTATION_TYPE =
-        "The argument does not represent an annotation type: ";
-
     /**
      * Returns the elements annotated with the given annotation type.
      * Only type elements <i>included</i> in this round of annotation
@@ -117,10 +116,9 @@
      */
     @DefinedBy(Api.ANNOTATION_PROCESSING)
     public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
+        throwIfNotAnnotation(a);
+
         Set<Element> result = Collections.emptySet();
-        if (a.getKind() != ElementKind.ANNOTATION_TYPE)
-            throw new IllegalArgumentException(NOT_AN_ANNOTATION_TYPE + a);
-
         ElementScanner9<Set<Element>, TypeElement> scanner =
             new AnnotationSetScanner(result);
 
@@ -130,41 +128,93 @@
         return result;
     }
 
+    @DefinedBy(Api.ANNOTATION_PROCESSING)
+    public Set<? extends Element> getElementsAnnotatedWithAny(TypeElement... annotations) {
+        // Don't bother to special-case annotations.length == 1 as
+        // return getElementsAnnotatedWith(annotations[0]);
+
+        Set<TypeElement> annotationSet = new LinkedHashSet<>(annotations.length);
+        for (TypeElement annotation : annotations) {
+            throwIfNotAnnotation(annotation);
+            annotationSet.add(annotation);
+        }
+
+        Set<Element> result = Collections.emptySet();
+        ElementScanner9<Set<Element>, Set<TypeElement>> scanner =
+            new AnnotationSetMultiScanner(result);
+
+        for (Element element : rootElements)
+            result = scanner.scan(element, annotationSet);
+
+        return result;
+    }
+
     // Could be written as a local class inside getElementsAnnotatedWith
     private class AnnotationSetScanner extends
-        ElementScanner9<Set<Element>, TypeElement> {
+        ElementScanningIncludingTypeParameters<Set<Element>, TypeElement> {
         // Insertion-order preserving set
-        Set<Element> annotatedElements = new LinkedHashSet<>();
+        private Set<Element> annotatedElements = new LinkedHashSet<>();
 
         AnnotationSetScanner(Set<Element> defaultSet) {
             super(defaultSet);
         }
 
         @Override @DefinedBy(Api.LANGUAGE_MODEL)
-        public Set<Element> visitType(TypeElement e, TypeElement p) {
+        public Set<Element> scan(Element e, TypeElement annotation) {
+            for (AnnotationMirror annotMirror :  eltUtils.getAllAnnotationMirrors(e)) {
+                if (annotation.equals(mirrorAsElement(annotMirror))) {
+                    annotatedElements.add(e);
+                    break;
+                }
+            }
+            e.accept(this, annotation);
+            return annotatedElements;
+        }
+    }
+
+    // Could be written as a local class inside getElementsAnnotatedWithAny
+    private class AnnotationSetMultiScanner extends
+        ElementScanningIncludingTypeParameters<Set<Element>, Set<TypeElement>> {
+        // Insertion-order preserving set
+        private Set<Element> annotatedElements = new LinkedHashSet<>();
+
+        AnnotationSetMultiScanner(Set<Element> defaultSet) {
+            super(defaultSet);
+        }
+
+        @Override @DefinedBy(Api.LANGUAGE_MODEL)
+        public Set<Element> scan(Element e, Set<TypeElement> annotations) {
+            for (AnnotationMirror annotMirror : eltUtils.getAllAnnotationMirrors(e)) {
+                if (annotations.contains(mirrorAsElement(annotMirror))) {
+                    annotatedElements.add(e);
+                    break;
+                }
+            }
+            e.accept(this, annotations);
+            return annotatedElements;
+        }
+    }
+
+    private static abstract class ElementScanningIncludingTypeParameters<R, P>
+        extends ElementScanner9<R, P> {
+
+        protected ElementScanningIncludingTypeParameters(R defaultValue) {
+            super(defaultValue);
+        }
+
+        @Override @DefinedBy(Api.LANGUAGE_MODEL)
+        public R visitType(TypeElement e, P p) {
             // Type parameters are not considered to be enclosed by a type
             scan(e.getTypeParameters(), p);
             return super.visitType(e, p);
         }
 
         @Override @DefinedBy(Api.LANGUAGE_MODEL)
-        public Set<Element> visitExecutable(ExecutableElement e, TypeElement p) {
+        public R visitExecutable(ExecutableElement e, P p) {
             // Type parameters are not considered to be enclosed by an executable
             scan(e.getTypeParameters(), p);
             return super.visitExecutable(e, p);
         }
-
-        @Override @DefinedBy(Api.LANGUAGE_MODEL)
-        public Set<Element> scan(Element e, TypeElement p) {
-            java.util.List<? extends AnnotationMirror> annotationMirrors =
-                processingEnv.getElementUtils().getAllAnnotationMirrors(e);
-            for (AnnotationMirror annotationMirror : annotationMirrors) {
-                if (p.equals(annotationMirror.getAnnotationType().asElement()))
-                    annotatedElements.add(e);
-            }
-            e.accept(this, p);
-            return annotatedElements;
-        }
     }
 
     /**
@@ -172,17 +222,48 @@
      */
     @DefinedBy(Api.ANNOTATION_PROCESSING)
     public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
-        if (!a.isAnnotation())
-            throw new IllegalArgumentException(NOT_AN_ANNOTATION_TYPE + a);
+        throwIfNotAnnotation(a);
         String name = a.getCanonicalName();
         if (name == null)
             return Collections.emptySet();
         else {
-            TypeElement annotationType = processingEnv.getElementUtils().getTypeElement(name);
+            TypeElement annotationType = eltUtils.getTypeElement(name);
             if (annotationType == null)
                 return Collections.emptySet();
             else
                 return getElementsAnnotatedWith(annotationType);
         }
     }
+
+    @DefinedBy(Api.ANNOTATION_PROCESSING)
+    public Set<? extends Element> getElementsAnnotatedWithAny(Set<Class<? extends Annotation>> annotations) {
+        List<TypeElement> annotationsAsElements = new ArrayList<>(annotations.size());
+
+        for (Class<? extends Annotation> annotation : annotations) {
+            throwIfNotAnnotation(annotation);
+            String name = annotation.getCanonicalName();
+            if (name == null)
+                continue;
+            annotationsAsElements.add(eltUtils.getTypeElement(name));
+        }
+
+        return getElementsAnnotatedWithAny(annotationsAsElements.toArray(new TypeElement[0]));
+    }
+
+    private Element mirrorAsElement(AnnotationMirror annotationMirror) {
+        return annotationMirror.getAnnotationType().asElement();
+    }
+
+    private static final String NOT_AN_ANNOTATION_TYPE =
+        "The argument does not represent an annotation type: ";
+
+    private void throwIfNotAnnotation(Class<? extends Annotation> a) {
+        if (!a.isAnnotation())
+            throw new IllegalArgumentException(NOT_AN_ANNOTATION_TYPE + a);
+    }
+
+    private void throwIfNotAnnotation(TypeElement a) {
+        if (a.getKind() != ElementKind.ANNOTATION_TYPE)
+            throw new IllegalArgumentException(NOT_AN_ANNOTATION_TYPE + a);
+    }
 }
--- a/langtools/test/tools/javac/processing/environment/round/TestElementsAnnotatedWith.java	Wed May 25 11:33:56 2016 -0400
+++ b/langtools/test/tools/javac/processing/environment/round/TestElementsAnnotatedWith.java	Wed May 25 22:32:16 2016 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 6397298 6400986 6425592 6449798 6453386 6508401 6498938 6911854 8030049 8038080
+ * @bug 6397298 6400986 6425592 6449798 6453386 6508401 6498938 6911854 8030049 8038080 8032230
  * @summary Tests that getElementsAnnotatedWith works properly.
  * @author  Joseph D. Darcy
  * @library /tools/javac/lib
@@ -51,69 +51,183 @@
 import java.util.Set;
 import java.util.HashSet;
 import java.util.Arrays;
+import java.util.Objects;
 import javax.annotation.processing.*;
 import javax.lang.model.element.*;
 import static javax.lang.model.util.ElementFilter.*;
 
 /**
  * This processor verifies that the information returned by
- * getElementsAnnotatedWith is consistent with the expected results
- * stored in an AnnotatedElementInfo annotation.
+ * getElementsAnnotatedWith and getElementsAnnotatedWithAny is
+ * consistent with the expected results stored in an
+ * AnnotatedElementInfo annotation.
  */
 @AnnotatedElementInfo(annotationName="java.lang.SuppressWarnings", expectedSize=0, names={})
 public class TestElementsAnnotatedWith extends JavacTestingAbstractProcessor {
 
     public boolean process(Set<? extends TypeElement> annotations,
-                           RoundEnvironment roundEnvironment) {
-        TypeElement annotatedElementInfoElement =
-            elements.getTypeElement("AnnotatedElementInfo");
-        Set<? extends Element> resultsMeta = Collections.emptySet();
-        Set<? extends Element> resultsBase = Collections.emptySet();
+                           RoundEnvironment roundEnv) {
+        // First check sets of annotated elements using the round
+        // environment from the annotation processing tool framework.
+        checkSetOfAnnotatedElements(roundEnv);
 
-        if (!roundEnvironment.processingOver()) {
-            testNonAnnotations(roundEnvironment);
+        // Next check sets of annotated elements using a round
+        // environment which uses the default implementations of the
+        // getElementsAnnotatedWithAny methods from the interface.
+        checkSetOfAnnotatedElements(new TestingRoundEnvironment(roundEnv));
+        return true;
+    }
+
+    /**
+     * To allow testing of the executable code of the default methods
+     * for the two overloaded getElementsAnnotatedWithAny methods
+     * defined in the RoundEnvironment interface, this class delegates
+     * the non-default methods of RoundEnvironment to a given
+     * RoundEnvironment object and then explicitly calls the default
+     * methods of the interface instead of relying on the object's
+     * implementation of those methods.
+     */
+    private class TestingRoundEnvironment implements RoundEnvironment {
+        private RoundEnvironment re;
+
+        public TestingRoundEnvironment(RoundEnvironment re) {
+            this.re = re;
+        }
+
+        @Override
+        public boolean errorRaised() {
+            return re.errorRaised();
+        }
+
+        @Override
+        public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
+            return re.getElementsAnnotatedWith(a);
+        }
+
+        @Override
+        public Set<? extends Element> getElementsAnnotatedWithAny(Set<Class<? extends Annotation>> a) {
+            // Default method defined in the interface
+            return RoundEnvironment.super.getElementsAnnotatedWithAny(a);
+        }
+
+        @Override
+        public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
+            return re.getElementsAnnotatedWith(a);
+        }
+
+        @Override
+        public Set<? extends Element> getElementsAnnotatedWithAny(TypeElement... a) {
+            // Default method defined in the interface
+            return RoundEnvironment.super.getElementsAnnotatedWithAny(a);
+        }
+
+        @Override
+        public Set<? extends Element> getRootElements() {
+            return re.getRootElements();
+        }
+
+        @Override
+        public boolean processingOver() {
+            return re.processingOver();
+        }
+
+    }
+
+    /**
+     * The method checks the following conditions:
+     *
+     * 1) The sets of elements found are equal for the TypeElement and
+     * Class<? extends Annotation> methods on logically equivalent
+     * arguments.
+     *
+     * 2) getElementsAnnotatedWithAny(X) is equal to
+     * getElementsAnnotatedWith(X') where X is a set/var-args array
+     * with one element and X' is the element.
+     *
+     * 3) Verify the result of getElementsAnnotatedWithAny({X, Y}) is equal to
+     * getElementsAnnotatedWith(X) UNION getElementsAnnotatedWith(Y).
+     */
+    void checkSetOfAnnotatedElements(RoundEnvironment re) {
+        TypeElement annotatedElemInfoElem =  elements.getTypeElement("AnnotatedElementInfo");
+
+        // For the "Any" methods, search for both the expected
+        // annotation and AnnotatedElementInfo and verify the return
+        // set is the union of searching for AnnotatedElementInfo and
+        // the other annotation
+        Set<? extends Element> resultsMeta         = Collections.emptySet();
+        Set<? extends Element> resultsMetaAny      = Collections.emptySet();
+        Set<Element>           resultsMetaMulti    = new HashSet<>();
+        Set<? extends Element> resultsMetaAnyMulti = Collections.emptySet();
+        Set<? extends Element> resultsBase         = Collections.emptySet();
+        Set<? extends Element> resultsBaseAny      = Collections.emptySet();
+        Set<? extends Element> resultsBaseAnyMulti = Collections.emptySet();
+
+        if (!re.processingOver()) {
+            testNonAnnotations(re);
 
             // Verify AnnotatedElementInfo is present on the first
             // specified type.
 
-            TypeElement firstType = typesIn(roundEnvironment.getRootElements()).iterator().next();
+            TypeElement firstType = typesIn(re.getRootElements()).iterator().next();
 
-            AnnotatedElementInfo annotatedElementInfo = firstType.getAnnotation(AnnotatedElementInfo.class);
+            AnnotatedElementInfo annotatedElemInfo =
+                firstType.getAnnotation(AnnotatedElementInfo.class);
 
             boolean failed = false;
 
-            if (annotatedElementInfo == null)
-                throw new IllegalArgumentException("Missing AnnotatedElementInfo annotation on " +
-                                                  firstType);
-            else {
-                // Verify that the annotation information is as
-                // expected.
+            Objects.requireNonNull(annotatedElemInfo,
+                                   "Missing AnnotatedElementInfo annotation on " + firstType);
 
-                Set<String> expectedNames = new HashSet<String>(Arrays.asList(annotatedElementInfo.names()));
+            // Verify that the annotation information is as expected.
+            Set<String> expectedNames =
+                new HashSet<>(Arrays.asList(annotatedElemInfo.names()));
 
-                resultsMeta =
-                    roundEnvironment.
-                    getElementsAnnotatedWith(elements.getTypeElement(annotatedElementInfo.annotationName()));
+            String annotationName = annotatedElemInfo.annotationName();
+            TypeElement annotationTypeElem = elements.getTypeElement(annotationName);
 
-                if (!resultsMeta.isEmpty())
-                    System.err.println("Results: " + resultsMeta);
+            resultsMeta         = re.getElementsAnnotatedWith(annotationTypeElem);
+            resultsMetaAny      = re.getElementsAnnotatedWithAny(annotationTypeElem);
+            resultsMetaMulti.addAll(resultsMeta);
+            resultsMetaMulti.addAll(re.getElementsAnnotatedWith(annotatedElemInfoElem));
+            resultsMetaAnyMulti = re.getElementsAnnotatedWithAny(annotationTypeElem, annotatedElemInfoElem);
 
-                if (resultsMeta.size() != annotatedElementInfo.expectedSize()) {
-                    failed = true;
-                    System.err.printf("Bad number of elements; expected %d, got %d%n",
-                                      annotatedElementInfo.expectedSize(), resultsMeta.size());
-                } else {
-                    for(Element element : resultsMeta) {
-                        String simpleName = element.getSimpleName().toString();
-                        if (!expectedNames.contains(simpleName) ) {
-                            failed = true;
-                            System.err.println("Name ``" + simpleName + "'' not expected.");
-                        }
+            if (!resultsMeta.isEmpty())
+                System.err.println("Results: " + resultsMeta);
+
+            if (!resultsMeta.equals(resultsMetaAny)) {
+                failed = true;
+                System.err.printf("Inconsistent Meta with vs withAny results");
+            }
+
+            if (resultsMeta.size() != annotatedElemInfo.expectedSize()) {
+                failed = true;
+                System.err.printf("Bad number of elements; expected %d, got %d%n",
+                                  annotatedElemInfo.expectedSize(), resultsMeta.size());
+            } else {
+                for(Element element : resultsMeta) {
+                    String simpleName = element.getSimpleName().toString();
+                    if (!expectedNames.contains(simpleName) ) {
+                        failed = true;
+                        System.err.println("Name ``" + simpleName + "'' not expected.");
                     }
                 }
             }
 
-            resultsBase = computeResultsBase(roundEnvironment, annotatedElementInfo.annotationName());
+            resultsBase    = computeResultsBase(re, annotationName);
+            resultsBaseAny = computeResultsBaseAny(re, annotationName);
+            try {
+                Set<Class<? extends Annotation>> tmp = new HashSet<>();
+                tmp.add(AnnotatedElementInfo.class);
+                tmp.add(Class.forName(annotationName).asSubclass(Annotation.class));
+                resultsBaseAnyMulti = re.getElementsAnnotatedWithAny(tmp);
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+
+            if (!resultsBase.equals(resultsBaseAny)) {
+                failed = true;
+                System.err.printf("Inconsistent Base with vs withAny results");
+            }
 
             if (!resultsMeta.equals(resultsBase)) {
                 failed = true;
@@ -121,29 +235,53 @@
                                    "\nbase: " + resultsBase);
             }
 
+            if (!resultsMetaAnyMulti.equals(resultsMetaMulti)) {
+                failed = true;
+                System.err.println("MetaMultAny and MetaMulti sets unequal;\n meta: " + resultsMeta +
+                                   "\nbase: " + resultsBase);
+            }
+
+            if (!resultsBaseAnyMulti.equals(resultsMetaAnyMulti)) {
+                failed = true;
+                System.err.println("BaseMulti and MetaMulti sets unequal;\n meta: " + resultsMeta +
+                                   "\nbase: " + resultsBase);
+            }
+
             if (failed) {
-                System.err.println("AnnotatedElementInfo: " + annotatedElementInfo);
+                System.err.println("AnnotatedElementInfo: " + annotatedElemInfo);
                 throw new RuntimeException();
             }
         } else {
             // If processing is over without an error, the specified
-            // elements should be empty so an empty set should be returned.
-            resultsMeta = roundEnvironment.getElementsAnnotatedWith(annotatedElementInfoElement);
-            resultsBase = roundEnvironment.getElementsAnnotatedWith(AnnotatedElementInfo.class);
-            if (!resultsMeta.isEmpty())
-                throw new RuntimeException("Nonempty resultsMeta: " + resultsMeta);
-            if (!resultsBase.isEmpty())
-                throw new RuntimeException("Nonempty resultsBase: " + resultsBase);
-
+            // elements should be empty so an empty set should be
+            // returned.
+            throwOnNonEmpty(re.getElementsAnnotatedWith(annotatedElemInfoElem), "resultsMeta");
+            throwOnNonEmpty(re.getElementsAnnotatedWithAny(annotatedElemInfoElem), "resultsMetaAny");
+            throwOnNonEmpty(re.getElementsAnnotatedWith(AnnotatedElementInfo.class),    "resultsBase");
+            throwOnNonEmpty(re.getElementsAnnotatedWithAny(Set.of(AnnotatedElementInfo.class)), "resultsBaseAny");
         }
-        return true;
     }
 
-    private Set<? extends Element> computeResultsBase(RoundEnvironment roundEnvironment, String name) {
+    private void throwOnNonEmpty(Set<? extends Element> results, String message) {
+        if (!results.isEmpty()) {
+                throw new RuntimeException("Nonempty " + message +  "\t"  + results);
+        }
+    }
+
+    private Set<? extends Element> computeResultsBase(RoundEnvironment roundEnv, String name) {
         try {
-            return roundEnvironment.
+            return roundEnv.
                 getElementsAnnotatedWith(Class.forName(name).asSubclass(Annotation.class));
-        } catch(ClassNotFoundException cnfe) {
+        } catch (ClassNotFoundException cnfe) {
+            throw new RuntimeException(cnfe);
+        }
+    }
+
+    private Set<? extends Element> computeResultsBaseAny(RoundEnvironment roundEnv, String name) {
+        try {
+            return roundEnv.
+                getElementsAnnotatedWithAny(Set.of(Class.forName(name).asSubclass(Annotation.class)));
+        } catch (ClassNotFoundException cnfe) {
             throw new RuntimeException(cnfe);
         }
     }
@@ -152,18 +290,28 @@
      * Verify non-annotation types result in
      * IllegalArgumentExceptions.
      */
-    private void testNonAnnotations(RoundEnvironment roundEnvironment) {
+    private void testNonAnnotations(RoundEnvironment roundEnv) {
+        Class objectClass = (Class)Object.class;
+        Set<? extends Element> elements;
         try {
-            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith((Class)Object.class );
+            elements = roundEnv.getElementsAnnotatedWith(objectClass);
             throw new RuntimeException("Illegal argument exception not thrown");
-        } catch(IllegalArgumentException iae) {}
+        } catch (IllegalArgumentException iae) {}
 
         try {
-            Set<? extends Element> elements =
-                roundEnvironment.getElementsAnnotatedWith(processingEnv.
-                                                          getElementUtils().
-                                                          getTypeElement("java.lang.Object") );
+            elements = roundEnv.getElementsAnnotatedWithAny(Set.of(objectClass));
             throw new RuntimeException("Illegal argument exception not thrown");
-        } catch(IllegalArgumentException iae) {}
+        } catch (IllegalArgumentException iae) {}
+
+        TypeElement objectElement = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
+        try {
+            elements = roundEnv.getElementsAnnotatedWith(objectElement);
+            throw new RuntimeException("Illegal argument exception not thrown");
+        } catch (IllegalArgumentException iae) {}
+
+        try {
+            elements = roundEnv.getElementsAnnotatedWithAny(objectElement);
+            throw new RuntimeException("Illegal argument exception not thrown");
+        } catch (IllegalArgumentException iae) {}
     }
 }