Lint‎ > ‎

Infer Support Annotations

We're working on a new feature to automatically infer annotations in your code. The feature is still experimental and hidden behind a flag, but if you'd like to try it we'd really appreciate your feedback and bug reports.

Resource Type Annotations (@StringRes, @DrawableRes, ...)

First, if your method extends or implements a method that has resource type annotations in its parameters or on the return value, these are then added to the overriding method as well.

Second, if we find a call to a method where the parameter values are annotated, then we attempt to flow the annotations back to the arguments. For example, in the following code snippet:
    int convert(int d) {
    return getResources().getDimensionPixelOffset(d);
    }
it can conclude that parameter d should be annotated @DimenRes since its usage in the getDimensionPixelOffset call matches up with a @DimenRes annotation on the first parameter in that method.

Third, if we find an explicit resource type in a parameter, we can infer a resource type annotation on the given parameter in the target method; e.g. in the following we can infer that parameter "int s" should have @StringRes:
    private void usage() {
    display(R.string.app_name);
    }

    private void display(int s) {
Fourth, similar logic is applied to method annotations, e.g. returning an annotated parameter or explicit resource reference lets it infer annotations on the return value.

Permission Annotations (@RequiresPermission)

If a method calls one of the "enforce" permission calls (enforcePermission, enforceCallingOrSelfPermission, etc) , then we look up the permission from the arguments and infer that the surrounding method also requires that permission.

Similarly, if you call a method that has already been annotated with @RequiresPermission, we flow that annotation up to the surrounding method as well.

In both cases, we only do this if the call is reached unconditionally from the method entry.

Shrinking Annotations (@Keep)

The code analyzer also looks for reflection usage (e.g. Class.forName, method.getDeclaredMethod, etc) and if it recognizes a method signature, and it finds that method signature elsewhere in the project, it will annotate that method with @Keep (which as of 2.2 is automatically passed as a -keep rule to ProGuard during builds.)

Future Annotations

We're investigating adding analysis to infer additional annotations, so you might want to periodically repeat the analysis to see if additional annotations are inferred.

We're trying to strike a balance between adding annotations that are useful and can help lint pinpoint issues, and avoiding adding too many annotations for things that are obvious. For example, we could fairly easily add a lot of @IntRange annotations on every parameter we conclude must be positive (e.g. is used in an array access context) but that might be more noise than be helpful.

Enabling Feature

You should add the following line to your custom idea.properties file. NOTE: do not edit the idea.properties file in the installation directory! There's an easy way to create  custom version if you haven't already: Invoke Help > Edit Custom Properties. This will create the file if it doesn't exist already, and then open it in the editor. Then add this line:
studio.infer.annotations=true

Then restart the IDE.

Usage

Invoke Analyze > Infer Support Annotations... If your project does not already directly or indirectly depend on the support annotations library, you'll be offered to add a dependency. Then, the analysis begins. This will run for a while, and if there are a lot of results, will open up a Preview window where you can see the affected elements and then hit OK to apply. (If there are fewer than 5 results, it will just add them).

It will also open up a simple "report" text file in the editor, explaining the analysis it performed (example below). This is useful during development of this feature. Let us know if you find this useful in figuring out what happened to your project and whether we should clean it up and include it as an optional report in the analysis when the feature is enabled by default as wel.

INFER SUPPORT ANNOTATIONS REPORT
================================
Class Child:
  Method foo:
      @DrawableRes because it extends or is overridden by an annotated method
  Method something:
    Parameter int foo:
      @DrawableRes because it extends a method with that parameter annotated or inferred 
Class EnforcePermission:
  Method impliedPermission:
      @RequiresPermission(EnforcePermission.MY_PERMISSION) because it calls enforceCallingOrSelfPermission
  Method unconditionalPermission:
      @RequiresPermission(EnforcePermission.MY_PERMISSION) because it calls EnforcePermission#impliedPermission
Class InferMethodAnnotationFromReturnValue:
  Method test:
      @DrawableRes because it returns R.mipmap.some_image
      @IdRes because it returns id

Comments