Class: RuboCop::Config

Inherits:
Object
  • Object
show all
Includes:
FileFinder, PathUtil
Defined in:
lib/rubocop/config.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

COMMON_PARAMS =
%w[Exclude Include Severity
AutoCorrect StyleGuide Details].freeze
DEFAULT_RUBY_VERSION =

2.1 is the oldest officially supported Ruby version.

2.1
KNOWN_RUBIES =
[2.1, 2.2, 2.3, 2.4, 2.5].freeze
OBSOLETE_RUBIES =
{ 1.9 => '0.50', 2.0 => '0.50' }.freeze
RUBY_VERSION_FILENAME =
'.ruby-version'.freeze
DEFAULT_RAILS_VERSION =
5.0
OBSOLETE_COPS =
{
  'Style/TrailingComma' =>
    'The `Style/TrailingComma` cop no longer exists. Please use ' \
    '`Style/TrailingCommaInArguments`, ' \
    '`Style/TrailingCommaInArrayLiteral`, and/or ' \
    '`Style/TrailingCommaInHashLiteral` instead.',
  'Style/TrailingCommaInLiteral' =>
    'The `Style/TrailingCommaInLiteral` cop no longer exists. Please use ' \
    '`Style/TrailingCommaInArrayLiteral` and/or ' \
    '`Style/TrailingCommaInHashLiteral` instead.',
  'Rails/DefaultScope' =>
    'The `Rails/DefaultScope` cop no longer exists.',
  'Lint/InvalidCharacterLiteral' =>
    'The `Lint/InvalidCharacterLiteral` cop has been removed since it ' \
    'was never being actually triggered.',
  'Style/SingleSpaceBeforeFirstArg' =>
    'The `Style/SingleSpaceBeforeFirstArg` cop has been renamed to ' \
    '`Layout/SpaceBeforeFirstArg`.',
  'Lint/RescueWithoutErrorClass' =>
    'The `Lint/RescueWithoutErrorClass` cop has been replaced by ' \
    '`Style/RescueStandardError`.',
  'Lint/SpaceBeforeFirstArg' =>
    'The `Lint/SpaceBeforeFirstArg` cop has been removed, since it was a ' \
    'duplicate of `Layout/SpaceBeforeFirstArg`. Please use ' \
    '`Layout/SpaceBeforeFirstArg` instead.',
  'Layout/SpaceAfterControlKeyword' =>
    'The `Layout/SpaceAfterControlKeyword` cop has been removed. Please ' \
    'use `Layout/SpaceAroundKeyword` instead.',
  'Layout/SpaceBeforeModifierKeyword' =>
    'The `Layout/SpaceBeforeModifierKeyword` cop has been removed. ' \
    'Please use `Layout/SpaceAroundKeyword` instead.',
  'Style/SpaceAfterControlKeyword' =>
    'The `Style/SpaceAfterControlKeyword` cop has been removed. Please ' \
    'use `Layout/SpaceAroundKeyword` instead.',
  'Style/SpaceBeforeModifierKeyword' =>
    'The `Style/SpaceBeforeModifierKeyword` cop has been removed. Please ' \
    'use `Layout/SpaceAroundKeyword` instead.',
  'Style/MethodCallParentheses' =>
    'The `Style/MethodCallParentheses` cop has been renamed to ' \
      '`Style/MethodCallWithoutArgsParentheses`.',
  'Lint/Eval' =>
    'The `Lint/Eval` cop has been renamed to `Security/Eval`.',
  'Style/DeprecatedHashMethods' =>
    'The `Style/DeprecatedHashMethods` cop has been renamed to ' \
      '`Style/PreferredHashMethods`.',
  'Style/AccessorMethodName' =>
    'The `Style/AccessorMethodName` cop has been moved to ' \
      '`Naming/AccessorMethodName`.',
  'Style/AsciiIdentifiers' =>
    'The `Style/AsciiIdentifiers` cop has been moved to ' \
      '`Naming/AccessorMethodName`.',
  'Style/OpMethod' =>
    'The `Style/OpMethod` cop has been renamed and moved to ' \
      '`Naming/BinaryOperatorParameterName`.',
  'Style/ClassAndModuleCamelCase' =>
    'The `Style/ClassAndModuleCamelCase` cop has been renamed to ' \
      '`Naming/ClassAndModuleCamelCase`.',
  'Style/ConstantName' =>
    'The `Style/ConstantName` cop has been renamed to ' \
      '`Naming/ConstantName`.',
  'Style/FileName' =>
    'The `Style/FileName` cop has been renamed to `Naming/FileName`.',
  'Style/MethodName' =>
    'The `Style/MethodName` cop has been renamed to ' \
      '`Naming/MethodName`.',
  'Style/PredicateName' =>
    'The `Style/PredicateName` cop has been renamed to ' \
      '`Naming/PredicateName`.',
  'Style/VariableName' =>
    'The `Style/VariableName` cop has been renamed to ' \
      '`Naming/VariableName`.',
  'Style/VariableNumber' =>
    'The `Style/VariableNumber` cop has been renamed to ' \
      '`Naming/VariableNumber`.',
  'Lint/BlockAlignment' =>
    'The `Lint/BlockAlignment` cop has been renamed to ' \
      '`Layout/BlockAlignment`.',
  'Lint/EndAlignment' =>
    'The `Lint/EndAlignment` cop has been renamed to ' \
      '`Layout/EndAlignment`.',
  'Lint/DefEndAlignment' =>
    'The `Lint/DefEndAlignment` cop has been renamed to ' \
      '`Layout/DefEndAlignment`.',
  'Performance/HashEachMethods' =>
    'The `Performance/HashEachMethods` cop has been removed ' \
      'since it no longer provides performance benefits in ' \
      'modern rubies.'
}.freeze
OBSOLETE_PARAMETERS =
[
  {
    cop: 'Layout/SpaceAroundOperators',
    parameter: 'MultiSpaceAllowedForOperators',
    alternative: 'If your intention was to allow extra spaces ' \
                 'for alignment, please use AllowForAlignment: ' \
                 'true instead.'
  },
  {
    cop: 'Style/Encoding',
    parameter: 'EnforcedStyle',
    alternative: 'Style/Encoding no longer supports styles. ' \
                 'The "never" behavior is always assumed.'
  },
  {
    cop: 'Style/Encoding',
    parameter: 'SupportedStyles',
    alternative: 'Style/Encoding no longer supports styles. ' \
                 'The "never" behavior is always assumed.'
  },
  {
    cop: 'Style/Encoding',
    parameter: 'AutoCorrectEncodingComment',
    alternative: 'Style/Encoding no longer supports styles. ' \
                 'The "never" behavior is always assumed.'
  },
  {
    cop: 'Style/IfUnlessModifier',
    parameter: 'MaxLineLength',
    alternative:
      '`Style/IfUnlessModifier: MaxLineLength` has been removed. Use ' \
      '`Metrics/LineLength: Max` instead'
  },
  {
    cop: 'Style/SpaceAroundOperators',
    parameter: 'MultiSpaceAllowedForOperators',
    alternative: 'If your intention was to allow extra spaces ' \
                 'for alignment, please use AllowForAlignment: ' \
                 'true instead.'
  },
  {
    cop: 'Style/WhileUntilModifier',
    parameter: 'MaxLineLength',
    alternative:
      '`Style/WhileUntilModifier: MaxLineLength` has been removed. Use ' \
      '`Metrics/LineLength: Max` instead'
  },
  {
    cop: 'AllCops',
    parameter: 'RunRailsCops',
    alternative: "Use the following configuration instead:\n" \
                 "Rails:\n  Enabled: true"
  },
  {
    cop: 'Layout/CaseIndentation',
    parameter: 'IndentWhenRelativeTo',
    alternative: '`IndentWhenRelativeTo` has been renamed to ' \
                 '`EnforcedStyle`'
  },
  {
    cop: 'Lint/BlockAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Layout/BlockAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Lint/EndAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Layout/EndAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Lint/DefEndAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Layout/DefEndAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Rails/UniqBeforePluck',
    parameter: 'EnforcedMode',
    alternative: '`EnforcedMode` has been renamed to ' \
                 '`EnforcedStyle`'
  }
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from FileFinder

#find_file_upwards, #find_files_upwards, root_level=, root_level?

Methods included from PathUtil

absolute?, match_path?, pwd, relative_path, reset_pwd, smart_path

Constructor Details

#initialize(hash = {}, loaded_path = nil) ⇒ Config

Returns a new instance of Config.



219
220
221
222
223
224
225
226
227
228
# File 'lib/rubocop/config.rb', line 219

def initialize(hash = {}, loaded_path = nil)
  @loaded_path = loaded_path
  @for_cop = Hash.new do |h, cop|
    qualified_cop_name = Cop::Cop.qualified_cop_name(cop, loaded_path)
    cop_options = self[qualified_cop_name] || {}
    cop_options['Enabled'] = enable_cop?(qualified_cop_name, cop_options)
    h[cop] = cop_options
  end
  @hash = hash
end

Instance Attribute Details

#loaded_pathObject (readonly)

Returns the value of attribute loaded_path.



217
218
219
# File 'lib/rubocop/config.rb', line 217

def loaded_path
  @loaded_path
end

Class Method Details

.create(hash, path) ⇒ Object



230
231
232
# File 'lib/rubocop/config.rb', line 230

def self.create(hash, path)
  new(hash, path).check
end

Instance Method Details

#[](key) ⇒ Object



243
244
245
# File 'lib/rubocop/config.rb', line 243

def [](key)
  @hash[key]
end

#[]=(key, value) ⇒ Object



247
248
249
# File 'lib/rubocop/config.rb', line 247

def []=(key, value)
  @hash[key] = value
end

#add_excludes_from_higher_level(highest_config) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
# File 'lib/rubocop/config.rb', line 307

def add_excludes_from_higher_level(highest_config)
  return unless highest_config.for_all_cops['Exclude']

  excludes = for_all_cops['Exclude'] ||= []
  highest_config.for_all_cops['Exclude'].each do |path|
    unless path.is_a?(Regexp) || absolute?(path)
      path = File.join(File.dirname(highest_config.loaded_path), path)
    end
    excludes << path unless excludes.include?(path)
  end
end

#base_dir_for_path_parametersObject

Paths specified in configuration files starting with .rubocop are relative to the directory where that file is. Paths in other config files are relative to the current directory. This is so that paths in config/default.yml, for example, are not relative to RuboCop’s config directory since that wouldn’t work.



408
409
410
411
412
413
414
415
416
# File 'lib/rubocop/config.rb', line 408

def base_dir_for_path_parameters
  @base_dir_for_path_parameters ||=
    if File.basename(loaded_path).start_with?('.rubocop') &&
       loaded_path != File.join(Dir.home, ConfigLoader::DOTFILE)
      File.expand_path(File.dirname(loaded_path))
    else
      Dir.pwd
    end
end

#checkObject



234
235
236
237
238
239
240
241
# File 'lib/rubocop/config.rb', line 234

def check
  deprecation_check do |deprecation_message|
    warn("#{loaded_path} - #{deprecation_message}")
  end
  validate
  make_excludes_absolute
  self
end

#delete(key) ⇒ Object



251
252
253
# File 'lib/rubocop/config.rb', line 251

def delete(key)
  @hash.delete(key)
end

#deprecation_checkObject



319
320
321
322
323
324
325
326
327
328
# File 'lib/rubocop/config.rb', line 319

def deprecation_check
  %w[Exclude Include].each do |key|
    plural = "#{key}s"
    next unless for_all_cops[plural]

    for_all_cops[key] = for_all_cops[plural] # Stay backwards compatible.
    for_all_cops.delete(plural)
    yield "AllCops/#{plural} was renamed to AllCops/#{key}"
  end
end

#each(&block) ⇒ Object



255
256
257
# File 'lib/rubocop/config.rb', line 255

def each(&block)
  @hash.each(&block)
end

#each_key(&block) ⇒ Object



267
268
269
# File 'lib/rubocop/config.rb', line 267

def each_key(&block)
  @hash.each_key(&block)
end

#file_to_exclude?(file) ⇒ Boolean

Returns:

  • (Boolean)


384
385
386
387
388
389
# File 'lib/rubocop/config.rb', line 384

def file_to_exclude?(file)
  file = File.expand_path(file)
  patterns_to_exclude.any? do |pattern|
    match_path?(pattern, file)
  end
end

#file_to_include?(file) ⇒ Boolean

Returns:

  • (Boolean)


357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/rubocop/config.rb', line 357

def file_to_include?(file)
  relative_file_path = path_relative_to_config(file)

  # Optimization to quickly decide if the given file is hidden (on the top
  # level) and can not be matched by any pattern.
  is_hidden = relative_file_path.start_with?('.') &&
              !relative_file_path.start_with?('..')
  return false if is_hidden && !possibly_include_hidden?

  absolute_file_path = File.expand_path(file)

  patterns_to_include.any? do |pattern|
    match_path?(pattern, relative_file_path) ||
      match_path?(pattern, absolute_file_path)
  end
end

#for_all_copsObject



334
335
336
# File 'lib/rubocop/config.rb', line 334

def for_all_cops
  @for_all_cops ||= self['AllCops'] || {}
end

#for_cop(cop) ⇒ Object



330
331
332
# File 'lib/rubocop/config.rb', line 330

def for_cop(cop)
  @for_cop[cop.respond_to?(:cop_name) ? cop.cop_name : cop]
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


259
260
261
# File 'lib/rubocop/config.rb', line 259

def key?(key)
  @hash.key?(key)
end

#keysObject



263
264
265
# File 'lib/rubocop/config.rb', line 263

def keys
  @hash.keys
end

#make_excludes_absoluteObject



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/rubocop/config.rb', line 291

def make_excludes_absolute
  each_key do |key|
    validate_section_presence(key)
    next unless self[key]['Exclude']

    self[key]['Exclude'].map! do |exclude_elem|
      if exclude_elem.is_a?(String) && !absolute?(exclude_elem)
        File.expand_path(File.join(base_dir_for_path_parameters,
                                   exclude_elem))
      else
        exclude_elem
      end
    end
  end
end

#map(&block) ⇒ Object



271
272
273
# File 'lib/rubocop/config.rb', line 271

def map(&block)
  @hash.map(&block)
end

#merge(other_hash) ⇒ Object



275
276
277
# File 'lib/rubocop/config.rb', line 275

def merge(other_hash)
  @hash.merge(other_hash)
end

#path_relative_to_config(path) ⇒ Object



399
400
401
# File 'lib/rubocop/config.rb', line 399

def path_relative_to_config(path)
  relative_path(path, base_dir_for_path_parameters)
end

#patterns_to_excludeObject



395
396
397
# File 'lib/rubocop/config.rb', line 395

def patterns_to_exclude
  for_all_cops['Exclude']
end

#patterns_to_includeObject



391
392
393
# File 'lib/rubocop/config.rb', line 391

def patterns_to_include
  for_all_cops['Include']
end

#possibly_include_hidden?Boolean

Returns true if there’s a chance that an Include pattern matches hidden files, false if that’s definitely not possible.

Returns:

  • (Boolean)


376
377
378
379
380
381
382
# File 'lib/rubocop/config.rb', line 376

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

  @possibly_include_hidden = patterns_to_include.any? do |s|
    s.is_a?(Regexp) || s.start_with?('.') || s.include?('/.')
  end
end

#target_rails_versionObject



433
434
435
436
437
438
439
440
441
442
# File 'lib/rubocop/config.rb', line 433

def target_rails_version
  @target_rails_version ||=
    if for_all_cops['TargetRailsVersion']
      for_all_cops['TargetRailsVersion'].to_f
    elsif target_rails_version_from_bundler_lock_file
      target_rails_version_from_bundler_lock_file
    else
      DEFAULT_RAILS_VERSION
    end
end

#target_ruby_versionObject



418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/rubocop/config.rb', line 418

def target_ruby_version
  @target_ruby_version ||=
    if for_all_cops['TargetRubyVersion']
      @target_ruby_version_source = :rubocop_yml

      for_all_cops['TargetRubyVersion']
    elsif target_ruby_version_from_version_file
      @target_ruby_version_source = :ruby_version_file

      target_ruby_version_from_version_file
    else
      DEFAULT_RUBY_VERSION
    end
end

#to_hObject



279
280
281
# File 'lib/rubocop/config.rb', line 279

def to_h
  @hash
end

#to_hashObject



283
284
285
# File 'lib/rubocop/config.rb', line 283

def to_hash
  @hash
end

#to_sObject



287
288
289
# File 'lib/rubocop/config.rb', line 287

def to_s
  @to_s ||= @hash.to_s
end

#validateObject



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/rubocop/config.rb', line 338

def validate
  # Don't validate RuboCop's own files. Avoids infinite recursion.
  base_config_path = File.expand_path(File.join(ConfigLoader::RUBOCOP_HOME,
                                                'config'))
  return if File.expand_path(loaded_path).start_with?(base_config_path)

  valid_cop_names, invalid_cop_names = keys.partition do |key|
    ConfigLoader.default_configuration.key?(key)
  end

  reject_obsolete_cops_and_parameters
  warn_about_unrecognized_cops(invalid_cop_names)
  check_target_ruby
  validate_parameter_names(valid_cop_names)
  validate_enforced_styles(valid_cop_names)
  validate_syntax_cop
  reject_mutually_exclusive_defaults
end