8014016: javac is too late detecting invalid annotation usage
authorjlahoda
Mon, 14 Oct 2013 22:11:09 +0200
changeset 21018 95d225149128
parent 21017 f61558e07e14
child 21019 ce43db751581
8014016: javac is too late detecting invalid annotation usage Summary: Adding new queue to Annotate for validation tasks, performing annotation validation during enter Reviewed-by: jjg, emc, jfranck
langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java
langtools/test/tools/javac/annotations/typeAnnotations/failures/CantAnnotateStaticClass.out
langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/GenerateFunctionalInterface.java
langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/GenerateSuperInterfaceProcessor.java
langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/Processor.java
langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/Source.java
--- a/langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java	Mon Oct 14 12:38:09 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java	Mon Oct 14 22:11:09 2013 +0200
@@ -50,6 +50,7 @@
 import com.sun.tools.javac.code.Symbol.MethodSymbol;
 import com.sun.tools.javac.comp.Annotate;
 import com.sun.tools.javac.comp.Annotate.Annotator;
+import com.sun.tools.javac.comp.Attr;
 import com.sun.tools.javac.comp.AttrContext;
 import com.sun.tools.javac.comp.Env;
 import com.sun.tools.javac.tree.JCTree;
@@ -95,6 +96,7 @@
     final Names names;
     final Symtab syms;
     final Annotate annotate;
+    final Attr attr;
     private final boolean typeAnnoAsserts;
 
     protected TypeAnnotations(Context context) {
@@ -103,6 +105,7 @@
         log = Log.instance(context);
         syms = Symtab.instance(context);
         annotate = Annotate.instance(context);
+        attr = Attr.instance(context);
         Options options = Options.instance(context);
         typeAnnoAsserts = options.isSet("TypeAnnotationAsserts");
     }
@@ -131,6 +134,21 @@
         } );
     }
 
+    public void validateTypeAnnotationsSignatures(final Env<AttrContext> env, final JCClassDecl tree) {
+        annotate.validate(new Annotator() { //validate annotations
+            @Override
+            public void enterAnnotation() {
+                JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile);
+
+                try {
+                    attr.validateTypeAnnotations(tree, true);
+                } finally {
+                    log.useSource(oldSource);
+                }
+            }
+        } );
+    }
+
     /**
      * This version only visits types in bodies, that is, field initializers,
      * top-level blocks, and method bodies, and should be called from Attr.
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java	Mon Oct 14 12:38:09 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java	Mon Oct 14 22:11:09 2013 +0200
@@ -92,6 +92,7 @@
     ListBuffer<Annotator> typesQ = new ListBuffer<Annotator>();
     ListBuffer<Annotator> repeatedQ = new ListBuffer<Annotator>();
     ListBuffer<Annotator> afterRepeatedQ = new ListBuffer<Annotator>();
+    ListBuffer<Annotator> validateQ = new ListBuffer<Annotator>();
 
     public void earlier(Annotator a) {
         q.prepend(a);
@@ -113,6 +114,10 @@
         afterRepeatedQ.append(a);
     }
 
+    public void validate(Annotator a) {
+        validateQ.append(a);
+    }
+
     /** Called when the Enter phase starts. */
     public void enterStart() {
         enterCount++;
@@ -140,6 +145,9 @@
             while (afterRepeatedQ.nonEmpty()) {
                 afterRepeatedQ.next().enterAnnotation();
             }
+            while (validateQ.nonEmpty()) {
+                validateQ.next().enterAnnotation();
+            }
         } finally {
             enterCount--;
         }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Oct 14 12:38:09 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Oct 14 22:11:09 2013 +0200
@@ -965,12 +965,6 @@
                 chk.validateAnnotationType(tree.restype);
                 // ensure that annotation method does not clash with members of Object/Annotation
                 chk.validateAnnotationMethod(tree.pos(), m);
-
-                if (tree.defaultValue != null) {
-                    // if default value is an annotation, check it is a well-formed
-                    // annotation value (e.g. no duplicate values, no missing values, etc.)
-                    chk.validateAnnotationTree(tree.defaultValue);
-                }
             }
 
             for (List<JCExpression> l = tree.thrown; l.nonEmpty(); l = l.tail)
@@ -1032,7 +1026,6 @@
 
             localEnv.info.scope.leave();
             result = tree.type = m.type;
