Lint.java revision 3822:d8766c39123a
1/*
2 * Copyright (c) 2005, 2016, 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.code;
27
28import java.util.Arrays;
29import java.util.EnumSet;
30import java.util.Map;
31import java.util.concurrent.ConcurrentHashMap;
32
33import com.sun.tools.javac.code.Symbol.*;
34import com.sun.tools.javac.main.Option;
35import com.sun.tools.javac.util.Context;
36import com.sun.tools.javac.util.List;
37import com.sun.tools.javac.util.Options;
38import com.sun.tools.javac.util.Pair;
39
40/**
41 * A class for handling -Xlint suboptions and @SuppresssWarnings.
42 *
43 *  <p><b>This is NOT part of any supported API.
44 *  If you write code that depends on this, you do so at your own risk.
45 *  This code and its internal interfaces are subject to change or
46 *  deletion without notice.</b>
47 */
48public class Lint
49{
50    /** The context key for the root Lint object. */
51    protected static final Context.Key<Lint> lintKey = new Context.Key<>();
52
53    /** Get the root Lint instance. */
54    public static Lint instance(Context context) {
55        Lint instance = context.get(lintKey);
56        if (instance == null)
57            instance = new Lint(context);
58        return instance;
59    }
60
61    /**
62     * Returns the result of combining the values in this object with
63     * the given annotation.
64     */
65    public Lint augment(Attribute.Compound attr) {
66        return augmentor.augment(this, attr);
67    }
68
69
70    /**
71     * Returns the result of combining the values in this object with
72     * the metadata on the given symbol.
73     */
74    public Lint augment(Symbol sym) {
75        Lint l = augmentor.augment(this, sym.getDeclarationAttributes());
76        if (sym.isDeprecated()) {
77            if (l == this)
78                l = new Lint(this);
79            l.values.remove(LintCategory.DEPRECATION);
80            l.suppressedValues.add(LintCategory.DEPRECATION);
81        }
82        return l;
83    }
84
85    /**
86     * Returns a new Lint that has the given LintCategorys suppressed.
87     * @param lc one or more categories to be suppressed
88     */
89    public Lint suppress(LintCategory... lc) {
90        Lint l = new Lint(this);
91        l.values.removeAll(Arrays.asList(lc));
92        l.suppressedValues.addAll(Arrays.asList(lc));
93        return l;
94    }
95
96    private final AugmentVisitor augmentor;
97
98    private final EnumSet<LintCategory> values;
99    private final EnumSet<LintCategory> suppressedValues;
100
101    private static final Map<String, LintCategory> map = new ConcurrentHashMap<>(20);
102
103    protected Lint(Context context) {
104        // initialize values according to the lint options
105        Options options = Options.instance(context);
106
107        if (options.isSet(Option.XLINT) || options.isSet(Option.XLINT_CUSTOM, "all")) {
108            // If -Xlint or -Xlint:all is given, enable all categories by default
109            values = EnumSet.allOf(LintCategory.class);
110        } else if (options.isSet(Option.XLINT_CUSTOM, "none")) {
111            // if -Xlint:none is given, disable all categories by default
112            values = EnumSet.noneOf(LintCategory.class);
113        } else {
114            // otherwise, enable on-by-default categories
115            values = EnumSet.noneOf(LintCategory.class);
116
117            Source source = Source.instance(context);
118            if (source.compareTo(Source.JDK1_9) >= 0) {
119                values.add(LintCategory.DEP_ANN);
120            }
121            values.add(LintCategory.MODULE);
122            values.add(LintCategory.REMOVAL);
123        }
124
125        // Look for specific overrides
126        for (LintCategory lc : LintCategory.values()) {
127            if (options.isSet(Option.XLINT_CUSTOM, lc.option)) {
128                values.add(lc);
129            } else if (options.isSet(Option.XLINT_CUSTOM, "-" + lc.option)) {
130                values.remove(lc);
131            }
132        }
133
134        suppressedValues = EnumSet.noneOf(LintCategory.class);
135
136        context.put(lintKey, this);
137        augmentor = new AugmentVisitor(context);
138    }
139
140    protected Lint(Lint other) {
141        this.augmentor = other.augmentor;
142        this.values = other.values.clone();
143        this.suppressedValues = other.suppressedValues.clone();
144    }
145
146    @Override
147    public String toString() {
148        return "Lint:[values" + values + " suppressedValues" + suppressedValues + "]";
149    }
150
151    /**
152     * Categories of warnings that can be generated by the compiler.
153     */
154    public enum LintCategory {
155        /**
156         * Warn when code refers to a auxiliary class that is hidden in a source file (ie source file name is
157         * different from the class name, and the type is not properly nested) and the referring code
158         * is not located in the same source file.
159         */
160        AUXILIARYCLASS("auxiliaryclass"),
161
162        /**
163         * Warn about use of unnecessary casts.
164         */
165        CAST("cast"),
166
167        /**
168         * Warn about issues related to classfile contents
169         */
170        CLASSFILE("classfile"),
171
172        /**
173         * Warn about use of deprecated items.
174         */
175        DEPRECATION("deprecation"),
176
177        /**
178         * Warn about items which are documented with an {@code @deprecated} JavaDoc
179         * comment, but which do not have {@code @Deprecated} annotation.
180         */
181        DEP_ANN("dep-ann"),
182
183        /**
184         * Warn about division by constant integer 0.
185         */
186        DIVZERO("divzero"),
187
188        /**
189         * Warn about empty statement after if.
190         */
191        EMPTY("empty"),
192
193        /**
194         * Warn about issues regarding module exports.
195         */
196        EXPORTS("exports"),
197
198        /**
199         * Warn about falling through from one case of a switch statement to the next.
200         */
201        FALLTHROUGH("fallthrough"),
202
203        /**
204         * Warn about finally clauses that do not terminate normally.
205         */
206        FINALLY("finally"),
207
208        /**
209         * Warn about module system related issues.
210         */
211        MODULE("module"),
212
213        /**
214         * Warn about issues relating to use of command line options
215         */
216        OPTIONS("options"),
217
218        /**
219         * Warn about issues regarding method overloads.
220         */
221        OVERLOADS("overloads"),
222
223        /**
224         * Warn about issues regarding method overrides.
225         */
226        OVERRIDES("overrides"),
227
228        /**
229         * Warn about invalid path elements on the command line.
230         * Such warnings cannot be suppressed with the SuppressWarnings
231         * annotation.
232         */
233        PATH("path"),
234
235        /**
236         * Warn about issues regarding annotation processing.
237         */
238        PROCESSING("processing"),
239
240        /**
241         * Warn about unchecked operations on raw types.
242         */
243        RAW("rawtypes"),
244
245        /**
246         * Warn about use of deprecated-for-removal items.
247         */
248        REMOVAL("removal"),
249
250        /**
251         * Warn about Serializable classes that do not provide a serial version ID.
252         */
253        SERIAL("serial"),
254
255        /**
256         * Warn about issues relating to use of statics
257         */
258        STATIC("static"),
259
260        /**
261         * Warn about issues relating to use of try blocks (i.e. try-with-resources)
262         */
263        TRY("try"),
264
265        /**
266         * Warn about unchecked operations on raw types.
267         */
268        UNCHECKED("unchecked"),
269
270        /**
271         * Warn about potentially unsafe vararg methods
272         */
273        VARARGS("varargs");
274
275        LintCategory(String option) {
276            this(option, false);
277        }
278
279        LintCategory(String option, boolean hidden) {
280            this.option = option;
281            this.hidden = hidden;
282            map.put(option, this);
283        }
284
285        static LintCategory get(String option) {
286            return map.get(option);
287        }
288
289        public final String option;
290        public final boolean hidden;
291    }
292
293    /**
294     * Checks if a warning category is enabled. A warning category may be enabled
295     * on the command line, or by default, and can be temporarily disabled with
296     * the SuppressWarnings annotation.
297     */
298    public boolean isEnabled(LintCategory lc) {
299        return values.contains(lc);
300    }
301
302    /**
303     * Checks is a warning category has been specifically suppressed, by means
304     * of the SuppressWarnings annotation, or, in the case of the deprecated
305     * category, whether it has been implicitly suppressed by virtue of the
306     * current entity being itself deprecated.
307     */
308    public boolean isSuppressed(LintCategory lc) {
309        return suppressedValues.contains(lc);
310    }
311
312    protected static class AugmentVisitor implements Attribute.Visitor {
313        private final Context context;
314        private Symtab syms;
315        private Lint parent;
316        private Lint lint;
317
318        AugmentVisitor(Context context) {
319            // to break an ugly sequence of initialization dependencies,
320            // we defer the initialization of syms until it is needed
321            this.context = context;
322        }
323
324        Lint augment(Lint parent, Attribute.Compound attr) {
325            initSyms();
326            this.parent = parent;
327            lint = null;
328            attr.accept(this);
329            return (lint == null ? parent : lint);
330        }
331
332        Lint augment(Lint parent, List<Attribute.Compound> attrs) {
333            initSyms();
334            this.parent = parent;
335            lint = null;
336            for (Attribute.Compound a: attrs) {
337                a.accept(this);
338            }
339            return (lint == null ? parent : lint);
340        }
341
342        private void initSyms() {
343            if (syms == null)
344                syms = Symtab.instance(context);
345        }
346
347        private void suppress(LintCategory lc) {
348            if (lint == null)
349                lint = new Lint(parent);
350            lint.suppressedValues.add(lc);
351            lint.values.remove(lc);
352        }
353
354        public void visitConstant(Attribute.Constant value) {
355            if (value.type.tsym == syms.stringType.tsym) {
356                LintCategory lc = LintCategory.get((String) (value.value));
357                if (lc != null)
358                    suppress(lc);
359            }
360        }
361
362        public void visitClass(Attribute.Class clazz) {
363        }
364
365        // If we find a @SuppressWarnings annotation, then we continue
366        // walking the tree, in order to suppress the individual warnings
367        // specified in the @SuppressWarnings annotation.
368        public void visitCompound(Attribute.Compound compound) {
369            if (compound.type.tsym == syms.suppressWarningsType.tsym) {
370                for (List<Pair<MethodSymbol,Attribute>> v = compound.values;
371                     v.nonEmpty(); v = v.tail) {
372                    Pair<MethodSymbol,Attribute> value = v.head;
373                    if (value.fst.name.toString().equals("value"))
374                        value.snd.accept(this);
375                }
376
377            }
378        }
379
380        public void visitArray(Attribute.Array array) {
381            for (Attribute value : array.values)
382                value.accept(this);
383        }
384
385        public void visitEnum(Attribute.Enum e) {
386        }
387
388        public void visitError(Attribute.Error e) {
389        }
390    }
391}
392