Clippy

コード品質リンターRustDevOps静的解析CargoRust コンパイラ

DevOpsツール

Clippy

概要

Clippyは、Rustプログラミング言語の公式静的解析ツールで、コードの品質向上とRustのベストプラクティス遵守を支援します。Rust コンパイラチームによって開発・メンテナンスされ、cargo clippy コマンドで簡単に実行可能。600以上のリントルールを提供し、パフォーマンス、正確性、スタイル、複雑性の4つのカテゴリーでコード品質をチェック。Cargoとの完全統合、設定可能なリントレベル、IDE統合により、Rust開発における標準的なコード品質管理ツールとして位置づけられています。

詳細

Clippyは「rusty-clippy」として2014年に開始され、現在はRust言語の公式ツールチェーンの一部として統合されています。Rustコンパイラの内部APIを活用し、Rustコードの潜在的な問題やより良い書き方を提案する静的解析ツールです。

主要な特徴

  • 包括的なリント機能: 600以上のリントルールによる多角的コード解析
  • カテゴリ別分類: correctness、perf、style、complexity、suspicious等の体系的分類
  • Cargo統合: cargo clippyコマンドによるシームレスな実行
  • 設定可能性: プロジェクト毎の詳細なリント設定とカスタマイズ
  • 自動修正提案: 多くのリントで具体的な修正案を提供
  • CI/CD対応: GitHub Actions、GitLab CI等での自動品質チェック
  • IDE統合: VS Code、IntelliJ、Vim等の主要エディター対応
  • 段階的導入: 既存プロジェクトへの安全な適用機能
  • パフォーマンス最適化: 増分コンパイルとキャッシュ活用

2025年版の特徴

  • Rust 2024 Edition対応: 最新Rust機能との完全互換性
  • enhanced diagnostics: より詳細でユーザーフレンドリーな診断メッセージ
  • 新リントルール: async/await、const generics等の最新機能向けルール
  • パフォーマンス向上: 大規模プロジェクトでの実行速度最適化

メリット・デメリット

メリット

  • Rust公式ツールとしての高い信頼性と標準化
  • 600以上のリントルールによる包括的コード品質管理
  • Cargoとの完全統合による簡単な導入と実行
  • カテゴリ別分類による体系的なコード品質向上
  • 具体的な修正提案による学習効果と開発効率向上
  • ゼロコスト抽象化原則に基づくパフォーマンス重視の提案
  • プロジェクト固有要件への柔軟な設定対応
  • CI/CD統合による自動品質ゲート実現
  • IDE統合によるリアルタイムフィードバック
  • Rustベストプラクティスの自動的な学習と適用

デメリット

  • Rust専用のため他言語プロジェクトでの使用不可
  • 初期学習コストと大量の警告による混乱
  • 厳格すぎる設定による開発速度低下のリスク
  • 一部のリントルールの誤検出(false positive)
  • コンパイル時間の増加(特に大規模プロジェクト)
  • 設定ファイルの複雑化によるメンテナンス負荷
  • チーム内でのコーディング規約合意形成の必要性
  • エディター・IDE設定の複雑さ
  • 頻繁なルール更新への対応負荷
  • レガシーコード適用時の大量警告対応

参考ページ

書き方の例

インストールと基本セットアップ

Rustupを使用したインストール

# Rust toolchain のインストール(Clippyも含む)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 既存環境へのClippyコンポーネント追加
rustup component add clippy

# インストール確認
cargo clippy --version
rustup component list | grep clippy

# 最新版への更新
rustup update
rustup component add clippy --toolchain stable

プロジェクト別セットアップ

# 新規Cargoプロジェクト作成
cargo new my_project
cd my_project

# 既存プロジェクトでのClippy実行
cargo clippy

# 初回実行(依存関係チェック込み)
cargo clippy --all-targets --all-features

基本的な実行方法

# 基本実行
cargo clippy

# 全ターゲット(tests、examples等も含む)
cargo clippy --all-targets

# 全機能有効
cargo clippy --all-features

# 特定パッケージのみ
cargo clippy -p my_package

# ワークスペース全体
cargo clippy --workspace

# リリースモード
cargo clippy --release

