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