Using Type Resolution
This page describes how to use detekt's type resolution feature.
What is type resolution
Type resolution is a feature that allows detekt to perform more advanced static analysis on your Kotlin source code.
Normally, detekt doesn't have access to the types and symbols that are available to the compiler during the compilation. This restricts the inspection capability. By enabling type resolution, you provide to detekt all the information to understand types and symbols in your code needed to perform more accurate analysis. This extends detekt's inspection capability to ones of the Kotlin compiler.
An example
detekt has a rule called MagicNumber to detect usages of magic numbers in your code.
In the following code:
val user = getUserById(42)?.toString()
detekt is able to report the usage of the number 42 as a magic number, without type resolution. All the information needed to run this inspection is already available in the source code.
Similarly, detekt has another rule called UnnecessarySafeCall to detect unnecessary usages of safe call operators (?.).
In the previous example, detekt is able to determine if the safe call in getUserById(42)?.toString() is required only with type resolution.
This is because detekt needs to know what is the return type of getUserById() in order to correctly perform the inspection. If the return type is a nullable type, then the code is valid. If the return type is a non-nullable type, detekt will report an UnnecessarySafeCall as the ?. is actually not needed.
With type resolution, detekt has access to all the symbols and types of your codebase. Type resolution can be enabled by providing the classpath that is used during compilation. This will give detekt access to all the code used to compile your project (both first and third party code) and will allow more advanced analysis.
Is my rule using type resolution?
If you're running detekt without type resolution, all the rules that require type resolution will not run.
All the rules that require type resolution implement the RequiresAnalysisApi marker interface.
Moreover, their official documentation in the detekt website will mention Requires Type Resolution (like here).
Please note that we do have some rules that have mixed behavior whether type resolution is enabled or not. Those rules are listed here: #2994
Before opening an issue that you're rule is not working, please verify, whether your rule requires type resolution and check if you have type resolution enabled.
Issues and proposals for rules that require type resolution are labelled with needs type and symbol solving on the Issue tracker.
Enabling on a JVM project
The easiest way to use type resolution is to use the Detekt Gradle Plugin. On a JVM project, the following tasks will be created:
detekt- Runs detekt WITHOUT type resolutiondetektMain- Runs detekt with type resolution on themainsource setdetektTest- Runs detekt with type resolution on thetestsource set
Moreover, you can use detektBaselineMain and detektBaselineTest to create baselines starting from runs of detekt with type resolution enabled.
Alternatively, you can create a custom detekt task, making sure to specify the classpath and jvmTarget properties correctly. See the Run detekt using the Detekt Gradle Plugin and the Run detekt using Gradle Task for further readings on this.
Enabling on an Android project
Other than the aforementioned tasks for JVM projects, you can use the following Android-specific gradle tasks:
detekt<Variant>- Runs detekt with type resolution on the specific build variantdetektBaseline<Variant>- Creates a detekt baselines starting from a run of detekt with type resolution enabled on the specific build variant.
Alternatively, you can create a custom detekt task, making sure to specify the classpath and jvmTarget properties correctly.
Doing this on Android is more complicated due to build types/flavors (see #2259 for further context).
Therefore, we recommend using the detekt<Variant> tasks offered by the Gradle plugins.
In case of build related issues, you may try detekt.android.disabled=true in gradle.properties to prevent detekt
Gradle plugins from configuring Android-specific gradle tasks.
Enabling on a KMP project
In a Kotlin Multiplatform project, the Detekt Gradle Plugin generates the following tasks:
detekt- Runs detekt WITHOUT type resolution.detekt<SourceSet>SourceSet- Runs detekt on a given Kotlin source set WITHOUT type resolution. A task is generated for every Kotlin source set (e.g.detektCommonMainSourceSet,detektJvmMainSourceSet,detektIosMainSourceSet,detektWasmJsMainSourceSet).detekt<Compilation><Target>- Runs detekt with type resolution on a given compilation of a JVM or Android target (e.g.detektMainJvm,detektTestJvm,detektMainAndroid).
Type resolution tasks are currently only generated for JVM and Android targets. Native, JS and Wasm targets are analyzed through their source-set tasks listed above, without type resolution.
Corresponding detektBaseline<SourceSet>SourceSet and detektBaseline<Compilation><Target> tasks are also generated to create baselines from each of the analysis tasks.
Enabling on Detekt CLI
If you're using detekt via CLI, you need to pass --analysis-mode full together with the
--classpath and --jvm-target flags. The default is --analysis-mode light, which runs only the
rules that don't need type resolution. See the list of CLI options for details.
Writing a rule that uses type resolution
If you're writing a custom rule or if you're willing to write a rule to contribute to detekt, you might want to leverage type resolution.
Rules that use type resolution implement the RequiresAnalysisApi marker interface and resolve
types through the Kotlin Analysis API inside an
analyze {} block:
class MyRule(config: Config) : Rule(
config,
description = "Reports something that can only be detected with type information."
), RequiresAnalysisApi {
override fun visitCallExpression(expression: KtCallExpression) {
analyze(expression) {
val symbol = expression.resolveToCall()?.successfulFunctionCallOrNull()?.symbol ?: return
// ...
}
}
}
Implementing RequiresAnalysisApi guarantees that your rule only runs in full analysis mode, so
you never have to deal with missing type information. As a rule of thumb, we recommend getting
inspiration from other rules on how they use the Analysis API.
The legacy K1 BindingContext was removed in detekt 2.0. JetBrains maintains a
K1 to Analysis API migration guide
if you are porting an existing rule.
Testing a rule that uses type resolution
To test a rule that uses type resolution, use the lintWithContext extension function
from dev.detekt:detekt-test. It only accepts rules that implement RequiresAnalysisApi, so the
compiler keeps you honest — use plain lint for rules that don't need type information.
If you're using JUnit 5 for testing, you can use the @KotlinCoreEnvironmentTest annotation from
dev.detekt:detekt-test-junit on your test class, and accept a parameter of type
KotlinEnvironmentContainer in the class constructor. You can then access the environment by
referencing the parameter specified in the constructor:
@KotlinCoreEnvironmentTest
class MyRuleSpec(private val env: KotlinEnvironmentContainer) {
@Test
fun `reports cast that cannot succeed`() {
val code = """/* The code you want to test */"""
assertThat(MyRule(Config.empty).lintWithContext(env, code)).hasSize(1)
}
}
If you're using another testing framework (e.g. JUnit 4), you can use the createEnvironment() method from detekt-test-utils.