ruby/rbsに取り込まれた私のパッチ
この記事はRuby 3.0 Advent Calendar 17日目の記事です。
昨日の記事は id:gamelinks007 さんで「C APIのヘッダーが分割された話」でした。
この記事では、Ruby 3に同梱されるrbsに対して私が書いたPull Requestを紹介します。本日(2020-12-17)時点で64個のPull Requestがマージされているようです。
古い順に紹介していきます。
ref: https://github.com/ruby/rbs/pulls?page=1&q=is%3Apr+author%3Apocke+is%3Amerged
gemspecのfilesで絶対パスを使っていてコケていたのを修正。
この頃はまだruby-signatureと呼ばれていたんですね、懐かしい。
Kernel.#p
の型を修正。p
は引数の型を返すけど、nil
を返すと誤った型付けがされていた。
Kernel
モジュールの型定義を修正。
これ頑張った気がする。
このへんはRHC起因でやるぞとなったやつな気がする。 https://rhc.connpass.com/event/151557/
テストコードのボイラープレートを生成するコードを書いた。
自動生成されたファイルをRuboCopの検査から外した。 これは手元のraccが古かったのが原因だった気がする
RBSで空Tupleが許可されているけどドキュメント上はそうなっていなかったのを直した。
WriterというRBSを書き出すためのクラスがあるのだけど、それが空タプルやoptionalなsymbolで壊れていたので直した。
しかし:foo ?
ってダサいよな。(:foo)?
とか書けないかな。
このころはrbsfmtを作っていたけど、だるくて放置している。コメントをいい感じにするのがだるそう…
RBSを読み込んでそれをまたWriterで書き戻すようなときに、行間が空いていたらそれを維持するようにした。
ruby/rbsにある.rbs
を全部読み込んでWriterを通した結果意味が変わらないかをテストするコードもついでに足した。
hash
メソッドの定義が間違っていて壊れていたので直した。 あとPRの最初の状態だとhash
メソッドの使い方が間違っていてそれを教えてもらった。
bin/setup
を使うようにドキュメントを書いた。自分はbin/setup
の存在を忘れがちであんまり使わないのだけど…
rbs
コマンドのヘルプメッセージの出力をいい感じにした。
rbs prototype rb
が引数のないFCALLや引数のないyieldでコケていたので直した。
仮引数名にキーワードを使えるようにした。
::Foo
のように絶対パスでクラスが参照されているとFoo
が定義されていなくてもそれっぽい値が返ってきてしまう問題を直した。これ難しかった気がする。
RBSでclass << self
を解釈するようにした。
self
じゃないときもとりあえずself
と同じように扱っているのだけど、それが原因でぶっ壊れているところがあって、そこは直したい気持ち…。
よく覚えていないけど、include元のモジュールで定義されている定数をinclude先のモジュールのnamespaceで参照しようとしたときにエラーになっていたのを直したっぽい。
Steepを使っていて、RBSの定義がなにかおかしいときに変なエラーになってしまっていたのを直した。
rbs prototype rb
でkwrestが不必要に出力されてしまっていたのを直した。
テストメソッド名の重複を直した
Rubyのmasterでkeyword argsがRuby 3の仕様になったのに合わせてテストが落ちていたので、ruby2_keywords
メソッドを使ってなんとかした。
original ideaはmameさんらしい。
テストメソッド名の重複を直した。
markdownでリストをネストするのに必要な空白の数が足りていなかったので足した。o
GitHub Flavored Markdown needs four spaces to indent unordered list items in ordered list items.
って言ってるけど"four spaces"は厳密には嘘で4つじゃ足りないこともあるのだよな。という記事を書こう書こうと思いつつ書けていない…
Hashの型を書いた。大変だったやつ
たぶんHashの型を書いているときに気がついたやつで、ドキュメントが間違っていたのを直した。
ライセンスの書き方が微妙な感じだったのをいい感じにした。
CLIの--no-stdlib
オプションが壊れていたのを直した。
https://github.com/ruby/rbs/pull/207github.com
RBSのsyntax checkだけを行うコマンドを追加した。これをエディタから使うとRBSを書きつつSyntax Checkが簡単にできて便利。
https://github.com/ruby/rbs/pull/208github.com
rbs prototype rb
でprivate def foo
スタイルのメソッド定義が抜け落ちていたのを直した。このスタイルのメソッド定義好き。
https://github.com/ruby/rbs/pull/209github.com
rbs prototype rb
で戻り値の型をいい感じに推測するようにした。prototype rbがでかくなっていく最初のパッチという感じがする…。
https://github.com/ruby/rbs/pull/212github.com
Pathnameの型を足した。これはRBS Railsで使っていてほしかったからだっけかな。
https://github.com/ruby/rbs/pull/213github.com
String#encode
のfallbackオプションの型が違っていたのを直した。
そしてそもそもRuby側の実装も壊れていたっぽいので報告した。
https://github.com/ruby/rbs/pull/219github.com
Dir.open
の型が間違っていたので直した。
https://github.com/ruby/rbs/pull/290github.com
Enumerator::Yielder
の型を直した。同じ頃るりまのドキュメントも書いていた気がする。
https://github.com/ruby/rbs/pull/291github.com
Ruby 2.7で追加されたEncoding::CESU_8
の型定義を追加した。これもるりまのドキュメントを書いていて気がついたやつな気がする。
https://github.com/ruby/rbs/pull/292github.com
builtinのFiberの型を直した。
これもるりまのドキュメントを書いていたり、あとはRDocの記述も間違っていたのでbugsにレポートしたりしていた。
https://github.com/rurema/doctree/pull/2277github.com
https://github.com/ruby/rbs/pull/293github.com
ドキュメントの修正をした
https://github.com/ruby/rbs/pull/296github.com
require 'fiber'
すると生えてくるFiberのメソッドの型を足した。
https://github.com/ruby/rbs/pull/303github.com
rbs prototype rb
で、メソッド本文が複数文あっても戻り値の型を推測するようにした。
https://github.com/ruby/rbs/pull/304github.com
_
始まりのメソッドが?
や!
で終わっているとSyntax Errorになってしまっていたので直した。
https://github.com/ruby/rbs/pull/306github.com
mutex_m
の型を書いた。Railsが依存していてRBS Railsでほしかった。
https://github.com/ruby/rbs/pull/314github.com
テストのボイラープレートを生成するgenerate:stdlib_test
rake taskが動いていなかったのを直した。これ今はちゃんと動いているのかな……
https://github.com/ruby/rbs/pull/316github.com
loggerライブラリの型を足した。 これもRailsで使っていて必要になったやつ。
Loggerはそこそこのサイズがあって多少の気合を出して書いた。
https://github.com/ruby/rbs/pull/317github.com
IO周りの型を直した。IO#write
はrest argumentsを受け取るらしい。このPRを見返すまでそんなことすっかり忘れていた。このPRを書いた時の自分はなんでこれに気がついたのだろう
https://github.com/ruby/rbs/pull/319github.com
ゴミを消した。
このPRの後でゴミの検出が自動化されていた。便利。 https://github.com/ruby/rbs/blob/49e4d7219d52e0e1ca25c91b2c1b33e7e8a98dd1/goodcheck.yml#L2-L12
https://github.com/ruby/rbs/pull/327github.com
この頃RBSのパースが遅いという話題があって、それをサッと速くした。
いつもどおりstackprofでプロファイルを取っているとStringScanner#charpos
がめっちゃ遅くて、実装を見てみるとたしかにめっちゃ遅そうな実装だった。
https://github.com/ruby/strscan/blob/d0c82c20c64323c45ee275f09e5a951a291f1ee2/ext/strscan/strscan.c#L442-L453
これはscannerが読み込んだ位置を文字単位で返すAPIなのだけど、scannerは読み込んだ位置をバイト単位でしか保持していない。
なので読み込んだ位置のバイトを使ってString#bytesliceを呼んで、その結果の部分文字列に対してString#length
を呼んでいる。遅そう。
というわけでStringScanner#charpos
が遅いので、これの呼び出し回数を減らした。
またRBSがASCIIだけで書かれている場合にはバイト単位の位置で充分なためcharpos
の代わりにpos
を使うようにした。
つまり、RBSはASCIIだけで書くと速い。
https://github.com/ruby/rbs/pull/331github.com
RBS::Environmentをうっかりp
すると画面が破滅するのを直した。
https://github.com/ruby/rbs/pull/334github.com
rbs prototype rb
で重複するメソッドは1つにまとめるようにした。
class C if RUBY_VERSION >= '2.7' def foo do_something_27 end else def foo do_something end end end
PRのdescriptionにもあるとおり、こういうコードがあるとfoo
に対する定義が2回出力されてエラーになってしまうため。こういうコードはRailsの内部とかで実際にある。
https://github.com/ruby/rbs/pull/337github.com
重複する定数がある際にいい感じのエラーが出ずにNoMethodError
になってしまっていたのを直した。
これは実際に重複する定数を生成してしまって意味不明なエラーが出ていたとかだったと思う
https://github.com/ruby/rbs/pull/338github.com
rbs validate
を速くした。当時のRBS RailsのRBSに対して3.77倍速くなったので結構でかい。
コードを読んでなんとなくの理解しかできていないのだけど、rbsでは型引数をメソッドに適用するために全てのメソッドに対してsub
メソッドでメソッド型を表すオブジェクトを作り直していた。
一方型引数を使っているメソッドというのは多くなくて、かつこのオブジェクトの作り直しは型引数を使っていないコードに対しては無意味なので、そこで無意味に遅くなってしまっていた。
なのでこのPRでは型引数を使っていないメソッド定義ではオブジェクトを生成しないようにして速くした。みたいな感じだったと思う。
https://github.com/ruby/rbs/pull/457github.com
rbs prototype rb
でaliasをサポートした。
これでRailsのコードに対してRBSを生成し直したらalias周りで色々バグが見つかって、いくつかIssueに報告した。むずそう。 https://github.com/ruby/rbs/pull/516
https://github.com/ruby/rbs/pull/462github.com
Enumerable#to_h
はブロック引数を受け付けるのだけど、そうなっていなかったので直した。
これはRBS Railsを触っているときにto_hの定義を見に行きたくなって見に行ったら「あれ?ブロック受け取らなかったっけ?」となって足したような気がする。
https://github.com/ruby/rbs/pull/463github.com
RBSが意図しないEOFでSyntax ErrorになったときにNoMethodErrorになってしまっていたのを直した。
raccのためのnext_token
メソッドが返す値が間違っていたのが原因だった。raccの使い方に少し詳しくなった。
https://github.com/ruby/rbs/pull/465github.com
rbs prototype runtime
がhash spread構文でエラーになってしまっていたのを直した。
https://github.com/ruby/rbs/pull/481github.com
rbs prototype rb
でpublic
とprivate
とmodule_function
に対応した。
正直public
とprivate
はおまけで、module_function
の対応が欲しかった。
そしてRBSにはprotectedがない。
https://github.com/ruby/rbs/pull/482github.com
Singleton moduleの型を書いた。
この型があって便利かと言うと、まあinclude Singleton
とinstance
メソッドがあっても型エラーにならなくなるぐらいかなあ。
class SingletonSingletonTest
、ひどい名前だ
https://github.com/ruby/rbs/pull/483github.com
RBSにmonitor libraryの型を追加しようとMonitorMixinに対してrbs prototype runtime
したらエラーになって、それの対応。
rbs prototype runtime
をarguments forwarding syntaxで定義されたメソッドに対して使うと、Syntax Errorなコードを生成していた。
https://github.com/ruby/rbs/pull/485github.com
monitor libraryの型を書いた。これもRailsが依存していたから追加しておきたかった型。
https://github.com/ruby/rbs/pull/486github.com
rbs prototype rb
のmodule_function
対応のバグ修正。別の場所で定義されたメソッドをmodule_function
しようとしているとエラーになっていた。これはRailsの型を生成しようとして気がついたやつな気がする。
https://github.com/ruby/rbs/pull/487github.com
rbs prototype rb
でrefinementsを無視するようにした。
RBSはrefinementsをサポートしていないし、まあ無視していいかなと言う気持ち。
https://github.com/ruby/rbs/pull/491github.com
rbs prototype rb
でpublic
やprivate
のサポートにもバグがあってそれを直した。なんかこの辺のコード死ぬほど難しくてむずい。書いたの自分だけど……
https://github.com/ruby/rbs/pull/492github.com
rbs prototype rb
でextend self
をサポートした。
私はextend self
を好きでよく書くからこれが出てくれるのは嬉しい。
https://github.com/ruby/rbs/pull/505github.com
soutaroさんが実装したsingletonのattr_*
をrbs prototype rb
でもサポートした。
https://github.com/ruby/rbs/pull/517github.com
この記事を書くために自分のPRを見返していたら、loggerの型のTODOの消化忘れを見つけたので直した。 loggerの型を書いたときにはMonitorMixinの型はまだなかったからincludeできなかったのだけど、MonitorMixinの型を書いたのでincludeできるようになった。
https://github.com/ruby/rbs/pull/518github.com
これもこの記事を書いていて気がついた問題で、前に追加したwriterのsmoke testでテスト対象のRBSファイルを増やした。