RuboCop Performance 1.11 をリリースした

RuboCop Performance 1.11 をリリースした。主な変更点は以下。

  • Ruby 2.4 のサポートを終了した
  • Ruby 2.7 で追加された Enumerable#filter_map への Cop を追加した

github.com

前者はそのままなので、後者について記す。

Enumerable#filter_map に対応づけられるのは、以下の2つの Cop となる。

これは当初 Performance/FilterMap cop として提案していたものだが、対象となる bad ケースのコンテキストや good ケースとの互換性に違いがあるため、別々の Cop として分割した。

Performance/SelectMap cop

github.com

この Cop は select.map のチェーンへのケースを filter_map に置き換えることを提示する。

# bad
ary.select(&:foo).map(&:bar)
ary.filter(&:foo).map(&:bar)

# good
ary.filter_map { |o| o.bar if o.foo }

振る舞いに互換性はあるが、good ケースの可読性について賛否があるのと、複雑なブロック処理については自動修正が困難だと思い自動修正を提供しておらず、手修正も大変そうということからデフォルトで無効にしている。

Performance/MapCompact cop

github.com

この Cop は map.compact のチェーンへのケースを filter_map に置き換える。このケースは good ケースになることでコードもシンプルになると思う。

# bad
ary.map(&:foo).compact
ary.collect(&:foo).compact

# good
ary.filter_map(&:foo)

以下のいずれのケースも nil を除外するが、filter_map は加えて false も除外する。このように compactfilter_map は振る舞いに互換性がないため、SafeAutoCorrect: false (rubocop -A で適用) としている。

[true, false, nil].compact              #=> [true, false]
[true, false, nil].filter_map(&:itself) #=> [true]

なお、Enumerable#filter_map! というメソッドは存在しないので、以下のケースは受け入れている。

ary.map(&:foo).compact!

Performance/SelectMap cop は好みが分かれるところだと思うが、Performance/MapCompact cop は適用可能であれば適用しておくとコードとしてもシンプルになると思う (ただし振る舞いの違いに気をつけて適材適所で) 。

いつもの新規 Cop のようにデフォルトで pending ステータスなので、NewCops: enable にしているわけでなければ、以下のように有効化が必要となる。

# .rubocop.yml
Performance/MapCompact:
  Enabled: true

今日はここまでです。ハックを続けましょう。