Tue, 04 Mar 2008 15:45:20 +0000
6663588: Compiler goes into infinite loop for Cyclic Inheritance test case
Summary: interplay between cyclic inheritance and tvar bounds hangs javac
Reviewed-by: jjg
1 /*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.javac.util;
28 import java.net.URI;
29 import java.text.MessageFormat;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.MissingResourceException;
33 import java.util.ResourceBundle;
35 import javax.tools.Diagnostic;
36 import javax.tools.FileObject;
37 import javax.tools.JavaFileObject;
39 import com.sun.tools.javac.tree.JCTree;
41 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
43 /** An abstraction of a diagnostic message generated by the compiler.
44 *
45 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
46 * you write code that depends on this, you do so at your own risk.
47 * This code and its internal interfaces are subject to change or
48 * deletion without notice.</b>
49 */
50 public class JCDiagnostic implements Diagnostic<JavaFileObject> {
51 /** A factory for creating diagnostic objects. */
52 public static class Factory {
53 /** The context key for the diagnostic factory. */
54 protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey =
55 new Context.Key<JCDiagnostic.Factory>();
57 /** Get the Factory instance for this context. */
58 public static Factory instance(Context context) {
59 Factory instance = context.get(diagnosticFactoryKey);
60 if (instance == null)
61 instance = new Factory(context);
62 return instance;
63 }
65 final Messages messages;
66 final String prefix;
68 /** Create a new diagnostic factory. */
69 protected Factory(Context context) {
70 context.put(diagnosticFactoryKey, this);
71 messages = Messages.instance(context);
72 prefix = "compiler";
73 }
75 /** Create a new diagnostic factory. */
76 public Factory(Messages messages, String prefix) {
77 this.messages = messages;
78 this.prefix = prefix;
79 }
81 /**
82 * Create an error diagnostic.
83 * @param source The source of the compilation unit, if any, in which to report the error.
84 * @param pos The source position at which to report the error.
85 * @param key The key for the localized error message.
86 * @param args Fields of the error message.
87 */
88 public JCDiagnostic error(
89 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
90 return new JCDiagnostic(messages, ERROR, true, source, pos, qualify(ERROR, key), args);
91 }
93 /**
94 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
95 * @param source The source of the compilation unit, if any, in which to report the warning.
96 * @param pos The source position at which to report the warning.
97 * @param key The key for the localized error message.
98 * @param args Fields of the error message.
99 * @see MandatoryWarningHandler
100 */
101 public JCDiagnostic mandatoryWarning(
102 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
103 return new JCDiagnostic(messages, WARNING, true, source, pos, qualify(WARNING, key), args);
104 }
106 /**
107 * Create a warning diagnostic.
108 * @param source The source of the compilation unit, if any, in which to report the warning.
109 * @param pos The source position at which to report the warning.
110 * @param key The key for the localized error message.
111 * @param args Fields of the error message.
112 */
113 public JCDiagnostic warning(
114 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
115 return new JCDiagnostic(messages, WARNING, false, source, pos, qualify(WARNING, key), args);
116 }
118 /**
119 * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
120 * @param key The key for the localized error message.
121 * @param args Fields of the error message.
122 * @see MandatoryWarningHandler
123 */
124 public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
125 return new JCDiagnostic(messages, NOTE, true, source, null, qualify(NOTE, key), args);
126 }
128 /**
129 * Create a note diagnostic.
130 * @param key The key for the localized error message.
131 * @param args Fields of the error message.
132 */
133 public JCDiagnostic note(String key, Object... args) {
134 return note(null, null, key, args);
135 }
137 /**
138 * Create a note diagnostic.
139 * @param source The source of the compilation unit, if any, in which to report the note.
140 * @param pos The source position at which to report the note.
141 * @param key The key for the localized error message.
142 * @param args Fields of the error message.
143 */
144 public JCDiagnostic note(
145 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
146 return new JCDiagnostic(messages, NOTE, false, source, pos, qualify(NOTE, key), args);
147 }
149 /**
150 * Create a fragment diagnostic, for use as an argument in other diagnostics
151 * @param key The key for the localized error message.
152 * @param args Fields of the error message.
153 */
154 public JCDiagnostic fragment(String key, Object... args) {
155 return new JCDiagnostic(messages, FRAGMENT, false, null, null, qualify(FRAGMENT, key), args);
156 }
158 protected String qualify(DiagnosticType t, String key) {
159 return prefix + "." + t.key + "." + key;
160 }
161 }
165 /**
166 * Create a fragment diagnostic, for use as an argument in other diagnostics
167 * @param key The key for the localized error message.
168 * @param args Fields of the error message.
169 */
170 // should be deprecated
171 public static JCDiagnostic fragment(String key, Object... args) {
172 return new JCDiagnostic(Messages.getDefaultMessages(),
173 FRAGMENT,
174 false,
175 null,
176 null,
177 "compiler." + FRAGMENT.key + "." + key,
178 args);
179 }
181 /**
182 * A simple abstraction of a source file, as needed for use in a diagnostic message.
183 */
184 // Note: This class may be superceded by a more general abstraction
185 public interface DiagnosticSource {
186 JavaFileObject getFile();
187 CharSequence getName();
188 int getLineNumber(int pos);
189 int getColumnNumber(int pos);
190 Map<JCTree, Integer> getEndPosTable();
191 };
193 /**
194 * A DiagnosticType defines the type of the diagnostic.
195 **/
196 public enum DiagnosticType {
197 /** A fragment of an enclosing diagnostic. */
198 FRAGMENT("misc"),
199 /** A note: similar to, but less serious than, a warning. */
200 NOTE("note"),
201 /** A warning. */
202 WARNING("warn"),
203 /** An error. */
204 ERROR("err");
206 final String key;
208 /** Create a DiagnosticType.
209 * @param key A string used to create the resource key for the diagnostic.
210 */
211 DiagnosticType(String key) {
212 this.key = key;
213 }
214 };
216 /**
217 * A DiagnosticPosition provides information about the positions in a file
218 * that gave rise to a diagnostic. It always defines a "preferred" position
219 * that most accurately defines the location of the diagnostic, it may also
220 * provide a related tree node that spans that location.
221 */
222 public static interface DiagnosticPosition {
223 /** Gets the tree node, if any, to which the diagnostic applies. */
224 JCTree getTree();
225 /** If there is a tree node, get the start position of the tree node.
226 * Otherwise, just returns the same as getPreferredPosition(). */
227 int getStartPosition();
228 /** Get the position within the file that most accurately defines the
229 * location for the diagnostic. */
230 int getPreferredPosition();
231 /** If there is a tree node, and if endPositions are available, get
232 * the end position of the tree node. Otherwise, just returns the
233 * same as getPreferredPosition(). */
234 int getEndPosition(Map<JCTree, Integer> endPosTable);
235 }
237 /**
238 * A DiagnosticPosition that simply identifies a position, but no related
239 * tree node, as the location for a diagnostic. Used for scanner and parser
240 * diagnostics. */
241 public static class SimpleDiagnosticPosition implements DiagnosticPosition {
242 public SimpleDiagnosticPosition(int pos) {
243 this.pos = pos;
244 }
246 public JCTree getTree() {
247 return null;
248 }
250 public int getStartPosition() {
251 return pos;
252 }
254 public int getPreferredPosition() {
255 return pos;
256 }
258 public int getEndPosition(Map<JCTree, Integer> endPosTable) {
259 return pos;
260 }
262 private final int pos;
263 }
265 private final Messages messages;
266 private final DiagnosticType type;
267 private final DiagnosticSource source;
268 private final DiagnosticPosition position;
269 private final int line;
270 private final int column;
271 private final String key;
272 private final Object[] args;
273 private boolean mandatory;
275 /**
276 * Create a diagnostic object.
277 * @param messages the resource for localized messages
278 * @param dt the type of diagnostic
279 * @param name the name of the source file, or null if none.
280 * @param pos the character offset within the source file, if given.
281 * @param key a resource key to identify the text of the diagnostic
282 * @param args arguments to be included in the text of the diagnostic
283 */
284 protected JCDiagnostic(Messages messages,
285 DiagnosticType dt,
286 boolean mandatory,
287 DiagnosticSource source,
288 DiagnosticPosition pos,
289 String key,
290 Object ... args) {
291 if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
292 throw new IllegalArgumentException();
294 this.messages = messages;
295 this.type = dt;
296 this.mandatory = mandatory;
297 this.source = source;
298 this.position = pos;
299 this.key = key;
300 this.args = args;
302 int n = (pos == null ? Position.NOPOS : pos.getPreferredPosition());
303 if (n == Position.NOPOS || source == null)
304 line = column = -1;
305 else {
306 line = source.getLineNumber(n);
307 column = source.getColumnNumber(n);
308 }
309 }
311 /**
312 * Get the type of this diagnostic.
313 * @return the type of this diagnostic
314 */
315 public DiagnosticType getType() {
316 return type;
317 }
319 /**
320 * Check whether or not this diagnostic is required to be shown.
321 * @return true if this diagnostic is required to be shown.
322 */
323 public boolean isMandatory() {
324 return mandatory;
325 }
327 /**
328 * Get the name of the source file referred to by this diagnostic.
329 * @return the name of the source referred to with this diagnostic, or null if none
330 */
331 public JavaFileObject getSource() {
332 if (source == null)
333 return null;
334 else
335 return source.getFile();
336 }
338 /**
339 * Get the name of the source file referred to by this diagnostic.
340 * @return the name of the source referred to with this diagnostic, or null if none
341 */
342 public String getSourceName() {
343 JavaFileObject s = getSource();
344 return s == null ? null : JavacFileManager.getJavacFileName(s);
345 }
347 /**
348 * Get the source referred to by this diagnostic.
349 * @return the source referred to with this diagnostic, or null if none
350 */
351 public DiagnosticSource getDiagnosticSource() {
352 return source;
353 }
355 protected int getIntStartPosition() {
356 return (position == null ? Position.NOPOS : position.getStartPosition());
357 }
359 protected int getIntPosition() {
360 return (position == null ? Position.NOPOS : position.getPreferredPosition());
361 }
363 protected int getIntEndPosition() {
364 return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
365 }
367 public long getStartPosition() {
368 return getIntStartPosition();
369 }
371 public long getPosition() {
372 return getIntPosition();
373 }
375 public long getEndPosition() {
376 return getIntEndPosition();
377 }
379 /**
380 * Get the line number within the source referred to by this diagnostic.
381 * @return the line number within the source referred to by this diagnostic
382 */
383 public long getLineNumber() {
384 return line;
385 }
387 /**
388 * Get the column number within the line of source referred to by this diagnostic.
389 * @return the column number within the line of source referred to by this diagnostic
390 */
391 public long getColumnNumber() {
392 return column;
393 }
395 /**
396 * Get the arguments to be included in the text of the diagnostic.
397 * @return the arguments to be included in the text of the diagnostic
398 */
399 public Object[] getArgs() {
400 return args;
401 }
403 /**
404 * Get the prefix string associated with this type of diagnostic.
405 * @return the prefix string associated with this type of diagnostic
406 */
407 public String getPrefix() {
408 return getPrefix(type);
409 }
411 /**
412 * Get the prefix string associated with a particular type of diagnostic.
413 * @return the prefix string associated with a particular type of diagnostic
414 */
415 public String getPrefix(DiagnosticType dt) {
416 switch (dt) {
417 case FRAGMENT: return "";
418 case NOTE: return getLocalizedString("compiler.note.note");
419 case WARNING: return getLocalizedString("compiler.warn.warning");
420 case ERROR: return getLocalizedString("compiler.err.error");
421 default:
422 throw new AssertionError("Unknown diagnostic type: " + dt);
423 }
424 }
426 /**
427 * Return the standard presentation of this diagnostic.
428 */
429 public String toString() {
430 if (defaultFormatter == null) {
431 defaultFormatter =
432 new DiagnosticFormatter();
433 }
434 return defaultFormatter.format(this);
435 }
437 private static DiagnosticFormatter defaultFormatter;
439 private static final String messageBundleName =
440 "com.sun.tools.javac.resources.compiler";
442 private String getLocalizedString(String key, Object... args) {
443 String[] strings = new String[args.length];
444 for (int i = 0; i < strings.length; i++) {
445 Object arg = args[i];
446 if (arg == null)
447 strings[i] = null;
448 else if (arg instanceof JCDiagnostic)
449 strings[i] = ((JCDiagnostic) arg).getMessage(null);
450 else
451 strings[i] = arg.toString();
452 }
454 return messages.getLocalizedString(key, (Object[]) strings);
455 }
457 // Methods for javax.tools.Diagnostic
459 public Diagnostic.Kind getKind() {
460 switch (type) {
461 case NOTE:
462 return Diagnostic.Kind.NOTE;
463 case WARNING:
464 return mandatory ? Diagnostic.Kind.MANDATORY_WARNING
465 : Diagnostic.Kind.WARNING;
466 case ERROR:
467 return Diagnostic.Kind.ERROR;
468 default:
469 return Diagnostic.Kind.OTHER;
470 }
471 }
473 public String getCode() {
474 return key;
475 }
477 public String getMessage(Locale locale) {
478 // RFE 6406133: JCDiagnostic.getMessage ignores locale argument
479 return getLocalizedString(key, args);
480 }
482 }