Joao Alves

Engineering Manager at Skyscanner

Kotlin Static Analysis — why and how?

Posted at — Feb 26, 2018

image

Hello everyone, welcome to article number 7 of the series. Before we jump into other examples, I think it would be nice to check the quality of the code I wrote so far to support the previous articles. So today we’ll be looking into ways of performing some static analysis on Kotlin code.

For Java code, Checkstyle and Lint are probably the most used tools but what about in Kotlin, how can we check if our code is respecting guidelines and conventions? And how we can scan the project for potential bugs? Lint you can also use for Kotlin but be aware that there’s an open issue at the moment regarding false positives with unused resources (here).

Why?

Today we will be looking at 2 of the most popular options but we’ll get to that later, first let’s see why we need static analysis in our code and list some of the things that we get from it:

Improves code quality by enforcing coding conventions and guidelines

Makes your code cleaner by not allowing unused resources

Improves accessibility and internationalisation by not allowing hardcoded strings and enforcing contentDescription

More understandable and cleaner code

Fewer chances of getting weird bugs> Much easier onboarding for new team members

So, all good basically, right? You might say that some of those things can be handled if you have coding guidelines that everyone in the team has to follow, but that’s not easily enforceable so mistakes will happen. Then you might say that we can pay more attention to those things while reviewing pull requests, we could but do we really want to?

I believe people should focus on the important things when reviewing a pull request and leave stuff that can be automated to a script. If you set it up properly your script won’t fail, while on the other hand you will, trust me.

I hope you’re now sold to this and will definitely use it for all your projects. Specially at work if you’re part of a team this is really really important so please do it.

How?

But enough selling, let’s assume you do want to do this, so let’s see how we can do it for Kotlin. From my research, the 2 most famous static analysis tools around for Koltin seem to be ktlint and detekt so we’ll be looking into both today.

ktlint

Let’s start with ktlint and with the features it offers according to the official GitHub page:

No configuration: ktlint tries to enforce the official code style from kotlinlang.org and Android Kotlin Style Guide

You can still add some custom configurations by adding simple rules to a .editorconfig file. Additional rule sets are also supported

Built-in formatter: no fixing everything by hand, you can format your code with a click of a button or a shortcut.

Customisable output: plain file, json or default and custom reports

A single jar with all the dependencies

Seems promising let’s dive into it, add it to our project and see how it goes. First, we need to add the dependency and the ktlint task in our app module build.gradle file:

repositories {  
    jcenter()  
}  
configurations {  
    ktlint  
}
dependencies {  
    // ktlint  
    ktlint "com.github.shyiko:ktlint:0.15.0"  
}
task ktlint(type: JavaExec, group: "verification") {
    description = "Check Kotlin code style."
    classpath = configurations.ktlint
    main = "com.github.shyiko.ktlint.Main"
    args "src/**/*.kt"
    // args "--reporter=checkstyle, output=${buildDir}/ktlint.xml
}

This is it, ktlint is now configured in our project. Super easy right? To run the check we just need to run ./gradlew ktlint ant that’s it. Let’s try it then and see the results:

image

Ok, this is something, 24 errors. Someone was not doing his best while writing all the code used so far in the series 😛. If you want to generate a custom XML report just uncomment the last line on the gradle task definition above.

Straight away we can see on the first and last lines issues with 2 files that we don’t even use, so let’s get rid of them and fix all the other issues and see the outcome:

image

Great all problems solved, but if you look at the list above most of the issues were actually extra or missing spaces and unnecessary empty lines. But it didn’t fail for files that don’t contain an empty line at the end, so let’s try and add that rule. All we need to do, is create a file named .editorConfig in the root folder of our project with following content and that’s it:

root=true  
[*.{kt,kts}]  
insert_final_newline= true

If we run ./gradlew ktlint again this is what we get now:

image

