View Javadoc
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. */
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       */
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       */
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  
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 
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
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 
229         LintCategory(String option) {
230             this(option, false);
231         }
232 
233         LintCategory(String option, boolean hidden) {
234             this.option = option;
235             this.hidden = hidden;
236             map.put(option, this);
237         }
238 
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      */
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      */
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 
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 
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 
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 
296         private void initSyms() {
297             if (syms == null)
298                 syms = Symtab.instance(context);
299         }
300 
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 
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 
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.
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 
334         public void visitArray(Attribute.Array array) {
335             for (Attribute value : array.values)
336                 value.accept(this);
337         }
338 
339         public void visitEnum(Attribute.Enum e) {
340         }
341 
342         public void visitError(Attribute.Error e) {
343         }
344     };
345 }