JCDiagnostic.java revision 2571:10fc81ac75b4
1/* 2 * Copyright (c) 2003, 2013, 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 26package com.sun.tools.javac.util; 27 28import java.util.EnumSet; 29import java.util.Locale; 30import java.util.Set; 31 32import javax.tools.Diagnostic; 33import javax.tools.JavaFileObject; 34 35import com.sun.tools.javac.api.DiagnosticFormatter; 36import com.sun.tools.javac.code.Lint.LintCategory; 37import com.sun.tools.javac.tree.EndPosTable; 38import com.sun.tools.javac.tree.JCTree; 39 40import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*; 41 42/** An abstraction of a diagnostic message generated by the compiler. 43 * 44 * <p><b>This is NOT part of any supported API. 45 * If you write code that depends on this, you do so at your own risk. 46 * This code and its internal interfaces are subject to change or 47 * deletion without notice.</b> 48 */ 49public class JCDiagnostic implements Diagnostic<JavaFileObject> { 50 /** A factory for creating diagnostic objects. */ 51 public static class Factory { 52 /** The context key for the diagnostic factory. */ 53 protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey = new Context.Key<>(); 54 55 /** Get the Factory instance for this context. */ 56 public static Factory instance(Context context) { 57 Factory instance = context.get(diagnosticFactoryKey); 58 if (instance == null) 59 instance = new Factory(context); 60 return instance; 61 } 62 63 DiagnosticFormatter<JCDiagnostic> formatter; 64 final String prefix; 65 final Set<DiagnosticFlag> defaultErrorFlags; 66 67 /** Create a new diagnostic factory. */ 68 protected Factory(Context context) { 69 this(JavacMessages.instance(context), "compiler"); 70 context.put(diagnosticFactoryKey, this); 71 72 final Options options = Options.instance(context); 73 initOptions(options); 74 options.addListener(new Runnable() { 75 public void run() { 76 initOptions(options); 77 } 78 }); 79 } 80 81 private void initOptions(Options options) { 82 if (options.isSet("onlySyntaxErrorsUnrecoverable")) 83 defaultErrorFlags.add(DiagnosticFlag.RECOVERABLE); 84 } 85 86 /** Create a new diagnostic factory. */ 87 public Factory(JavacMessages messages, String prefix) { 88 this.prefix = prefix; 89 this.formatter = new BasicDiagnosticFormatter(messages); 90 defaultErrorFlags = EnumSet.of(DiagnosticFlag.MANDATORY); 91 } 92 93 /** 94 * Create an error diagnostic. 95 * @param source The source of the compilation unit, if any, in which to report the error. 96 * @param pos The source position at which to report the error. 97 * @param key The key for the localized error message. 98 * @param args Fields of the error message. 99 */ 100 public JCDiagnostic error( 101 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 102 return create(ERROR, null, defaultErrorFlags, source, pos, key, args); 103 } 104 105 /** 106 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. 107 * @param source The source of the compilation unit, if any, in which to report the warning. 108 * @param pos The source position at which to report the warning. 109 * @param key The key for the localized warning message. 110 * @param args Fields of the warning message. 111 * @see MandatoryWarningHandler 112 */ 113 public JCDiagnostic mandatoryWarning( 114 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 115 return create(WARNING, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args); 116 } 117 118 /** 119 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. 120 * @param lc The lint category for the diagnostic 121 * @param source The source of the compilation unit, if any, in which to report the warning. 122 * @param pos The source position at which to report the warning. 123 * @param key The key for the localized warning message. 124 * @param args Fields of the warning message. 125 * @see MandatoryWarningHandler 126 */ 127 public JCDiagnostic mandatoryWarning( 128 LintCategory lc, 129 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 130 return create(WARNING, lc, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args); 131 } 132 133 /** 134 * Create a warning diagnostic. 135 * @param lc The lint category for the diagnostic 136 * @param key The key for the localized error message. 137 * @param args Fields of the warning message. 138 * @see MandatoryWarningHandler 139 */ 140 public JCDiagnostic warning( 141 LintCategory lc, String key, Object... args) { 142 return create(WARNING, lc, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args); 143 } 144 145 /** 146 * Create a warning diagnostic. 147 * @param source The source of the compilation unit, if any, in which to report the warning. 148 * @param pos The source position at which to report the warning. 149 * @param key The key for the localized warning message. 150 * @param args Fields of the warning message. 151 */ 152 public JCDiagnostic warning( 153 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 154 return create(WARNING, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args); 155 } 156 157 /** 158 * Create a warning diagnostic. 159 * @param lc The lint category for the diagnostic 160 * @param source The source of the compilation unit, if any, in which to report the warning. 161 * @param pos The source position at which to report the warning. 162 * @param key The key for the localized warning message. 163 * @param args Fields of the warning message. 164 * @see MandatoryWarningHandler 165 */ 166 public JCDiagnostic warning( 167 LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 168 return create(WARNING, lc, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args); 169 } 170 171 /** 172 * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options. 173 * @param key The key for the localized message. 174 * @param args Fields of the message. 175 * @see MandatoryWarningHandler 176 */ 177 public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) { 178 return create(NOTE, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, null, key, args); 179 } 180 181 /** 182 * Create a note diagnostic. 183 * @param key The key for the localized error message. 184 * @param args Fields of the message. 185 */ 186 public JCDiagnostic note(String key, Object... args) { 187 return create(NOTE, null, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args); 188 } 189 190 /** 191 * Create a note diagnostic. 192 * @param source The source of the compilation unit, if any, in which to report the note. 193 * @param pos The source position at which to report the note. 194 * @param key The key for the localized message. 195 * @param args Fields of the message. 196 */ 197 public JCDiagnostic note( 198 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 199 return create(NOTE, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args); 200 } 201 202 /** 203 * Create a fragment diagnostic, for use as an argument in other diagnostics 204 * @param key The key for the localized message. 205 * @param args Fields of the message. 206 */ 207 public JCDiagnostic fragment(String key, Object... args) { 208 return create(FRAGMENT, null, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args); 209 } 210 211 /** 212 * Create a new diagnostic of the given kind, which is not mandatory and which has 213 * no lint category. 214 * @param kind The diagnostic kind 215 * @param source The source of the compilation unit, if any, in which to report the message. 216 * @param pos The source position at which to report the message. 217 * @param key The key for the localized message. 218 * @param args Fields of the message. 219 */ 220 public JCDiagnostic create( 221 DiagnosticType kind, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 222 return create(kind, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args); 223 } 224 225 /** 226 * Create a new diagnostic of the given kind. 227 * @param kind The diagnostic kind 228 * @param lc The lint category, if applicable, or null 229 * @param flags The set of flags for the diagnostic 230 * @param source The source of the compilation unit, if any, in which to report the message. 231 * @param pos The source position at which to report the message. 232 * @param key The key for the localized message. 233 * @param args Fields of the message. 234 */ 235 public JCDiagnostic create( 236 DiagnosticType kind, LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { 237 return new JCDiagnostic(formatter, kind, lc, flags, source, pos, qualify(kind, key), args); 238 } 239 240 protected String qualify(DiagnosticType t, String key) { 241 return prefix + "." + t.key + "." + key; 242 } 243 } 244 245 246 247 /** 248 * Create a fragment diagnostic, for use as an argument in other diagnostics 249 * @param key The key for the localized error message. 250 * @param args Fields of the error message. 251 * 252 */ 253 @Deprecated 254 public static JCDiagnostic fragment(String key, Object... args) { 255 return new JCDiagnostic(getFragmentFormatter(), 256 FRAGMENT, 257 null, 258 EnumSet.noneOf(DiagnosticFlag.class), 259 null, 260 null, 261 "compiler." + FRAGMENT.key + "." + key, 262 args); 263 } 264 //where 265 @Deprecated 266 public static DiagnosticFormatter<JCDiagnostic> getFragmentFormatter() { 267 if (fragmentFormatter == null) { 268 fragmentFormatter = new BasicDiagnosticFormatter(JavacMessages.getDefaultMessages()); 269 } 270 return fragmentFormatter; 271 } 272 273 /** 274 * A DiagnosticType defines the type of the diagnostic. 275 **/ 276 public enum DiagnosticType { 277 /** A fragment of an enclosing diagnostic. */ 278 FRAGMENT("misc"), 279 /** A note: similar to, but less serious than, a warning. */ 280 NOTE("note"), 281 /** A warning. */ 282 WARNING("warn"), 283 /** An error. */ 284 ERROR("err"); 285 286 final String key; 287 288 /** Create a DiagnosticType. 289 * @param key A string used to create the resource key for the diagnostic. 290 */ 291 DiagnosticType(String key) { 292 this.key = key; 293 } 294 } 295 296 /** 297 * A DiagnosticPosition provides information about the positions in a file 298 * that gave rise to a diagnostic. It always defines a "preferred" position 299 * that most accurately defines the location of the diagnostic, it may also 300 * provide a related tree node that spans that location. 301 */ 302 public static interface DiagnosticPosition { 303 /** Gets the tree node, if any, to which the diagnostic applies. */ 304 JCTree getTree(); 305 /** If there is a tree node, get the start position of the tree node. 306 * Otherwise, just returns the same as getPreferredPosition(). */ 307 int getStartPosition(); 308 /** Get the position within the file that most accurately defines the 309 * location for the diagnostic. */ 310 int getPreferredPosition(); 311 /** If there is a tree node, and if endPositions are available, get 312 * the end position of the tree node. Otherwise, just returns the 313 * same as getPreferredPosition(). */ 314 int getEndPosition(EndPosTable endPosTable); 315 } 316 317 /** 318 * A DiagnosticPosition that simply identifies a position, but no related 319 * tree node, as the location for a diagnostic. Used for scanner and parser 320 * diagnostics. */ 321 public static class SimpleDiagnosticPosition implements DiagnosticPosition { 322 public SimpleDiagnosticPosition(int pos) { 323 this.pos = pos; 324 } 325 326 public JCTree getTree() { 327 return null; 328 } 329 330 public int getStartPosition() { 331 return pos; 332 } 333 334 public int getPreferredPosition() { 335 return pos; 336 } 337 338 public int getEndPosition(EndPosTable endPosTable) { 339 return pos; 340 } 341 342 private final int pos; 343 } 344 345 public enum DiagnosticFlag { 346 MANDATORY, 347 RESOLVE_ERROR, 348 SYNTAX, 349 RECOVERABLE, 350 NON_DEFERRABLE, 351 COMPRESSED 352 } 353 354 private final DiagnosticType type; 355 private final DiagnosticSource source; 356 private final DiagnosticPosition position; 357 private final String key; 358 protected final Object[] args; 359 private final Set<DiagnosticFlag> flags; 360 private final LintCategory lintCategory; 361 362 /** source line position (set lazily) */ 363 private SourcePosition sourcePosition; 364 365 /** 366 * This class is used to defer the line/column position fetch logic after diagnostic construction. 367 */ 368 class SourcePosition { 369 370 private final int line; 371 private final int column; 372 373 SourcePosition() { 374 int n = (position == null ? Position.NOPOS : position.getPreferredPosition()); 375 if (n == Position.NOPOS || source == null) 376 line = column = -1; 377 else { 378 line = source.getLineNumber(n); 379 column = source.getColumnNumber(n, true); 380 } 381 } 382 383 public int getLineNumber() { 384 return line; 385 } 386 387 public int getColumnNumber() { 388 return column; 389 } 390 } 391 392 /** 393 * Create a diagnostic object. 394 * @param formatter the formatter to use for the diagnostic 395 * @param dt the type of diagnostic 396 * @param lc the lint category for the diagnostic 397 * @param source the name of the source file, or null if none. 398 * @param pos the character offset within the source file, if given. 399 * @param key a resource key to identify the text of the diagnostic 400 * @param args arguments to be included in the text of the diagnostic 401 */ 402 protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter, 403 DiagnosticType dt, 404 LintCategory lc, 405 Set<DiagnosticFlag> flags, 406 DiagnosticSource source, 407 DiagnosticPosition pos, 408 String key, 409 Object... args) { 410 if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS) 411 throw new IllegalArgumentException(); 412 413 this.defaultFormatter = formatter; 414 this.type = dt; 415 this.lintCategory = lc; 416 this.flags = flags; 417 this.source = source; 418 this.position = pos; 419 this.key = key; 420 this.args = args; 421 } 422 423 /** 424 * Get the type of this diagnostic. 425 * @return the type of this diagnostic 426 */ 427 public DiagnosticType getType() { 428 return type; 429 } 430 431 /** 432 * Get the subdiagnostic list 433 * @return subdiagnostic list 434 */ 435 public List<JCDiagnostic> getSubdiagnostics() { 436 return List.nil(); 437 } 438 439 public boolean isMultiline() { 440 return false; 441 } 442 443 /** 444 * Check whether or not this diagnostic is required to be shown. 445 * @return true if this diagnostic is required to be shown. 446 */ 447 public boolean isMandatory() { 448 return flags.contains(DiagnosticFlag.MANDATORY); 449 } 450 451 /** 452 * Check whether this diagnostic has an associated lint category. 453 */ 454 public boolean hasLintCategory() { 455 return (lintCategory != null); 456 } 457 458 /** 459 * Get the associated lint category, or null if none. 460 */ 461 public LintCategory getLintCategory() { 462 return lintCategory; 463 } 464 465 /** 466 * Get the name of the source file referred to by this diagnostic. 467 * @return the name of the source referred to with this diagnostic, or null if none 468 */ 469 public JavaFileObject getSource() { 470 if (source == null) 471 return null; 472 else 473 return source.getFile(); 474 } 475 476 /** 477 * Get the source referred to by this diagnostic. 478 * @return the source referred to with this diagnostic, or null if none 479 */ 480 public DiagnosticSource getDiagnosticSource() { 481 return source; 482 } 483 484 protected int getIntStartPosition() { 485 return (position == null ? Position.NOPOS : position.getStartPosition()); 486 } 487 488 protected int getIntPosition() { 489 return (position == null ? Position.NOPOS : position.getPreferredPosition()); 490 } 491 492 protected int getIntEndPosition() { 493 return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable())); 494 } 495 496 public long getStartPosition() { 497 return getIntStartPosition(); 498 } 499 500 public long getPosition() { 501 return getIntPosition(); 502 } 503 504 public long getEndPosition() { 505 return getIntEndPosition(); 506 } 507 508 public DiagnosticPosition getDiagnosticPosition() { 509 return position; 510 } 511 512 /** 513 * Get the line number within the source referred to by this diagnostic. 514 * @return the line number within the source referred to by this diagnostic 515 */ 516 public long getLineNumber() { 517 if (sourcePosition == null) { 518 sourcePosition = new SourcePosition(); 519 } 520 return sourcePosition.getLineNumber(); 521 } 522 523 /** 524 * Get the column number within the line of source referred to by this diagnostic. 525 * @return the column number within the line of source referred to by this diagnostic 526 */ 527 public long getColumnNumber() { 528 if (sourcePosition == null) { 529 sourcePosition = new SourcePosition(); 530 } 531 return sourcePosition.getColumnNumber(); 532 } 533 534 /** 535 * Get the arguments to be included in the text of the diagnostic. 536 * @return the arguments to be included in the text of the diagnostic 537 */ 538 public Object[] getArgs() { 539 return args; 540 } 541 542 /** 543 * Get the prefix string associated with this type of diagnostic. 544 * @return the prefix string associated with this type of diagnostic 545 */ 546 public String getPrefix() { 547 return getPrefix(type); 548 } 549 550 /** 551 * Get the prefix string associated with a particular type of diagnostic. 552 * @return the prefix string associated with a particular type of diagnostic 553 */ 554 public String getPrefix(DiagnosticType dt) { 555 return defaultFormatter.formatKind(this, Locale.getDefault()); 556 } 557 558 /** 559 * Return the standard presentation of this diagnostic. 560 */ 561 @Override 562 public String toString() { 563 return defaultFormatter.format(this,Locale.getDefault()); 564 } 565 566 private DiagnosticFormatter<JCDiagnostic> defaultFormatter; 567 @Deprecated 568 private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter; 569 570 // Methods for javax.tools.Diagnostic 571 572 public Diagnostic.Kind getKind() { 573 switch (type) { 574 case NOTE: 575 return Diagnostic.Kind.NOTE; 576 case WARNING: 577 return flags.contains(DiagnosticFlag.MANDATORY) 578 ? Diagnostic.Kind.MANDATORY_WARNING 579 : Diagnostic.Kind.WARNING; 580 case ERROR: 581 return Diagnostic.Kind.ERROR; 582 default: 583 return Diagnostic.Kind.OTHER; 584 } 585 } 586 587 public String getCode() { 588 return key; 589 } 590 591 public String getMessage(Locale locale) { 592 return defaultFormatter.formatMessage(this, locale); 593 } 594 595 public void setFlag(DiagnosticFlag flag) { 596 flags.add(flag); 597 598 if (type == DiagnosticType.ERROR) { 599 switch (flag) { 600 case SYNTAX: 601 flags.remove(DiagnosticFlag.RECOVERABLE); 602 break; 603 case RESOLVE_ERROR: 604 flags.add(DiagnosticFlag.RECOVERABLE); 605 break; 606 } 607 } 608 } 609 610 public boolean isFlagSet(DiagnosticFlag flag) { 611 return flags.contains(flag); 612 } 613 614 public static class MultilineDiagnostic extends JCDiagnostic { 615 616 private final List<JCDiagnostic> subdiagnostics; 617 618 public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) { 619 super(other.defaultFormatter, 620 other.getType(), 621 other.getLintCategory(), 622 other.flags, 623 other.getDiagnosticSource(), 624 other.position, 625 other.getCode(), 626 other.getArgs()); 627 this.subdiagnostics = subdiagnostics; 628 } 629 630 @Override 631 public List<JCDiagnostic> getSubdiagnostics() { 632 return subdiagnostics; 633 } 634 635 @Override 636 public boolean isMultiline() { 637 return true; 638 } 639 } 640} 641