123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::CommentConfig

Relationships & Source Files
Namespace Children
Classes:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, SimpleForwardable
Inherits: Object
Defined in: lib/rubocop/comment_config.rb

Overview

This class parses the special rubocop:disable comments in a source and provides a way to check if each cop is enabled at arbitrary line.

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(processed_source) ⇒ CommentConfig

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 34

def initialize(processed_source)
  @processed_source = processed_source
  @no_directives = !processed_source.raw_source.include?('rubocop')
  @stack = []
end

Instance Attribute Details

#processed_source (readonly)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 30

attr_reader :processed_source

Instance Method Details

#analyze (private)

rubocop:todo Metrics/AbcSize, Metrics/MethodLength

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 98

def analyze # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
  return {} if @no_directives

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

  each_directive do |directive|
    if directive.push?
      resolved = resolve_push_cops(directive)
      @stack.push(snapshot_cops(analyses, resolved.values.flatten))
      apply_push(analyses, resolved, directive.line_number)
    elsif directive.pop?
      pop_state(analyses, directive.line_number) if @stack.any?
    else
      directive.cop_names.each do |cop_name|
        cop_name = qualified_cop_name(cop_name)
        analyses[cop_name] = analyze_cop(analyses[cop_name], directive)
      end
    end
  end

  analyses.each_with_object({}) do |element, hash|
    cop_name, analysis = *element
    hash[cop_name] = cop_line_ranges(analysis)
  end
end

#analyze_cop(analysis, directive) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 177

def analyze_cop(analysis, directive)
  # Disabling cops after comments like `#=SomeDslDirective` does not related to single line
  if !comment_only_line?(directive.line_number) || directive.single_line?
    analyze_single_line(analysis, directive)
  elsif directive.disabled?
    analyze_disabled(analysis, directive)
  else
    analyze_rest(analysis, directive)
  end
end

#analyze_disabled(analysis, directive) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 195

def analyze_disabled(analysis, directive)
  line = directive.line_number
  start_line = analysis.start_line_number
  new_ranges = start_line ? analysis.line_ranges + [start_line..line] : analysis.line_ranges
  CopAnalysis.new(new_ranges, line)
end

#analyze_rest(analysis, directive) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 202

def analyze_rest(analysis, directive)
  line = directive.line_number
  start_line = analysis.start_line_number
  new_ranges = start_line ? analysis.line_ranges + [start_line..line] : analysis.line_ranges
  CopAnalysis.new(new_ranges, nil)
end

#analyze_single_line(analysis, directive) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 188

def analyze_single_line(analysis, directive)
  return analysis unless directive.disabled?

  line = directive.line_number
  CopAnalysis.new(analysis.line_ranges + [(line..line)], analysis.start_line_number)
end

#apply_cop_op(analyses, operation, cop, line) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 147

def apply_cop_op(analyses, operation, cop, line)
  analysis = analyses[cop]
  if operation == '-' && !analysis.start_line_number
    analyses[cop] = CopAnalysis.new(analysis.line_ranges, line)
  elsif operation == '+' && analysis.start_line_number
    analyses[cop] =
      CopAnalysis.new(analysis.line_ranges + [analysis.start_line_number..line], nil)
  end
end

#apply_push(analyses, resolved_cops, line) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 141

def apply_push(analyses, resolved_cops, line)
  resolved_cops.each do |op, cops|
    cops.each { |cop| apply_cop_op(analyses, op, cop, line) }
  end
end

#comment_only_line?(line_number) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 64

def comment_only_line?(line_number)
  non_comment_token_line_numbers.none?(line_number)
end

#cop_disabled_line_ranges

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 52

def cop_disabled_line_ranges
  @cop_disabled_line_ranges ||= analyze
end

#cop_enabled_at_line?(cop, line_number) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 40

def cop_enabled_at_line?(cop, line_number)
  cop = cop.cop_name if cop.respond_to?(:cop_name)
  disabled_line_ranges = cop_disabled_line_ranges[cop]
  return true unless disabled_line_ranges

  disabled_line_ranges.none? { |range| range.include?(line_number) }
end

#cop_line_ranges(analysis) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 209

