8035890: jdk8 javac -source 7 compiles test case it should not
Summary: Ensuring source level checks are performed in two additional cases related to type annotations, adding specialized error message for annotations after method type parameters.
Reviewed-by: jfranck, jjg
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Source.java Sat Mar 29 11:06:33 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Source.java Mon Mar 31 21:27:25 2014 +0200
@@ -219,6 +219,9 @@
public boolean allowTypeAnnotations() {
return compareTo(JDK1_8) >= 0;
}
+ public boolean allowAnnotationsAfterTypeParams() {
+ return compareTo(JDK1_8) >= 0;
+ }
public boolean allowRepeatedAnnotations() {
return compareTo(JDK1_8) >= 0;
}
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Sat Mar 29 11:06:33 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Mar 31 21:27:25 2014 +0200
@@ -161,6 +161,7 @@
this.allowStaticInterfaceMethods = source.allowStaticInterfaceMethods();
this.allowIntersectionTypesInCast = source.allowIntersectionTypesInCast();
this.allowTypeAnnotations = source.allowTypeAnnotations();
+ this.allowAnnotationsAfterTypeParams = source.allowAnnotationsAfterTypeParams();
this.keepDocComments = keepDocComments;
docComments = newDocCommentTable(keepDocComments, fac);
this.keepLineMap = keepLineMap;
@@ -254,6 +255,10 @@
*/
boolean allowTypeAnnotations;
+ /** Switch: should we allow annotations after the method type parameters?
+ */
+ boolean allowAnnotationsAfterTypeParams;
+
/** Switch: is "this" allowed as an identifier?
* This is needed to parse receiver types.
*/
@@ -2026,7 +2031,7 @@
/** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
*/
JCExpression creator(int newpos, List<JCExpression> typeArgs) {
- List<JCAnnotation> newAnnotations = annotationsOpt(Tag.ANNOTATION);
+ List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
switch (token.kind) {
case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
@@ -3464,6 +3469,7 @@
nextToken();
} else {
if (annosAfterParams.nonEmpty()) {
+ checkAnnotationsAfterTypeParams(annosAfterParams.head.pos);
mods.annotations = mods.annotations.appendList(annosAfterParams);
if (mods.pos == Position.NOPOS)
mods.pos = mods.annotations.head.pos;
@@ -4063,6 +4069,12 @@
allowTypeAnnotations = true;
}
}
+ void checkAnnotationsAfterTypeParams(int pos) {
+ if (!allowAnnotationsAfterTypeParams) {
+ log.error(pos, "annotations.after.type.params.not.supported.in.source", source.name);
+ allowAnnotationsAfterTypeParams = true;
+ }
+ }
/*
* a functional source tree and end position mappings
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Sat Mar 29 11:06:33 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Mar 31 21:27:25 2014 +0200
@@ -2325,6 +2325,11 @@
(use -source 8 or higher to enable type annotations)
# 0: string
+compiler.err.annotations.after.type.params.not.supported.in.source=\
+ annotations after method type parameters are not supported in -source {0}\n\
+(use -source 8 or higher to enable annotations after method type parameters)
+
+# 0: string
compiler.err.repeatable.annotations.not.supported.in.source=\
repeated annotations are not supported in -source {0}\n\
(use -source 8 or higher to enable repeated annotations)
--- a/langtools/test/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out Sat Mar 29 11:06:33 2014 -0700
+++ b/langtools/test/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out Mon Mar 31 21:27:25 2014 +0200
@@ -6,6 +6,7 @@
CantAnnotateScoping.java:56:37: compiler.err.cant.type.annotate.scoping: @TA,@TA2
CantAnnotateScoping.java:40:14: compiler.err.cant.type.annotate.scoping.1: @TA
CantAnnotateScoping.java:42:34: compiler.err.cant.type.annotate.scoping: @TA,@DA,@TA2
+CantAnnotateScoping.java:42:25: compiler.err.annotation.type.not.applicable
CantAnnotateScoping.java:44:38: compiler.err.cant.type.annotate.scoping: @TA,@DA
CantAnnotateScoping.java:44:34: compiler.err.annotation.type.not.applicable
-10 errors
\ No newline at end of file
+11 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/annotations/typeAnnotations/failures/CheckErrorsForSource7.java Mon Mar 31 21:27:25 2014 +0200
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2014, 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 8035890
+ * @summary Verify that the parser correctly checks for source level 8 on the new places where
+ * annotations can appear in 8.
+ * @run main CheckErrorsForSource7 CheckErrorsForSource7.java
+ */
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+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.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.file.JavacFileManager;
+
+/**For each place where an annotation can syntactically appear with -source 8, but not with
+ * -source 7, this test verifies that an error is correctly emitted from the parser for
+ * the annotation for -source 7. This test first gathers the occurrences of @TA from
+ * the CheckErrorsForSource7Data class below, and then repeatedly removes all these annotations
+ * except one and checks the parser reports an expected error. This is needed as as the parser
+ * typically produces only one 'insufficient source level' error for each new feature used.
+ */
+public class CheckErrorsForSource7 {
+ public static void main(String... args) throws IOException, URISyntaxException {
+ new CheckErrorsForSource7().run(args);
+ }
+
+ private void run(String... args) throws IOException, URISyntaxException {
+ //the first and only parameter must be the name of the file to be analyzed:
+ if (args.length != 1) throw new IllegalStateException("Must provide source file!");
+ File testSrc = new File(System.getProperty("test.src"));
+ File testFile = new File(testSrc, args[0]);
+ if (!testFile.canRead()) throw new IllegalStateException("Cannot read the test source");
+ JavacFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
+
+ //gather spans of the @TA annotations into typeAnnotationSpans:
+ JavacTask task = JavacTool.create().getTask(null,
+ fm,
+ null,
+ Collections.<String>emptyList(),
+ null,
+ fm.getJavaFileObjects(testFile));
+ final Trees trees = Trees.instance(task);
+ final CompilationUnitTree cut = task.parse().iterator().next();
+ final List<int[]> typeAnnotationSpans = new ArrayList<>();
+
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void visitAnnotation(AnnotationTree node, Void p) {
+ if (node.getAnnotationType().getKind() == Kind.IDENTIFIER &&
+ ((IdentifierTree) node.getAnnotationType()).getName().contentEquals("TA")) {
+ int start = (int) trees.getSourcePositions().getStartPosition(cut, node);
+ int end = (int) trees.getSourcePositions().getEndPosition(cut, node);
+ typeAnnotationSpans.add(new int[] {start, end});
+ }
+ return null;
+ }
+ }.scan(cut, null);
+
+ //sort the spans in the reverse order, to simplify removing them from the source:
+ Collections.sort(typeAnnotationSpans, new Comparator<int[]>() {
+ @Override
+ public int compare(int[] o1, int[] o2) {
+ return o2[0] - o1[0];
+ }
+ });
+
+ //verify the errors are produce correctly:
+ String originalSource = cut.getSourceFile().getCharContent(false).toString();
+
+ for (int[] toKeep : typeAnnotationSpans) {
+ //prepare updated source code by removing all the annotations except the toKeep one:
+ String updated = originalSource;
+
+ for (int[] span : typeAnnotationSpans) {
+ if (span == toKeep) continue;
+
+ updated = updated.substring(0, span[0]) + updated.substring(span[1]);
+ }
+
+ //parse and verify:
+ JavaFileObject updatedFile = new TestFO(cut.getSourceFile().toUri(), updated);
+ DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>();
+ JavacTask task2 = JavacTool.create().getTask(null,
+ fm,
+ errors,
+ Arrays.asList("-source", "7"),
+ null,
+ Arrays.asList(updatedFile));
+ task2.parse();
+
+ boolean found = false;
+
+ for (Diagnostic<? extends JavaFileObject> d : errors.getDiagnostics()) {
+ if (d.getKind() == Diagnostic.Kind.ERROR && EXPECTED_ERRORS.contains(d.getCode())) {
+ if (found) {
+ throw new IllegalStateException("More than one expected error found.");
+ }
+ found = true;
+ }
+ }
+
+ if (!found)
+ throw new IllegalStateException("Did not produce proper errors for: " + updated);
+ }
+ }
+
+ static final Set<String> EXPECTED_ERRORS = new HashSet<>(Arrays.asList(
+ "compiler.err.type.annotations.not.supported.in.source",
+ "compiler.err.annotations.after.type.params.not.supported.in.source"
+ ));
+
+ 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;
+ }
+ }
+}
+
+//data on which the source level check is verified:
+class CheckErrorsForSource7Data {
+ @Target(ElementType.TYPE_USE)
+ @interface TA { }
+
+ Object n1 = new @TA ArrayList<@TA String>();
+ Object n2 = new @TA Object() {};
+ Object [] @TA [] arr @TA[];
+ <T> @TA int @TA[] ret(Object obj) @TA[] throws @TA Exception {
+ this.<@TA String>ret(null);
+ Object c1 = new @TA String[1];
+
+ int val = obj instanceof @TA String ? ((@TA String) obj).length() : 0;
+ List<@TA ?> l;
+ return null;
+ }
+ void vararg(String @TA ... args) { }
+
+ abstract class C<@TA T extends @TA Number & @TA Runnable>
+ extends @TA ArrayList<@TA String>
+ implements java.util. @TA Comparator<@TA T> { }
+
+ interface I extends java.util. @TA Comparator<@TA String> { }
+
+}
--- a/langtools/test/tools/javac/annotations/typeAnnotations/failures/common/arrays/DeclarationAnnotation.out Sat Mar 29 11:06:33 2014 -0700
+++ b/langtools/test/tools/javac/annotations/typeAnnotations/failures/common/arrays/DeclarationAnnotation.out Mon Mar 31 21:27:25 2014 +0200
@@ -1,5 +1,5 @@
+DeclarationAnnotation.java:13:21: compiler.err.annotation.type.not.applicable
DeclarationAnnotation.java:10:21: compiler.err.annotation.type.not.applicable
DeclarationAnnotation.java:11:21: compiler.err.annotation.type.not.applicable
DeclarationAnnotation.java:12:21: compiler.err.annotation.type.not.applicable
-DeclarationAnnotation.java:13:21: compiler.err.annotation.type.not.applicable
4 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/AnnotationsAfterTypeParamsNotSupportedInSource.java Mon Mar 31 21:27:25 2014 +0200
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+// key: compiler.err.annotations.after.type.params.not.supported.in.source
+// key: compiler.warn.source.no.bootclasspath
+// options: -source 7
+
+@interface Anno { }
+
+class AnnotationsAfterTypeParamsNotSupportedInSource {
+ <T> @Anno int m() {
+ return 0;
+ }
+}