class RuboCop::Runner

This class handles the processing of files, which includes dealing with formatters and letting cops inspect the files.

Attributes

aborting[R]
aborting?[R]
errors[R]
warnings[R]

Public Class Methods

new(options, config_store) click to toggle source
# File lib/rubocop/runner.rb, line 21
def initialize(options, config_store)
  @options = options
  @config_store = config_store
  @errors = []
  @warnings = []
  @aborting = false
end

Public Instance Methods

abort() click to toggle source
# File lib/rubocop/runner.rb, line 38
def abort
  @aborting = true
end
run(paths) click to toggle source
# File lib/rubocop/runner.rb, line 29
def run(paths)
  target_files = find_target_files(paths)
  if @options[:list_target_files]
    list_files(target_files)
  else
    inspect_files(target_files)
  end
end

Private Instance Methods

add_unneeded_disables(file, offenses, source) click to toggle source
# File lib/rubocop/runner.rb, line 98
def add_unneeded_disables(file, offenses, source)
  if source.disabled_line_ranges.any? &&
     # Don't check unneeded disable if --only or --except option is
     # given, because these options override configuration.
     (@options[:except] || []).empty? && (@options[:only] || []).empty?
    config = @config_store.for(file)
    if config.cop_enabled?(Cop::Lint::UnneededDisable)
      cop = Cop::Lint::UnneededDisable.new(config, @options)
      if cop.relevant_file?(file)
        cop.check(offenses, source.disabled_line_ranges, source.comments)
        offenses += cop.offenses
      end
    end
  end

  offenses.sort.reject(&:disabled?).freeze
end
cached_run?() click to toggle source
# File lib/rubocop/runner.rb, line 122
def cached_run?
  @cached_run ||=
    (@options[:cache] == 'true' ||
     @options[:cache] != 'false' &&
     @config_store.for(Dir.pwd).for_all_cops['UseCache']) &&
    # When running --auto-gen-config, there's some processing done in the
    # cops related to calculating the Max parameters for Metrics cops. We
    # need to do that processing and can not use caching.
    !@options[:auto_gen_config] &&
    # Auto-correction needs a full run. It can not use cached results.
    !@options[:auto_correct] &&
    # We can't cache results from code which is piped in to stdin
    !@options[:stdin]
end
check_for_infinite_loop(processed_source, offenses) click to toggle source

Check whether a run created source identical to a previous run, which means that we definitely have an infinite loop.

# File lib/rubocop/runner.rb, line 190
def check_for_infinite_loop(processed_source, offenses)
  checksum = processed_source.checksum

  if @processed_sources.include?(checksum)
    raise InfiniteCorrectionLoop.new(processed_source.path, offenses)
  end

  @processed_sources << checksum
end
considered_failure?(offense) click to toggle source
# File lib/rubocop/runner.rb, line 258
def considered_failure?(offense)
  # For :autocorrect level, any offense - corrected or not - is a failure.
  return false if offense.disabled?

  return true if @options[:fail_level] == :autocorrect

  !offense.corrected? && offense.severity >= minimum_severity_to_fail
end
do_inspection_loop(file, processed_source) click to toggle source
# File lib/rubocop/runner.rb, line 147
def do_inspection_loop(file, processed_source)
  offenses = []

  # Keep track of the state of the source. If a cop modifies the source
  # and another cop undoes it producing identical source we have an
  # infinite loop.
  @processed_sources = []

  # It is also possible for a cop to keep adding indefinitely to a file,
  # making it bigger and bigger. If the inspection loop runs for an
  # excessively high number of iterations, this is likely happening.
  @iterations = 0

  # When running with --auto-correct, we need to inspect the file (which
  # includes writing a corrected version of it) until no more corrections
  # are made. This is because automatic corrections can introduce new
  # offenses. In the normal case the loop is only executed once.
  loop do
    check_for_infinite_loop(processed_source, offenses)

    if (@iterations += 1) > 200
      raise InfiniteCorrectionLoop.new(processed_source.path, offenses)
    end

    # The offenses that couldn't be corrected will be found again so we
    # only keep the corrected ones in order to avoid duplicate reporting.
    offenses.select!(&:corrected?)
    new_offenses, updated_source_file = inspect_file(processed_source)
    offenses.concat(new_offenses).uniq!

    # We have to reprocess the source to pickup the changes. Since the
    # change could (theoretically) introduce parsing errors, we break the
    # loop if we find any.
    break unless updated_source_file

    processed_source = get_processed_source(file)
  end

  [processed_source, offenses]
end
enable_rails_cops(config) click to toggle source
# File lib/rubocop/runner.rb, line 210
def enable_rails_cops(config)
  config['Rails'] ||= {}
  config['Rails']['Enabled'] = true
end
file_started(file) click to toggle source
# File lib/rubocop/runner.rb, line 116
def file_started(file)
  formatter_set.file_started(file,
                             cli_options: @options,
                             config_store: @config_store)
