Analyzing Kotlin sources just got simpler

Marcin Aman
VirtusLab

--

Analyzing Kotlin code used to be hard and required extensive know-how, because most of the concepts required to make it work were internal and not documented. Not anymore.

Dokka is a documentation engine for Kotlin, performing the same function as javadoc for Java. It understands standard Javadoc comments in Java files and KDoc comments in Kotlin files, and can generate documentation in multiple formats including standard Javadoc, HTML and Markdown.
Recently it has been rewritten to be more flexible and allow users to write custom plugins. Thanks to its pluggability, Dokka code is available as separate artifacts and can be added as project dependencies. The newest 1.4 release introducesdokka-analysis dependency that makes it easy to analyse Kotlin and Java code. Let’s learn how we can use it by setting up a simple project that will analyse the source code of klaxon, a JSON parser for Kotlin.

First, we need to add dependencies on the dokka-analysis and dokka-core artifacts to provide interfaces required for the job:

implementation("org.jetbrains.dokka:dokka-analysis:1.4.10.2")    implementation("org.jetbrains.dokka:dokka-core:1.4.10.2")

Second, we need to define what should be analysed by defining a source set:

After that we are almost set up — all we need is to create a KotlinAnalysis
instance and get the packages from the code:

Now that the hard work is over, let’s dive into how we can analyse Kotlin code. We got some objects representing packages called Descriptors. Kotlin uses them to represent its own code. They contain all the information about accessibility, documentation, declared elements etc., a broader explanation of this topic is better left for another discussion. For now, let’s keep to this simplified view.

We can extract the data from those directly, but I think it is a great opportunity to use a visitor pattern — by extending a basic visitor from the Kotlin compiler we can gather information about relevant pieces of code.

To show a basic example, let’s find the names of all classes in the source set and determine which one is the longest. Piece of cake!

This may look convoluted at first, but it can be very quickly understood. In this case, we visit a package first, then analyse a class to get its name and return all names in a list. Simple stuff.

After cloning the repo and running our program we get:

Longest class name: JsonParsingException, length: 20

Now let’s create something a little bit more advanced. Suppose that the API of some class has changed, and we would like to estimate how much work it would take to adjust our codebase to the new standard. To measure that, we need information about functions that take this class as a parameter. Of course, this won’t give us a whole picture, but for sake of simplicity of this article, it is sufficient.

Let’s go back to our previous code and check how we can extract information about the function’s parameters. First, let’s create a handler for function descriptors as we definitely will need them:

Then we should analyse what this descriptor contains. We can see that it holds its parameters list in valueParameterproperty, bingo!

Ok, but how can we get information about functions? To do that, we need to look into places where they can be stored. In Kotlin functions can be declared on package level or class level. For each place we need to check what is declared there using getContributedDescriptors and filter for functions:

It might be useful to fetch the information about constructors as well, as they practically are functions:

Usages as a parameter for: kotlin.reflect.KClass: 
TypeFor/<init>
Annotations/Companion/findJsonAnnotation
Annotations/Companion/findProperties
Annotations/Companion/findNonIgnoredProperties
Annotations/Companion/findJsonPaths
Annotations/Companion/findJsonPaths
Annotations/Companion/isList
Annotations/Companion/retrieveJsonFieldName
JsonObjectConverter/fromJson
JsonObjectConverter/initIntoUserClass
JsonObjectConverter/retrieveKeyValues
JsonObjectConverter/calculatePolymorphicClass
JsonObjectConverter/PolymorphicInfo/<init>
Klaxon/parser
Klaxon/fieldConverter
Klaxon/fromJsonObject
Reflection/Companion/isAssignableFromAny

The project used in this article is available on Github.

With the latest Dokka release users have received not only a significantly better documentation tool, but also an easy way to have fun playing with source code analysis. I hope that in the foreseeable future more and more tools will be built on those foundations to make writing in Kotlin even more enjoyable experience.

--

--