跳到正文
cccskills
免费在 GitHub 公开

detekt-custom-rules

Register, activate, and test a custom detekt rule in :quality:detekt-rules. Use when adding a DI/privacy/suppression guardrail rule or debugging a false positive.

中文简介正在准备中。当前显示原文说明。

集成工具GitLab
查看安装方法

包含的文件(1)

  • SKILL.md7.8 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Custom Detekt Rules

Custom rule set diGuardrails in module :quality:detekt-rules. Enforces DI, Hilt, suppression, and resolver-log privacy conventions at compile time.

Existing Rules

scripts/ci/check_harness_manifests.py (validate_factual_ground_truth) mechanically checks that every rule registered in RipDpiRuleSetProvider.kt appears in this table -- omitting a rule here fails CI rather than silently drifting.

RuleWhat It CatchesWhy
InjectConstructorDefaultParameterDefault values on @Inject constructor paramsHilt ignores defaults, causing silent behavior mismatch
HiltViewModelApplicationContext@ApplicationContext in @HiltViewModel constructorsForces narrower abstractions, improves testability
DisallowNewSuppression@Suppress without an adjacent ROADMAP-architecture-refactor allowlist commentPrevents new lint debt from becoming invisible
NoResolverIpInLogsResolver or upstream IP identifiers interpolated into log callsKeeps user-visible network state out of logs

Module Structure

quality/detekt-rules/
  src/main/kotlin/com/poyka/ripdpi/quality/detekt/
    RipDpiRuleSetProvider.kt           -- Registers all rules (id: "diGuardrails")
    AnnotationMatchers.kt              -- Shared helpers: hasAnnotation(), findAnnotation()
    InjectConstructorDefaultParameter.kt
    HiltViewModelApplicationContext.kt
    DisallowNewSuppression.kt
    NoResolverIpInLogs.kt
  src/test/kotlin/com/poyka/ripdpi/quality/detekt/
    InjectConstructorDefaultParameterTest.kt
    HiltViewModelApplicationContextTest.kt
    DisallowNewSuppressionTest.kt
    NoResolverIpInLogsTest.kt

Creating a New Rule

Step 1: Write the Rule Class

Create src/main/kotlin/com/poyka/ripdpi/quality/detekt/<YourRuleName>.kt:

package com.poyka.ripdpi.quality.detekt

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.KtClass

class YourRuleName(config: Config) : Rule(config) {
    override val issue: Issue =
        Issue(
            id = "YourRuleName",
            severity = Severity.Defect,
            description = "One-line description of what this catches.",
            debt = Debt.FIVE_MINS,
        )

    override fun visitClass(klass: KtClass) {
        super.visitClass(klass)
        // Use hasAnnotation() from AnnotationMatchers.kt
        if (!klass.hasAnnotation("SomeAnnotation")) return

        // Check condition and report
        report(
            CodeSmell(
                issue = issue,
                entity = Entity.from(klass),
                message = "Actionable message: what to do instead.",
            ),
        )
    }
}

Step 2: Register in RuleSetProvider

Add to RipDpiRuleSetProvider.kt:

override fun instance(config: Config): RuleSet =
    RuleSet(
        ruleSetId,
        listOf(
            InjectConstructorDefaultParameter(config),
            HiltViewModelApplicationContext(config),
            DisallowNewSuppression(config),
            NoResolverIpInLogs(config),
            YourRuleName(config),  // <-- add here
        ),
    )

Step 3: Activate in detekt.yml

Add to config/detekt/detekt.yml:

diGuardrails:
  active: true
  InjectConstructorDefaultParameter:
    active: true
  HiltViewModelApplicationContext:
    active: true
  NoResolverIpInLogs:
    active: true
  YourRuleName:               # <-- add here
    active: true

DisallowNewSuppression is registered in RipDpiRuleSetProvider and inherits the active diGuardrails rule-set setting; it has no per-rule override in the current config.

Step 4: Write Tests

Create src/test/kotlin/com/poyka/ripdpi/quality/detekt/<YourRuleName>Test.kt:

package com.poyka.ripdpi.quality.detekt

import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

class YourRuleNameTest {
    private val rule = YourRuleName(Config.empty)

    @Test
    fun `reports violation on annotated class`() {
        val code = """
            @SomeAnnotation
            class Bad { /* violating pattern */ }
        """.trimIndent()
        val findings = rule.compileAndLint(code)
        assertEquals(1, findings.size)
    }

