Skip to content

Commit

Permalink
[Fix rubocop#5356] Add new Lint/UnneededCopEnableDirective cop
Browse files Browse the repository at this point in the history
  • Loading branch information
garettarrowood committed Dec 31, 2017
1 parent 44c3ba0 commit f306205
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 2 deletions.
5 changes: 5 additions & 0 deletions config/enabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,11 @@ Lint/UnneededCopDisableDirective:
It must be explicitly disabled.
Enabled: true

Lint/UnneededCopEnableDirective:
Description: Checks for rubocop:enable comments that can be removed.

Enabled: true

Lint/UnneededRequireStatement:
Description: 'Checks for unnecessary `require` statement.'
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@
require_relative 'rubocop/cop/lint/underscore_prefixed_variable_name'
require_relative 'rubocop/cop/lint/unified_integer'
require_relative 'rubocop/cop/lint/unneeded_cop_disable_directive'
require_relative 'rubocop/cop/lint/unneeded_cop_enable_directive'
require_relative 'rubocop/cop/lint/unneeded_require_statement'
require_relative 'rubocop/cop/lint/unneeded_splat_expansion'
require_relative 'rubocop/cop/lint/unreachable_code'
Expand Down
21 changes: 21 additions & 0 deletions lib/rubocop/comment_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,29 @@ def cop_disabled_line_ranges
@cop_disabled_line_ranges ||= analyze
end

def extra_enabled_comments
extra_enabled_comments_with_names([], {})
end

private

def extra_enabled_comments_with_names(extras, names)
each_directive do |comment, cop_names, disabled|
next unless comment_only_line?(comment.loc.expression.line)
cop_names.each do |name|
names[name] ||= 0
if disabled
names[name] += 1
elsif names[name] > 0
names[name] -= 1
else
extras << [comment, name]
end
end
end
extras
end

def analyze
analyses = Hash.new { |hash, key| hash[key] = CopAnalysis.new([], nil) }

Expand Down
77 changes: 77 additions & 0 deletions lib/rubocop/cop/lint/unneeded_cop_enable_directive.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

# The Lint/UnneededCopEnableDirective cop needs to be disabled so as
# to be able to provide a (bad) example of an unneeded enable.

# rubocop:disable Lint/UnneededCopEnableDirective
module RuboCop
module Cop
module Lint
# This cop detects instances of rubocop:enable comments that can be
# removed.
#
# @example
# # bad
# foo = 1
# # rubocop:enable Metrics/LineLength
#
# # good
# foo = 1
class UnneededCopEnableDirective < Cop
include RangeHelp

MSG = 'Unnecessary enabling of %<cop>s.'.freeze

def investigate(processed_source)
return if processed_source.blank?
offenses = processed_source.comment_config.extra_enabled_comments
offenses.each do |comment, name|
add_offense(
[comment, name],
location: range_of_offense(comment, name),
message: format(MSG, cop: name)
)
end
end

def autocorrect(comment_and_name)
lambda do |corrector|
comment, name = *comment_and_name
range = range_of_offense(*comment_and_name)
index = cop_name_indention(comment, name)
make_corrections(corrector, comment, name, range, index)
end
end

private

def range_of_offense(comment, name)
comment_start = comment.loc.expression.begin_pos
offense_start = comment_start + cop_name_indention(comment, name)
range_between(offense_start, offense_start + name.size)
end

def cop_name_indention(comment, name)
comment.text.index(name)
end

# rubocop:disable Metrics/AbcSize
def make_corrections(corrector, comment, name, range, index)
if comment.text[index - 2] == ','
corrector.remove(
range_between(range.begin_pos - 2, range.end_pos)
)
elsif comment.text[index + name.size] == ','
corrector.remove(
range_between(range.begin_pos, range.end_pos + 2)
)
else
corrector.remove(comment.loc.expression)
end
end
# rubocop:enable Metrics/AbcSize
end
end
end
end
# rubocop:enable Lint/UnneededCopEnableDirective
5 changes: 3 additions & 2 deletions spec/rubocop/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -302,9 +302,10 @@ def and_with_args
.to eq(['example.rb: Style/LineLength has the wrong ' \
'namespace - should be Metrics',
''].join("\n"))
# 3 cops were disabled, then 2 were enabled again, so we
# should get 2 offenses reported.
# 2 real cops were disabled, and 1 that was incorrect
# 2 real cops was enabled, but only 1 had been disabled correctly
expect($stdout.string).to eq(<<-RESULT.strip_indent)
#{abs('example.rb')}:6:21: W: Lint/UnneededCopEnableDirective: Unnecessary enabling of Metrics/LineLength.
#{abs('example.rb')}:7:81: C: Metrics/LineLength: Line is too long. [95/80]
#{abs('example.rb')}:9:5: C: Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
RESULT
Expand Down
109 changes: 109 additions & 0 deletions spec/rubocop/cop/lint/unneeded_cop_enable_directive_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Lint::UnneededCopEnableDirective do
subject(:cop) { described_class.new }

it 'registers offense for unnecessary enable' do
expect_offense(<<-RUBY.strip_indent)
foo
# rubocop:enable Metrics/LineLength
^^^^^^^^^^^^^^^^^^ Unnecessary enabling of Metrics/LineLength.
RUBY
end

it 'registers multiple offenses for same comment' do
expect_offense(<<-RUBY.strip_indent)
foo
# rubocop:enable Metrics/ModuleLength, Metrics/AbcSize
^^^^^^^^^^^^^^^ Unnecessary enabling of Metrics/AbcSize.
^^^^^^^^^^^^^^^^^^^^ Unnecessary enabling of Metrics/ModuleLength.
bar
RUBY
end

it 'registers correct offense when combined with necessary enable' do
expect_offense(<<-RUBY.strip_indent)
# rubocop:disable Metrics/LineLength
fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo = barrrrrrrrrrrrrrrrrrrrrrrrrr
# rubocop:enable Metrics/AbcSize, Metrics/LineLength
^^^^^^^^^^^^^^^ Unnecessary enabling of Metrics/AbcSize.
bar
RUBY
end

context 'autocorrection' do
context 'when entire comment unnecessarily enables' do
let(:source) do
<<-RUBY.strip_indent
foo
# rubocop:enable Metrics/LineLength
RUBY
end

it 'removes unnecessary enables' do
corrected = autocorrect_source(source)
expect(corrected).to eq(<<-RUBY.strip_indent)
foo
RUBY
end
end

context 'when first cop unnecessarily enables' do
let(:source) do
<<-RUBY.strip_indent
# rubocop:disable Metrics/LineLength
foo
# rubocop:enable Metrics/AbcSize, Metrics/LineLength
RUBY
end

it 'removes unnecessary enables' do
corrected = autocorrect_source(source)
expect(corrected).to eq(<<-RUBY.strip_indent)
# rubocop:disable Metrics/LineLength
foo
# rubocop:enable Metrics/LineLength
RUBY
end
end

context 'when last cop unnecessarily enables' do
let(:source) do
<<-RUBY.strip_indent
# rubocop:disable Metrics/LineLength
foo
# rubocop:enable Metrics/LineLength, Metrics/AbcSize
RUBY
end

it 'removes unnecessary enables' do
corrected = autocorrect_source(source)
expect(corrected).to eq(<<-RUBY.strip_indent)
# rubocop:disable Metrics/LineLength
foo
# rubocop:enable Metrics/LineLength
RUBY
end
end

context 'when middle cop unnecessarily enables' do
let(:source) do
<<-RUBY.strip_indent
# rubocop:disable Metrics/LineLength, Lint/Debugger
foo
# rubocop:enable Metrics/LineLength, Metrics/AbcSize, Lint/Debugger
RUBY
end

it 'removes unnecessary enables' do
corrected = autocorrect_source(source)
expect(corrected).to eq(<<-RUBY.strip_indent)
# rubocop:disable Metrics/LineLength, Lint/Debugger
foo
# rubocop:enable Metrics/LineLength, Lint/Debugger
RUBY
end
end
end
end

0 comments on commit f306205

Please sign in to comment.