Help us understand the problem. What is going on with this article?

golang.tokyo #22+Okayama.go/Sendai.go 概要まとめ ~静的解析ハンズオン~

More than 1 year has passed since last update.

はじめに

golang.tokyo #22+Okayama.go/Sendai.go - connpass
このイベントに行ってまいりました。
内容としては、このイベントの概要となります。
今回のテーマは、静的解析でした。

登壇者:
@tenntenn
資料:
A Tour of Static Analysis - Google スライド

このページについて | golang.tokyo コードラボ

そもそも静的解析ってなんの役に立つ

  • go vetとかgolintみたいなツールを作るのに向いている
  • コンパイラじゃ見つけてくれないバグを探すのに使える
  • Goの文法に詳しくなれる

静的解析の流れ

image.png

  • 字句解析
    文字列をトークンへ!! 字句解析で、文字列の塊だったソースコードが予約語のfuncなのか、識別子(変数名や関数名など)なのか、数値リテラルなのかなどを区別することができるトークンの塊に変換される。

字句解析についてはgo/parserパッケージ内でgo/scannerパッケージを用いて行われています。

go/parserで自動でやられるので点線表記みたいですね

  • 構文解析
    トークンをASTへ!! 構文解析を行うと、どの部分が関数定義で、どの部分がその引数の定義なのか、などを抽象構文木から取得することができるようになる。
  • 型チェック
    最後に型チェックを行うことで、抽象構文木から型情報を抽出
    型チェックは次の3つの工程から成る。
    1.識別子の解決
    2.型の推論
    3.定数の評価
    この3つの工程を行い、型情報を抽出することで、どの変数(識別子)がどういうデータ型でどこで定義され、どこで使用されているかなどを知ることができる。

Gopherを探せ!ハンズオン

資料:
Gopherを探せ!ハンズオン資料

概要

go@_gopher.go
package main

import (
        "fmt"
)

type Gopher struct {
        Gopher string `json:"gopher"`
}

func main() {
        const gopher = "GOPHER"
        gogopher := GOPHER()
        gogopher.Gopher = gopher
        fmt.Println(gogopher)
}

func GOPHER() (gopher *Gopher) {
        gopher = &Gopher{Gopher: "gopher"}
        return
}

上記のファイルから、名前付き型かつGopherという名前の識別子だけを
サーチするにはどうしたらいいかが

  • grepコマンドの限界
  • 式の構造解析
  • ファイルの構造解析
  • 型チェック

という流れで学べる、すごくわかりやすいハンズオンになっていました。

ハンズオンを書き換えて、理解を深める

ここからは個人で勝手にやった内容となります。
リテラルの"gopher"文字列をサーチするように書き換えてみます。

go@main.go
package main

import (
    "fmt"
    "go/ast"
    "go/importer"
    "go/parser"
    "go/token"
    "go/types"
    "log"
    "strconv"
)

func main() {
    // ファイルごとのトークンの位置を記録するFileSetを作成する
    fset := token.NewFileSet()

    // ファイル単位で構文解析を行う
    f, err := parser.ParseFile(fset, "_gopher.go", nil, 0)
    if err != nil {
        log.Fatal("Error:", err)
    }

    // 識別子が定義または利用されてる部分を記録する
    defsOrUses := map[*ast.Ident]types.Object{}
    info := &types.Info{
        Defs: defsOrUses,
        Uses: defsOrUses,
    }

    // 型チェックを行うための設定
    config := &types.Config{
        Importer: importer.Default(),
    }

    // 型チェックを行う
    _, err = config.Check("main", fset, []*ast.File{f}, info)
    if err != nil {
        log.Fatal("Error:", err)
    }

    // 抽象構文木を深さ優先で探索する
    ast.Inspect(f, func(n ast.Node) bool {

        // リテラルではない場合は無視
        basic, ok := n.(*ast.BasicLit)
        if !ok {
            return true
        }

        basicValue,err := strconv.Unquote(basic.Value)
        if err !=nil{
            panic(err)
        }

        //リテラルが"gopher"という値でなければ無視
        if basicValue  != "gopher" && basicValue != `json:"gopher"` {
            return true
        }

        fmt.Println(fset.Position(basic.Pos()))

        return true
    })
}

takafk9@narikawakiyoshinoMacBook-Pro [16時39分00秒] [~/go/src/github.com/golangtokyo/codelab/find-gophers/3_typecheck] [master *]
-> % go run main.go
_gopher.go:8:16
_gopher.go:19:27

きちんとリテラルを捕まえてますね

structタグのastが

Tag: *ast.BasicLit {
 ValuePos: 70
    Kind: STRING
    Value: "`json:\"gopher\"`"
}

なようなので、

if basicValue  != "gopher" && basicValue != `json:"gopher"`

という規制で、タグ内の"gopher"も捕まえられました
(slackで回答していただいた方々、ありがとうございました!)

感想

静的解析についてかなりディープで難しい領域に思っていましたが、今回のハンズオンで
少しイメージが変わりました。
(この流れをモジュール化したAnalyzerまで手を出せなかったので、時間あるときに、そちらもやっていきたい)
golang.tokyoのイベントにお邪魔するのは初めてなのですが、Slackでも疑問に即レスで答えていただけて、非常に素晴らしいイベントでした。
ちょっとastにおこして解析するだけで、かなり文法の知識を深められたので、
今度は自作で静的解析ツールを作りたいと思います。

ありがとうございました!

その他有益な資料まとめ

analysis.Analyzerを使っている今回のようなサンプルプログラム

Goの式の定義

静的解析のサンプルコード

tenntennさんが作ったAnalyzerを使った静的解析CLIを作り始めるのに便利奴

knsh14さんによるanalysisパッケージの解説記事

Goの標準パッケージではじめる静的解析入門①準備編 · mom0tomo

Goにおける静的解析のモジュール化について

analysis pkgh設計者(“プログラミング言語Go”執筆者)によるtypesの説明

motemenさんによるgo/astパッケージやgo/typesパッケージ

余談

Gopherオタクになりたて人間には最高のサイト、、、

fukubaka0825
Site Reliability Engineer at eureka, Inc. devops/AWS/IaC/Terraform/コンテナ/サーバレス/Go/DDD
https://www.fukubaka0825.dev/
eure
オンラインデーティングサービス「Pairs」の運営・開発をしている企業。様々なモダンな技術を駆使してビジネスを成長させています。
https://eure.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away