test/tools/javac/diags/CheckResourceKeys.java

Wed, 27 Apr 2016 01:34:52 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:34:52 +0800
changeset 0
959103a6100f
child 2525
2eb010b6cb22
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/langtools/
changeset: 2573:53ca196be1ae
tag: jdk8u25-b17

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation.
aoqi@0 8 *
aoqi@0 9 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 12 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 13 * accompanied this code).
aoqi@0 14 *
aoqi@0 15 * You should have received a copy of the GNU General Public License version
aoqi@0 16 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 18 *
aoqi@0 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 20 * or visit www.oracle.com if you need additional information or have any
aoqi@0 21 * questions.
aoqi@0 22 */
aoqi@0 23
aoqi@0 24 /*
aoqi@0 25 * @test
aoqi@0 26 * @bug 6964768 6964461 6964469 6964487 6964460 6964481 6980021
aoqi@0 27 * @summary need test program to validate javac resource bundles
aoqi@0 28 */
aoqi@0 29
aoqi@0 30 import java.io.*;
aoqi@0 31 import java.util.*;
aoqi@0 32 import javax.tools.*;
aoqi@0 33 import com.sun.tools.classfile.*;
aoqi@0 34
aoqi@0 35 /**
aoqi@0 36 * Compare string constants in javac classes against keys in javac resource bundles.
aoqi@0 37 */
aoqi@0 38 public class CheckResourceKeys {
aoqi@0 39 /**
aoqi@0 40 * Main program.
aoqi@0 41 * Options:
aoqi@0 42 * -finddeadkeys
aoqi@0 43 * look for keys in resource bundles that are no longer required
aoqi@0 44 * -findmissingkeys
aoqi@0 45 * look for keys in resource bundles that are missing
aoqi@0 46 *
aoqi@0 47 * @throws Exception if invoked by jtreg and errors occur
aoqi@0 48 */
aoqi@0 49 public static void main(String... args) throws Exception {
aoqi@0 50 CheckResourceKeys c = new CheckResourceKeys();
aoqi@0 51 if (c.run(args))
aoqi@0 52 return;
aoqi@0 53
aoqi@0 54 if (is_jtreg())
aoqi@0 55 throw new Exception(c.errors + " errors occurred");
aoqi@0 56 else
aoqi@0 57 System.exit(1);
aoqi@0 58 }
aoqi@0 59
aoqi@0 60 static boolean is_jtreg() {
aoqi@0 61 return (System.getProperty("test.src") != null);
aoqi@0 62 }
aoqi@0 63
aoqi@0 64 /**
aoqi@0 65 * Main entry point.
aoqi@0 66 */
aoqi@0 67 boolean run(String... args) throws Exception {
aoqi@0 68 boolean findDeadKeys = false;
aoqi@0 69 boolean findMissingKeys = false;
aoqi@0 70
aoqi@0 71 if (args.length == 0) {
aoqi@0 72 if (is_jtreg()) {
aoqi@0 73 findDeadKeys = true;
aoqi@0 74 findMissingKeys = true;
aoqi@0 75 } else {
aoqi@0 76 System.err.println("Usage: java CheckResourceKeys <options>");
aoqi@0 77 System.err.println("where options include");
aoqi@0 78 System.err.println(" -finddeadkeys find keys in resource bundles which are no longer required");
aoqi@0 79 System.err.println(" -findmissingkeys find keys in resource bundles that are required but missing");
aoqi@0 80 return true;
aoqi@0 81 }
aoqi@0 82 } else {
aoqi@0 83 for (String arg: args) {
aoqi@0 84 if (arg.equalsIgnoreCase("-finddeadkeys"))
aoqi@0 85 findDeadKeys = true;
aoqi@0 86 else if (arg.equalsIgnoreCase("-findmissingkeys"))
aoqi@0 87 findMissingKeys = true;
aoqi@0 88 else
aoqi@0 89 error("bad option: " + arg);
aoqi@0 90 }
aoqi@0 91 }
aoqi@0 92
aoqi@0 93 if (errors > 0)
aoqi@0 94 return false;
aoqi@0 95
aoqi@0 96 Set<String> codeStrings = getCodeStrings();
aoqi@0 97 Set<String> resourceKeys = getResourceKeys();
aoqi@0 98
aoqi@0 99 if (findDeadKeys)
aoqi@0 100 findDeadKeys(codeStrings, resourceKeys);
aoqi@0 101
aoqi@0 102 if (findMissingKeys)
aoqi@0 103 findMissingKeys(codeStrings, resourceKeys);
aoqi@0 104
aoqi@0 105 return (errors == 0);
aoqi@0 106 }
aoqi@0 107
aoqi@0 108 /**
aoqi@0 109 * Find keys in resource bundles which are probably no longer required.
aoqi@0 110 * A key is probably required if there is a string fragment in the code
aoqi@0 111 * that is part of the resource key, or if the key is well-known
aoqi@0 112 * according to various pragmatic rules.
aoqi@0 113 */
aoqi@0 114 void findDeadKeys(Set<String> codeStrings, Set<String> resourceKeys) {
aoqi@0 115 String[] prefixes = {
aoqi@0 116 "compiler.err.", "compiler.warn.", "compiler.note.", "compiler.misc.",
aoqi@0 117 "javac."
aoqi@0 118 };
aoqi@0 119 for (String rk: resourceKeys) {
aoqi@0 120 // some keys are used directly, without a prefix.
aoqi@0 121 if (codeStrings.contains(rk))
aoqi@0 122 continue;
aoqi@0 123
aoqi@0 124 // remove standard prefix
aoqi@0 125 String s = null;
aoqi@0 126 for (int i = 0; i < prefixes.length && s == null; i++) {
aoqi@0 127 if (rk.startsWith(prefixes[i])) {
aoqi@0 128 s = rk.substring(prefixes[i].length());
aoqi@0 129 }
aoqi@0 130 }
aoqi@0 131 if (s == null) {
aoqi@0 132 error("Resource key does not start with a standard prefix: " + rk);
aoqi@0 133 continue;
aoqi@0 134 }
aoqi@0 135
aoqi@0 136 if (codeStrings.contains(s))
aoqi@0 137 continue;
aoqi@0 138
aoqi@0 139 // keys ending in .1 are often synthesized
aoqi@0 140 if (s.endsWith(".1") && codeStrings.contains(s.substring(0, s.length() - 2)))
aoqi@0 141 continue;
aoqi@0 142
aoqi@0 143 // verbose keys are generated by ClassReader.printVerbose
aoqi@0 144 if (s.startsWith("verbose.") && codeStrings.contains(s.substring(8)))
aoqi@0 145 continue;
aoqi@0 146
aoqi@0 147 // mandatory warning messages are synthesized with no characteristic substring
aoqi@0 148 if (isMandatoryWarningString(s))
aoqi@0 149 continue;
aoqi@0 150
aoqi@0 151 // check known (valid) exceptions
aoqi@0 152 if (knownRequired.contains(rk))
aoqi@0 153 continue;
aoqi@0 154
aoqi@0 155 // check known suspects
aoqi@0 156 if (needToInvestigate.contains(rk))
aoqi@0 157 continue;
aoqi@0 158
aoqi@0 159 error("Resource key not found in code: " + rk);
aoqi@0 160 }
aoqi@0 161 }
aoqi@0 162
aoqi@0 163 /**
aoqi@0 164 * The keys for mandatory warning messages are all synthesized and do not
aoqi@0 165 * have a significant recognizable substring to look for.
aoqi@0 166 */
aoqi@0 167 private boolean isMandatoryWarningString(String s) {
aoqi@0 168 String[] bases = { "deprecated", "unchecked", "varargs", "sunapi" };
aoqi@0 169 String[] tails = { ".filename", ".filename.additional", ".plural", ".plural.additional", ".recompile" };
aoqi@0 170 for (String b: bases) {
aoqi@0 171 if (s.startsWith(b)) {
aoqi@0 172 String tail = s.substring(b.length());
aoqi@0 173 for (String t: tails) {
aoqi@0 174 if (tail.equals(t))
aoqi@0 175 return true;
aoqi@0 176 }
aoqi@0 177 }
aoqi@0 178 }
aoqi@0 179 return false;
aoqi@0 180 }
aoqi@0 181
aoqi@0 182 Set<String> knownRequired = new TreeSet<String>(Arrays.asList(
aoqi@0 183 // See Resolve.getErrorKey
aoqi@0 184 "compiler.err.cant.resolve.args",
aoqi@0 185 "compiler.err.cant.resolve.args.params",
aoqi@0 186 "compiler.err.cant.resolve.location.args",
aoqi@0 187 "compiler.err.cant.resolve.location.args.params",
aoqi@0 188 "compiler.misc.cant.resolve.location.args",
aoqi@0 189 "compiler.misc.cant.resolve.location.args.params",
aoqi@0 190 // JavaCompiler, reports #errors and #warnings
aoqi@0 191 "compiler.misc.count.error",
aoqi@0 192 "compiler.misc.count.error.plural",
aoqi@0 193 "compiler.misc.count.warn",
aoqi@0 194 "compiler.misc.count.warn.plural",
aoqi@0 195 // Used for LintCategory
aoqi@0 196 "compiler.warn.lintOption",
aoqi@0 197 // Other
aoqi@0 198 "compiler.misc.base.membership" // (sic)
aoqi@0 199 ));
aoqi@0 200
aoqi@0 201
aoqi@0 202 Set<String> needToInvestigate = new TreeSet<String>(Arrays.asList(
aoqi@0 203 "compiler.misc.fatal.err.cant.close.loader", // Supressed by JSR308
aoqi@0 204 "compiler.err.cant.read.file", // UNUSED
aoqi@0 205 "compiler.err.illegal.self.ref", // UNUSED
aoqi@0 206 "compiler.err.io.exception", // UNUSED
aoqi@0 207 "compiler.err.limit.pool.in.class", // UNUSED
aoqi@0 208 "compiler.err.name.reserved.for.internal.use", // UNUSED
aoqi@0 209 "compiler.err.no.match.entry", // UNUSED
aoqi@0 210 "compiler.err.not.within.bounds.explain", // UNUSED
aoqi@0 211 "compiler.err.signature.doesnt.match.intf", // UNUSED
aoqi@0 212 "compiler.err.signature.doesnt.match.supertype", // UNUSED
aoqi@0 213 "compiler.err.type.var.more.than.once", // UNUSED
aoqi@0 214 "compiler.err.type.var.more.than.once.in.result", // UNUSED
aoqi@0 215 "compiler.misc.ccf.found.later.version", // UNUSED
aoqi@0 216 "compiler.misc.non.denotable.type", // UNUSED
aoqi@0 217 "compiler.misc.unnamed.package", // should be required, CR 6964147
aoqi@0 218 "compiler.misc.verbose.retro", // UNUSED
aoqi@0 219 "compiler.misc.verbose.retro.with", // UNUSED
aoqi@0 220 "compiler.misc.verbose.retro.with.list", // UNUSED
aoqi@0 221 "compiler.warn.proc.type.already.exists", // TODO in JavacFiler
aoqi@0 222 "javac.err.invalid.arg", // UNUSED ??
aoqi@0 223 "javac.opt.arg.class", // UNUSED ??
aoqi@0 224 "javac.opt.arg.pathname", // UNUSED ??
aoqi@0 225 "javac.opt.moreinfo", // option commented out
aoqi@0 226 "javac.opt.nogj", // UNUSED
aoqi@0 227 "javac.opt.printflat", // option commented out
aoqi@0 228 "javac.opt.printsearch", // option commented out
aoqi@0 229 "javac.opt.prompt", // option commented out
aoqi@0 230 "javac.opt.retrofit", // UNUSED
aoqi@0 231 "javac.opt.s", // option commented out
aoqi@0 232 "javac.opt.scramble", // option commented out
aoqi@0 233 "javac.opt.scrambleall" // option commented out
aoqi@0 234 ));
aoqi@0 235
aoqi@0 236 /**
aoqi@0 237 * For all strings in the code that look like they might be fragments of
aoqi@0 238 * a resource key, verify that a key exists.
aoqi@0 239 */
aoqi@0 240 void findMissingKeys(Set<String> codeStrings, Set<String> resourceKeys) {
aoqi@0 241 for (String cs: codeStrings) {
aoqi@0 242 if (cs.matches("[A-Za-z][^.]*\\..*")) {
aoqi@0 243 // ignore filenames (i.e. in SourceFile attribute
aoqi@0 244 if (cs.matches(".*\\.java"))
aoqi@0 245 continue;
aoqi@0 246 // ignore package and class names
aoqi@0 247 if (cs.matches("(com|java|javax|sun)\\.[A-Za-z.]+"))
aoqi@0 248 continue;
aoqi@0 249 // explicit known exceptions
aoqi@0 250 if (noResourceRequired.contains(cs))
aoqi@0 251 continue;
aoqi@0 252 // look for matching resource
aoqi@0 253 if (hasMatch(resourceKeys, cs))
aoqi@0 254 continue;
aoqi@0 255 error("no match for \"" + cs + "\"");
aoqi@0 256 }
aoqi@0 257 }
aoqi@0 258 }
aoqi@0 259 // where
aoqi@0 260 private Set<String> noResourceRequired = new HashSet<String>(Arrays.asList(
aoqi@0 261 // system properties
aoqi@0 262 "application.home", // in Paths.java
aoqi@0 263 "env.class.path",
aoqi@0 264 "line.separator",
aoqi@0 265 "os.name",
aoqi@0 266 "user.dir",
aoqi@0 267 // file names
aoqi@0 268 "ct.sym",
aoqi@0 269 "rt.jar",
aoqi@0 270 "tools.jar",
aoqi@0 271 // -XD option names
aoqi@0 272 "process.packages",
aoqi@0 273 "ignore.symbol.file",
aoqi@0 274 // prefix/embedded strings
aoqi@0 275 "compiler.",
aoqi@0 276 "compiler.misc.",
aoqi@0 277 "count.",
aoqi@0 278 "illegal.",
aoqi@0 279 "javac.",
aoqi@0 280 "verbose."
aoqi@0 281 ));
aoqi@0 282
aoqi@0 283 /**
aoqi@0 284 * Look for a resource that ends in this string fragment.
aoqi@0 285 */
aoqi@0 286 boolean hasMatch(Set<String> resourceKeys, String s) {
aoqi@0 287 for (String rk: resourceKeys) {
aoqi@0 288 if (rk.endsWith(s))
aoqi@0 289 return true;
aoqi@0 290 }
aoqi@0 291 return false;
aoqi@0 292 }
aoqi@0 293
aoqi@0 294 /**
aoqi@0 295 * Get the set of strings from (most of) the javac classfiles.
aoqi@0 296 */
aoqi@0 297 Set<String> getCodeStrings() throws IOException {
aoqi@0 298 Set<String> results = new TreeSet<String>();
aoqi@0 299 JavaCompiler c = ToolProvider.getSystemJavaCompiler();
aoqi@0 300 JavaFileManager fm = c.getStandardFileManager(null, null, null);
aoqi@0 301 JavaFileManager.Location javacLoc = findJavacLocation(fm);
aoqi@0 302 String[] pkgs = {
aoqi@0 303 "javax.annotation.processing",
aoqi@0 304 "javax.lang.model",
aoqi@0 305 "javax.tools",
aoqi@0 306 "com.sun.source",
aoqi@0 307 "com.sun.tools.javac"
aoqi@0 308 };
aoqi@0 309 for (String pkg: pkgs) {
aoqi@0 310 for (JavaFileObject fo: fm.list(javacLoc,
aoqi@0 311 pkg, EnumSet.of(JavaFileObject.Kind.CLASS), true)) {
aoqi@0 312 String name = fo.getName();
aoqi@0 313 // ignore resource files, and files which are not really part of javac
aoqi@0 314 if (name.matches(".*resources.[A-Za-z_0-9]+\\.class.*")
aoqi@0 315 || name.matches(".*CreateSymbols\\.class.*"))
aoqi@0 316 continue;
aoqi@0 317 scan(fo, results);
aoqi@0 318 }
aoqi@0 319 }
aoqi@0 320 return results;
aoqi@0 321 }
aoqi@0 322
aoqi@0 323 // depending on how the test is run, javac may be on bootclasspath or classpath
aoqi@0 324 JavaFileManager.Location findJavacLocation(JavaFileManager fm) {
aoqi@0 325 JavaFileManager.Location[] locns =
aoqi@0 326 { StandardLocation.PLATFORM_CLASS_PATH, StandardLocation.CLASS_PATH };
aoqi@0 327 try {
aoqi@0 328 for (JavaFileManager.Location l: locns) {
aoqi@0 329 JavaFileObject fo = fm.getJavaFileForInput(l,
aoqi@0 330 "com.sun.tools.javac.Main", JavaFileObject.Kind.CLASS);
aoqi@0 331 if (fo != null)
aoqi@0 332 return l;
aoqi@0 333 }
aoqi@0 334 } catch (IOException e) {
aoqi@0 335 throw new Error(e);
aoqi@0 336 }
aoqi@0 337 throw new IllegalStateException("Cannot find javac");
aoqi@0 338 }
aoqi@0 339
aoqi@0 340 /**
aoqi@0 341 * Get the set of strings from a class file.
aoqi@0 342 * Only strings that look like they might be a resource key are returned.
aoqi@0 343 */
aoqi@0 344 void scan(JavaFileObject fo, Set<String> results) throws IOException {
aoqi@0 345 InputStream in = fo.openInputStream();
aoqi@0 346 try {
aoqi@0 347 ClassFile cf = ClassFile.read(in);
aoqi@0 348 for (ConstantPool.CPInfo cpinfo: cf.constant_pool.entries()) {
aoqi@0 349 if (cpinfo.getTag() == ConstantPool.CONSTANT_Utf8) {
aoqi@0 350 String v = ((ConstantPool.CONSTANT_Utf8_info) cpinfo).value;
aoqi@0 351 if (v.matches("[A-Za-z0-9-_.]+"))
aoqi@0 352 results.add(v);
aoqi@0 353 }
aoqi@0 354 }
aoqi@0 355 } catch (ConstantPoolException ignore) {
aoqi@0 356 } finally {
aoqi@0 357 in.close();
aoqi@0 358 }
aoqi@0 359 }
aoqi@0 360
aoqi@0 361 /**
aoqi@0 362 * Get the set of keys from the javac resource bundles.
aoqi@0 363 */
aoqi@0 364 Set<String> getResourceKeys() {
aoqi@0 365 Set<String> results = new TreeSet<String>();
aoqi@0 366 for (String name : new String[]{"javac", "compiler"}) {
aoqi@0 367 ResourceBundle b =
aoqi@0 368 ResourceBundle.getBundle("com.sun.tools.javac.resources." + name);
aoqi@0 369 results.addAll(b.keySet());
aoqi@0 370 }
aoqi@0 371 return results;
aoqi@0 372 }
aoqi@0 373
aoqi@0 374 /**
aoqi@0 375 * Report an error.
aoqi@0 376 */
aoqi@0 377 void error(String msg) {
aoqi@0 378 System.err.println("Error: " + msg);
aoqi@0 379 errors++;
aoqi@0 380 }
aoqi@0 381
aoqi@0 382 int errors;
aoqi@0 383 }

mercurial