-            chk.validateAnnotations(tree.mods.annotations, m);
         }
         finally {
             chk.setLint(prevLint);
@@ -1090,7 +1083,6 @@
                 }
             }
             result = tree.type = v.type;
-            chk.validateAnnotations(tree.mods.annotations, v);
         }
         finally {
             chk.setLint(prevLint);
@@ -4155,7 +4147,6 @@
         JCCompilationUnit toplevel = env.toplevel;
         try {
             annotate.flush();
-            chk.validateAnnotations(toplevel.packageAnnotations, toplevel.packge);
         } catch (CompletionFailure ex) {
             chk.completionError(toplevel.pos(), ex);
         }
@@ -4240,6 +4231,7 @@
 
                 chk.checkDeprecatedAnnotation(env.tree.pos(), c);
                 chk.checkClassOverrideEqualsAndHashIfNeeded(env.tree.pos(), c);
+                chk.checkFunctionalInterface((JCClassDecl) env.tree, c);
             } finally {
                 env.info.returnResult = prevReturnRes;
                 log.useSource(prev);
@@ -4258,9 +4250,6 @@
         JCClassDecl tree = (JCClassDecl)env.tree;
         Assert.check(c == tree.sym);
 
-        // Validate annotations
-        chk.validateAnnotations(tree.mods.annotations, c);
-
         // Validate type parameters, supertype and interfaces.
         attribStats(tree.typarams, env);
         if (!c.isAnonymous()) {
@@ -4361,7 +4350,7 @@
             typeAnnotations.organizeTypeAnnotationsBodies(tree);
 
             // Check type annotations applicability rules
-            validateTypeAnnotations(tree);
+            validateTypeAnnotations(tree, false);
         }
     }
         // where
@@ -4436,14 +4425,19 @@
         return types.capture(type);
     }
 
-    private void validateTypeAnnotations(JCTree tree) {
-        tree.accept(typeAnnotationsValidator);
+    public void validateTypeAnnotations(JCTree tree, boolean sigOnly) {
+        tree.accept(new TypeAnnotationsValidator(sigOnly));
     }
     //where
-    private final JCTree.Visitor typeAnnotationsValidator = new TreeScanner() {
-
+    private final class TypeAnnotationsValidator extends TreeScanner {
+
+        private final boolean sigOnly;
         private boolean checkAllAnnotations = false;
 
+        public TypeAnnotationsValidator(boolean sigOnly) {
+            this.sigOnly = sigOnly;
+        }
+
         public void visitAnnotation(JCAnnotation tree) {
             if (tree.hasTag(TYPE_ANNOTATION) || checkAllAnnotations) {
                 chk.validateTypeAnnotation(tree, false);
@@ -4467,12 +4461,26 @@
             if (tree.restype != null && tree.restype.type != null) {
                 validateAnnotatedType(tree.restype, tree.restype.type);
             }
-            super.visitMethodDef(tree);
+            if (sigOnly) {
+                scan(tree.mods);
+                scan(tree.restype);
+                scan(tree.typarams);
+                scan(tree.recvparam);
+                scan(tree.params);
+                scan(tree.thrown);
+            } else {
+                scan(tree.defaultValue);
+                scan(tree.body);
+            }
         }
         public void visitVarDef(final JCVariableDecl tree) {
             if (tree.sym != null && tree.sym.type != null)
                 validateAnnotatedType(tree, tree.sym.type);
-            super.visitVarDef(tree);
+            scan(tree.mods);
+            scan(tree.vartype);
+            if (!sigOnly) {
+                scan(tree.init);
+            }
         }
         public void visitTypeCast(JCTypeCast tree) {
             if (tree.clazz != null && tree.clazz.type != null)
@@ -4509,6 +4517,29 @@
             super.visitNewArray(tree);
         }
 
+        @Override
+        public void visitClassDef(JCClassDecl tree) {
+            if (sigOnly) {
+                scan(tree.mods);
+                scan(tree.typarams);
+                scan(tree.extending);
+                scan(tree.implementing);
+            }
+            for (JCTree member : tree.defs) {
+                if (member.hasTag(Tag.CLASSDEF)) {
+                    continue;
+                }
+                scan(member);
+            }
+        }
+
+        @Override
+        public void visitBlock(JCBlock tree) {
+            if (!sigOnly) {
+                scan(tree.stats);
+            }
+        }
+
         /* I would want to model this after
          * com.sun.tools.javac.comp.Check.Validator.visitSelectInternal(JCFieldAccess)
          * and override visitSelect and visitTypeApply.
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Mon Oct 14 12:38:09 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Mon Oct 14 22:11:09 2013 +0200
@@ -30,6 +30,7 @@
 import javax.tools.JavaFileManager;
 
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Attribute.Compound;
 import com.sun.tools.javac.jvm.*;
 import com.sun.tools.javac.tree.*;
 import com.sun.tools.javac.util.*;
@@ -1951,7 +1952,7 @@
      *                      for errors.
      *  @param m            The overriding method.
      */
-    void checkOverride(JCTree tree, MethodSymbol m) {
+    void checkOverride(JCMethodDecl tree, MethodSymbol m) {
         ClassSymbol origin = (ClassSymbol)m.owner;
         if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name))
             if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
@@ -1967,6 +1968,17 @@
                 checkOverride(tree, t2, origin, m);
             }
         }
+
+        if (m.attribute(syms.overrideType.tsym) != null && !isOverrider(m)) {
+            DiagnosticPosition pos = tree.pos();
+            for (JCAnnotation a : tree.getModifiers().annotations) {
+                if (a.annotationType.type.tsym == syms.overrideType.tsym) {
+                    pos = a.pos();
+                    break;
+                }
+            }
+            log.error(pos, "method.does.not.override.superclass");
+        }
     }
 
     void checkOverride(JCTree tree, Type site, ClassSymbol origin, MethodSymbol m) {
@@ -2725,20 +2737,11 @@
         if (!annotationApplicable(a, s))
             log.error(a.pos(), "annotation.type.not.applicable");
 
-        if (a.annotationType.type.tsym == syms.overrideType.tsym) {
-            if (!isOverrider(s))
-                log.error(a.pos(), "method.does.not.override.superclass");
-        }
-
         if (a.annotationType.type.tsym == syms.functionalInterfaceType.tsym) {
             if (s.kind != TYP) {
                 log.error(a.pos(), "bad.functional.intf.anno");
-            } else {
-                try {
-                    types.findDescriptorSymbol((TypeSymbol)s);
-                } catch (Types.FunctionDescriptorLookupError ex) {
-                    log.error(a.pos(), "bad.functional.intf.anno.1", ex.getDiagnostic());
-                }
+            } else if (!s.isInterface() || (s.flags() & ANNOTATION) != 0) {
+                log.error(a.pos(), "bad.functional.intf.anno.1", diags.fragment("not.a.functional.intf", s));
             }
         }
     }
@@ -2953,7 +2956,7 @@
         return false;
     }
 
