CheckErrorsForSource7.java revision 2933:49d207bf704d
1/* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/**@test 25 * @bug 8035890 26 * @summary Verify that the parser correctly checks for source level 8 on the new places where 27 * annotations can appear in 8. 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.file 30 * @run main CheckErrorsForSource7 CheckErrorsForSource7.java 31 */ 32import java.io.File; 33import java.io.IOException; 34import java.lang.annotation.ElementType; 35import java.lang.annotation.Target; 36import java.net.URI; 37import java.net.URISyntaxException; 38import java.util.ArrayList; 39import java.util.Arrays; 40import java.util.Collections; 41import java.util.Comparator; 42import java.util.HashSet; 43import java.util.List; 44import java.util.Set; 45import javax.tools.Diagnostic; 46import javax.tools.DiagnosticCollector; 47import javax.tools.JavaFileObject; 48import javax.tools.SimpleJavaFileObject; 49import com.sun.source.tree.AnnotationTree; 50import com.sun.source.tree.CompilationUnitTree; 51import com.sun.source.tree.IdentifierTree; 52import com.sun.source.tree.Tree.Kind; 53import com.sun.source.util.JavacTask; 54import com.sun.source.util.TreePathScanner; 55import com.sun.source.util.Trees; 56import com.sun.tools.javac.api.JavacTool; 57import com.sun.tools.javac.file.JavacFileManager; 58 59/**For each place where an annotation can syntactically appear with -source 8, but not with 60 * -source 7, this test verifies that an error is correctly emitted from the parser for 61 * the annotation for -source 7. This test first gathers the occurrences of @TA from 62 * the CheckErrorsForSource7Data class below, and then repeatedly removes all these annotations 63 * except one and checks the parser reports an expected error. This is needed as as the parser 64 * typically produces only one 'insufficient source level' error for each new feature used. 65 */ 66public class CheckErrorsForSource7 { 67 public static void main(String... args) throws IOException, URISyntaxException { 68 new CheckErrorsForSource7().run(args); 69 } 70 71 private void run(String... args) throws IOException, URISyntaxException { 72 //the first and only parameter must be the name of the file to be analyzed: 73 if (args.length != 1) throw new IllegalStateException("Must provide source file!"); 74 File testSrc = new File(System.getProperty("test.src")); 75 File testFile = new File(testSrc, args[0]); 76 if (!testFile.canRead()) throw new IllegalStateException("Cannot read the test source"); 77 try (JavacFileManager fm = JavacTool.create().getStandardFileManager(null, null, null)) { 78 79 //gather spans of the @TA annotations into typeAnnotationSpans: 80 JavacTask task = JavacTool.create().getTask(null, 81 fm, 82 null, 83 Collections.<String>emptyList(), 84 null, 85 fm.getJavaFileObjects(testFile)); 86 final Trees trees = Trees.instance(task); 87 final CompilationUnitTree cut = task.parse().iterator().next(); 88 final List<int[]> typeAnnotationSpans = new ArrayList<>(); 89 90 new TreePathScanner<Void, Void>() { 91 @Override 92 public Void visitAnnotation(AnnotationTree node, Void p) { 93 if (node.getAnnotationType().getKind() == Kind.IDENTIFIER && 94 ((IdentifierTree) node.getAnnotationType()).getName().contentEquals("TA")) { 95 int start = (int) trees.getSourcePositions().getStartPosition(cut, node); 96 int end = (int) trees.getSourcePositions().getEndPosition(cut, node); 97 typeAnnotationSpans.add(new int[] {start, end}); 98 } 99 return null; 100 } 101 }.scan(cut, null); 102 103 //sort the spans in the reverse order, to simplify removing them from the source: 104 Collections.sort(typeAnnotationSpans, new Comparator<int[]>() { 105 @Override 106 public int compare(int[] o1, int[] o2) { 107 return o2[0] - o1[0]; 108 } 109 }); 110 111 //verify the errors are produce correctly: 112 String originalSource = cut.getSourceFile().getCharContent(false).toString(); 113 114 for (int[] toKeep : typeAnnotationSpans) { 115 //prepare updated source code by removing all the annotations except the toKeep one: 116 String updated = originalSource; 117 118 for (int[] span : typeAnnotationSpans) { 119 if (span == toKeep) continue; 120 121 updated = updated.substring(0, span[0]) + updated.substring(span[1]); 122 } 123 124 //parse and verify: 125 JavaFileObject updatedFile = new TestFO(cut.getSourceFile().toUri(), updated); 126 DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); 127 JavacTask task2 = JavacTool.create().getTask(null, 128 fm, 129 errors, 130 Arrays.asList("-source", "7"), 131 null, 132 Arrays.asList(updatedFile)); 133 task2.parse(); 134 135 boolean found = false; 136 137 for (Diagnostic<? extends JavaFileObject> d : errors.getDiagnostics()) { 138 if (d.getKind() == Diagnostic.Kind.ERROR && EXPECTED_ERRORS.contains(d.getCode())) { 139 if (found) { 140 throw new IllegalStateException("More than one expected error found."); 141 } 142 found = true; 143 } 144 } 145 146 if (!found) 147 throw new IllegalStateException("Did not produce proper errors for: " + updated); 148 } 149 } 150 } 151 152 static final Set<String> EXPECTED_ERRORS = new HashSet<>(Arrays.asList( 153 "compiler.err.type.annotations.not.supported.in.source", 154 "compiler.err.annotations.after.type.params.not.supported.in.source" 155 )); 156 157 class TestFO extends SimpleJavaFileObject { 158 private final String content; 159 public TestFO(URI uri, String content) { 160 super(uri, Kind.SOURCE); 161 this.content = content; 162 } 163 164 @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 165 return content; 166 } 167 168 @Override public boolean isNameCompatible(String simpleName, Kind kind) { 169 return true; 170 } 171 } 172} 173 174//data on which the source level check is verified: 175class CheckErrorsForSource7Data { 176 @Target(ElementType.TYPE_USE) 177 @interface TA { } 178 179 Object n1 = new @TA ArrayList<@TA String>(); 180 Object n2 = new @TA Object() {}; 181 Object [] @TA [] arr @TA[]; 182 <T> @TA int @TA[] ret(Object obj) @TA[] throws @TA Exception { 183 this.<@TA String>ret(null); 184 Object c1 = new @TA String[1]; 185 186 int val = obj instanceof @TA String ? ((@TA String) obj).length() : 0; 187 List<@TA ?> l; 188 return null; 189 } 190 void vararg(String @TA ... args) { } 191 192 abstract class C<@TA T extends @TA Number & @TA Runnable> 193 extends @TA ArrayList<@TA String> 194 implements java.util. @TA Comparator<@TA T> { } 195 196 interface I extends java.util. @TA Comparator<@TA String> { } 197 198} 199