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

about #74462 go/token#FileSet

Avatar for tom twinkle tom twinkle
September 30, 2025

about #74462 go/token#FileSet

Avatar for tom twinkle

tom twinkle

September 30, 2025
Tweet

More Decks by tom twinkle

Other Decks in Technology

Transcript

  1. © 2025 ANDPAD All Rights Reserved. about #74462 go/token#FileSet invalid

    array length -delta * delta (constant -256 of type int64) tomtwinkle techlead@andpad
  2. © 2025 ANDPAD All Rights Reserved. ANDPADとは 社内 現場 営業

    / 監督 / 設計 事務 / 管理職 職人 / 業者 メーカー / 流通 現場の効率化から経営改善まで一元管理できる クラウド型建設プロジェクト管理サービス 案件管理 資料 工程表 写真 報告 チャット 黒板 図面 受発注 • • • 
 

  3. © 2025 ANDPAD All Rights Reserved. Go を間接的に使っているプロダクト ANDPAD の

    Go のプロダクト Go がメインのプロダクト The Go gopher was designed by Renée French. 施工管理 図面 引合粗利管理 検査 黒板 受発注 ボード 資料承認 おうちノート … 歩掛管理 請求管理
  4. © 2025 ANDPAD All Rights Reserved. Goのソースコードを静的解析する際に、コードを構成する最小単位の要素 (トークン)を定義し、管理する役割を担います。 細かい話は時間足りないのでしません。 プログラミング言語Go完全入門

    by tenntenn https://tenn.in/analysis Goで作る静的解析ツール開発入門 by H.Saki https://zenn.dev/hsaki/books/golang-static-analysis 辺りを見ると分かりやすいと思います。 go/token
  5. © 2025 ANDPAD All Rights Reserved. https://go.dev/doc/go1.25#gotokenpkggotoken What changed go/token#FileSet

    in Go 1.25? 新しいFileSet.AddExistingFilesメソッドにより、既存のファイルをFileSetに追加したり、任 意のファイル群からFileSetを構築したりできるようになり、長寿命アプリケーションにおける 単一のグローバルFileSetに関連する問題を軽減します。
  6. © 2025 ANDPAD All Rights Reserved. http://go.dev/issue/53200 #53200 [意訳] Goのコード解析で使われる

    token.FileSetは、一度追加したファイル情報を解放し ない設計になっています。 そのためgoplsのような常駐ツールでは、ファイルを解析する度にメモリ使用量が 無限に増え続け、メモリリークを引き起こします。 token.FileSetは事実上ユビキタスな単一のグローバル変数となっています。 これ自体はGo1.20で入った
  7. © 2025 ANDPAD All Rights Reserved. Go 1.25以前の go/token#FileSet の構造

    type FileSet struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file files []*File // list of files in the order added to the set last atomic.Pointer[File] // cache of last file looked up } func (s *FileSet) AddFile(filename string, base, size int) *File { /* - - 以下抜粋 - - */ if base < s.base { panic(fmt.Sprintf("invalid base %d (should be >= %d)", …) } // base >= s.base && size >= 0 base += size + 1 // +1 because EOF also has a position // add the file to the file set s.base = base } 管理下にあるすべてのファイルに対し て、連続的で重複のない「アドレス空間」 を形成するというモデルを採用していま す。 FileSetに新たに追加されるファイルは、 そのbaseオフセットが、直前に追加され たファイルの範囲(base + size)よりも大 きい値でなければなりません。 ファイルは常にアドレス空間の末尾に追 加される、つまり追記専用の操作しか許 されません。 https://cs.opensource.google/go/go/+/refs/tags/go1.24.7:src/go/token/position.go;l=426
  8. © 2025 ANDPAD All Rights Reserved. Go 1.25以前の go/token#FileSet の構造

    type FileSet struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file files []*File // list of files in the order added to the set last atomic.Pointer[File] // cache of last file looked up } func (s *FileSet) AddFile(filename string, base, size int) *File { /* - - 以下抜粋 - - */ if base < s.base { panic(fmt.Sprintf("invalid base %d (should be >= %d)", …) } // base >= s.base && size >= 0 base += size + 1 // +1 because EOF also has a position // add the file to the file set s.base = base } 管理下にあるすべてのファイルに対し て、連続的で重複のない「アドレス空間」 を形成するというモデルを採用していま す。 FileSetに新たに追加されるファイルは、 そのbaseオフセットが、直前に追加され たファイルの範囲(base + size)よりも大 きい値でなければなりません。 ファイルは常にアドレス空間の末尾に追 加される、つまり追記専用の操作しか許 されません。 https://cs.opensource.google/go/go/+/refs/tags/go1.24.7:src/go/token/position.go;l=426 FileSet file_a.go (size=100) base=102 base+100+1 file_b.go (size=200) FileSet file_a.go (size=100) file_b.go (size=200) base=303 base+200+1 FileSet base=1 file_a.go (size=100)
  9. © 2025 ANDPAD All Rights Reserved. Go 1.25以前の go/token#FileSet の構造

    type File struct { base int // Pos value range for this file is [base...base+size] } func searchFiles(a []*File, x int) int { i, found := slices.BinarySearchFunc(a, x, func(a *File, x int) int { return cmp.Compare(a.base, x) }) if !found { i-- } return i } func (s *FileSet) file(p Pos) *File { /* - - 抜粋 - - */ if i := searchFiles(s.files, int(p)); i >= 0 { f := s.files[i] } } token.Posは、FileSet内で管理されるす べてのファイルを通じて一意な値を持つ ように設計されています。 例えば、token.Posからどの*Fileに属す るかを特定するFile(s Pos)は二分探索 で検索するためファイル数が nの場合、 探索の計算量は O(log n)となり、非常に 効率的。 FileSetにファイルを追加する際は単純 な末尾追記なので 計算量は O(1) で爆 速。 https://cs.opensource.google/go/go/+/refs/tags/ go1.24.7:src/go/token/position.go;l=539
  10. © 2025 ANDPAD All Rights Reserved. Go 1.25以前の go/token#FileSet の課題

    func main() { // 1. pkgA のコードを解析 fsetA := token.NewFileSet() filePathA := "pkgA/a.go" astA, _ := parser.ParseFile(fsetA, filePathA, readFile(filePathA), …) // 2. pkgB のコードが変更され、再解析 fsetB := token.NewFileSet() filePathB := "pkgB/b.go" astB, _ := parser.ParseFile(fsetB, filePathB, readFile(filePathB), …) // 3. 結果のマージを試みる //fsetA をマスターとし、astBのfsetBをそこに追加したい } コード生成ツールなど動的にソースコー ドが変更されるツールで変更差分だけ 解析してFileSetをマージしたいパターン pkgAを解析後にpkgBのコードが変更さ れたので再解析する例
  11. © 2025 ANDPAD All Rights Reserved. Go 1.25以前の go/token#FileSet の課題

    func main() { // 1. pkgA のコードを解析 fsetA := token.NewFileSet() filePathA := "pkgA/a.go" astA, _ := parser.ParseFile(fsetA, filePathA, readFile(filePathA), …) // 2. pkgB のコードが変更され、再解析 fsetB := token.NewFileSet() filePathB := "pkgB/b.go" astB, _ := parser.ParseFile(fsetB, filePathB, readFile(filePathB), …) // 3. 結果のマージを試みる //fsetA をマスターとし、astBのfsetBをそこに追加したい // しかし、直接マージするmethodはない // fsetA.Merge(fsetB) // <--- こういうのが欲しい } しかし、(簡単に)マージする事はできな い。 必然的に複数のFileSetを同時に扱うか 毎回新規にFileSetを構築する必要があ り多数ファイルを扱う場合はメモリ消費 量が尋常ではなくなる。 独立して解析されたソースコードの情報 を後から結合し、単一のコンテキストで 扱いたい場合にはFileSet同士のマージ が要件的に必須。
  12. © 2025 ANDPAD All Rights Reserved. Go 1.25以前の go/token#FileSet の課題

    • Go Language Server(gopls) ◦ エディターの裏で長期間起動し、ソースコード全体を監視 ◦ コード補完、コードジャンプ、エラー検出など • 大規模コードインクリメンタル(増分)解析を行う静的解析ツール • コード生成ツール
  13. © 2025 ANDPAD All Rights Reserved. cl469858 gopls v0.7.0 (add

    an LRU parse cache) gopls v0.7.0 (2023-03-04 07:59) parse済みファイルと変更のあったファイルの差分解析のためのLRU cacheが追加 https://go.dev/cl/469858
  14. © 2025 ANDPAD All Rights Reserved. cl476437 x/tools v0.8.0 (add

    AddExistingFiles helper) x/tools v0.8.0 (2023-03-16 00:30) internal/tokeninternal にFileSetのマージのための AddExistingFiles helperが追加 https://go.dev/cl/476437
  15. © 2025 ANDPAD All Rights Reserved. x/tools/internal/tokeninternal#AddExistingFiles!? https://cs.opensource.google/go/x/tools/+/refs/tags/v0.8.0:internal/tokeninternal/tokeninternal.go;l=66;bpv=1;bpt=0 func AddExistingFiles(fset

    *token.FileSet, files []*token.File) { type tokenFileSet struct { // This type remained essentially consistent from go1.16 to go1.21. mutex sync.RWMutex base int files []*token.File _ *token.File // changed to atomic.Pointer[token.File] in go1.19 } const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) var _ [-delta * delta]int type uP = unsafe.Pointer var ptr *tokenFileSet *(*uP)(uP(&ptr)) = uP(fset) ptr.mutex.Lock() defer ptr.mutex.Unlock() } FileSetハック界(?)に流星の如く現 れたAddExistingFiles これは先ほどやりたかった FileSet のマージの為のhelperです。
  16. © 2025 ANDPAD All Rights Reserved. x/tools/internal/tokeninternal#AddExistingFiles!? https://cs.opensource.google/go/x/tools/+/refs/tags/v0.8.0:internal/tokeninternal/tokeninternal.go;l=66;bpv=1;bpt=0 func AddExistingFiles(fset

    *token.FileSet, files []*token.File) { type tokenFileSet struct { // This type remained essentially consistent from go1.16 to go1.21. mutex sync.RWMutex base int files []*token.File _ *token.File // changed to atomic.Pointer[token.File] in go1.19 } const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) var _ [-delta * delta]int type uP = unsafe.Pointer var ptr *tokenFileSet *(*uP)(uP(&ptr)) = uP(fset) ptr.mutex.Lock() defer ptr.mutex.Unlock() … go.token#FileSetのクローンのオ レオレFileSet structを用意 unsafe.Pointer を利用して引数 のtoken.FileSetからオレオレ FileSetに強制的に型変換 これでカプセル化されていた FileSetの非公開フィールドに直接 アクセス可能にし、base, filesを 弄ってFileSetのマージを可能にし た黒魔術
  17. © 2025 ANDPAD All Rights Reserved. x/tools/internal/tokeninternal#AddExistingFiles!? https://cs.opensource.google/go/x/tools/+/refs/tags/v0.8.0:internal/tokeninternal/tokeninternal.go;l=66;bpv=1;bpt=0 type FileSet

    struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file files []*File // list of files in the order added to the set last atomic.Pointer[File] // cache of last file looked up } func AddExistingFiles(fset *token.FileSet, files []*token.File) { type tokenFileSet struct { // This type remained essentially consistent from go1.16 to go1.21. mutex sync.RWMutex base int files []*token.File _ *token.File // changed to atomic.Pointer[token.File] in go1.19 } type uP = unsafe.Pointer var ptr *tokenFileSet *(*uP)(uP(&ptr)) = uP(fset) } unsafe.Pointerを使った型変換 は、メモリ上のデータ配置(レイア ウト)が同一であるという強い仮定 に基づいている。 つまり、元のFileSetの定義が変 更されれば壊れる。 やってんな!
  18. © 2025 ANDPAD All Rights Reserved. 黒魔術に対する防衛術 https://cs.opensource.google/go/x/tools/+/refs/tags/v0.8.0:internal/tokeninternal/tokeninternal.go;l=66;bpv=1;bpt=0 func AddExistingFiles(fset

    *token.FileSet, files []*token.File) { type tokenFileSet struct { // This type remained essentially consistent from go1.16 to go1.21. mutex sync.RWMutex base int files []*token.File _ *token.File // changed to atomic.Pointer[token.File] in go1.19 } // If the size of token.FileSet changes, this will fail to compile. const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) var _ [-delta * delta]int }
  19. © 2025 ANDPAD All Rights Reserved. 黒魔術に対する防衛術 https://pkg.go.dev/unsafe#Sizeof // このコード、何をしているかわかりますか?

    const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) var _ [-delta * delta]int
  20. © 2025 ANDPAD All Rights Reserved. 黒魔術に対する防衛術 https://go.dev/play/p/z61PLs9JXmO func main()

    { type Foo struct { F1 int32 } type Bar struct { F1 int64 } fmt.Printf("Hoge = %d\n", int64(unsafe.Sizeof(Foo{}))) // Foo = 4 fmt.Printf("Fuga = %d\n", int64(unsafe.Sizeof(Bar{}))) // Bar = 8 delta := int64(unsafe.Sizeof(Foo{})) - int64(unsafe.Sizeof(Bar{})) fmt.Printf("Foo-Bar = %d\n", delta) // Foo-Bar = -4 delta = int64(unsafe.Sizeof(Bar{})) - int64(unsafe.Sizeof(Foo{})) fmt.Printf("Bar-Foo = %d\n", delta) // Bar-Foo = 4 } 定義が異なる2つのstructを unsafe.SizeOfで比較した例 定義が異なる場合、パディン グで調整しない限りdeltaは0 以外の数字になる
  21. © 2025 ANDPAD All Rights Reserved. 黒魔術に対する防衛術 https://go.dev/play/p/pqVUNIv2jiE func main()

    { type Foo struct { F1 int32 } type Bar struct { F1 int64 } delta := int64(unsafe.Sizeof(Foo{})) - int64(unsafe.Sizeof(Bar{})) // delta = -4 fmt.Printf("-delta * delta = %d\n", -delta*delta) // -delta * delta = -16 delta = int64(unsafe.Sizeof(Bar{})) - int64(unsafe.Sizeof(Foo{})) // delta = 4 fmt.Printf("-delta * delta = %d\n", -delta*delta) // -delta * delta = -16 } deltaが0以外の場合 -delta * deltaの計算結果は必 ず負数になる
  22. © 2025 ANDPAD All Rights Reserved. 黒魔術に対する防衛術 https://go.dev/ref/spec#Array_types func main()

    { var _ [0]int // コンパイルが通る } func main() { var _ [-1]int // invalid array length -1 (untyped int constant) } Goの言語仕様 Array typeの要素の数は決し て負数にならない https://go.dev/ref/spec#Arr ay_types length=0 コンパイルが通る length=-1 コンパイルエラー
  23. © 2025 ANDPAD All Rights Reserved. 黒魔術(略)防御術〜コンパイルアサーション〜 https://cs.opensource.google/go/x/tools/+/refs/tags/v0.8.0:internal/tokeninternal/tokeninternal.go;l=66;bpv=1;bpt=0 func AddExistingFiles(fset

    *token.FileSet, files []*token.File) { type tokenFileSet struct { // This type remained essentially consistent from go1.16 to go1.21. mutex sync.RWMutex base int files []*token.File _ *token.File // changed to atomic.Pointer[token.File] in go1.19 } // If the size of token.FileSet changes, this will fail to compile. const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) var _ [-delta * delta]int } 元のFileSetが変更された場合を 考慮し、unsafe.Pointerでの型変 換の暴発を防ぐためにコンパイル アサーションの仕組みを導入。 正常: 自作tokenFileSet とtoken.FileSet のサイズが同じなら、 delta は 0 になる。[0]int という配列 の宣言は有効なので、問題なくコンパイルが通る 異常: token.FileSet の構造が変わってサイズが変化すると、 delta は 0 以外になる。 -delta * delta は負の数になる。マイナスサイズの配列は作れないため、ここでコンパイルエラーが発 生
  24. © 2025 ANDPAD All Rights Reserved. https://go.dev/issue/73205 #73205 [意訳] goplsでは断片的に追加される

    FileSetの再構築が必須だ。 今まで「backdoor (unsafe) version」のx/tools版AddExistingFiles で頑張ってきたけど流石にそろそろどうにかしようぜ。
  25. © 2025 ANDPAD All Rights Reserved. https://go.dev/doc/go1.25#gotokenpkggotoken go/token#FileSet.AddExistingFiles 爆誕 新しいFileSet.AddExistingFilesメソッドにより、既存のファイルをFileSetに追加したり、任

    意のファイル群からFileSetを構築したりできるようになり、長寿命アプリケーションにおける 単一のグローバルFileSetに関連する問題を軽減します。 要するに先ほどの goplsの問題
  26. © 2025 ANDPAD All Rights Reserved. Go 1.25 の go/token#FileSet.AddExistingFiles実装

    https://cs.opensource.google/go/go/+/refs/tags/go1.25.0:src/go/token/position.go;l=404 type FileSet struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file tree tree // tree of files in ascending base order last atomic.Pointer[File] // cache of last file looked up } func (s *FileSet) AddExistingFiles(files ...*File) { s.mutex.Lock() defer s.mutex.Unlock() for _, f := range files { s.tree.add(f) s.base = max(s.base, f.Base()+f.Size()+1) } } []*token.Fileが、より効率的に データを扱えるTreeに代わり #73205 の要望にある AddExistingFilesも追加。 パフォーマンスも向上して unsafe なx/toolsを使わなくて良くなっ た! 万々歳!
  27. © 2025 ANDPAD All Rights Reserved. Go 1.25 の go/token#FileSet.AddExistingFiles実装

    https://cs.opensource.google/go/go/+/refs/tags/go1.24.7:src/go/token/position.go;l=426 https://cs.opensource.google/go/go/+/refs/tags/go1.25.0:src/go/token/position.go;l=404 // Go 1.24 までのFileSet type FileSet struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file files []*File // list of files in the order added to the set last atomic.Pointer[File] // cache of last file looked up } // Go 1.25 のFileSet type FileSet struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file tree tree // tree of files in ascending base order last atomic.Pointer[File] // cache of last file looked up } []*token.Fileが、より効率的に データを扱えるTreeに代わり #73205 の要望にある AddExistingFilesも追加。 パフォーマンスも向上して unsafe なx/toolsを使わなくて良くなっ た! 万々歳!
  28. © 2025 ANDPAD All Rights Reserved. Go 1.25.0 環境で発生する google/wire

    の謎のエラー 0.068 go install github.com/google/wire/cmd/wire@latest 0.208 go: downloading github.com/google/wire v0.6.0 0.645 go: downloading github.com/google/subcommands v1.2.0 0.647 go: downloading github.com/pmezard/go-difflib v1.0.0 0.653 go: downloading golang.org/x/tools v0.17.0 0.974 go: downloading golang.org/x/mod v0.14.0 13.07 # golang.org/x/tools/internal/tokeninternal 13.07 /go/pkg/mod/golang.org/x/[email protected]/internal/tokeninternal/tokeninternal.go:78:9: invalid array length -delta * delta (constant -256 of type int64) 13.67 make: *** [Makefile:73: get-google-wire] Error 1
  29. © 2025 ANDPAD All Rights Reserved. Go 1.25.0 環境で発生する google/wire

    の謎のエラー 0.068 go install github.com/google/wire/cmd/wire@latest 0.208 go: downloading github.com/google/wire v0.6.0 0.645 go: downloading github.com/google/subcommands v1.2.0 0.647 go: downloading github.com/pmezard/go-difflib v1.0.0 0.653 go: downloading golang.org/x/tools v0.17.0 0.974 go: downloading golang.org/x/mod v0.14.0 13.07 # golang.org/x/tools/internal/tokeninternal 13.07 /go/pkg/mod/golang.org/x/[email protected]/internal/tokeninternal/tokeninternal.go:78:9: invalid array length -delta * delta (constant -256 of type int64) 13.67 make: *** [Makefile:73: get-google-wire] Error 1
  30. © 2025 ANDPAD All Rights Reserved. 黒魔術(略)防御術〜コンパイルアサーション〜 https://cs.opensource.google/go/x/tools/+/refs/tags/v0.8.0:internal/tokeninternal/tokeninternal.go;l=66;bpv=1;bpt=0 func AddExistingFiles(fset

    *token.FileSet, files []*token.File) { type tokenFileSet struct { // This type remained essentially consistent from go1.16 to go1.21. mutex sync.RWMutex base int files []*token.File _ *token.File // changed to atomic.Pointer[token.File] in go1.19 } // If the size of token.FileSet changes, this will fail to compile. const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) var _ [-delta * delta]int } 元のFileSetが変更された場合を 考慮し、unsafe.Pointerでの型変 換の暴発を防ぐためにコンパイル アサーションの仕組み 正常: 自作tokenFileSet とtoken.FileSet のサイズが同じなら、 delta は 0 になる。[0]int という配列 の宣言は有効なので、問題なくコンパイルが通る 異常: token.FileSet の構造が変わってサイズが変化すると、delta は 0 以外になる。 -delta * delta は負の数になる。マイナスサイズの配列は作れないため、ここでコンパイルエラーが 発生
  31. © 2025 ANDPAD All Rights Reserved. #74462 今回の主題 https://go.dev/issue/74462 issueの中身自体は

    x/tools AddExistingFiles のコンパイルアサーションと その対策の話
  32. © 2025 ANDPAD All Rights Reserved. #74462 コンパイルアサーション暴発の原因 https://go.dev/issue/74462#issuecomment-3192864978 [意訳]

    x/toolsに、goplsパフォーマンス対策用のコード (AddExistingFiles)が含まれたtokeninternal パッケージがpublicな gcimporter パッケージによって使用され意図せず公開されてしまうバグ がありました。 このバグは既に修正済みですが、その影響で、バグがあった古いバージョンに依存しているプロ ジェクトで問題が発生しています。 今回の問題は我々のミスです。ご迷惑をおかけし、大変申し訳ございません。
  33. © 2025 ANDPAD All Rights Reserved. cl464301 x/tools v0.6.0 https://go.dev/cl/464301

    tokeninternalにAddExistingFiles helperが追加されたのは x/tools v0.8.0 なのでこの時点ではまだコンパイルアサーションは存在しない
  34. © 2025 ANDPAD All Rights Reserved. cl464301 x/tools v0.6.0 https://go.dev/cl/464301

    グローバルなFileSetへの依存を減らすために token.Fileの情報だけ使ってコード整形する FormatNodeWithFileを gopls/internal/lsp/source/util.goに追加したよ。 その際にinternal/gcimporterパッケージの中に あったGetLinesヘルパー関数を使いたいから tokeninternalという共通パッケージに移動させまし た。
  35. © 2025 ANDPAD All Rights Reserved. cl476437 x/tools v0.8.0 (add

    AddExistingFiles helper) x/tools v0.8.0 (2023-03-16 00:30) internal/tokeninternal にFileSetのマージのための AddExistingFiles helperが追加 https://go.dev/cl/476437
  36. © 2025 ANDPAD All Rights Reserved. Go1.25.0 での x/tools 影響バージョンまとめ

    ❌コンパイルアサーションの影響あり • x/tools v0.8.0〜v0.24.0 • x/tools v0.25.0 ⭕コンパイルアサーションの(おそらく)影響なし • x/tools v0.24.1 • x/tools v0.25.1 • x/tools v0.26.0 以降 今回の件でbackport対応されたバージョン 今回の件でbackport対応されたバージョン
  37. © 2025 ANDPAD All Rights Reserved. Will Go 1.25.x support?

    https://go.dev/issue/74462#issuecomment-3197774637
  38. © 2025 ANDPAD All Rights Reserved. 影響のあったライブラリ(の一部) • google/wire https://github.com/google/wire/issues/431

    • go-swagger/go-swagger https://github.com/go-swagger/go-swagger/issues/3220 • uber-go/mock https://github.com/uber-go/mock/issues/274 • go-gorm/gen https://github.com/go-gorm/gen/issues/1366 • gotestyourself/gotestsum https://github.com/gotestyourself/gotestsum/issues/504 • terraform-docs/terraform-docs https://github.com/terraform-docs/terraform-docs/issues/870
  39. © 2025 ANDPAD All Rights Reserved. まとめ & 感想 •

    x/tools使ったライブラリ公開している人は要注意 • unsafe packageを利用したライブラリをpublicに触れるAPI の形で提供する時は十分に検討してからにしよう • internal な util が本当にinternalなのかよく確認しよう • 優秀なGoogleのGoチームでさえたまには間違える、ミスは あるものと思って前向きに生きよう
  40. © 2025 ANDPAD All Rights Reserved. 復習記事 & 宣伝 「†黒魔術†に対する防衛術」を詳しく知りたい人向け記事

    “Goの†黒魔術†に対する防衛術 ~Defence Against the Go's dark Arts~” https://tech.andpad.co.jp/entry/2025/09/23/100000 「go:linknameディレクティブ」を詳しく知りたい人向け記事 “Go界隈で巻き起こった go:linkname 騒動について ” https://tech.andpad.co.jp/entry/2024/06/20/140000