`Layout/IndentationWidth` cop の偽陽性をなおした

rails/rails で実行したら offense の出るリグレッションのパッチの PR を開いたくらい。

github.com

偽陽性の起きるケース例として、以下のようにブロックのあとにメソッド呼び出しをしているのがある。

obj = Class.new do
  private def private_property
    "That would be great."
  end
end.new

Cop 実装の解決としては、再帰で親ノードを遡って send ノードでなくなった時点で打ち切るのがポイントだった。

         def leftmost_modifier_of(node)
-          node.each_ancestor(:send).to_a.last
+          return node unless node.parent && node.parent.send_type?
+
+          leftmost_modifier_of(node.parent)
         end

Rails Developers Meetup 2018 Day 3 Extreme に登壇した

Rails Developers Meetup 2018 Day 3 Extreme で『RuboCop Headquarters 2018』というタイトルで登壇した。

techplay.jp

Rails Developers Meetup では 2018 Day 1 から4ヶ月ぶり4度目の登壇となる発表資料は以下 (もしかすると登壇回数最多?) 。

今回は秒速 284km さんから RubyKaigi 2018 で話していた Part 3: Cross over OSS communities みたいな感じで「衝突してしまった RuboCop Rails の Gem 名を譲ってもらうにあたる話をしてもらいたい」とのオファーを Asakusa.rb でもらったのがきっかけ。あまり同じ話はしないタイプだけれど、これまで RuboCop で活動してきた話を RubyKaigi 2018 で話したので、これから RuboCop で行う話としてまとめることで違いを出してみたつもり。

前半は RuboCop Headquarters の話。後半は RuboCop 1.0 に向けたマイルストーンと実現に向けた活動の話の2パート構成になっている。

RuboCop 1.0 について3行でざっくりまとめる。

  • 設計どおりに振る舞う Cop に限定する safe と、設計どおり自動修正される safe-auto-correct などのメタデータと実行オプションが導入される
  • バージョンアップで導入される新たな (枯れていない) Cop を利用する/しないを選択できるようにして、アップグレードのハードルを下げる
  • Rails cops と Performance cops はコアから外れる

safe な Cop かどうかだけれど、まず safe が前提になる。そのうえで unsafe であるかどうかの判断は、Issue が元になるとおもうので問題があれば Issue を開いてもらいたい。

このあたりを基準に safe がまずそうであれば、unsafe な Cop としていくと思うので、もし false positive などの問題に遭遇したときは RuboCop 1.0 への参加方法のひとつとしてもらいたい。

あるいは私が所属している https://github.com/esminc という企業 organization に参加して伝えてもらえれば、うまいことやりたいと思っている (ステマ) 。

もう少し詳しい情報はスライド参照。現時点で見えている RuboCop 1.0 の姿がまとまった唯一無二のスライドだと思う。

質問の余地を残すためあえて記さなかったけれど、RuboCop 1.0 にいつなるかについては年内か年明けくらいには条件は整えておきたいという目標。

その他、個人的には金子さんが開発を進めている Ruby 2.6.0 で試験導入される RubyVM::AST の静的解析ツールなどへの実用性などに興味があって、金子さんと話せたのは良かった。いまのところ複数の Ruby バージョンを梱包している Parser gem と異なり、実行処理系の AST に依存するというあたり、マルチプラットフォームが必要な Gem ではない何らかの用途を前提での利用検討となりそうという所感。

また 2019 年にも開催されるということで、次回の Railsdm も楽しみにしています。スタッフのみなさんありがとうございました。

rubocop-hqディレクトリを`rm -rf`してしまった

本日 16:30 頃、ghq 管理されている github.com/rubocop-hq ディレクトリに対して rm -rf コマンドが実行され、rubocop をはじめ rubocop-rails, rubocop-rails, rubocop-performance など多数のローカルリポジトリが消失。rubocop のローカルリポジトリにおいては、およそ1年以上に渡る様々な開発中ブランチがなくなるという被害が発生しました。

その被害を受け、定期的なバックアップや rm 実行後ゴミ箱に移動するなどの二次災害防止への活動を呼びかけています。 (リポーター風に)

`Metrics/AbcSize` のデフォルト値

Metrics/AbcSize はデフォルトが厳しいので、緩めると良いかもという見解のはなし。

RuboCop のデフォルト値は 15 となっている。

