[Kotlin] Detekt — пишем свои правила
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Мы в «Ситимобил», используем статический анализатор кода Detekt. Это инструмент, который при запуске проходит по проекту и показывает допущенные в коде code smell. И самостоятельно исправляет некоторые из них, если вы включите эту функцию. Detekt решает такие проблемы, как:
- трата времени команды на дискуссии о незначительных правках (стиль кода);
- отсутствие единообразного стиля кода в большой команде;
- незнание разработчиками некоторых best-practices, которые отражены в наборе правил.
Хоть у Detekt из коробки достаточно большая база правил, иногда этого недостаточно. Наверняка у вас есть свои договоренности о том, как писать код в проекте, какие конструкции использовать, какие нет и какие на усмотрение разработчика, и это называется code-style of your awesome project! И вот вы на очередной встрече решили, каким будет ваш код, а соответствующего правила в списке Detekt не нашли. Как настоящий программист вы не унываете и решаете написать своё правило, с блекджеком и корутинами. А как это сделать, мы расскажем в этой статье!Шаг 1: создаём модульСоздаём модуль для наших новых правил и назовём его detekt-custom-rules. Сразу идем в .gradle модуля и удаляем всё, что там есть, оно нам не понадобится. Нужно подключить плагин Kotlin и несколько зависимостей. Получится вот такой файл:
apply plugin: 'kotlin'
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
implementation "io.gitlab.arturbosch.detekt:detekt-api:$versions.detekt"
implementation "io.gitlab.arturbosch.detekt:detekt-cli:$versions.detekt"
}
Затем нужно создать специальный класс Provider, в котором будут перечислены наши кастомные правила:
package provider
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
class CustomDetektRuleSetProvider : RuleSetProvider {
override val ruleSetId = "custom-detekt-rules"
override fun instance(config: Config) = RuleSet(
id = ruleSetId,
rules = listOf(
// тут будет наше правило
)
)
}
А теперь зарегистрируем его: создаём ресурсную папку, папку META_INF, еще services, и в ней текстовый файл io.gitlab.arturbosch.detekt.api.RuleSetProvider. В файле нужно будет указать путь до нашего класса. Вот что получится:
А внутри текстового файла будет в нашем случае так:
Напоследок нужно подключить наш модуль там же, где описана задача Detekt:
dependencies {
detektPlugins detektFormattingPlugin
detekt project(':detekt-custom-rules')
}
Чтобы вам было понятно, где именно мы подключали модуль, приведем и наш вариант подключения Detekt. Мы вынесли его задачи в отдельный файл detekt.gradle, и выглядит он так:
buildscript {
apply from: "dependencies.gradle"
repositories {
mavenCentral()
}
dependencies {
classpath detektPlugin
}
}
apply plugin: io.gitlab.arturbosch.detekt.DetektPlugin
tasks {
task detektAll(type: io.gitlab.arturbosch.detekt.Detekt) {
// только проверяет
}
task detektFixAll(type: io.gitlab.arturbosch.detekt.Detekt) {
// проверяет и исправляет
}
task detektAllCreateBaseline(type: io.gitlab.arturbosch.detekt.DetektCreateBaselineTask) {
// создает/обновляет baseline файл
}
}
dependencies {
detektPlugins detektFormattingPlugin
detekt project(':detekt-custom-rules')
}
А подключается этот файл в build.gradle проекта:
apply from: "detekt/detekt.gradle"
Делаем Clean Project && sync, и мы готовы к следующему шагу.Шаг 2: пишем своё первое правилоСоздаем класс по пути: detekt-custom-rules/src/main/java/rules
class MyRule(config: Config = Config.empty) : Rule(config) {
// code
}
Переопределяем константу Issue, где:
- id — идентификатор, по которому мы можем включить или выключить правило в detekt-config.yml. Важно, чтобы в yml и у идентификатор были одинаковые строковые значения;
- description — короткое описание, которое можно увидеть в .html-отчёте;
- severity — enum с возможными значениями, рекомендую ставить просто CodeSmell, это не так важно. Но можно самостоятельно изучить другие типы :);
- debt — описывает предполагаемый объём работы, необходимой для устранения данной проблемы.
override val issue = Issue(
id = "myAwesomeRule1",
description = "Must use the MyRule!",
severity = Severity.CodeSmell,
debt = Debt.FIVE_MINS
)
Переопределяем один из методов visit. Их очень много, ознакомьтесь самостоятельно и выберите необходимый метод под конкретные нужды. Стоит учесть, что если используете visitKtFile, то это может ухудшить сложность прохода. Чтобы ускорить выполнение, нужно стараться подобрать наиболее подходящий visit-метод.
override fun visitNamedFunction(function: KtNamedFunction) {
// посетили функцию
}
override fun visit(root: KtFile) {
// посетили файл
}
override fun visitIfExpression(expression: KtIfExpression) {
// посетили if блок
}
Теперь проинформируем анализатор о найденном codeSmell. Тут нужно считать offset, чтобы в ссылке в консоли или отчётах указатель был на той строке, где произошла ошибка. Иначе может быть непонятно, где конкретно в файле codeSmell, который нужно исправить.
override fun visitNamedFunction(function: KtNamedFunction) {
var offset = 0
val lines = function.text.lines()
for (line in lines) {
offset += line.length
if (YOUR_CONDITION) {
report(
CodeSmell(
issue = issue,
entity = Entity.from(function, offset),
message = "Your message for the detekt.html report"
)
)
}
offset += 1 // '\n'
}
Шаг 3: добавляем новое правило в ProviderМы создали класс CustomDetektRuleSetProvider, и теперь нужно добавить новое правило в список — rules.
package provider
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
class CustomDetektRuleSetProvider : RuleSetProvider {
override val ruleSetId = "custom-detekt-rules"
override fun instance(config: Config) = RuleSet(
id = ruleSetId,
rules = listOf(
MyAwesomeRule1(config),
MyAwesomeRule2(config),
)
)
}
Шаг 4: добавляем новое правило в detekt-config.ymlНаходим config в .yml с правилами Detekt:
config:
validation: true
warningsAsErrors: false
excludes: "custom-detekt-rules"
Затем добавим новое правило в .yml и сделаем его активным, при этом название правила обязательно должно совпадать с id в Issue в шаге 1.
custom-detekt-rules:
myAwesomeRule1:
active: true
myAwesomeRule2:
active: false
Шаг 5: Clean projectDetekt кеширует данные, поэтому перед запуском нужно обязательно выполнить Build -> Clean Project.Шаг 6: обновляем файл baseline.xmlНовое правило, скорее всего, вызовет множество новых codeSmell, связанных с только что написанным правилом. И может не оказаться времени исправить сразу все их. Поэтому можно запустить задачу ./gradlew detektAllCreateBaseline, тем самым обновив baseline-файл, и в него добавятся новые codeSmell, которые будут игнорироваться при дальнейших запусках анализатора.Наш пример правила
package rules
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import org.jetbrains.kotlin.psi.KtNamedFunction
class NeedToUseSubscribeByRule(config: Config = Config.empty) : Rule(config) {
companion object {
private const val TRIGGER_VALUE = ".subscribe("
}
override val issue = Issue(
id = "NeedToUseSubscribeBy",
description = "Must use a .subscribeBy(...) ext instead .subscribe(...)",
severity = Severity.CodeSmell,
debt = Debt.FIVE_MINS
)
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
var offset = 0
val lines = function.text.lines()
for (line in lines) {
offset += line.length
if (line.contains(TRIGGER_VALUE)) {
report(
CodeSmell(
issue = issue,
entity = Entity.from(function, offset),
message = "The function ${function.name} using RxJava chain. " +
"You must use a .subscribeBy(...) ext instead .subscribe(...) here."
)
)
}
offset += 1 // '\n'
}
}
}
Примеры на GitHubСамый простой способ писать свои правила — подсмотреть у авторов проекта. Советую подобрать в GitHub-репозитории Detekt правило, которое похоже на ваше. На всякий случай оставлю тут несколько ссылок :)
- https://github.com/detekt/detekt/blob/main/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNaming.kt
- https://github.com/detekt/detekt/blob/main/detekt-rules-exceptions/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/exceptions/InstanceOfCheckForException.kt
Другие примеры можно найти в репозитории https://github.com/detekt/detekt
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка под Android, Kotlin] Пишем комикс-приключение на Kotlin
- [Разработка мобильных приложений, Разработка под Android, Kotlin] Пишем свой профайлер для анализа производительности приложения на Android
- [Анализ и проектирование систем, Разработка под Android] «Оливье в каждой семьей свой»: или как мы придумали ещё одну многомодульную архитектуру
- [Программирование, Разработка под Android, Kotlin] Более безопасный способ сбора потоков данных из пользовательских интерфейсов Android (перевод)
- [Разработка под Android, Kotlin, Gradle] Аналог R.string в android приложении
- [] JetBrains Academy: платформенные обновления, любимые проекты пользователей и годовая подписка
- [PostgreSQL, Java, Kotlin] PostGIS + JPA. Погружение в детали и тонкости для чайников
- [JavaScript, ReactJS] React. Не в глубь, а в ширь. Композиция против реальности
- [Программирование, Java, Kotlin] Увеличиваем throughput приложения в 2 раза или неблокирующая работа с Elasticsearch с использованием Kotlin coroutines
- [Ненормальное программирование, Программирование, ООП, Функциональное программирование, Kotlin] Мультивселенная и задачи о переправе
Теги для поиска: #_kotlin, #_detekt, #_statisticheskij_analiz (статистический анализ), #_staticheskij_analiz_koda (статический анализ кода), #_sitimobil (ситимобил), #_codestyle, #_blog_kompanii_sitimobil (
Блог компании Ситимобил
), #_kotlin
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:16
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Мы в «Ситимобил», используем статический анализатор кода Detekt. Это инструмент, который при запуске проходит по проекту и показывает допущенные в коде code smell. И самостоятельно исправляет некоторые из них, если вы включите эту функцию. Detekt решает такие проблемы, как:
apply plugin: 'kotlin'
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" implementation "io.gitlab.arturbosch.detekt:detekt-api:$versions.detekt" implementation "io.gitlab.arturbosch.detekt:detekt-cli:$versions.detekt" } package provider
import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.RuleSet import io.gitlab.arturbosch.detekt.api.RuleSetProvider class CustomDetektRuleSetProvider : RuleSetProvider { override val ruleSetId = "custom-detekt-rules" override fun instance(config: Config) = RuleSet( id = ruleSetId, rules = listOf( // тут будет наше правило ) ) } А внутри текстового файла будет в нашем случае так: Напоследок нужно подключить наш модуль там же, где описана задача Detekt: dependencies {
detektPlugins detektFormattingPlugin detekt project(':detekt-custom-rules') } buildscript {
apply from: "dependencies.gradle" repositories { mavenCentral() } dependencies { classpath detektPlugin } } apply plugin: io.gitlab.arturbosch.detekt.DetektPlugin tasks { task detektAll(type: io.gitlab.arturbosch.detekt.Detekt) { // только проверяет } task detektFixAll(type: io.gitlab.arturbosch.detekt.Detekt) { // проверяет и исправляет } task detektAllCreateBaseline(type: io.gitlab.arturbosch.detekt.DetektCreateBaselineTask) { // создает/обновляет baseline файл } } dependencies { detektPlugins detektFormattingPlugin detekt project(':detekt-custom-rules') } apply from: "detekt/detekt.gradle"
class MyRule(config: Config = Config.empty) : Rule(config) {
// code }
override val issue = Issue(
id = "myAwesomeRule1", description = "Must use the MyRule!", severity = Severity.CodeSmell, debt = Debt.FIVE_MINS ) override fun visitNamedFunction(function: KtNamedFunction) {
// посетили функцию } override fun visit(root: KtFile) { // посетили файл } override fun visitIfExpression(expression: KtIfExpression) { // посетили if блок } override fun visitNamedFunction(function: KtNamedFunction) {
var offset = 0 val lines = function.text.lines() for (line in lines) { offset += line.length if (YOUR_CONDITION) { report( CodeSmell( issue = issue, entity = Entity.from(function, offset), message = "Your message for the detekt.html report" ) ) } offset += 1 // '\n' } package provider
import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.RuleSet import io.gitlab.arturbosch.detekt.api.RuleSetProvider class CustomDetektRuleSetProvider : RuleSetProvider { override val ruleSetId = "custom-detekt-rules" override fun instance(config: Config) = RuleSet( id = ruleSetId, rules = listOf( MyAwesomeRule1(config), MyAwesomeRule2(config), ) ) } config:
validation: true warningsAsErrors: false excludes: "custom-detekt-rules" custom-detekt-rules:
myAwesomeRule1: active: true myAwesomeRule2: active: false package rules
import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Debt import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity import org.jetbrains.kotlin.psi.KtNamedFunction class NeedToUseSubscribeByRule(config: Config = Config.empty) : Rule(config) { companion object { private const val TRIGGER_VALUE = ".subscribe(" } override val issue = Issue( id = "NeedToUseSubscribeBy", description = "Must use a .subscribeBy(...) ext instead .subscribe(...)", severity = Severity.CodeSmell, debt = Debt.FIVE_MINS ) override fun visitNamedFunction(function: KtNamedFunction) { super.visitNamedFunction(function) var offset = 0 val lines = function.text.lines() for (line in lines) { offset += line.length if (line.contains(TRIGGER_VALUE)) { report( CodeSmell( issue = issue, entity = Entity.from(function, offset), message = "The function ${function.name} using RxJava chain. " + "You must use a .subscribeBy(...) ext instead .subscribe(...) here." ) ) } offset += 1 // '\n' } } }
=========== Источник: habr.com =========== Похожие новости:
Блог компании Ситимобил ), #_kotlin |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:16
Часовой пояс: UTC + 5