Fri, 29 Apr 2011 16:05:29 +0100
7039937: Improved catch analysis fails to handle a common idiom in the libraries
Summary: Disable generation of 'unreachable catch' warnings for catch statements catching Exception/Throwable
Reviewed-by: jjg
jjg@842 | 1 | /* |
jjg@842 | 2 | * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. |
jjg@842 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@842 | 4 | * |
jjg@842 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@842 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jjg@842 | 7 | * published by the Free Software Foundation. |
jjg@842 | 8 | * |
jjg@842 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@842 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@842 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@842 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@842 | 13 | * accompanied this code). |
jjg@842 | 14 | * |
jjg@842 | 15 | * You should have received a copy of the GNU General Public License version |
jjg@842 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@842 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@842 | 18 | * |
jjg@842 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jjg@842 | 20 | * or visit www.oracle.com if you need additional information or have any |
jjg@842 | 21 | * questions. |
jjg@842 | 22 | */ |
jjg@842 | 23 | |
jjg@842 | 24 | import java.io.*; |
jjg@842 | 25 | import java.util.*; |
jjg@842 | 26 | import java.util.List; |
jjg@842 | 27 | import javax.tools.*; |
jjg@842 | 28 | |
jjg@842 | 29 | import com.sun.tools.javac.api.*; |
jjg@842 | 30 | import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart; |
jjg@842 | 31 | import com.sun.tools.javac.api.Formattable.LocalizedString; |
jjg@842 | 32 | import com.sun.tools.javac.code.Flags.Flag; |
jjg@842 | 33 | import com.sun.tools.javac.code.Kinds.KindName; |
jjg@842 | 34 | import com.sun.tools.javac.code.*; |
jjg@842 | 35 | import com.sun.tools.javac.file.*; |
jjg@842 | 36 | import com.sun.tools.javac.main.Main; |
jjg@893 | 37 | import com.sun.tools.javac.main.JavaCompiler; |
jjg@842 | 38 | import com.sun.tools.javac.parser.Token; |
jjg@842 | 39 | import com.sun.tools.javac.util.*; |
jjg@842 | 40 | import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration; |
jjg@842 | 41 | import javax.lang.model.SourceVersion; |
jjg@842 | 42 | |
jjg@842 | 43 | /** |
jjg@842 | 44 | * Compiler factory for instances of Example.Compiler that use custom |
jjg@842 | 45 | * DiagnosticFormatter and Messages objects to track the types of args |
jjg@842 | 46 | * when when localizing diagnostics. |
jjg@842 | 47 | * The compiler objects only support "output" mode, not "check" mode. |
jjg@842 | 48 | */ |
jjg@842 | 49 | class ArgTypeCompilerFactory implements Example.Compiler.Factory { |
jjg@842 | 50 | // Same code as Example.Compiler.DefaultFactory, but the names resolve differently |
jjg@842 | 51 | public Example.Compiler getCompiler(List<String> opts, boolean verbose) { |
jjg@842 | 52 | String first; |
jjg@842 | 53 | String[] rest; |
jjg@842 | 54 | if (opts == null || opts.isEmpty()) { |
jjg@842 | 55 | first = null; |
jjg@842 | 56 | rest = new String[0]; |
jjg@842 | 57 | } else { |
jjg@842 | 58 | first = opts.get(0); |
jjg@842 | 59 | rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]); |
jjg@842 | 60 | } |
jjg@842 | 61 | if (first == null || first.equals("jsr199")) |
jjg@842 | 62 | return new Jsr199Compiler(verbose, rest); |
jjg@842 | 63 | else if (first.equals("simple")) |
jjg@842 | 64 | return new SimpleCompiler(verbose); |
jjg@842 | 65 | else if (first.equals("backdoor")) |
jjg@842 | 66 | return new BackdoorCompiler(verbose); |
jjg@842 | 67 | else |
jjg@842 | 68 | throw new IllegalArgumentException(first); |
jjg@842 | 69 | } |
jjg@842 | 70 | |
jjg@842 | 71 | /** |
jjg@842 | 72 | * Compile using the JSR 199 API. The diagnostics generated are |
jjg@842 | 73 | * scanned for resource keys. Not all diagnostic keys are generated |
jjg@842 | 74 | * via the JSR 199 API -- for example, rich diagnostics are not directly |
jjg@842 | 75 | * accessible, and some diagnostics generated by the file manager may |
jjg@842 | 76 | * not be generated (for example, the JSR 199 file manager does not see |
jjg@842 | 77 | * -Xlint:path). |
jjg@842 | 78 | */ |
jjg@842 | 79 | static class Jsr199Compiler extends Example.Compiler { |
jjg@842 | 80 | List<String> fmOpts; |
jjg@842 | 81 | |
jjg@842 | 82 | Jsr199Compiler(boolean verbose, String... args) { |
jjg@842 | 83 | super(verbose); |
jjg@842 | 84 | for (int i = 0; i < args.length; i++) { |
jjg@842 | 85 | String arg = args[i]; |
jjg@842 | 86 | if (arg.equals("-filemanager") && (i + 1 < args.length)) { |
jjg@842 | 87 | fmOpts = Arrays.asList(args[++i].split(",")); |
jjg@842 | 88 | } else |
jjg@842 | 89 | throw new IllegalArgumentException(arg); |
jjg@842 | 90 | } |
jjg@842 | 91 | } |
jjg@842 | 92 | |
jjg@842 | 93 | @Override |
jjg@842 | 94 | boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { |
jjg@842 | 95 | assert out != null && keys == null; |
jjg@842 | 96 | |
jjg@842 | 97 | if (verbose) |
jjg@842 | 98 | System.err.println("run_jsr199: " + opts + " " + files); |
jjg@842 | 99 | |
jjg@842 | 100 | JavacTool tool = JavacTool.create(); |
jjg@842 | 101 | |
jjg@842 | 102 | StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); |
jjg@842 | 103 | if (fmOpts != null) |
jjg@842 | 104 | fm = new FileManager(fm, fmOpts); |
jjg@842 | 105 | |
jjg@842 | 106 | Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files); |
jjg@842 | 107 | |
jjg@842 | 108 | JavacTaskImpl t = (JavacTaskImpl) tool.getTask(out, fm, null, opts, null, fos); |
jjg@842 | 109 | Context c = t.getContext(); |
jjg@842 | 110 | ArgTypeMessages.preRegister(c); |
jjg@893 | 111 | ArgTypeJavaCompiler.preRegister(c); |
jjg@842 | 112 | Boolean ok = t.call(); |
jjg@842 | 113 | |
jjg@842 | 114 | return ok; |
jjg@842 | 115 | } |
jjg@842 | 116 | } |
jjg@842 | 117 | |
jjg@842 | 118 | /** |
jjg@842 | 119 | * Run the test using the standard simple entry point. |
jjg@842 | 120 | */ |
jjg@842 | 121 | static class SimpleCompiler extends Example.Compiler { |
jjg@842 | 122 | SimpleCompiler(boolean verbose) { |
jjg@842 | 123 | super(verbose); |
jjg@842 | 124 | } |
jjg@842 | 125 | |
jjg@842 | 126 | @Override |
jjg@842 | 127 | boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { |
jjg@842 | 128 | assert out != null && keys == null; |
jjg@842 | 129 | |
jjg@842 | 130 | if (verbose) |
jjg@842 | 131 | System.err.println("run_simple: " + opts + " " + files); |
jjg@842 | 132 | |
jjg@842 | 133 | List<String> args = new ArrayList<String>(); |
jjg@842 | 134 | |
jjg@842 | 135 | args.addAll(opts); |
jjg@842 | 136 | for (File f: files) |
jjg@842 | 137 | args.add(f.getPath()); |
jjg@842 | 138 | |
jjg@842 | 139 | Main main = new Main("javac", out); |
jjg@842 | 140 | Context c = new Context() { |
jjg@842 | 141 | @Override public void clear() { |
jjg@842 | 142 | ((JavacFileManager) get(JavaFileManager.class)).close(); |
jjg@842 | 143 | super.clear(); |
jjg@842 | 144 | } |
jjg@842 | 145 | }; |
jjg@842 | 146 | JavacFileManager.preRegister(c); // can't create it until Log has been set up |
jjg@893 | 147 | ArgTypeJavaCompiler.preRegister(c); |
jjg@842 | 148 | ArgTypeMessages.preRegister(c); |
jjg@842 | 149 | int result = main.compile(args.toArray(new String[args.size()]), c); |
jjg@842 | 150 | |
jjg@842 | 151 | return (result == 0); |
jjg@842 | 152 | } |
jjg@842 | 153 | } |
jjg@842 | 154 | |
jjg@842 | 155 | static class BackdoorCompiler extends Example.Compiler { |
jjg@842 | 156 | BackdoorCompiler(boolean verbose) { |
jjg@842 | 157 | super(verbose); |
jjg@842 | 158 | } |
jjg@842 | 159 | |
jjg@842 | 160 | @Override |
jjg@842 | 161 | boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { |
jjg@842 | 162 | assert out != null && keys == null; |
jjg@842 | 163 | |
jjg@842 | 164 | if (verbose) |
jjg@842 | 165 | System.err.println("run_simple: " + opts + " " + files); |
jjg@842 | 166 | |
jjg@842 | 167 | List<String> args = new ArrayList<String>(opts); |
jjg@842 | 168 | for (File f: files) |
jjg@842 | 169 | args.add(f.getPath()); |
jjg@842 | 170 | |
jjg@842 | 171 | Context c = new Context(); |
jjg@842 | 172 | JavacFileManager.preRegister(c); // can't create it until Log has been set up |
jjg@893 | 173 | ArgTypeJavaCompiler.preRegister(c); |
jjg@842 | 174 | ArgTypeMessages.preRegister(c); |
jjg@842 | 175 | com.sun.tools.javac.main.Main m = new com.sun.tools.javac.main.Main("javac", out); |
jjg@842 | 176 | int rc = m.compile(args.toArray(new String[args.size()]), c); |
jjg@842 | 177 | |
jjg@842 | 178 | return (rc == 0); |
jjg@842 | 179 | } |
jjg@842 | 180 | |
jjg@842 | 181 | } |
jjg@842 | 182 | |
jjg@842 | 183 | |
jjg@842 | 184 | // <editor-fold defaultstate="collapsed" desc="Custom Javac components"> |
jjg@842 | 185 | |
jjg@842 | 186 | /** |
jjg@842 | 187 | * Diagnostic formatter which reports formats a diag as a series of lines |
jjg@842 | 188 | * containing a key, and a possibly empty set of descriptive strings for the |
jjg@842 | 189 | * arg types. |
jjg@842 | 190 | */ |
jjg@842 | 191 | static class ArgTypeDiagnosticFormatter extends AbstractDiagnosticFormatter { |
jjg@842 | 192 | |
jjg@842 | 193 | ArgTypeDiagnosticFormatter(Options options) { |
jjg@842 | 194 | super(null, new SimpleConfiguration(options, |
jjg@842 | 195 | EnumSet.of(DiagnosticPart.SUMMARY, |
jjg@842 | 196 | DiagnosticPart.DETAILS, |
jjg@842 | 197 | DiagnosticPart.SUBDIAGNOSTICS))); |
jjg@842 | 198 | } |
jjg@842 | 199 | |
jjg@842 | 200 | @Override |
jjg@842 | 201 | protected String formatDiagnostic(JCDiagnostic d, Locale locale) { |
jjg@842 | 202 | return formatMessage(d, locale); |
jjg@842 | 203 | } |
jjg@842 | 204 | |
jjg@842 | 205 | @Override |
jjg@842 | 206 | public String formatMessage(JCDiagnostic d, Locale l) { |
jjg@842 | 207 | StringBuilder buf = new StringBuilder(); |
jjg@842 | 208 | formatMessage(d, buf); |
jjg@842 | 209 | return buf.toString(); |
jjg@842 | 210 | } |
jjg@842 | 211 | |
jjg@842 | 212 | private void formatMessage(JCDiagnostic d, StringBuilder buf) { |
jjg@842 | 213 | String key = d.getCode(); |
jjg@842 | 214 | Object[] args = d.getArgs(); |
jjg@842 | 215 | // report the primary arg types, without recursing into diag fragments |
jjg@842 | 216 | buf.append(getKeyArgsString(key, args)); |
jjg@842 | 217 | // report details for any diagnostic fragments |
jjg@842 | 218 | for (Object arg: args) { |
jjg@842 | 219 | if (arg instanceof JCDiagnostic) { |
jjg@842 | 220 | buf.append("\n"); |
jjg@842 | 221 | formatMessage((JCDiagnostic) arg, buf); |
jjg@842 | 222 | } |
jjg@842 | 223 | } |
jjg@842 | 224 | // report details for any subdiagnostics |
jjg@842 | 225 | for (String s: formatSubdiagnostics(d, null)) { |
jjg@842 | 226 | buf.append("\n"); |
jjg@842 | 227 | buf.append(s); |
jjg@842 | 228 | } |
jjg@842 | 229 | } |
jjg@842 | 230 | |
jjg@842 | 231 | @Override |
jjg@842 | 232 | public boolean isRaw() { |
jjg@842 | 233 | return true; |
jjg@842 | 234 | } |
jjg@842 | 235 | } |
jjg@842 | 236 | |
jjg@842 | 237 | /** |
jjg@893 | 238 | * Trivial subtype of JavaCompiler to get access to the protected compilerKey field. |
jjg@893 | 239 | * The factory is used to ensure that the log is initialized with an instance of |
jjg@893 | 240 | * ArgTypeDiagnosticFormatter before we create the required JavaCompiler. |
jjg@893 | 241 | */ |
jjg@893 | 242 | static class ArgTypeJavaCompiler extends JavaCompiler { |
jjg@893 | 243 | static void preRegister(Context context) { |
jjg@893 | 244 | context.put(compilerKey, new Context.Factory<JavaCompiler>() { |
jjg@893 | 245 | public JavaCompiler make(Context c) { |
jjg@893 | 246 | Log log = Log.instance(c); |
jjg@893 | 247 | Options options = Options.instance(c); |
jjg@893 | 248 | log.setDiagnosticFormatter(new ArgTypeDiagnosticFormatter(options)); |
jjg@893 | 249 | return new JavaCompiler(c); |
jjg@893 | 250 | } |
jjg@893 | 251 | }); |
jjg@893 | 252 | } |
jjg@893 | 253 | |
jjg@893 | 254 | // not used |
jjg@893 | 255 | private ArgTypeJavaCompiler() { |
jjg@893 | 256 | super(null); |
jjg@893 | 257 | } |
jjg@893 | 258 | } |
jjg@893 | 259 | |
jjg@893 | 260 | /** |
jjg@842 | 261 | * Diagnostic formatter which "localizes" a message as a line |
jjg@842 | 262 | * containing a key, and a possibly empty set of descriptive strings for the |
jjg@842 | 263 | * arg types. |
jjg@842 | 264 | */ |
jjg@842 | 265 | static class ArgTypeMessages extends JavacMessages { |
jjg@893 | 266 | static void preRegister(Context context) { |
jjg@893 | 267 | context.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() { |
jjg@893 | 268 | public JavacMessages make(Context c) { |
jjg@842 | 269 | return new ArgTypeMessages(c) { |
jjg@842 | 270 | @Override |
jjg@842 | 271 | public String getLocalizedString(Locale l, String key, Object... args) { |
jjg@842 | 272 | return getKeyArgsString(key, args); |
jjg@842 | 273 | } |
jjg@842 | 274 | }; |
jjg@842 | 275 | } |
jjg@842 | 276 | }); |
jjg@842 | 277 | } |
jjg@842 | 278 | |
jjg@842 | 279 | ArgTypeMessages(Context context) { |
jjg@842 | 280 | super(context); |
jjg@842 | 281 | } |
jjg@842 | 282 | } |
jjg@842 | 283 | |
jjg@842 | 284 | /** |
jjg@842 | 285 | * Utility method to generate a string for key and args |
jjg@842 | 286 | */ |
jjg@842 | 287 | static String getKeyArgsString(String key, Object... args) { |
jjg@842 | 288 | StringBuilder buf = new StringBuilder(); |
jjg@842 | 289 | buf.append(key); |
jjg@842 | 290 | String sep = ": "; |
jjg@842 | 291 | for (Object o : args) { |
jjg@842 | 292 | buf.append(sep); |
jjg@842 | 293 | buf.append(getArgTypeOrStringValue(o)); |
jjg@842 | 294 | sep = ", "; |
jjg@842 | 295 | } |
jjg@842 | 296 | return buf.toString(); |
jjg@842 | 297 | } |
jjg@842 | 298 | |
jjg@842 | 299 | static boolean showStringValues = false; |
jjg@842 | 300 | |
jjg@842 | 301 | static String getArgTypeOrStringValue(Object o) { |
jjg@842 | 302 | if (showStringValues && o instanceof String) |
jjg@842 | 303 | return "\"" + o + "\""; |
jjg@842 | 304 | return getArgType(o); |
jjg@842 | 305 | } |
jjg@842 | 306 | |
jjg@842 | 307 | static String getArgType(Object o) { |
jjg@842 | 308 | if (o == null) |
jjg@842 | 309 | return "null"; |
jjg@842 | 310 | if (o instanceof Name) |
jjg@842 | 311 | return "name"; |
jjg@842 | 312 | if (o instanceof Boolean) |
jjg@842 | 313 | return "boolean"; |
jjg@842 | 314 | if (o instanceof Integer) |
jjg@842 | 315 | return "number"; |
jjg@842 | 316 | if (o instanceof String) |
jjg@842 | 317 | return "string"; |
jjg@842 | 318 | if (o instanceof Flag) |
jjg@842 | 319 | return "modifier"; |
jjg@842 | 320 | if (o instanceof KindName) |
jjg@842 | 321 | return "symbol kind"; |
jjg@842 | 322 | if (o instanceof Token) |
jjg@842 | 323 | return "token"; |
jjg@842 | 324 | if (o instanceof Symbol) |
jjg@842 | 325 | return "symbol"; |
jjg@842 | 326 | if (o instanceof Type) |
jjg@842 | 327 | return "type"; |
jjg@842 | 328 | if (o instanceof List) { |
jjg@842 | 329 | List<?> l = (List<?>) o; |
jjg@842 | 330 | if (l.isEmpty()) |
jjg@842 | 331 | return "list"; |
jjg@842 | 332 | else |
jjg@842 | 333 | return "list of " + getArgType(l.get(0)); |
jjg@842 | 334 | } |
jjg@842 | 335 | if (o instanceof ListBuffer) |
jjg@842 | 336 | return getArgType(((ListBuffer) o).toList()); |
jjg@842 | 337 | if (o instanceof Set) { |
jjg@842 | 338 | Set<?> s = (Set<?>) o; |
jjg@842 | 339 | if (s.isEmpty()) |
jjg@842 | 340 | return "set"; |
jjg@842 | 341 | else |
jjg@842 | 342 | return "set of " + getArgType(s.iterator().next()); |
jjg@842 | 343 | } |
jjg@842 | 344 | if (o instanceof SourceVersion) |
jjg@842 | 345 | return "source version"; |
jjg@842 | 346 | if (o instanceof FileObject || o instanceof File) |
jjg@842 | 347 | return "file name"; |
jjg@842 | 348 | if (o instanceof JCDiagnostic) |
jjg@842 | 349 | return "message segment"; |
jjg@842 | 350 | if (o instanceof LocalizedString) |
jjg@842 | 351 | return "message segment"; // only instance is "no arguments" |
jjg@842 | 352 | String s = o.getClass().getSimpleName(); |
jjg@842 | 353 | return (s.isEmpty() ? o.getClass().getName() : s); |
jjg@842 | 354 | } |
jjg@842 | 355 | |
jjg@842 | 356 | // </editor-fold> |
jjg@842 | 357 | |
jjg@842 | 358 | } |