Thanks Driven Life

日々是感謝

Rubyのメソッドコードを渡すと返り値を推測する gem を作りました

github.com

ネタgemです。

Overview

こういうやつです。

WhatDyaReturn::Extractor.new.extract(<<-CODE)
  def foo
    42
  end
CODE
# => ['42']

WhatDyaReturn::Extractor.new.extract(<<-CODE)
  def foo
    if bar
      1
    else
      2
    end
  end
CODE
# => ['1', '2']
# `bar` が true か false か確定していないため、2つともありえる

できること/できないこと

確認しているシンタックスtest/what_dya_return/extractor_test.rb にずらーっと書いているので、そちらをご覧ください。

✅ early return や break はそれっぽく動いています。

WhatDyaReturn::Extractor.new.extract(<<-CODE)
  def foo
    return 42

    'baz' # unreachable
  end
CODE
# => ['42']

while とか for あたりもそれっぽく動くと思います。

WhatDyaReturn::Extractor.new.extract(<<-CODE)
  def foo
    while bar
      1
      break 2 if baz
      3
    end
  end
CODE
# => ['2', 'nil']
# break せずに while を抜ける場合は nil が返る

リテラル値が入っていることが確定している変数でも展開はしません。

WhatDyaReturn::Extractor.new.extract(<<-CODE)
  def foo
    a = 1
    a
  end
CODE
# => ['a']

❌ その他、まだ見ぬ(試していない)シンタックス。たくさんありそう。

作るに至ったモチベーション

  1. 業務や趣味のプロダクトでは RuboCop を使っている
  2. 「今更だけど RuboCop ってどうやってコードをチェックしているんだろう」
  3. rubocop から rubocop-ast に辿りつく
  4. AbstractSyntaxTreeは存在は知っていたものの、あまり深く入り込んだことがなかった
  5. なんかこれを使ってネタコード書いてみたいわ ← 今ここ

題材としては「そういえばRubyは(基本的には)最後に評価された値が返り値になるよなー」からの「じゃあ node.children.last 返すやつ書けばいいんじゃね!簡単!」ってことで、今回のテーマに決まりました。(実際にはそんなに簡単ではなかったけど)

というわけで作ってみました

当初は RubyVM::AbstractSyntaxTreeparser gem を直接使う方向で実装していたのですが

  1. パース後のツリーを辿っていく時は、ある程度抽象化された形でアクセスしたいな
    • ひたすら children[N] でやっていくのもつらいしのぉ……
    • 自分で Parser::AST::Node を拡張するという手も考えましたが、ここで次の話題に
  2. early return や if/else などの「これ以降のコードには絶対到達しない」場合は省略したいな
    • そういえば RuboCop には Lint/UnreachableCode があるやん
    • 実装真似したいけど、結構 RuboCop::AST::Node ありきの書き方が多いな
    • まるごとパクってもいいけど……
  3. ほなら RuboCop::AST に乗っかった方が早いやんけ! ← 今ここ

というわけで、実装は RuboCop::AST にがっつり依存することにしました。

結果としては上記の Lint/UnreachableCode 以外の実装も参考になったため、良い方針転換になりました。

まとめ

そこそこのパターンでそれっぽく動いたので満足しています。 まだ見ぬ未対応シンタックスについては暇潰しの1つとしてゆったり対応していこうと思います。

[おまけ1] gemの名前について

  • 「そのメソッド、何返すの?」的な役割なので、まさにそれを表現できる名前にしたかった
  • 「それってこれ?」みたいな奴で似たようなもの did_you_mean を思い浮かべた

というわけで what do you return(?)what dya return としました。

[おまけ2] 先行事例について

多分見つけられていないだけでありそう。 RBS似たようなことをやっている のも気になる(本 gem みたいにがっつり値を返すってことまではしていないはずだけど)

[おまけ3] ChatGPT

賢すぎる……