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行のエラーまでしか表示されない|

### ドキュメント

https://golang.org/cmd/gofmt/

## go fmt

- `gofmt -l -w`のこと
- 修正内容を標準出力に書き出すのではなく、直接ファイルに書き込み、それを行ったファイル名を表示する
    - https://golang.org/cmd/go/#hdr-Run_gofmt_on_package_sources

```
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.
```

### 使い方

```golang: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を削除してくれる

### ドキュメント

https://godoc.org/golang.org/x/tools/cmd/goimports

### インストール

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


### 使い方

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

### ドキュメント
https://golang.org/cmd/vet/


### 使い方

- 単一のgoファイルに対して

```
$ go tool vet xxx.go
```

```golang: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(提案)」である

### ドキュメント

https://github.com/golang/lint

### インストール

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

### 使い方

```golang: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
```

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

```golang: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
```

## 参考

http://blog.ralch.com/tutorial/golang-tools-inspection/

https://codeengineered.com/blog/2014/golint/