class RuboCop::ConfigLoader

directories are inspected.
during a run of the rubocop program, if files in several
file from which it was read. Several different Configs can be used
and all its cops. A Config is associated with a YAML configuration
This class represents the configuration of the RuboCop application

def add_excludes_from_files(config, config_file)

def add_excludes_from_files(config, config_file)
  exclusion_file = find_last_file_upwards(DOTFILE, config_file, project_root)
  return unless exclusion_file
  return if PathUtil.relative_path(exclusion_file) == PathUtil.relative_path(config_file)
  print 'AllCops/Exclude ' if debug?
  config.add_excludes_from_higher_level(load_file(exclusion_file))
end

def add_loaded_features(loaded_features)

Other tags:
    Api: - private
def add_loaded_features(loaded_features)
  @loaded_features.merge(Array(loaded_features))
end

def add_missing_namespaces(path, hash)

def add_missing_namespaces(path, hash)
  # Using `hash.each_key` will cause the
  # `can't add a new key into hash during iteration` error
  hash_keys = hash.keys
  hash_keys.each do |key|
    q = Cop::Registry.qualified_cop_name(key, path)
    next if q == key
    hash[q] = hash.delete(key)
  end
end

def check_duplication(yaml_code, absolute_path)

def check_duplication(yaml_code, absolute_path)
  smart_path = PathUtil.smart_path(absolute_path)
  YAMLDuplicationChecker.check(yaml_code, absolute_path) do |key1, key2|
    value = key1.value
    # .start_line is only available since ruby 2.5 / psych 3.0
    message = if key1.respond_to? :start_line
                line1 = key1.start_line + 1
                line2 = key2.start_line + 1
                "#{smart_path}:#{line1}: " \
                "`#{value}` is concealed by line #{line2}"
              else
                "#{smart_path}: `#{value}` is concealed by duplicate"
              end
    warn Rainbow(message).yellow
  end
end

def clear_options

def clear_options
  @debug = nil
  @loaded_features = Set.new
  FileFinder.root_level = nil
end

def configuration_file_for(target_dir)

there either, the path to the default file is returned.
user's home directory is checked. If there's no .rubocop.yml
inspected file is. If no .rubocop.yml is found there, the
directory structure starting at the given directory where the
Returns the path of .rubocop.yml searching upwards in the
def configuration_file_for(target_dir)
  find_project_dotfile(target_dir) || find_user_dotfile ||
    find_user_xdg_config || DEFAULT_FILE
end

def configuration_from_file(config_file, check: true)

def configuration_from_file(config_file, check: true)
  return default_configuration if config_file == DEFAULT_FILE
  config = load_file(config_file, check: check)
  if ignore_parent_exclusion?
    print 'Ignoring AllCops/Exclude from parent folders' if debug?
  else
    add_excludes_from_files(config, config_file)
  end
  merge_with_default(config, config_file).tap do |merged_config|
    warn_on_pending_cops(merged_config.pending_cops) unless possible_new_cops?(merged_config)
  end
end

def default_configuration

def default_configuration
  @default_configuration ||= begin
    print 'Default ' if debug?
    load_file(DEFAULT_FILE)
  end
end

def expand_path(path)

def expand_path(path)
  File.expand_path(path)
rescue ArgumentError
  # Could happen because HOME or ID could not be determined. Fall back on
  # using the path literally in that case.
  path
end

def file_path(file)

def file_path(file)
  File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
end

def find_project_dotfile(target_dir)

def find_project_dotfile(target_dir)
  find_file_upwards(DOTFILE, target_dir, project_root)
end

def find_project_root

def find_project_root
  pwd = Dir.pwd
  gems_file = find_last_file_upwards('Gemfile', pwd) || find_last_file_upwards('gems.rb', pwd)
  return unless gems_file
  File.dirname(gems_file)
end

def find_user_dotfile

def find_user_dotfile
  return unless ENV.key?('HOME')
  file = File.join(Dir.home, DOTFILE)
  return file if File.exist?(file)
end

def find_user_xdg_config

