Class: RuboCop::Config

Inherits:
Object
  • Object
show all
Includes:
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/TrailingCommaInLiteral` and/or ' \
    '`Style/TrailingCommaInArguments` 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`.'
}.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: 'Lint/EndAlignment',
    parameter: 'AlignWith',
    alternative: '`AlignWith` has been renamed to ' \
                 '`EnforcedStyleAlignWith`'
  },
  {
    cop: 'Lint/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 PathUtil

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

Constructor Details

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

Returns a new instance of Config.



182
183
184
185
186
187
188
189
190
191
# File 'lib/rubocop/config.rb', line 182

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.



180
181
182
# File 'lib/rubocop/config.rb', line 180

def loaded_path
  @loaded_path
end

Class Method Details

.create(hash, path) ⇒ Object



193
194
195
# File 'lib/rubocop/config.rb', line 193

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

Instance Method Details

#[](key) ⇒ Object



206
207
208
# File 'lib/rubocop/config.rb', line 206

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

#[]=(key, value) ⇒ Object



210
211
212
# File 'lib/rubocop/config.rb', line 210

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

#add_excludes_from_higher_level(highest_config) ⇒ Object



270
271
272
273
274
275
276
277
278
279
280
# File 'lib/rubocop/config.rb', line 270

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.



371
372
373
374
375
376
377
378
379
# File 'lib/rubocop/config.rb', line 371

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



197
198
199
200
201
202
203
204
# File 'lib/rubocop/config.rb', line 197

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

#delete(key) ⇒ Object



214
215
216
# File 'lib/rubocop/config.rb', line 214

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

#deprecation_checkObject



282
283
284
285
286
287
288
289
290
291
# File 'lib/rubocop/config.rb', line 282

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



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

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

#each_key(&block) ⇒ Object



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

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

#file_to_exclude?(file) ⇒ Boolean

Returns:

  • (Boolean)


347
348
349
350
351
352
# File 'lib/rubocop/config.rb', line 347

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)


320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/rubocop/config.rb', line 320

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



297
298
299
# File 'lib/rubocop/config.rb', line 297

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

#for_cop(cop) ⇒ Object



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

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

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


222
223
224
# File 'lib/rubocop/config.rb', line 222

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

#keysObject



226
227
228
# File 'lib/rubocop/config.rb', line 226

def keys
  @hash.keys
end

#make_excludes_absoluteObject



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/rubocop/config.rb', line 254

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



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

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

#merge(other_hash) ⇒ Object



238
239
240
# File 'lib/rubocop/config.rb', line 238

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

#path_relative_to_config(path) ⇒ Object



362
363
364
# File 'lib/rubocop/config.rb', line 362

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

#patterns_to_excludeObject



358
359
360
# File 'lib/rubocop/config.rb', line 358

def patterns_to_exclude
  for_all_cops['Exclude']
end

#patterns_to_includeObject



354
355
356
# File 'lib/rubocop/config.rb', line 354

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)


339
340
341
342
343
344
345
# File 'lib/rubocop/config.rb', line 339

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



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

def target_rails_version
  @target_rails_version ||=
    for_all_cops.fetch('TargetRailsVersion', DEFAULT_RAILS_VERSION)
end

#target_ruby_versionObject



381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/rubocop/config.rb', line 381

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



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

def to_h
  @hash
end

#to_hashObject



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

def to_hash
  @hash
end

#to_sObject



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

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

#validateObject



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/rubocop/config.rb', line 301

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