1/* 2 * Copyright (c) 2016, 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 8145471 27 * @summary javac changes for enhanced deprecation 28 * @library /tools/lib 29 * @modules jdk.compiler/com.sun.tools.javac.api 30 * @modules jdk.compiler/com.sun.tools.javac.main 31 * @build toolbox.JavacTask toolbox.TestRunner toolbox.ToolBox 32 * @run main Removal 33 */ 34 35import java.io.IOException; 36import java.nio.file.Files; 37import java.nio.file.Path; 38import java.nio.file.Paths; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.EnumMap; 42import java.util.List; 43import java.util.Map; 44import java.util.stream.Collectors; 45 46import toolbox.JavacTask; 47import toolbox.Task.Expect; 48import toolbox.Task.OutputKind; 49import toolbox.TestRunner; 50import toolbox.ToolBox; 51 52/* 53 * From JEP 277, JDK-8085614 54 * 55 * use site | API declaration site 56 * context | not dep. ord. dep. term. dep. 57 * +---------------------------------- 58 * not dep. | N W W 59 * | 60 * ord. dep. | N N (2) W (4) 61 * | 62 * term. dep. | N N (3) W (5) 63 */ 64 65public class Removal extends TestRunner { 66 public static void main(String... args) throws Exception { 67 Removal r = new Removal(); 68 r.runTests(m -> new Object[] { Paths.get(m.getName()) }); 69 r.report(); 70 } 71 72 private final ToolBox tb = new ToolBox(); 73 private final Path libSrc = Paths.get("lib").resolve("src"); 74 private final Path libClasses = Paths.get("lib").resolve("classes"); 75 int testCount = 0; 76 77 /** 78 * Options that may be used during compilation. 79 */ 80 enum Options { 81 DEFAULT(), 82 XLINT_DEPRECATED("-Xlint:deprecation"), 83 XLINT_NO_REMOVAL("-Xlint:-removal"); 84 85 Options(String... opts) { 86 this.opts = Arrays.asList(opts); 87 } 88 89 final List<String> opts; 90 } 91 92 /** 93 * The kind of deprecation. 94 */ 95 enum DeprKind { 96 NONE("", null), 97 DEPRECATED("@Deprecated ", "compiler.warn.has.been.deprecated"), 98 REMOVAL("@Deprecated(forRemoval=true) ", "compiler.warn.has.been.deprecated.for.removal"); 99 DeprKind(String anno, String warn) { 100 this.anno = anno; 101 this.warn = warn; 102 } 103 final String anno; 104 final String warn; 105 } 106 107 final String[] lib = { 108 "package lib; public class Class {\n" 109 + " public static void method() { }\n" 110 + " @Deprecated public static void depMethod() { }\n" 111 + " @Deprecated(forRemoval=true) public static void remMethod() { }\n" 112 + " public static int field;\n" 113 + " @Deprecated public static int depField;\n" 114 + " @Deprecated(forRemoval=true) public static int remField;\n" 115 + "}", 116 "package lib; @Deprecated public class DepClass { }", 117 "package lib; @Deprecated(forRemoval=true) public class RemClass { }" 118 }; 119 120 /** 121 * The kind of declaration to be referenced at the use site. 122 */ 123 enum RefKind { 124 CLASS("lib.%s c;", "Class", "DepClass", "RemClass"), 125 METHOD("{ lib.Class.%s(); }", "method", "depMethod", "remMethod"), 126 FIELD("int i = lib.Class.%s;", "field", "depField", "remField"); 127 128 RefKind(String template, String def, String dep, String rem) { 129 fragments.put(DeprKind.NONE, String.format(template, def)); 130 fragments.put(DeprKind.DEPRECATED, String.format(template, dep)); 131 fragments.put(DeprKind.REMOVAL, String.format(template, rem)); 132 } 133 134 String getFragment(DeprKind k) { 135 return fragments.get(k); 136 } 137 138 private final Map<DeprKind, String> fragments = new EnumMap<>(DeprKind.class); 139 } 140 141 /** 142 * Get source code for a reference to a possibly-deprecated item declared in a library. 143 * @param refKind the kind of element (class, method, field) being referenced 144 * @param declDeprKind the kind of deprecation on the declaration of the item being referenced 145 * @param useDeprKind the kind of deprecation enclosing the use site 146 * @return 147 */ 148 static String getSource(RefKind refKind, DeprKind declDeprKind, DeprKind useDeprKind) { 149 return "package p; " 150 + useDeprKind.anno 151 + "class Class { " 152 + refKind.getFragment(declDeprKind) 153 + " }"; 154 } 155 156 private static final String NO_OUTPUT = null; 157 158 public Removal() throws IOException { 159 super(System.err); 160 initLib(); 161 } 162 163 void initLib() throws IOException { 164 tb.writeJavaFiles(libSrc, lib); 165 166 new JavacTask(tb) 167 .outdir(Files.createDirectories(libClasses)) 168 .files(tb.findJavaFiles(libSrc)) 169 .run() 170 .writeAll(); 171 } 172 173 void report() { 174 out.println(testCount + " test cases"); 175 } 176 177 /* 178 * Declaration site: not deprecated; use site: not deprecated 179 * Options: default 180 * Expect: no warnings 181 */ 182 @Test 183 public void test_DeclNone_UseNone(Path base) throws IOException { 184 for (RefKind rk : RefKind.values()) { 185 test(base, 186 getSource(rk, DeprKind.NONE, DeprKind.NONE), 187 Options.DEFAULT, 188 NO_OUTPUT); 189 } 190 } 191 192 /* 193 * Declaration site: not deprecated; use site: deprecated 194 * Options: default 195 * Expect: no warnings 196 */ 197 @Test 198 public void test_DeclNone_UseDeprecated(Path base) throws IOException { 199 for (RefKind rk : RefKind.values()) { 200 test(base, 201 getSource(rk, DeprKind.NONE, DeprKind.DEPRECATED), 202 Options.DEFAULT, 203 NO_OUTPUT); 204 } 205 } 206 207 /* 208 * Declaration site: not deprecated; use site: deprecated for removal 209 * Options: default 210 * Expect: no warnings 211 */ 212 @Test 213 public void test_DeclNone_UseRemoval(Path base) throws IOException { 214 for (RefKind rk : RefKind.values()) { 215 test(base, 216 getSource(rk, DeprKind.NONE, DeprKind.REMOVAL), 217 Options.DEFAULT, 218 NO_OUTPUT); 219 } 220 } 221 222 /* 223 * Declaration site: deprecated; use site: not deprecated 224 * Options: default 225 * Expect: deprecated note 226 */ 227 @Test 228 public void test_DeclDeprecated_UseNone_Default(Path base) throws IOException { 229 for (RefKind rk : RefKind.values()) { 230 test(base, 231 getSource(rk, DeprKind.DEPRECATED, DeprKind.NONE), 232 Options.DEFAULT, 233 "compiler.note.deprecated.filename: Class.java"); 234 } 235 } 236 237 /* 238 * Declaration site: deprecated; use site: not deprecated 239 * Options: -Xlint:deprecation 240 * Expect: deprecated warning 241 */ 242 @Test 243 public void test_DeclDeprecated_UseNone_XlintDep(Path base) throws IOException { 244 for (RefKind rk : RefKind.values()) { 245 String error = "<unset>"; 246 switch (rk) { 247 case CLASS: 248 error = "Class.java:1:29: compiler.warn.has.been.deprecated: lib.DepClass, lib"; 249 break; 250 251 case METHOD: 252 error = "Class.java:1:37: compiler.warn.has.been.deprecated: depMethod(), lib.Class"; 253 break; 254 255 case FIELD: 256 error = "Class.java:1:43: compiler.warn.has.been.deprecated: depField, lib.Class"; 257 break; 258 } 259 260 test(base, 261 getSource(rk, DeprKind.DEPRECATED, DeprKind.NONE), 262 Options.XLINT_DEPRECATED, 263 error); 264 } 265 } 266 267 /* 268 * Declaration site: deprecated; use site: deprecated 269 * Options: default 270 * Expect: no warnings 271 */ 272 @Test 273 public void test_DeclDeprecated_UseDeprecated(Path base) throws IOException { 274 for (RefKind rk : RefKind.values()) { 275 test(base, 276 getSource(rk, DeprKind.DEPRECATED, DeprKind.DEPRECATED), 277 Options.DEFAULT, 278 NO_OUTPUT); 279 } 280 } 281 282 /* 283 * Declaration site: deprecated; use site: deprecated for removal 284 * Options: default 285 * Expect: no warnings 286 */ 287 @Test 288 public void test_DeclDeprecated_UseRemoval(Path base) throws IOException { 289 for (RefKind rk : RefKind.values()) { 290 test(base, 291 getSource(rk, DeprKind.DEPRECATED, DeprKind.REMOVAL), 292 Options.DEFAULT, 293 NO_OUTPUT); 294 } 295 } 296 297 /* 298 * Declaration site: deprecated for removal; use site: not deprecated 299 * Options: default 300 * Expect: removal warning 301 */ 302 @Test 303 public void test_DeclRemoval_UseNone_Default(Path base) throws IOException { 304 for (RefKind rk : RefKind.values()) { 305 String error = "<unset>"; 306 switch (rk) { 307 case CLASS: 308 error = "Class.java:1:29: compiler.warn.has.been.deprecated.for.removal: lib.RemClass, lib"; 309 break; 310 311 case METHOD: 312 error = "Class.java:1:37: compiler.warn.has.been.deprecated.for.removal: remMethod(), lib.Class"; 313 break; 314 315 case FIELD: 316 error = "Class.java:1:43: compiler.warn.has.been.deprecated.for.removal: remField, lib.Class"; 317 break; 318 } 319 320 test(base, 321 getSource(rk, DeprKind.REMOVAL, DeprKind.NONE), 322 Options.DEFAULT, 323 error); 324 } 325 } 326 327 /* 328 * Declaration site: deprecated for removal; use site: not deprecated 329 * Options: default, @SuppressWarnings("removal") 330 * Expect: removal warning 331 */ 332 @Test 333 public void test_DeclRemoval_UseNone_SuppressRemoval(Path base) throws IOException { 334 for (RefKind rk : RefKind.values()) { 335 String source = 336 getSource(rk, DeprKind.REMOVAL, DeprKind.NONE) 337 .replace("class Class", "@SuppressWarnings(\"removal\") class Class"); 338 339 test(base, 340 source, 341 Options.DEFAULT, 342 null); 343 } 344 } 345 346 /* 347 * Declaration site: deprecated for removal; use site: not deprecated 348 * Options: -Xlint:-removal 349 * Expect: removal note 350 */ 351 @Test 352 public void test_DeclRemoval_UseNone_XlintNoRemoval(Path base) throws IOException { 353 for (RefKind rk : RefKind.values()) { 354 test(base, 355 getSource(rk, DeprKind.REMOVAL, DeprKind.NONE), 356 Options.XLINT_NO_REMOVAL, 357 "compiler.note.removal.filename: Class.java"); 358 } 359 } 360 361 /* 362 * Declaration site: deprecated for removal; use site: deprecated 363 * Options: default 364 * Expect: removal warning 365 */ 366 @Test 367 public void test_DeclRemoval_UseDeprecated_Default(Path base) throws IOException { 368 for (RefKind rk : RefKind.values()) { 369 String error = "<unset>"; 370 switch (rk) { 371 case CLASS: 372 error = "Class.java:1:41: compiler.warn.has.been.deprecated.for.removal: lib.RemClass, lib"; 373 break; 374 375 case METHOD: 376 error = "Class.java:1:49: compiler.warn.has.been.deprecated.for.removal: remMethod(), lib.Class"; 377 break; 378 379 case FIELD: 380 error = "Class.java:1:55: compiler.warn.has.been.deprecated.for.removal: remField, lib.Class"; 381 break; 382 } 383 384 test(base, 385 getSource(rk, DeprKind.REMOVAL, DeprKind.DEPRECATED), 386 Options.DEFAULT, 387 error); 388 } 389 } 390 391 /* 392 * Declaration site: deprecated for removal; use site: deprecated 393 * Options: -Xlint:-removal 394 * Expect: removal note 395 */ 396 @Test 397 public void test_DeclRemoval_UseDeprecated_XlintNoRemoval(Path base) throws IOException { 398 for (RefKind rk : RefKind.values()) { 399 test(base, 400 getSource(rk, DeprKind.REMOVAL, DeprKind.DEPRECATED), 401 Options.XLINT_NO_REMOVAL, 402 "compiler.note.removal.filename: Class.java"); 403 } 404 } 405 406 /* 407 * Declaration site: deprecated for removal; use site: deprecated for removal 408 * Options: default 409 * Expect: removal warning 410 */ 411 @Test 412 public void test_DeclRemoval_UseRemoval_Default(Path base) throws IOException { 413 for (RefKind rk : RefKind.values()) { 414 String error = "<unset>"; 415 switch (rk) { 416 case CLASS: 417 error = "Class.java:1:58: compiler.warn.has.been.deprecated.for.removal: lib.RemClass, lib"; 418 break; 419 420 case METHOD: 421 error = "Class.java:1:66: compiler.warn.has.been.deprecated.for.removal: remMethod(), lib.Class"; 422 break; 423 424 case FIELD: 425 error = "Class.java:1:72: compiler.warn.has.been.deprecated.for.removal: remField, lib.Class"; 426 break; 427 } 428 429 test(base, 430 getSource(rk, DeprKind.REMOVAL, DeprKind.REMOVAL), 431 Options.DEFAULT, 432 error); 433 } 434 } 435 436 /* 437 * Declaration site: deprecated for removal; use site: deprecated for removal 438 * Options: -Xlint:-removal 439 * Expect: removal note 440 */ 441 @Test 442 public void test_DeclRemoval_UseRemoval_XlintNoRemoval(Path base) throws IOException { 443 for (RefKind rk : RefKind.values()) { 444 test(base, 445 getSource(rk, DeprKind.REMOVAL, DeprKind.REMOVAL), 446 Options.XLINT_NO_REMOVAL, 447 "compiler.note.removal.filename: Class.java"); 448 } 449 } 450 451 /* 452 * Additional special case: 453 * there should not be any warnings for any reference in a type-import statement. 454 */ 455 @Test 456 public void test_UseImports(Path base) throws IOException { 457 String source = 458 "import lib.Class;\n" 459 + "import lib.DepClass;\n" 460 + "import lib.RemClass;\n" 461 + "class C { }"; 462 for (Options o : Options.values()) { 463 test(base, source, o, NO_OUTPUT); 464 } 465 } 466 467 /** 468 * Compile source code with given options, and check for expected output. 469 * The compilation is done twice, first against the library in source form, 470 * and then again, against the compiled library. 471 * @param base base working directory 472 * @param source the source code to be compiled 473 * @param options the options for the compilation 474 * @param expectText the expected output, or NO_OUTPUT, if none expected. 475 * @throws IOException if an error occurs during the compilation 476 */ 477 private void test(Path base, String source, Options options, String expectText) throws IOException { 478 test(base.resolve("lib-source"), libSrc, source, options, expectText); 479 test(base.resolve("lib-classes"), libClasses, source, options, expectText); 480 } 481 482 /** 483 * Compile source code with given options against a given version of the library, 484 * and check for expected output. 485 * @param base base working directory 486 * @param lib the directory containing the library, in either source or compiled form 487 * @param source the source code to be compiled 488 * @param options the options for the compilation 489 * @param expectText the expected output, or NO_OUTPUT, if none expected. 490 * @throws IOException if an error occurs during the compilation 491 */ 492 private void test(Path base, Path lib, String source, Options options, String expectText) 493 throws IOException { 494 Expect expect = (expectText != null && expectText.contains("compiler.warn.")) ? Expect.FAIL : Expect.SUCCESS; 495 test(base, lib, source, options.opts, expect, expectText); 496 } 497 498 /** 499 * Compile source code with given options against a given version of the library, 500 * and check for expected exit code and expected output. 501 * @param base base working directory 502 * @param lib the directory containing the library, in either source or compiled form 503 * @param source the source code to be compiled 504 * @param options the options for the compilation 505 * @param expect the expected outcome of the compilation 506 * @param expectText the expected output, or NO_OUTPUT, if none expected. 507 * @throws IOException if an error occurs during the compilation 508 */ 509 private void test(Path base, Path lib, String source, List<String> options, 510 Expect expect, String expectText) throws IOException { 511 testCount++; 512 513 Path src = base.resolve("src"); 514 Path classes = Files.createDirectories(base.resolve("classes")); 515 tb.writeJavaFiles(src, source); 516 517 List<String> allOptions = new ArrayList<>(); 518 allOptions.add("-XDrawDiagnostics"); 519 allOptions.add("-Werror"); 520 allOptions.addAll(options); 521 522 out.println("Source: " + source); 523 out.println("Classpath: " + lib); 524 out.println("Options: " + options.stream().collect(Collectors.joining(" "))); 525 526 String log = new JavacTask(tb) 527 .outdir(classes) 528 .classpath(lib) // use classpath for libSrc or libClasses 529 .files(tb.findJavaFiles(src)) 530 .options(allOptions.toArray(new String[0])) 531 .run(expect) 532 .writeAll() 533 .getOutput(OutputKind.DIRECT); 534 535 if (expectText == null) { 536 if (!log.trim().isEmpty()) 537 error("Unexpected text found: >>>" + log + "<<<"); 538 } else { 539 if (!log.contains(expectText)) 540 error("expected text not found: >>>" + expectText + "<<<"); 541 } 542 } 543} 544 545