Metrics/AbcSize:
  # The ABC size is a calculated magnitude, so this number can be an Integer or
  # a Float.
  Max: 15

https://github.com/rubocop-hq/rubocop/blob/v0.58.1/config/default.yml#L1547-L1550

これはなかなかきびしく OnkCop では 24 となっている。

# 30 まではギリギリ許せる範囲だったけど
# リリースごとに 3 ずつぐらい下げていきます。20 まで下げたい。
Metrics/AbcSize:
  Max: 24

https://github.com/onk/onkcop/blob/v0.53.0.0/config/rubocop.yml#L105-L108

AbcSize 15 の世界はなかなか厳しく、RuboCop 自体が RuboCop で引っかかって RuboCop では .rubocop_todo.yml で Metrics/AbcSize の値が 18 になっている🤔

# Offense count: 88
Metrics/AbcSize:
  Max: 18

https://github.com/rubocop-hq/rubocop/blob/v0.58.1/.rubocop_todo.yml#L9-L11

デフォルトを変更するのは大きな変更のため説得を考える必要があるが、AbcSize のデフォルト値を緩めるための説得要因のひとつとして挙げられると思うので、どこかのタイミングで提案を行うかもしれない。

2018-07-13 追記 以前にいちど pocke さんが提案してクローズになっていたのだった。

github.com

zsuperの振る舞い

RuboCop というか Parser gem では、引数なしの superzsuper (zero arity super) という名前で表している。

以下のケースについて、Lint/ShadowedArgument cop はデフォルトで offense を出す。

def do_something(arg)
  arg = 'world'
  super
end

しかし、これは以下のように引数なし super の振る舞いの特性により offense を出してもらいたくないケースがある。

class Foo
  def do_something(arg)
    "hello, #{arg}"
  end
end

class Bar < Foo
  def do_something(arg)
    arg = 'world'
    super
  end
end

puts Bar.new.do_something('hi') # => 'hello, world'

サブクラスを少し変えた振る舞いはこのとおり。

class Bar < Foo
  def do_something(arg)
    super
  end
end

puts Bar.new.do_something('hi') # => 'hello, hi'

IgnoreImplicitReferencestrue にすることで、これらのような zsuper の使い方を受け入れる設定になる。

Lint/ShadowedArgument:
  IgnoreImplicitReferences: true

RuboCop 0.58.1 がリリースされた

RuboCop 0.58.1 がリリースされた。

github.com

Parser gem 2.5.1.1 に以下の問題があったため、そのバージョンへの依存を回避するのがリリースのきっかけになっている。

% cat foo.rb
class Foo < Bar
end
% ruby-parse --25 foo.rb
foo.rb:2:1: error: unexpected token kEND
foo.rb:2: end
foo.rb:2: ^~~

この関係で Parser に依存する RuboCop で起きる問題を回避している。そのほかバグフィックスがいくつか入っているので、RuboCop 0.58.0 を使っているユーザーは問答無用で 0.58.1 にアップグレードをしておくと良い。

以下は自分の直したもの。

Bug fixes

#6086: Fix an error for Gemspec/OrderedDependencies when using method call to gem names in gemspec. (@koic)

以下のように add_development_dependency の引数の Gem 名の文字列にメソッド呼び出しがあるケースがエラーになっていたのに対処した。

Gem::Specification.new do |spec|
  spec.add_development_dependency 'rspec'.freeze
  spec.add_development_dependency 'rubocop'.freeze
end

再帰を使ってメソッドチェーンしているケースにも対応している。

#6088: Fix an error for Layout/MultilineAssignmentLayout cop when using multi-line block defines on separate lines. (@koic)

foo = -> { ... といった代入のケースについて取り締まる Cop だが、以下のようにブロックでの代入がないケースについて RuboCop 0.58.0 でリグレッションが起きてエラーになっていたのを直した。

default_scope -> {
  where(foo: "bar")
}

テストケースの隙間をぬったリグレッションだったので、不足していたテストケースを追加している (フィードバックのコードに基づいていたものの、例が default_scope というのもあれなのだった) 。

whois agilemanifesto.org

アジャイルソフトウェア開発は、アジャイルマニフェストに始まりアジャイルマニフェストに終わるという個人の見解は置いておいて、ふと関心を持って見てみた。

% whois agilemanifesto.org

Registrant Name に燦然と輝く名前はぜひ自身で確認してもらいたい。