静的解析と学ぶ
型パラメタ(ジェネリクス)
mercari.go #18
2022/01/20(木)
資料URL:https://tenn.in/typeparamanalysis
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.
上田拓也
Go ビギナーズ
Go Conference
@tenntenn
Google Developer Expert (Go)
一般社団法人 Gophers Japan 代表理事
tenntenn Conference主催
Experts Team
オンラインでセッションを見るときは
書いたところ
Go1.18 リリースパーティ
こちらもどうぞ
静的解析についてはこちら
インターンシップで学ぶ
今日話すこと
型パラメタ
var printStr func([]string) = Print[string]
printStr([]string{"Hello, ", "playground\n"})
Print[T]
Print[string]
func([]string)
Print[int]
func([]int)
T => string
T => int
インスタンス化
func Print[T any](s []T) {
for _, v := range s {
fmt.Print(v)
}
}
静的解析
11
コーディング
010100101000101000
コンパイル
010100101000101000
デプロイ
リリース
テスト
QA
監視
静的解析
Goにおける静的解析のフェーズ
12
構文解析
型チェック
静的単一代入形式
ポインタ解析
静的解析に使えるパッケージ
13
ast | 抽象構文木(AST)を提供 |
build | パッケージに関する情報を集める |
constant | 定数に関する型を提供 |
doc | ドキュメントをASTから取り出す |
format | コードフォーマッタ機能を提供 |
importer | コンパイラに適したImporterを提供 |
parser | 構文解析 機能を提供 |
printer | AST 表示機能を提供 |
scanner | 字句解析 機能を提供 |
token | トークンに関する型を提供 |
types | 型チェックに関する機能を提供 |
analysis | 静的解析ツールをモジュール化するパッケージ |
ast | AST関連のユーティリティ |
callgraph | call graph関連 |
cfg | control flow graph関連 |
expect | 構造化されたコメントを処理する |
packages | Go Modulesを前提としたパッケージ情報の収集から構文解析、�型チェックまでを行うパッケージ |
pointer | ポインタ解析 |
ssa | Static Single Assignment (SSA) 関連 |
types | 型情報関連 |
goパッケージのサブパッケージ
golang.org/x/tools/goパッケージのサブパッケージ
字句解析- go/scanner,go/token
14
IDENT
ADD
INT
トークン
ソースコード:
v + 1
構文解析 - go/parser,go/ast
15
v + 1
IDENT
ADD
INT
ソースコード:
+
v
1
BinaryExpr
Ident
BasicLit
トークン:
抽象構文木(AST):
型チェック - go/types,go/constant
16
n := 100 + 200
m := n + 300
定数の評価
= 300
型の推論
-> int
識別子の解決
静的解析ツールを簡単に作る
var Analyzer = &analysis.Analyzer{
Name: "simple",
Doc: "simple is simple Analyzer",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer},
}
func run(pass *analysis.Pass) (interface{}, error) {
/* 解析処理 */
return nil, nil
}
依存するAnalyzer
抽象構文木や型情報を保持
skeleton
$ skeleton myanalyzer
myanalyzer
├── cmd
│ └── myanalyzer
│ └── main.go
├── myanalyzer.go
├── myanalyzer_test.go
└── testdata
└── src
└── a
└── a.go
goパッケージの何が変わるのか
$ grep "go/" `go1.18beta1 env GOROOT`/api/go1.18.txt pkg go/ast, method (*IndexListExpr) End() token.Pos pkg go/ast, method (*IndexListExpr) Pos() token.Pos pkg go/ast, type FuncType struct, TypeParams *FieldList pkg go/ast, type IndexListExpr struct pkg go/ast, type IndexListExpr struct, Indices []Expr pkg go/ast, type IndexListExpr struct, Lbrack token.Pos pkg go/ast, type IndexListExpr struct, Rbrack token.Pos pkg go/ast, type IndexListExpr struct, X Expr pkg go/ast, type TypeSpec struct, TypeParams *FieldList pkg go/constant, method (Kind) String() string (略) |
抽象構文木をダンプして確かめる
ast.Print(pass.Fset, pass.Files[0])
抽象構文木から型パラメタを得る
func Print[T any](s []T) {...} |
type Vector[T any] []T |
ノードから型情報の型パラメタを得る
型情報から型パラメタを取得
制約を取得する
型パラメタを持つ関数の呼び出し
型パラメタのインスタンス化
Print[T]
Print[string]
func([]string)
Print[int]
func([]int)
T => string
T => int
インスタンス化
func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) |
新しく導入されるトークン
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" . InterfaceElem = MethodElem | TypeElem . MethodElem = MethodName Signature . MethodName = identifier . TypeElem = TypeTerm { "|" TypeTerm } . TypeTerm = Type | UnderlyingType . UnderlyingType = "~" Type . |
制約のインタフェースを表すノード
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" . InterfaceElem = MethodElem | TypeElem . MethodElem = MethodName Signature . MethodName = identifier . TypeElem = TypeTerm { "|" TypeTerm } . TypeTerm = Type | UnderlyingType . UnderlyingType = "~" Type . |
まとめ