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

# はじめに

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">今日はこれにきてます！<a href="https://t.co/37YE7MtfxD">https://t.co/37YE7MtfxD</a> #22+Okayama.go/Sendai.go <a href="https://t.co/p7xAv5GJiz">https://t.co/p7xAv5GJiz</a> <a href="https://twitter.com/hashtag/golangtokyo?src=hash&amp;ref_src=twsrc%5Etfw">#golangtokyo</a></p>&mdash; nari@エンタメ系エンジニア (@fukubaka0825) <a href="https://twitter.com/fukubaka0825/status/1109309913535123456?ref_src=twsrc%5Etfw">March 23, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


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

登壇者:
@tenntenn
資料:
[A Tour of Static Analysis - Google スライド](http://bit.ly/atourofgostaticanalysis)

[このページについて \| golang.tokyo コードラボ](https://golangtokyo.github.io/codelab/)

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

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

# 静的解析の流れ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/321850/0691a2a3-d6d7-b92e-ee1d-8fcbecfc87b8.png)

- 字句解析  
文字列をトークンへ!!
字句解析で、文字列の塊だったソースコードが予約語のfuncなのか、識別子（変数名や関数名など）なのか、数値リテラルなのかなどを区別することができるトークンの塊に変換される。
 
>字句解析についてはgo/parserパッケージ内でgo/scannerパッケージを用いて行われています。

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

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

# Gopherを探せ！ハンズオン
資料:
[Gopherを探せ！ハンズオン資料](https://golangtokyo.github.io/codelab/find-gophers/?index=codelab#0)

## 概要
```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
	})
}

```

 ```console
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\"`"
}
```

なようなので、

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

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

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

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

# その他有益な資料まとめ
[analysis.Analyzerを使っている今回のようなサンプルプログラム](https://github.com/gostaticanalysis?utf8=%E2%9C%93&q=Analyzer&type=&language=)

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">なぜ SSA が扱いやすいのかについてのお気持ち的なやつ。<a href="https://twitter.com/hashtag/golangtokyo?src=hash&amp;ref_src=twsrc%5Etfw">#golangtokyo</a> <a href="https://t.co/F7TjjOJ3Ot">https://t.co/F7TjjOJ3Ot</a></p>&mdash; チェシャ猫 (@y_taka_23) <a href="https://twitter.com/y_taka_23/status/1109328474844520448?ref_src=twsrc%5Etfw">March 23, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[Goの式の定義]
(https://golang.org/ref/spec#Expressions)

[静的解析のサンプルコード]
(https://github.com/gostaticanalysis)

[tenntennさんが作ったAnalyzerを使った静的解析CLIを作り始めるのに便利奴]
(https://github.com/gostaticanalysis/skeleton)

[knsh14さんによるanalysisパッケージの解説記事]
(https://gihyo.jp/dp/ebook/2019/978-4-7741-9727-2)

[Goの標準パッケージではじめる静的解析入門①準備編 · mom0tomo](https://mom0tomo.github.io/post/go_ast_parser_static_analysis/)

[Goにおける静的解析のモジュール化について]
(https://tech.mercari.com/entry/2018/12/16/150000)

[analysis pkgh設計者(“プログラミング言語Go”執筆者)によるtypesの説明]
(https://github.com/golang/example/tree/master/gotypes)

[motemenさんによるgo/astパッケージやgo/typesパッケージ]
(https://motemen.github.io/go-for-go-book/)

# 余談
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">こんなサイトあるんや、、、<br>自分でカスタマイズしたgopher君入りのCapとか買えるししゅごい<br>I just Gopherized myself! <a href="https://twitter.com/hashtag/gopherize?src=hash&amp;ref_src=twsrc%5Etfw">#gopherize</a> <a href="https://twitter.com/hashtag/golang?src=hash&amp;ref_src=twsrc%5Etfw">#golang</a> <a href="https://t.co/R9bq9gRPCR">https://t.co/R9bq9gRPCR</a></p>&mdash; nari@エンタメ系エンジニア (@fukubaka0825) <a href="https://twitter.com/fukubaka0825/status/1109321027618631680?ref_src=twsrc%5Etfw">March 23, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

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