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

静的解析を使ったリファクタリングのススメ - tenntenn.go#1

静的解析を使ったリファクタリングのススメ - tenntenn.go#1

この資料はtenntenn.go#1にて発表を行った際に用いた資料です。

■ tenntenn.goとは
tenntenn.goはtenntennが主催し、そしてすべてのセッションがtenntennによる登壇のGoに関する勉強会です。5のつく日に開催します。

イベントページ:https://tenntenn.connpass.com/event/237376/
ハッシュタグ:#tenngo
動画:https://tenn.in/refactor-video
資料(Googleスライド):https://tenn.in/refactor

■ 内容
Goで書かれたソフトウェアのリファクタリングについて自作の静的解析ツールを活用しながら行う方法について解説します。リファクタリングをしたいけれど、どこから手をつけていいか分からない方は必見です!

■ 登壇者&主催者

・名前:tenntenn / 上田拓也
・HP:https://tenntenn.dev
・Twitter:https://twitter.com/tenntenn

メルカリ/メルペイ所属。バックエンドエンジニアとして日々Goを書いている。Google Developer Expert (Go)。一般社団法人Gophers Japan代表。Go Conference主催者。大学時代にGoに出会い、それ以来のめり込む。人類をGopherにしたいと考え、Goの普及に取り組んでいる。複数社でGoに関する技術アドバイザーをしている。マスコットのGopherの絵を描くのも好き。

■ Gopher道場 自習室

https://gopherdojo.org/studyroom/

Gopher道場とは、実践的なGoを体系的に学べる場です。
Gopher道場 自習室では、以下のようなコンテンツや学びの場を提供します。

・Gopher道場の講義を録画した動画(10時間以上分)
・Slackにおける受講者同士のコミュニティ
・Gopher道場卒業生による課題のレビュー(ボランティアでご協力頂いているのでベストエフォートです)

■ Meety(カジュアル面談)

・ソフトウェアエンジニアの地方移住ってどうなの?:https://meety.net/matches/jyZgDkEEwmMk
・メルカリグループにおけるGoの使いどころ:https://meety.net/matches/LbeVbIACxLqk
・地方からの技術コミュニティへの貢献:https://meety.net/matches/gVeMtImLkWJE

■ お仕事の依頼について

副業にて技術顧問やアドバイザーなどを行っています。過去の実績や問い合わせフォームは以下のURLからご確認ください。
https://tenntenn.dev/ja/job/

#golang #tenntenn #tenngo #Go言語

tenntenn - Takuya Ueda

February 05, 2022
Tweet

More Decks by tenntenn - Takuya Ueda

Other Decks in Programming

