静的解析とリンター

静的解析とは

静的解析は、プログラムを実行せずにソースコードそのものを読んで問題を指摘する検査です。ツールはコードを構文木(プログラムの構造を木にしたもの)へ変換し、「この変数はどこからも使われていない」「この比較は必ず false になる」といったパターンを機械的に探します。

実行しないことには、はっきりした利点があります。

  • 書いた瞬間に分かる — エディタ上で波線が出る。テストの実行やデプロイを待つ必要がない
  • 通りにくい経路も検査できる — めったに実行されないエラー処理の分岐も、コードとして書かれてさえいれば見てもらえる
  • 結果が安定している — 実行環境や入力データに左右されず、誰の手元でも同じ指摘が出る

一方で、静的解析は仕様どおりかどうかは判断できません。「割引率を掛け忘れている」は文法的に正しいコードなので指摘されません。それを見つけるのは、実際に動かして結果を確かめる [[自動テスト]] の役割です。静的解析と自動テストは競合するものではなく、役割の違う車の両輪だと考えてください。

リンターとフォーマッタの違い

現場でよく混同されるのがこの2つです。

リンターフォーマッタ
見るものコードの意味・危うさ見た目
出力警告・エラーの一覧整形し直したコード
判断「これは間違いかもしれない」「規則の形はこうだ」
議論どのルールを使うかは議論になる一度決めたら議論しない

リンターは「未使用の変数がある」「await を忘れている」のように、動くかもしれないが怪しい書き方を指摘します。名前の由来は1970年代のC言語向けツール lint で、布に付いた糸くず(lint)を取り除くという比喩です。

フォーマッタはインデント幅や改行位置、引用符の種類などを規則どおりに書き換えます。正しさは判断せず、見た目だけを扱います。

両者は守備範囲が重なることがあります。整形に関するルールをリンター側にも持たせると、保存のたびに互いの主張がぶつかって延々と書き換え合うことになります。整形はフォーマッタに一本化し、リンターは意味の検査に専念させるのが定石です。

代表的なルール

分類指摘される理由
未使用・到達不能使われていない変数・import、return の後ろのコード消し忘れや書き換えの残骸。読み手を迷わせる
危うい型の扱い暗黙の型変換を伴う比較、なんでも入る型への逃げ意図しない値の一致・すり抜けを生む
明らかなバグの型条件式の中での代入、await 忘れ、分岐の書き漏れそのまま実行時の不具合になる
命名・書式命名規則、ファイル名の付け方一貫性が崩れると読む速度が落ちる
セキュリティ文字列連結によるSQL組み立て、危険な動的評価脆弱性に直結する
if (user = admin) {  // 比較 === のつもりが代入。リンターが指摘する
  grantAccess()
}

await db.query('select * from users where id = ' + id) // 危険な連結

どのルールを有効にするかはチームの判断です。既定の推奨セットから始め、議論になったものだけ足し引きするのが現実的な進め方になります。

CIで lint をゲートにする

指摘は早いほど安いので、検査は多層に置きます。

  1. エディタ — 書いている最中に表示。ここで直せば誰の時間も使わない
  2. コミット前 — フックで変更したファイルだけ検査する。壊れたコードが履歴に入りにくくなる
  3. CI — [[CI/CD]] のパイプラインで lint をジョブとして実行し、失敗したらマージさせない

3つ目が要です。手元での実行は忘れられますが、CIのゲートは忘れられません。「警告は出るがマージはできる」状態にすると、警告は必ず溜まり、やがて誰も読まなくなります。警告ゼロを既定の状態にするところまで含めて仕組みです。

既存のプロジェクトに後から導入すると、初回に数千件の指摘が出ることがあります。その場合は一度に直そうとせず、次のどちらかを採ります。

  • 段階導入 — ルールを数個ずつ有効化し、その都度直す
  • ベースライン方式 — 既存の指摘を「現状」として記録し、新しく増えた分だけを失敗扱いにする

どちらも狙いは同じで、既存分を [[技術的負債]] として計上したうえで、新規の負債を増やさない線を引くことです。

false positive との付き合い方

静的解析は「怪しい」を機械的に判定するため、正しいコードを警告することがあります。これを false positive(偽陽性)と呼びます。逆に、本物の問題を見逃す偽陰性もあります。ツールが黙っていることは「バグが無い証明」ではありません。

偽陽性に出会ったら、多くのツールが用意している無効化コメントで抑制できます。ただし扱いには作法があります。

  • 理由を必ず書く — 「なぜここでは安全なのか」を残す。理由の無い抑制は、後から誰も消せなくなります
  • 範囲は最小にする — ファイル全体ではなく、その1行だけを対象にする
  • 同じ抑制が何度も出るならルールを疑う — 現場に合っていない証拠です。個々に抑制し続けるより、チームで無効化を決めるほうが健全です

ルールは多ければ良いわけではありません。ノイズが増えると本当に重要な指摘が埋もれ、警告全体が無視されるようになります。指摘の総量ではなく、指摘が信用されている状態を保つのが運用の目標です。

初学者向けポイント

  • lint のエラーは人格への評価ではありません。機械が定型作業を肩代わりしてくれていると考えましょう
  • まずエディタに入れるのが最も効きます。保存のたびに直す習慣がつきます
  • 自動修正の機能で多くは直せますが、一度は「なぜこのルールがあるのか」を読んでおくと応用が利きます
  • [[TypeScript]] のような型検査も静的解析の一種です。型のチェックとリンターは重なりつつ役割が分かれています

関連技術とのつながり

  • [[クリーンコード]] — 読みやすさの規約のうち、機械化できる部分を担うのがリンター
  • [[コードレビュー]] — 書式や凡ミスの指摘をツールに任せ、人は設計と仕様に集中できる
  • [[CI/CD]] — 静的解析をパイプラインのゲートに置いて、品質を自動で守る
  • [[自動テスト]] — 実行して確かめる動的な検査。静的解析と補い合う関係
  • [[技術的負債]] — 放置された警告や抑制コメントは負債として積み上がる
  • [[TypeScript]] — 型検査という形の静的解析。実行前に誤りを見つける発想は共通
Q: 静的解析の説明として正しいのはどれ?
- [x] プログラムを実行せずにソースコードを検査する
- [ ] 本番環境で実際の負荷をかけて性能を測る
- [ ] 実行中のメモリ使用量を記録して分析する
解説: 静的解析はコードを動かさずに構文の構造を調べ、怪しい書き方や明らかな誤りを指摘します。

Q: リンターとフォーマッタの関係として本文が推奨しているのはどれ?
- [ ] 整形ルールは両方に持たせて二重にチェックする
- [x] 整形はフォーマッタに一本化し、リンターは意味の検査に専念させる
- [ ] フォーマッタだけを使い、リンターは導入しない
解説: 整形ルールを両方に持たせると互いに書き換え合って衝突します。役割を分けるのが定石です。

Q: 静的解析の警告を無効化するコメントを書くときの作法はどれ?
- [ ] 影響範囲を広げるためファイル全体を対象にする
- [ ] 説明は不要なので短く書く
- [x] 理由を書き、対象範囲は最小にする
解説: 理由の無い抑制は後から誰も判断できず消せなくなります。範囲も1行など最小に留めます。