虎の穴ラボ技術ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Kotlinの静的コード解析ツール「detekt」でコードをキレイに保とう!

こんにちは!虎の穴ラボの鷺山です。

ソフトウェア開発で静的コード解析ツールを導入すると、コードの品質や生産性を上げることができます。ツールが自動で問題を検出するため、開発者はより本質的な作業に集中できるようになります。

今回は、弊社でも使用しているKotlin向け静的コード解析ツール「detekt」の導入&活用方法をご紹介します!

目次

前提環境

環境 バージョン
JDK Amazon Corretto 21
Gradle Gradle Wrapper 8.5
Kotlin 2.0.0
detekt 1.23.7

detektの特徴

detektは次の特徴を持つKotlin向け静的コード解析ツールです。

  • コードスメル(潜在的な問題)を検出できる
  • 各ルールをオン・オフしたり、しきい値を柔軟に設定できる
  • 特定のルールを部分的に除外できる(設定ファイルやアノテーションにより)
  • コードフォーマッター機能もある
  • プロジェクト独自のカスタムルールを追加できる(これは別の記事で紹介しています)

まずはさっと試してみる

detektはCLIも提供しているため、Javaが実行可能な環境であればすぐに試すことができます。

CLIのJARファイルは公式のReleasesページからダウンロードできます。

$ cd <Kotlinプロジェクトのディレクトリ>
$ curl -LO https://github.com/detekt/detekt/releases/download/v1.23.7/detekt-cli-1.23.7-all.jar
$ java -jar detekt-cli-1.23.7-all.jar --input src

--input に解析対象のパスを指定してCLIを実行します。

実行結果の例

src/main/kotlin/Main.kt:1:1: The package declaration does not match the actual file location. [InvalidPackageDeclaration]
src/main/kotlin/Main.kt:16:2: The file src/main/kotlin/Main.kt is not ending with a new line. [NewLineAtEndOfFile]
src/main/kotlin/Main.kt:12:1: Line detected, which is longer than the defined maximum line length in the code style. [MaxLineLength]
src/main/kotlin/Main.kt:11:18: This expression contains a magic number. Consider defining it to a well named constant. [MagicNumber]

Analysis failed with 4 weighted issues.

ルールに抵触しているコードを検出すると、そのファイルパスや位置、検出理由や該当ルール名が表示されます。

各ルールの詳細は公式のドキュメントページから検索して参照できます(右上の検索ボックスから)。
以下は MagicNumber ルールを参照する例です。

Gradleプロジェクトに導入する

detektをGradleプロジェクトに導入する手順を紹介します。ビルドプロセスに組み込むことで、コード解析の自動化やルール管理が容易になります。

1. Gradleプラグインを適用する

プロジェクトの build.gradle.kts (Kotlin DSL形式) に以下の設定を追加します。これにより、detektプラグインをプロジェクトに組み込めます。

plugins {
    ...
    id("io.gitlab.arturbosch.detekt") version "1.23.7"
}

2. 設定ファイルを生成する

以下のコマンドでdetektの設定ファイルを生成できます。

$ ./gradlew detektGenerateConfig

これにより config/detekt/detekt.yml という設定ファイルが生成されます。このファイルを利用して、ルールやしきい値をプロジェクトに応じてカスタマイズできます。

そして build.gradle.kts に以下の設定を追加し、設定ファイルをGradleに認識させます。

detekt {
    config.setFrom(file("config/detekt/detekt.yml"))
    buildUponDefaultConfig = true
}

buildUponDefaultConfig オプションを有効にすると「デフォルトからの変更分」だけを設定ファイルに記述するモードになります。カスタムしたい項目だけを記述する形式になり、設定ファイルの記述量を減らすことができます。

3. detektタスクを実行する

以下のコマンドでコード解析を実行します。

$ ./gradlew detekt

実行結果はコンソールに出力され、修正が必要な箇所を確認できます。

なお ./gradlew build でも detekt タスクは実行されます。detekt タスクは check タスクに紐付けられており、build → check → detekt と呼び出されるためです。

特定のファイルやコードを解析対象から除外する

detektはデフォルトでは全ファイルを対象に解析しますが、中には問題への対応が不要なコード対応できないコードもあると思います。これらのファイルを個別に解析対象から除外できます。

たとえば以下のようなコードです。

  • 自動生成コード
  • テストコード
  • 何らかの理由で修正できないコード
  • 修正すべきだが今は手が付けられないコード

ファイルパス指定で除外する

build.gradle.kts に以下のような設定を追加することで、特定のファイルを解析対象から除外できます。

tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
    exclude("**/api/autogen/**") // 自動生成コードの出力先
    exclude("**/**Test.kt")      // テストコード全般
    exclude("**/TestUtils.kt")   // テスト用のユーティリティクラス
}

自動生成コードやテストコードは、上記のように書くことで除外できます。

アノテーションで除外する

該当コードの直前に @Suppress アノテーションを記述することで、解析対象から除外できます。

@Suppress("LargeClass") // クラスにアノテーションを記述
class SomeClass {
    @Suppress("LongMethod", "LongParameterList") // メソッドにアノテーションを記述
    fun someMethod(...) {
        ...
    }
}

