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
--- 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 {}