end
filter_cop_classes(cop_classes, config) click to toggle source
# File lib/rubocop/runner.rb, line 236
def filter_cop_classes(cop_classes, config)
  # use only cops that link to a style guide if requested
  if style_guide_cops_only?(config)
    cop_classes.select! { |cop| config.for_cop(cop)['StyleGuide'] }
  end
end
find_target_files(paths) click to toggle source
# File lib/rubocop/runner.rb, line 44
def find_target_files(paths)
  target_finder = TargetFinder.new(@config_store, @options)
  target_files = target_finder.find(paths)
  target_files.each(&:freeze).freeze
end
formatter_set() click to toggle source
# File lib/rubocop/runner.rb, line 247
def formatter_set
  @formatter_set ||= begin
    set = Formatter::FormatterSet.new(@options)
    pairs = @options[:formatters] || [['progress']]
    pairs.each do |formatter_key, output_path|
      set.add_formatter(formatter_key, output_path)
    end
    set
  end
end
get_processed_source(file) click to toggle source
# File lib/rubocop/runner.rb, line 274
def get_processed_source(file)
  ruby_version = @config_store.for(file).for_all_cops['TargetRubyVersion']

  if @options[:stdin]
    ProcessedSource.new(@options[:stdin], ruby_version, file)
  else
    ProcessedSource.from_file(file, ruby_version)
  end
end
inspect_file(processed_source) click to toggle source
# File lib/rubocop/runner.rb, line 200
def inspect_file(processed_source)
  config = @config_store.for(processed_source.path)
  enable_rails_cops(config) if @options[:rails]
  team = Cop::Team.new(mobilized_cop_classes(config), config, @options)
  offenses = team.inspect_file(processed_source)
  @errors.concat(team.errors)
  @warnings.concat(team.warnings)
  [offenses, team.updated_source_file?]
end
inspect_files(files) click to toggle source
# File lib/rubocop/runner.rb, line 50
def inspect_files(files)
  inspected_files = []
  all_passed = true

  formatter_set.started(files)

  files.each do |file|
    break if aborting?
    offenses = process_file(file)
    all_passed = false if offenses.any? { |o| considered_failure?(o) }
    inspected_files << file
    break if @options[:fail_fast] && !all_passed
  end

  all_passed
ensure
  ResultCache.cleanup(@config_store, @options[:debug]) if cached_run?
  formatter_set.finished(inspected_files.freeze)
  formatter_set.close_output_files
end
list_files(paths) click to toggle source
# File lib/rubocop/runner.rb, line 71
def list_files(paths)
  paths.each do |path|
    puts PathUtil.relative_path(path)
  end
end
minimum_severity_to_fail() click to toggle source
# File lib/rubocop/runner.rb, line 267
def minimum_severity_to_fail
  @minimum_severity_to_fail ||= begin
    name = @options[:fail_level] || :refactor
    RuboCop::Cop::Severity.new(name)
  end
end
mobilized_cop_classes(config) click to toggle source
# File lib/rubocop/runner.rb, line 215
def mobilized_cop_classes(config)
  @mobilized_cop_classes ||= {}
  @mobilized_cop_classes[config.object_id] ||= begin
    cop_classes = Cop::Cop.all

    [:only, :except].each do |opt|
      OptionsValidator.validate_cop_list(@options[opt])
    end

    if @options[:only]
      cop_classes.select! { |c| c.match?(@options[:only]) }
    else
      filter_cop_classes(cop_classes, config)
    end

    cop_classes.reject! { |c| c.match?(@options[:except]) }

    cop_classes
  end
end
process_file(file) click to toggle source
# File lib/rubocop/runner.rb, line 77
def process_file(file)
  puts "Scanning #{file}" if @options[:debug]
  file_started(file)

  cache = ResultCache.new(file, @options, @config_store) if cached_run?
  if cache && cache.valid?
    offenses = cache.load
  else
    source = get_processed_source(file)
    source, offenses = do_inspection_loop(file, source)
    offenses = add_unneeded_disables(file, offenses.compact.sort, source)
    save_in_cache(cache, offenses)
  end

  formatter_set.file_finished(file, offenses)
  offenses
rescue InfiniteCorrectionLoop => e
  formatter_set.file_finished(file, e.offenses.compact.sort.freeze)
  raise
end
save_in_cache(cache, offenses) click to toggle source
# File lib/rubocop/runner.rb, line 137
def save_in_cache(cache, offenses)
  return unless cache
  # Caching results when a cop has crashed would prevent the crash in the
  # next run, since the cop would not be called then. We want crashes to
  # show up the same in each run.
  return if errors.any? || warnings.any?

  cache.save(offenses)
end
style_guide_cops_only?(config) click to toggle source
# File lib/rubocop/runner.rb, line 243
def style_guide_cops_only?(config)
  @options[:only_guide_cops] || config.for_all_cops['StyleGuideCopsOnly']
end