From bdef4c453087ba880876febb37aef98abb7ea464 Mon Sep 17 00:00:00 2001 From: Ryo Nakamura Date: Fri, 29 Jul 2022 07:49:25 +0900 Subject: [PATCH] Add `Rails/TopLevelHashWithIndifferentAccess` cop --- ..._top_level_hash_with_indifferent_access.md | 1 + config/default.yml | 5 ++ .../top_level_hash_with_indifferent_access.rb | 51 +++++++++++++++++++ lib/rubocop/cop/rails_cops.rb | 1 + ...level_hash_with_indifferent_access_spec.rb | 45 ++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 changelog/new_add_rails_top_level_hash_with_indifferent_access.md create mode 100644 lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb create mode 100644 spec/rubocop/cop/rails/top_level_hash_with_indifferent_access_spec.rb diff --git a/changelog/new_add_rails_top_level_hash_with_indifferent_access.md b/changelog/new_add_rails_top_level_hash_with_indifferent_access.md new file mode 100644 index 0000000000..cbbc5be8c6 --- /dev/null +++ b/changelog/new_add_rails_top_level_hash_with_indifferent_access.md @@ -0,0 +1 @@ +* [#751](https://github.com/rubocop/rubocop-rails/pull/751): Add `Rails/TopLevelHashWithIndifferentAccess` cop. ([@r7kamura][]) diff --git a/config/default.yml b/config/default.yml index b693a86b67..066980353a 100644 --- a/config/default.yml +++ b/config/default.yml @@ -958,6 +958,11 @@ Rails/ToSWithArgument: Safe: false VersionAdded: '<>' +Rails/TopLevelHashWithIndifferentAccess: + Description: 'Identifies top-level `HashWithIndifferentAccess`.' + Enabled: pending + VersionAdded: '<>' + Rails/TransactionExitStatement: Description: 'Avoid the usage of `return`, `break` and `throw` in transaction blocks.' Enabled: pending diff --git a/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb b/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb new file mode 100644 index 0000000000..1e026d8ffd --- /dev/null +++ b/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Rails + # Identifies top-level `HashWithIndifferentAccess`. + # This has been soft-deprecated since Rails 5.1. + # + # @example + # # bad + # HashWithIndifferentAccess.new(foo: 'bar') + # + # # good + # ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar') + # + class TopLevelHashWithIndifferentAccess < Base + extend AutoCorrector + extend TargetRailsVersion + + minimum_target_rails_version 5.1 + + MSG = 'Avoid top-level `HashWithIndifferentAccess`.' + + # @!method top_level_hash_with_indifferent_access?(node) + # @param [RuboCop::AST::ConstNode] node + # @return [Boolean] + def_node_matcher :top_level_hash_with_indifferent_access?, <<~PATTERN + (const {nil? cbase} :HashWithIndifferentAccess) + PATTERN + + # @param [RuboCop::AST::ConstNode] node + def on_const(node) + return unless top_level_hash_with_indifferent_access?(node) + + add_offense(node) do |corrector| + autocorrect(corrector, node) + end + end + + private + + def autocorrect(corrector, node) + corrector.insert_before( + node.location.name, + 'ActiveSupport::' + ) + end + end + end + end +end diff --git a/lib/rubocop/cop/rails_cops.rb b/lib/rubocop/cop/rails_cops.rb index bfaedc72ee..14dcb13b30 100644 --- a/lib/rubocop/cop/rails_cops.rb +++ b/lib/rubocop/cop/rails_cops.rb @@ -114,6 +114,7 @@ require_relative 'rails/time_zone_assignment' require_relative 'rails/to_formatted_s' require_relative 'rails/to_s_with_argument' +require_relative 'rails/top_level_hash_with_indifferent_access' require_relative 'rails/transaction_exit_statement' require_relative 'rails/uniq_before_pluck' require_relative 'rails/unique_validation_without_index' diff --git a/spec/rubocop/cop/rails/top_level_hash_with_indifferent_access_spec.rb b/spec/rubocop/cop/rails/top_level_hash_with_indifferent_access_spec.rb new file mode 100644 index 0000000000..d335a7d68a --- /dev/null +++ b/spec/rubocop/cop/rails/top_level_hash_with_indifferent_access_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Rails::TopLevelHashWithIndifferentAccess, :config, :rails51 do + context 'with top-level HashWithIndifferentAccess' do + it 'registers and corrects an offense' do + expect_offense(<<~RUBY) + HashWithIndifferentAccess.new(foo: 'bar') + ^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid top-level `HashWithIndifferentAccess`. + RUBY + + expect_correction(<<~RUBY) + ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar') + RUBY + end + end + + context 'with top-level ::HashWithIndifferentAccess' do + it 'registers and corrects an offense' do + expect_offense(<<~RUBY) + ::HashWithIndifferentAccess.new(foo: 'bar') + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid top-level `HashWithIndifferentAccess`. + RUBY + + expect_correction(<<~RUBY) + ::ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar') + RUBY + end + end + + context 'with ActiveSupport::HashWithIndifferentAccess' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar') + RUBY + end + end + + context 'with ActiveSupport::HashWithIndifferentAccess on Rails 5.0', :rails50 do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + HashWithIndifferentAccess.new(foo: 'bar') + RUBY + end + end +end