Goの静的解析ツールまとめ


まとめようと思ったきっかけ

Goを使いこなせる組織作りの取り組み / DeNA.go #1
https://speakerdeck.com/daisuzu/dena-dot-go-number-1?slide=10

こちらのスライドを見て、Goを始めたばかりの私はgofmtしか知らなかったので、この機会に調べまとめてみようと思いました。

今回まとめたツール一覧

  • gofmt
  • go fmt
  • goimports
  • go vet
  • golint

gofmt

  • 書いたコードを整形してくれる
引数 説明
-l フォーマットする必要のあるファイルを表示する
-w 修正内容を標準出力に書き出すのではなく、直接ファイルに書き込む
-d ファイルに書き込まずに、フォーマット前後のdiffを表示
-e すべての文法エラーを標準出力に書き出す。この引数を使わない場合は10行のエラーまでしか表示されない

ドキュメント

go fmt

Fmt runs the command 'gofmt -l -w' on the packages named by the import paths. It prints the names of the files that are modified.

使い方

main.go
package main

import "fmt"

func main() {
fmt.Println("gofmt")
}
$ go fmt main.go
main.go

修正内容が標準出力に書き出されず、それを行ったファイル名が表示され、

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("gofmt")
}

直接修正内容が反映されていることがわかります

goimports

  • importに関して、足りないimportを追加したり、使われていないimportを削除してくれる

ドキュメント

インストール

$ go get golang.org/x/tools/cmd/goimports

使い方

main.go
package main

import (
        "fmt"
        "os"
)

func main() {
        fmt.Println("goimports")
}

goimportsを実行(-wを付けないと標準出力に書き出し)

$ goimports main.go
package main

import (
        "fmt"
)

func main() {
        fmt.Println("goimports")
}

-wを付けると、importが整理された状態にファイルが変更されます

$ goimports -w main.go

go vet

  • goの標準パッケージに含まれる静的解析ツール
  • コンパイラによって検出されないエラーを見つけることができる
  • ただし、本当に問題あるかは保証できない

ドキュメント

使い方

  • 単一のgoファイルに対して
$ go tool vet xxx.go
main.go
package main

import "fmt"

func main() {
        a := 0
        if a != 1 || a != 2 {
                a++
        }

        fmt.Printf("a = %s\n", a)
}
$ go tool vet main.go
main.go:7: suspect or: a != 1 || a != 2
main.go:11: Printf format %s has arg a of wrong type int
  • ディレクトリ内のすべてのgoファイルに対して
$ go tool vet [ディレクトリ]

golint

  • コードのコーディングスタイルの問題を検出する
  • エラーというより、「Suggestion(提案)」である

ドキュメント

インストール

$ go get golang.org/x/lint/golint

使い方

main.go
package main

import "fmt"

func Foo(bar int) int {
        if bar > 0 {
                return 1
        } else {
                return 0
        }
}

func main() {
        fmt.Println("golint")
        fmt.Println(Foo(1))
}
$ golint main.go
main.go:5:1: exported function Foo should have comment or be unexported
main.go:8:9: if block ends with a return statement, so drop this else and outdent its block

以下のように修正すると、

main.go
package main

import "fmt"

func Foo(bar int) int {
        if bar > 0 {
                return 1
        }

        return 0
}
func main() {
        fmt.Println("golint")
        fmt.Println(Foo(1))
}

二つ目の提案が消えました。

$ golint main.go
main.go:5:1: exported function Foo should have comment or be unexported

参考