1 /* 2 * Copyright (c) 2010, 2022, 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 7097436 8006694 7196160 27 * @summary Project Coin: Annotation to reduce varargs warnings 28 * temporarily workaround combo tests are causing time out in several platforms 29 * @enablePreview 30 * @library /tools/javac/lib 31 * @modules jdk.compiler/com.sun.tools.javac.api 32 * jdk.compiler/com.sun.tools.javac.file 33 * jdk.compiler/com.sun.tools.javac.util 34 * @build combo.ComboTestHelper 35 * @run main/othervm Warn5 36 */ 37 38 import java.io.IOException; 39 import java.util.EnumSet; 40 import javax.tools.Diagnostic; 41 import javax.tools.Diagnostic.Kind; 42 import javax.tools.JavaFileObject; 43 44 import combo.ComboInstance; 45 import combo.ComboParameter; 46 import combo.ComboTask.Result; 47 import combo.ComboTestHelper; 48 49 50 public class Warn5 extends ComboInstance<Warn5> { 51 52 enum XlintOption { 53 NONE("none"), 54 ALL("all"); 55 56 String opt; 57 58 XlintOption(String opt) { 59 this.opt = opt; 60 } 61 62 String getXlintOption() { 63 return "-Xlint:" + opt; 64 } 65 } 66 67 enum TrustMe implements ComboParameter { 68 DONT_TRUST(""), 69 TRUST("@java.lang.SafeVarargs"); 70 71 String anno; 72 73 TrustMe(String anno) { 74 this.anno = anno; 75 } 76 77 @Override 78 public String expand(String optParameter) { 79 return anno; 80 } 81 } 82 83 enum SuppressLevel implements ComboParameter { 84 NONE, 85 VARARGS; 86 87 @Override 88 public String expand(String optParameter) { 89 return this == VARARGS ? 90 "@SuppressWarnings(\"varargs\")" : 91 ""; 92 } 93 } 94 95 enum ModifierKind implements ComboParameter { 96 NONE(""), 97 FINAL("final"), 98 STATIC("static"), 99 PRIVATE("private"); 100 101 String mod; 102 103 ModifierKind(String mod) { 104 this.mod = mod; 105 } 106 107 @Override 108 public String expand(String optParameter) { 109 return mod; 110 } 111 } 112 113 enum MethodKind implements ComboParameter { 114 METHOD("void m"), 115 CONSTRUCTOR("Test"); 116 117 String name; 118 119 MethodKind(String name) { 120 this.name = name; 121 } 122 123 @Override 124 public String expand(String optParameter) { 125 return name; 126 } 127 } 128 129 // Handling of varargs warnings changed in JDK 9 compared to JDK 8 130 // and then remained consistent; test 8 and then current release. 131 enum SourceLevel { 132 JDK_8("8"), 133 LATEST(Integer.toString(javax.lang.model.SourceVersion.latest().runtimeVersion().feature())); 134 135 String sourceKey; 136 137 SourceLevel(String sourceKey) { 138 this.sourceKey = sourceKey; 139 } 140 } 141 142 enum SignatureKind implements ComboParameter { 143 VARARGS_X("<X>#{NAME}(X... x)", false, true), 144 VARARGS_STRING("#{NAME}(String... x)", true, true), 145 ARRAY_X("<X>#{NAME}(X[] x)", false, false), 146 ARRAY_STRING("#{NAME}(String[] x)", true, false); 147 148 String stub; 149 boolean isReifiableArg; 150 boolean isVarargs; 151 152 SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) { 153 this.stub = stub; 154 this.isReifiableArg = isReifiableArg; 155 this.isVarargs = isVarargs; 156 } 157 158 @Override 159 public String expand(String optParameter) { 160 return stub; 161 } 162 } 163 164 // javac does not currently perform analysis of the method body 165 // with respect to the validity of the @SafeVargs annotation. If 166 // that changes, the body tests should be expanded. 167 enum BodyKind implements ComboParameter { 168 // ASSIGN("Object o = x;", true), 169 // CAST("Object o = (Object)x;", true), 170 METH("test(x);", true), 171 PRINT("System.out.println(x.toString());", false), 172 // ARRAY_ASSIGN("Object[] o = x;", true), 173 ARRAY_CAST("Object[] o = (Object[])x;", true), 174 ARRAY_METH("testArr(x);", true); 175 176 String body; 177 boolean hasAliasing; 178 179 BodyKind(String body, boolean hasAliasing) { 180 this.body = body; 181 this.hasAliasing = hasAliasing; 182 } 183 184 @Override 185 public String expand(String optParameter) { 186 return body; 187 } 188 } 189 190 enum WarningKind { 191 UNSAFE_BODY("compiler.warn.varargs.unsafe.use.varargs.param"), 192 UNSAFE_DECL("compiler.warn.unchecked.varargs.non.reifiable.type"), 193 MALFORMED_SAFEVARARGS("compiler.err.varargs.invalid.trustme.anno"), 194 REDUNDANT_SAFEVARARGS("compiler.warn.varargs.redundant.trustme.anno"); 195 196 String code; 197 198 WarningKind(String code) { 199 this.code = code; 200 } 201 } 202 203 public static void main(String[] args) { 204 new ComboTestHelper<Warn5>() 205 .withFilter(Warn5::badTestFilter) 206 .withDimension("SOURCE", (x, level) -> x.sourceLevel = level, SourceLevel.values()) 207 .withDimension("LINT", (x, lint) -> x.xlint = lint, XlintOption.values()) 208 .withDimension("TRUSTME", (x, trustme) -> x.trustMe = trustme, TrustMe.values()) 209 .withDimension("SUPPRESS", (x, suppress) -> x.suppressLevel = suppress, SuppressLevel.values()) 210 .withDimension("MOD", (x, mod) -> x.modKind = mod, ModifierKind.values()) 211 .withDimension("NAME", (x, name) -> x.methKind = name, MethodKind.values()) 212 .withDimension("SIG", (x, sig) -> x.sig = sig, SignatureKind.values()) 213 .withDimension("BODY", (x, body) -> x.body = body, BodyKind.values()) 214 .run(Warn5::new); 215 } 216 217 SourceLevel sourceLevel; 218 XlintOption xlint; 219 TrustMe trustMe; 220 SuppressLevel suppressLevel; 221 ModifierKind modKind; 222 MethodKind methKind; 223 SignatureKind sig; 224 BodyKind body; 225 226 boolean badTestFilter() { 227 return (methKind != MethodKind.CONSTRUCTOR || modKind == ModifierKind.NONE); 228 } 229 230 String template = """ 231 import com.sun.tols.javac.api.*; 232 import java.util.List; 233 class Test { 234 static void test(Object o) {} 235 static void testArr(Object[] o) {} 236 #{TRUSTME} #{SUPPRESS} #{MOD} #{SIG} { #{BODY} } 237 } 238 """; 239 240 @Override 241 public void doWork() throws IOException { 242 newCompilationTask() 243 .withOption(xlint.getXlintOption()) 244 .withOption("--release") 245 .withOption(sourceLevel.sourceKey) 246 .withSourceFromTemplate(template) 247 .analyze(this::check); 248 } 249 250 void check(Result<?> res) { 251 252 EnumSet<WarningKind> foundWarnings = EnumSet.noneOf(WarningKind.class); 253 for (Diagnostic.Kind kind : new Kind[] { Kind.ERROR, Kind.MANDATORY_WARNING, Kind.WARNING}) { 254 for (Diagnostic<? extends JavaFileObject> diag : res.diagnosticsForKind(kind)) { 255 for (WarningKind wk : WarningKind.values()) { 256 if (wk.code.equals(diag.getCode())) { 257 foundWarnings.add(wk); 258 } 259 } 260 } 261 } 262 263 EnumSet<WarningKind> expectedWarnings = 264 EnumSet.noneOf(WarningKind.class); 265 266 if (trustMe == TrustMe.TRUST && 267 suppressLevel != SuppressLevel.VARARGS && 268 xlint != XlintOption.NONE && 269 sig.isVarargs && 270 !sig.isReifiableArg && 271 body.hasAliasing && 272 (methKind == MethodKind.CONSTRUCTOR || 273 (methKind == MethodKind.METHOD && 274 modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC || 275 (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_8) > 0)))) { 276 expectedWarnings.add(WarningKind.UNSAFE_BODY); 277 } 278 279 if (trustMe == TrustMe.DONT_TRUST && 280 sig.isVarargs && 281 !sig.isReifiableArg && 282 xlint == XlintOption.ALL) { 283 expectedWarnings.add(WarningKind.UNSAFE_DECL); 284 } 285 286 if (trustMe == TrustMe.TRUST && 287 (!sig.isVarargs || 288 ((modKind == ModifierKind.NONE || 289 modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_8) <= 0 ) && 290 methKind == MethodKind.METHOD))) { 291 expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS); 292 } 293 294 if (trustMe == TrustMe.TRUST && 295 xlint != XlintOption.NONE && 296 suppressLevel != SuppressLevel.VARARGS && 297 (modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC || 298 (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_8) > 0) || 299 methKind == MethodKind.CONSTRUCTOR) && 300 sig.isVarargs && 301 sig.isReifiableArg) { 302 expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS); 303 } 304 305 if (!expectedWarnings.containsAll(foundWarnings) || 306 !foundWarnings.containsAll(expectedWarnings)) { 307 fail("invalid diagnostics for source:\n" + 308 res.compilationInfo() + 309 "\nOptions: " + xlint.getXlintOption() + 310 "\nSource Level: " + sourceLevel + 311 "\nExpected warnings: " + expectedWarnings + 312 "\nFound warnings: " + foundWarnings); 313 } 314 } 315 }