この記事は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ファイルを増やした。