Thu, 03 Feb 2011 09:36:28 +0000
5017953: spurious cascaded diagnostics when name not found
Summary: when an operator is applied to one or more erroneous operands, spurious diagnostics are generated
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@842 | 37 | import com.sun.tools.javac.parser.Token; |
jjg@842 | 38 | import com.sun.tools.javac.util.*; |
jjg@842 | 39 | import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration; |
jjg@842 | 40 | import javax.lang.model.SourceVersion; |
jjg@842 | 41 | |
jjg@842 | 42 | /** |
jjg@842 | 43 | * Compiler factory for instances of Example.Compiler that use custom |
jjg@842 | 44 | * DiagnosticFormatter and Messages objects to track the types of args |
jjg@842 | 45 | * when when localizing diagnostics. |
jjg@842 | 46 | * The compiler objects only support "output" mode, not "check" mode. |
jjg@842 | 47 | */ |
jjg@842 | 48 | class ArgTypeCompilerFactory implements Example.Compiler.Factory { |
jjg@842 | 49 | // Same code as Example.Compiler.DefaultFactory, but the names resolve differently |
jjg@842 | 50 | public Example.Compiler getCompiler(List<String> opts, boolean verbose) { |
jjg@842 | 51 | String first; |
jjg@842 | 52 | String[] rest; |
jjg@842 | 53 | if (opts == null || opts.isEmpty()) { |
jjg@842 | 54 | first = null; |
jjg@842 | 55 | rest = new String[0]; |
jjg@842 | 56 | } else { |
jjg@842 | 57 | first = opts.get(0); |
jjg@842 | 58 | rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]); |
jjg@842 | 59 | } |
jjg@842 | 60 | if (first == null || first.equals("jsr199")) |
jjg@842 | 61 | return new Jsr199Compiler(verbose, rest); |
jjg@842 | 62 | else if (first.equals("simple")) |
jjg@842 | 63 | return new SimpleCompiler(verbose); |
jjg@842 | 64 | else if (first.equals("backdoor")) |
jjg@842 | 65 | return new BackdoorCompiler(verbose); |
jjg@842 | 66 | else |
jjg@842 | 67 | throw new IllegalArgumentException(first); |
jjg@842 | 68 | } |
jjg@842 | 69 | |
jjg@842 | 70 | /** |
jjg@842 | 71 | * Compile using the JSR 199 API. The diagnostics generated are |
jjg@842 | 72 | * scanned for resource keys. Not all diagnostic keys are generated |
jjg@842 | 73 | * via the JSR 199 API -- for example, rich diagnostics are not directly |
jjg@842 | 74 | * accessible, and some diagnostics generated by the file manager may |
jjg@842 | 75 | * not be generated (for example, the JSR 199 file manager does not see |
jjg@842 | 76 | * -Xlint:path). |
jjg@842 | 77 | */ |
jjg@842 | 78 | static class Jsr199Compiler extends Example.Compiler { |
jjg@842 | 79 | List<String> fmOpts; |
jjg@842 | 80 | |
jjg@842 | 81 | Jsr199Compiler(boolean verbose, String... args) { |
jjg@842 | 82 | super(verbose); |
jjg@842 | 83 | for (int i = 0; i < args.length; i++) { |
jjg@842 | 84 | String arg = args[i]; |
jjg@842 | 85 | if (arg.equals("-filemanager") && (i + 1 < args.length)) { |
jjg@842 | 86 | fmOpts = Arrays.asList(args[++i].split(",")); |
jjg@842 | 87 | } else |
jjg@842 | 88 | throw new IllegalArgumentException(arg); |
jjg@842 | 89 | } |
jjg@842 | 90 | } |
jjg@842 | 91 | |
jjg@842 | 92 | @Override |
jjg@842 | 93 | boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) { |
jjg@842 | 94 | assert out != null && keys == null; |
jjg@842 | 95 | |
jjg@842 | 96 | if (verbose) |
jjg@842 | 97 | System.err.println("run_jsr199: " + opts + " " + files); |
jjg@842 | 98 | |
jjg@842 | 99 | JavacTool tool = JavacTool.create(); |
jjg@842 | 100 | |
jjg@842 | 101 | StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); |
jjg@842 | 102 | if (fmOpts != null) |
jjg@842 | 103 | fm = new FileManager(fm, fmOpts); |
jjg@842 | 104 | |
jjg@842 | 105 | Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files); |
jjg@842 | 106 | |
jjg@842 | 107 | JavacTaskImpl t = (JavacTaskImpl) tool.getTask(out, fm, null, opts, null, fos); |
jjg@842 | 108 | Context c = t.getContext(); |
jjg@842 | 109 | ArgTypeMessages.preRegister(c); |
jjg@842 | 110 | Options options = Options.instance(c); |
jjg@842 | 111 | Log.instance(c).setDiagnosticFormatter(new ArgTypeDiagnosticFormatter(options)); |
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@842 | 147 | ArgTypeDiagnosticFormatter.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@842 | 173 | ArgTypeDiagnosticFormatter.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 | static void preRegister(final Context context) { |
jjg@842 | 193 | context.put(Log.logKey, new Context.Factory<Log>() { |
jjg@842 | 194 | public Log make() { |
jjg@842 | 195 | Log log = new Log(context) { }; |
jjg@842 | 196 | Options options = Options.instance(context); |
jjg@842 | 197 | log.setDiagnosticFormatter(new ArgTypeDiagnosticFormatter(options)); |
jjg@842 | 198 | return log; |
jjg@842 | 199 | } |
jjg@842 | 200 | }); |
jjg@842 | 201 | |
jjg@842 | 202 | } |
jjg@842 | 203 | |
jjg@842 | 204 | ArgTypeDiagnosticFormatter(Options options) { |
jjg@842 | 205 | super(null, new SimpleConfiguration(options, |
jjg@842 | 206 | EnumSet.of(DiagnosticPart.SUMMARY, |
jjg@842 | 207 | DiagnosticPart.DETAILS, |
jjg@842 | 208 | DiagnosticPart.SUBDIAGNOSTICS))); |
jjg@842 | 209 | } |
jjg@842 | 210 | |
jjg@842 | 211 | @Override |
jjg@842 | 212 | protected String formatDiagnostic(JCDiagnostic d, Locale locale) { |
jjg@842 | 213 | return formatMessage(d, locale); |
jjg@842 | 214 | } |
jjg@842 | 215 | |
jjg@842 | 216 | @Override |
jjg@842 | 217 | public String formatMessage(JCDiagnostic d, Locale l) { |
jjg@842 | 218 | StringBuilder buf = new StringBuilder(); |
jjg@842 | 219 | formatMessage(d, buf); |
jjg@842 | 220 | return buf.toString(); |
jjg@842 | 221 | } |
jjg@842 | 222 | |
jjg@842 | 223 | private void formatMessage(JCDiagnostic d, StringBuilder buf) { |
jjg@842 | 224 | String key = d.getCode(); |
jjg@842 | 225 | Object[] args = d.getArgs(); |
jjg@842 | 226 | // report the primary arg types, without recursing into diag fragments |
jjg@842 | 227 | buf.append(getKeyArgsString(key, args)); |
jjg@842 | 228 | // report details for any diagnostic fragments |
jjg@842 | 229 | for (Object arg: args) { |
jjg@842 | 230 | if (arg instanceof JCDiagnostic) { |
jjg@842 | 231 | buf.append("\n"); |
jjg@842 | 232 | formatMessage((JCDiagnostic) arg, buf); |
jjg@842 | 233 | } |
jjg@842 | 234 | } |
jjg@842 | 235 | // report details for any subdiagnostics |
jjg@842 | 236 | for (String s: formatSubdiagnostics(d, null)) { |
jjg@842 | 237 | buf.append("\n"); |
jjg@842 | 238 | buf.append(s); |
jjg@842 | 239 | } |
jjg@842 | 240 | } |
jjg@842 | 241 | |
jjg@842 | 242 | @Override |
jjg@842 | 243 | public boolean isRaw() { |
jjg@842 | 244 | return true; |
jjg@842 | 245 | } |
jjg@842 | 246 | } |
jjg@842 | 247 | |
jjg@842 | 248 | /** |
jjg@842 | 249 | * Diagnostic formatter which "localizes" a message as a line |
jjg@842 | 250 | * containing a key, and a possibly empty set of descriptive strings for the |
jjg@842 | 251 | * arg types. |
jjg@842 | 252 | */ |
jjg@842 | 253 | static class ArgTypeMessages extends JavacMessages { |
jjg@842 | 254 | static void preRegister(final Context c) { |
jjg@842 | 255 | c.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() { |
jjg@842 | 256 | public JavacMessages make() { |
jjg@842 | 257 | return new ArgTypeMessages(c) { |
jjg@842 | 258 | @Override |
jjg@842 | 259 | public String getLocalizedString(Locale l, String key, Object... args) { |
jjg@842 | 260 | return getKeyArgsString(key, args); |
jjg@842 | 261 | } |
jjg@842 | 262 | }; |
jjg@842 | 263 | } |
jjg@842 | 264 | }); |
jjg@842 | 265 | } |
jjg@842 | 266 | |
jjg@842 | 267 | ArgTypeMessages(Context context) { |
jjg@842 | 268 | super(context); |
jjg@842 | 269 | } |
jjg@842 | 270 | } |
jjg@842 | 271 | |
jjg@842 | 272 | /** |
jjg@842 | 273 | * Utility method to generate a string for key and args |
jjg@842 | 274 | */ |
jjg@842 | 275 | static String getKeyArgsString(String key, Object... args) { |
jjg@842 | 276 | StringBuilder buf = new StringBuilder(); |
jjg@842 | 277 | buf.append(key); |
jjg@842 | 278 | String sep = ": "; |
jjg@842 | 279 | for (Object o : args) { |
jjg@842 | 280 | buf.append(sep); |
jjg@842 | 281 | buf.append(getArgTypeOrStringValue(o)); |
jjg@842 | 282 | sep = ", "; |
jjg@842 | 283 | } |
jjg@842 | 284 | return buf.toString(); |
jjg@842 | 285 | } |
jjg@842 | 286 | |
jjg@842 | 287 | static boolean showStringValues = false; |
jjg@842 | 288 | |
jjg@842 | 289 | static String getArgTypeOrStringValue(Object o) { |
jjg@842 | 290 | if (showStringValues && o instanceof String) |
jjg@842 | 291 | return "\"" + o + "\""; |
jjg@842 | 292 | return getArgType(o); |
jjg@842 | 293 | } |
jjg@842 | 294 | |
jjg@842 | 295 | static String getArgType(Object o) { |
jjg@842 | 296 | if (o == null) |
jjg@842 | 297 | return "null"; |
jjg@842 | 298 | if (o instanceof Name) |
jjg@842 | 299 | return "name"; |
jjg@842 | 300 | if (o instanceof Boolean) |
jjg@842 | 301 | return "boolean"; |
jjg@842 | 302 | if (o instanceof Integer) |
jjg@842 | 303 | return "number"; |
jjg@842 | 304 | if (o instanceof String) |
jjg@842 | 305 | return "string"; |
jjg@842 | 306 | if (o instanceof Flag) |
jjg@842 | 307 | return "modifier"; |
jjg@842 | 308 | if (o instanceof KindName) |
jjg@842 | 309 | return "symbol kind"; |
jjg@842 | 310 | if (o instanceof Token) |
jjg@842 | 311 | return "token"; |
jjg@842 | 312 | if (o instanceof Symbol) |
jjg@842 | 313 | return "symbol"; |
jjg@842 | 314 | if (o instanceof Type) |
jjg@842 | 315 | return "type"; |
jjg@842 | 316 | if (o instanceof List) { |
jjg@842 | 317 | List<?> l = (List<?>) o; |
jjg@842 | 318 | if (l.isEmpty()) |
jjg@842 | 319 | return "list"; |
jjg@842 | 320 | else |
jjg@842 | 321 | return "list of " + getArgType(l.get(0)); |
jjg@842 | 322 | } |
jjg@842 | 323 | if (o instanceof ListBuffer) |
jjg@842 | 324 | return getArgType(((ListBuffer) o).toList()); |
jjg@842 | 325 | if (o instanceof Set) { |
jjg@842 | 326 | Set<?> s = (Set<?>) o; |
jjg@842 | 327 | if (s.isEmpty()) |
jjg@842 | 328 | return "set"; |
jjg@842 | 329 | else |
jjg@842 | 330 | return "set of " + getArgType(s.iterator().next()); |
jjg@842 | 331 | } |
jjg@842 | 332 | if (o instanceof SourceVersion) |
jjg@842 | 333 | return "source version"; |
jjg@842 | 334 | if (o instanceof FileObject || o instanceof File) |
jjg@842 | 335 | return "file name"; |
jjg@842 | 336 | if (o instanceof JCDiagnostic) |
jjg@842 | 337 | return "message segment"; |
jjg@842 | 338 | if (o instanceof LocalizedString) |
jjg@842 | 339 | return "message segment"; // only instance is "no arguments" |
jjg@842 | 340 | String s = o.getClass().getSimpleName(); |
jjg@842 | 341 | return (s.isEmpty() ? o.getClass().getName() : s); |
jjg@842 | 342 | } |
jjg@842 | 343 | |
jjg@842 | 344 | // </editor-fold> |
jjg@842 | 345 | |
jjg@842 | 346 | } |