detekt
Meet detekt, a static code analysis tool for the Kotlin programming language. It operates on the abstract syntax tree provided by the Kotlin compiler.
Usage/Build
Building all submodules
- cd detekt
- gradle clean build
Using the command line interface
- cd detekt-cli
- gradle shadow
- java -jar build/libs/detekt-cli-[version]-all.jar
Parameters of CLI
The CLI uses jcommander for argument parsing. This is the generated help text:
The following option is required: --project, -p
Usage: detekt [options]
Options:
--config, -c
Path to the config file (path/to/config).
--filters, -f
Path filters defined through regex with separator ';' (".*test.*").
Default: <empty string>
--help, -h
Shows the usage.
Default: false
* --project, -p
Project path to analyze (path/to/project).
--rules, -r
Extra paths to ruleset jars separated by ';'.
Default: <empty string>
--project can either be a directory or a single Kotlin file. The currently only supported configuration format is yaml. --config should point to one. Filters can for example be used to exclude all test directories. With --rules you can point to additional ruleset.jar's creating by yourself or others. More on this topic see section Custom RuleSets.
RuleSets
Currently there are three rule sets which are used per default when running the cli.
- code-smell - has rules to detect LongMethod, LongParameterList, LargeClass, ComplexMethod ... smells
- style - has rules to detect optional keywords, wildcast imports and implements rules according to Kotlin's coding conventions
- comments - has rules to detect missing KDoc over public members and unnecessary KDoc over private members
RuleSet Configuration
To turn off specific rules/rule sets or change threshold values for certain rules a yaml configuration file can be used.
code-smell:
LongMethod:
active: true
threshold: 20
LongParameterList:
active: false
threshold: 5
LargeClass:
active: false
threshold: 70
...
style:
active: true
...
comments:
active: false
Custom RuleSets
detekt uses a ServiceLoader to collect all instances of RuleSetProvider-interfaces. So it is possible
to define rules/rule sets and enhance detekt with your own flavor.
Attention: You need a resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider file which
has as content the fully qualified name of your RuleSetProvider e.g. io.gitlab.arturbosch.detekt.sampleruleset.SampleProvider.
The easiest way to define an rule set is to clone the provided detekt-sample-ruleset project.
Own rules have to extend the abstract Rule class and override the visitXXX functions from the AST.
A RuleSetProvider must be implemented which declares a RuleSet in the instance method.
To allow your rule to be configurable, pass it a Config object from within your rule set provider.
You can also specify a Severity type for your rule.
Example of a custom rule:
class TooManyFunctions : Rule("TooManyFunctions") {
private var amount: Int = 0
override fun visitFile(file: PsiFile) {
super.visitFile(file)
if (amount > 10) {
addFindings(CodeSmell(id, Entity.from(file)))
}
}
override fun visitNamedFunction(function: KtNamedFunction) {
amount++
}
}
Example of a much preciser rule in terms of more specific CodeSmell constructor and Rule attributes:
class TooManyFunctions2(config: Config) : Rule("TooManyFunctionsTwo", Severity.Maintainability, config) {
private var amount: Int = 0
override fun visitFile(file: PsiFile) {
super.visitFile(file)
if (amount > 10) {
addFindings(CodeSmell(
id = id, entity = Entity.from(file),
description = "Too many functions can make the maintainability of a file more costly",
metrics = listOf(Metric(type = "SIZE", value = amount, threshold = 10)),
references = listOf())
)
}
}
override fun visitNamedFunction(function: KtNamedFunction) {
amount++
}
}
Maven
If your using maven to build rule sets or use detekt as a dependency, you have to run the additional task publishToMavenLocal
Testing your rules
To test your rules you need a KtFile object and use it's visit method. There are two predefined methods to help obtaining a KtFile:
- compileContentForTest(content: String): KtFile
- compileForTest(path: Path): KtFile