LoginSignup
9
2

More than 3 years have passed since last update.

【Ruby3】 型を書かずに型検査する "型プロファイラ" を試す

Last updated at Posted at 2020-02-17

追記(2020/02/27): セットアップ方法が変更されたので記事に反映しました

型プロファイラとは?

型プロファイラは、

型注釈がない素の Ruby プログラムを入力して、
型エラーの可能性を警告したり(Testing)、
型シグネチャのプロトタイプを生成したり(Understanding)
できるツールです

※開発者である遠藤さんのブログ記事から引用

この機能は2020年にリリースが予定されている Ruby3 の新機能の中の静的解析の一要素となっています。
その他の静的解析の情報については Ruby の型関連の情報まとめ にまとめてあるので良ければ見てください。

環境

  • Ruby 2.7.0

セットアップ

$ git clone --recursive https://github.com/mame/ruby-type-profiler.git
$ cd ruby-type-profiler
$ bundle install

デモ

README の DEMO と同じものを動かしてみる。
以下のコードを ruby-type-profiler/tmp/test.rb として保存。

test.rb
def foo(x)
  if x > 10
    x.to_s
  else
    x.boo()
    x + 42
  end
end

foo(42)
$ cd ~/work/ruby-type-profiler
$ ./run.sh tmp/test.rb

上記を実行すると、以下のように、定義が存在しない定義が検出される。また、推定された型シグネチャも出力されている。

# Errors
tmp/test.rb:6: [error] undefined method: Integer#boo

# Classes
class Object
  foo : (Integer) -> (Integer | String)
end

ここで x.boo() の行をコメントアウトしてみると、予想通りエラーが消える。

$ ./run.sh tmp/test.rb
# Classes
class Object
  foo : (Integer) -> (Integer | String)
end

次に、引数を文字列にしてみる。

test.rb
def foo(x)
  if x
    x + '42'
  else
    x + 42
  end
end

foo('a')

型プロファイラの実行結果は以下のようになる。

$ ./run.sh tmp/test.rb
# Errors
tmp/test.rb:6: [error] failed to resolve overload: String#+

# Classes
class Object
  foo : (String) -> (String | any)
end

x が文字列のとき、 x + 42 を実行すると TypeError が発生するが、上のコードでは else ブロックに入らなくてもそれを検出できている。
引数を文字列にしたことで、推定される型が先程は Integer だったのが String に変わっている。

サンプルコード

smoke/ に大量のサンプルコードがあるので、これらで遊んでみると良さそう。

$ ./run.sh smoke/demo.rb
# Classes
class Object
  foo : (FalseClass | TrueClass) -> (Integer | String)
  identity : (:sym | Integer | String) -> (:sym | Integer | String)
  fib : (Integer) -> Integer
end

class A
  foo : (Integer | String) -> NilClass
  bar : (Integer | String) -> NilClass
end

class B
  bar : (Integer | String) -> NilClass
end

テスト

./smoke.sh を利用することでテストを実行できる。
OK の場合はそれ以外表示されず、NG の場合エラー内容が表示される。

$ ./smoke.sh smoke/demo.rb
# OK: smoke/demo.rb

$ ./smoke.sh tmp/test.rb
# NG: tmp/test.rb
expected:
actual:
# Errors
tmp/test.rb:6: [error] failed to resolve overload: String#+
# Classes
class Object
  foo : (String) -> (String | any)
end

参考

type-profiler のレポジトリ
https://github.com/mame/ruby-type-profiler

開発者である遠藤さんによる解説記事
https://techlife.cookpad.com/entry/2019/04/16/164858

A Static Type Analyzer of Untyped Ruby Code for Ruby 3
https://www.slideshare.net/mametter/a-static-type-analyzer-of-untyped-ruby-code-for-ruby-3

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2