class RuboCop::ConfigValidator

names, and Ruby versions.
Handles validation of configuration, for example cop names, parameter

def alert_about_unrecognized_cops(invalid_cop_names)

def alert_about_unrecognized_cops(invalid_cop_names)
  unknown_cops = []
  invalid_cop_names.each do |name|
    # There could be a custom cop with this name. If so, don't warn
    next if Cop::Registry.global.contains_cop_matching?([name])
    # Special case for inherit_mode, which is a directive that we keep in
    # the configuration (even though it's not a cop), because it's easier
    # to do so than to pass the value around to various methods.
    next if name == 'inherit_mode'
    message = <<~MESSAGE.rstrip
      unrecognized cop or department #{name} found in #{smart_loaded_path}
      #{suggestion(name)}
    MESSAGE
    unknown_cops << message
  end
  raise ValidationError, unknown_cops.join("\n") if unknown_cops.any?
end

def check_cop_config_value(hash, parent = nil)

def check_cop_config_value(hash, parent = nil)
  hash.each do |key, value|
    check_cop_config_value(value, key) if value.is_a?(Hash)
    next unless CONFIG_CHECK_KEYS.include?(key) && value.is_a?(String)
    next if key == 'Enabled' && CONFIG_CHECK_DEPARTMENTS.include?(value)
    raise ValidationError, msg_not_boolean(parent, key, value)
  end
end

def check_obsoletions

def check_obsoletions
  @config_obsoletion.reject_obsolete!
  return unless @config_obsoletion.warnings.any?
  warn Rainbow("Warning: #{@config_obsoletion.warnings.join("\n")}").yellow
end

def check_target_ruby

def check_target_ruby
  return if target_ruby.supported?
  source = target_ruby.source
  last_version = target_ruby.rubocop_version_with_support
  msg = if last_version
          "RuboCop found unsupported Ruby version #{target_ruby_version} " \
            "in #{source}. #{target_ruby_version}-compatible " \
            "analysis was dropped after version #{last_version}."
        else
          'RuboCop found unknown Ruby version ' \
            "#{target_ruby_version.inspect} in #{source}."
        end
  msg += "\nSupported versions: #{TargetRuby.supported_versions.join(', ')}"
  raise ValidationError, msg
end

def each_invalid_parameter(cop_name)

def each_invalid_parameter(cop_name)
  default_config = ConfigLoader.default_configuration[cop_name]
  @config[cop_name].each_key do |param|
    next if COMMON_PARAMS.include?(param) || default_config.key?(param)
    supported_params = default_config.keys - INTERNAL_PARAMS
    yield param, supported_params
  end
end

def initialize(config)

def initialize(config)
  @config = config
  @config_obsoletion = ConfigObsoletion.new(config)
  @target_ruby = TargetRuby.new(config)
end

def msg_not_boolean(parent, key, value)

FIXME: Handling colors in exception messages like this is ugly.
def msg_not_boolean(parent, key, value)
  "#{Rainbow('').reset}" \
    "Property #{Rainbow(key).yellow} of cop #{Rainbow(parent).yellow}" \
    " is supposed to be a boolean and #{Rainbow(value).yellow} is not."
end

def reject_conflicting_safe_settings

def reject_conflicting_safe_settings
  @config.each do |name, cop_config|
    next unless cop_config.is_a?(Hash)
    next unless cop_config['Safe'] == false && cop_config['SafeAutoCorrect'] == true
    msg = 'Unsafe cops cannot have a safe auto-correction ' \
          "(section #{name} in #{smart_loaded_path})"
    raise ValidationError, msg
  end
end

def reject_mutually_exclusive_defaults

def reject_mutually_exclusive_defaults
  disabled_by_default = for_all_cops['DisabledByDefault']
  enabled_by_default = for_all_cops['EnabledByDefault']
  return unless disabled_by_default && enabled_by_default
  msg = 'Cops cannot be both enabled by default and disabled by default'
  raise ValidationError, msg
end

def suggestion(name)

