Warn5.java revision 891:3e30c95da3c6
1/* 2 * Copyright (c) 2010, 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 6993978 27 * @summary Project Coin: Annotation to reduce varargs warnings 28 * @author mcimadamore 29 * @run main Warn5 30 */ 31import com.sun.source.util.JavacTask; 32import com.sun.tools.javac.api.JavacTool; 33import java.net.URI; 34import java.util.ArrayList; 35import java.util.Arrays; 36import javax.tools.Diagnostic; 37import javax.tools.JavaCompiler; 38import javax.tools.JavaFileObject; 39import javax.tools.SimpleJavaFileObject; 40import javax.tools.StandardJavaFileManager; 41import javax.tools.ToolProvider; 42 43public class Warn5 { 44 45 enum XlintOption { 46 NONE("none"), 47 ALL("all"); 48 49 String opt; 50 51 XlintOption(String opt) { 52 this.opt = opt; 53 } 54 55 String getXlintOption() { 56 return "-Xlint:" + opt; 57 } 58 } 59 60 enum TrustMe { 61 DONT_TRUST(""), 62 TRUST("@java.lang.SafeVarargs"); 63 64 String anno; 65 66 TrustMe(String anno) { 67 this.anno = anno; 68 } 69 } 70 71 enum SuppressLevel { 72 NONE, 73 VARARGS; 74 75 String getSuppressAnno() { 76 return this == VARARGS ? 77 "@SuppressWarnings(\"varargs\")" : 78 ""; 79 } 80 } 81 82 enum ModifierKind { 83 NONE(""), 84 FINAL("final"), 85 STATIC("static"); 86 87 String mod; 88 89 ModifierKind(String mod) { 90 this.mod = mod; 91 } 92 } 93 94 enum MethodKind { 95 METHOD("void m"), 96 CONSTRUCTOR("Test"); 97 98 99 String name; 100 101 MethodKind(String name) { 102 this.name = name; 103 } 104 } 105 106 enum SourceLevel { 107 JDK_6("6"), 108 JDK_7("7"); 109 110 String sourceKey; 111 112 SourceLevel(String sourceKey) { 113 this.sourceKey = sourceKey; 114 } 115 } 116 117 enum SignatureKind { 118 VARARGS_X("#K <X>#N(X... x)", false, true), 119 VARARGS_STRING("#K #N(String... x)", true, true), 120 ARRAY_X("#K <X>#N(X[] x)", false, false), 121 ARRAY_STRING("#K #N(String[] x)", true, false); 122 123 String stub; 124 boolean isReifiableArg; 125 boolean isVarargs; 126 127 SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) { 128 this.stub = stub; 129 this.isReifiableArg = isReifiableArg; 130 this.isVarargs = isVarargs; 131 } 132 133 String getSignature(ModifierKind modKind, MethodKind methKind) { 134 return methKind != MethodKind.CONSTRUCTOR ? 135 stub.replace("#K", modKind.mod).replace("#N", methKind.name) : 136 stub.replace("#K", "").replace("#N", methKind.name); 137 } 138 } 139 140 enum BodyKind { 141 ASSIGN("Object o = x;", true), 142 CAST("Object o = (Object)x;", true), 143 METH("test(x);", true), 144 PRINT("System.out.println(x.toString());", false), 145 ARRAY_ASSIGN("Object[] o = x;", true), 146 ARRAY_CAST("Object[] o = (Object[])x;", true), 147 ARRAY_METH("testArr(x);", true); 148 149 String body; 150 boolean hasAliasing; 151 152 BodyKind(String body, boolean hasAliasing) { 153 this.body = body; 154 this.hasAliasing = hasAliasing; 155 } 156 } 157 158 static class JavaSource extends SimpleJavaFileObject { 159 160 String template = "import com.sun.tools.javac.api.*;\n" + 161 "import java.util.List;\n" + 162 "class Test {\n" + 163 " static void test(Object o) {}\n" + 164 " static void testArr(Object[] o) {}\n" + 165 " #T \n #S #M { #B }\n" + 166 "}\n"; 167 168 String source; 169 170 public JavaSource(TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind, 171 MethodKind methKind, SignatureKind meth, BodyKind body) { 172 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 173 source = template.replace("#T", trustMe.anno). 174 replace("#S", suppressLevel.getSuppressAnno()). 175 replace("#M", meth.getSignature(modKind, methKind)). 176 replace("#B", body.body); 177 } 178 179 @Override 180 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 181 return source; 182 } 183 } 184 185 public static void main(String... args) throws Exception { 186 for (SourceLevel sourceLevel : SourceLevel.values()) { 187 for (XlintOption xlint : XlintOption.values()) { 188 for (TrustMe trustMe : TrustMe.values()) { 189 for (SuppressLevel suppressLevel : SuppressLevel.values()) { 190 for (ModifierKind modKind : ModifierKind.values()) { 191 for (MethodKind methKind : MethodKind.values()) { 192 for (SignatureKind sig : SignatureKind.values()) { 193 for (BodyKind body : BodyKind.values()) { 194 test(sourceLevel, 195 xlint, 196 trustMe, 197 suppressLevel, 198 modKind, 199 methKind, 200 sig, 201 body); 202 } 203 } 204 } 205 } 206 } 207 } 208 } 209 } 210 } 211 212 // Create a single file manager and reuse it for each compile to save time. 213 static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); 214 215 static void test(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, 216 ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) throws Exception { 217 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 218 JavaSource source = new JavaSource(trustMe, suppressLevel, modKind, methKind, sig, body); 219 DiagnosticChecker dc = new DiagnosticChecker(); 220 JavacTask ct = (JavacTask)tool.getTask(null, fm, dc, 221 Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source)); 222 ct.analyze(); 223 check(sourceLevel, dc, source, xlint, trustMe, 224 suppressLevel, modKind, methKind, sig, body); 225 } 226 227 static void check(SourceLevel sourceLevel, DiagnosticChecker dc, JavaSource source, 228 XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind, 229 MethodKind methKind, SignatureKind meth, BodyKind body) { 230 231 boolean hasPotentiallyUnsafeBody = sourceLevel == SourceLevel.JDK_7 && 232 trustMe == TrustMe.TRUST && 233 suppressLevel != SuppressLevel.VARARGS && 234 xlint != XlintOption.NONE && 235 meth.isVarargs && !meth.isReifiableArg && body.hasAliasing && 236 (methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE)); 237 238 boolean hasPotentiallyPollutingDecl = sourceLevel == SourceLevel.JDK_7 && 239 trustMe == TrustMe.DONT_TRUST && 240 meth.isVarargs && 241 !meth.isReifiableArg && 242 xlint == XlintOption.ALL; 243 244 boolean hasMalformedAnnoInDecl = sourceLevel == SourceLevel.JDK_7 && 245 trustMe == TrustMe.TRUST && 246 (!meth.isVarargs || 247 (modKind == ModifierKind.NONE && methKind == MethodKind.METHOD)); 248 249 boolean hasRedundantAnnoInDecl = sourceLevel == SourceLevel.JDK_7 && 250 trustMe == TrustMe.TRUST && 251 xlint != XlintOption.NONE && 252 suppressLevel != SuppressLevel.VARARGS && 253 (modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) && 254 meth.isVarargs && 255 meth.isReifiableArg; 256 257 if (hasPotentiallyUnsafeBody != dc.hasPotentiallyUnsafeBody || 258 hasPotentiallyPollutingDecl != dc.hasPotentiallyPollutingDecl || 259 hasMalformedAnnoInDecl != dc.hasMalformedAnnoInDecl || 260 hasRedundantAnnoInDecl != dc.hasRedundantAnnoInDecl) { 261 throw new Error("invalid diagnostics for source:\n" + 262 source.getCharContent(true) + 263 "\nOptions: " + xlint.getXlintOption() + 264 "\nExpected potentially unsafe body warning: " + hasPotentiallyUnsafeBody + 265 "\nExpected potentially polluting decl warning: " + hasPotentiallyPollutingDecl + 266 "\nExpected malformed anno error: " + hasMalformedAnnoInDecl + 267 "\nExpected redundant anno warning: " + hasRedundantAnnoInDecl + 268 "\nFound potentially unsafe body warning: " + dc.hasPotentiallyUnsafeBody + 269 "\nFound potentially polluting decl warning: " + dc.hasPotentiallyPollutingDecl + 270 "\nFound malformed anno error: " + dc.hasMalformedAnnoInDecl + 271 "\nFound redundant anno warning: " + dc.hasRedundantAnnoInDecl); 272 } 273 } 274 275 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 276 277 boolean hasPotentiallyUnsafeBody = false; 278 boolean hasPotentiallyPollutingDecl = false; 279 boolean hasMalformedAnnoInDecl = false; 280 boolean hasRedundantAnnoInDecl = false; 281 282 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 283 if (diagnostic.getKind() == Diagnostic.Kind.WARNING) { 284 if (diagnostic.getCode().contains("unsafe.use.varargs.param")) { 285 hasPotentiallyUnsafeBody = true; 286 } else if (diagnostic.getCode().contains("redundant.trustme")) { 287 hasRedundantAnnoInDecl = true; 288 } 289 } else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING && 290 diagnostic.getCode().contains("varargs.non.reifiable.type")) { 291 hasPotentiallyPollutingDecl = true; 292 } else if (diagnostic.getKind() == Diagnostic.Kind.ERROR && 293 diagnostic.getCode().contains("invalid.trustme")) { 294 hasMalformedAnnoInDecl = true; 295 } 296 } 297 } 298} 299