rubocopをRBSファイルにも効かせたい

背景

RBSも利用が増えてくると、細かい"こうしたほうがいい"が出てくることがあると予想される。

例えば、

  • インデント
  • initializeの返り値はvoidを使う
  • untyped?ではなくuntypedを使う
  • メソッド引数にvoidを使わない

などなど。

こうしたことを指摘するツールとしては、Rubyではrubocopが覇権をとっており、大体の環境ですでに使われている。

このエコシステムにRBSも乗っかりたい。

問題点

VSCode上で警告を表示できない

大きな問題点として、VSCode上の波線(squigglyというらしい)が表示できないという問題がある。

エディタでインタラクティブに問題点が見える体験はかなり重要だと思っているので対応したい。

しかしながら何が原因なのかはわからない。

試しにrubocop orgが公式にホストしているrubocop-mdを使ってみたが、こちらも特にエディタ上ではなにも表示されなかった(ruby-lsp)。

原因を究明するのに時間を使うべきか、より機能追加に時間を使うべきか、悩みどころである。

名前をrubocop-rbsにできない

今回作っているものはrubocop-rbsと言うしかないものなのだが、

rubocop-rbsというgemはすでに存在している。

これはRBSを見て、Ruby側のメソッド定義の引数の形式と数のマッチングをみるもののようだ。

名前は早い者勝ちなのでしょうがないが、この作っているものの名前に悩んでいる。

今は仮にrubocop-rbs-sigとしている。

機能

とりあえず、

  • シンタックスエラー表示
  • initializeの返り値はvoidを使う
  • untyped?ではなくuntypedを使う

できた。

sig/rubocop/rbs/sig.rbs:5:31: C: [Correctable] RBS/Sig/InitializeReturnType: #initialize method should return void
        def initialize: () -> nil
                              ^^^
sig/rubocop/rbs/sig.rbs:6:19: C: [Correctable] RBS/Sig/Untyped: untyped? should be untyped
        def foo: (untyped?) -> (Integer | untyped)
                  ^^^^^^^^
sig/rubocop/rbs/sig.rbs:6:33: C: [Correctable] RBS/Sig/Untyped: Integer | untyped should be untyped
        def foo: (untyped?) -> (Integer | untyped)
                                ^^^^^^^^^^^^^^^^^
  • autocorrect
  • 機能別にCopを分ける

といったこともできている。

実装方法

rubocopはRubyに特化したツールであり、RBSRubyとは違う。

Ruby以外のファイルにrubocopを実行する参考例としては、rubocop-erbrubocop-mdがみつかった。これを参考にした。

rubocopではターゲットファイルがRubyとしてパースできなかった場合、on_other_fileを呼び出すようになっている。

rubocop/lib/rubocop/cop/commissioner.rb at 673495b70e8b9e42d82d85526292945d47189f10 · rubocop/rubocop · GitHub

このon_other_fileRBSとしてパースし、パースできたらRBSとして扱うようにしてみている。

Rubyとしてパースして、RBSとしてもパースしているので無駄はある気はするが、ここを変えようとするとrubocopにかなり無茶させないといけなくなり、コード量も増える気がするので一旦こうしている。

つまり

やればできそう。