@Supress(...) のカッコ内に除外するルール名を記述します。ルール名は複数記述できます。

ルールを「あくまで暫定的に」除外する場合は、あとで修正するのを忘れないようにTODOコメントで課題管理システムなどの番号と紐づけておくとよいと思います。

@Suppress("LongMethod", "LongParameterList") // TODO ISSUE-1111 要リファクタリング

ルールごとに除外する

設定ファイル config/detekt/detekt.ymlexcludes キーワードを書くことで、解析対象から除外するファイルをルールごとに設定できます。 特定のルールに抵触せざるを得ないクラスの傾向が分かっている場合などに有用です。

  TooManyFunctions:
    excludes:
      # ユーティリティクラスはメソッド数が多くなるため除外する
      - '**/utils/**Utils.kt'

上記の例では TooManyFunctions ルールにおいて **/utils/**Utils.kt にマッチする全ファイルが除外されます。

また active: false と書くことで、ルールそのものを無効化できます。

  TooManyFunctions:
    active: false

しきい値のチューニング

デフォルト設定のままだと制約が厳しいと感じる場合は、設定ファイル config/detekt/detekt.ymlthreshold でしきい値を調整できます。 プロジェクトの特性に合わせて適切なしきい値を設定してください。

complexity:
  ComplexCondition:
    threshold: 6  # ← デフォルトは 4
  TooManyFunctions:
    thresholdInClasses: 16  # ← デフォルトは 11

上記は ComplexConditionTooManyFunctions ルールのしきい値を緩和する設定例です。

コードフォーマッター

detektではコードフォーマッターも使用できます。
使用するには、まずGradleに detekt-formatting プラグインの依存を追加します。このプラグイン自体はコードフォーマット違反を検出するものです。

dependencies {
    ...
    detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7")
}

そして、フォーマット違反を自動的に修正するには、ビルドスクリプトの detekt { ... } ブロックに autoCorrect = true を追記します。これでGradleの detekt タスクでコードフォーマッターが実行されるようになります。

detekt {
    ...
    autoCorrect = true
}

特定のフォーマットルールの無効化

特定のコードフォーマットルールを無効化するには、設定ファイル config/detekt/detekt.ymlformatting: ブロックを追加し、無効化したいルール名に active: false と書きます。

formatting:
  ArgumentListWrapping:
    active: false
  ParameterListWrapping:
    active: false
  StringTemplate:
    active: false

上記の例では ArgumentListWrapping, ParameterListWrapping, StringTemplate ルールを無効化しています。

GitHub Actionsでコードフォーマッターを実行

GitHubリポジトリにブランチをpushしたり、プルリクエストを作成したタイミングでGitHub Actionsからdetektのコードフォーマッターを実行できます。

整形により発生したコード差分は、GitHub Actionsボットが自動で該当ブランチにpushします。

以下は、プルリクエストの作成をトリガーにフォーマッターを実行するGitHub Actionsワークフローの記述例です。

name: Auto Format by detekt

on:
  pull_request:
    paths:
      - "config/detekt/detekt.yml"
      - "**/*.kt"

permissions:
  contents: write

jobs:
  format:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: "21"
          distribution: "corretto"
      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@v3
      - name: Code format by detekt
        run: ./gradlew detekt --auto-correct --continue
        continue-on-error: true
      - name: Push
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git diff --exit-code || (git add . && git commit -m "Auto format by detekt")
          git push

GitHub Actionsからdetektのコードフォーマッターを実行
GitHub Actionsからdetektのコードフォーマッターを実行

IntelliJ IDEAにdetektプラグインを導入

IDEにIntelliJ IDEAを使っている場合は、detekt公式によるdetektプラグインが利用可能です。

プラグインを導入すると、IDE上でdetekt違反を把握できるようになります。

また、右クリックメニューから自動修正 (AutoCorrect) を実行できます。

detekt プラグインのインストール

  1. IntelliJ IDEAメニューの Settings から Plugins を開きます。
  2. Marketplacedetekt を検索します。
  3. 「detekt」プラグインが表示されるので、Install をクリックします。

detektプラグインの設定

  1. IntelliJ IDEAメニューの Settings から Tools > detekt を開きます。
  2. Enable background analysis をチェックします。
  3. RulesBuild rules upon the default configuration をチェックします。
    • コードフォーマッターを導入している場合は Enable formatting (ktlint) rules をチェックします。
  4. FilesConfiguration file(s) に設定ファイル config/detekt/detekt.yml を指定します。

まとめ

detektを導入することで、コードスメルやフォーマット違反を機械的に発見でき、コーディングやコードレビューの際により本質的な作業に集中できるようになります。

またdetektはカスタマイズ性が高く、既存プロジェクトへの導入も容易です。「いったん制約を緩めてまずは導入し、徐々に厳しくしていく」といった運用もできると思います。

Kotlinプロジェクトで静的コード解析ツールをまだ導入していない場合は、ぜひdetektを試してみてはいかがでしょうか?

採用情報

虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp