メインコンテンツまでスキップ

ツールチェーンと静的解析

.NET (C#) の経験がある開発者向けに、gofmt、go vet、golangci-lint、go generate などの開発ツールを解説します。C# では Roslyn Analyzer、.editorconfig、dotnet format、Source Generator が担う役割を、Go ではツールチェーン標準のコマンドと少数の外部ツールで分担します。設定項目が少なく「議論の余地がない」ことを重視する文化なので、最初に押さえるべきコマンドと CI での固め方を整理します。

1. このドキュメントで扱うこと​

  • gofmt による議論の余地のないフォーマット
  • go vet と golangci-lint による静的解析、および v2 の設定
  • go generate の使いどころ
  • CIへの組み込み

gofmt による議論の余地のないフォーマット​

Go のフォーマットは gofmt が唯一の正解を決めます。インデントはタブ、括弧や空白の位置もツールが決めるため、スタイルに関するレビュー指摘やチーム内の設定議論がほぼ発生しません。

  • gofmt -l . は整形が必要なファイルを一覧表示します。gofmt -d . は差分を表示し、gofmt -w . はファイルを書き換えます。
  • gofmt -s は []T{T{...}} から []T{{...}} への簡略化など、安全な単純化も行います。
  • goimports (golang.org/x/tools/cmd/goimports) は gofmt の機能に加えて、import の追加・削除とグループ分けを行います。多くのエディターは保存時に gopls 経由で同等の処理を実行できます。
  • gofumpt は gofmt より厳格なフォーマッターです。gofmt と互換な範囲でさらに規則を足します。導入は任意で、golangci-lint のフォーマッターとして有効化できます。

go vet と golangci-lint による静的解析​

go vet は Go ツールチェーン標準の解析で、Printf の書式文字列の不一致、context の cancel 呼び忘れ (lostcancel)、ロックを含む値のコピー (copylocks) など、バグである可能性が高いものを検出します。go test は実行時に go vet の一部のチェックを自動で走らせます。

golangci-lint は多数のリンターをまとめて実行する、事実上の標準ランナーです。並列実行とキャッシュで高速に動作し、.golangci.yml で設定を共有します。staticcheck もこの中に統合されています。バージョン 2 (v2) では設定ファイルの書式が v1 から変わっており、version: "2" を必ず書きます。

区分v2 のキー内容
リンターlintersdefault (standard など) と enable / disable、個別設定は linters.settings
除外linters.exclusionsプリセット (comments など)、パス・テキストによる除外ルール
フォーマッターformattersgofmt、goimports、gofumpt などを enable で有効化

default: standard で有効になるのは errcheck、govet、ineffassign、staticcheck、unused です。

go generate の使いどころ​

go generate は、ソースコード中の //go:generate コマンド という特別なコメントを見つけて実行するだけのコマンドです。go build や go test からは自動実行されません。生成物はリポジトリにコミットし、生成を忘れていないかを CI で確認します。

  • stringer による列挙型の String() メソッド生成
  • mockgen や moq によるモック生成
  • sqlc や oapi-codegen による SQL、OpenAPI からのコード生成
  • Protocol Buffers のコード生成

生成されたファイルには // Code generated ... DO NOT EDIT. という形式の 1 行目付近のコメントを入れる規約があり、リンターやエディターはこれを見て生成コードを特別扱いします。

CIへの組み込み​

ローカルと CI で同じコマンドを使い、次の順に確認するのが基本です。

  1. フォーマット (gofmt -l、または golangci-lint fmt --diff)
  2. go vet ./... と golangci-lint run
  3. 生成コードと go.mod / go.sum が最新であること (go generate ./... や go mod tidy の後に差分がない)
  4. テスト (go test -race ./...。詳細は テスト を参照)
  5. 脆弱性チェック (govulncheck ./...)

ビルドとデプロイ側の CI は Goのビルドとデプロイ で扱います。

2. .NET との対応表​

.NET (C#)Go
dotnet format / EditorConfiggofmt / goimports
Roslyn Analyzersgo vet / staticcheck / golangci-lint
Source Generatorsgo generate + コード生成ツール

上の表を補う対応関係です。

.NET (C#)Go
dotnet format --verify-no-changesgofmt -l . の出力が空であることを確認 / golangci-lint fmt --diff
.editorconfig の dotnet_diagnostic.*.severity.golangci.yml の linters / exclusions
TreatWarningsAsErrorsgolangci-lint run が問題を検出すると終了コード 1 を返す
#pragma warning disable CA1822//nolint:linter名 // 理由
NuGet の Analyzer パッケージgolangci-lint に同梱されたリンター (追加パッケージ不要)
dotnet-tools.json (ローカルツール)go.mod の tool ディレクティブ (Go 1.24 以降) と go tool
Source Generator (ビルド中に自動実行)go generate (明示的に実行し、生成物をコミット)
T4 テンプレートtext/template と //go:generate を組み合わせた独自ジェネレーター
NuGet audit / dotnet list package --vulnerablegovulncheck ./...

3. C# 開発者がつまずきやすいポイント​

フォーマットの設定項目がない​

.editorconfig でインデント幅や波括弧の位置を調整する感覚は不要です。gofmt には実質的に設定がなく、全員が同じ出力になります。エディターの保存時フォーマットを有効にするだけで済みます。

gofmt -l は差分があっても終了コード 0 を返す​

CI で「未フォーマットなら失敗」にしたいときは、gofmt -l . の出力が空かどうかを自分で判定する必要があります。dotnet format --verify-no-changes のような専用フラグはありません。golangci-lint fmt --diff や、golangci-lint run にフォーマッターを含めた構成にすると扱いやすくなります。

ビルド時にアナライザーは動かない​

C# では Roslyn Analyzer がコンパイル中に走り、警告やエラーとしてビルドに混ざります。Go のコンパイラは、未使用の変数や未使用の import をエラーにしますが、それ以外の解析はしません。go vet や golangci-lint は別コマンドで、自分で実行する必要があります。

golangci-lint v1 と v2 の設定は互換ではない​

ネット上の記事には v1 形式 (linters-settings、issues.exclude-rules など) が多く残っています。v2 では version: "2" が必須で、linters.settings、linters.exclusions、formatters に再編されました。golangci-lint migrate で旧設定を変換でき、golangci-lint config verify で設定ファイルを検証できます。

リンターは「全部有効」にしない​

大量のリンターを有効にすると、好みの分かれる指摘が増えて開発体験が落ちます。default: standard から始め、バグ検出に効果の高いものを選んで足します。既存コードに導入する場合は、新しく書いた部分だけを対象にする new-from-rev 設定 (issues.new-from-rev) を使うと段階的に導入できます。

//nolint は理由を添える​

//nolint:errcheck のように対象のリンターを指定し、理由をコメントで添えます。リンター名を省略した //nolint はすべてのリンターを黙らせるため避けます。nolintlint を有効にすると、使われていない //nolint や理由のない //nolint を検出できます。

go generate は自動では動かない​

Source Generator のように、ビルドすれば生成されるわけではありません。生成元を変更したら go generate ./... を実行し、生成物を一緒にコミットします。忘れると、古い生成物のままビルドが通ってしまいます。CI で生成後の差分を検出してください。

ツールのバージョンをチームで揃える​

go install foo@latest を各自が実行すると、ツールのバージョンが人によって異なります。Go 1.24 以降は、go get -tool で go.mod に tool ディレクティブを記録し、go tool 名前 で go.mod に固定したバージョンを実行できます。golangci-lint 自体は、公式ドキュメントが推奨するバイナリのインストールや公式 GitHub Action を使うのが無難です。

4. コード例​

例 1: 日常的に使うコマンド​

# フォーマット (差分表示、書き換え、簡略化つき)
gofmt -l .
gofmt -d .
gofmt -s -w .

# import の整理 (インストールは一度だけ)
go install golang.org/x/tools/cmd/goimports@latest
goimports -w .

# 標準の静的解析
go vet ./...

# golangci-lint v2
golangci-lint run
golangci-lint fmt # フォーマッターの実行 (書き換え)
golangci-lint fmt --diff # 差分の表示のみ
golangci-lint config verify

# 依存関係の脆弱性チェック
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

C# では dotnet format や IDE のコードクリーンアップに相当する操作です。

例 2: golangci-lint v2 の設定​

.golangci.yml の例です。standard を土台に、バグ検出に有効なものを足します。

version: "2"

run:
timeout: 5m

linters:
default: standard # errcheck, govet, ineffassign, staticcheck, unused
enable:
- bodyclose # http.Response.Body の閉じ忘れ
- errorlint # errors.Is / errors.As を使うべき箇所
- gocritic
- misspell
- nolintlint # 不適切な //nolint の検出
- revive
- unparam
settings:
errcheck:
check-type-assertions: true
govet:
enable:
- shadow # 変数のシャドーイング
nolintlint:
require-specific: true # //nolint:linter名 の形式を必須にする
require-explanation: true
exclusions:
presets:
- comments
- std-error-handling
rules:
- path: _test\.go
linters:
- errcheck
- unparam

formatters:
enable:
- gofumpt
- goimports
settings:
goimports:
local-prefixes:
- example.com/app

default には standard、all、none、fast を指定できます。個別の設定項目は、使用する golangci-lint のバージョンの公式ドキュメントで確認してください。

例 3: //nolint の書き方​

// 良い例: リンター名を限定し、理由を添える
defer f.Close() //nolint:errcheck // 読み取り専用のため Close のエラーは無視してよい

// 悪い例: すべてのリンターを無効にしてしまう
defer f.Close() //nolint

C# の #pragma warning disable CA2000 や [SuppressMessage] に対応します。

例 4: go generate と tool ディレクティブ​

列挙型の String() を stringer で生成する例です。

package color

//go:generate go tool stringer -type=Color

type Color int

const (
Red Color = iota
Green
Blue
)

stringer を go.mod に固定します (Go 1.24 以降)。

go get -tool golang.org/x/tools/cmd/stringer

go.mod には次のような行が追加されます。

tool golang.org/x/tools/cmd/stringer
go generate ./... # color_string.go が生成される
go tool stringer -help
go install tool # go.mod の tool をすべて GOBIN にインストール

go generate の指示は、そのファイルがあるディレクトリで実行されます。複数のディレクティブは上から順に実行されます。Go 1.23 以前では、ツールを tools.go に空 import (_ "golang.org/x/tools/cmd/stringer"、//go:build tools つき) して固定する方法が使われていました。

例 5: GitHub Actions での lint と検証​

name: lint

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Check formatting
run: test -z "$(gofmt -l .)"

- name: Vet
run: go vet ./...

- name: golangci-lint
uses: golangci/golangci-lint-action@v8
with:
version: v2.5

- name: Verify generated code is up to date
run: |
go generate ./...
git diff --exit-code

- name: Verify go.mod is tidy
run: go mod tidy -diff

- name: Test
run: go test -race -cover ./...

- name: Vulnerability check
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

golangci-lint-action の version は、使用する v2 系のバージョンに合わせて固定します。go mod tidy -diff は Go 1.23 以降で使え、変更が必要なら差分を表示して失敗します。

例 6: C# での同等設定​

C# 側では、Directory.Build.props と .editorconfig で次のように品質ゲートを構成するのが一般的です。

<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>
</Project>
# .editorconfig
[*.cs]
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.CA1062.severity = warning

Go では、この役割を .golangci.yml の 1 ファイルと CI のコマンド列が担います。ビルド自体には解析が入らないため、CI で明示的に実行する点が違いです。

5. まとめ​

  • フォーマットは gofmt (または goimports、gofumpt) に任せ、設定を議論しません。CI では gofmt -l の出力が空かどうかを判定します。
  • go vet は標準の静的解析で、golangci-lint が staticcheck を含む多数のリンターをまとめて実行します。v2 では version: "2"、linters、formatters の構成で設定します。
  • リンターは default: standard から始め、価値の高いものだけを足します。//nolint はリンター名と理由を必ず書きます。
  • go generate は明示的に実行するコマンドで、生成物はコミットします。CI で再生成して差分がないことを確認します。
  • Go 1.24 以降は go.mod の tool ディレクティブと go tool で、開発ツールのバージョンを固定できます。
  • CI では、フォーマット、go vet、golangci-lint、生成コードと go.mod の整合、go test -race、govulncheck を順に確認します。

参考資料​