def find_user_xdg_config
  xdg_config_home = expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config'))
  xdg_config = File.join(xdg_config_home, 'rubocop', XDG_CONFIG)
  return xdg_config if File.exist?(xdg_config)
end

def load_file(file, check: true)

def load_file(file, check: true)
  path = file_path(file)
  hash = load_yaml_configuration(path)
  # Resolve requires first in case they define additional cops
  loaded_features = resolver.resolve_requires(path, hash)
  add_loaded_features(loaded_features)
  add_missing_namespaces(path, hash)
  resolver.override_department_setting_for_cops({}, hash)
  resolver.resolve_inheritance_from_gems(hash)
  resolver.resolve_inheritance(path, hash, file, debug?)
  hash.delete('inherit_from')
  Config.create(hash, path, check: check)
end

def load_yaml_configuration(absolute_path)

def load_yaml_configuration(absolute_path)
  file_contents = read_file(absolute_path)
  yaml_code = Dir.chdir(File.dirname(absolute_path)) do
    ERB.new(file_contents).result
  end
  check_duplication(yaml_code, absolute_path)
  hash = yaml_safe_load(yaml_code, absolute_path) || {}
  puts "configuration from #{absolute_path}" if debug?
  raise(TypeError, "Malformed configuration in #{absolute_path}") unless hash.is_a?(Hash)
  hash
end

def merge(base_hash, derived_hash)

arguments, will also be merged. And so on.
with the addition that any value that is a hash, and occurs in both
Return a recursive merge of two hashes. That is, a normal hash merge,
def merge(base_hash, derived_hash)
  resolver.merge(base_hash, derived_hash)
end

def merge_with_default(config, config_file, unset_nil: true)

Merges the given configuration with the default one.
def merge_with_default(config, config_file, unset_nil: true)
  resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
end

def possible_new_cops?(config)

def possible_new_cops?(config)
  disable_pending_cops || enable_pending_cops ||
    config.disabled_new_cops? || config.enabled_new_cops?
end

def project_root

searches will go past this directory.
Returns the path rubocop inferred as the root of the project. No file
def project_root
  @project_root ||= find_project_root
end

def read_file(absolute_path)

found" error.
stderr. Care is taken to use the standard OS exit code for a "file not
Read the specified file, or exit with a friendly, concise message on
def read_file(absolute_path)
  IO.read(absolute_path, encoding: Encoding::UTF_8)
rescue Errno::ENOENT
  raise ConfigNotFoundError,
        "Configuration file not found: #{absolute_path}"
end

def resolver

def resolver
  @resolver ||= ConfigLoaderResolver.new
end

def warn_on_pending_cops(pending_cops)

def warn_on_pending_cops(pending_cops)
  return if pending_cops.empty?
  warn Rainbow(PENDING_BANNER).yellow
  pending_cops.each do |cop|
    warn_pending_cop cop
  end
  warn Rainbow('For more information: https://docs.rubocop.org/rubocop/versioning.html').yellow
end

def warn_pending_cop(cop)

def warn_pending_cop(cop)
  version = cop.metadata['VersionAdded'] || 'N/A'
  warn Rainbow("#{cop.name}: # (new in #{version})").yellow
  warn Rainbow('  Enabled: true').yellow
end

def yaml_safe_load(yaml_code, filename)

def yaml_safe_load(yaml_code, filename)
  yaml_safe_load!(yaml_code, filename)
rescue ::StandardError
  if defined?(::SafeYAML)
    raise 'SafeYAML is unmaintained, no longer needed and should be removed'
  end
  raise
end

def yaml_safe_load!(yaml_code, filename)

def yaml_safe_load!(yaml_code, filename)
  YAML.safe_load(yaml_code,
                 permitted_classes: [Regexp, Symbol],
                 permitted_symbols: [],
                 aliases: true,
                 filename: filename)
end

def yaml_safe_load!(yaml_code, filename)

Ruby < 2.6
def yaml_safe_load!(yaml_code, filename)
  YAML.safe_load(yaml_code, [Regexp, Symbol], [], true, filename)
end