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.2k
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.6k
認証の仕組みとclient-go credential plugin / authentication and client-go credential plugin
int128
7
7.2k
CLIでOAuth/OIDCを快適に利用する
int128
0
740
AppEngine × Spring Boot × Kotlin
int128
0
87
いつものJIRA設定
int128
1
160
Swaggerのテンプレートを魔改造した話 / Customize Swagger Templates
int128
1
4.7k
本番環境のリリースを自動化した話
int128
0
700
Swagger × Spring Cloud
int128
0
83
The Evolution of System Architecture
int128
0
160
Other Decks in Programming
See All in Programming
テストコード文化を0から作り、変化し続けた組織
kazatohiei
2
1.5k
create_tableをしただけなのに〜囚われのuuid編〜
daisukeshinoku
0
270
毎日13時間もかかるバッチ処理をたった3日で60%短縮するためにやったこと
sho_ssk_
1
180
良いユニットテストを書こう
mototakatsu
8
2.8k
Zoneless Testing
rainerhahnekamp
0
120
20年もののレガシープロダクトに 0からPHPStanを入れるまで / phpcon2024
hirobe1999
0
500
useSyncExternalStoreを使いまくる
ssssota
6
1.2k
CSC305 Lecture 26
javiergs
PRO
0
140
tidymodelsによるtidyな生存時間解析 / Japan.R2024
dropout009
1
790
PHPで作るWebSocketサーバー ~リアクティブなアプリケーションを知るために~ / WebSocket Server in PHP - To know reactive applications
seike460
PRO
2
520
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
3
500
コンテナをたくさん詰め込んだシステムとランタイムの変化
makihiro
1
140
Featured
See All Featured
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Designing on Purpose - Digital PM Summit 2013
jponch
116
7k
Designing for humans not robots
tammielis
250
25k
Six Lessons from altMBA
skipperchong
27
3.5k
Visualization
eitanlees
146
15k
Optimizing for Happiness
mojombo
376
70k
Building Adaptive Systems
keathley
38
2.3k
A Tale of Four Properties
chriscoyier
157
23k
Designing Experiences People Love
moore
138
23k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Making Projects Easy
brettharned
116
5.9k
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