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

    View full-size slide

  2. 上田拓也
    Go ビギナーズ

    Go Conference

    @tenntenn
    tenntenn.dev
    Google Developer Expert (Go)
    一般社団法人 Gophers Japan 代表理事
    Experts Team

    View full-size slide

  3. リファクタリング
    ■ 外部から見たプログラムの挙動を変えずに改善する
    ● 可読性を上げる
    ● パフォーマンスを改善する
    ● 設計を見直す

    View full-size slide

  4. 静的解析ツール
    ■ プログラムを実行せずに解析するツール
    ● 公式ではgo vetがある
    コーディング
    010100
    101000
    101000
    コンパイル
    010100
    101000
    101000
    デプロイ リリース
    テスト QA 監視
    静的解析

    View full-size slide

  5. 静的解析とリファクタリング
    ■ 静的解析によってリファクタリングをサポート
    ● コードの書き換え
    ○ プログラムの意味を変えずに安全に変更する
    ○ 文字列置換では実現が難しい
    ● 静的解析ツールで見つける
    ○ go/analysisパッケージを使ったツール
    ○ golangci-lintなどのサードパーティ製ツール
    ○ バグとは言えないが可読性が低いもの
    ● ソースコードの定量的な情報を分析する
    ○ 関数の長さ、循環複雑度

    View full-size slide

  6. 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

    View full-size slide

  7. 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

    View full-size slide

  8. 静的解析のフェーズ
    ■ 静的解析はいくつかのフェーズに分かれている
    ● 後のフェーズにいくこと、さらに詳しい情報が手に入る
    ● 各フェーズで手に入る情報を使い分けながら解析する
    ● 各フェーズで手に入る情報の紐付けの仕方がキモ
    ○ このノードの型情報は?など
    8
    構文解析
    型チェック
    静的単一代入形式
    ポインタ解析

    View full-size slide

  9. コードの修正
    ■ 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作のリファクタリングツール

    View full-size slide

  10. go/analysisを使った修正
    ■ analysis.SuggestedFix構造体を使う
    ● analysis.Diagnostic構造体のフィールドとして設定する
    ● -fixオプションで提案を反映することができる
    ○ singlecheckerとmulticheckerの場合のみ
    ● goplsからも使用できる
    ● 例:https://github.com/gostaticanalysis/nakedreturn
    10
    type SuggestedFix struct {
    Message string // 変更の概要
    TextEdits []TextEdit
    }

    View full-size slide

  11. リファクタリングポイントを見つける
    ■ 静的解析ツールで見つける
    ● 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

    View full-size slide

  12. 自作の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,
    )...)
    }

    View full-size slide

  13. tennvetを使う
    ■ tenntennさんの秘伝のタレ
    ● https://github.com/tenntenn/tennvet

    View full-size slide

  14. 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

    View full-size slide

  15. リファクタリングする関数を見つける
    ■ 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

    View full-size slide

  16. 循環複雑度
    ■ プログラムの複雑度を測る指標
    ● 制御フローグラフ(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
    }

    View full-size slide

  17. 制御フローグラフ(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構造体を保持

    View full-size slide

  18. まとめ
    ■ 静的解析でリファクタリングをサポート
    ● 既存のコマンドでリファクタリング
    ○ 名前の変更、パッケージの変更
    ● リファクタリングポイントを見つける
    ○ 良くないコードを探す
    ○ 定量的な情報を出力する

    View full-size slide