From 58c74cc36bd14f657c9341db20a2da0099ca0c93 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 15 Oct 2020 19:59:28 +0300 Subject: [PATCH] Add new `Lint/EmptyBlock` cop --- CHANGELOG.md | 4 ++ config/default.yml | 6 ++ docs/modules/ROOT/pages/cops.adoc | 1 + docs/modules/ROOT/pages/cops_lint.adoc | 61 ++++++++++++++++++ lib/rubocop.rb | 1 + .../cli/command/auto_genenerate_config.rb | 2 +- lib/rubocop/cop/corrector.rb | 2 +- lib/rubocop/cop/lint/empty_block.rb | 46 ++++++++++++++ spec/rubocop/cop/lint/empty_block_spec.rb | 63 +++++++++++++++++++ 9 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 lib/rubocop/cop/lint/empty_block.rb create mode 100644 spec/rubocop/cop/lint/empty_block_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 02cabcabf17..1abf666ea36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## master (unreleased) +### New features + +* [#8895](https://github.com/rubocop-hq/rubocop/pull/8895): Add new `Lint/EmptyBlock` cop. ([@fatkodima][]) + ## 1.0.0 (2020-10-21) ### New features diff --git a/config/default.yml b/config/default.yml index 3262d9ccc18..0a20668e99b 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1474,6 +1474,12 @@ Lint/ElseLayout: Enabled: true VersionAdded: '0.17' +Lint/EmptyBlock: + Description: 'This cop checks for blocks without a body.' + Enabled: pending + VersionAdded: '1.1' + AllowComments: true + Lint/EmptyConditionalBody: Description: 'This cop checks for the presence of `if`, `elsif` and `unless` branches without a body.' Enabled: true diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 80a3f3de169..a32e3621b4f 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -204,6 +204,7 @@ In the following section you find all available cops: * xref:cops_lint.adoc#lintduplicaterescueexception[Lint/DuplicateRescueException] * xref:cops_lint.adoc#linteachwithobjectargument[Lint/EachWithObjectArgument] * xref:cops_lint.adoc#lintelselayout[Lint/ElseLayout] +* xref:cops_lint.adoc#lintemptyblock[Lint/EmptyBlock] * xref:cops_lint.adoc#lintemptyconditionalbody[Lint/EmptyConditionalBody] * xref:cops_lint.adoc#lintemptyensure[Lint/EmptyEnsure] * xref:cops_lint.adoc#lintemptyexpression[Lint/EmptyExpression] diff --git a/docs/modules/ROOT/pages/cops_lint.adoc b/docs/modules/ROOT/pages/cops_lint.adoc index 9ce1ac18826..f7c34a760ca 100644 --- a/docs/modules/ROOT/pages/cops_lint.adoc +++ b/docs/modules/ROOT/pages/cops_lint.adoc @@ -994,6 +994,67 @@ else end ---- +== Lint/EmptyBlock + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Pending +| Yes +| No +| 1.1 +| - +|=== + +This cop checks for blocks without a body. +Such empty blocks are typically an oversight or we should provide a comment +be clearer what we're aiming for. + +=== Examples + +[source,ruby] +---- +# bad +items.each { |item| } + +# good +items.each { |item| puts item } +---- + +==== AllowComments: true (default) + +[source,ruby] +---- +# good +items.each do |item| + # TODO: implement later (inner comment) +end + +items.each { |item| } # TODO: implement later (inline comment) +---- + +==== AllowComments: false + +[source,ruby] +---- +# bad +items.each do |item| + # TODO: implement later (inner comment) +end + +items.each { |item| } # TODO: implement later (inline comment) +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| AllowComments +| `true` +| Boolean +|=== + == Lint/EmptyConditionalBody |=== diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 0e02661ade2..f0b428fba20 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -266,6 +266,7 @@ require_relative 'rubocop/cop/lint/duplicate_rescue_exception' require_relative 'rubocop/cop/lint/each_with_object_argument' require_relative 'rubocop/cop/lint/else_layout' +require_relative 'rubocop/cop/lint/empty_block' require_relative 'rubocop/cop/lint/empty_conditional_body' require_relative 'rubocop/cop/lint/empty_ensure' require_relative 'rubocop/cop/lint/empty_expression' diff --git a/lib/rubocop/cli/command/auto_genenerate_config.rb b/lib/rubocop/cli/command/auto_genenerate_config.rb index 5dd87a554f0..aa60e02043d 100644 --- a/lib/rubocop/cli/command/auto_genenerate_config.rb +++ b/lib/rubocop/cli/command/auto_genenerate_config.rb @@ -90,7 +90,7 @@ def run_all_cops(line_length_contents) def reset_config_and_auto_gen_file @config_store = ConfigStore.new @config_store.options_config = @options[:config] if @options[:config] - File.open(AUTO_GENERATED_FILE, 'w') {} + File.open(AUTO_GENERATED_FILE, 'w') {} # create or truncate if exists add_inheritance_from_auto_generated_file(@options[:config]) end diff --git a/lib/rubocop/cop/corrector.rb b/lib/rubocop/cop/corrector.rb index a8ca6a9b2e0..b2d1c4c0f3c 100644 --- a/lib/rubocop/cop/corrector.rb +++ b/lib/rubocop/cop/corrector.rb @@ -23,7 +23,7 @@ def initialize(source) ) # Don't print warnings to stderr if corrections conflict with each other - diagnostics.consumer = ->(diagnostic) {} + diagnostics.consumer = ->(diagnostic) {} # noop end alias rewrite process # Legacy diff --git a/lib/rubocop/cop/lint/empty_block.rb b/lib/rubocop/cop/lint/empty_block.rb new file mode 100644 index 00000000000..89bffd32db3 --- /dev/null +++ b/lib/rubocop/cop/lint/empty_block.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Lint + # This cop checks for blocks without a body. + # Such empty blocks are typically an oversight or we should provide a comment + # be clearer what we're aiming for. + # + # @example + # # bad + # items.each { |item| } + # + # # good + # items.each { |item| puts item } + # + # @example AllowComments: true (default) + # # good + # items.each do |item| + # # TODO: implement later (inner comment) + # end + # + # items.each { |item| } # TODO: implement later (inline comment) + # + # @example AllowComments: false + # # bad + # items.each do |item| + # # TODO: implement later (inner comment) + # end + # + # items.each { |item| } # TODO: implement later (inline comment) + # + class EmptyBlock < Base + MSG = 'Empty block detected.' + + def on_block(node) + return if node.body + return if cop_config['AllowComments'] && + processed_source.contains_comment?(node.source_range) + + add_offense(node) + end + end + end + end +end diff --git a/spec/rubocop/cop/lint/empty_block_spec.rb b/spec/rubocop/cop/lint/empty_block_spec.rb new file mode 100644 index 00000000000..4e9c37d4ac0 --- /dev/null +++ b/spec/rubocop/cop/lint/empty_block_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Lint::EmptyBlock, :config do + let(:cop_config) do + { 'AllowComments' => true } + end + + it 'registers an offense for empty block within method call' do + expect_offense(<<~RUBY) + items.each { |item| } + ^^^^^^^^^^^^^^^^^^^^^ Empty block detected. + RUBY + end + + it 'registers an offense for empty block within lambda' do + expect_offense(<<~RUBY) + lambda { |item| } + ^^^^^^^^^^^^^^^^^ Empty block detected. + RUBY + end + + it 'does not register an offense for empty block with inner comments' do + expect_no_offenses(<<~RUBY) + items.each do |item| + # TODO: implement later + end + RUBY + end + + it 'does not register an offense for empty block with inline comments' do + expect_no_offenses(<<~RUBY) + items.each { |item| } # TODO: implement later + RUBY + end + + it 'does not register an offense when block is not empty' do + expect_no_offenses(<<~RUBY) + items.each { |item| puts item } + RUBY + end + + context 'when AllowComments is false' do + let(:cop_config) do + { 'AllowComments' => false } + end + + it 'registers an offense for empty block with inner comments' do + expect_offense(<<~RUBY) + items.each do |item| + ^^^^^^^^^^^^^^^^^^^^ Empty block detected. + # TODO: implement later + end + RUBY + end + + it 'registers an offense for empty block with inline comments' do + expect_offense(<<~RUBY) + items.each { |item| } # TODO: implement later + ^^^^^^^^^^^^^^^^^^^^^ Empty block detected. + RUBY + end + end +end