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