Formatting Code Analysis Rule with Android Lint (part 1/2)

For Kotlin and Java at once

Balázs Ruda
Sep 9, 2019 · 9 min read
Image for post
Image for post

TL;DR

In this first part of the article, primarily, I want to show that it’s worth the time to write a custom code analysis rule to enforce code conventions. Secondly, I want to demonstrate that in addition to Checkstyle and ktlint, Android Lint should also be considered when creating a formatting-related code analysis rule, despite that not being its basic purpose. I’ll explain its main advantage in my case against the other tools, and describe the inner workings that provide this benefit. For inspiration, I’ll also show the steps I took to make my formatting rule work with Android Lint.

Motivation

I get frustrated when I get formatting-related comments on my merge requests or when I have to add such comments to others’. Sometimes, we have to change branches in order to fix a single formatting error in our MR, which is quite demotivating. That’s why I started thinking about ways we could automate fixing this minor (yet important) problem, so we could focus on more complex issues during code reviews.

Missing empty line around “block statements” issue

What is the best tool for this problem

We can say that ktlint for Kotlin and Checkstyle for Java are the main formatting code analysis tools, but now I’ll pick another tool yet: Android Lint (not only for Android projects), because it also supports writing custom rules that can be applied to Kotlin and Java at the same time. It seems a more logical choice, because we use both languages in our Android projects, and I wanted neither to write the same rule twice nor maintain them concurrently. Not to mention the integration of the rule, which would also need to be done two times.

Why Java is still important at all

As an Android developer, I have to write code in both Java and Kotlin. However, we can say that it’s not worth the time to focus on Java anymore, because as Kotlin is coming up, we use it more and more instead of Java in our codebase. On the other hand, I believe the transition isn’t happening that quickly in big projects already in production. So, as we want to see nicely formatted code in Java as well, it’s important to have this rule for both languages.

How to write custom lint rules

There are many tutorials and articles about writing custom code analysis rules, so I won’t detail it too much in this post. But if you’re interested, here are a couple of links I can recommend for the different tools: To Checkstyle, the official doc, to ktlint and Android Lint, Niklas Baudy’s great medium articles.

Image for post
Image for post
Image for post
Image for post
Example for AST (source: Wikipedia about Abstract Syntax Tree)

How Android Lint and other static code analysis tools work

Initially, Android Lint was created for finding mainly Android specific issues in Java code. In order to analyze the Java code, Android Lint used to create a Java-specific Abstract Syntax Tree (or just AST, learn more on Wikipedia), which is a tree representation of the source code. Other static code analysis tools also use a language specific AST for analysis;
Checkstyle: Java-, ktlint: Kotlin-, detekt: Kotlin-specific. The tools traverse this AST and find errors by checking its nodes and their attributes.

How Android Lint can check two languages at once

The difference between Android Lint and other tools is that as Kotlin also became a supported language for Android, Android Lint started to support Kotlin as well regarding the rules we already had to Java. For this, they introduced the so called Universal Abstract Syntax Tree (developed by JetBrains) which provides a tree representation of the code that can be applied for both Kotlin and Java languages at the same time, so it’s on a higher abstraction level than a language specific AST. For more information, I recommend this talk part from Tor Norbye — the creator and maintainer of Android Lint.

Image for post
Image for post
Image for post
Image for post
By calling the asRecursiveLogString() method on a certain UAST node in the Android Lint API, we can print out the UAST.
Image for post
Image for post
UIfExpression common interface is implemented by the JavaUIfExpression, JavaUTernaryExpression, and KotlinUIfExpression.

PSI Tree (Program Structure Interface Tree)

The PSI tree is built on the UAST in case of Android Lint (in case of other tools, it’s built on the language specific AST), and this is the tree that contains more details about the structure of the code, such as whitespaces, braces, and similar elements.

Image for post
Image for post
PSI Tree representation of a method in Java and Kotlin

Using both UAST and PSI Tree

I need both UAST and PSI Tree to create my formatting rule, because UAST is great to filter and visit nodes that I’m interested in for both languages at once, but as I mentioned it doesn’t provide information about formatting, eg. whitespace information that is essential for me. UAST rather focuses on a higher abstraction level, so primarily it isn’t for creating formatting rules.

How the rule is implemented

First, I need to create my issue, where I set the scope to JAVA_FILE, which means both Java and Kotlin in Android Lint currently. (Here is where we can set other kinds of files as well such as XML or Gradle files, because Android Lint can check them as well.)

Initialization of a custom Issue class
How to tell Android Lint what nodes we want to check
“Block statement” in value assignment
Single block statement in a method
Having a comment before a “block statement”

The result

As a result, I expect the following warnings in the IDE, so it’ll be clear for each developer that an additional line has to be added. I also can get these warnings in the Lint report if I run lint gradle task. Furthermore, I can even raise the severity of the issue to error, if I want to block the merge request job.

Image for post
Image for post
Image for post
Image for post
IDE warning before and after block statement

Conclusion

After integrating the custom rule, we don’t have to focus on this frustrating issue anymore; instead, we can spend our brain power on finding more complex problems when we review each other’s code. Even better, we can 100% guarantee that this mistake won’t get in our codebase accidentally, because we can configure our MR job to fail when someone misses to fix this issue.

Next part…

If you liked the first part of the article, please check the second part as well, where I’ll detail the integration of my rule in Android (and non-Android) projects, how to fix the already existing issues in our codebase, and how we can unit test and debug our rules.

Source code

If you’re interested in my code more thoroughly, please use this Github link: https://github.com/team-supercharge/lint-checks.

Supercharge's Digital Product Guide

As innovation partners, Supercharge works with clients to…

Thanks to István Mészáros, Csaba Kozák, Tamás F, Niklas Baudy, Attila Polacsek, and Danny Bain

Balázs Ruda

Written by

Android App Developer @ Supercharge

Supercharge's Digital Product Guide

As innovation partners, Supercharge works with clients to create transformative digital solutions. We create digital strategies, design delightful interfaces and build robust software. We are happy to share with you what we learn on this journey.

Balázs Ruda

Written by

Android App Developer @ Supercharge

Supercharge's Digital Product Guide

As innovation partners, Supercharge works with clients to create transformative digital solutions. We create digital strategies, design delightful interfaces and build robust software. We are happy to share with you what we learn on this journey.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store