Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Rewrite Go error handling using AST transformation
Search
Hidetake Iwata
December 04, 2019
Programming
1.4k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Rewrite Go error handling using AST transformation
golang.tokyo #28
2019.12.4
Hidetake Iwata
December 04, 2019
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.9k
認証の仕組みとclient-go credential plugin / authentication and client-go credential plugin
int128
7
8k
CLIでOAuth/OIDCを快適に利用する
int128
0
1k
AppEngine × Spring Boot × Kotlin
int128
0
170
いつものJIRA設定
int128
1
250
Swaggerのテンプレートを魔改造した話 / Customize Swagger Templates
int128
1
5k
本番環境のリリースを自動化した話
int128
0
860
Swagger × Spring Cloud
int128
0
150
The Evolution of System Architecture
int128
0
250
Other Decks in Programming
See All in Programming
HTML-in-Canvasで作る未来のWeb UI表現
clockmaker
5
5.3k
APNsからLive Activityを開始する話
yumnumm
0
170
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
160
Simple Storage Service(S3) is not simple
iwatsukayura
0
310
Groovy as a human and AI friendly programming language
paulk
0
100
sbt 2.0: takeoff
eed3si9n
0
170
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
320
No More NoSuchMethodError
exoego
1
110
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
2
900
気持ちぃ〜角丸 Squircle
ryu1013
0
330
[2026-10-14](C)Re:ゼロから始める異世界生活〜始まりの終わりと終わりの始まり〜
tosite
0
150
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
230
Featured
See All Featured
The untapped power of vector embeddings
frankvandijk
2
1.9k
The Curious Case for Waylosing
cassininazir
1
570
The Invisible Side of Design
smashingmag
301
52k
We Are The Robots
honzajavorek
0
400
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
790
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.2k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
4
1.2k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
Building Applications with DynamoDB
mza
96
7.2k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
How to train your dragon (web standard)
notwaldorf
96
6.9k
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
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