# 詳細出力
cargo clippy -- -D warnings

# 特定リントのみ
cargo clippy -- -A clippy::all -D clippy::correctness

# JSON出力(CI/CD用)
cargo clippy --message-format=json

# 特定ファイルのみ(rustc使用)
rustc -A unused --extern clippy_dev=target/debug/deps/libclippy_dev.rlib src/main.rs

設定ファイル(clippy.toml)

# clippy.toml - プロジェクトルートに配置
# 基本設定
allow-expect-in-tests = true
allow-unwrap-in-tests = true
allow-dbg-in-tests = true

# メトリクス設定
too-many-arguments-threshold = 8
type-complexity-threshold = 250
single-char-binding-names-threshold = 5
trivial-copy-size-limit = 64

# 命名規則
enum-variant-name-threshold = 3
struct-excessive-bools = 4
doc-valid-idents = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]

# パフォーマンス設定
vec-box-size-threshold = 4096
trivial-copy-size-limit = 64

# 避けるべきパターン
blacklisted-names = ["foo", "bar", "baz", "quux"]
cognitive-complexity-threshold = 30

# モジュール設定
max-trait-bounds = 3
max-fn-params-bools = 3

Cargo.toml設定

# Cargo.toml
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"

# Clippy設定セクション
[lints.clippy]
# 厳格レベル設定
all = "warn"
correctness = "deny"
perf = "warn"
style = "warn"
complexity = "warn"
suspicious = "warn"

# 個別リント設定
too_many_arguments = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"

# カスタムリント設定
missing_docs_in_private_items = "warn"
unwrap_used = "deny"
expect_used = "warn"

[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"

IDE統合設定

VS Code設定

// .vscode/settings.json
{
  "rust-analyzer.checkOnSave.command": "clippy",
  "rust-analyzer.checkOnSave.allTargets": true,
  "rust-analyzer.checkOnSave.extraArgs": [
    "--all-features"
  ],
  "rust-analyzer.diagnostics.disabled": [],
  "rust-analyzer.diagnostics.enable": true,
  "rust-analyzer.cargo.features": "all",
  "[rust]": {
    "editor.defaultFormatter": "rust-lang.rust-analyzer",
    "editor.formatOnSave": true,
    "editor.rulers": [100]
  },
  "rust-analyzer.lens.enable": true,
  "rust-analyzer.lens.run": true,
  "rust-analyzer.lens.implementations": true
}

// tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Clippy Check",
      "type": "shell",
      "command": "cargo",
      "args": ["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"],
      "group": "build",
      "presentation": {
        "echo": true,
        "reveal": "always",
        "focus": false,
        "panel": "shared"
      }
    },
    {
      "label": "Clippy Fix",
      "type": "shell",
      "command": "cargo",
      "args": ["clippy", "--fix", "--allow-dirty", "--allow-staged"],
      "group": "build"
    }
  ]
}

IntelliJ Rust設定

# IntelliJ IDEA / CLion設定
# File → Settings → Languages & Frameworks → Rust

# External Linter設定:
# Tool: Custom
# Program: cargo
# Arguments: clippy --all-targets --all-features --message-format=json
# Working Directory: $ProjectFileDir$

# Rustfmt設定:
# Use rustfmt instead of built-in formatter: ✓
# Rustfmt executable: ~/.cargo/bin/rustfmt

# Cargo設定:
# Offline mode: ✗
# All features: ✓

CI/CD統合例

GitHub Actions

# .github/workflows/clippy.yml
name: Clippy

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  CARGO_TERM_COLOR: always

jobs:
  clippy:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Rust
      uses: actions-rs/toolchain@v1
      with:
        toolchain: stable
        components: clippy
        override: true
    
    - name: Cache cargo registry
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
        restore-keys: |
          ${{ runner.os }}-cargo-
    
    - name: Cache cargo build
      uses: actions/cache@v4
      with:
        path: target
        key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
    
    - name: Run Clippy
      uses: actions-rs/clippy-check@v1
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        args: --all-features --all-targets -- -D warnings
    
    - name: Run Clippy (JSON output)
      run: |
        cargo clippy --all-features --all-targets --message-format=json > clippy-results.json
    
    - name: Upload Clippy results
      uses: actions/upload-artifact@v4
      if: always()
      with:
        name: clippy-results
        path: clippy-results.json

