• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.lint.detector.api;
18 
19 import com.android.annotations.NonNull;
20 import com.android.annotations.Nullable;
21 import com.android.tools.lint.client.api.LintDriver;
22 import com.google.common.annotations.Beta;
23 
24 import org.objectweb.asm.tree.ClassNode;
25 import org.w3c.dom.Attr;
26 import org.w3c.dom.Document;
27 import org.w3c.dom.Element;
28 
29 import java.io.File;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
33 
34 import lombok.ast.AstVisitor;
35 import lombok.ast.MethodInvocation;
36 
37 /**
38  * A detector is able to find a particular problem. It might also be thought of as enforcing
39  * a rule, but "rule" is a bit overloaded in ADT terminology since ViewRules are used in
40  * the Rules API to allow views to specify designtime behavior in the graphical layout editor.
41  * <p>
42  * Each detector provides information about the issues it can find, such as an explanation
43  * of how to fix the issue, the priority, the category, etc. It also has an id which is
44  * used to persistently identify a particular type of error.
45  * <p>
46  * Detectors will be called in a predefined order:
47  * <ol>
48  *   <li> Manifest file
49  *   <li> Resource files, in alphabetical order by resource type
50  *        (therefore, "layout" is checked before "values", "values-de" is checked before
51  *        "values-en" but after "values", and so on.
52  *   <li> Java sources
53  *   <li> Java classes
54  *   <li> Proguard files
55  * </ol>
56  * If a detector needs information when processing a file type that comes from a type of
57  * file later in the order above, they can request a second phase; see
58  * {@link LintDriver#requestRepeat}.
59  * <p>
60  * NOTE: Detectors might be constructed just once and shared between lint runs, so
61  * any per-detector state should be initialized and reset via the before/after
62  * methods.
63  * <p/>
64  * <b>NOTE: This is not a public or final API; if you rely on this be prepared
65  * to adjust your code for the next tools release.</b>
66  */
67 @Beta
68 public abstract class Detector {
69     /** Specialized interface for detectors that scan Java source file parse trees */
70     public interface JavaScanner  {
71         /**
72          * Create a parse tree visitor to process the parse tree. All
73          * {@link JavaScanner} detectors must provide a visitor, unless they
74          * either return true from {@link #appliesToResourceRefs()} or return
75          * non null from {@link #getApplicableMethodNames()}.
76          * <p>
77          * If you return specific AST node types from
78          * {@link #getApplicableNodeTypes()}, then the visitor will <b>only</b>
79          * be called for the specific requested node types. This is more
80          * efficient, since it allows many detectors that apply to only a small
81          * part of the AST (such as method call nodes) to share iteration of the
82          * majority of the parse tree.
83          * <p>
84          * If you return null from {@link #getApplicableNodeTypes()}, then your
85          * visitor will be called from the top and all node types visited.
86          * <p>
87          * Note that a new visitor is created for each separate compilation
88          * unit, so you can store per file state in the visitor.
89          *
90          * @param context the {@link Context} for the file being analyzed
91          * @return a visitor, or null.
92          */
93         @Nullable
createJavaVisitor(@onNull JavaContext context)94         AstVisitor createJavaVisitor(@NonNull JavaContext context);
95 
96         /**
97          * Return the types of AST nodes that the visitor returned from
98          * {@link #createJavaVisitor(JavaContext)} should visit. See the
99          * documentation for {@link #createJavaVisitor(JavaContext)} for details
100          * on how the shared visitor is used.
101          * <p>
102          * If you return null from this method, then the visitor will process
103          * the full tree instead.
104          * <p>
105          * Note that for the shared visitor, the return codes from the visit
106          * methods are ignored: returning true will <b>not</b> prune iteration
107          * of the subtree, since there may be other node types interested in the
108          * children. If you need to ensure that your visitor only processes a
109          * part of the tree, use a full visitor instead. See the
110          * OverdrawDetector implementation for an example of this.
111          *
112          * @return the list of applicable node types (AST node classes), or null
113          */
114         @Nullable
getApplicableNodeTypes()115         List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes();
116 
117         /**
118          * Return the list of method names this detector is interested in, or
119          * null. If this method returns non-null, then any AST nodes that match
120          * a method call in the list will be passed to the
121          * {@link #visitMethod(JavaContext, AstVisitor, MethodInvocation)}
122          * method for processing. The visitor created by
123          * {@link #createJavaVisitor(JavaContext)} is also passed to that
124          * method, although it can be null.
125          * <p>
126          * This makes it easy to write detectors that focus on some fixed calls.
127          * For example, the StringFormatDetector uses this mechanism to look for
128          * "format" calls, and when found it looks around (using the AST's
129          * {@link lombok.ast.Node#getParent()} method) to see if it's called on
130          * a String class instance, and if so do its normal processing. Note
131          * that since it doesn't need to do any other AST processing, that
132          * detector does not actually supply a visitor.
133          *
134          * @return a set of applicable method names, or null.
135          */
136         @Nullable
getApplicableMethodNames()137         List<String> getApplicableMethodNames();
138 
139         /**
140          * Method invoked for any method calls found that matches any names
141          * returned by {@link #getApplicableMethodNames()}. This also passes
142          * back the visitor that was created by
143          * {@link #createJavaVisitor(JavaContext)}, but a visitor is not
144          * required. It is intended for detectors that need to do additional AST
145          * processing, but also want the convenience of not having to look for
146          * method names on their own.
147          *
148          * @param context the context of the lint request
149          * @param visitor the visitor created from
150          *            {@link #createJavaVisitor(JavaContext)}, or null
151          * @param node the {@link MethodInvocation} node for the invoked method
152          */
visitMethod( @onNull JavaContext context, @Nullable AstVisitor visitor, @NonNull MethodInvocation node)153         void visitMethod(
154                 @NonNull JavaContext context,
155                 @Nullable AstVisitor visitor,
156                 @NonNull MethodInvocation node);
157 
158         /**
159          * Returns whether this detector cares about Android resource references
160          * (such as {@code R.layout.main} or {@code R.string.app_name}). If it
161          * does, then the visitor will look for these patterns, and if found, it
162          * will invoke {@link #visitResourceReference} passing the resource type
163          * and resource name. It also passes the visitor, if any, that was
164          * created by {@link #createJavaVisitor(JavaContext)}, such that a
165          * detector can do more than just look for resources.
166          *
167          * @return true if this detector wants to be notified of R resource
168          *         identifiers found in the code.
169          */
appliesToResourceRefs()170         boolean appliesToResourceRefs();
171 
172         /**
173          * Called for any resource references (such as {@code R.layout.main}
174          * found in Java code, provided this detector returned {@code true} from
175          * {@link #appliesToResourceRefs()}.
176          *
177          * @param context the lint scanning context
178          * @param visitor the visitor created from
179          *            {@link #createJavaVisitor(JavaContext)}, or null
180          * @param node the variable reference for the resource
181          * @param type the resource type, such as "layout" or "string"
182          * @param name the resource name, such as "main" from
183          *            {@code R.layout.main}
184          * @param isFramework whether the resource is a framework resource
185          *            (android.R) or a local project resource (R)
186          */
visitResourceReference( @onNull JavaContext context, @Nullable AstVisitor visitor, @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name, boolean isFramework)187         void visitResourceReference(
188                 @NonNull JavaContext context,
189                 @Nullable AstVisitor visitor,
190                 @NonNull lombok.ast.Node node,
191                 @NonNull String type,
192                 @NonNull String name,
193                 boolean isFramework);
194     }
195 
196     /** Specialized interface for detectors that scan Java class files */
197     public interface ClassScanner  {
198         /**
199          * Checks the given class' bytecode for issues.
200          *
201          * @param context the context of the lint check, pointing to for example
202          *            the file
203          * @param classNode the root class node
204          */
checkClass(@onNull ClassContext context, @NonNull ClassNode classNode)205         void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode);
206     }
207 
208     /** Specialized interface for detectors that scan XML files */
209     public interface XmlScanner {
210         /**
211          * Visit the given document. The detector is responsible for its own iteration
212          * through the document.
213          * @param context information about the document being analyzed
214          * @param document the document to examine
215          */
visitDocument(@onNull XmlContext context, @NonNull Document document)216         void visitDocument(@NonNull XmlContext context, @NonNull Document document);
217 
218         /**
219          * Visit the given element.
220          * @param context information about the document being analyzed
221          * @param element the element to examine
222          */
visitElement(@onNull XmlContext context, @NonNull Element element)223         void visitElement(@NonNull XmlContext context, @NonNull Element element);
224 
225         /**
226          * Visit the given element after its children have been analyzed.
227          * @param context information about the document being analyzed
228          * @param element the element to examine
229          */
visitElementAfter(@onNull XmlContext context, @NonNull Element element)230         void visitElementAfter(@NonNull XmlContext context, @NonNull Element element);
231 
232         /**
233          * Visit the given attribute.
234          * @param context information about the document being analyzed
235          * @param attribute the attribute node to examine
236          */
visitAttribute(@onNull XmlContext context, @NonNull Attr attribute)237         void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute);
238 
239         /**
240          * Returns the list of elements that this detector wants to analyze. If non
241          * null, this detector will be called (specifically, the
242          * {@link #visitElement} method) for each matching element in the document.
243          * <p>
244          * If this method returns null, and {@link #getApplicableAttributes()} also returns
245          * null, then the {@link #visitDocument} method will be called instead.
246          *
247          * @return a collection of elements, or null, or the special
248          *         {@link XmlScanner#ALL} marker to indicate that every single
249          *         element should be analyzed.
250          */
251         @Nullable
getApplicableElements()252         Collection<String> getApplicableElements();
253 
254         /**
255          * Returns the list of attributes that this detector wants to analyze. If non
256          * null, this detector will be called (specifically, the
257          * {@link #visitAttribute} method) for each matching attribute in the document.
258          * <p>
259          * If this method returns null, and {@link #getApplicableElements()} also returns
260          * null, then the {@link #visitDocument} method will be called instead.
261          *
262          * @return a collection of attributes, or null, or the special
263          *         {@link XmlScanner#ALL} marker to indicate that every single
264          *         attribute should be analyzed.
265          */
266         @Nullable
getApplicableAttributes()267         Collection<String> getApplicableAttributes();
268 
269         /**
270          * Special marker collection returned by {@link #getApplicableElements()} or
271          * {@link #getApplicableAttributes()} to indicate that the check should be
272          * invoked on all elements or all attributes
273          */
274         @NonNull
275         public static final List<String> ALL = new ArrayList<String>(0); // NOT Collections.EMPTY!
276         // We want to distinguish this from just an *empty* list returned by the caller!
277     }
278 
279     /**
280      * Runs the detector. This method will not be called for certain specialized
281      * detectors, such as {@link XmlScanner} and {@link JavaScanner}, where
282      * there are specialized analysis methods instead such as
283      * {@link XmlScanner#visitElement(XmlContext, Element)}.
284      *
285      * @param context the context describing the work to be done
286      */
run(@onNull Context context)287     public void run(@NonNull Context context) {
288     }
289 
290     /**
291      * Returns true if this detector applies to the given file
292      *
293      * @param context the context to check
294      * @param file the file in the context to check
295      * @return true if this detector applies to the given context and file
296      */
appliesTo(@onNull Context context, @NonNull File file)297     public boolean appliesTo(@NonNull Context context, @NonNull File file) {
298         return false;
299     }
300 
301     /**
302      * Analysis is about to begin, perform any setup steps.
303      *
304      * @param context the context for the check referencing the project, lint
305      *            client, etc
306      */
beforeCheckProject(@onNull Context context)307     public void beforeCheckProject(@NonNull Context context) {
308     }
309 
310     /**
311      * Analysis has just been finished for the whole project, perform any
312      * cleanup or report issues that require project-wide analysis.
313      *
314      * @param context the context for the check referencing the project, lint
315      *            client, etc
316      */
afterCheckProject(@onNull Context context)317     public void afterCheckProject(@NonNull Context context) {
318     }
319 
320     /**
321      * Analysis is about to begin for the given library project, perform any setup steps.
322      *
323      * @param context the context for the check referencing the project, lint
324      *            client, etc
325      */
beforeCheckLibraryProject(@onNull Context context)326     public void beforeCheckLibraryProject(@NonNull Context context) {
327     }
328 
329     /**
330      * Analysis has just been finished for the given library project, perform any
331      * cleanup or report issues that require library-project-wide analysis.
332      *
333      * @param context the context for the check referencing the project, lint
334      *            client, etc
335      */
afterCheckLibraryProject(@onNull Context context)336     public void afterCheckLibraryProject(@NonNull Context context) {
337     }
338 
339     /**
340      * Analysis is about to be performed on a specific file, perform any setup
341      * steps.
342      * <p>
343      * Note: When this method is called at the beginning of checking an XML
344      * file, the context is guaranteed to be an instance of {@link XmlContext},
345      * and similarly for a Java source file, the context will be a
346      * {@link JavaContext} and so on.
347      *
348      * @param context the context for the check referencing the file to be
349      *            checked, the project, etc.
350      */
beforeCheckFile(@onNull Context context)351     public void beforeCheckFile(@NonNull Context context) {
352     }
353 
354     /**
355      * Analysis has just been finished for a specific file, perform any cleanup
356      * or report issues found
357      * <p>
358      * Note: When this method is called at the end of checking an XML
359      * file, the context is guaranteed to be an instance of {@link XmlContext},
360      * and similarly for a Java source file, the context will be a
361      * {@link JavaContext} and so on.
362      *
363      * @param context the context for the check referencing the file to be
364      *            checked, the project, etc.
365      */
afterCheckFile(@onNull Context context)366     public void afterCheckFile(@NonNull Context context) {
367     }
368 
369     /**
370      * Returns the expected speed of this detector
371      *
372      * @return the expected speed of this detector
373      */
374     @NonNull
getSpeed()375     public abstract Speed getSpeed();
376 
377     // ---- Dummy implementations to make implementing XmlScanner easier: ----
378 
379     @SuppressWarnings("javadoc")
visitDocument(@onNull XmlContext context, @NonNull Document document)380     public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
381         // This method must be overridden if your detector does
382         // not return something from getApplicableElements or
383         // getApplicableATtributes
384         assert false;
385     }
386 
387     @SuppressWarnings("javadoc")
visitElement(@onNull XmlContext context, @NonNull Element element)388     public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
389         // This method must be overridden if your detector returns
390         // tag names from getApplicableElements
391         assert false;
392     }
393 
394     @SuppressWarnings("javadoc")
visitElementAfter(@onNull XmlContext context, @NonNull Element element)395     public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) {
396     }
397 
398     @SuppressWarnings("javadoc")
visitAttribute(@onNull XmlContext context, @NonNull Attr attribute)399     public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) {
400         // This method must be overridden if your detector returns
401         // attribute names from getApplicableAttributes
402         assert false;
403     }
404 
405     @SuppressWarnings("javadoc")
406     @Nullable
getApplicableElements()407     public Collection<String> getApplicableElements() {
408         return null;
409     }
410 
411     @Nullable
412     @SuppressWarnings("javadoc")
getApplicableAttributes()413     public Collection<String> getApplicableAttributes() {
414         return null;
415     }
416 
417     // ---- Dummy implementations to make implementing JavaScanner easier: ----
418 
419     @Nullable @SuppressWarnings("javadoc")
getApplicableMethodNames()420     public List<String> getApplicableMethodNames() {
421         return null;
422     }
423 
424     @Nullable @SuppressWarnings("javadoc")
createJavaVisitor(@onNull JavaContext context)425     public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
426         return null;
427     }
428 
429     @Nullable @SuppressWarnings("javadoc")
getApplicableNodeTypes()430     public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() {
431         return null;
432     }
433 
434     @SuppressWarnings("javadoc")
visitMethod(@onNull JavaContext context, @Nullable AstVisitor visitor, @NonNull MethodInvocation node)435     public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
436             @NonNull MethodInvocation node) {
437     }
438 
439     @SuppressWarnings("javadoc")
appliesToResourceRefs()440     public boolean appliesToResourceRefs() {
441         return false;
442     }
443 
444     @SuppressWarnings("javadoc")
visitResourceReference(@onNull JavaContext context, @Nullable AstVisitor visitor, @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name, boolean isFramework)445     public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor,
446             @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name,
447             boolean isFramework) {
448     }
449 
450     // ---- Dummy implementations to make implementing a ClassScanner easier: ----
451 
452     @SuppressWarnings("javadoc")
checkClass(@onNull ClassContext context, @NonNull ClassNode classNode)453     public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) {
454     }
455 }
456