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 6945418 6993978 27 * @summary Project Coin: Simplified Varargs Method Invocation 28 * @author mcimadamore 29 * @run main Warn4 30 */ 31 import com.sun.source.util.JavacTask; 32 import com.sun.tools.javac.api.JavacTool; 33 import java.net.URI; 34 import java.util.Arrays; 35 import java.util.Set; 36 import java.util.HashSet; 37 import javax.tools.Diagnostic; 38 import javax.tools.JavaCompiler; 39 import javax.tools.JavaFileObject; 40 import javax.tools.SimpleJavaFileObject; 41 import javax.tools.StandardJavaFileManager; 42 import javax.tools.ToolProvider; 43 44 public class Warn4 { 45 46 final static Warning[] error = null; 47 final static Warning[] none = new Warning[] {}; 48 final static Warning[] vararg = new Warning[] { Warning.VARARGS }; 49 final static Warning[] unchecked = new Warning[] { Warning.UNCHECKED }; 50 final static Warning[] both = new Warning[] { Warning.VARARGS, Warning.UNCHECKED }; 51 52 enum Warning { 53 UNCHECKED("generic.array.creation"), 54 VARARGS("varargs.non.reifiable.type"); 55 56 String key; 57 58 Warning(String key) { 59 this.key = key; 60 } 61 62 boolean isSuppressed(TrustMe trustMe, SourceLevel source, SuppressLevel suppressLevelClient, 63 SuppressLevel suppressLevelDecl, ModifierKind modKind) { 64 switch(this) { 65 case VARARGS: 66 return source == SourceLevel.JDK_6 || 67 suppressLevelDecl == SuppressLevel.UNCHECKED || 68 trustMe == TrustMe.TRUST; 69 case UNCHECKED: 70 return suppressLevelClient == SuppressLevel.UNCHECKED || 71 (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE && source == SourceLevel.JDK_7); 72 } 73 74 SuppressLevel supLev = this == VARARGS ? 75 suppressLevelDecl : 76 suppressLevelClient; 77 return supLev == SuppressLevel.UNCHECKED || 78 (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE); 79 } 80 } 81 82 enum SourceLevel { 83 JDK_6("6"), 84 JDK_7("7"); 85 86 String sourceKey; 87 88 SourceLevel(String sourceKey) { 89 this.sourceKey = sourceKey; 90 } 91 } 92 93 enum TrustMe { 94 DONT_TRUST(""), 95 TRUST("@java.lang.SafeVarargs"); 96 97 String anno; 98 99 TrustMe(String anno) { 100 this.anno = anno; 101 } 102 } 103 104 enum ModifierKind { 105 NONE(" "), 106 FINAL("final "), 107 STATIC("static "); 108 109 String mod; 110 111 ModifierKind(String mod) { 112 this.mod = mod; 113 } 114 } 115 116 enum SuppressLevel { 117 NONE(""), 118 UNCHECKED("unchecked"); 119 120 String lint; 121 122 SuppressLevel(String lint) { 123 this.lint = lint; 124 } 125 126 String getSuppressAnno() { 127 return "@SuppressWarnings(\"" + lint + "\")"; 128 } 129 } 130 131 enum Signature { 132 UNBOUND("void #name(List<?>#arity arg) { #body }", 133 new Warning[][] {none, none, none, none, error}), 134 INVARIANT_TVAR("<Z> void #name(List<Z>#arity arg) { #body }", 135 new Warning[][] {both, both, error, both, error}), 136 TVAR("<Z> void #name(Z#arity arg) { #body }", 137 new Warning[][] {both, both, both, both, vararg}), 138 INVARIANT("void #name(List<String>#arity arg) { #body }", 139 new Warning[][] {error, error, error, both, error}), 140 UNPARAMETERIZED("void #name(String#arity arg) { #body }", 141 new Warning[][] {error, error, error, error, none}); 142 143 String template; 144 Warning[][] warnings; 145 146 Signature(String template, Warning[][] warnings) { 147 this.template = template; 148 this.warnings = warnings; 149 } 150 151 boolean isApplicableTo(Signature other) { 152 return warnings[other.ordinal()] != null; 153 } 154 155 boolean giveUnchecked(Signature other) { 156 return warnings[other.ordinal()] == unchecked || 157 warnings[other.ordinal()] == both; 158 } 159 160 boolean giveVarargs(Signature other) { 161 return warnings[other.ordinal()] == vararg || 162 warnings[other.ordinal()] == both; 163 } 164 } 165 166 public static void main(String... args) throws Exception { 167 for (SourceLevel sourceLevel : SourceLevel.values()) { 168 for (TrustMe trustMe : TrustMe.values()) { 169 for (SuppressLevel suppressLevelClient : SuppressLevel.values()) { 170 for (SuppressLevel suppressLevelDecl : SuppressLevel.values()) { 171 for (ModifierKind modKind : ModifierKind.values()) { 172 for (Signature vararg_meth : Signature.values()) { 173 for (Signature client_meth : Signature.values()) { 174 if (vararg_meth.isApplicableTo(client_meth)) { 175 test(sourceLevel, 176 trustMe, 177 suppressLevelClient, 178 suppressLevelDecl, 179 modKind, 180 vararg_meth, 181 client_meth); 182 } 183 } 184 } 185 } 186 } 187 } 188 } 189 } 190 } 191 192 // Create a single file manager and reuse it for each compile to save time. 193 static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); 194 195 static void test(SourceLevel sourceLevel, TrustMe trustMe, SuppressLevel suppressLevelClient, 196 SuppressLevel suppressLevelDecl, ModifierKind modKind, Signature vararg_meth, Signature client_meth) throws Exception { 197 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 198 JavaSource source = new JavaSource(trustMe, suppressLevelClient, suppressLevelDecl, modKind, vararg_meth, client_meth); 199 DiagnosticChecker dc = new DiagnosticChecker(); 200 JavacTask ct = (JavacTask)tool.getTask(null, fm, dc, 201 Arrays.asList("-Xlint:unchecked", "-source", sourceLevel.sourceKey), 202 null, Arrays.asList(source)); 203 ct.generate(); //to get mandatory notes 204 check(dc.warnings, sourceLevel, 205 new boolean[] {vararg_meth.giveUnchecked(client_meth), 206 vararg_meth.giveVarargs(client_meth)}, 207 source, trustMe, suppressLevelClient, suppressLevelDecl, modKind); 208 } 209 210 static void check(Set<Warning> warnings, SourceLevel sourceLevel, boolean[] warnArr, JavaSource source, 211 TrustMe trustMe, SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl, ModifierKind modKind) { 212 boolean badOutput = false; 213 for (Warning wkind : Warning.values()) { 214 boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel, 215 suppressLevelClient, suppressLevelDecl, modKind); 216 System.out.println("SUPPRESSED = " + isSuppressed); 217 badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) != warnings.contains(wkind); 218 } 219 if (badOutput) { 220 throw new Error("invalid diagnostics for source:\n" + 221 source.getCharContent(true) + 222 "\nExpected unchecked warning: " + warnArr[0] + 223 "\nExpected unsafe vararg warning: " + warnArr[1] + 224 "\nWarnings: " + warnings + 225 "\nSource level: " + sourceLevel); 226 } 227 } 228 229 static class JavaSource extends SimpleJavaFileObject { 230 231 String source; 232 233 public JavaSource(TrustMe trustMe, SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl, 234 ModifierKind modKind, Signature vararg_meth, Signature client_meth) { 235 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 236 String meth1 = vararg_meth.template.replace("#arity", "..."); 237 meth1 = meth1.replace("#name", "m"); 238 meth1 = meth1.replace("#body", ""); 239 meth1 = trustMe.anno + "\n" + suppressLevelDecl.getSuppressAnno() + modKind.mod + meth1; 240 String meth2 = client_meth.template.replace("#arity", ""); 241 meth2 = meth2.replace("#name", "test"); 242 meth2 = meth2.replace("#body", "m(arg);"); 243 meth2 = suppressLevelClient.getSuppressAnno() + meth2; 244 source = "import java.util.List;\n" + 245 "class Test {\n" + meth1 + 246 "\n" + meth2 + "\n}\n"; 247 } 248 249 @Override 250 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 251 return source; 252 } 253 } 254 255 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 256 257 Set<Warning> warnings = new HashSet<>(); 258 259 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 260 if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING || 261 diagnostic.getKind() == Diagnostic.Kind.WARNING) { 262 if (diagnostic.getCode().contains(Warning.VARARGS.key)) { 263 warnings.add(Warning.VARARGS); 264 } else if(diagnostic.getCode().contains(Warning.UNCHECKED.key)) { 265 warnings.add(Warning.UNCHECKED); 266 } 267 } 268 } 269 } 270 }