class RuboCop::Cop::Lint::RedundantCopDisableDirective
Detects instances of rubocop:disable comments that can be removed without causing any offenses to be reported. It's implemented as a cop in that it inherits from the Cop
base class and calls add_offense. The unusual part of its implementation is that it doesn't have any on_* methods or an investigate method. This means that it doesn't take part in the investigation phase when the other cops do their work. Instead, it waits until it's called in a later stage of the execution. The reason it can't be implemented as a normal cop is that it depends on the results of all other cops to do its work.
@example
# bad # rubocop:disable Layout/LineLength x += 1 # rubocop:enable Layout/LineLength # good x += 1
Constants
- COP_NAME
- DEPARTMENT_MARKER
Attributes
offenses_to_check[RW]
Public Class Methods
new(config = nil, options = nil, offenses = nil)
click to toggle source
Calls superclass method
RuboCop::Cop::Base::new
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 37 def initialize(config = nil, options = nil, offenses = nil) @offenses_to_check = offenses super(config, options) end
Public Instance Methods
on_new_investigation()
click to toggle source
Calls superclass method
RuboCop::Cop::Base#on_new_investigation
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 42 def on_new_investigation return unless offenses_to_check redundant_cops = Hash.new { |h, k| h[k] = Set.new } each_redundant_disable do |comment, redundant_cop| redundant_cops[comment].add(redundant_cop) end add_offenses(redundant_cops) super end
Private Instance Methods
add_department_marker(department)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 294 def add_department_marker(department) DEPARTMENT_MARKER + department end
add_offense_for_entire_comment(comment, cops)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 206 def add_offense_for_entire_comment(comment, cops) location = DirectiveComment.new(comment).range cop_names = cops.sort.map { |c| describe(c) }.join(', ') add_offense(location, message: message(cop_names)) do |corrector| range = comment_range_with_surrounding_space(location, comment.loc.expression) if leave_free_comment?(comment, range) corrector.replace(range, ' # ') else corrector.remove(range) end end end
add_offense_for_some_cops(comment, cops)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 221 def add_offense_for_some_cops(comment, cops) cop_ranges = cops.map { |c| [c, cop_range(comment, c)] } cop_ranges.sort_by! { |_, r| r.begin_pos } ranges = cop_ranges.map { |_, r| r } cop_ranges.each do |cop, range| cop_name = describe(cop) add_offense(range, message: message(cop_name)) do |corrector| range = directive_range_in_list(range, ranges) corrector.remove(range) end end end
add_offenses(redundant_cops)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 196 def add_offenses(redundant_cops) redundant_cops.each do |comment, cops| if all_disabled?(comment) || directive_count(comment) == cops.size add_offense_for_entire_comment(comment, cops) else add_offense_for_some_cops(comment, cops) end end end
all_cop_names()
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 277 def all_cop_names @all_cop_names ||= Registry.global.names end
all_disabled?(comment)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 177 def all_disabled?(comment) DirectiveComment.new(comment).disabled_all? end
comment_range_with_surrounding_space(directive_comment_range, line_comment_range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 69 def comment_range_with_surrounding_space(directive_comment_range, line_comment_range) if previous_line_blank?(directive_comment_range) && processed_source.comment_config.comment_only_line?(directive_comment_range.line) && directive_comment_range.begin_pos == line_comment_range.begin_pos # When the previous line is blank, it should be retained range_with_surrounding_space(directive_comment_range, side: :right) else # Eat the entire comment, the preceding space, and the preceding # newline if there is one. original_begin = directive_comment_range.begin_pos range = range_with_surrounding_space( directive_comment_range, side: :left, newlines: true ) range_with_surrounding_space(range, side: :right, # Special for a comment that # begins the file: remove # the newline at the end. newlines: original_begin.zero?) end end
cop_disabled_line_ranges()
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 57 def cop_disabled_line_ranges processed_source.disabled_line_ranges end
cop_range(comment, cop)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 241 def cop_range(comment, cop) cop = remove_department_marker(cop) matching_range(comment.loc.expression, cop) || matching_range(comment.loc.expression, Badge.parse(cop).cop_name) || raise("Couldn't find #{cop} in comment: #{comment.text}") end
department_disabled?(cop, comment)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 187 def department_disabled?(cop, comment) directive = DirectiveComment.new(comment) directive.in_directive_department?(cop) && !directive.overridden_by_department?(cop) end
department_marker?(department)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 286 def department_marker?(department) department.start_with?(DEPARTMENT_MARKER) end
describe(cop)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 264 def describe(cop) return 'all cops' if cop == 'all' return "`#{remove_department_marker(cop)}` department" if department_marker?(cop) return "`#{cop}`" if all_cop_names.include?(cop) similar = NameSimilarity.find_similar_name(cop, all_cop_names) similar ? "`#{cop}` (did you mean `#{similar}`?)" : "`#{cop}` (unknown cop)" end
directive_count(comment)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 192 def directive_count(comment) DirectiveComment.new(comment).directive_count end
directive_range_in_list(range, ranges)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 92 def directive_range_in_list(range, ranges) # Is there any cop between this one and the end of the line, which # is NOT being removed? if ends_its_line?(ranges.last) && trailing_range?(ranges, range) # Eat the comma on the left. range = range_with_surrounding_space(range, side: :left) range = range_with_surrounding_comma(range, :left) end range = range_with_surrounding_comma(range, :right) # Eat following spaces up to EOL, but not the newline itself. range_with_surrounding_space(range, side: :right, newlines: false) end
disabled_ranges()
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 61 def disabled_ranges cop_disabled_line_ranges[COP_NAME] || [0..0] end
each_already_disabled(cop, line_ranges) { |comment, cop| ... }
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 130 def each_already_disabled(cop, line_ranges) line_ranges.each_cons(2) do |previous_range, range| next if ignore_offense?(range) next unless followed_ranges?(previous_range, range) # If a cop is disabled in a range that begins on the same line as # the end of the previous range, it means that the cop was # already disabled by an earlier comment. So it's redundant # whether there are offenses or not. comment = processed_source.comment_at_line(range.begin) # Comments disabling all cops don't count since it's reasonable # to disable a few select cops first and then all cops further # down in the code. yield comment, cop if comment && !all_disabled?(comment) end end
each_line_range(cop, line_ranges) { |comment, redundant| ... }
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 113 def each_line_range(cop, line_ranges) line_ranges.each_with_index do |line_range, line_range_index| next if ignore_offense?(line_range) comment = processed_source.comment_at_line(line_range.begin) redundant = if all_disabled?(comment) find_redundant_all(line_range, line_ranges[line_range_index + 1]) elsif department_disabled?(cop, comment) find_redundant_department(cop, line_range) else find_redundant_cop(cop, line_range) end yield comment, redundant if redundant end end
each_redundant_disable(&block)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 106 def each_redundant_disable(&block) cop_disabled_line_ranges.each do |cop, line_ranges| each_already_disabled(cop, line_ranges, &block) each_line_range(cop, line_ranges, &block) end end
ends_its_line?(range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 281 def ends_its_line?(range) line = range.source_buffer.source_line(range.last_line) (line =~ /\s*\z/) == range.last_column end
find_redundant_all(range, next_range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 153 def find_redundant_all(range, next_range) # If there's a disable all comment followed by a comment # specifically disabling `cop`, we don't report the `all` # comment. If the disable all comment is truly redundant, we will # detect that when examining the comments of another cop, and we # get the full line range for the disable all. has_no_next_range = next_range.nil? || !followed_ranges?(range, next_range) 'all' if has_no_next_range && range_with_offense?(range) end
find_redundant_cop(cop, range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 148 def find_redundant_cop(cop, range) cop_offenses = offenses_to_check.select { |offense| offense.cop_name == cop } cop if range_with_offense?(range, cop_offenses) end
find_redundant_department(cop, range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 163 def find_redundant_department(cop, range) department = cop.split('/').first offenses = offenses_to_check.select { |offense| offense.cop_name.start_with?(department) } add_department_marker(department) if range_with_offense?(range, offenses) end
followed_ranges?(range, next_range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 169 def followed_ranges?(range, next_range) range.end == next_range.begin end
ignore_offense?(line_range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 181 def ignore_offense?(line_range) disabled_ranges.any? do |range| range.cover?(line_range.min) && range.cover?(line_range.max) end end
leave_free_comment?(comment, range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 235 def leave_free_comment?(comment, range) free_comment = comment.text.gsub(range.source.strip, '') !free_comment.empty? && !free_comment.start_with?('#') end
matching_range(haystack, needle)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 248 def matching_range(haystack, needle) offset = haystack.source.index(needle) return unless offset offset += haystack.begin_pos Parser::Source::Range.new(haystack.source_buffer, offset, offset + needle.size) end
message(cop_names)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 273 def message(cop_names) "Unnecessary disabling of #{cop_names}." end
previous_line_blank?(range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 65 def previous_line_blank?(range) processed_source.buffer.source_line(range.line - 1).blank? end
range_with_offense?(range, offenses = offenses_to_check)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 173 def range_with_offense?(range, offenses = offenses_to_check) offenses.none? { |offense| range.cover?(offense.line) } end
remove_department_marker(department)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 290 def remove_department_marker(department) department.gsub(DEPARTMENT_MARKER, '') end
trailing_range?(ranges, range)
click to toggle source
# File lib/rubocop/cop/lint/redundant_cop_disable_directive.rb, line 256 def trailing_range?(ranges, range) ranges .drop_while { |r| !r.equal?(range) } .each_cons(2) .map { |range1, range2| range1.end.join(range2.begin).source } .all? { |intervening| /\A\s*,\s*\Z/.match?(intervening) } end