    @Test
    fun `allows non-annotated class`() {
        val code = """
            class Fine { /* same pattern but no annotation */ }
        """.trimIndent()
        val findings = rule.compileAndLint(code)
        assertEquals(0, findings.size)
    }
}

Cover: positive detection, negative (no false positive), edge cases (primary vs secondary constructors, nested classes).

Annotation Matching

Use the shared helpers from AnnotationMatchers.kt:

// Check if a KtAnnotated element has an annotation by simple name
klass.hasAnnotation("HiltViewModel")        // matches @HiltViewModel
constructor.hasAnnotation("Inject")          // matches @Inject

// Find the annotation entry (for Entity.from targeting)
parameter.findAnnotation("ApplicationContext")  // returns KtAnnotationEntry?

Important: These match by simple name, not fully-qualified name. hasAnnotation("Inject") matches both @javax.inject.Inject and @com.google.inject.Inject.

Common PSI Visitor Methods

MethodWhen To Use
visitClass(KtClass)Checking class-level patterns
visitPrimaryConstructor(KtPrimaryConstructor)Constructor parameter rules
visitSecondaryConstructor(KtSecondaryConstructor)Must handle alongside primary
visitNamedFunction(KtNamedFunction)Function-level rules
visitProperty(KtProperty)Property declaration rules

Use getStrictParentOfType<KtClassOrObject>() to navigate from a constructor to its owning class.

Convention Plugin

ripdpi.android.detekt.gradle.kts configures detekt for all modules:

  • Parallel: true
  • Config: config/detekt/detekt.yml
  • Reports: HTML + XML
  • JVM target: 17
  • Excludes: **/build/**, **/generated/**, **/jni/**, **/cpp/**
  • Baseline: Auto-detected from detekt-baseline.xml if present in module
  • Plugin deps: Custom rules (:quality:detekt-rules) + detekt-compose-rules

Run detekt:

./gradlew staticAnalysis          # detekt + ktlint + lint
./gradlew detektDebug             # detekt only (single variant)

Common Mistakes

MistakeFix
Forgetting to register in RipDpiRuleSetProviderRule exists but never runs. Add to the listOf(...) in instance().
Using FQN for annotation matchinghasAnnotation("javax.inject.Inject") won't match. Use simple name: "Inject".
Not activating in detekt.ymlRule is registered but inactive. Add YourRuleName: { active: true } under diGuardrails:.
Testing only primary constructorsMany rules apply to both primary and secondary. Override both visitor methods.
Reporting on wrong entityUse Entity.from(specificElement) not Entity.from(constructor) -- highlights the exact problem location.
Missing super.visitX() callAlways call super.visitPrimaryConstructor(constructor) etc. to continue traversal.

See Also

  • config/detekt/detekt.yml -- Full detekt configuration
  • build-logic/convention/src/main/kotlin/ripdpi.android.detekt.gradle.kts -- Convention plugin

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

RIPDPI's own Compose conventions: ViewModel pattern, Route/Screen split, DataStore->StateFlow, RipDpiThemeTokens. Use for how this app does Compose, not generic Compose API questions (see compose).

中文简介正在准备中。当前显示原文说明。

po4yka/RIPDPI812026年10月21日 更新

ADB-based RIPDPI device/emulator debugging: build/install/launch, logcat filtering, fixture port-forwarding, instrumented tests, crash/ANR triage. Use when debugging on a real device or emulator.

中文简介正在准备中。当前显示原文说明。

po4yka/RIPDPI812026年10月21日 更新

RIPDPI Appium launch-contract reference: start routes and permission/service/data presets. Use when a test launches to the wrong screen or a new automation route is added. General flakiness: appium-test-debug.

中文简介正在准备中。当前显示原文说明。

po4yka/RIPDPI812026年10月21日 更新

RIPDPI Appium test authoring: page objects, resource-id locators, assertions, wait tiers. Use when writing a new Appium test or page object, or adding coverage for a new screen.

中文简介正在准备中。当前显示原文说明。

po4yka/RIPDPI812026年10月21日 更新

RIPDPI Appium failure triage: flaky tests, locator/session/wait issues, screenshot and element-tree debugging. Use when an Appium test fails or is flaky.

中文简介正在准备中。当前显示原文说明。

po4yka/RIPDPI812026年10月21日 更新

Add or modify a GitHub Actions job. Use when editing a file under .github/workflows/. Not for running a workflow locally (local-ci-act) or release signing (release-signing).

中文简介正在准备中。当前显示原文说明。

po4yka/RIPDPI812026年10月21日 更新

查看 po4yka 的全部技能

报告此技能的问题