-    /** Is the annotation applicable to type annotations? */
+    /** Is the annotation applicable to types? */
     protected boolean isTypeAnnotation(JCAnnotation a, boolean isTypeParameter) {
         Attribute.Compound atTarget =
             a.annotationType.type.tsym.attribute(syms.annotationTargetType.tsym);
@@ -3507,4 +3510,23 @@
     public Warner convertWarner(DiagnosticPosition pos, Type found, Type expected) {
         return new ConversionWarner(pos, "unchecked.assign", found, expected);
     }
+
+    public void checkFunctionalInterface(JCClassDecl tree, ClassSymbol cs) {
+        Compound functionalType = cs.attribute(syms.functionalInterfaceType.tsym);
+
+        if (functionalType != null) {
+            try {
+                types.findDescriptorSymbol((TypeSymbol)cs);
+            } catch (Types.FunctionDescriptorLookupError ex) {
+                DiagnosticPosition pos = tree.pos();
+                for (JCAnnotation a : tree.getModifiers().annotations) {
+                    if (a.annotationType.type.tsym == syms.functionalInterfaceType.tsym) {
+                        pos = a.pos();
+                        break;
+                    }
+                }
+                log.error(pos, "bad.functional.intf.anno.1", ex.getDiagnostic());
+            }
+        }
+    }
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Mon Oct 14 12:38:09 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Mon Oct 14 22:11:09 2013 +0200
@@ -871,6 +871,18 @@
                     }
                 }
             });
+
+        annotate.validate(new Annotate.Annotator() { //validate annotations
+            @Override
+            public void enterAnnotation() {
+                JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile);
+                try {
+                    chk.validateAnnotations(annotations, s);
+                } finally {
+                    log.useSource(prev);
+                }
+            }
+        });
     }
 
     /**
@@ -951,6 +963,19 @@
                     }
                 }
             });
+        annotate.validate(new Annotate.Annotator() { //validate annotations
+            @Override
+            public void enterAnnotation() {
+                JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile);
+                try {
+                    // if default value is an annotation, check it is a well-formed
+                    // annotation value (e.g. no duplicate values, no missing values, etc.)
+                    chk.validateAnnotationTree(defaultValue);
+                } finally {
+                    log.useSource(prev);
+                }
+            }
+        });
     }
 
     /** Enter a default value for an attribute method. */
