`Layout/EmptyLinesAroundAccessModifier` で発生する RuboCop の Infinite loop エラーを直した

Layout/EmptyLinesAroundAccessModifier で発生する RuboCop の Infinite loop エラーを直した。

github.com

rubocop-hq/rubocop#6507 で直した問題の裏で起きていたリグレッションだった。

Layout/EmptyLinesAroundAccessModifierLayout/EmptyLinesAroundClassBody の auto-correct がカチあって無限ループになる。

以下のようなコードがあるとして Layout/EmptyLinesAroundAccessModifier による auto-correct をする。

% cat example.rb
class SomeController < SomeOtherController
  def index; end

  private
end

% rubocop example.rb --only Layout/EmptyLinesAroundAccessModifier -a
Inspecting 1 file
C

Offenses:

example.rb:4:3: C: [Corrected] Layout/EmptyLinesAroundAccessModifier:
Keep a blank line before and after private.
  private
  ^^^^^^^

1 file inspected, 1 offense detected, 1 offense corrected

以下のように private 修飾子の後ろに改行を入れる。これは false positive に起因しており end の前に private が置かれている場合は空行は無視されるのが期待したものだった。

 % cat example.rb
 class SomeController < SomeOtherController
   def index; end

   private
+
 end

この状態で Layout/EmptyLinesAroundClassBody での auto-correct をする。

% rubocop example.rb --only Layout/EmptyLinesAroundClassBody -a
Inspecting 1 file
C

Offenses:

example.rb:5:1: C: [Corrected] Layout/EmptyLinesAroundClassBody: Extra
empty line detected at class body end.

1 file inspected, 1 offense detected, 1 offense corrected

今度は private の後ろの空行を削除する。

 % cat example.rb
 class SomeController < SomeOtherController
   def index; end

   private
-
 end

このように空行を入れたり消したりという無限ループになっていた。

問題はスーパークラスがある場合にスーパークラスに対応する最終行の位置をとっていたことだった。これは冒頭でエンバグを入れた以下のようにスーパークラスの指定前で改行するケースへのパッチでの解決がよくなかった。

class SomeController <
         SomeOtherController
  def index; end

  private
end

その解決として、class 宣言に対応する end の位置となる最終行を個別にとっておいて参照するようにしたのが本パッチだった。