Wednesday, May 2, 2018

A Tale of Lint and Android

I'm an Android Developer who spent the last few years working with React and React Native development. One my favorite tools of that world was ESLint, a static analysis tool to help prevent errors and standardize code styles. My exposure to ESLint stuck with me as I have returned to Android development and grappled with improving my code. Eventually I started to look for familiar tools and found that Android offered a flexible linting api similar to ESLint. Oh how lucky I was!

Spoiler, I was less than lucky, this is a story of my (ultimately successful) misadventures with Android Lint.

If you are an Android developer, chances are you're acquainted with Android Lint. It's that tool that is often ignored, regularly disabled, and rarely extended. If that weren't enough, there is no official API and documentation online covers 3 different apis that follow the same principles with distinct syntaxes. With this type of treatment you can accurately concluded that finding the right documentation can be... challenging.

Side note: If you have Lint disabled, I urge you to turn it on, it's a cheap way to start improving your code quality. If you have a legion of lint issues to be fixed, you can create a lint baseline file. This will let you put off fixing lintmageddon while preventing new lint issues.

So I set out to find documentation on the subject and chronicled my findings. Here I am a few months later, and wiser, to give my tale and lessons to you...

The people! Lint is yours!
I started at the obvious place, Android's documentation pages on lint, but found they did not cover how to create your own lint rules. After trekking through Github and Google, I eventually came upon docs that explained the concepts of writing lint rules and offered tutorials (one, two and three).

They were a few years old and dusty, but what could go wrong? I tasked myself with building a lint rule and managed to get through half of it before thinking, "how will this work with my Kotlin project". Simply said, it didn't.

Based on what little I could find, there was a new parse tree implementation, the UastTree (Universal Abstract Syntax Tree) which handled Java and Kotlin files. To use this api, I had to extend slightly different classes. If relearning the api wasn't confusing enough, there was a brief period of time where the standard was the PSI tree but that had been abandoned.

This is where the trail went cold. I couldn't find any docs on using the UastScanner, save the ones in the class itself which were helpful, but still lacking (you can open them in Android Studio, I couldn't find it online). Beyond that, my knowledge was gleaned from trial and error and the rules written by Jetbrains. I even tried posting on r/androidDev to see if I could get some community input, but the response was lacking.

Eventually I circled back to the articles I originally found on JavaScanner, and discovered a gem of a comment that was hidden right under my nose. It linked to a guide on how to make use of the new UastScanner and was paired with a repo overflowing with more examples.
So I wrote my lint rule after much struggle and then learned how to deploy it with some trial and error. Even after this point I was still confused on some major points. Why didn't my link rule show up in Android Studio? Why was there so much churn on the Lint API? Why did my Uast elements have PSI elements on them? And why had I still not found any official docs??
All these answers were shortly answered when I stumbled upon the lint-dev google group. This was the goal I had been searching for the whole time, the home for official lint information. This presentation and the corresponding slide deck from the lint-dev group clarified all those details I had buzzing in my head.

The reason my lint rule didn't show up in the IDE, was that it relied on processing multiple files. Lint was changing a lot because they were trying to generalize the API to handle more file types (first Groovy and then later Kotlin). And PSI elements were present in Uast elements to allow you access to Java specific constructs.

At long last I have managed to write both my lint rules and tests, and for the most part, my library seem to be working well. Looking back on the journey I would ask for a few things from Google to help pave the path:
  • Links from the lint doc pages that Android has, to the google group 
  • Clarity in the docs that the API for writing lint rules is unstable 
  • Support for 100% of lint rules in the IDE (as covered in Tor's talk) 
  • The up-and-coming stable api for writing lint rules 
To any that follow my path, I hope you find my tale amusing, and the resources I shared help you on your journey.


Post a Comment