123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::TodoAudit Private

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: lib/rubocop/todo_audit.rb

Overview

Audits .rubocop_todo.yml for Exclude entries that are no longer needed: files a cop would not flag anymore, or files that no longer exist. Used by the --report-unused-todo-entries option.

Class Method Summary

Instance Method Summary

Constructor Details

.new(config_store, options) ⇒ TodoAudit

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 11

def initialize(config_store, options)
  @config_store = config_store
  @options = options
end

Instance Method Details

#absolute(path) (private)

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 107

def absolute(path)
  File.expand_path(path, File.dirname(todo_path))
end

#audit_config(config, entries) (private)

A copy of the configuration with the todo exclusions removed for the audited cops, so their offenses in the listed files become visible.

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 89

def audit_config(config, entries)
  hash = config.to_hash.dup

  entries.group_by(&:cop_name).each do |cop_name, cop_entries|
    cop_config = hash[cop_name]
    next unless cop_config.is_a?(Hash) && cop_config['Exclude'].is_a?(Array)

    hash[cop_name] = subtract_excludes(cop_config, cop_entries)
  end

  Config.create(hash, config.loaded_path, check: false)
end

#auditable_cop?(cop_name, cop_config) ⇒ Boolean (private)

Only registered cops with an Exclude list can be audited - for an unknown cop (e.g. from an extension that is not loaded in this run) the absence of offenses proves nothing.

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 55

def auditable_cop?(cop_name, cop_config)
  cop_name.include?('/') &&
    cop_config.is_a?(Hash) &&
    cop_config['Exclude'].is_a?(Array) &&
    Cop::Registry.global.contains_cop_matching?([cop_name])
end

#inspect_file(file, entries) (private)

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 74

def inspect_file(file, entries)
  config = audit_config(@config_store.for_file(file), entries)
  team = Cop::Team.mobilize(Cop::Registry.global, config, @options.merge(autocorrect: false))

  processed_source = ProcessedSource.from_file(
    file, config.target_ruby_version, parser_engine: config.parser_engine
  )
  processed_source.config = config
  processed_source.registry = Cop::Registry.global

  team.investigate(processed_source).offenses.reject(&:disabled?)
end

#offending_cops_without_todo(entries) (private)

Inspects every file the todo entries mention, with the todo exclusions subtracted from the configuration, and returns the names of the cops that still report offenses, keyed by absolute file path.

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 65

def offending_cops_without_todo(entries)
  files = entries.map { |entry| absolute(entry.path) }.uniq.select { |file| File.file?(file) }

  files.to_h do |file|
    offenses = inspect_file(file, entries)
    [file, offenses.to_set(&:cop_name)]
  end
end

#subtract_excludes(cop_config, cop_entries) (private)

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 102

def subtract_excludes(cop_config, cop_entries)
  removals = cop_entries.map { |entry| absolute(entry.path) }
  cop_config.merge('Exclude' => cop_config['Exclude'] - removals)
end

#todo_exclude_entries (private)

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 41

def todo_exclude_entries
  yaml = YAML.safe_load_file(todo_path, permitted_classes: [Regexp, Symbol], aliases: true)
  return [] unless yaml.is_a?(Hash)

  yaml.flat_map do |cop_name, cop_config|
    next [] unless auditable_cop?(cop_name, cop_config)

    cop_config['Exclude'].grep(String).map { |path| Entry.new(cop_name, path) }
  end
end

#todo_file

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 28

def todo_file
  CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
end

#todo_path (private)

[ GitHub ]

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

def todo_path
  return @todo_path if defined?(@todo_path)

  path = File.expand_path(todo_file)
  @todo_path = File.exist?(path) ? path : nil
end

#unused_entriesArray<Entry>?

Returns:

  • (Array<Entry>, nil)

    the unused entries, or nil when there is no todo file to audit

[ GitHub ]

  
# File 'lib/rubocop/todo_audit.rb', line 18

def unused_entries
  return nil unless todo_path

  entries = todo_exclude_entries
  return [] if entries.empty?

  offending_cops = offending_cops_without_todo(entries)
  entries.reject { |entry| offending_cops[absolute(entry.path)]&.include?(entry.cop_name) }
end