[Lint] Some questions about writing custom lint rules

545 views
Skip to first unread message

cpu...@twitter.com

unread,
Jul 20, 2015, 8:48:25 AM7/20/15
to adt...@googlegroups.com
Hi!

My team has been working on integration some custom rules in our build process, and we have a few questions:

- Is it possible to enable custom rules to run within Android Studio and highlight errors like built-in rules do?
- Is it possible (and practical!) to run Android Lint on non Android java projects?
- We are generating our custom lint jar as part of the build, and declare a dependent task to all lint tasks that makes it available to the lint tool. ANDROID_LINT_JARS is not convenient for us, since we can't modify an env variable from within the process, and the home directory won't work in shared servers. Copying the jar file into ${buildDir}/lint/lint.jar works, but it feels hacky. What is your recommendation? Also, would it be possible to add a list of custom rule jar files to lintOptions?
- I couldn't find a way to pass configuration parameters for my rules through the Lint options and I have been using Java system properties, but it feels ugly. It would be great to be able to configure rules in the lint configuration file. What is your recommendation for passing configuration into custom lint rules?
- Lint tries to load rules from every jar file in the lint directory, making it impossible for me to add dependent jar files. Is there a way to use custom rule jar files that have external dependencies?
- Your sample code doesn't include support for unit tests, which would be really useful for debugging rules. How can I set them up?

Also, I have a couple of minor questions about writing the rules themselves:

- ResolvedClass has getMethods() but not getFields() (which would be useful, for example, to validate immutability). Is that an intentional omission?
- applicableSuperClasses() can't be used with generic base classes, since the Java visitor seems to check against the full signature (which includes type parameters). We are working around this by selecting all classes instead and checking manually, but it seems wasteful. Would it make sense for the visitor to look up the class name as well?
- Super minor nit: Lint only admits "//noinspection" to disable issues, while AS will also take "// noinspection". We've been using the latter, and it took me a while to figure out the problem!

Sorry about the long email!

Thanks,
César

Tor Norbye

unread,
Jul 20, 2015, 11:10:32 AM7/20/15
to adt...@googlegroups.com, cpu...@twitter.com
Hi!

My team has been working on integration some custom rules in our build process, and we have a few questions:

- Is it possible to enable custom rules to run within Android Studio and highlight errors like built-in rules do?

Yes -- but only in Studio 1.4 which we'll put in canary as soon as 1.3 goes stable (we're at RC3 now.)  

The reason custom lint rules do not show up in the IDE is that the IDE, what you see running are really IntelliJ inspections. We wrap each lint rule as an IDE inspection. And IDE inspections have to be registered *statically* (in a plugin XML registration file). For all the builtin lint rules, we've done this. But we recently fixed this:

Note that there is one limitation though: Normally, in the Analyze window, you get to see the full explanation text for the issue. That doesn't work for third party rules. For custom rules, these will all be using a common category and explanation (Third party Inspection or something like that), and the explanation says that to see each full description to run Gradle's lint target to get the HTML report with full explanations.  So, it's really important that the error message itself be pretty descriptive. 

- Is it possible (and practical!) to run Android Lint on non Android java projects?

It probably doesn't work, since there are a number of assumptions for example that a manifest exist. Note however that lint *should* handle the case where you are using a non-Android library module. It will still look for problems in that non-Android module. So perhaps you could just create a dummy Android app module referencing the non-Android library for this case?

Note however that lint *doesn't* try to duplicate all the "general" programming checks done by most IDEs -- assignment in conditional, etc. Instead it focuses exclusively on flagging just Android-specific issues. The idea was that IDEs and CI plugins already do a pretty good job checking for general Java issues so let's not (a) duplicate effort and (b) have the user end up with 2 sets of error messages for each error.

- We are generating our custom lint jar as part of the build, and declare a dependent task to all lint tasks that makes it available to the lint tool. ANDROID_LINT_JARS is not convenient for us, since we can't modify an env variable from within the process, and the home directory won't work in shared servers. Copying the jar file into ${buildDir}/lint/lint.jar works, but it feels hacky. What is your recommendation? Also, would it be possible to add a list of custom rule jar files to lintOptions?

The best way for this to work is for you to inject your custom rules lint jars by using the exact name "lint.jar", and then packaging this inside a library AAR file that your project depends on. When lint runs on your project, it gathers custom rules provided for any libraries your project is using, if those libraries provide custom rules (and the way to do that is to include them in the AAR payload using that exact location and name inside the AAR file (which is just a .zip).

Longer term we'd like to make it easy to create lint custom rules by just having a new lint source set in your project (next to src/main/java, src/main/test, etc we'd have src/main/lint), and those lint sources would automatically be compiled with the lint API dependencies, and packaged into the AAR (or if in an app module, be used when lintint this project.)

