Looking for static analysis tool to detect calls to certain functions or properties

We use a third party library that unfortunately has a bug in it when retrieving a certain member property on a type (but in general we might want this on a function call as well). We have implemented our own extension function that works around the bug. I can make sure to remove all accesses to that property or function, but I want to prevent someone accidentally using the property or function in the future so was looking for a static analysis tool that can achieve that.

So for illustration lets say we have a type Foo and it has a member property of Foo.bar or a member function of Foo.bar(). We want to have an error if any code calls the getter on the property or calls the function.

Ktlint and Detekt will not work for this as they do not have type information in the rule. If you have an expression like f.bar it has no way to know the type of f to know if it is of type Foo or some other type that happens to have a similar named property (In our actual case there are other types with members of the same name)

I am guessing that it will take some form of compiler plugin. I looked at Arrow Analysis and it doesn’t seem to let you write custom rules. I could probably put something together using Arrow Meta in a day but wondered if there might already be some kind of tool to make the job easier.

1 Like

I think that might be doable with ArchUnit. Violations will be reported as test failures rather then compile errors, but in most setups, that should be ok.

Does look interesting and might find most of the cases. Main issue is that it is Jvm only, while our project is a KMM app for iOS and Android. Another complication is that I also need to check the tests themselves. But I will look more into it and can see other uses for it.

In the past I did quite a lot of heavy static analysis of JVM applications and I used either Soot or Wala frameworks. But they are pretty “academic” and they are probably too heavy for your needs. Also, as above they can’t be used for multiplatform.

Also, you need to be aware there is a big difference between static analysis of classes, members and other definitions/symbols and static analysis of the executable code. For example, by looking quickly at ArchUnit docs it seems all mentioned use cases only touch symbols. It is the same for KSP. For your use case you need to read the contents of methods and this is more advanced stuff, not always supported by static analysis frameworks.

Since you seem to have a fix for the bug (with the extension function you mentioned) couldn’t you file this fix with the library you’re using? Then you could continue to use the library and would help others as well.

My team had a very good experience with adding custom lint rules to Detekt (Documentation). You just have tu run it with type resolution enabled.

For example we were able to remove calls to LocalDate.of(Int, Int, Int) to use LocalDate.of(Int, Month, Int) from our codebase by implementing a lint rule forbidding it.

Only downside is that you have to play with the Kotlin compiler internals , and that is badly documented so you have to whip out the debugger and try a lot of things before finding what you want. But existing Detekt rules provide good examples and testing your custom rules is easy.

My workaround was to use another library that does the same the work so not a fix for them. I of course did file it as a bug and they have fixed it, but unfortunately due to some other changes they made to the library we cannot upgrade to it.

Thanks. Was not aware of the ability to enable type resolution. Will try that.

Oddly enough our case also involves a time library, but in our case Klock which had a broken DateTime.local conversion for iOS that did not handle DST correctly. We are going to try to move away from Klock to kotlinx-datetime, which didn’t exist in the early days of our project, but that is a large job.

If you’re looking for a static analysis tool that can enforce restrictions on member properties or functions at compile time, you’re correct that you’ll likely need to use a compiler plugin or a language-specific tool that supports custom rules. Here are a few options you can consider:

Arrow Meta: As you mentioned, Arrow Meta is a Kotlin library for meta-programming and allows you to write custom compiler plugins and rules. While it may require some effort to set up, it provides flexibility in defining custom static analysis rules.

Kotlin compiler plugins: The Kotlin compiler itself supports custom plugins that can modify the compilation process and enforce additional checks. You can write a compiler plugin that analyzes the AST (abstract syntax tree) and emits warnings or errors when it encounters the specific usage of the property or function you want to disallow.

SonarQube: SonarQube is a popular static code analysis tool that supports multiple languages, including Kotlin. It has a plugin ecosystem that allows you to extend its functionality with custom rules. You can write a custom SonarQube plugin for Kotlin that enforces the restriction you want.

IntelliJ IDEA inspections: IntelliJ IDEA, a popular IDE for Kotlin development, provides an inspection feature that can detect and highlight potential issues in your code. It also supports creating custom inspections based on specific patterns. You can define a custom inspection that identifies the usage of the property or function you want to prevent and reports it as a warning or error.

In any of these approaches, you would need to define a custom rule or inspection that analyzes the code and reports an error or warning when it encounters the disallowed property or function usage.

Keep in mind that implementing custom static analysis rules can be a non-trivial task, especially when it comes to analyzing type information accurately. It may require a deep understanding of the Kotlin compiler or the specific tools you choose to work with, so it might be worth getting help from software development experts https://tech-stack.com/services/custom-software-development to help you implement this.