From b307d03c6c6d3b9e1cef0b6139b49eac9e22d299 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Wed, 8 Jul 2020 15:50:32 +0300 Subject: [PATCH] Add new `Style/HashAsLastArrayItem` cop --- CHANGELOG.md | 1 + config/default.yml | 12 ++++ docs/modules/ROOT/pages/cops.adoc | 1 + docs/modules/ROOT/pages/cops_style.adoc | 53 ++++++++++++++++ lib/rubocop.rb | 1 + .../cop/style/hash_as_last_array_item.rb | 62 ++++++++++++++++++ .../cop/style/hash_as_last_array_item_spec.rb | 63 +++++++++++++++++++ 7 files changed, 193 insertions(+) create mode 100644 lib/rubocop/cop/style/hash_as_last_array_item.rb create mode 100644 spec/rubocop/cop/style/hash_as_last_array_item_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d197089735..df1850f65ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### New features * [#8242](https://github.com/rubocop-hq/rubocop/pull/8242): Internal profiling available with `bin/rubocop-profile` and rake tasks. ([@marcandre][]) +* [#4286](https://github.com/rubocop-hq/rubocop/issues/4286): Add new `Style/HashAsLastArrayItem` cop. ([@fatkodima][]) ### Bug fixes diff --git a/config/default.yml b/config/default.yml index c08228f3652..efd3d3ca37a 100644 --- a/config/default.yml +++ b/config/default.yml @@ -2961,6 +2961,18 @@ Style/GuardClause: # needs to have to trigger this cop MinBodyLength: 1 +Style/HashAsLastArrayItem: + Description: >- + Checks for presence or absence of braces around hash literal as a last + array item depending on configuration. + StyleGuide: '#hash-literal-as-last-array-item' + Enabled: 'pending' + VersionAdded: '0.88' + EnforcedStyle: braces + SupportedStyles: + - braces + - no_braces + Style/HashEachMethods: Description: 'Use Hash#each_key and Hash#each_value.' StyleGuide: '#hash-each' diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 08a7310745d..d1ea7a8b604 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -369,6 +369,7 @@ In the following section you find all available cops: * xref:cops_style.adoc#stylefrozenstringliteralcomment[Style/FrozenStringLiteralComment] * xref:cops_style.adoc#styleglobalvars[Style/GlobalVars] * xref:cops_style.adoc#styleguardclause[Style/GuardClause] +* xref:cops_style.adoc#stylehashaslastarrayitem[Style/HashAsLastArrayItem] * xref:cops_style.adoc#stylehasheachmethods[Style/HashEachMethods] * xref:cops_style.adoc#stylehashsyntax[Style/HashSyntax] * xref:cops_style.adoc#stylehashtransformkeys[Style/HashTransformKeys] diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index dd3fe61c0df..b651f1e2161 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -3354,6 +3354,59 @@ ok * https://rubystyle.guide#no-nested-conditionals +== Style/HashAsLastArrayItem + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Pending +| Yes +| Yes +| 0.88 +| - +|=== + +Checks for presence or absence of braces around hash literal as a last +array item depending on configuration. + +=== Examples + +==== EnforcedStyle: braces (default) + +[source,ruby] +---- +# bad +[1, 2, one: 1, two: 2] + +# good +[1, 2, { one: 1, two: 2 }] +---- + +==== EnforcedStyle: no_braces + +[source,ruby] +---- +# bad +[1, 2, { one: 1, two: 2 }] + +# good +[1, 2, one: 1, two: 2] +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| EnforcedStyle +| `braces` +| `braces`, `no_braces` +|=== + +=== References + +* https://rubystyle.guide#hash-literal-as-last-array-item + == Style/HashEachMethods |=== diff --git a/lib/rubocop.rb b/lib/rubocop.rb index b273a8c6764..9a29023e217 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -413,6 +413,7 @@ require_relative 'rubocop/cop/style/frozen_string_literal_comment' require_relative 'rubocop/cop/style/global_vars' require_relative 'rubocop/cop/style/guard_clause' +require_relative 'rubocop/cop/style/hash_as_last_array_item' require_relative 'rubocop/cop/style/hash_each_methods' require_relative 'rubocop/cop/style/hash_syntax' require_relative 'rubocop/cop/style/hash_transform_keys' diff --git a/lib/rubocop/cop/style/hash_as_last_array_item.rb b/lib/rubocop/cop/style/hash_as_last_array_item.rb new file mode 100644 index 00000000000..e14339729aa --- /dev/null +++ b/lib/rubocop/cop/style/hash_as_last_array_item.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # Checks for presence or absence of braces around hash literal as a last + # array item depending on configuration. + # + # @example EnforcedStyle: braces (default) + # # bad + # [1, 2, one: 1, two: 2] + # + # # good + # [1, 2, { one: 1, two: 2 }] + # + # @example EnforcedStyle: no_braces + # # bad + # [1, 2, { one: 1, two: 2 }] + # + # # good + # [1, 2, one: 1, two: 2] + # + class HashAsLastArrayItem < Base + include ConfigurableEnforcedStyle + extend AutoCorrector + + def on_hash(node) + return unless node.parent&.array_type? + + if braces_style? + check_braces(node) + else + check_no_braces(node) + end + end + + private + + def check_braces(node) + return if node.braces? + + add_offense(node, message: 'Wrap hash in `{` and `}`.') do |corrector| + corrector.wrap(node, '{', '}') + end + end + + def check_no_braces(node) + return unless node.braces? + + add_offense(node, message: 'Omit the braces around the hash.') do |corrector| + corrector.remove(node.loc.begin) + corrector.remove(node.loc.end) + end + end + + def braces_style? + style == :braces + end + end + end + end +end diff --git a/spec/rubocop/cop/style/hash_as_last_array_item_spec.rb b/spec/rubocop/cop/style/hash_as_last_array_item_spec.rb new file mode 100644 index 00000000000..ddb329b4880 --- /dev/null +++ b/spec/rubocop/cop/style/hash_as_last_array_item_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Style::HashAsLastArrayItem, :config do + subject(:cop) { described_class.new(config) } + + context 'when EnforcedStyle is braces' do + let(:cop_config) do + { 'EnforcedStyle' => 'braces' } + end + + it 'registers an offense and corrects when hash without braces' do + expect_offense(<<~RUBY) + [1, 2, one: 1, two: 2] + ^^^^^^^^^^^^^^ Wrap hash in `{` and `}`. + RUBY + + expect_correction(<<~RUBY) + [1, 2, {one: 1, two: 2}] + RUBY + end + + it 'does not register an offense when hash with braces' do + expect_no_offenses(<<~RUBY) + [1, 2, { one: 1, two: 2 }] + RUBY + end + + it 'does not register an offense when hash is not inside array' do + expect_no_offenses(<<~RUBY) + foo(one: 1, two: 2) + RUBY + end + end + + context 'when EnforcedStyle is no_braces' do + let(:cop_config) do + { 'EnforcedStyle' => 'no_braces' } + end + + it 'registers an offense and corrects when hash with braces' do + expect_offense(<<~RUBY) + [1, 2, { one: 1, two: 2 }] + ^^^^^^^^^^^^^^^^^^ Omit the braces around the hash. + RUBY + + expect_correction(<<~RUBY) + [1, 2, one: 1, two: 2 ] + RUBY + end + + it 'does not register an offense when hash without braces' do + expect_no_offenses(<<~RUBY) + [1, 2, one: 1, two: 2] + RUBY + end + + it 'does not register an offense when hash is not inside array' do + expect_no_offenses(<<~RUBY) + foo({ one: 1, two: 2 }) + RUBY + end + end +end