本文へ移動
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.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.8 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

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

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

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/RIPDPI792026年10月13日 更新

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/RIPDPI792026年10月13日 更新

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/RIPDPI792026年10月13日 更新

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/RIPDPI792026年10月13日 更新

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/RIPDPI792026年10月13日 更新

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/RIPDPI792026年10月13日 更新

po4yka のスキルをすべて見る

このスキルの問題を報告する