Home | History | Annotate | Download | only in api
      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
     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
    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
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    375     public abstract Speed getSpeed();
    376 
    377     // ---- Dummy implementations to make implementing XmlScanner easier: ----
    378 
    379     @SuppressWarnings("javadoc")
    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")
    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")
    395     public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) {
    396     }
    397 
    398     @SuppressWarnings("javadoc")
    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
    407     public Collection<String> getApplicableElements() {
    408         return null;
    409     }
    410 
    411     @Nullable
    412     @SuppressWarnings("javadoc")
    413     public Collection<String> getApplicableAttributes() {
    414         return null;
    415     }
    416 
    417     // ---- Dummy implementations to make implementing JavaScanner easier: ----
    418 
    419     @Nullable @SuppressWarnings("javadoc")
    420     public List<String> getApplicableMethodNames() {
    421         return null;
    422     }
    423 
    424     @Nullable @SuppressWarnings("javadoc")
    425     public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
    426         return null;
    427     }
    428 
    429     @Nullable @SuppressWarnings("javadoc")
    430     public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() {
    431         return null;
    432     }
    433 
    434     @SuppressWarnings("javadoc")
    435     public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
    436             @NonNull MethodInvocation node) {
    437     }
    438 
    439     @SuppressWarnings("javadoc")
    440     public boolean appliesToResourceRefs() {
    441         return false;
    442     }
    443 
    444     @SuppressWarnings("javadoc")
    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")
    453     public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) {
    454     }
    455 }
    456