@@ -1157,15 +1182,17 @@
         if (wasFirst) {
             try {
                 while (halfcompleted.nonEmpty()) {
-                    finish(halfcompleted.next());
+                    Env<AttrContext> toFinish = halfcompleted.next();
+                    finish(toFinish);
+                    if (allowTypeAnnos) {
+                        typeAnnotations.organizeTypeAnnotationsSignatures(toFinish, (JCClassDecl)toFinish.tree);
+                        typeAnnotations.validateTypeAnnotationsSignatures(toFinish, (JCClassDecl)toFinish.tree);
+                    }
                 }
             } finally {
                 isFirst = true;
             }
         }
-        if (allowTypeAnnos) {
-            typeAnnotations.organizeTypeAnnotationsSignatures(env, tree);
-        }
     }
 
     /*
--- a/langtools/test/tools/javac/annotations/typeAnnotations/failures/CantAnnotateStaticClass.out	Mon Oct 14 12:38:09 2013 -0700
+++ b/langtools/test/tools/javac/annotations/typeAnnotations/failures/CantAnnotateStaticClass.out	Mon Oct 14 22:11:09 2013 +0200
@@ -1,10 +1,10 @@
 CantAnnotateStaticClass.java:22:20: compiler.err.cant.annotate.static.class
 CantAnnotateStaticClass.java:23:13: compiler.err.cant.annotate.static.class
 CantAnnotateStaticClass.java:24:29: compiler.err.cant.annotate.static.class
-CantAnnotateStaticClass.java:26:29: compiler.err.cant.annotate.static.class
 CantAnnotateStaticClass.java:29:26: compiler.err.cant.annotate.static.class
 CantAnnotateStaticClass.java:30:9: compiler.err.cant.annotate.static.class
 CantAnnotateStaticClass.java:31:35: compiler.err.cant.annotate.static.class
+CantAnnotateStaticClass.java:26:29: compiler.err.cant.annotate.static.class
 - compiler.note.unchecked.filename: CantAnnotateStaticClass.java
 - compiler.note.unchecked.recompile
-7 errors
+7 errors
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/GenerateFunctionalInterface.java	Mon Oct 14 22:11:09 2013 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8014016
+ * @summary Ensure that an annotation processor can generate a super-interface
+ *          which will make the current interface functional
+ * @build GenerateSuperInterfaceProcessor
+ * @compile -processor GenerateSuperInterfaceProcessor GenerateFunctionalInterface.java
+ */
+
+import java.lang.FunctionalInterface;
+
+@FunctionalInterface
+@Generate(fileName="SuperInterface.java", content="interface SuperInterface { public void run(); }")
+interface GenerateFunctionalInterface extends SuperInterface {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/GenerateSuperInterfaceProcessor.java	Mon Oct 14 22:11:09 2013 +0200
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.tools.javac.util.Assert;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+@SupportedAnnotationTypes("*")
+public class GenerateSuperInterfaceProcessor extends AbstractProcessor {
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        for (Element el : roundEnv.getElementsAnnotatedWith(Generate.class)) {
+            Generate g = el.getAnnotation(Generate.class);
+
+            Assert.checkNonNull(g);
+
+            try (OutputStream out =
+                    processingEnv.getFiler().createSourceFile(g.fileName()).openOutputStream()) {
+                out.write(g.content().getBytes());
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        return false;
+    }
+
+}
+
+@interface Generate {
+    String fileName();
+    String content();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/Processor.java	Mon Oct 14 22:11:09 2013 +0200
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TreeScanner;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.util.Assert;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+
+@SupportedAnnotationTypes("*")
+public class Processor extends AbstractProcessor {
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        throw new IllegalStateException("Should not be invoked.");
+    }
+
+    public static void main(String... args) throws IOException, URISyntaxException {
+        if (args.length != 1) throw new IllegalStateException("Must provide class name!");
+        String testContent = null;
+        List<File> sourcePath = new ArrayList<>();
+        for (String sourcePaths : System.getProperty("test.src.path").split(":")) {
+            sourcePath.add(new File(sourcePaths));
+        }
+        JavacFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
+        for (File sp : sourcePath) {
+            File inp = new File(sp, args[0]);
+
+            if (inp.canRead()) {
+                testContent = fm.getRegularFile(inp).getCharContent(true).toString();
+            }
+        }
+        if (testContent == null) throw new IllegalStateException();
+        DiagnosticListener<JavaFileObject> devNull = new DiagnosticListener<JavaFileObject>() {
+            @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) { }
+        };
+        JavaFileObject testFile = new TestFO(new URI("mem://" + args[0]), testContent);
+        JavacTask task = JavacTool.create().getTask(null,
+                                                    new TestFM(fm),
+                                                    devNull,
+                                                    Arrays.asList("-Xjcov"),
+                                                    null,
+                                                    Arrays.asList(testFile));
+        final Trees trees = Trees.instance(task);
+        final CompilationUnitTree cut = task.parse().iterator().next();
+        task.analyze();
+
+        final List<int[]> annotations = new ArrayList<>();
+
+        new TreeScanner<Void, Void>() {
+            @Override
+            public Void visitAnnotation(AnnotationTree node, Void p) {
+                int endPos = (int) trees.getSourcePositions().getEndPosition(cut, node);
+
+                Assert.check(endPos >= 0);
+
+                annotations.add(new int[] {(int) trees.getSourcePositions().getStartPosition(cut, node), endPos});
+                return super.visitAnnotation(node, p);
+            }
+        }.scan(cut.getTypeDecls().get(0), null);
+
+        Collections.sort(annotations, new Comparator<int[]>() {
+            @Override public int compare(int[] o1, int[] o2) {
+                return o2[0] - o1[0];
+            }
+        });
+
+        for (final int[] annotation : annotations) {
+            StringBuilder updatedContent = new StringBuilder();
+            int last = testContent.length();
+
+            for (int[] toRemove : annotations) {
+                if (toRemove == annotation) continue;
+                updatedContent.insert(0, testContent.substring(toRemove[1], last));
+                last = toRemove[0];
+            }
+
+            updatedContent.insert(0, testContent.substring(0, last));
+
+            JavaFileObject updatedFile = new TestFO(new URI("mem://" + args[0]), updatedContent.toString());
+            JavacTask testTask = JavacTool.create().getTask(null,
+                                                            new TestFM(fm),
+                                                            devNull,
+                                                            Arrays.asList("-processor", "Processor"),
+                                                            null,
+                                                            Arrays.asList(updatedFile));
+
+            try {
+                testTask.analyze();
+            } catch (Throwable e) {
+                System.out.println("error while processing:");
+                System.out.println(updatedContent);
+                throw e;
+            }
+        }
+    }
+
+    private static final class TestFO extends SimpleJavaFileObject {
+        private final String content;
+        public TestFO(URI uri, String content) {
+            super(uri, Kind.SOURCE);
+            this.content = content;
+        }
+
+        @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+            return content;
+        }
+
+        @Override public boolean isNameCompatible(String simpleName, Kind kind) {
+            return true;
+        }
+    }
+
+    private static final class TestFM extends ForwardingJavaFileManager<JavaFileManager> {
+
+        public TestFM(JavaFileManager fileManager) {
+            super(fileManager);
+        }
+
+        @Override
+        public boolean isSameFile(FileObject a, FileObject b) {
+            return a.equals(b);
+        }
+
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/errors/StopOnInapplicableAnnotations/Source.java	Mon Oct 14 22:11:09 2013 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8014016
+ * @summary Verify that annotation processors do not get invalid annotations
+ * @build Processor
+ * @run main Processor Source.java
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@OnMethod
+@OnField
+class Class<@OnType @OnMethod @OnField T extends @OnType @OnMethod @OnField CharSequence & @OnType @OnMethod @OnField Runnable> extends @OnType @OnMethod @OnField Object {
+
+    @OnType
+    @OnTypeUse
+    @OnField
+    private void testMethod(@OnType @OnField @OnMethod int i) { }
+
+    @OnType
+    @OnMethod
+    private java.lang.@OnType @OnMethod @OnField String testField;
+}
+
+@Target(ElementType.TYPE)
+@interface OnType {}
+
+@Target(ElementType.METHOD)
+@interface OnMethod {}
+
+@Target(ElementType.TYPE_USE)
+@interface OnTypeUse {}
+
+@Target(ElementType.FIELD)
+@interface OnField {}