Detekt
DevOps Tool
Detekt
Overview
Detekt is a static code analysis tool specifically designed for the Kotlin programming language, aimed at improving Kotlin code quality and detecting code smells. Developed as an open-source tool by the Kotlin community, it supports Android, JVM, JS, Native, and multiplatform projects. With highly configurable rule sets, multiple report formats, custom rule creation capabilities, and IDE integration, it provides comprehensive code quality management for Kotlin development.
Details
Detekt serves as the standard static code analysis tool in the Kotlin ecosystem, developed community-driven. With deep understanding of Kotlin language characteristics, it promotes Kotlin-idiomatic code writing, helping build maintainable codebases.
Key Features
- Comprehensive Code Analysis: Detection of code smells, anti-patterns, and potential bugs
- Advanced Configuration: Rule set customization for project-specific requirements
- Rich Report Formats: Support for HTML, Markdown, SARIF, XML (Checkstyle), and custom reports
- Cross-platform Support: Complete support for Android, JVM, JS, Native, and multiplatform
- Baseline Functionality: Gradual introduction support for legacy projects
- Custom Rules: Project-specific rule creation and application capabilities
- Complexity Reports: Measurement of cyclomatic complexity, lines of code, and code smell counts
- IDE Integration: Real-time analysis in IntelliJ IDEA, Android Studio, etc.
- ktlint Integration: Unified execution of code style enforcement and Detekt analysis
2025 Features
- Kotlin 2.0 Support: Complete compatibility with latest Kotlin language features
- Performance Improvements: Analysis speed optimization for large projects
- Enhanced Baseline: More flexible baseline management functionality
- Improved Reporting: More detailed and practical report generation
Pros and Cons
Pros
- Deep language understanding and optimized analysis through Kotlin-specific design
- Continuous improvement and updates by open-source community
- Unified quality management across Android, JVM, and multiplatform development
- Project-specific requirement support through highly configurable rules
- Gradual improvement of legacy code through baseline functionality
- Flexible quality status visualization through multiple report formats
- Real-time quality feedback through IDE integration
- Enterprise/project-specific standard support through custom rule creation
- Centralized style and quality management through ktlint integration
- Automated quality gates through CI/CD integration
Cons
- Limited to Kotlin only, cannot be used for other language projects
- Complexity of initial setup and learning costs
- Long analysis times for large projects
- Risk of development speed reduction due to excessive configuration
- Heavy warning handling burden when applying to legacy code
- Difficulty in understanding and properly configuring rule sets
- Technical requirements for custom rule development
- Maintenance burden from configuration file complexity
- Handling false positives from some rules
- Compatibility management due to Kotlin version dependencies
Reference Links
- Detekt Official Website
- Detekt GitHub Repository
- Detekt Documentation
- Kotlin Official Website
- ktlint Official Website
- Gradle Detekt Plugin
Code Examples
Installation and Basic Setup
Gradle Plugin Installation
// build.gradle.kts (recommended)
plugins {
id("io.gitlab.arturbosch.detekt") version "1.23.6"
}
detekt {
toolVersion = "1.23.6"
config = files("config/detekt/detekt.yml")
buildUponDefaultConfig = true
allRules = false
baseline = file("config/detekt/baseline.xml")
parallel = true
}
dependencies {
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.6")
detektPlugins("io.gitlab.arturbosch.detekt:detekt-rules-libraries:1.23.6")
}
// build.gradle (Groovy)
plugins {
id 'io.gitlab.arturbosch.detekt' version '1.23.6'
}
detekt {
toolVersion = '1.23.6'
config = files('config/detekt/detekt.yml')
buildUponDefaultConfig = true
allRules = false
baseline = file('config/detekt/baseline.xml')
parallel = true
}
Maven Configuration
<!-- pom.xml -->
<plugin>
<groupId>com.github.ozsie</groupId>
<artifactId>detekt-maven-plugin</artifactId>
<version>1.23.6</version>
<configuration>
<config>config/detekt/detekt.yml</config>
<baseline>config/detekt/baseline.xml</baseline>
<parallel>true</parallel>
<buildUponDefaultConfig>true</buildUponDefaultConfig>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Command Line Usage
# Download CLI
curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.6/detekt-cli-1.23.6-all.jar
# Basic execution
java -jar detekt-cli-1.23.6-all.jar --input path/to/project
# Specify configuration file
java -jar detekt-cli-1.23.6-all.jar --input path/to/project --config config/detekt.yml
# Generate report
java -jar detekt-cli-1.23.6-all.jar --input path/to/project --report html:report.html
# Multiple report formats
java -jar detekt-cli-1.23.6-all.jar --input path/to/project --report html:report.html --report xml:report.xml
Basic Execution Methods
Gradle Task Execution
# Basic analysis
./gradlew detekt
# Generate configuration file
./gradlew detektGenerateConfig
# Generate baseline
./gradlew detektBaseline
# Specific rule set only
./gradlew detekt -Ddetekt.config=config/custom-detekt.yml
# Detailed output
./gradlew detekt --info
# Parallel execution
./gradlew detekt --parallel
# All source sets
./gradlew detektMain detektTest
Configuration File (detekt.yml)
# config/detekt/detekt.yml
build:
maxIssues: 0
excludeCorrectable: false
weights:
complexity: 2
LongParameterList: 1
style: 1
comments: 1
config:
validation: true
warningsAsErrors: false
checkExhaustiveness: false
excludes: ""
processors:
active: true
exclude:
- 'DetektProgressListener'
parallel: true
console-reports:
active: true
exclude:
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
- 'FindingsReport'
- 'FileBasedFindingsReport'
output-reports:
active: true
exclude:
- 'TxtOutputReport'
reports:
- type: 'html'
output: 'reports/detekt.html'
- type: 'xml'
output: 'reports/detekt.xml'
- type: 'md'
output: 'reports/detekt.md'
comments:
active: true
CommentOverPrivateFunction:
active: false
CommentOverPrivateProperty:
active: false
UndocumentedPublicClass:
active: false
UndocumentedPublicFunction:
active: false
complexity:
active: true
ComplexCondition:
active: true
threshold: 4
ComplexInterface:
active: false
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
CyclomaticComplexMethod:
active: true
threshold: 15
ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false
LargeClass:
active: true
threshold: 600
LongMethod:
active: true
threshold: 60
LongParameterList:
active: true
functionThreshold: 6
constructorThreshold: 7
ignoreDefaultParameters: false
ignoreDataClasses: true
ignoreAnnotated: []
coroutines:
active: true
GlobalCoroutineUsage:
active: false
RedundantSuspendModifier:
active: false
SleepInsteadOfDelay:
active: true
SuspendFunWithFlowReturnType:
active: true
empty-blocks:
active: true
EmptyCatchBlock:
active: true
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
EmptyClassBlock:
active: true
EmptyDefaultConstructor:
active: true
EmptyDoWhileBlock:
active: true
EmptyElseBlock:
active: true
EmptyFinallyBlock:
active: true
EmptyForBlock:
active: true
EmptyFunctionBlock:
active: true
ignoreOverridden: false
EmptyIfBlock:
active: true
EmptyInitBlock:
active: true
EmptyKtFile:
active: true
EmptySecondaryConstructor:
active: true
EmptyTryBlock:
active: true
EmptyWhenBlock:
active: true
EmptyWhileBlock:
active: true
exceptions:
active: true
ExceptionRaisedInUnexpectedLocation:
active: false
methodNames: 'toString,hashCode,equals,finalize'
InstanceOfCheckForException:
active: false
NotImplementedDeclaration:
active: false
PrintStackTrace:
active: false
RethrowCaughtException:
active: false
ReturnFromFinally:
active: false
SwallowedException:
active: false
ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'
ThrowingExceptionFromFinally:
active: false
ThrowingExceptionInMain:
active: false
ThrowingExceptionsWithoutMessageOrCause:
active: false
exceptions: 'IllegalArgumentException,IllegalStateException,IOException'
TooGenericExceptionCaught:
active: true
excludes: ['src/test/**']
exceptionNames:
- ArrayIndexOutOfBoundsException
- Error
- Exception
- IllegalMonitorStateException
- NullPointerException
- IndexOutOfBoundsException
- RuntimeException
- Throwable
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
TooGenericExceptionThrown:
active: true
exceptionNames:
- Error
- Exception
- Throwable
- RuntimeException
formatting:
active: true
android: false
autoCorrect: true
AnnotationOnSeparateLine:
active: false
autoCorrect: true
AnnotationSpacing:
active: false
autoCorrect: true
ArgumentListWrapping:
active: false
autoCorrect: true
ChainWrapping:
active: true
autoCorrect: true
CommentSpacing:
active: true
autoCorrect: true
EnumEntryNameCase:
active: false
autoCorrect: true
Filename:
active: true
FinalNewline:
active: true
autoCorrect: true
ImportOrdering:
active: false
autoCorrect: true
Indentation:
active: false
autoCorrect: true
indentSize: 4
continuationIndentSize: 4
MaximumLineLength:
active: true
maxLineLength: 120
ModifierOrdering:
active: true
autoCorrect: true
MultiLineIfElse:
active: false
autoCorrect: true
NoBlankLineBeforeRbrace:
active: true
autoCorrect: true
NoConsecutiveBlankLines:
active: true
autoCorrect: true
NoEmptyClassBody:
active: true
autoCorrect: true
NoEmptyFirstLineInMethodBlock:
active: false
autoCorrect: true
NoLineBreakAfterElse:
active: false
autoCorrect: true
NoLineBreakBeforeAssignment:
active: false
autoCorrect: true
NoMultipleSpaces:
active: true
autoCorrect: true
NoSemicolons:
active: true
autoCorrect: true
NoTrailingSpaces:
active: true
autoCorrect: true
NoUnitReturn:
active: true
autoCorrect: true
NoUnusedImports:
active: true
autoCorrect: true
NoWildcardImports:
active: true
PackageName:
active: true
autoCorrect: true
ParameterListWrapping:
active: false
autoCorrect: true
SpacingAroundColon:
active: true
autoCorrect: true
SpacingAroundComma:
active: true
autoCorrect: true
SpacingAroundCurly:
active: true
autoCorrect: true
SpacingAroundDot:
active: true
autoCorrect: true
SpacingAroundKeyword:
active: true
autoCorrect: true
SpacingAroundOperators:
active: true
autoCorrect: true
SpacingAroundParens:
active: true
autoCorrect: true
SpacingAroundRangeOperator:
active: true
autoCorrect: true
StringTemplate:
active: true
autoCorrect: true
naming:
active: true
ClassNaming:
active: true
excludes: ['src/test/**']
classPattern: '[A-Z$][a-zA-Z0-9$]*'
ConstructorParameterNaming:
active: true
excludes: ['src/test/**']
parameterPattern: '[a-z][A-Za-z0-9]*'
privateParameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
EnumNaming:
active: true
excludes: ['src/test/**']
enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
ForbiddenClassName:
active: false
excludes: ['src/test/**']
forbiddenName: []
FunctionMaxLength:
active: false
excludes: ['src/test/**']
maximumFunctionNameLength: 30
FunctionMinLength:
active: false
excludes: ['src/test/**']
minimumFunctionNameLength: 3
FunctionNaming:
active: true
excludes: ['src/test/**']
functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
excludeClassPattern: '$^'
ignoreOverridden: true
ignoreAnnotated: ['Composable']
FunctionParameterNaming:
active: true
excludes: ['src/test/**']
parameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
InvalidPackageDeclaration:
active: false
MemberNameEqualsClassName:
active: true
ignoreOverridden: true
ObjectNaming:
active: true
excludes: ['src/test/**']
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
PackageNaming:
active: true
excludes: ['src/test/**']
packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
TopLevelPropertyNaming:
active: true
excludes: ['src/test/**']
constantPattern: '[A-Z][_A-Z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
VariableMaxLength:
active: false
excludes: ['src/test/**']
maximumVariableNameLength: 64
VariableMinLength:
active: false
excludes: ['src/test/**']
minimumVariableNameLength: 1
VariableNaming:
active: true
excludes: ['src/test/**']
variablePattern: '[a-z][A-Za-z0-9]*'
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
performance:
active: true
ArrayPrimitive:
active: false
CouldBeSequence:
active: false
threshold: 3
ForEachOnRange:
active: true
excludes: ['src/test/**']
SpreadOperator:
active: true
excludes: ['src/test/**']
UnnecessaryTemporaryInstantiation:
active: true
potential-bugs:
active: true
AvoidReferentialEquality:
active: false
forbiddenTypePatterns:
- 'kotlin.String'
CastToNullableType:
active: false
Deprecation:
active: false
DontDowncastCollectionTypes:
active: false
DoubleMutabilityForCollection:
active: false
DuplicateCaseInWhenExpression:
active: true
EqualsAlwaysReturnsTrueOrFalse:
active: false
EqualsWithHashCodeExist:
active: true
ExplicitGarbageCollectionCall:
active: true
HasPlatformType:
active: false
IgnoredReturnValue:
active: false
restrictToAnnotatedMethods: true
returnValueAnnotations: ['*.CheckReturnValue', '*.CheckResult']
ImplicitDefaultLocale:
active: false
ImplicitUnitReturnType:
active: false
allowExplicitReturnType: true
InvalidRange:
active: false
IteratorHasNextCallsNextMethod:
active: false
IteratorNotThrowingNoSuchElementException:
active: false
LateinitUsage:
active: false
excludes: ['src/test/**']
excludeAnnotatedProperties: []
ignoreOnClassesPattern: ""
MapGetWithNotNullAssertionOperator:
active: false
MissingWhenCase:
active: false
NullableToStringCall:
active: false
RedundantElseInWhen:
active: true
UnconditionalJumpStatementInLoop:
active: false
UnnecessaryNotNullOperator:
active: false
UnnecessarySafeCall:
active: false
UnreachableCode:
active: true
UnsafeCallOnNullableType:
active: false
UnsafeCast:
active: false
UselessPostfixExpression:
active: false
WrongEqualsTypeParameter:
active: false
style:
active: true
ClassOrdering:
active: false
CollapsibleIfStatements:
active: false
DataClassContainsFunction:
active: false
conversionFunctionPrefix: 'to'
DataClassShouldBeImmutable:
active: false
DestructuringDeclarationWithTooManyEntries:
active: false
maxDestructuringEntries: 3
EqualsNullCall:
active: false
EqualsOnSignChangingDataType:
active: false
enabled: true
ExplicitCollectionElementAccessMethod:
active: false
ExplicitItLambdaParameter:
active: false
ExpressionBodySyntax:
active: false
includeLineWrapping: false
ForbiddenComment:
active: true
values: ['TODO:', 'FIXME:', 'STOPSHIP:']
allowedPatterns: ""
ForbiddenImport:
active: false
imports: []
forbiddenPatterns: ""
ForbiddenMethodCall:
active: false
methods: []
ForbiddenPublicDataClass:
active: false
excludes: ['src/test/**']
ForbiddenVoid:
active: false
ignoreOverridden: false
ignoreUsageInGenerics: false
FunctionOnlyReturningConstant:
active: false
ignoreOverridableFunction: true
excludedFunctions: 'describeContents'
excludeAnnotatedFunction: ['dagger.Provides']
LibraryCodeMustSpecifyReturnType:
active: false
LibraryEntitiesShouldNotBePublic:
active: false
LoopWithTooManyJumpStatements:
active: false
maxJumpCount: 1
MagicNumber:
active: true
excludes: ['src/test/**']
ignoreNumbers: ['-1', '0', '1', '2']
ignoreHashCodeFunction: true
ignorePropertyDeclaration: false
ignoreLocalVariableDeclaration: false
ignoreConstantDeclaration: true
ignoreCompanionObjectPropertyDeclaration: true
ignoreAnnotation: false
ignoreNamedArgument: true
ignoreEnums: false
ignoreRanges: false
MandatoryBracesIfStatements:
active: false
MandatoryBracesLoops:
active: false
MaxLineLength:
active: true
maxLineLength: 120
excludePackageStatements: true
excludeImportStatements: true
excludeCommentStatements: false
MayBeConst:
active: false
ModifierOrder:
active: true
NestedClassesVisibility:
active: false
NewLineAtEndOfFile:
active: true
NoTabs:
active: false
OptionalAbstractKeyword:
active: true
OptionalUnit:
active: false
OptionalWhenBraces:
active: false
PreferToOverPairSyntax:
active: false
ProtectedMemberInFinalClass:
active: false
RedundantExplicitType:
active: false
RedundantHigherOrderMapUsage:
active: false
RedundantVisibilityModifierRule:
active: false
ReturnCount:
active: true
max: 2
excludedFunctions: "equals"
excludeLabeled: false
excludeReturnFromLambda: true
excludeGuardClauses: false
SafeCast:
active: true
SerialVersionUIDInSerializableClass:
active: false
SpacingBetweenPackageAndImports:
active: false
ThrowsCount:
active: true
max: 2
excludeGuardClauses: false
TrailingWhitespace:
active: false
UnderscoresInNumericLiterals:
active: false
acceptableDecimalLength: 5
UnnecessaryAbstractClass:
active: false
excludeAnnotatedClasses: ['dagger.Module']
UnnecessaryAnnotationUseSiteTarget:
active: false
UnnecessaryApply:
active: false
UnnecessaryInheritance:
active: false
UnnecessaryLet:
active: false
UnnecessaryParentheses:
active: false
UntilInsteadOfRangeTo:
active: false
UnusedImports:
active: false
UnusedPrivateClass:
active: false
UnusedPrivateMember:
active: false
allowedNames: "(_|ignored|expected|serialVersionUID)"
UseArrayLiteralsInAnnotations:
active: false
UseCheckOrError:
active: false
UseDataClass:
active: false
excludeAnnotatedClasses: []
allowVars: false
UseEmptyCounterpart:
active: false
UseIfEmptyOrIfBlank:
active: false
UseIfInsteadOfWhen:
active: false
UseIsNullOrEmpty:
active: false
UseOrEmpty:
active: false
UseRequire:
active: false
UseRequireNotNull:
active: false
UselessCallOnNotNull:
active: false
UtilityClassWithPublicConstructor:
active: false
VarCouldBeVal:
active: false
WildcardImport:
active: true
excludes: ['src/test/**']
excludeImports: ['java.util.*', 'kotlinx.android.synthetic.*']
Android Project Configuration
Android Configuration Example
// app/build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("io.gitlab.arturbosch.detekt")
}
android {
compileSdk 34
defaultConfig {
minSdk 21
targetSdk 34
}
}
detekt {
toolVersion = "1.23.6"
config = files("$projectDir/config/detekt/detekt.yml")
buildUponDefaultConfig = true
allRules = false
baseline = file("$projectDir/config/detekt/baseline.xml")
parallel = true
source = files(
"src/main/java",
"src/main/kotlin",
"src/test/java",
"src/test/kotlin"
)
}
dependencies {
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.6")
detektPlugins("io.gitlab.arturbosch.detekt:detekt-rules-libraries:1.23.6")
}
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
// Target version of the generated JVM bytecode
jvmTarget = "1.8"
}
tasks.withType<io.gitlab.arturbosch.detekt.DetektCreateBaselineTask>().configureEach {
jvmTarget = "1.8"
}
Multiplatform Project Configuration
Kotlin Multiplatform Configuration
// build.gradle.kts (root project)
plugins {
kotlin("multiplatform") version "1.9.10"
id("io.gitlab.arturbosch.detekt") version "1.23.6"
}
kotlin {
jvm()
js(IR) {
browser()
nodejs()
}
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
val commonMain by getting
val commonTest by getting
val jvmMain by getting
val jvmTest by getting
val jsMain by getting
val jsTest by getting
val iosMain by creating
val iosTest by creating
}
}
detekt {
source = files(
"src/commonMain/kotlin",
"src/commonTest/kotlin",
"src/jvmMain/kotlin",
"src/jvmTest/kotlin",
"src/jsMain/kotlin",
"src/jsTest/kotlin",
"src/iosMain/kotlin",
"src/iosTest/kotlin"
)
parallel = true
config = files("detekt-config.yml")
buildUponDefaultConfig = true
}
IDE Integration Configuration
IntelliJ IDEA / Android Studio Configuration
# Plugin installation
# File → Settings → Plugins → Search for "Detekt" and install
# Configuration steps
# File → Settings → Tools → Detekt
# Configuration file: Specify detekt.yml in project root
# Enable Detekt: ✓
# Enable on-the-fly inspection: ✓
# External Tools configuration (optional)
# File → Settings → Tools → External Tools → Add
Name: Detekt
Description: Kotlin static analysis
Program: ./gradlew
Arguments: detekt
Working directory: $ProjectFileDir$
VS Code Configuration
// .vscode/settings.json
{
"kotlin.compiler.jvm.target": "1.8",
"kotlin.detekt.enabled": true,
"kotlin.detekt.configPath": "config/detekt/detekt.yml",
"[kotlin]": {
"editor.defaultFormatter": "fwcd.kotlin",
"editor.formatOnSave": true,
"editor.rulers": [120]
}
}
// tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Detekt Check",
"type": "shell",
"command": "./gradlew",
"args": ["detekt"],
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
}
},
{
"label": "Detekt Baseline",
"type": "shell",
"command": "./gradlew",
"args": ["detektBaseline"],
"group": "build"
}
]
}
CI/CD Integration Examples
GitHub Actions
# .github/workflows/detekt.yml
name: Detekt
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
detekt:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Run Detekt
run: ./gradlew detekt
- name: Upload Detekt reports
uses: actions/upload-artifact@v4
if: always()
with:
name: detekt-reports
path: |
build/reports/detekt/
*/build/reports/detekt/
- name: Annotate PR with Detekt results
uses: lcollins/detekt-action@v1
if: github.event_name == 'pull_request'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
detekt_config_path: config/detekt/detekt.yml
GitLab CI/CD
# .gitlab-ci.yml
stages:
- quality
detekt:
stage: quality
image: openjdk:11-jdk
before_script:
- chmod +x ./gradlew
script:
- ./gradlew detekt
- ./gradlew detekt --continue || true # Generate reports even if issues found
artifacts:
reports:
codequality: build/reports/detekt/detekt.sarif
paths:
- build/reports/detekt/
expire_in: 1 week
only:
- main
- develop
- merge_requests
Custom Rule Creation
Custom Rule Example
// custom-rules/src/main/kotlin/CustomRuleSet.kt
package com.example.detekt.rules
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
class CustomRuleSetProvider : RuleSetProvider {
override val ruleSetId: String = "custom-rules"
override fun instance(config: Config): RuleSet {
return RuleSet(
ruleSetId,
listOf(
NoHardcodedStringsRule(config),
ProperLogTagUsage(config),
AvoidSystemPrintRule(config)
)
)
}
}
// NoHardcodedStringsRule.kt
class NoHardcodedStringsRule(config: Config) : Rule(config) {
override val issue = Issue(
javaClass.simpleName,
Severity.Warning,
"Hardcoded strings should be avoided, use string resources instead",
Debt.FIVE_MINS
)
override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
super.visitStringTemplateExpression(expression)
if (expression.entries.size == 1 && expression.entries[0] is KtLiteralStringTemplateEntry) {
val text = expression.entries[0].text
if (text.length > 10 && !isInTestFile(expression)) {
report(CodeSmell(issue, Entity.from(expression), "Hardcoded string found: $text"))
}
}
}
private fun isInTestFile(element: PsiElement): Boolean {
return element.containingFile?.virtualFile?.path?.contains("/test/") == true
}
}
Applying Custom Rules
# detekt.yml
custom-rules:
active: true
NoHardcodedStringsRule:
active: true
ProperLogTagUsage:
active: true
maxTagLength: 23
AvoidSystemPrintRule:
active: true
excludes: ['src/test/**']
Baseline and Gradual Introduction
Baseline Generation and Management
# Initial baseline generation
./gradlew detektBaseline
# Update baseline
./gradlew detektBaseline --update-baseline
# Check ignoring baseline
./gradlew detekt --auto-correct
Gradual Introduction Strategy
// Gradle task for gradually enabling rules
tasks.register("detektGradual") {
group = "verification"
description = "Run detekt with gradually increasing rule strictness"
doLast {
val phases = listOf("phase1.yml", "phase2.yml", "phase3.yml")
phases.forEach { phase ->
exec {
commandLine("./gradlew", "detekt", "--config", "config/detekt/$phase")
}
}
}
}
Troubleshooting
Common Issues and Solutions
# Memory shortage
export GRADLE_OPTS="-Xmx2g -XX:MaxMetaspaceSize=512m"
./gradlew detekt
# Access conflicts during parallel execution
./gradlew detekt --no-parallel
# Configuration file validation
./gradlew detektGenerateConfig
diff detekt-default.yml config/detekt/detekt.yml
# Clear cache
./gradlew clean
rm -rf ~/.gradle/caches/modules-2/files-2.1/io.gitlab.arturbosch.detekt/
# Dependency verification
./gradlew dependencies --configuration detektPlugins
# Debug execution
./gradlew detekt --info --stacktrace