Transcript

  1. The Go gopher was designed by Renée French. The gopher

    stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license. 静的解析を使った リファクタリングのススメ 2022年02月05日(土) 資料:https://tenn.in/refactor
  2. 上田拓也 Go ビギナーズ
 Go Conference
 @tenntenn tenntenn.dev Google Developer Expert

    (Go) 一般社団法人 Gophers Japan 代表理事 Experts Team
  3. 静的解析とリファクタリング ▪ 静的解析によってリファクタリングをサポート • コードの書き換え ◦ プログラムの意味を変えずに安全に変更する ◦ 文字列置換では実現が難しい •

    静的解析ツールで見つける ◦ go/analysisパッケージを使ったツール ◦ golangci-lintなどのサードパーティ製ツール ◦ バグとは言えないが可読性が低いもの • ソースコードの定量的な情報を分析する ◦ 関数の長さ、循環複雑度
  4. goパッケージ go/ast 抽象構文木(AST)を提供 go/build パッケージに関する情報を集める go/constant 定数に関する型を提供 go/doc ドキュメントをASTから取り出す go/format

    コードフォーマッタ機能を提供 go/importer コンパイラに適したImporterを提供 go/parser 構文解析 機能を提供 go/printer AST 表示機能を提供 go/scanner 字句解析 機能を提供 go/token トークンに関する型を提供 go/types 型チェックに関する機能を提供 6
  5. x/tools/goパッケージ analysis 静的解析ツールをモジュール化するパッケージ ast AST関連のユーティリティ callgraph call graph関連 cfg control

    flow graph関連 expect 構造化されたコメントを処理する packages Go Modulesを前提としたパッケージ情報の収集から構文解析、 型チェックまでを行うパッケージ pointer ポインタ解析 ssa Static Single Assignment (SSA) 関連 types 型情報関連 7
  6. コードの修正 ▪ gofmtを使った修正 • 例:interface{} -> any ◦ gofmt -w

    -r 'interface{} -> any' ◦ https://go-review.googlesource.com/c/go/+/368254 • ただの文字列置換 ▪ 標準&準標準の静的解析ツールによる修正 • https://pkg.go.dev/golang.org/x/tools/cmd • go fix:言語仕様の変更などに追従(Go1.0以前に活躍) • eg:exampleベースのリファクタリングツール • gorename:名前を変更するツール • gomvpkg:パッケージを移動させるツール ▪ その他 • rsc/rf:謎のrsc作のリファクタリングツール
  7. go/analysisを使った修正 ▪ analysis.SuggestedFix構造体を使う • analysis.Diagnostic構造体のフィールドとして設定する • -fixオプションで提案を反映することができる ◦ singlecheckerとmulticheckerの場合のみ •

    goplsからも使用できる • 例:https://github.com/gostaticanalysis/nakedreturn 10 type SuggestedFix struct { Message string // 変更の概要 TextEdits []TextEdit }
  8. リファクタリングポイントを見つける ▪ 静的解析ツールで見つける • https://github.com/gostaticanalysis/nakedreturn • https://github.com/gostaticanalysis/dive • https://github.com/gostaticanalysis/zero •

    https://github.com/gostaticanalysis/noctor • https://github.com/gostaticanalysis/debugcode • https://github.com/gostaticanalysis/elseless • https://github.com/gostaticanalysis/innertypealias • https://github.com/gostaticanalysis/emptycase • https://github.com/gostaticanalysis/notparam • https://github.com/gostaticanalysis/unuseparam • https://github.com/gostaticanalysis/signature • https://github.com/gostaticanalysis/notest • https://github.com/gostaticanalysis/lion • https://github.com/gostaticanalysis/dupimport • https://github.com/gostaticanalysis/ctxfield • https://github.com/sharefull/refactortools
  9. 自作のAnalyzerコレクションを作る ▪ unitchecker.Main関数に複数のAnalyzerを設定する 12 package main import ( "github.com/gostaticanalysis/forcetypeassert" "github.com/gostaticanalysis/nofmt"

    "github.com/gostaticanalysis/notest" "github.com/gostaticanalysis/vetgen/analyzers" "golang.org/x/tools/go/analysis/unitchecker" ) func main() { unitchecker.Main(append( analyzers.Govet(), // go vetと同じもの nofmt.Analyzer, notest.Analyzer, forcetypeassert.Analyzer, )...) }
  10. knife(ナイフ) ▪ go listのように型情報を表示できるツール • https://github.com/gostaticanalysis/knife • アーミーナイフのようにマルチに使えるツール • 指定したパッケージ(Goファイル)に対し静的解析を行う

    • 型情報をテンプレートをもとに出力する ◦ コード生成にも利用ができる • 下の例ではfmtパッケージの関数を表示している ◦ grepコマンドでさらにPrintで始まる関数だけ絞っている $ knife -f "{{names .Funcs}}" fmt | grep "^Print" Print Printf Println 参考:https://engineering.mercari.com/blog/entry/mercari_codecast_3/ 14
  11. リファクタリングする関数を見つける ▪ gostaticanalysis/funcstat • https://github.com/gostaticanalysis/funcstat • 関数のサイズ、循環複雑度などを出力する $ funcstat encoding/json

    | head package,file,line,name,lines,bytes,params,returns,cyclomatic complexity encoding/json,decode.go,96,Unmarshal,87,3970,2,1,1 encoding/json,decode.go,132,Error,6,309,0,1,1 encoding/json,decode.go,149,Error,3,191,0,1,1 encoding/json,decode.go,159,Error,10,263,0,1,1 encoding/json,decode.go,170,unmarshal,15,320,1,1,1 encoding/json,decode.go,191,String,2,102,0,1,1 encoding/json,decode.go,194,Float64,4,132,0,2,1 encoding/json,decode.go,199,Int64,4,127,0,2,1 encoding/json,decode.go,222,readIndex,4,116,0,1,1
  12. 循環複雑度 ▪ プログラムの複雑度を測る指標 • 制御フローグラフ(Control Flow Graph)を用いる • どのくらい分岐や繰り返しがあるか分かる •

    E – N + 2P ◦ E:エッジの数 ◦ N:ノードの数(基本ブロックの数) ◦ Pは単一の関数の場合は1 func f2(x, y int) int { if x == 2 { if y == 2 { if x+y == 4 { return x + y } } } return 0 }
  13. 制御フローグラフ(CFG)の取得 ▪ x/go/analysis/passes/ctrlflowパッケージを使う • https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/ctrlflow • Analyzerを提供 • 内部ではx/go/cfgを使用している ▪

    x/go/analysis/passes/buildssaパッケージを使う • https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/buildssa • 静的単一代入(SSA)形式に変換時にCFGが手に入る • ssa.Function構造体がssa.BasicBlock構造体を保持