Step 1. Add the JitPack repository to your build file
Add it in your root settings.gradle at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Add it in your settings.gradle.kts at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Add to pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
Add it in your build.sbt at the end of resolvers:
resolvers += "jitpack" at "https://jitpack.io"
Add it in your project.clj at the end of repositories:
:repositories [["jitpack" "https://jitpack.io"]]
Step 2. Add the dependency
dependencies {
implementation 'com.github.warnergodfrey:detekt:M6'
}
dependencies {
implementation("com.github.warnergodfrey:detekt:M6")
}
<dependency>
<groupId>com.github.warnergodfrey</groupId>
<artifactId>detekt</artifactId>
<version>M6</version>
</dependency>
libraryDependencies += "com.github.warnergodfrey" % "detekt" % "M6"
:dependencies [[com.github.warnergodfrey/detekt "M6"]]
Meet detekt, a static code analysis tool for the Kotlin programming language. It operates on the abstract syntax tree provided by the Kotlin compiler.
The CLI uses jcommander for argument parsing.
The following option is required: --project, -p
Usage: detekt [options]
Options:
--baseline, -b
Treats current analysis findings as a smell baseline for further detekt
runs. If a baseline xml file exists, only new code smells not in the baseline
are printed in the console.
Default: false
--config, -c
Path to the config file (path/to/config).
--filters, -f
Path filters defined through regex with separator ';' (".*test.*").
--format
Enables formatting of source code. Cannot be used together with --config.
Default: false
--help, -h
Shows the usage.
Default: false
--output, -o
True if findings should be written into a report.detekt file inside the
report folder.
Default: false
--parallel
Enables parallel compilation of source files. Should only be used if the
analyzing project has more than ~200 kotlin files.
Default: false
* --project, -p
Project path to analyze (path/to/project).
--report, -rp
Path to the report directory where findings should be stored (if
--output) and baseline.xml generated (if --baseline).
--rules, -r
Extra paths to ruleset jars separated by ';'.
--useTabs
Tells the formatter that indentation with tabs are valid.
Default: false
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 be used for example 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.
The report
parameter is optional and when used, it should point to
gradle detekt
check.dependsOn detekt
if you want to run detekt on every build
repositories {
// if you 'gradle install' all detekt modules
mavenLocal()
// or when all modules should be provided
maven {
url "http://dl.bintray.com/arturbosch/code-analysis"
}
}
configurations {
detekt
}
task detekt(type: JavaExec) {
main = "io.gitlab.arturbosch.detekt.cli.Main"
classpath = configurations.detekt
def input = "$project.projectDir.absolutePath"
def config = "$project.projectDir/detekt.yml"
def filters = ".*test.*"
def rulesets = ""
def params = [ '-p', input, '-c', config, '-f', filters, '-r', rulesets]
args(params)
}
dependencies {
detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.0.M6'
detekt 'io.gitlab.arturbosch.detekt:detekt-formatting:1.0.0.M6'
}
mvn verify
(when using the verify phase as I did here)<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>detekt</id>
<phase>verify</phase>
<configuration>
<target name="detekt">
<java taskname="detekt" dir="${basedir}" fork="true" failonerror="true"
classname="io.gitlab.arturbosch.detekt.cli.Main" classpathref="maven.plugin.classpath">
<arg value="-p"/>
<arg value="${basedir}/src"/>
<arg value="-f"/>
<arg value=".*test.*"/>
<arg value="--useTabs"/>
</java>
</target>
</configuration>
<goals><goal>run</goal></goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>io.gitlab.arturbosch.detekt</groupId>
<artifactId>detekt-cli</artifactId>
<version>1.0.0.M6</version>
</dependency>
</dependencies>
</plugin>
Currently there are six rule sets which are used per default when running the cli.
As of milestone six, the formatting rule set is shipped as an standalone plugin which must be linked to a detekt run through the --rules "path/to/jar" parameter.
To turn off specific rules/rule sets or change threshold values for certain rules a yaml configuration file can be used.
Copy and modify the default-detekt-config.yml
from the detekt folder for your needs.
code-smell:
LongMethod:
active: true
threshold: 20
LongParameterList:
active: false
threshold: 5
LargeClass:
active: false
threshold: 70
...
style:
active: true
...
comments:
active: false
autoCorrect: true
formatting:
ConsecutiveBlankLines:
autoCorrect: true
...
Every rule of the default rule sets can be turned off. Thresholded code-smell rules can have an additional field threshold
.
Formatting
rules can be configured to autoCorrect
the style mistake.
Active
keyword is only needed if you want to turn off the rule. Active
on the rule set level turn off whole rule set.
autoCorrect
on the top level must be set to true or else all configured formatting rules are ignored.
This is done to prevent you from changing your project files if your not 100% sure about it.
detekt now can throw a SmellThresholdReachedError and let the build fail with following config parameters:
build:
warningThreshold: 5
failThreshold: 10
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++
}
}
If you want your rule to be configurable, write down your properties inside the detekt.yml file
and use the withConfig
function:
MyRuleSet:
MyRule:
MyMetric: 5
threshold: 10
OtherRule:
active: false
By specifying the rule set and rule ids, detekt will use the sub configuration of MyRule:
val threshold = withConfig { valueOrDefault("threshold") { threshold } }
If your using maven to build rule sets or use detekt as a dependency, you have to run the additional task install
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:
New with M3 there is a special detekt-test module, which specifies above two methods but also Rule extension functions that allow allow to skip compilation, ktFile and visit procedures.
KtLint was first to support auto correct formatting according to the kotlin coding conventions. In Detekt I made an effort to port over all available formatting rules to detect style violations and auto correct them.
Following configuration I use to check the style for detekt
. If your like me who prefer tabs over spaces, use useTabs
in the
rule set level to turn off indentation check for spaces (or simple turn off Indentation
rule).
autoCorrect: true
formatting:
active: true
useTabs: true
Indentation:
active: false
autoCorrect: false
ConsecutiveBlankLines:
active: true
autoCorrect: true
MultipleSpaces:
active: true
autoCorrect: true
SpacingAfterComma:
active: true
autoCorrect: true
SpacingAfterKeyword:
active: true
autoCorrect: true
SpacingAroundColon:
active: true
autoCorrect: true
SpacingAroundCurlyBraces:
active: true
autoCorrect: true
SpacingAroundOperator:
active: true
autoCorrect: true
TrailingSpaces:
active: true
autoCorrect: true
UnusedImports:
active: true
autoCorrect: true
Specify a report directory with --report
parameter.
Now with --output you can generate a report.detekt
inside your
report directory which holds all findings of current analysis.
With --baseline
you generate a baseline.xml
where code smells are white- or blacklisted.
<SmellBaseline>
<Blacklist timestamp="1483388204705">
<ID>CatchRuntimeException:Junk.kt$e: RuntimeException</ID>
</Blacklist>
<Whitelist timestamp="1483446226153">
<ID>NestedBlockDepth:OptionalSemicolon.kt$OptionalSemicolon$override fun procedure(node: ASTNode)</ID>
<ID>NestedBlockDepth:Indentation.kt$Indentation$override fun procedure(node: ASTNode)</ID>
<ID>EmptyFunctionBlock:Rule.kt$Rule${ }</ID>
<ID>NoElseInWhenExpression:ExplicitGarbageCollectionCall.kt$ExplicitGarbageCollectionCall$when (it.text) { "System", "Runtime.getRuntime()" -> addFindings(CodeSmell(id, Entity.Companion.from(expression))) }</ID>
<ID>NamingConventionViolation:TooManyFunctions2.kt$TooManyFunctions2 : Rule</ID>
</Whitelist>
</SmellBaseline>
The intention of a whitelist is that only new code smells are printed on further analysis. The blacklist can be used
to write down false positive detections. The ID
node must be build of <RuleID>:<Signature>
. Both values can be found
inside the report.detekt
file.