Class: RuboCop::Cop::Lint::RedundantCopDisableDirective
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
::RuboCop::Cop::AutoCorrector ,
::RuboCop::Cop::Base ,
::RuboCop::ExcludeLimit ,
NodePattern::Macros,
RuboCop::AST::Sexp
|
|
Instance Chain:
self,
::RuboCop::Cop::RangeHelp ,
::RuboCop::Cop::Base ,
::RuboCop::Cop::AutocorrectLogic ,
::RuboCop::Cop::IgnoredNode ,
::RuboCop::Util ,
RuboCop::AST::Sexp
|
|
Inherits: |
RuboCop::Cop::Base
|
Defined in: | lib/rubocop/cop/lint/redundant_cop_disable_directive.rb |
Overview
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.
Constant Summary
-
COP_NAME =
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 32'Lint/RedundantCopDisableDirective'
-
DEPARTMENT_MARKER =
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 33'DEPARTMENT'
-
SIMILAR_COP_NAMES_CACHE =
private
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 287Hash.new do |hash, cop_name| hash[:all_cop_names] = Registry.global.names unless hash.key?(:all_cop_names) hash[cop_name] = NameSimilarity.find_similar_name(cop_name, hash[:all_cop_names]) end
::RuboCop::Cop::Base
- Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::RangeHelp
- Included
Class Attribute Summary
::RuboCop::Cop::AutoCorrector
- Extended
::RuboCop::Cop::Base
- Inherited
.gem_requirements, .lint?, | |
.support_autocorrect? | Returns if class supports autocorrect. |
.support_multiple_source? | Override if your cop should be called repeatedly for multiple investigations Between calls to #on_new_investigation and |
Class Method Summary
::RuboCop::Cop::Base
- Inherited
.autocorrect_incompatible_with | List of cops that should not try to autocorrect at the same time as this cop. |
.badge | Naming. |
.callbacks_needed, .cop_name, .department, | |
.documentation_url | Returns a url to view this cops documentation online. |
.exclude_from_registry | Call for abstract Cop classes. |
.inherited, | |
.joining_forces | Override and return the Force class(es) you need to join. |
.match? | Returns true if the cop name or the cop namespace matches any of the given names. |
.new, | |
.requires_gem | Register a version requirement for the given gem name. |
.restrict_on_send |
::RuboCop::ExcludeLimit
- Extended
exclude_limit | Sets up a configuration option to have an exclude limit tracked. |
transform |
Instance Attribute Summary
::RuboCop::Cop::Base
- Inherited
::RuboCop::Cop::AutocorrectLogic
- Included
Instance Method Summary
- #on_new_investigation
- #add_department_marker(department) private
- #add_offense_for_entire_comment(comment, cops) private
- #add_offense_for_some_cops(comment, cops) private
- #add_offenses(redundant_cops) private
- #all_cop_names private
- #all_disabled?(comment) ⇒ Boolean private
- #comment_range_with_surrounding_space(directive_comment_range, line_comment_range) private
- #cop_disabled_line_ranges private
- #cop_range(comment, cop) private
- #department_disabled?(cop, comment) ⇒ Boolean private
- #department_marker?(department) ⇒ Boolean private
- #describe(cop) private
- #directive_count(comment) private
- #directive_range_in_list(range, ranges) private
- #disabled_ranges private
-
#each_already_disabled(cop, line_ranges)
private
Metrics/PerceivedComplexity.
- #each_line_range(cop, line_ranges) private
- #each_redundant_disable(&block) private
- #ends_its_line?(range) ⇒ Boolean private
- #expected_final_disable?(cop, line_range) ⇒ Boolean private
- #find_redundant_all(range, next_range) private
-
#find_redundant_cop(cop, range)
private
Metrics/PerceivedComplexity.
- #find_redundant_department(cop, range) private
- #followed_ranges?(range, next_range) ⇒ Boolean private
- #ignore_offense?(line_range) ⇒ Boolean private
- #leave_free_comment?(comment, range) ⇒ Boolean private
- #matching_range(haystack, needle) private
- #message(cop_names) private
- #previous_line_blank?(range) ⇒ Boolean private
- #range_with_offense?(range, offenses = offenses_to_check) ⇒ Boolean private
- #remove_department_marker(department) private
- #trailing_range?(ranges, range) ⇒ Boolean private
::RuboCop::Cop::RangeHelp
- Included
#add_range, #column_offset_between, | |
#contents_range | A range containing only the contents of a literal with delimiters (e.g. |
#directions, | |
#effective_column | Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value. |
#final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range |
::RuboCop::Cop::Base
- Inherited
#add_global_offense | Adds an offense that has no particular location. |
#add_offense | Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense. |
#begin_investigation | Called before any investigation. |
#callbacks_needed, | |
#cop_config | Configuration Helpers. |
#cop_name, #excluded_file?, | |
#external_dependency_checksum | This method should be overridden when a cop’s behavior depends on state that lives outside of these locations: |
#inspect, | |
#message | Gets called if no message is specified when calling |
#name | Alias for Base#cop_name. |
#offenses, | |
#on_investigation_end | Called after all on_… |
#on_new_investigation | Called before all on_… |
#on_other_file | Called instead of all on_… |
#parse | There should be very limited reasons for a Cop to do it’s own parsing. |
#parser_engine, | |
#ready | Called between investigations. |
#relevant_file?, #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction, | |
#callback_argument | Reserved for Cop::Cop. |
#complete_investigation | Called to complete an investigation. |
#correct, #current_corrector, | |
#current_offense_locations | Reserved for Commissioner: |
#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range, | |
#reset_investigation | Actually private methods. |
#use_corrector |
::RuboCop::Cop::AutocorrectLogic
- Included
#disable_offense, #disable_offense_at_end_of_line, #disable_offense_before_and_after, #disable_offense_with_eol_or_surround_comment, #max_line_length, | |
#range_by_lines | Expand the given range to include all of any lines it covers. |
#range_of_first_line, #range_overlaps_offense?, #string_continuation, #string_continuation?, #surrounding_heredoc, #surrounding_percent_array |
::RuboCop::Cop::IgnoredNode
- Included
Constructor Details
.new(config = nil, options = nil, offenses = nil) ⇒ RedundantCopDisableDirective
Instance Attribute Details
#offenses_to_check (rw)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 35
attr_accessor :offenses_to_check
Instance Method Details
#add_department_marker(department) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 323
def add_department_marker(department) DEPARTMENT_MARKER + department end
#add_offense_for_entire_comment(comment, cops) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 229
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: (cop_names)) do |corrector| range = comment_range_with_surrounding_space(location, comment.source_range) if leave_free_comment?(comment, range) corrector.replace(range, ' # ') else corrector.remove(range) end end end
#add_offense_for_some_cops(comment, cops) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 244
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: (cop_name)) do |corrector| range = directive_range_in_list(range, ranges) corrector.remove(range) end end end
#add_offenses(redundant_cops) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 219
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 (private)
[ GitHub ]
#all_disabled?(comment) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 190
def all_disabled?(comment) DirectiveComment.new(comment).disabled_all? end
#comment_range_with_surrounding_space(directive_comment_range, line_comment_range) (private)
[ GitHub ]# 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 (private)
[ GitHub ]# 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) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 264
def cop_range(comment, cop) cop = remove_department_marker(cop) matching_range(comment.source_range, cop) || matching_range(comment.source_range, Badge.parse(cop).cop_name) || raise("Couldn't find #{cop} in comment: #{comment.text}") end
#department_disabled?(cop, comment) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 210
def department_disabled?(cop, comment) directive = DirectiveComment.new(comment) directive.in_directive_department?(cop) && !directive.overridden_by_department?(cop) end
#department_marker?(department) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 315
def department_marker?(department) department.start_with?(DEPARTMENT_MARKER) end
#describe(cop) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 293
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 = SIMILAR_COP_NAMES_CACHE[cop] similar ? "`#{cop}` (did you mean `#{similar}`?)" : "`#{cop}` (unknown cop)" end
#directive_count(comment) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 215
def directive_count(comment) DirectiveComment.new(comment).directive_count end
#directive_range_in_list(range, ranges) (private)
[ GitHub ]# 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 (private)
[ GitHub ]# 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) (private)
Metrics/PerceivedComplexity
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 132
def each_already_disabled(cop, line_ranges) line_ranges.each_cons(2) do |previous_range, range| next if ignore_offense?(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. next unless followed_ranges?(previous_range, range) comment = processed_source.comment_at_line(range.begin) next unless comment # 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. next if all_disabled?(comment) redundant = if department_disabled?(cop, comment) find_redundant_department(cop, range) else cop end yield comment, redundant if redundant end end
#each_line_range(cop, line_ranges) (private)
[ GitHub ]# 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) next if expected_final_disable?(cop, 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) (private)
[ GitHub ]# 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) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 310
def ends_its_line?(range) line = range.source_buffer.source_line(range.last_line) (line =~ /\s*\z/) == range.last_column end
#expected_final_disable?(cop, line_range) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 202
def expected_final_disable?(cop, line_range) # A cop which is disabled in the config is being re-disabled until end of file cop_class = RuboCop::Cop::Registry.global.find_by_cop_name cop cop_class && !processed_source.registry.enabled?(cop_class, config) && line_range.max == Float::INFINITY end
#find_redundant_all(range, next_range) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 166
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) (private)
Metrics/PerceivedComplexity
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 161
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 176
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) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 182
def followed_ranges?(range, next_range) range.end == next_range.begin end
#ignore_offense?(line_range) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 194
def ignore_offense?(line_range) return true if line_range.min == CommentConfig::CONFIG_DISABLED_LINE_RANGE_MIN disabled_ranges.any? do |range| range.cover?(line_range.min) && range.cover?(line_range.max) end end
#leave_free_comment?(comment, range) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 258
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 271
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 302
def (cop_names) "Unnecessary disabling of #{cop_names}." end
#on_new_investigation
[ GitHub ]# 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
#previous_line_blank?(range) ⇒ Boolean
(private)
# 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) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 186
def range_with_offense?(range, offenses = offenses_to_check) offenses.none? { |offense| range.cover?(offense.line) } end
#remove_department_marker(department) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 319
def remove_department_marker(department) department.gsub(DEPARTMENT_MARKER, '') end
#trailing_range?(ranges, range) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/lint/redundant_cop_disable_directive.rb', line 279
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?(/\A\s*,\s*\z/) end