GitLab CI/CD

# .gitlab-ci.yml
variables:
  CARGO_HOME: $CI_PROJECT_DIR/.cargo
  RUSTUP_HOME: $CI_PROJECT_DIR/.rustup

cache:
  key: rust-cache
  paths:
    - .cargo/
    - .rustup/
    - target/

stages:
  - quality

clippy:
  stage: quality
  image: rust:1.75
  before_script:
    - apt-get update -qq && apt-get install -y -qq git
    - rustup component add clippy
  script:
    - cargo clippy --version
    - cargo clippy --all-targets --all-features -- -D warnings
    - cargo clippy --all-targets --all-features --message-format=json > clippy-report.json
  artifacts:
    reports:
      codequality: clippy-report.json
    paths:
      - clippy-report.json
    expire_in: 1 week
  only:
    - main
    - develop
    - merge_requests

Lint設定とカスタマイズ

段階的導入設定

// src/lib.rs - 段階的にClippyを有効化
#![warn(clippy::all)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::too_many_arguments)]

// 特定関数でのlint制御
#[allow(clippy::unwrap_used)]
fn legacy_function() {
    // レガシーコードでの一時的な許可
    let value = some_option.unwrap();
}

// 条件付きlint
#[cfg_attr(feature = "strict", deny(clippy::unwrap_used))]
fn conditional_strict() {
    // strict featureが有効な場合のみ厳格チェック
}

プロジェクト全体設定

// src/main.rs または src/lib.rs
// 全体的なlint設定
#![deny(clippy::correctness)]
#![warn(clippy::perf)]
#![warn(clippy::style)]
#![allow(clippy::complexity)]

// 特定カテゴリの詳細設定
#![warn(clippy::explicit_iter_loop)]
#![warn(clippy::manual_memcpy)]
#![warn(clippy::match_wildcard_for_single_variants)]
#![warn(clippy::needless_continue)]
#![warn(clippy::needless_pass_by_value)]

// セキュリティ関連
#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![warn(clippy::panic)]

実用的なコード例とClippy提案

パフォーマンス最適化の例

// Clippy推奨: 効率的なイテレーション
// Before (clippy::manual_memcpy warning)
for i in 0..src.len() {
    dst[i] = src[i];
}

// After: Clippyの推奨に従った修正
dst[..src.len()].copy_from_slice(src);

// Before (clippy::needless_collect warning)
let vec: Vec<_> = iterator.collect();
vec.len()

// After: 不要なcollectを削除
iterator.count()

// Before (clippy::redundant_closure warning)
items.map(|x| func(x))

// After: クロージャの簡略化
items.map(func)

正確性向上の例

// Before (clippy::float_cmp warning)
if x == 0.1 + 0.2 {
    // 浮動小数点比較の問題
}

// After: 適切な浮動小数点比較
const EPSILON: f64 = 1e-10;
if (x - (0.1 + 0.2)).abs() < EPSILON {
    // 正確な比較
}

// Before (clippy::option_unwrap_used warning)
let value = option.unwrap();

// After: 安全なOption処理
let value = option.expect("値が必要です");
// または
if let Some(value) = option {
    // 安全な処理
}

トラブルシューティングとデバッグ

設定診断

# Clippy設定確認
cargo clippy -- --help

# 利用可能なlint一覧
cargo clippy -- -W help

# 特定lintの詳細情報
cargo clippy -- --explain E0308

# 設定ファイル検証
cargo clippy --config clippy.toml -- --cfg test

# 詳細デバッグ出力
RUST_LOG=clippy=debug cargo clippy

# パフォーマンス分析
time cargo clippy --all-targets --all-features

よくある問題と解決法

# キャッシュクリア
cargo clean
rm -rf ~/.cargo/registry/cache

# toolchainの問題
rustup update
rustup component add clippy --toolchain stable

# 依存関係の問題
cargo update
cargo check

# メモリ不足対策
export CARGO_BUILD_JOBS=1
cargo clippy --release

# 大規模プロジェクトの最適化
cargo clippy --frozen --offline
cargo clippy --no-default-features