def cop_line_ranges(analysis)
  return analysis.line_ranges unless analysis.start_line_number

  analysis.line_ranges + [(analysis.start_line_number..Float::INFINITY)]
end

#cop_opted_in?(cop) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 48

def cop_opted_in?(cop)
  opt_in_cops.include?(cop.cop_name)
end

#each_directive (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 215

def each_directive
  return if @no_directives

  processed_source.comments.each do |comment|
    directive = DirectiveComment.new(comment)
    yield directive if directive.cop_names
  end
end

#expand_cop_name(name) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 135

def expand_cop_name(name)
  registry = Cop::Registry.global
  cops = registry.department?(name) ? registry.names_for_department(name) : [name]
  cops.map { |c| qualified_cop_name(c) }
end

#extra_enabled_comments

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 56

def extra_enabled_comments
  disable_count = Hash.new(0)
  registry.disabled(config).each do |cop|
    disable_count[cop.cop_name] += 1
  end
  extra_enabled_comments_with_names(extras: Hash.new { |h, k| h[k] = [] }, names: disable_count)
end

#extra_enabled_comments_with_names(extras:, names:) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 70

def extra_enabled_comments_with_names(extras:, names:)
  each_directive do |directive|
    next unless comment_only_line?(directive.line_number)
    next if directive.push? || directive.pop?

    if directive.enabled_all?
      handle_enable_all(directive, names, extras)
    else
      handle_switch(directive, names, extras)
    end
  end

  extras
end

#handle_enable_all(directive, names, extras) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 235

def handle_enable_all(directive, names, extras)
  enabled_cops = 0
  names.each do |name, counter|
    next unless counter.positive?

    names[name] -= 1
    enabled_cops += 1
  end

  extras[directive.comment] << 'all' if enabled_cops.zero?
end

#handle_switch(directive, names, extras) (private)

Collect cops that have been disabled or enabled by name in a directive comment so that Lint/RedundantCopEnableDirective can register offenses correctly.

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 249

def handle_switch(directive, names, extras)
  directive.cop_names.each do |name|
    if directive.disabled?
      names[name] += 1
    elsif names[name].positive?
      names[name] -= 1
    else
      extras[directive.comment] << name
    end
  end
end

#inject_disabled_cops_directives(analyses) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 168

def inject_disabled_cops_directives(analyses)
  registry.disabled(config).each do |cop|
    analyses[cop.cop_name] = analyze_cop(
      analyses[cop.cop_name],
      DirectiveComment.new(ConfigDisabledCopDirectiveComment.new(cop.cop_name))
    )
  end
end

#non_comment_token_line_numbers (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 228

def non_comment_token_line_numbers
  @non_comment_token_line_numbers ||= begin
    non_comment_tokens = processed_source.tokens.reject(&:comment?)
    non_comment_tokens.map(&:line).uniq
  end
end

#opt_in_cops (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 85

def opt_in_cops
  @opt_in_cops ||= begin
    cops = Set.new
    each_directive do |directive|
      next unless directive.enabled?
      next if directive.all_cops?

      cops.merge(directive.raw_cop_names)
    end
    cops
  end
end

#pop_state(analyses, line) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 157

def pop_state(analyses, line)
  saved = @stack.pop
  saved.each do |cop, old|
    cur = analyses[cop]
    new_range = cur.start_line_number ? [cur.start_line_number..(line - 1)] : []
    ranges = cur.line_ranges + new_range
    new_start = old.start_line_number ? line : nil
    analyses[cop] = CopAnalysis.new(ranges, new_start)
  end
end

#qualified_cop_name(cop_name) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 224

def qualified_cop_name(cop_name)
  Cop::Registry.qualified_cop_name(cop_name.strip, processed_source.file_path)
end

#resolve_push_cops(directive) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 129

def resolve_push_cops(directive)
  directive.push_args.transform_values do |names|
    names.flat_map { |name| expand_cop_name(name) }
  end
end

#snapshot_cops(analyses, cop_names) (private)

[ GitHub ]

  
# File 'lib/rubocop/comment_config.rb', line 125

def snapshot_cops(analyses, cop_names)
  cop_names.to_h { |name| [name, analyses[name].dup] }
end