RubyのTypo Checkerについての考察

RubyKaigi 2014の基調講演でまつもとさんが静的型の野望を明かしてから2年半が経った。 その間の進捗は芳しいものとは言えないけれど、それでもまじめな研究として例えば多相型、推論、Ruby が行われている。普通の人は私のこの記事を読むよりもこちらを読んだ方がよいと思う。 じゃあなぜこの記事を書いたかというと、それでも一部の人には得るところがあると思っているからである。この記事の読者の中にはRubyKaigi 2014中に書かれたakrさんの日記で「非常に簡単化した静的解析」の話を読んだ人もいるのではないかと思う。この話をそのまま発展させた場合にどういう迷路に迷い込むのかという点についていくつかの知見を得たものの、これまでそれを書いていなかったのでちゃんと書くことにしたのだ。

さて、nurse/static-check.rbである。 これは大きく分けて3つの部分からなっている。定義されているメソッドのリストを得る部分、呼ばれているメソッドのリストを得る部分、あるメソッドから呼ばれているメソッドをよりfalse positiveを減らしつつ得る部分だ。

定義されているメソッドのリストを得る部分はより漏れがないようにしている。具体的には特異メソッドも取れるようになったのだが、デメリットとして全てのオブジェクトに特異クラスを作ってしまう。(最近のRubyではいわゆる即値にあたるオブジェクトでは特異クラスがつくれなくなっているので、それ以外)これを避けるにはRuby側に新たな機能が必要なんだけど、これ、ぼくいがいにだれかほしい?

2つ目の呼ばれているメソッドのリストを得る部分はakrさんのものから本質的な違いは無い。RubyVM::InstructionSequence.disasm(meth)の戻り値はStringだが、RubyVM::InstructionSequence.of(meth).to_a[13]の戻り値はオブジェクトなのでより構造的に扱えるというくらいだ。

このスクリプトで本質的なのは3つ目のfalse positiveを減らす部分だ。false positive、つまり存在しないものとして扱われて欲しいのに、メソッドを呼び出しているとされてしまう例は例えばGem.load_yamlにある以下のようなコードだ。

        if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
          YAML::ENGINE.yamler = "psych"
        end

これらのYAML::ENGINE、YAML::ENGINE.yamler、YAML::ENGINE.yamler=はRuby 1.9でのSyckからPsychへのYAMLライブラリ移行に際して存在した移行用の定数とメソッドで、2.2以降ではもはや存在しない。こういうコードをそのまま読んでしまうと、typo checkに引っかかってしまう。これを避けるため、静的実行のように、スタックを管理しながら絶対に実行されない部分を除外しようとしている。

まぁここまで書くと、もっと効率的に数学的に解析出来ないものかと思い始めるわけですね。わたしはSSA形式に変換した方がいいんじゃないかなと思いました。で、中断して今に至るわけです。ですから、RubyバイトコードSSA形式に変換して変数への代入をうまく処理しながらデッドコードを削除してtypo checkをしたりするのは読者の宿題とします。