コメント_2019-08-15_001845

RuboCopに出したPull Request #7272 について

きっかけ

ある日、有効にしたはずのないRuboCopのCopで違反が検知されていることに気づきました。原因は、以下スクショのように、EnabledにfalseではなくStringを渡していたためです。

画像1

自然言語的には正しいのですが(ん、本当にそうか?)、ここには true/false 以外を渡すべきではありません。

どこで検知するか

これは、そもそも Enabled という YAMLのkeyに対して true/false 以外の値を渡すべきではないので、どうにかそれを検知すればよいわけです。

ということで、RuboCopにおいて .rubocop.yml が読みこまれる過程をざっくりと追いかけます。

まず、 bin/exe/rubocop を見ると、RuboCop::CLI に処理の実体がありそうです。

その先、 rubocop/cli.rb 内で act_on_options という関数により、 ConfigStore を経由して ConfigLoader.load_file が呼ばれます。

その後、 load_yaml_configuration 内部でYAMLを読みこんでいます。ここに処理を挟んでみましょう。

どのように検知するか

.rubocop.yml は YAML.safe_load によってRubyのオブジェクトになります。この結果のHashを再帰的に掘っていき、 Enabled という key に対応する value が TrueClassでもFalseClassでもない場合に警告を発すればよさそうです。

という処理のために書いた、初期のコードがこれです。

def check_enabled_value(hash, parent=nil)
 hash.each do |key, value|
   check_enabled_value(value, key) if value.is_a?(Hash)
   if key == 'Enabled' && value.class == String
     warn Rainbow("Cop `#{parent}` is enabled by value '#{value}' specified. It may unexpected behavior unless set ture or false.").yellow
   end
 end
end

画像2

これによって、このように true/false でない値の場合に警告を出すようになります。

レビューによる仕様の変更

上記内容でPull Requestを作成したところ、bbatsov氏からレビューにてアドバイスを頂きました。そして最終的には以下のような仕様に変更を行いました。

・Enabled, Safe, SafeAutoCorrect, AutoCorrect の key について value を検証する

・true/false 以外の値が設定されていた場合には、エラーメッセージを出力して exit status 1 で終了する

def check_cop_config_value(hash, parent = nil)
 hash.each do |key, value|
   check_cop_config_value(value, key) if value.is_a?(Hash)
   next unless %w[Enabled
                  Safe
                  SafeAutoCorrect
                  AutoCorrect].include?(key) && value.is_a?(String)
   abort(
     "Property #{Rainbow(key).yellow} of cop #{Rainbow(parent).yellow}" \
     " is supposed to be a boolean and #{Rainbow(value).yellow} is not."
   )
 end
end

画像3

まとめ

そんなこんなで、僕のPull Requestが取り込まれたRuboCop v0.75.0 がリリースされました。


投げ銭していただけるととても嬉しいです