Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Rewrite Go error handling using AST transformation
Search
Hidetake Iwata
December 04, 2019
Programming
1
1.3k
Rewrite Go error handling using AST transformation
golang.tokyo #28
2019.12.4
Hidetake Iwata
December 04, 2019
Tweet
Share
More Decks by Hidetake Iwata
See All by Hidetake Iwata
Cluster AutoscalerをTerraformとHelmfileでデプロイしてPrometheusでモニタリングする / Deploy the Cluster Autoscaler with Terraform and Helmfile, Monitor with Prometheus
int128
3
1.7k
認証の仕組みとclient-go credential plugin / authentication and client-go credential plugin
int128
7
7.3k
CLIでOAuth/OIDCを快適に利用する
int128
0
800
AppEngine × Spring Boot × Kotlin
int128
0
94
いつものJIRA設定
int128
1
170
Swaggerのテンプレートを魔改造した話 / Customize Swagger Templates
int128
1
4.7k
本番環境のリリースを自動化した話
int128
0
720
Swagger × Spring Cloud
int128
0
86
The Evolution of System Architecture
int128
0
160
Other Decks in Programming
See All in Programming
Amazon S3 TablesとAmazon S3 Metadataを触ってみた / 20250201-jawsug-tochigi-s3tables-s3metadata
kasacchiful
0
170
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
120
定理証明プラットフォーム lapisla.net
abap34
1
1.8k
GoとPHPのインターフェイスの違い
shimabox
2
190
2,500万ユーザーを支えるSREチームの6年間のスクラムのカイゼン
honmarkhunt
6
5.3k
第3回 Snowflake 中部ユーザ会- dbt × Snowflake ハンズオン
hoto17296
4
370
CI改善もDatadogとともに
taumu
0
120
Domain-Driven Transformation
hschwentner
2
1.9k
Conform を推す - Advocating for Conform
mizoguchicoji
3
690
PHPのバージョンアップ時にも役立ったAST
matsuo_atsushi
0
110
pylint custom ruleで始めるレビュー自動化
shogoujiie
0
120
一休.com のログイン体験を支える技術 〜Web Components x Vue.js 活用事例と最適化について〜
atsumim
0
490
Featured
See All Featured
The Power of CSS Pseudo Elements
geoffreycrofte
75
5.5k
Become a Pro
speakerdeck
PRO
26
5.1k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
366
25k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
Done Done
chrislema
182
16k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
4
410
BBQ
matthewcrist
87
9.5k
Testing 201, or: Great Expectations
jmmastey
42
7.2k
Intergalactic Javascript Robots from Outer Space
tanoku
270
27k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
7
630
The Cost Of JavaScript in 2023
addyosmani
47
7.3k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Transcript
Rewrite Go error handling using AST transformation golang.tokyo #28 Hidetake
Iwata (@int128)
岩田 英丈 / Hidetake Iwata 2 Software Engineer at NTT
DATA (Senior YAML/Terraform Engineer ) Open Source Developer at https://github.com/int128
Agenda 3 お話しすること • Goにおける抽象構文木の変換と操作 • アプリケーションのエラー処理を書き換える方法 お話ししないこと • Goにおけるエラー処理の詳細
Error handling in Go Goでエラー処理を行うには主に以下の方法がある • errorsパッケージを利用する(Go 1.13で拡張された) • golang.org/x/xerrors
パッケージを利用する • github.com/pkg/errors パッケージを利用する 他にもサードパーティのパッケージが公開されている (e.g. github.com/morikuni/failure) 4
以下のような目的で、アプリケーション内のエラー処理を別の方法に書き換えたいこ とがある • 特定の機能を使いたい(e.g. エラー型判定、スタックトレース) • 外部パッケージを使いたくない • ソースコードの可読性を改善したい Rewrite
the Go error handling 5
例:pkg/errors.Wrapf() を xerrors.Errorf() に 書き換える • import文を変更する • 関数名を変更する •
引数の順序を入れ替える • 引数のフォーマット文字列に : %w を付け加える 6 // pkg/errorsの場合 return nil, errors.Wrapf(err, "item id=%s not found", id) // xerrorsの場合 return nil, xerrors.Errorf("item id=%s not found: %w", id, err)
簡単な書き換えは正規表現による一括置換でできるが、 複雑な書き換えには以下の手法が広く利用されている 1. ソースコードを抽象構文木に変換する 2. 抽象構文木を操作する 3. 抽象構文木をソースコードに書き出す @tenntennさんのスライドが分かりやすいです https://www.slideshare.net/takuyaueda967/go-74970321
抽象構文木の操作による書き換え 7
golang.org/x/tools/go/packages パッケージ等を利用すると、ソースコードと抽象 構文木を相互に変換できる Goのソースコードと抽象構文木の相互変換 https://godoc.org/golang.org/x/tools/go/packages cfg := &packages.Config{/* … */}
pkgs, err := packages.Load(cfg, pkgNames...) // ソースコードを解析する if err != nil {/* … */} if packages.PrintErrors(pkgs) > 0 {/* … */} for _, pkg := range pkgs { for _, file := range pkg.Syntax { err := printer.Fprint(os.Stdout, pkg.Fset, file) // 抽象構文木を出力する } } 8
0 *packages.Package { 1 . ID: "_/hello-go-ast-transformation" 2 . Name:
"" 3 . PkgPath: "" 4 . CompiledGoFiles: []string (len = 1) { 5 . . 0: "/hello-go-ast-transformation/main.go" 6 . } 7 . ExportFile: "" 8 . Types: *types.Package {} 9 . Fset: *token.FileSet {} 10 . IllTyped: false 11 . Syntax: []*ast.File (len = 1) { 12 . . 0: *ast.File { 13 . . . Package: /hello-go-ast-transformation/main.go:1:1 14 . . . Name: *ast.Ident { 15 . . . . NamePos: /hello-go-ast-transformation/main.go:1:9 16 . . . . Name: "main" 17 . . . } 18 . . . Decls: []ast.Decl (len = 3) { 19 . . . . 0: *ast.GenDecl { 20 . . . . . TokPos: /hello-go-ast-transformation/main.go:3:1 21 . . . . . Tok: import 22 . . . . . Lparen: /hello-go-ast-transformation/main.go:3:8 23 . . . . . Specs: []ast.Spec (len = 5) { 24 . . . . . . 0: *ast.ImportSpec { 25 . . . . . . . Path: *ast.BasicLit { packages.Load() が返す抽象構文木の基本構造 パッケージ ファイル ファイル import宣言 関数宣言 文 文 https://github.com/int128/hello-go-ast-transformation 9
148 1: *ast.CallExpr { 149 . Fun: *ast.SelectorExpr { 150
. . X: *ast.Ident { 151 . . . NamePos: hello.go:12:14 152 . . . Name: "errors" 153 . . } 154 . . Sel: *ast.Ident { 155 . . . NamePos: hello.go:12:21 156 . . . Name: "Wrapf" 157 . . } 158 . } 159 . Lparen: hello.go:12:26 160 . Args: []ast.Expr (len = 3) { 161 . . 0: *ast.Ident { 162 . . . NamePos: hello.go:12:27 163 . . . Name: "err" 164 . . . Obj: *(obj @ 107) 165 . . } 166 . . 1: *ast.BasicLit { 167 . . . ValuePos: hello.go:12:32 168 . . . Kind: STRING 169 . . . Value: "\"item id=%s not found\"" 170 . . } 171 . . 2: *ast.Ident { 172 . . . NamePos: hello.go:12:56 173 . . . Name: "id" 174 . . . Obj: *(obj @ 33) 175 . . } 176 . } errors.Wrapf(err, "item id=%s not found", id) に対応する部分木 CallExpr Selector Expr Ident “errors” Ident “Wrapf” Ident “err” BasicLit “item...” Ident “id” []Expr 10
156 1: *ast.CallExpr { 157 . Fun: *ast.SelectorExpr { 158
. . X: *ast.Ident { 159 . . . NamePos: hello.go:13:14 160 . . . Name: "xerrors" 161 . . } 162 . . Sel: *ast.Ident { 163 . . . NamePos: hello.go:13:22 164 . . . Name: "Errorf" 165 . . } 166 . } 167 . Lparen: hello.go:13:28 168 . Args: []ast.Expr (len = 3) { 169 . . 0: *ast.BasicLit { 170 . . . ValuePos: hello.go:13:29 171 . . . Kind: STRING 172 . . . Value: "\"item id=%s not found: %w\"" 173 . . } 174 . . 1: *ast.Ident { 175 . . . NamePos: hello.go:13:57 176 . . . Name: "id" 177 . . . Obj: *(obj @ 41) 178 . . } 179 . . 2: *ast.Ident { 180 . . . NamePos: hello.go:13:61 181 . . . Name: "err" 182 . . . Obj: *(obj @ 115) 183 . . } 184 . } xerrors.Errorf("item id=%s not found: %w", id, err) に対応する部分木 CallExpr Selector Expr Ident “xerrors” Ident “Errorf” Ident “err” BasicLit “item...” Ident “id” []Expr 11
pkg/errors.Wrapf() を xerrors.Errorf() に書き換える = 抽象構文木を操作する CallExpr Selector Expr Ident
“errors” Ident “Wrapf” Ident “err” BasicLit “item...” Ident “id” []Expr CallExpr Selector Expr Ident “xerrors” Ident “Errorf” Ident “err” BasicLit “item...” Ident “id” []Expr pkg/errors.Wrapf() の部分木 xerrors.Errorf() の部分木 12
抽象構文木の探索 go/astパッケージの Inspect() や Walk() を利用すると、抽象構文木を深さ優先探索 できる ast.Inspect(file, func(node ast.Node)
bool { switch node := node.(type) { case *ast.CallExpr: // 関数呼び出しの場合 switch fun := node.Fun.(type) { case *ast.SelectorExpr: switch x := fun.X.(type) { case *ast.Ident: if x.Sel.Name == "errors" { // パッケージ名がerrorsの場合 x.Sel.Name = "xerrors" // パッケージ名を書き換える } 13 https://golang.org/pkg/go/ast/#Inspect
関数呼び出しの型解決 抽象構文木のノードには型情報が含まれないので、 そのままでは変数や関数呼び出しの実体を判断できない 例:ソースコードに書かれているerrorsの実体はどれでしょう? • 標準パッケージ • 別名インポートされたパッケージ? • パッケージスコープのerrors変数?
14 func HelloWorld() error { return errors.New("hello") }
go/typesによる型解決 15 go/typesパッケージの Info.ObjectOf() で識別子の型を解決できる ast.Inspect(file, func(node ast.Node) bool {
switch node := node.(type) { case *ast.CallExpr: // 関数呼び出しの場合 switch fun := node.Fun.(type) { case *ast.SelectorExpr: switch x := fun.X.(type) { case *ast.Ident: switch o := pkg.TypesInfo.ObjectOf(x).(type) { case *types.PkgName: // 関数呼び出しの左辺がパッケージの場合 pkgPath := o.Imported().Path() https://golang.org/pkg/go/types/#Info.ObjectOf
まとめ Goにおける抽象構文木の変換と操作を用いて、アプリケーションのエラー処理を書 き換える方法を紹介しました アプリケーションのエラー処理を書き換えるツールを作っているので、 よかったらフィードバックをお願いします!! ✨ https://github.com/int128/transerr 16
We are hiring! www.nttdata-careers.com 17