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 inherit_mode
AutoCorrect StyleGuide Details].freeze
DEFAULT_RUBY_VERSION =

2.2 is the oldest officially supported Ruby version.

2.2
KNOWN_RUBIES =
[2.2, 2.3, 2.4, 2.5, 2.6].freeze
OBSOLETE_RUBIES =
{ 1.9 => '0.50', 2.0 => '0.50', 2.1 => '0.58' }.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.',
  'Style/MethodMissing' =>
    'The `Style/MethodMissing` cop has been split into ' \
      '`Style/MethodMissingSuper` and `Style/MissingRespondToMissing`.'
}.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?, hidden_dir?, hidden_file_in_not_hidden_dir?, match_path?, pwd, relative_path, reset_pwd, smart_path

Constructor Details

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

Returns a new instance of Config.



222
223
224
225
226
227
228
229
230
231
# File 'lib/rubocop/config.rb', line 222

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.



220
221
222
# File 'lib/rubocop/config.rb', line 220

def loaded_path
  @loaded_path
end

Class Method Details

.create(hash, path) ⇒ Object



233
234
235
# File 'lib/rubocop/config.rb', line 233

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

Instance Method Details

#[](key) ⇒ Object



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

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

#[]=(key, value) ⇒ Object



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

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

#add_excludes_from_higher_level(highest_config) ⇒ Object



314
315
316
317
318
319
320
321
322
323
324
# File 'lib/rubocop/config.rb', line 314

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

#allowed_camel_case_file?(file) ⇒ Boolean

Returns:

  • (Boolean)


385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/rubocop/config.rb', line 385

def allowed_camel_case_file?(file)
  # Gemspecs are allowed to have dashes because that fits with bundler best
  # practices in the case when the gem is nested under a namespace (e.g.,
  # `bundler-console` conveys `Bundler::Console`).
  return true if File.extname(file) == '.gemspec'

  file_to_include?(file) do |pattern, relative_path, absolute_path|
    pattern.to_s =~ /[A-Z]/ &&
      (match_path?(pattern, relative_path) ||
       match_path?(pattern, absolute_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.



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

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



237
238
239
240
241
242
243
244
# File 'lib/rubocop/config.rb', line 237

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

#delete(key) ⇒ Object



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

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

#deprecation_checkObject



326
327
328
329
330
331
332
333
334
335
# File 'lib/rubocop/config.rb', line 326

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



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

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

#each_key(&block) ⇒ Object



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

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

#file_to_exclude?(file) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/rubocop/config.rb', line 364

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|
    if block_given?
      yield pattern, relative_file_path, absolute_file_path
    else
      match_path?(pattern, relative_file_path) ||
        match_path?(pattern, absolute_file_path)
    end
  end
end

#for_all_copsObject



341
342
343
# File 'lib/rubocop/config.rb', line 341

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

#for_cop(cop) ⇒ Object



337
338
339
# File 'lib/rubocop/config.rb', line 337

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

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#keysObject



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

def keys
  @hash.keys
end

#make_excludes_absoluteObject



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/rubocop/config.rb', line 298

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



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

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

#merge(other_hash) ⇒ Object



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

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

#path_relative_to_config(path) ⇒ Object



423
424
425
# File 'lib/rubocop/config.rb', line 423

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

#patterns_to_excludeObject



419
420
421
# File 'lib/rubocop/config.rb', line 419

def patterns_to_exclude
  for_all_cops['Exclude'] || []
end

#patterns_to_includeObject



415
416
417
# File 'lib/rubocop/config.rb', line 415

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)


400
401
402
403
404
405
406
# File 'lib/rubocop/config.rb', line 400

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

#signatureObject



294
295
296
# File 'lib/rubocop/config.rb', line 294

def signature
  @signature ||= Digest::MD5.hexdigest(to_s)
end

#target_rails_versionObject



462
463
464
465
466
467
468
469
470
471
# File 'lib/rubocop/config.rb', line 462

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



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/rubocop/config.rb', line 442

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

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

      target_ruby_version_from_version_file
    elsif target_ruby_version_from_bundler_lock_file
      @target_ruby_version_source = :bundler_lock_file

      target_ruby_version_from_bundler_lock_file
    else
      DEFAULT_RUBY_VERSION
    end
  end
end

#to_hObject



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

def to_h
  @hash
end

#to_hashObject



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

def to_hash
  @hash
end

#to_sObject



290
291
292
# File 'lib/rubocop/config.rb', line 290

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

#validateObject



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/rubocop/config.rb', line 345

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