Slide 1

Slide 1 text

Copyright © 2023 ANDPAD Inc. Copyright © 2023 ANDPAD Inc. ANDPADについて 1 Copyright © 2023 ANDPAD Inc. Go 1.22 の Vet 変更点について bushiyama

Slide 2

Slide 2 text

Copyright © 2023 ANDPAD Inc. 会社‧⾃⼰紹介 2 株式会社アンドパッドのbushiyamaです。 業務では、ANDPADボードという差配管理アプリのバックエンドを担当しています。

Slide 3

Slide 3 text

Copyright © 2023 ANDPAD Inc. 会社‧⾃⼰紹介 3 建築系のバーティカルSaaSということで(?)、現場猫がとても好きです。 アイコンにしている愛猫も、どことなく似ています。

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Copyright © 2023 ANDPAD Inc. 弊チームで⽣きるVet 5 まずおさらいからということで Vetとは https://pkg.go.dev/cmd/vet > Go ソース コードを検査し、不審な構造を報告します。 > すべてのレポートが真の問題であることを保証するものではありませんが、コンパイラーに よって捕捉されなかったエラーを⾒つけることはできます。

Slide 6

Slide 6 text

Copyright © 2023 ANDPAD Inc. 弊チームで⽣きるVet VSCode勢には、初期設定で⾃動実⾏してくれる⾝近な存在ですが、 弊チームでは、 CI でもあらためて golangci-lint から実⾏しています。 golangci.yml ... linters: enable: ... - govet ... ブラウザからの杜撰なコミットを防⽌してくれたりなど活躍しています。 ちなみに、golangci-lint での 1.22 対応は、v1.56.1 で対応済みのようです。

Slide 7

Slide 7 text

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! ※リリースノート記載なし

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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() }

Slide 11

Slide 11 text

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!

Slide 12

Slide 12 text

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 の組み込み関数で扱いが特別なためのよ うです。

Slide 13

Slide 13 text

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! ※リリースノート記載なし

Slide 14

Slide 14 text

Copyright © 2023 ANDPAD Inc. buildtag 14 関連で調べていたところ、 以下のようなスペース混⼊を検知する実装が新たに⼊りそうでした。 これは地味に助かりそうです。 // go:embed <= NG //go:embed <= OK https://github.com/golang/go/issues/43698

Slide 15

Slide 15 text

Copyright © 2023 ANDPAD Inc. New warnings for deferring time.Since defer で time.Since() をしてしまっているものを警告するようになりました🎉 defers 15 defers new!

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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() の利⽤だけしか⾒ません。 名前負けの感が、少し残念です。

Slide 19

Slide 19 text

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! ※リリースノート記載なし

Slide 20

Slide 20 text

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! ※リリースノート記載なし

Slide 21

Slide 21 text

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! ※リリースノート記載なし

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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! ※リリースノート記載なし

Slide 24

Slide 24 text

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!

Slide 25

Slide 25 text

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! ※リリースノート記載なし

Slide 26

Slide 26 text

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! ※リリースノート記載なし

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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! ※リリースノート記載なし

Slide 29

Slide 29 text

Copyright © 2023 ANDPAD Inc. default に⼊るほどの内容か疑問でしたが、 Vetに⼊るということは⼀定数以上の対象が存在しているということですね。 Vet基準 ● Correctness(正しさ) ● Frequency(頻度) ● Precision(精度) rsc⽒も驚きつつ認めていました。 https://github.com/golang/go/issues/48801#issuecomment-942544870 timeformat 29

Slide 30

Slide 30 text

Copyright © 2023 ANDPAD Inc. timeformat 30 いやでもうちは⼤丈夫だよとたかを括っていたら 弊社privateのとあるrepoで time.ParseInLocation() にて 2006-02-01 していたのを発⾒。 time.ParseInLocation()は本件の対象外ですが、検知するキッカケになってよかったです。 視聴の皆様も、試しに組織で検索してみるといいかもしれません。

Slide 31

Slide 31 text

Copyright © 2023 ANDPAD Inc. Vetとうまく付き合って、不幸なバグを回避していきたいですね。 ご視聴、ありがとうございました! さいごに 31

Slide 32

Slide 32 text

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