Goで静的解析!

26 Mar 2019

Daigo Ikeda

Knightso, LLC

Profile

Daigo Ikeda
@hogedigo

Knightso, LLC
http://www.knightso.co.jp/
Shizuoka, JAPAN

2

静的解析とは

3

用途

4

用例

5

静的解析の流れ

6

goパッケージ

7

golang.org/x/tools/goパッケージ

(抜粋)
- analysis ... 静的解析モジュールとドライバ間のインターフェースを提供
- ast ... AST関連ユーティリティ
- packages ... パッケージ情報をロードする(Go Modulesに対応)
- callgraph ... call graph関連のアルゴリズム&ユーティリティ
- cfg ... control flow graph関連のアルゴリズム&ユーティリティ
- ssa ... Static Single Assignment関連アルゴリズム&ユーティリティ
- pointer ... ポインタ解析アルゴリズム&ユーティリティ
- pointer ... ポインタ解析アルゴリズム&ユーティリティ
- types ... 型情報関連ユーティリティ

8

やってみよう

package main

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

func main() {
    // 解析するソースコード
    const src = `
package main

import (
    "fmt"
)

func main() {
    greeting := "Hello, world"
    fmt.Println(greeting)
}`

    // 構文解析
    fs := token.NewFileSet()
    f, err := parser.ParseFile(fs, "my.go", src, 0)
    if err != nil {
        log.Fatal(err.Error())
    }

    // AST出力
    ast.Print(fs, f)

    // 型情報解析
    info := types.Info{
        Types: make(map[ast.Expr]types.TypeAndValue),
        Defs:  make(map[*ast.Ident]types.Object),
        Uses:  make(map[*ast.Ident]types.Object),
    }

    conf := &types.Config{
        Importer: importer.Default(),
    }
    _, err = conf.Check("fib", fs, []*ast.File{f}, &info)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("\n\nTypes")
    for k, v := range info.Types {
        fmt.Printf("%v : %v\n", k, v)
    }

    fmt.Println("\n\nDefs")
    for k, v := range info.Defs {
        fmt.Printf("%v : %v\n", k, v)
    }

    fmt.Println("\n\nUses")
    for k, v := range info.Uses {
        fmt.Printf("%v : %v\n", k, v)
    }

    // ASTをトラバースして型チェック
    fmt.Println("\n\n#### Inspect!!")
    ast.Inspect(f, func(n ast.Node) bool {

        // 識別子ではない場合は無視
        ident, ok := n.(*ast.Ident)
        if !ok {
            return true
        }

        // 識別子が定義または利用されている部分の情報を取得
        obj := info.Defs[ident]
        if obj == nil {
            obj = info.Uses[ident]
            if obj == nil {
                return true
            }
        }

        typ := obj.Type()

        fmt.Printf("%v %s %T %v\n", fs.Position(ident.Pos()), ident.Name, typ, typ)

        return true
    })

}
9

analysisパッケージ

静的解析モジュールとドライバ間のインターフェースを提供

10

analysisパッケージ

11

Analyzer

type Analyzer struct {
    Name             string
    Doc              string
    Flags            flag.FlagSet
    Run              func(*analysis.Pass) (interface{}, error)
    RunDespiteErrors bool
    Requires         []*analysis.Analyzer
    ResultType       reflect.Type
    FactTypes        []Fact
}
12

Pass

type Pass struct {
    Analyzer          *analysis.Analyzer
    Fset              *token.FileSet
    Files             []*ast.File
    OtherFiles        []string
    Pkg               *types.Package
    TypesInfo         *types.Info
    TypesSizes        types.Sizes
    Report            func(analysis.Diagnostic)
    ResultOf          map[*analysis.Analyzer]interface{}
    ImportObjectFact  func(obj types.Object, fact analysis.Fact) bool
    ImportPackageFact func(pkg *types.Package, fact analysis.Fact) bool
    ExportObjectFact  func(obj types.Object, fact analysis.Fact)
    ExportPackageFact func(fact analysis.Fact)
}

診断結果出力

func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{})
13

singlechecker

package main

import (
    "example.org/findbadness"
    "golang.org/x/tools/go/analysis/singlechecker"
)

func main() { singlechecker.Main(findbadness.Analyzer) }
14

multichecker

package main

import (
    "golang.org/x/tools/go/analysis/multichecker"

    // analysis plug-ins
    "golang.org/x/tools/go/analysis/passes/asmdecl"
    "golang.org/x/tools/go/analysis/passes/assign"
    "golang.org/x/tools/go/analysis/passes/atomic"
)

func main() {
    multichecker.Main(
        asmdecl.Analyzer,
        assign.Analyzer,
        atomic.Analyzer,
    )
}
15

unitchecker

package main

import (
    "golang.org/x/tools/go/analysis/unitchecker"

    "golang.org/x/tools/go/analysis/passes/asmdecl"
    "golang.org/x/tools/go/analysis/passes/assign"
    "golang.org/x/tools/go/analysis/passes/atomic"
)

func main() {
    unitchecker.Main(
        asmdecl.Analyzer,
        assign.Analyzer,
        atomic.Analyzer,
    )
}

呼び出し

go vet -vettool=$(which myvet) pkgname
16

Example

17

参考資料

18

まとめ

Thank you!

19

Thank you

26 Mar 2019

Daigo Ikeda

Knightso, LLC

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)