`Layout/SpaceInsideReferenceBrackets` cop の false negative を直した

レポートが分かりやすかったので着手していた。問題解決には Token まわりのコードを見るのに骨があった。

github.com

問題としては以下のようにネストした reference brackets の外側の括弧に対して期待した offense が出ないというもの。

record[ options[:attribute] ]

AST へのイベント処理としては先に record[ が処理されてから options[ が処理されるので、開き括弧 [ (left_ref_bracket) の前のトークンが閉じ括弧 ] (right_bracket) でないときは、トークンを reverse させないのがパッチの肝。このケースでも常に reverse していたので、内側の options[ の括弧を常に参照していたのが問題の原因だった。

逆に [ の前の token] はどういったものかというと o[:foo][:bar] のケースで、そういった場合とは処理を振り分けておく必要があったというものだった。

@@ -111,10 +111,14 @@ def bracket_method?(node)
         end
 
         def left_ref_bracket(node, tokens)
-          if node.method?(:[]=)
+          current_token = tokens.reverse.find(&:left_ref_bracket?)
+          previous_token = previous_token(current_token)
+
+          if node.method?(:[]=) ||
+             previous_token && !previous_token.right_bracket?
             tokens.find(&:left_ref_bracket?)
           else
-            tokens.reverse.find(&:left_ref_bracket?)
+            current_token
           end
         end
 
@@ -131,6 +135,11 @@ def closing_bracket(tokens, opening_bracket)
           end
         end
 
+        def previous_token(current_token)
+          index = processed_source.tokens.index(current_token)
+          index.nil? || index.zero? ? nil : processed_source.tokens[index - 1]
+        end
+
         def empty_config
           cop_config['EnforcedStyleForEmptyBrackets']
         end