VerifyErroneousAnnotationsAttributed.java revision 2702:b9daa6475f12
1/* 2 * Copyright (c) 2014, 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/* 25 * @test 26 * @bug 8029161 8029376 27 */ 28 29import com.sun.source.tree.CompilationUnitTree; 30import com.sun.source.tree.IdentifierTree; 31import com.sun.source.tree.MemberSelectTree; 32import com.sun.source.util.JavacTask; 33import com.sun.source.util.TreePathScanner; 34import com.sun.source.util.Trees; 35import com.sun.tools.javac.api.JavacTool; 36import java.io.IOException; 37import java.net.URI; 38import java.net.URISyntaxException; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.List; 42import javax.lang.model.element.Element; 43import javax.lang.model.element.ElementKind; 44import javax.lang.model.type.TypeKind; 45import javax.tools.Diagnostic; 46import javax.tools.DiagnosticListener; 47import javax.tools.JavaFileManager; 48import javax.tools.JavaFileObject; 49import javax.tools.SimpleJavaFileObject; 50 51public class VerifyErroneousAnnotationsAttributed { 52 public static void main(String... args) throws IOException, URISyntaxException { 53 new VerifyErroneousAnnotationsAttributed().run(); 54 } 55 56 void run() throws IOException { 57 try { 58 int failCount = 0; 59 for (String ann : generateAnnotations()) { 60 String code = PATTERN.replace("PLACEHOLDER", ann); 61 try { 62 validate(code); 63 } catch (Throwable t) { 64 System.out.println("Failed for: "); 65 System.out.println(code); 66 t.printStackTrace(System.out); 67 failCount++; 68 } 69 } 70 71 if (failCount > 0) { 72 throw new IllegalStateException("failed sub-tests: " + failCount); 73 } 74 } finally { 75 fm.close(); 76 } 77 } 78 79 List<String> generateAnnotations() { 80 List<String> result = new ArrayList<>(); 81 82 result.addAll(Kind.ANNOTATION.generateValue(2, true)); 83 result.addAll(Kind.ANNOTATION.generateValue(2, false)); 84 85 return result; 86 } 87 88 enum Kind { 89 INT("i", "ValueInt") { 90 @Override public List<String> generateValue(int depth, boolean valid) { 91 if (valid) { 92 return Arrays.asList("INT_VALUE"); 93 } else { 94 return Arrays.asList("BROKEN_INT_VALUE"); 95 } 96 } 97 }, 98 ANNOTATION("a", "ValueAnnotation") { 99 @Override public List<String> generateValue(int depth, boolean valid) { 100 String ad = "@Annotation" + depth + (valid ? "" : "Unknown"); 101 102 if (depth <= 0) { 103 return Arrays.asList(ad); 104 } 105 106 List<String> result = new ArrayList<>(); 107 final Kind[][] generateKindCombinations = new Kind[][] { 108 new Kind[] {Kind.INT}, 109 new Kind[] {Kind.ANNOTATION}, 110 new Kind[] {Kind.INT, Kind.ANNOTATION} 111 }; 112 113 for (boolean generateAssignment : new boolean[] {false, true}) { 114 for (boolean generateValid : new boolean[] {false, true}) { 115 for (Kind[] generateKinds : generateKindCombinations) { 116 if (generateKinds.length > 1 && generateValid && !generateAssignment) { 117 //skip: the same code is generated for generateValid == false. 118 continue; 119 } 120 List<String> attributes = generateAttributes(generateAssignment, 121 generateValid, depth, generateKinds); 122 String annotation; 123 if (generateAssignment) { 124 annotation = ad; 125 } else { 126 annotation = ad + generateKinds[0].annotationWithValueSuffix; 127 } 128 for (String attr : attributes) { 129 result.add(annotation + "(" + attr + ")"); 130 } 131 } 132 } 133 } 134 135 return result; 136 } 137 138 List<String> generateAttributes(boolean generateAssignment, boolean valid, int depth, 139 Kind... kinds) { 140 List<List<String>> combinations = new ArrayList<>(); 141 142 for (boolean subValid : new boolean[] {false, true}) { 143 for (Kind k : kinds) { 144 String prefix; 145 146 if (generateAssignment) { 147 if (valid) { 148 prefix = k.validAttributeName + "="; 149 } else { 150 prefix = "invalid" + k.validAttributeName + "="; 151 } 152 } else { 153 prefix = ""; 154 } 155 156 List<String> combination = new ArrayList<>(); 157 158 combinations.add(combination); 159 160 for (String val : k.generateValue(depth - 1, subValid)) { 161 combination.add(prefix + val); 162 } 163 } 164 } 165 166 List<String> result = new ArrayList<>(); 167 168 combine(combinations, new StringBuilder(), result); 169 170 return result; 171 } 172 173 void combine(List<List<String>> combinations, StringBuilder current, List<String> to) { 174 if (combinations.isEmpty()) { 175 to.add(current.toString()); 176 return ; 177 } 178 179 int currLen = current.length(); 180 181 for (String str : combinations.get(0)) { 182 if (current.length() > 0) current.append(", "); 183 current.append(str); 184 185 combine(combinations.subList(1, combinations.size()), current, to); 186 187 current.delete(currLen, current.length()); 188 } 189 } 190 }; 191 String validAttributeName; 192 String annotationWithValueSuffix; 193 194 private Kind(String validAttributeName, String annotationWithValueSuffix) { 195 this.validAttributeName = validAttributeName; 196 this.annotationWithValueSuffix = annotationWithValueSuffix; 197 } 198 199 public abstract List<String> generateValue(int depth, boolean valid); 200 201 } 202 203 private static final String PATTERN = 204 "public class Test {\n" + 205 " public static final int INT_VALUE = 1;\n" + 206 " @interface Annotation0 {}\n" + 207 " @interface Annotation1 {int i() default 0; Annotation0 a() default @Annotation0; }\n" + 208 " @interface Annotation2 {int i() default 0; Annotation1 a() default @Annotation1; }\n" + 209 " @interface Annotation1ValueInt {int value() default 0; }\n" + 210 " @interface Annotation2ValueInt {int value() default 0; }\n" + 211 " @interface Annotation1ValueAnnotation {Annotation0 a() default @Annotation0; }\n" + 212 " @interface Annotation2ValueAnnotation {Annotation1 a() default @Annotation1; }\n" + 213 " PLACEHOLDER\n" + 214 " private void test() { }\n" + 215 "}"; 216 217 static final class TestCase { 218 final String code; 219 final boolean valid; 220 221 public TestCase(String code, boolean valid) { 222 this.code = code; 223 this.valid = valid; 224 } 225 226 } 227 228 final JavacTool tool = JavacTool.create(); 229 final JavaFileManager fm = tool.getStandardFileManager(null, null, null); 230 final DiagnosticListener<JavaFileObject> devNull = new DiagnosticListener<JavaFileObject>() { 231 @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) {} 232 }; 233 234 void validate(String code) throws IOException, URISyntaxException { 235 JavacTask task = tool.getTask(null, 236 fm, 237 devNull, 238 Arrays.asList("-XDshouldStopPolicy=FLOW"), 239 null, 240 Arrays.asList(new MyFileObject(code))); 241 242 final Trees trees = Trees.instance(task); 243 final CompilationUnitTree cut = task.parse().iterator().next(); 244 task.analyze(); 245 246 //ensure all the annotation attributes are annotated meaningfully 247 //all the attributes in the test file should contain either an identifier 248 //or a select, so only checking those for a reasonable Element/Symbol. 249 new TreePathScanner<Void, Void>() { 250 @Override 251 public Void visitIdentifier(IdentifierTree node, Void p) { 252 verifyAttributedMeaningfully(); 253 return super.visitIdentifier(node, p); 254 } 255 @Override 256 public Void visitMemberSelect(MemberSelectTree node, Void p) { 257 verifyAttributedMeaningfully(); 258 return super.visitMemberSelect(node, p); 259 } 260 private void verifyAttributedMeaningfully() { 261 Element el = trees.getElement(getCurrentPath()); 262 263 if (el == null || el.getKind() == ElementKind.OTHER || 264 el.asType().getKind() == TypeKind.OTHER) { 265 throw new IllegalStateException("Not attributed properly: " + 266 getCurrentPath().getParentPath().getLeaf()); 267 } 268 } 269 }.scan(cut, null); 270 } 271 static class MyFileObject extends SimpleJavaFileObject { 272 private final String text; 273 public MyFileObject(String text) { 274 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 275 this.text = text; 276 } 277 @Override 278 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 279 return text; 280 } 281 } 282} 283