2019-12-30
Steepを試しにKibelaに突っ込んでいて、エラーが標準出力に出ていて邪魔だったので標準エラー出力に出すようにしてみた。
最低限クラスがあることがrbsで書かれていないと、部分的に型を書いたとしてもSteepは使い物にならなさそうなので、まずは全部のクラスの型を生成する必要がありそう。 これはruby-signatureにある適当なコマンドでいけるかな。
# Userクラスの型定義 # class User # def self.find: (Integer) -> instnace # def name: () -> String # end user = User.find(1) user.name # これはok user.full_name # これはNoMethodErrorがちゃんと見つかる class UsersController def show # Userクラスがanyにfallbackしてしまう # 当然user変数もanyにfallbackする user = User.find(1) user.name # anyに対するメソッド呼び出しなのでチェックが無 user.full_name # これもanyに対するメソッド呼び出しなのでチェックが無 end end
つまりなんらかのクラス定義の中にある定数参照は、外側のクラス定義が型として定義されていなければすべてanyにfallbackするので、解析が無になる。
たしかに🦀と思ったのでrevertした。勉強になった。
rbsコマンドのヘルプメッセージをいい感じにした。OptionParserではbannerに文字列をセットするといい感じにできる。
rbs prototype rb
コマンドがうまく動かないので直している。
想定してないnode (今回は引数なしのFCALLとyield)にぶつかったときはまあ直せばいいので簡単。
syntax errorのコードを吐いているのがむずかしくて、type
キーワード引数があると死ぬ。というか普通の引数でも死ぬ。
class X # ダメ # # def foo: (type: untyped type) -> untyped def foo(type:) end # これもダメ # # def bar: (untyped type) -> untyped def bar(type) end end
上のクラス定義はコメントのようにrbsが生成されるけど、rbsではtypeは予約語であり、かつvar_name_opt
(うしろのtype
)では予約語は許されていないので死ぬ。
keyword_name
(まえのtype
)では予約語が許されているからこちらはokぽい。
どうするのがいいのだろう。とりあえず次の2つは思いつく。
var_name_opt
で予約語を許す- とりあえずconflictを増やさず実現はできそう。
- メソッド名みたいにbacktickでescapeできるようにする
rbs prototype rb
コマンドが生成するrbsの名前を適当にunderscoreとかつけてごまかすこともできるけど、rbsを手書きする時にtypeって書きたい人もいるだろうし、書けるようになっていたほうが便利な気がするなあ。
……ということをIssueに書いた。
hanachinさんがruby-signatureに出していたPRをレビューしてた。
https://github.com/ruby/ruby-signature/pull/150#discussion_r362002475 これは型検査器がメソッドのオーバーロードを見る時に定義順で見ることをruby-signatureが前提として置いていいのか、というのが気になった。 前提としていいのならこの型定義でいいんだけど(便利)、前提にするべきでないなら壊れる可能性があるからやめたほうがよさそう。
https://github.com/ruby/ruby-signature/pull/149#discussion_r361998221 もっと狭い型を返せそうだったからコメントした