こんにちは。Tomoyuki(@tomoyuki65)です。
Go言語(Golang)は標準機能が充実していますが、Go標準の静的解析ツールとして「go vet」コマンドがあります。
ただし、一般的なlintツールとは違っていて、「コンパイルは通るが、バグにつながる可能性が高いコードを検出する」ようなツールになっているため、その点は注意が必要です。
この記事では、そんな「go vet」コマンドの使い方についてご紹介します。
Go言語(Golang)標準の静的解析ツール「go vet」の使い方|バグになりそうな怪しいコードの調査
例えば以下のような変数の値をログ出力させるスクリプトがあったとします。
package main
import (
"fmt"
)
func main() {
name := "田中太郎"
fmt.Printf("%d\n", name)
}
次に「go vet」コマンドを実行し、静的解析チェックをします。
go vet ./...
※必要に応じて適切なファイルパスを指定してチェックして下さい。
実行後、以下のようなメッセージがログ出力され、バグになりそうな怪しいコードをチェックできます。
![]()
※メッセージ内容「fmt.Printf format %d has arg name of wrong type string」の翻訳「fmt.Printfの書式指定子%dに対し、string型の引数nameが渡されています。」
また、チェック内容については、内部にある「アナライザー(修正アルゴリズム)」をもとにチェックしており、特定のアナライザーを指定してチェックも可能です。
アナライザー一覧(Go1.26の場合)
| アナライザー | 概要 | 主な検出対象・例 |
|---|---|---|
appends |
append の引数不足を検出 |
append(slice) のように追加する値がない呼び出し |
asmdecl |
Go宣言とアセンブリの不整合を検出 | アセンブリ関数の引数・戻り値サイズなどの不一致 |
assign |
無意味な代入を検出 | 代入直後に上書きされ、値が一度も使われない |
atomic |
sync/atomic のよくある誤りを検出 |
atomic操作対象のコピーなど |
bools |
boolean演算のよくある誤りを検出 | 冗長・矛盾した真偽値条件 |
buildtag |
ビルドタグを検査 | //go:build や // +build の記述ミス |
cgocall |
cgoのポインタ渡しルール違反を検出 | GoポインタをC側へ不正に渡すケース |
composites |
キーなし複合リテラルを検出 | 外部パッケージの構造体を T{a, b} のように初期化 |
copylocks |
lockを値コピーしてしまう問題を検出 | sync.Mutex を含む構造体の値渡し・コピー |
defers |
defer のよくある誤りを検出 |
defer の引数評価タイミングに関する問題 |
directive |
Goツールチェーン用ディレクティブを検査 | //go:debug などの不正な指定 |
errorsas |
errors.As の誤った使用を検出 |
第2引数がポインタでない、error型として不適切 |
framepointer |
アセンブリのフレームポインタ破壊を検出 | 保存前にframe pointerを上書き |
hostport |
net.Dial 等に渡すアドレス形式を検査 |
IPv6などで host:port 形式が不正 |
httpresponse |
HTTPレスポンス処理の誤りを検出 | resp.Body の扱いなど、HTTP利用時の問題 |
ifaceassert |
実現不可能なインターフェース型アサーションを検出 | x.(SomeInterface) が型の関係上成立しない |
loopclosure |
ループ変数をクロージャから参照する問題を検出 | goroutineや無名関数で意図しないループ変数を参照 |
lostcancel |
context のCancelFuncを呼び忘れる問題を検出 |
context.WithCancel の戻り値 cancel を使用しない |
nilfunc |
関数値と nil の無意味な比較を検出 |
常に真・偽になる可能性がある比較 |
printf |
フォーマット文字列と引数の不整合を検出 | %d にstringを渡す、引数不足など |
shift |
ビット幅以上のシフトを検出 | uint8 相当の値に対して8ビット以上のシフト |
sigchanyzer |
os.Signal 用の非バッファチャネルを検出 |
シグナル受信用チャネルがunbuffered |
slog |
log/slog の構造化ログ呼び出しを検査 |
key/valueの組み合わせ不正など |
stdmethods |
有名な標準インターフェースのメソッドシグネチャを検査 | String()、Error()、MarshalJSON() 等の誤ったシグネチャ |
stdversion |
対象Goバージョンより新しい標準ライブラリAPIを検出 | 古いGoバージョン向けコードで新APIを使用 |
stringintconv |
string(int) 変換の誤解を検出 |
数値を文字列化するつもりで string(123) と書く |
structtag |
struct tagの形式を検査 | reflect.StructTag.Get で解釈できないタグ |
testinggoroutine |
テスト中のgoroutineから Fatal 等を呼ぶ問題を検出 |
goroutine内の t.Fatal()、t.FailNow() 系 |
tests |
テスト・Exampleのよくある誤用を検出 | テスト関数やExampleの命名・使用ミス |
timeformat |
Goの日時フォーマット指定ミスを検出 | YYYY-MM-DD のような他言語形式を使用 |
unmarshal |
Unmarshal先として不適切な値を検出 | 非ポインタや不適切な値を渡す |
unreachable |
到達不能コードを検出 | return の後に実行されない処理 |
unsafeptr |
uintptr と unsafe.Pointer の危険な変換を検出 |
GCとの関係で不正になるポインタ変換 |
unusedresult |
戻り値の未使用を検出 | fmt.Sprintf()、errors.New() 等の結果を捨てる |
waitgroup |
sync.WaitGroup の誤用を検出 |
Add と Wait の不適切な並行使用など |
※利用可能なアナライザー一覧は「go tool vet help」で確認できます。特定のアナライザーでチェックしたい場合は、「go vet -printf ./…」のように実行するとチェックできます。
最後に
今回はGo標準の静的解析ツール「go vet」の使い方についてご紹介しました。
一般的なlintとは少し違うため、実際には他のツール(Staticcheckなど)も合わせて使うのを推薦しますが、Go標準で静的解析チェックができるのは便利です。
Go言語でバグになりそうな怪しいコードの調査をしたい場合は、ぜひ参考にしてみて下さい。



コメント