2016/4/5 追記: こっちを使った方が便利そうです。
今回は Golang でイケてるソースコードを書くのに使うと良さそうなツールについて調べた。
使った環境は次の通り。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.11.4 BuildVersion: 15E65 $ go version go version go1.6 darwin/amd64
標準のコンパイラ
まずは大前提として Golang は他の言語に比べると標準のコンパイラも結構細かくお作法について口出ししてくれる。
例えば、次のようにして使われない変数 n を残したソースコードを用意する。
$ cat << 'EOF' > helloworld.go package main import ( "fmt" ) func main() { n := 1 // 使われない変数 fmt.Println("Hello, World!") } EOF
これを go run コマンドでコンパイル&実行してみるとコンパイラがちゃんと注意してくれる。 オプションとかは特につけることなく厳しくチェックしてくれるのはとても心強い。
$ go run helloworld.go # command-line-arguments ./helloworld.go:8: n declared and not used
親切だね。
go vet
とはいえコンパイラでは拾ってくれないものもある。
例えば、次のようにして到達しないコードが含まれるソースコードを用意してみよう。
$ cat << 'EOF' > helloworld.go package main import ( "fmt" ) func main() { return fmt.Println("Hello, World!") // 到達しないコード } EOF
今度も go run コマンドで実行してみる。 するとエラーなどは特に出ることがなく実行できてしまった。
$ go run helloworld.go
こんなときは標準のツールとして用意されている go vet コマンドにかけてみよう。 すると、先ほどの問題の行を特定して教えてくれた。
$ go vet helloworld.go helloworld.go:9: unreachable code exit status 1
gofmt
また Golang は標準ツールとしてコーディングスタイルのチェッカが同梱されている。
例えば、次のようにインデントにスペースを使ったソースコードを用意しておこう。
$ cat << 'EOF' > helloworld.go package main import ( "fmt" ) func main() { fmt.Println("Hello, World!") } EOF
これを gofmt コマンドにかけるとインデントがタブに変換されたものが標準出力として得られる。 Golang のコーディングスタイルではインデントが 8 タブに決まっているため。
$ gofmt helloworld.go package main import ( "fmt" ) func main() { fmt.Println("Hello, World!") }
直接ファイルを上書きしてしまいたいときは -w オプションを付ける。 あるいは go fmt コマンドを使っても良さそう。
$ gofmt -w helloworld.go
golint
次に紹介するのは Golang 版の lint の golint だ。
これは標準で同梱されているツールではないので go get コマンドでインストールする。
$ go get -u github.com/golang/lint/golint
次は、一見すると何も問題がないようなソースコードを用意しておこう。
$ cat << 'EOF' > helloworld.go package main import ( "fmt" ) func Greet() { fmt.Println("Hello, World!") } func main() { Greet() } EOF
これを golint にかけると公開されている関数 (先頭が大文字) にも関わらず godoc のコメントがないことで怒られる。
$ golint helloworld.go helloworld.go:7:1: exported function Greet should have comment or be unexported
次はコメントをつけてみよう。
$ cat << 'EOF' > helloworld.go package main import ( "fmt" ) // Greet は挨拶する関数です func Greet() { fmt.Println("Hello, World!") } func main() { Greet() } EOF
今度は怒られない。
$ golint helloworld.go
goimports
Golang のコンパイラは使われていないインポートがあるときも激おこになる。
使われていないパッケージ os をインポートしたソースコードを用意しておこう。
$ cat << 'EOF' > helloworld.go package main import ( "fmt" "os" // os パッケージは使われていない ) // Greet は挨拶する関数です func Greet() { fmt.Println("Hello, World!") } func main() { Greet() } EOF
もちろん、これは go run コマンドすると怒られる。
$ go run helloworld.go # command-line-arguments ./helloworld.go:5: imported and not used: "os"
まあ、これは手動で削除すれば良いだけなんだけど、それをアシストしてくれる goimports というツールもある。 これも標準で同梱されていないのでインストールしよう。
$ go get golang.org/x/tools/cmd/goimports
先ほどのソースコードにかけると os が削除された内容が標準出力で得られる。 とはいえ、同じ行にあったコメントなんかは残るみたいだ。
$ goimports helloworld.go package main import "fmt" // os パッケージは使われていない // Greet は挨拶する関数です func Greet() { fmt.Println("Hello, World!") } func main() { Greet() }
ファイルを直接上書きしたいときは gofmt コマンドと同じように -w オプションをつける。
$ goimports -w helloworld.go
ちなみに goimports は不要なパッケージを削除するのとは反対に必要なパッケージを追加することもできる。
次のようにして fmt パッケージが足りていないソースコードを用意しておこう。
$ cat << 'EOF' > helloworld.go package main // Greet は挨拶する関数です func Greet() { fmt.Println("Hello, World!") } func main() { Greet() } EOF
これを goimports にかけると、今度は fmt パッケージが追加されている。
$ goimports helloworld.go package main import "fmt" // Greet は挨拶する関数です func Greet() { fmt.Println("Hello, World!") } func main() { Greet() }
べんり。
まとめ
今回は質の高い Golang のソースコードを書く助けになるツールをいくつか使ってみた。 ここではすべてコマンドラインでそれぞれのツールを実行したけど、実際にはエディタや CI と連携させて使うことになるはず。