def suggestion(name)
  registry = Cop::Registry.global
  departments = registry.departments.map(&:to_s)
  suggestions = NameSimilarity.find_similar_names(name, departments + registry.map(&:cop_name))
  if suggestions.any?
    "Did you mean `#{suggestions.join('`, `')}`?"
  else
    # Department names can contain slashes, e.g. Chef/Correctness, but there's no support for
    # the concept of higher level departments in RuboCop. It's a flat structure. So if the user
    # tries to configure a "top level department", we hint that it's the bottom level
    # departments that should be configured.
    suggestions = departments.select { |department| department.start_with?("#{name}/") }
    "#{name} is not a department. Use `#{suggestions.join('`, `')}`." if suggestions.any?
  end
end

def target_ruby_version

def target_ruby_version
  target_ruby.version
end

def validate

def validate
  check_cop_config_value(@config)
  reject_conflicting_safe_settings
  # Don't validate RuboCop's own files further. Avoids infinite recursion.
  return if @config.internal?
  valid_cop_names, invalid_cop_names = @config.keys.partition do |key|
    ConfigLoader.default_configuration.key?(key)
  end
  check_obsoletions
  alert_about_unrecognized_cops(invalid_cop_names)
  check_target_ruby
  validate_new_cops_parameter
  validate_parameter_names(valid_cop_names)
  validate_enforced_styles(valid_cop_names)
  validate_syntax_cop
  reject_mutually_exclusive_defaults
end

def validate_enforced_styles(valid_cop_names) # rubocop:todo Metrics/AbcSize

rubocop:todo Metrics/AbcSize
def validate_enforced_styles(valid_cop_names) # rubocop:todo Metrics/AbcSize
  valid_cop_names.each do |name|
    styles = @config[name].select { |key, _| key.start_with?('Enforced') }
    styles.each do |style_name, style|
      supported_key = RuboCop::Cop::Util.to_supported_styles(style_name)
      valid = ConfigLoader.default_configuration[name][supported_key]
      next unless valid
      next if valid.include?(style)
      next if validate_support_and_has_list(name, style, valid)
      msg = "invalid #{style_name} '#{style}' for #{name} found in " \
            "#{smart_loaded_path}\n" \
            "Valid choices are: #{valid.join(', ')}"
      raise ValidationError, msg
    end
  end
end

def validate_new_cops_parameter

def validate_new_cops_parameter
  new_cop_parameter = @config.for_all_cops['NewCops']
  return if new_cop_parameter.nil? || NEW_COPS_VALUES.include?(new_cop_parameter)
  message = "invalid #{new_cop_parameter} for `NewCops` found in" \
            "#{smart_loaded_path}\n" \
            "Valid choices are: #{NEW_COPS_VALUES.join(', ')}"
  raise ValidationError, message
end

def validate_parameter_names(valid_cop_names)

def validate_parameter_names(valid_cop_names)
  valid_cop_names.each do |name|
    validate_section_presence(name)
    each_invalid_parameter(name) do |param, supported_params|
      warn Rainbow(<<~MESSAGE).yellow
        Warning: #{name} does not support #{param} parameter.
        Supported parameters are:
          - #{supported_params.join("\n  - ")}
      MESSAGE
    end
  end
end

def validate_section_presence(name)

def validate_section_presence(name)
  return unless @config.key?(name) && @config[name].nil?
  raise ValidationError, "empty section #{name} found in #{smart_loaded_path}"
end

def validate_support_and_has_list(name, formats, valid)

def validate_support_and_has_list(name, formats, valid)
  ConfigLoader.default_configuration[name]['AllowMultipleStyles'] &&
    formats.is_a?(Array) &&
    formats.all? { |format| valid.include?(format) }
end

def validate_syntax_cop

def validate_syntax_cop
  syntax_config = @config['Lint/Syntax']
  default_config = ConfigLoader.default_configuration['Lint/Syntax']
  return unless syntax_config && default_config.merge(syntax_config) != default_config
  raise ValidationError,
        "configuration for Syntax cop found in #{smart_loaded_path}\n" \
        'It\'s not possible to disable this cop.'
end