Detekt для Kotlin: полное руководство с примерами
1. Что это и зачем нужна
Detekt — это мощный инструмент статического анализа кода для языка Kotlin, созданный компанией Arturbosch и поддерживаемый сообществом. Он предназначен для автоматического обнаружения потенциальных проблем, "запахов кода" (code smells), нарушения стилевых соглашений и уязвимостей в проектах на Kotlin. Detekt работает на уровне абстрактного синтаксического дерева (AST), что позволяет ему анализировать код глубже, чем простые текстовые линтеры. Инструмент интегрируется с системами сборки Gradle, Maven и другими CI/CD инструментами, что делает его незаменимым для поддержания качества кода в командной разработке.
Зачем нужен Detekt? Во-первых, он автоматизирует проверку кода на соответствие стандартам Kotlin Coding Conventions, что особенно важно в больших проектах с участием нескольких разработчиков. Во-вторых, Detekt помогает выявлять потенциальные баги на ранних стадиях: например, неиспользуемые переменные, возможные NullPointerException, сложные условные конструкции и другие проблемы, которые могут привести к ошибкам в рантайме. В-третьих, инструмент способствует рефакторингу: он подсвечивает участки кода с высокой цикломатической сложностью, длинные функции, избыточные элвис-операторы и другие паттерны, которые ухудшают читаемость и поддерживаемость кода.
Detekt предоставляет более 150 встроенных правил, разбитых на категории: потенциальные ошибки (potential bugs), стиль кода (style), производительность (performance), сложность (complexity), исключения (exceptions) и другие. При этом инструмент полностью настраиваем: вы можете включать/отключать отдельные правила, менять их пороговые значения, а также писать собственные правила на Kotlin. Detekt поддерживает генерацию отчетов в форматах HTML, XML, SARIF и текстовом, что позволяет интегрировать его с любыми системами анализа кода.
2. Установка
Detekt устанавливается как плагин для системы сборки Gradle (рекомендуемый способ) или как отдельный CLI-инструмент. Ниже приведены точные команды для установки.
Установка через Gradle (плагин)
Добавьте в файл build.gradle.kts (или build.gradle):
// build.gradle.kts
plugins {
id("io.gitlab.arturbosch.detekt") version "1.23.6" // актуальная версия на момент написания
}
// Настройка Detekt (опционально)
detekt {
buildUponDefaultConfig = true // использовать стандартные правила + свои
allRules = false // не включать все правила (только рекомендованные)
config.setFrom("$projectDir/config/detekt/detekt.yml") // путь к конфигу
baseline = file("$projectDir/config/detekt/baseline.xml") // baseline для игнорирования ошибок
reports {
html.required.set(true) // генерировать HTML-отчет
xml.required.set(true) // генерировать XML-отчет
txt.required.set(true) // генерировать текстовый отчет
}
}
После добавления плагина выполните команду:
./gradlew detekt
Установка через CLI (Homebrew для macOS/Linux)
brew install detekt
Установка через SDKMAN
sdk install detekt
Установка через JAR (универсальный способ)
curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.6/detekt-cli-1.23.6-all.jar
alias detekt='java -jar detekt-cli-1.23.6-all.jar'
3. Быстрый старт — минимальный рабочий пример
Создайте простой Kotlin-файл src/main/kotlin/example.kt:
// example.kt
fun main() {
val x = 1
val y = 2
if (x > 0) {
println("Positive")
} else {
println("Non-positive")
}
println("Sum: ${x + y}")
}
Создайте конфигурационный файл config/detekt/detekt.yml (можно пустой, Detekt использует стандартные настройки):
# config/detekt/detekt.yml
build:
maxIssues: 0
Запустите Detekt через Gradle:
./gradlew detekt
Вы увидите отчет в консоли. Если все хорошо, Detekt не найдет нарушений. Теперь измените код, чтобы Detekt нашел проблему — например, удалите использование переменной y:
fun main() {
val x = 1
val y = 2 // Unused variable
if (x > 0) {
println("Positive")
}
}
Запустите Detekt снова — он выдаст предупреждение о неиспользуемой переменной y.
4. Основные методы/функции/классы
Detekt предоставляет множество классов и методов для настройки и запуска анализа. Ниже приведены 8 ключевых элементов API.
4.1. DetektFacade — основной класс для запуска анализа
Сигнатура:
class DetektFacade(
settings: Settings,
ruleSetProvider: List<RuleSetProvider>,
config: Config
)
Что делает: Это главный класс, который инициализирует и запускает процесс статического анализа. Он принимает настройки, провайдеры наборов правил и конфигурацию.
Пример:
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.FileProcessor
import io.gitlab.arturbosch.detekt.core.DetektFacade
import io.gitlab.arturbosch.detekt.core.config.ConfigFactory
import io.gitlab.arturbosch.detekt.core.rules.RuleSetProvider
fun analyzeCode(code: String) {
val config = ConfigFactory.load("detekt.yml")
val facade = DetektFacade(
settings = Settings(),
ruleSetProvider = listOf(RuleSetProvider()),
config = config
)
val result = facade.run(listOf(FileProcessor(code)))
println(result)
}
4.2. ConfigFactory — загрузка конфигурации
Сигнатура:
object ConfigFactory {
fun load(configPath: String): Config
fun load(configPath: String, fallbackConfig: Config): Config
}
Что делает: Загружает YAML-конфигурацию Detekt из файла или ресурсов. Позволяет указать fallback-конфигурацию (например, стандартную).
Пример:
import io.gitlab.arturbosch.detekt.core.config.ConfigFactory
fun loadCustomConfig(): Config {
val config = ConfigFactory.load("custom_detekt.yml")
val defaultConfig = ConfigFactory.load("default-detekt-config.yml")
return ConfigFactory.load("custom_detekt.yml", defaultConfig)
}
4.3. RuleSetProvider — провайдер набора правил
Сигнатура:
interface RuleSetProvider {
val ruleSetId: String
fun getRuleSet(config: Config): RuleSet
}
Что делает: Интерфейс для создания кастомных наборов правил. Вы можете реализовать его, чтобы добавить свои правила анализа.
Пример:
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
class MyRuleSetProvider : RuleSetProvider {
override val ruleSetId: String = "my-rules"
override fun getRuleSet(config: Config): RuleSet {
return RuleSet(ruleSetId, listOf(MyCustomRule(config)))
}
}
4.4. Rule — базовый класс для создания правил
Сигнатура:
abstract class Rule(
config: Config,
description: String = "",
val severity: Severity = Severity.Warning
) : TokenVisitor
Что делает: Абстрактный класс, от которого наследуются все правила Detekt. Содержит методы для обхода AST и выдачи предупреждений.
Пример:
import io.gitlab.arturbosch.detekt.api.*
import org.jetbrains.kotlin.psi.KtNamedFunction
class TooManyParametersRule(config: Config) : Rule(config) {
override val issue = Issue(
id = "TooManyParameters",
severity = Severity.Warning,
description = "Функция содержит слишком много параметров",
debt = Debt.FIVE_MINS
)
override fun visitFunction(function: KtNamedFunction) {
if (function.valueParameters.size > 5) {
report(CodeSmell(issue, Entity.from(function), "Функция имеет ${function.valueParameters.size} параметров"))
}
}
}
4.5. CodeSmell — представление найденной проблемы
Сигнатура:
data class CodeSmell(
val issue: Issue,
val entity: Entity,
val message: String,
val references: List<Entity> = emptyList(),
val metrics: List<Metric> = emptyList()
)
Что делает: Класс, представляющий одну найденную проблему в коде. Содержит информацию о правиле, местоположении, сообщении и метриках.
Пример:
import io.gitlab.arturbosch.detekt.api.*
fun createSmell(issue: Issue, entity: Entity): CodeSmell {
return CodeSmell(
issue = issue,
entity = entity,
message = "Найдена потенциальная проблема",
metrics = listOf(Metric("complexity", 15))
)
}
4.6. Entity — информация о местоположении в коде
Сигнатура:
data class Entity(
val name: String,
val signature: String,
val location: Location,
val ktElement: KtElement? = null
)
Что делает: Содержит информацию о конкретном элементе кода: имя, сигнатуру, местоположение (файл, строка, колонка) и ссылку на элемент PSI.
Пример:
import io.gitlab.arturbosch.detekt.api.*
fun createEntity(element: KtElement): Entity {
return Entity(
name = element.name ?: "anonymous",
signature = element.text,
location = Location(
source = SourceLocation(element.containingFile?.name ?: "unknown"),
line = element.lineNumber ?: 0,
column = element.columnNumber ?: 0
),
ktElement = element
)
}
4.7. DetektReport — генерация отчетов
Сигнатура:
interface DetektReport {
val format: String
fun render(result: Detektion): String
}
Что делает: Интерфейс для создания отчетов в различных форматах. Встроенные реализации: HtmlReport, XmlReport, TextReport, SarifReport.
Пример:
import io.gitlab.arturbosch.detekt.report.html.HtmlReport
import io.gitlab.arturbosch.detekt.api.Detektion
fun generateHtmlReport(result: Detektion): String {
val htmlReport = HtmlReport()
return htmlReport.render(result)
}
4.8. Detektion — результат анализа
Сигнатура:
interface Detektion {
val findings: Map<String, List<CodeSmell>>
val errors: List<String>
val metrics: List<Metric>
val runErrors: List<String>
}
Что делает: Интерфейс, представляющий результат выполнения анализа. Содержит все найденные проблемы, ошибки выполнения и метрики.
Пример:
import io.gitlab.arturbosch.detekt.api.Detektion
fun printFindings(result: Detektion) {
result.findings.forEach { (ruleSet, smells) ->
println("Набор правил: $ruleSet")
smells.forEach { smell ->
println(" - ${smell.issue.id}: ${smell.message} (строка ${smell.entity.location.line})")
}
}
}
5. Реальный практический пример (100-200 строк кода)
Рассмотрим реальный сценарий: у нас есть Android-проект с Kotlin-кодом, и мы хотим настроить Detekt для автоматической проверки кода перед каждым коммитом. Создадим конфигурацию, которая будет проверять стиль кода, сложность и потенциальные ошибки.
Файл конфигурации config/detekt/detekt.yml:
# config/detekt/detekt.yml
build:
maxIssues: 10
comments:
active: true
CommentOverPrivateFunction:
active: true
CommentOverPrivateProperty:
active: true
complexity:
active: true
ComplexCondition:
active: true
threshold: 3
LargeClass:
active: true
threshold: 200
LongMethod:
active: true
threshold: 30
TooManyFunctions:
active: true
threshold: 10
exceptions:
active: true
ExceptionRaisedInU