Open18

nilawayの実装調査

mazreanmazrean

とりあえずREADMEを確認した

以下あたりがポイントっぽそう

  • 完全自動
    →アノテーション必要なし
  • 速い
  • 関数またいだnil panicは少なくとも一部は検出可能
    • packageまたいでも問題なし
mazreanmazrean

発生した疑問点

  • 偽陽性は発生するのか?
    • ルールベースの静的解析では常に偽陽性、偽陰性のトレードオフを考える必要がある
      • 偽陽性(nil panicしないものへの警告)が多いとうるさくて使い物にならない
      • 偽陰性(nil panicするものへ警告がない)と入れる意味が少ない
    • 「実践的」の部分から以下は読み取れる
      • 全てのnil panicを検出するわけではない
        →偽陰性は発生する
      • 偽陽性については言及なし
        →偽陽性が発生しうるのか不明
  • 関数をまたいだ場合、どこまで検出できるのか?
    • 経験上、interfaceを通した場合や関数の変数への代入を挟んだ場合に検出が難しい
      • interfaceを通した場合は特に、検出できないと結構偽陰性が増えてしまい検出率が落ちる
    • ポインタ解析を使うとこれらも解決可能だが使えない
      • ポインタ解析はinterfaceなどのポインタにどのような値が入るか解析する解析
      • 計算コストが非常に高いため、「高速」をうたう以上おそらく使っていない
    • 頑張って静的解析で対応しているなら、どのように対応しているか見れるとかなり面白いと思われる
mazreanmazrean

wikiがあるので見ていく
https://github.com/uber-go/nilaway/wiki

mazreanmazrean

Architectureは実装周りの話。

大まかな仕組みは

  • 各Analyserでtrigger?を作成
    • triggerの補助情報を作成するAnalyzerもあるらしい
  • Accumulation Analyzerがtriggerを基にinference algorithmを実行

仕組み面で気になるポイントは以下。

  • Accumulation Analyzer
    • inference algorithmの中身(おそらく最重要)
  • Anonymous Function Analyzer
    • 関数リテラル(=無名関数)の情報を収集
    • 関数の変数代入に対応している期待が高くなる
  • Affiliation Analyzer
    • interface-struct affiliationのtrigger作成
    • implementではなくaffiliation?
      • implementのことなら型がinterfaceを実装しているかの判定になるので、すごい
      • structのフィールドにinterfaceがあるか、みたいな話っぽくも聞こえる
        • それはそれで使いどころが思いつかないので気になる

実装方法的に以下が面白そう

  • エラー生成ロジックとエラー報告ロジックを切り離している
    • Accumulation Analyzerは直でanalysis.Pass.Reportを使わない
    • 最上位のNilAway Analyzerがanalysis.Pass.Reportでエラー(おそらく警告のこと)の報告をする
    • これによって、必要ならエラーをファイルに書き出すとかが楽にできるらしい
      • これをしたい場合があまり思いついていないけど…
    • analysis.Pass.Reportで雑に警告出せるのはanalysisパッケージのメリットだと思っていたので、ここを捨てるのが吉と出るか凶と出るか気になる
  • Config Analyzer
    • flagからの設定の読み取りを専用analyzerに任せているの、なかなか見ない印象
  • Analyzerを分割の単位として使っている
    • 規模感的にGoの関数やパッケージで構成要素を切り分けても良い気がするが、Analyzerでコンポーネントを切り分けている
    • これのメリット・デメリットは気になる
mazreanmazrean

Configurationの方は、そこまで特筆するべきポイントはなさそう

mazreanmazrean

書き忘れていたけど、説明がないAnnotation Analyzerがある当たり、必要になったら最終手段としてアノテーションで情報を追加する機能をつけることとかは考えているのかも?
これを考えると、Annotation Analyzerの役割も気になるポイント。

mazreanmazrean

いよいよ実装見ていく

とりあえず、Analyzerとディレクトリの対応関係は以下になっていそう。

Analyzer ディレクトリ
NilAway /
Accumulation /accumulation
Annotation /annotation
Config /config
その他 /assertion/*

ディレクトリ構造からもAccumulationは特別なanalyserで、そのほかのAnalyzerはそのための情報収集をしていることが読み取れる。

また、wikiにはなかったが、情報収集系AnalyzerをまとめるAssertion Analyzerが存在しているっぽい。
このAssertion AnalyzerからAnonymous Function AnalyzerとStruct Field Analyzerが参照されておらず、他からも参照されている気配がなかったので、どうやらこの2つは今のところ使われいないと思われる。

mazreanmazrean

Accumulation Analyzerは/diagnostic/inferenceを利用してinference algorismを行っているよう。

mazreanmazrean

Anonymous Function AnalyzerとStruct Field Analyzerが参照されておらず

大嘘で、ばっちりFunction Analyzerから参照されていた

mazreanmazrean

/assertionの下の各Analyzerから見ていく。
まずは、Affiliation Analyzerから読む。

mazreanmazrean

all affiliations (e.g., interface and its implementing struct)

実装関係とかのことをaffiliationと呼んでいたみたい。
Implementではなかったのは他の関係も含んでいるからと考えるのがよさそう。
楽しみになってきた。

mazreanmazrean

Resultannotation.FullTriggerを返す。
これを見て気が付いたが、triggerが何か理解してからこのあたり見た方がよさそうなので、一旦切り上げて/annotation見る。

mazreanmazrean

FullTriggerは基本的には「nilが発生する条件」と「発生したnilを使用する条件」の組と考えれば良さそう?