Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go 1.22 の Vet 変更点について

ANDPAD inc
March 18, 2024
1k

Go 1.22 の Vet 変更点について

ANDPAD inc

March 18, 2024
Tweet

Transcript

  1. Copyright © 2023 ANDPAD Inc. Copyright © 2023 ANDPAD Inc.

    ANDPADについて 1 Copyright © 2023 ANDPAD Inc. Go 1.22 の Vet 変更点について bushiyama
  2. Copyright © 2023 ANDPAD Inc. 本⽇、お話すること 4 本⽇は Go 1.22

    のお祝い 🎉 ということで、 リリースノートのTool関連から、 Vet周りの更新内容についてお話できたらと思います。 発表の内容はサンプルコードなど、資料で欲しい内容かとも思いますので 後⽇、弊社テックブログにも掲載の予定です。 https://tech.andpad.co.jp/
  3. Copyright © 2023 ANDPAD Inc. 弊チームで⽣きるVet 5 まずおさらいからということで Vetとは https://pkg.go.dev/cmd/vet

    > Go ソース コードを検査し、不審な構造を報告します。 > すべてのレポートが真の問題であることを保証するものではありませんが、コンパイラーに よって捕捉されなかったエラーを⾒つけることはできます。
  4. Copyright © 2023 ANDPAD Inc. 弊チームで⽣きるVet VSCode勢には、初期設定で⾃動実⾏してくれる⾝近な存在ですが、 弊チームでは、 CI でもあらためて

    golangci-lint から実⾏しています。 golangci.yml ... linters: enable: ... - govet ... ブラウザからの杜撰なコミットを防⽌してくれたりなど活躍しています。 ちなみに、golangci-lint での 1.22 対応は、v1.56.1 で対応済みのようです。
  5. Copyright © 2023 ANDPAD Inc. ここから本題 まずは v1.21.0 からの変更をダイジェスト。 本⽇はこれらについて時間の許す限り解説していきたいと思います。

    サマリ 7 - (omit!) References to loop variables - appends check for missing values after append new! - buildtag check //go:build and // +build directives update! ※リリースノート記載なし - defers report common mistakes in defer statements new! - directive check Go toolchain directives such as //go:debug defaultJoin! ※リリースノート記載なし - errorsas report passing non-pointer or non-error values to errors.As defaultJoin! ※リリースノート記載なし - framepointer report assembly that clobbers the frame pointer before saving it defaultJoin! ※リリースノート記載なし - ifaceassert detect impossible interface-to-interface type assertions defaultJoin! ※リリースノート記載なし - sigchanyzer check for unbuffered channel of os.Signal defaultJoin! ※リリースノート記載なし - slog check for invalid structured logging calls update! - stringintconv check for string(int) conversions defaultJoin! ※リリースノート記載なし - testinggoroutine report calls to (*testing.T).Fatal from goroutines started by a test defaultJoin! ※リリースノート記載なし - timeformat check for calls of (time.Time).Format or time.Parse with 2006-02-01 defaultJoin! ※リリースノート記載なし
  6. Copyright © 2023 ANDPAD Inc. ループ変数の参照(References to loop variables)問題の解消 8

    ループ変数への参照問題が解消されたことにより、警告されることがひとつ減りまし た 🎉 Go 1.20 でVetに追加された、以下の警告が役⽬を終えます。 loop variable i captured by func literal (omit!) References to loop variables
  7. Copyright © 2023 ANDPAD Inc. 他の⽅の発表で詳しく触れられると思いますが、 もうループ内で `i := i`

    を書かなくてよくなります。 逆に以下のような、 v1.22 で旧来コード記述でも問題のないことを確認しています。 ループ変数の参照(References to loop variables)問題の解消 9 for i := 0; i < 3; i++ { i := i wg.Add(1)
  8. Copyright © 2023 ANDPAD Inc. ループ変数の参照(References to loop variables)問題の解消 10

    ちなみに go.mod は 1.21 のままでも v1.22 で run/build した際には v1.22 の挙動になります。 local だけ v1.22 にしている場合などは挙動に差分がでるので注意が必要そうです。 必ず go.mod を確認しましょう。 Playground: https://go.dev/play/p/OBdtXOrFkQU でversionを切り替え試せます。 ※ go.dev ドメインに移⾏してから2年ほど vet が効かないようでしたが、最近しれっと復活していました🎉 func main() { const limit = 5 var wg sync.WaitGroup for i := 0; i < limit; i++ { wg.Add(1) go func() { defer wg.Done() fmt.Println(i) }() } wg.Wait() }
  9. Copyright © 2023 ANDPAD Inc. New warnings for missing values

    after append appned()は引数が無くても問題ありません(!)でしたが、今回Vetで警告とするように なりました🎉 append 11 func main() { x := []int{1} x = append(x) fmt.Println(x) } appends new!
  10. Copyright © 2023 ANDPAD Inc. append 12 staticcheckでは2019年から実装されていたものと同じ内容ですね。 https://staticcheck.io/docs/checks/#SA4021 SA4021

    - x = append(y) is equivalent to x = y append()で引数がなくて問題ないのは、 Go の組み込み関数で扱いが特別なためのよ うです。
  11. Copyright © 2023 ANDPAD Inc. 既存のbuildtagで変更があったようです。 • v1.21.0 doc check

    that +build tags are well-formed and correctly located • v1.22.0 doc check //go:build and // +build directives 更新が多く差分は調べきれませんでしたがこれはそもそもなんだろう?というところで コード内で使⽤されていないbuildtagや⽭盾するtagなどを検出します。 以下のようなコードが対象です。 buildtag 13 //go:build !(bad || worse) // +build !bad // +build !worse package a buildtag update! ※リリースノート記載なし
  12. Copyright © 2023 ANDPAD Inc. New warnings for deferring time.Since

    defer で time.Since() をしてしまっているものを警告するようになりました🎉 defers 15 defers new!
  13. Copyright © 2023 ANDPAD Inc. defers 16 以下は⼀⾒、 最後にstart からdeferで関数が終了した経過時間を出してくれそうに思えますが

    出⼒結果を保持して、最後にfmt.Printするといった挙動になります。 defer の引数とした関数は即時に評価されるためです。 start := time.Now() defer fmt.Println(time.Since(start)) time.Sleep(1 * time.Second) // => 0s
  14. Copyright © 2023 ANDPAD Inc. しかし Since() の省略形でない time.Now().Sub(t) とした場合は警告されません。

    defers 17 start := time.Now() defer fmt.Println(time.Now().Sub(start)) time.Sleep(1 * time.Second) // => 0s
  15. Copyright © 2023 ANDPAD Inc. defers 18 この time.Now().Sub(t) とする記述については別途、

    staticcheck で警告されます。 S1012 - Replace time.Now().Sub(x) with time.Since(x) これに従い time.Since() を利⽤するようにすると今度は本題の警告がされます。 タライ回しの感がありますねw defers という名称だと他にも⾊々と⾒てくれそうに思えますが、 本件はあくまで time.Since() の利⽤だけしか⾒ません。 名前負けの感が、少し残念です。
  16. Copyright © 2023 ANDPAD Inc. directive 19 check Go toolchain

    directives such as //go:debug 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 Go本体外での Go ツールチェーンで debug などの指定を警告します。 //go:debug build -o debug.out func main() { directive defaultJoin! ※リリースノート記載なし
  17. Copyright © 2023 ANDPAD Inc. report passing non-pointer or non-error

    values to errors.As 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 errors.As にポインタやエラーでない値を渡すことを警告してくれます。 import "errors" のみサポートとしているようなので注意が必要です。 errorsas 20 var pathError *fs.PathError err := fmt.Errorf("foo") // NG if errors.As(err, pathError) { fmt.Println("bad") } // ok if errors.As(err, &pathError) { fmt.Println("bad") } errorsas defaultJoin! ※リリースノート記載なし
  18. Copyright © 2023 ANDPAD Inc. report assembly that clobbers the

    frame pointer before saving it 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 スタックトレース関連のもので、 以下のようなフレームポインタを破壊するような記述を警告してくれます。 framepointer 21 TEXT ·z1(SB), 0, $0 MOVQ $0, BP // want `frame pointer is clobbered before saving` RET framepointer defaultJoin! ※リリースノート記載なし
  19. Copyright © 2023 ANDPAD Inc. ifaceassert 22 detect impossible interface-to-interface

    type assertions 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 インターフェイス間の不可能なアサーションを検知します。 いままで default じゃなかったんですねえ。 var v interface { Read() } _ = v.(io.Reader) ifaceassert defaultJoin! ※リリースノート記載なし
  20. Copyright © 2023 ANDPAD Inc. check for unbuffered channel of

    os.Signal 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 signal.Notify() に渡す chan の容量が確保(バッファリング)されていないと警告されます。 いままで default じゃなかったんですねえ。 main に書いて test もできずにそのままというパターンが結構ありそうなので助かりそうです。 sigchanyzer 23 c := make(chan os.Signal) f := signal.Notify // want "misuse of unbuffered os.Signal channel as argument to signal.Notify" f(c, os.Interrupt) sigchanyzer defaultJoin! ※リリースノート記載なし
  21. Copyright © 2023 ANDPAD Inc. New warnings for mismatched key-value

    pairs in log/slog calls log/slogで 引数のkeyが⽂字列でない場合や数が⼀致しない場合に警告するようになりました🎉 以下のような場合でBADKEY表記になってしまうパターンですね。 こちらは、 Go 1.21 にも出荷済みの内容のようです。 slog 24 log.Info("message", "key1", "value1", 2, 3, 4) slog update!
  22. Copyright © 2023 ANDPAD Inc. stringintconv 25 check for string(int)

    conversions 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 整数から⽂字列への型変換を警告します。 多くの場合、strconv.Itoa(x) をすべきかと思われます。 x := 41 s := string(x) // NG s := string(rune(x)) // ok stringintconv defaultJoin! ※リリースノート記載なし
  23. Copyright © 2023 ANDPAD Inc. report calls to (testing.T).Fatal from

    goroutines started by a test 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 test で記載した goroutine で t.Fatal() しているという警告ですね。 func TestSample(t *testing.T) { var wg sync.WaitGroup wg.Add(1) go func() { defer func() { t.Log("defer") wg.Done() }() t.Fatal("Fatal") t.Log("A") testinggoroutine 26 testinggoroutine defaultJoin! ※リリースノート記載なし
  24. Copyright © 2023 ANDPAD Inc. 残念ながら httptest などwrap されている goroutine

    の検知はできないようでした。 これに関してtenntennさんが以前に記事にされています。 https://zenn.dev/tenntenn/articles/cadd13074e6d61#%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3%E5%86%85%E3 %81%AEfatal%E3%82%92%E9%81%BF%E3%81%91%E3%82%8B httptest の中で t.Fatal してもダメなどを doc に記載してほしいとか、 Vetの要件を満たせないなら golangci-lint にほしいなど、 弊社のgopher会で少し盛り上がりました。 testinggoroutine 27
  25. Copyright © 2023 ANDPAD Inc. check for calls of (time.Time).Format

    or time.Parse with 2006-02-01 昔から存在はしていましたが、 default 実⾏リストに仲間⼊り。 time.Formatに指定できるのは 2006-01-02 ですよ、というものです。 2006-02-02 や 2006-01-01 は検出してくれないのが微妙な感あります。 t := time.Date(2024, 3, 18, 0, 0, 0, 0, time.Local) fmt.Println(t.Format("2006-01-02")) // ok fmt.Println(t.Format("2006-02-01")) // ng timeformat 28 timeformat defaultJoin! ※リリースノート記載なし
  26. Copyright © 2023 ANDPAD Inc. default に⼊るほどの内容か疑問でしたが、 Vetに⼊るということは⼀定数以上の対象が存在しているということですね。 Vet基準 •

    Correctness(正しさ) • Frequency(頻度) • Precision(精度) rsc⽒も驚きつつ認めていました。 https://github.com/golang/go/issues/48801#issuecomment-942544870 timeformat 29
  27. Copyright © 2023 ANDPAD Inc. timeformat 30 いやでもうちは⼤丈夫だよとたかを括っていたら 弊社privateのとあるrepoで time.ParseInLocation()

    にて 2006-02-01 していたのを発⾒。 time.ParseInLocation()は本件の対象外ですが、検知するキッカケになってよかったです。 視聴の皆様も、試しに組織で検索してみるといいかもしれません。
  28. Copyright © 2023 ANDPAD Inc. 32 Copyright © 2023 ANDPAD

    Inc. いま建築‧建設業界で “ものづくり” に携わる⽅の⼈⼿不⾜や ⻑時間労働が社会問題となっています。 今後これらの課題に対して、デジタルシフトによる⽣産性向上や、 就労者数の底上げを急ぐ必要があります。 本来、ものづくりに携わる⼈々は、誰かに幸せを届ける⼈たちです。 そんな⽅々がもっとクリエイティブに、もっと豊かに働けるよう、 私たちは熱い想いで⽇々現場に向き合っています。