But note that none of this is automated yet; primarily because the lint API is not yet stable, and will probably change a bit more before we get there.
 
- I couldn't find a way to pass configuration parameters for my rules through the Lint options and I have been using Java system properties, but it feels ugly. It would be great to be able to configure rules in the lint configuration file. What is your recommendation for passing configuration into custom lint rules?

I agree, it's ugly, but there isn't a better way to do it yet.
 
- Lint tries to load rules from every jar file in the lint directory, making it impossible for me to add dependent jar files. Is there a way to use custom rule jar files that have external dependencies?

Right now you'll need to use jarjar to include your dependencies (other than lint's built-in dependencies) with your custom rule inside the same jar. That's necessary because there isn't a way to describe what jars it needs and have all the different lint-embedding contexts (studio/intellij, gradle, command line script, eclipse) find and load it with a suitable class loader.
  
- Your sample code doesn't include support for unit tests, which would be really useful for debugging rules. How can I set them up?

There's a lint-tests AAR artifact now which makes this better, but sadly it depends on another library, testutils, which wasn't published right, so it doesn't work at the moment. As soon as that's republished (hopefully as part of the 1.3 push) I'll update the sample which makes it trivial to unit test lint checks.

Also, I have a couple of minor questions about writing the rules themselves:

- ResolvedClass has getMethods() but not getFields() (which would be useful, for example, to validate immutability). Is that an intentional omission?

 
- applicableSuperClasses() can't be used with generic base classes, since the Java visitor seems to check against the full signature (which includes type parameters). We are working around this by selecting all classes instead and checking manually, but it seems wasteful. Would it make sense for the visitor to look up the class name as well?

Yes - that was not intentional, it should be using the raw type there. Fixed by https://android-review.googlesource.com/160420 .
 
- Super minor nit: Lint only admits "//noinspection" to disable issues, while AS will also take "// noinspection". We've been using the latter, and it took me a while to figure out the problem!

Interesting - I think it only used to work for the //noinspection version. I guess we should make it a bit more flexible.


Sorry about the long email!

No problem, thanks for the feedback.

-- Tor 

Jake Wharton

unread,
Jul 21, 2015, 9:30:45 AM7/21/15
to adt...@googlegroups.com, cpu...@twitter.com

Great, informative response! Library devs dearly look forward to a lint source set.


--
You received this message because you are subscribed to the Google Groups "adt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to adt-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jun

unread,
Jan 14, 2016, 8:23:40 PM1/14/16
to adt-dev
Hi Tor,

Are there any instructions on how to add custom lint rules to Android Studio 1.4+ where it'd underline problematic areas within the IDE? I'm unable to figure it out even when looking at  the Analyze menu since I see no signs of any of the custom rules' existence within the menu.

Thanks!

Jake Wharton

unread,
Jan 27, 2016, 11:36:13 PM1/27/16
to adt...@googlegroups.com
I'm pretty sure that works out of the box now? I swear I saw a lint error from a library's lint rule in AS the other day. Will confirm tomorrow.

--

Nagesh Susarla

unread,
Jan 28, 2016, 4:48:02 PM1/28/16
to adt-dev
As of Android Studio 2.0 Preview 8, the lint issues are shown in both batch mode (Analyze -> Inspect Code) as well as in the edit mode (shows the problematic areas in the current file being edited)
In batch mode it shows up under "Android Lint" -> "Error from Custom Lint Check" -> ..
As far as instructions, packaging it as part of the AAR as mentioned in the thread above OR placing the custom jar in $HOME/.android/lint should both work.

-- Nagesh

Tor Norbye

unread,
Jan 29, 2016, 8:14:35 PM1/29/16
to adt-dev
Yep -- even though custom rules started working in the IDE in 1.4/1.5, there was a bug which caused them to sometimes not run (in particular, caching detectors per scope would prevent newly discovered lint rules from project AAR dependencies from showing up.)  As Nagesh said, try the latest Studio 2.0 previews. 

In addition, there is now also a "Show Explanation" quickfix for custom lint rules, which brings up the full explanation text for third party lint rules. This is necessary since in the IDE, all custom rules are mapped to two builtin inspections (custom-error and custom-warning), since there isn't a way to dynamically create inspections in the IDE, they have to be registered statically. 

There were a bunch of other infrastructure improvements in 2.0 too so please give it a try and let us know of any problems before it's too late :-)

--

cpu...@twitter.com

unread,
Feb 6, 2016, 2:00:46 AM2/6/16
to adt-dev
Is the bug you mention caused by the static caches of rules in IssueRegistry? I've tracked down a few bugs we've come across lately to that class, and I wondered whether they'd be fixed in a future release.
Reply all
Reply to author
Forward
0 new messages