1 /* 2 * Copyright (c) 1999, 2019, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.util; 27 28 import java.io.*; 29 import java.util.Arrays; 30 import java.util.EnumMap; 31 import java.util.EnumSet; 32 import java.util.HashSet; 33 import java.util.Map; 34 import java.util.Queue; 35 import java.util.Set; 36 import java.util.function.Predicate; 37 38 import javax.tools.DiagnosticListener; 39 import javax.tools.JavaFileObject; 40 41 import com.sun.tools.javac.api.DiagnosticFormatter; 42 import com.sun.tools.javac.main.Main; 43 import com.sun.tools.javac.main.Option; 44 import com.sun.tools.javac.tree.EndPosTable; 45 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; 46 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; 47 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 48 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; 49 50 import static com.sun.tools.javac.main.Option.*; 51 52 /** A class for error logs. Reports errors and warnings, and 53 * keeps track of error numbers and positions. 54 * 55 * <p><b>This is NOT part of any supported API. 56 * If you write code that depends on this, you do so at your own risk. 57 * This code and its internal interfaces are subject to change or 58 * deletion without notice.</b> 59 */ 60 public class Log extends AbstractLog { 61 /** The context key for the log. */ 62 public static final Context.Key<Log> logKey = new Context.Key<>(); 63 64 /** The context key for the standard output PrintWriter. */ 65 public static final Context.Key<PrintWriter> outKey = new Context.Key<>(); 66 67 /** The context key for the diagnostic PrintWriter. */ 68 public static final Context.Key<PrintWriter> errKey = new Context.Key<>(); 69 70 /* TODO: Should unify this with prefix handling in JCDiagnostic.Factory. */ 71 public enum PrefixKind { 72 JAVAC("javac."), 73 COMPILER_MISC("compiler.misc."); PrefixKind(String v)74 PrefixKind(String v) { 75 value = v; 76 } key(String k)77 public String key(String k) { 78 return value + k; 79 } 80 final String value; 81 } 82 83 /** 84 * DiagnosticHandler's provide the initial handling for diagnostics. 85 * When a diagnostic handler is created and has been initialized, it 86 * should install itself as the current diagnostic handler. When a 87 * client has finished using a handler, the client should call 88 * {@code log.removeDiagnosticHandler();} 89 * 90 * Note that javax.tools.DiagnosticListener (if set) is called later in the 91 * diagnostic pipeline. 92 */ 93 public static abstract class DiagnosticHandler { 94 /** 95 * The previously installed diagnostic handler. 96 */ 97 protected DiagnosticHandler prev; 98 99 /** 100 * Install this diagnostic handler as the current one, 101 * recording the previous one. 102 */ install(Log log)103 protected void install(Log log) { 104 prev = log.diagnosticHandler; 105 log.diagnosticHandler = this; 106 } 107 108 /** 109 * Handle a diagnostic. 110 */ report(JCDiagnostic diag)111 public abstract void report(JCDiagnostic diag); 112 } 113 114 /** 115 * A DiagnosticHandler that discards all diagnostics. 116 */ 117 public static class DiscardDiagnosticHandler extends DiagnosticHandler { DiscardDiagnosticHandler(Log log)118 public DiscardDiagnosticHandler(Log log) { 119 install(log); 120 } 121 122 @Override report(JCDiagnostic diag)123 public void report(JCDiagnostic diag) { } 124 } 125 126 /** 127 * A DiagnosticHandler that can defer some or all diagnostics, 128 * by buffering them for later examination and/or reporting. 129 * If a diagnostic is not deferred, or is subsequently reported 130 * with reportAllDiagnostics(), it will be reported to the previously 131 * active diagnostic handler. 132 */ 133 public static class DeferredDiagnosticHandler extends DiagnosticHandler { 134 private Queue<JCDiagnostic> deferred = new ListBuffer<>(); 135 private final Filter<JCDiagnostic> filter; 136 DeferredDiagnosticHandler(Log log)137 public DeferredDiagnosticHandler(Log log) { 138 this(log, null); 139 } 140 DeferredDiagnosticHandler(Log log, Filter<JCDiagnostic> filter)141 public DeferredDiagnosticHandler(Log log, Filter<JCDiagnostic> filter) { 142 this.filter = filter; 143 install(log); 144 } 145 146 @Override report(JCDiagnostic diag)147 public void report(JCDiagnostic diag) { 148 if (!diag.isFlagSet(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE) && 149 (filter == null || filter.accepts(diag))) { 150 deferred.add(diag); 151 } else { 152 prev.report(diag); 153 } 154 } 155 getDiagnostics()156 public Queue<JCDiagnostic> getDiagnostics() { 157 return deferred; 158 } 159 160 /** Report all deferred diagnostics. */ reportDeferredDiagnostics()161 public void reportDeferredDiagnostics() { 162 reportDeferredDiagnostics(d -> true); 163 } 164 165 /** Report selected deferred diagnostics. */ reportDeferredDiagnostics(Predicate<JCDiagnostic> accepter)166 public void reportDeferredDiagnostics(Predicate<JCDiagnostic> accepter) { 167 JCDiagnostic d; 168 while ((d = deferred.poll()) != null) { 169 if (accepter.test(d)) 170 prev.report(d); 171 } 172 deferred = null; // prevent accidental ongoing use 173 } 174 } 175 176 public enum WriterKind { NOTICE, WARNING, ERROR, STDOUT, STDERR } 177 178 private final Map<WriterKind, PrintWriter> writers; 179 180 /** The maximum number of errors/warnings that are reported. 181 */ 182 protected int MaxErrors; 183 protected int MaxWarnings; 184 185 /** Switch: prompt user on each error. 186 */ 187 public boolean promptOnError; 188 189 /** Switch: emit warning messages. 190 */ 191 public boolean emitWarnings; 192 193 /** Switch: suppress note messages. 194 */ 195 public boolean suppressNotes; 196 197 /** Print stack trace on errors? 198 */ 199 public boolean dumpOnError; 200 201 /** 202 * Diagnostic listener, if provided through programmatic 203 * interface to javac (JSR 199). 204 */ 205 protected DiagnosticListener<? super JavaFileObject> diagListener; 206 207 /** 208 * Formatter for diagnostics. 209 */ 210 private DiagnosticFormatter<JCDiagnostic> diagFormatter; 211 212 /** 213 * Keys for expected diagnostics. 214 */ 215 public Set<String> expectDiagKeys; 216 217 /** 218 * Set to true if a compressed diagnostic is reported 219 */ 220 public boolean compressedOutput; 221 222 /** 223 * JavacMessages object used for localization. 224 */ 225 private JavacMessages messages; 226 227 /** 228 * Handler for initial dispatch of diagnostics. 229 */ 230 private DiagnosticHandler diagnosticHandler; 231 232 /** Get the Log instance for this context. */ instance(Context context)233 public static Log instance(Context context) { 234 Log instance = context.get(logKey); 235 if (instance == null) 236 instance = new Log(context); 237 return instance; 238 } 239 240 /** 241 * Register a Context.Factory to create a Log. 242 */ preRegister(Context context, PrintWriter w)243 public static void preRegister(Context context, PrintWriter w) { 244 context.put(Log.class, (Context.Factory<Log>) (c -> new Log(c, w))); 245 } 246 247 /** 248 * Construct a log with default settings. 249 * If no streams are set in the context, the log will be initialized to use 250 * System.out for normal output, and System.err for all diagnostic output. 251 * If one stream is set in the context, with either Log.outKey or Log.errKey, 252 * it will be used for all output. 253 * Otherwise, the log will be initialized to use both streams found in the context. 254 */ Log(Context context)255 protected Log(Context context) { 256 this(context, initWriters(context)); 257 } 258 259 /** 260 * Initialize a map of writers based on values found in the context 261 * @param context the context in which to find writers to use 262 * @return a map of writers 263 */ initWriters(Context context)264 private static Map<WriterKind, PrintWriter> initWriters(Context context) { 265 PrintWriter out = context.get(outKey); 266 PrintWriter err = context.get(errKey); 267 if (out == null && err == null) { 268 out = new PrintWriter(System.out, true); 269 err = new PrintWriter(System.err, true); 270 return initWriters(out, err); 271 } else if (out == null || err == null) { 272 PrintWriter pw = (out != null) ? out : err; 273 return initWriters(pw, pw); 274 } else { 275 return initWriters(out, err); 276 } 277 } 278 279 /** 280 * Construct a log with all output sent to a single output stream. 281 */ Log(Context context, PrintWriter writer)282 protected Log(Context context, PrintWriter writer) { 283 this(context, initWriters(writer, writer)); 284 } 285 286 /** 287 * Construct a log. 288 * The log will be initialized to use stdOut for normal output, and stdErr 289 * for all diagnostic output. 290 */ Log(Context context, PrintWriter out, PrintWriter err)291 protected Log(Context context, PrintWriter out, PrintWriter err) { 292 this(context, initWriters(out, err)); 293 } 294 295 /** 296 * Initialize a writer map for a stream for normal output, and a stream for diagnostics. 297 * @param out a stream to be used for normal output 298 * @param err a stream to be used for diagnostic messages, such as errors, warnings, etc 299 * @return a map of writers 300 */ initWriters(PrintWriter out, PrintWriter err)301 private static Map<WriterKind, PrintWriter> initWriters(PrintWriter out, PrintWriter err) { 302 Map<WriterKind, PrintWriter> writers = new EnumMap<>(WriterKind.class); 303 writers.put(WriterKind.ERROR, err); 304 writers.put(WriterKind.WARNING, err); 305 writers.put(WriterKind.NOTICE, err); 306 307 writers.put(WriterKind.STDOUT, out); 308 writers.put(WriterKind.STDERR, err); 309 310 return writers; 311 } 312 313 /** 314 * Construct a log with given I/O redirections. 315 * @deprecated 316 * This constructor is provided to support the supported but now-deprecated javadoc entry point 317 * com.sun.tools.javadoc.Main.execute(String programName, 318 * PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter, 319 * String defaultDocletClassName, String... args) 320 */ 321 @Deprecated Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter)322 protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) { 323 this(context, initWriters(errWriter, warnWriter, noticeWriter)); 324 } 325 326 /** 327 * Initialize a writer map with different streams for different types of diagnostics. 328 * @param errWriter a stream for writing error messages 329 * @param warnWriter a stream for writing warning messages 330 * @param noticeWriter a stream for writing notice messages 331 * @return a map of writers 332 * @deprecated This method exists to support a supported but now deprecated javadoc entry point. 333 */ 334 @Deprecated initWriters(PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter)335 private static Map<WriterKind, PrintWriter> initWriters(PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) { 336 Map<WriterKind, PrintWriter> writers = new EnumMap<>(WriterKind.class); 337 writers.put(WriterKind.ERROR, errWriter); 338 writers.put(WriterKind.WARNING, warnWriter); 339 writers.put(WriterKind.NOTICE, noticeWriter); 340 341 writers.put(WriterKind.STDOUT, noticeWriter); 342 writers.put(WriterKind.STDERR, errWriter); 343 344 return writers; 345 } 346 347 /** 348 * Creates a log. 349 * @param context the context in which the log should be registered 350 * @param writers a map of writers that can be accessed by the kind of writer required 351 */ Log(Context context, Map<WriterKind, PrintWriter> writers)352 private Log(Context context, Map<WriterKind, PrintWriter> writers) { 353 super(JCDiagnostic.Factory.instance(context)); 354 context.put(logKey, this); 355 this.writers = writers; 356 357 @SuppressWarnings("unchecked") // FIXME 358 DiagnosticListener<? super JavaFileObject> dl = 359 context.get(DiagnosticListener.class); 360 this.diagListener = dl; 361 362 diagnosticHandler = new DefaultDiagnosticHandler(); 363 364 messages = JavacMessages.instance(context); 365 messages.add(Main.javacBundleName); 366 367 final Options options = Options.instance(context); 368 initOptions(options); 369 options.addListener(() -> initOptions(options)); 370 } 371 // where initOptions(Options options)372 private void initOptions(Options options) { 373 this.dumpOnError = options.isSet(DOE); 374 this.promptOnError = options.isSet(PROMPT); 375 this.emitWarnings = options.isUnset(XLINT_CUSTOM, "none"); 376 this.suppressNotes = options.isSet("suppressNotes"); 377 this.MaxErrors = getIntOption(options, XMAXERRS, getDefaultMaxErrors()); 378 this.MaxWarnings = getIntOption(options, XMAXWARNS, getDefaultMaxWarnings()); 379 380 boolean rawDiagnostics = options.isSet("rawDiagnostics"); 381 this.diagFormatter = rawDiagnostics ? new RawDiagnosticFormatter(options) : 382 new BasicDiagnosticFormatter(options, messages); 383 384 String ek = options.get("expectKeys"); 385 if (ek != null) 386 expectDiagKeys = new HashSet<>(Arrays.asList(ek.split(", *"))); 387 } 388 getIntOption(Options options, Option option, int defaultValue)389 private int getIntOption(Options options, Option option, int defaultValue) { 390 String s = options.get(option); 391 try { 392 if (s != null) { 393 int n = Integer.parseInt(s); 394 return (n <= 0 ? Integer.MAX_VALUE : n); 395 } 396 } catch (NumberFormatException e) { 397 // silently ignore ill-formed numbers 398 } 399 return defaultValue; 400 } 401 402 /** Default value for -Xmaxerrs. 403 */ getDefaultMaxErrors()404 protected int getDefaultMaxErrors() { 405 return 100; 406 } 407 408 /** Default value for -Xmaxwarns. 409 */ getDefaultMaxWarnings()410 protected int getDefaultMaxWarnings() { 411 return 100; 412 } 413 414 /** The number of errors encountered so far. 415 */ 416 public int nerrors = 0; 417 418 /** The number of warnings encountered so far. 419 */ 420 public int nwarnings = 0; 421 422 /** The number of errors encountered after MaxErrors was reached. 423 */ 424 public int nsuppressederrors = 0; 425 426 /** The number of warnings encountered after MaxWarnings was reached. 427 */ 428 public int nsuppressedwarns = 0; 429 430 /** A set of all errors generated so far. This is used to avoid printing an 431 * error message more than once. For each error, a pair consisting of the 432 * source file name and source code position of the error is added to the set. 433 */ 434 protected Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<>(); 435 436 /** A set of "not-supported-in-source-X" errors produced so far. This is used to only generate 437 * one such error per file. 438 */ 439 protected Set<Pair<JavaFileObject, List<String>>> recordedSourceLevelErrors = new HashSet<>(); 440 hasDiagnosticListener()441 public boolean hasDiagnosticListener() { 442 return diagListener != null; 443 } 444 setEndPosTable(JavaFileObject name, EndPosTable endPosTable)445 public void setEndPosTable(JavaFileObject name, EndPosTable endPosTable) { 446 Assert.checkNonNull(name); 447 getSource(name).setEndPosTable(endPosTable); 448 } 449 450 /** Return current sourcefile. 451 */ currentSourceFile()452 public JavaFileObject currentSourceFile() { 453 return source == null ? null : source.getFile(); 454 } 455 456 /** Get the current diagnostic formatter. 457 */ getDiagnosticFormatter()458 public DiagnosticFormatter<JCDiagnostic> getDiagnosticFormatter() { 459 return diagFormatter; 460 } 461 462 /** Set the current diagnostic formatter. 463 */ setDiagnosticFormatter(DiagnosticFormatter<JCDiagnostic> diagFormatter)464 public void setDiagnosticFormatter(DiagnosticFormatter<JCDiagnostic> diagFormatter) { 465 this.diagFormatter = diagFormatter; 466 } 467 getWriter(WriterKind kind)468 public PrintWriter getWriter(WriterKind kind) { 469 return writers.get(kind); 470 } 471 setWriter(WriterKind kind, PrintWriter pw)472 public void setWriter(WriterKind kind, PrintWriter pw) { 473 Assert.checkNonNull(pw); 474 writers.put(kind, pw); 475 } 476 setWriters(PrintWriter pw)477 public void setWriters(PrintWriter pw) { 478 Assert.checkNonNull(pw); 479 for (WriterKind k: WriterKind.values()) 480 writers.put(k, pw); 481 } 482 483 /** 484 * Replace the specified diagnostic handler with the 485 * handler that was current at the time this handler was created. 486 * The given handler must be the currently installed handler; 487 * it must be specified explicitly for clarity and consistency checking. 488 */ popDiagnosticHandler(DiagnosticHandler h)489 public void popDiagnosticHandler(DiagnosticHandler h) { 490 Assert.check(diagnosticHandler == h); 491 diagnosticHandler = h.prev; 492 } 493 494 /** Flush the logs 495 */ flush()496 public void flush() { 497 for (PrintWriter pw: writers.values()) { 498 pw.flush(); 499 } 500 } 501 flush(WriterKind kind)502 public void flush(WriterKind kind) { 503 getWriter(kind).flush(); 504 } 505 506 /** Returns true if an error needs to be reported for a given 507 * source name and pos. 508 */ shouldReport(JavaFileObject file, int pos)509 protected boolean shouldReport(JavaFileObject file, int pos) { 510 if (file == null) 511 return true; 512 513 Pair<JavaFileObject,Integer> coords = new Pair<>(file, pos); 514 boolean shouldReport = !recorded.contains(coords); 515 if (shouldReport) 516 recorded.add(coords); 517 return shouldReport; 518 } 519 520 /** Returns true if a diagnostics needs to be reported. 521 */ shouldReport(JCDiagnostic d)522 private boolean shouldReport(JCDiagnostic d) { 523 JavaFileObject file = d.getSource(); 524 525 if (file == null) 526 return true; 527 528 if (!shouldReport(file, d.getIntPosition())) 529 return false; 530 531 if (!d.isFlagSet(DiagnosticFlag.SOURCE_LEVEL)) 532 return true; 533 534 Pair<JavaFileObject, List<String>> coords = new Pair<>(file, getCode(d)); 535 boolean shouldReport = !recordedSourceLevelErrors.contains(coords); 536 if (shouldReport) 537 recordedSourceLevelErrors.add(coords); 538 return shouldReport; 539 } 540 541 //where getCode(JCDiagnostic d)542 private List<String> getCode(JCDiagnostic d) { 543 ListBuffer<String> buf = new ListBuffer<>(); 544 getCodeRecursive(buf, d); 545 return buf.toList(); 546 } 547 getCodeRecursive(ListBuffer<String> buf, JCDiagnostic d)548 private void getCodeRecursive(ListBuffer<String> buf, JCDiagnostic d) { 549 buf.add(d.getCode()); 550 for (Object o : d.getArgs()) { 551 if (o instanceof JCDiagnostic) { 552 getCodeRecursive(buf, (JCDiagnostic)o); 553 } 554 } 555 } 556 557 /**Is an error reported at the given pos (inside the current source)?*/ hasErrorOn(DiagnosticPosition pos)558 public boolean hasErrorOn(DiagnosticPosition pos) { 559 JavaFileObject file = source != null ? source.fileObject : null; 560 561 return file != null && recorded.contains(new Pair<>(file, pos.getPreferredPosition())); 562 } 563 564 /** Prompt user after an error. 565 */ prompt()566 public void prompt() { 567 if (promptOnError) { 568 System.err.println(localize("resume.abort")); 569 try { 570 while (true) { 571 switch (System.in.read()) { 572 case 'a': case 'A': 573 System.exit(-1); 574 return; 575 case 'r': case 'R': 576 return; 577 case 'x': case 'X': 578 throw new AssertionError("user abort"); 579 default: 580 } 581 } 582 } catch (IOException e) {} 583 } 584 } 585 586 /** Print the faulty source code line and point to the error. 587 * @param pos Buffer index of the error position, must be on current line 588 */ printErrLine(int pos, PrintWriter writer)589 private void printErrLine(int pos, PrintWriter writer) { 590 String line = (source == null ? null : source.getLine(pos)); 591 if (line == null) 592 return; 593 int col = source.getColumnNumber(pos, false); 594 595 printRawLines(writer, line); 596 for (int i = 0; i < col - 1; i++) { 597 writer.print((line.charAt(i) == '\t') ? "\t" : " "); 598 } 599 writer.println("^"); 600 writer.flush(); 601 } 602 printNewline()603 public void printNewline() { 604 PrintWriter noticeWriter = writers.get(WriterKind.NOTICE); 605 noticeWriter.println(); 606 } 607 printNewline(WriterKind wk)608 public void printNewline(WriterKind wk) { 609 getWriter(wk).println(); 610 } 611 printLines(String key, Object... args)612 public void printLines(String key, Object... args) { 613 PrintWriter noticeWriter = writers.get(WriterKind.NOTICE); 614 printRawLines(noticeWriter, localize(key, args)); 615 } 616 printLines(DiagnosticInfo diag)617 public void printLines(DiagnosticInfo diag) { 618 PrintWriter noticeWriter = writers.get(WriterKind.NOTICE); 619 printRawLines(noticeWriter, localize(diag)); 620 } 621 printLines(PrefixKind pk, String key, Object... args)622 public void printLines(PrefixKind pk, String key, Object... args) { 623 PrintWriter noticeWriter = writers.get(WriterKind.NOTICE); 624 printRawLines(noticeWriter, localize(pk, key, args)); 625 } 626 printLines(WriterKind wk, String key, Object... args)627 public void printLines(WriterKind wk, String key, Object... args) { 628 printRawLines(getWriter(wk), localize(key, args)); 629 } 630 printLines(WriterKind wk, PrefixKind pk, String key, Object... args)631 public void printLines(WriterKind wk, PrefixKind pk, String key, Object... args) { 632 printRawLines(getWriter(wk), localize(pk, key, args)); 633 } 634 635 /** Print the text of a message, translating newlines appropriately 636 * for the platform. 637 */ printRawLines(String msg)638 public void printRawLines(String msg) { 639 PrintWriter noticeWriter = writers.get(WriterKind.NOTICE); 640 printRawLines(noticeWriter, msg); 641 } 642 643 /** Print the text of a message, translating newlines appropriately 644 * for the platform. 645 */ printRawLines(WriterKind kind, String msg)646 public void printRawLines(WriterKind kind, String msg) { 647 printRawLines(getWriter(kind), msg); 648 } 649 650 /** Print the text of a message, translating newlines appropriately 651 * for the platform. 652 */ printRawLines(PrintWriter writer, String msg)653 public static void printRawLines(PrintWriter writer, String msg) { 654 int nl; 655 while ((nl = msg.indexOf('\n')) != -1) { 656 writer.println(msg.substring(0, nl)); 657 msg = msg.substring(nl+1); 658 } 659 if (msg.length() != 0) writer.println(msg); 660 } 661 662 /** 663 * Print the localized text of a "verbose" message to the 664 * noticeWriter stream. 665 */ printVerbose(String key, Object... args)666 public void printVerbose(String key, Object... args) { 667 PrintWriter noticeWriter = writers.get(WriterKind.NOTICE); 668 printRawLines(noticeWriter, localize("verbose." + key, args)); 669 } 670 671 @Override directError(String key, Object... args)672 protected void directError(String key, Object... args) { 673 PrintWriter errWriter = writers.get(WriterKind.ERROR); 674 printRawLines(errWriter, localize(key, args)); 675 errWriter.flush(); 676 } 677 678 /** Report a warning that cannot be suppressed. 679 * @param pos The source position at which to report the warning. 680 * @param key The key for the localized warning message. 681 * @param args Fields of the warning message. 682 */ strictWarning(DiagnosticPosition pos, String key, Object ... args)683 public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { 684 writeDiagnostic(diags.warning(null, source, pos, key, args)); 685 nwarnings++; 686 } 687 688 /** 689 * Primary method to report a diagnostic. 690 * @param diagnostic 691 */ 692 @Override report(JCDiagnostic diagnostic)693 public void report(JCDiagnostic diagnostic) { 694 diagnosticHandler.report(diagnostic); 695 } 696 697 /** 698 * Common diagnostic handling. 699 * The diagnostic is counted, and depending on the options and how many diagnostics have been 700 * reported so far, the diagnostic may be handed off to writeDiagnostic. 701 */ 702 private class DefaultDiagnosticHandler extends DiagnosticHandler { 703 @Override report(JCDiagnostic diagnostic)704 public void report(JCDiagnostic diagnostic) { 705 if (expectDiagKeys != null) 706 expectDiagKeys.remove(diagnostic.getCode()); 707 708 switch (diagnostic.getType()) { 709 case FRAGMENT: 710 throw new IllegalArgumentException(); 711 712 case NOTE: 713 // Print out notes only when we are permitted to report warnings 714 // Notes are only generated at the end of a compilation, so should be small 715 // in number. 716 if ((emitWarnings || diagnostic.isMandatory()) && !suppressNotes) { 717 writeDiagnostic(diagnostic); 718 } 719 break; 720 721 case WARNING: 722 if (emitWarnings || diagnostic.isMandatory()) { 723 if (nwarnings < MaxWarnings) { 724 writeDiagnostic(diagnostic); 725 nwarnings++; 726 } else { 727 nsuppressedwarns++; 728 } 729 } 730 break; 731 732 case ERROR: 733 if (diagnostic.isFlagSet(DiagnosticFlag.API) || 734 shouldReport(diagnostic)) { 735 if (nerrors < MaxErrors) { 736 writeDiagnostic(diagnostic); 737 nerrors++; 738 } else { 739 nsuppressederrors++; 740 } 741 } 742 break; 743 } 744 if (diagnostic.isFlagSet(JCDiagnostic.DiagnosticFlag.COMPRESSED)) { 745 compressedOutput = true; 746 } 747 } 748 } 749 750 /** 751 * Write out a diagnostic. 752 */ writeDiagnostic(JCDiagnostic diag)753 protected void writeDiagnostic(JCDiagnostic diag) { 754 if (diagListener != null) { 755 diagListener.report(diag); 756 return; 757 } 758 759 PrintWriter writer = getWriterForDiagnosticType(diag.getType()); 760 761 printRawLines(writer, diagFormatter.format(diag, messages.getCurrentLocale())); 762 763 if (promptOnError) { 764 switch (diag.getType()) { 765 case ERROR: 766 case WARNING: 767 prompt(); 768 } 769 } 770 771 if (dumpOnError) 772 new RuntimeException().printStackTrace(writer); 773 774 writer.flush(); 775 } 776 777 @Deprecated getWriterForDiagnosticType(DiagnosticType dt)778 protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) { 779 switch (dt) { 780 case FRAGMENT: 781 throw new IllegalArgumentException(); 782 783 case NOTE: 784 return writers.get(WriterKind.NOTICE); 785 786 case WARNING: 787 return writers.get(WriterKind.WARNING); 788 789 case ERROR: 790 return writers.get(WriterKind.ERROR); 791 792 default: 793 throw new Error(); 794 } 795 } 796 797 /** Find a localized string in the resource bundle. 798 * Because this method is static, it ignores the locale. 799 * Use localize(key, args) when possible. 800 * @param key The key for the localized string. 801 * @param args Fields to substitute into the string. 802 */ getLocalizedString(String key, Object ... args)803 public static String getLocalizedString(String key, Object ... args) { 804 return JavacMessages.getDefaultLocalizedString(PrefixKind.COMPILER_MISC.key(key), args); 805 } 806 807 /** Find a localized string in the resource bundle. 808 * @param key The key for the localized string. 809 * @param args Fields to substitute into the string. 810 */ localize(String key, Object... args)811 public String localize(String key, Object... args) { 812 return localize(PrefixKind.COMPILER_MISC, key, args); 813 } 814 localize(JCDiagnostic.DiagnosticInfo diagInfo)815 public String localize(JCDiagnostic.DiagnosticInfo diagInfo) { 816 if (useRawMessages) { 817 return diagInfo.key(); 818 } else { 819 return messages.getLocalizedString(diagInfo); 820 } 821 } 822 823 /** Find a localized string in the resource bundle. 824 * @param key The key for the localized string. 825 * @param args Fields to substitute into the string. 826 */ localize(PrefixKind pk, String key, Object... args)827 public String localize(PrefixKind pk, String key, Object... args) { 828 if (useRawMessages) 829 return pk.key(key); 830 else 831 return messages.getLocalizedString(pk.key(key), args); 832 } 833 // where 834 // backdoor hook for testing, should transition to use -XDrawDiagnostics 835 private static boolean useRawMessages = false; 836 837 /*************************************************************************** 838 * raw error messages without internationalization; used for experimentation 839 * and quick prototyping 840 ***************************************************************************/ 841 842 /** print an error or warning message: 843 */ printRawDiag(PrintWriter pw, String prefix, int pos, String msg)844 private void printRawDiag(PrintWriter pw, String prefix, int pos, String msg) { 845 if (source == null || pos == Position.NOPOS) { 846 printRawLines(pw, prefix + msg); 847 } else { 848 int line = source.getLineNumber(pos); 849 JavaFileObject file = source.getFile(); 850 if (file != null) 851 printRawLines(pw, 852 file.getName() + ":" + 853 line + ": " + msg); 854 printErrLine(pos, pw); 855 } 856 pw.flush(); 857 } 858 859 /** report an error: 860 */ rawError(int pos, String msg)861 public void rawError(int pos, String msg) { 862 PrintWriter errWriter = writers.get(WriterKind.ERROR); 863 if (nerrors < MaxErrors && shouldReport(currentSourceFile(), pos)) { 864 printRawDiag(errWriter, "error: ", pos, msg); 865 prompt(); 866 nerrors++; 867 } else { 868 nsuppressederrors++; 869 } 870 errWriter.flush(); 871 } 872 873 /** report a warning: 874 */ rawWarning(int pos, String msg)875 public void rawWarning(int pos, String msg) { 876 PrintWriter warnWriter = writers.get(WriterKind.ERROR); 877 if (emitWarnings) { 878 if (nwarnings < MaxWarnings) { 879 printRawDiag(warnWriter, "warning: ", pos, msg); 880 } else { 881 nsuppressedwarns++; 882 } 883 } 884 prompt(); 885 nwarnings++; 886 warnWriter.flush(); 887 } 888 format(String fmt, Object... args)889 public static String format(String fmt, Object... args) { 890 return String.format((java.util.Locale)null, fmt, args); 891 } 892 893 } 894