A lot of files missing empty lines in the end. We added this one at work as some diff tools don’t handle very well files without it so better play it safe. Make sure to go to your Kotlin file templates in Android Studio and add those empty lines when creating a new file as it’s not there by default and you’ll get tired of all these errors quickly.

If you’re familiar with using Checkstyle and Lint in your project you’ll notice that this is more a replacement for Checkstyle with Kotlin than anything. You should keep using Lint for all the other checks. That said, if you want a quick way to enforce code styles and guidelines in Kotlin this is definitely a very good option. As you can see is pretty quick and straight forward to setup and start using. We’re using it Babylon and we’re quite happy with it so far.

detekt

Moving on to detekt and starting the same way, with some of the features it offers according to their official GitHub page:

Code smell analysis

Code complexity reports: reports based on logical lines of code and McCabe complexity for example

Highly configurable (rule set or rule level)

Thresholds configurations: configure your checks to break builds or just print warnings

Baseline file and ignore lists: ignore existing issues and start checking all new code straight away

Modules and sub-modules support

Looks like it offers a few more things than ktlint but let’s give it a try and find out. Let’s start by adding the dependency. As opposed to ktlint, to add detekt dependency we need to work on the project level build.gradle file and add the following:

buildscript {  
    repositories {  
        jcenter()  
    }  
}
plugins {  
    id "io.gitlab.arturbosch.detekt" version "1.0.0.RC6-3"  
}  
detekt {  
    version = "1.0.0.RC6-3"  
    profile("main") {  
        input = "$projectDir/app/src/main/java"  
        config = "$projectDir/default-detekt-config.yml"  
        filters = ".*test.*,.*/resources/.*,.*/tmp/.*"  
    }  
}

In input you need to include the path to your main src folder. In config is where you put the path to the configuration file to be used to run the checks. We don’t get a default one out of the box but we can generate one by running the ./gradlew detektGenerateConfig command.

Now we can run ./gradlew detektCheck and see what happens. But first we need to stash all the changes we did to fix the errors given by ktlint to see if they come through again:

image

The full output is too big to take a screenshot, but on the image we have all the errors reported and in the end, this is the result we got:

image

So, 38 issues in total and as we can see, the build fails because we reached the 10 code smells that are defined with failThreshold: 10 in our generated config file. Apart from all the errors above, detekt also gives us a few other interesting metrics when running the check command:

image

Now, if we look at the errors, apart from the NewLineAtEndOfFile ones that we added later with ktlint, all the others are different and more specific to code smells rather than just basic code style guidelines. So all those extra and missing spaces errors that don’t respect the official Kotlin and Android guidelines are not flagged here. On the other hand, we’re getting errors regarding classes, functions and variable naming, as well as reports for magic numbers in the code.

If you want to know more about those specific errors check their official documentation for rulesets.

Conclusion

As we could see, both tools are capable of finding a good amount of issues in the code, even with their default configurations. And as you know the project is still quite small so imagine running those in a real life commercial project. You would probably use the baseline files to suppress existing issues and address them later when possible.

While ktlint seems to be very oriented to validate code styles, detekt has that and adds some checks for code smells as well. They both work well with gradle as we saw above, so it should be pretty easy to add to a CI setup to make sure we don’t merge PRs with some of those issues. At Babylon we picked ktlint because by default reflects Kotlin and Android Kotlin official guidelines but I guess detekt could easily do the job as well if we start customising it.

I guess you could use both and deal with the overlapping, but since they’re both quite customisable probably makes more sense to pick one and improve it rather than having 2 tasks scanning the code looking for similar things.

The important thing, in the end, is to use it, you’ll not regret and your code quality will improve a lot after using these tools for a while.No more checks you can go now :) Give some 👏 if you liked the article and as always, please share your ideas and comments. The code is available in the Series Github project (link below). See you at number 7 👋

Kotlin Sealed Classes — enums with swag ⇦ PREVIOUS

NEXT ⇨ Kotlin backend? Yes it’s possible


comments powered by Disqus