rubocop 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +52 -7
  4. data/config/obsoletion.yml +196 -0
  5. data/lib/rubocop.rb +14 -0
  6. data/lib/rubocop/cli/command/suggest_extensions.rb +21 -8
  7. data/lib/rubocop/config.rb +8 -5
  8. data/lib/rubocop/config_loader.rb +10 -6
  9. data/lib/rubocop/config_loader_resolver.rb +21 -4
  10. data/lib/rubocop/config_obsoletion.rb +64 -262
  11. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  12. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  13. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  14. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  15. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  16. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  17. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  18. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  19. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  20. data/lib/rubocop/config_validator.rb +11 -4
  21. data/lib/rubocop/cop/base.rb +17 -15
  22. data/lib/rubocop/cop/cop.rb +2 -2
  23. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  24. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
  25. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  26. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
  27. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
  28. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +1 -1
  29. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  30. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +26 -0
  31. data/lib/rubocop/cop/layout/line_length.rb +6 -16
  32. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
  33. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
  34. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
  35. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  36. data/lib/rubocop/cop/layout/space_before_brackets.rb +64 -0
  37. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
  38. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  39. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
  40. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
  41. data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
  42. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  43. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
  44. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  45. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
  46. data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
  47. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  48. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  49. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  50. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  51. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
  52. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  53. data/lib/rubocop/cop/registry.rb +10 -0
  54. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
  55. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  56. data/lib/rubocop/cop/style/collection_methods.rb +14 -1
  57. data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
  58. data/lib/rubocop/cop/style/float_division.rb +44 -1
  59. data/lib/rubocop/cop/style/for.rb +2 -0
  60. data/lib/rubocop/cop/style/hash_except.rb +95 -0
  61. data/lib/rubocop/cop/style/if_unless_modifier.rb +4 -0
  62. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  63. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
  64. data/lib/rubocop/cop/style/lambda_call.rb +2 -1
  65. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -0
  66. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
  67. data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
  68. data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
  69. data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
  70. data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
  71. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  72. data/lib/rubocop/cop/style/raise_args.rb +2 -0
  73. data/lib/rubocop/cop/style/redundant_argument.rb +21 -2
  74. data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
  75. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
  76. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  77. data/lib/rubocop/cop/style/single_line_methods.rb +4 -0
  78. data/lib/rubocop/cop/style/sole_nested_conditional.rb +24 -8
  79. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  80. data/lib/rubocop/cop/style/string_concatenation.rb +26 -1
  81. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  82. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  83. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  84. data/lib/rubocop/cop/util.rb +3 -1
  85. data/lib/rubocop/ext/regexp_node.rb +31 -9
  86. data/lib/rubocop/ext/regexp_parser.rb +21 -3
  87. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  88. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  89. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  90. data/lib/rubocop/lockfile.rb +40 -0
  91. data/lib/rubocop/options.rb +9 -9
  92. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  93. data/lib/rubocop/rspec/expect_offense.rb +34 -22
  94. data/lib/rubocop/runner.rb +16 -1
  95. data/lib/rubocop/target_finder.rb +4 -2
  96. data/lib/rubocop/util.rb +16 -0
  97. data/lib/rubocop/version.rb +8 -2
  98. metadata +33 -5
@@ -3,7 +3,10 @@
3
3
  module RuboCop
4
4
  class CLI
5
5
  module Command
6
- # Run all the selected cops and report the result.
6
+ # Suggest RuboCop extensions to install based on Gemfile dependencies.
7
+ # Only primary dependencies are evaluated, so if a dependency depends on a
8
+ # gem with an extension, it is not suggested. However, if an extension is
9
+ # a transitive dependency, it will not be suggested.
7
10
  # @api private
8
11
  class SuggestExtensions < Base
9
12
  # Combination of short and long formatter names.
@@ -18,14 +21,17 @@ module RuboCop
18
21
  puts 'Tip: Based on detected gems, the following '\
19
22
  'RuboCop extension libraries might be helpful:'
20
23
 
21
- extensions.each do |extension|
24
+ extensions.sort.each do |extension|
22
25
  puts " * #{extension} (http://github.com/rubocop-hq/#{extension})"
23
26
  end
24
27
 
25
28
  puts
26
- puts 'You can opt out of this message by adding the following to your config:'
29
+ puts 'You can opt out of this message by adding the following to your config '\
30
+ '(see https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions '\
31
+ 'for more options):'
27
32
  puts ' AllCops:'
28
33
  puts ' SuggestExtensions: false'
34
+
29
35
  puts if @options[:display_time]
30
36
  end
31
37
 
@@ -46,15 +52,22 @@ module RuboCop
46
52
  end
47
53
 
48
54
  def extensions
49
- extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions']
50
- return [] unless extensions
55
+ return [] unless lockfile.dependencies.any?
56
+
57
+ extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions'] || {}
58
+ extensions.select { |_, v| (Array(v) & dependent_gems).any? }.keys - installed_gems
59
+ end
51
60
 
52
- extensions.select { |_, v| (v & dependent_gems).any? }.keys - dependent_gems
61
+ def lockfile
62
+ @lockfile ||= Lockfile.new
53
63
  end
54
64
 
55
65
  def dependent_gems
56
- # This only includes gems in Gemfile, not in lockfile
57
- Bundler.load.dependencies.map(&:name)
66
+ lockfile.dependencies.map(&:name)
67
+ end
68
+
69
+ def installed_gems
70
+ lockfile.gems.map(&:name)
58
71
  end
59
72
 
60
73
  def puts(*args)
@@ -50,8 +50,8 @@ module RuboCop
50
50
  self
51
51
  end
52
52
 
53
- def_delegators :@hash, :[], :[]=, :delete, :each, :key?, :keys, :each_key,
54
- :fetch, :map, :merge, :to_h, :to_hash, :transform_values
53
+ def_delegators :@hash, :[], :[]=, :delete, :dig, :each, :key?, :keys, :each_key,
54
+ :fetch, :map, :merge, :replace, :to_h, :to_hash, :transform_values
55
55
  def_delegators :@validator, :validate, :target_ruby_version
56
56
 
57
57
  def to_s
@@ -281,6 +281,9 @@ module RuboCop
281
281
  end
282
282
 
283
283
  def enable_cop?(qualified_cop_name, cop_options)
284
+ # If the cop is explicitly enabled, the other checks can be skipped.
285
+ return true if cop_options['Enabled'] == true
286
+
284
287
  department = department_of(qualified_cop_name)
285
288
  cop_enabled = cop_options.fetch('Enabled') do
286
289
  !for_all_cops['DisabledByDefault']
@@ -292,10 +295,10 @@ module RuboCop
292
295
  end
293
296
 
294
297
  def department_of(qualified_cop_name)
295
- cop_department, cop_name = qualified_cop_name.split('/')
296
- return nil if cop_name.nil?
298
+ *cop_department, _ = qualified_cop_name.split('/')
299
+ return nil if cop_department.empty?
297
300
 
298
- self[cop_department]
301
+ self[cop_department.join('/')]
299
302
  end
300
303
  end
301
304
  end
@@ -31,6 +31,7 @@ module RuboCop
31
31
 
32
32
  def clear_options
33
33
  @debug = nil
34
+ @loaded_features = []
34
35
  FileFinder.root_level = nil
35
36
  end
36
37
 
@@ -177,12 +178,9 @@ module RuboCop
177
178
  @loaded_features.flatten.compact
178
179
  end
179
180
 
180
- private
181
-
182
- def file_path(file)
183
- File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
184
- end
185
-
181
+ # @api private
182
+ # Used to add features that were required inside a config or from
183
+ # the CLI using `--require`.
186
184
  def add_loaded_features(loaded_features)
187
185
  if instance_variable_defined?(:@loaded_features)
188
186
  instance_variable_get(:@loaded_features) << loaded_features
@@ -191,6 +189,12 @@ module RuboCop
191
189
  end
192
190
  end
193
191
 
192
+ private
193
+
194
+ def file_path(file)
195
+ File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
196
+ end
197
+
194
198
  def find_project_dotfile(target_dir)
195
199
  find_file_upwards(DOTFILE, target_dir, project_root)
196
200
  end
@@ -20,12 +20,13 @@ module RuboCop
20
20
  end
21
21
  end
22
22
 
23
- # rubocop:disable Metrics/MethodLength
24
- def resolve_inheritance(path, hash, file, debug)
23
+ def resolve_inheritance(path, hash, file, debug) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
25
24
  inherited_files = Array(hash['inherit_from'])
26
25
  base_configs(path, inherited_files, file)
27
26
  .reverse.each_with_index do |base_config, index|
28
27
  override_department_setting_for_cops(base_config, hash)
28
+ override_enabled_for_disabled_departments(base_config, hash)
29
+
29
30
  base_config.each do |k, v|
30
31
  next unless v.is_a?(Hash)
31
32
 
@@ -39,7 +40,6 @@ module RuboCop
39
40
  end
40
41
  end
41
42
  end
42
- # rubocop:enable Metrics/MethodLength
43
43
 
44
44
  def resolve_inheritance_from_gems(hash)
45
45
  gems = hash.delete('inherit_gem')
@@ -75,6 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  config = handle_disabled_by_default(config, default_configuration) if disabled_by_default
78
+ override_enabled_for_disabled_departments(default_configuration, config)
78
79
 
79
80
  opts = { inherit_mode: config['inherit_mode'] || {},
80
81
  unset_nil: unset_nil }
@@ -122,10 +123,26 @@ module RuboCop
122
123
  end
123
124
  end
124
125
 
126
+ # If a cop was previously explicitly enabled, but then superseded by the
127
+ # department being disabled, disable it.
128
+ def override_enabled_for_disabled_departments(base_hash, derived_hash)
129
+ cops_to_disable = derived_hash.each_key.with_object([]) do |key, cops|
130
+ next unless disabled?(derived_hash, key)
131
+
132
+ cops.concat(base_hash.keys.grep(Regexp.new("^#{key}/")))
133
+ end
134
+
135
+ cops_to_disable.each do |cop_name|
136
+ next unless base_hash.dig(cop_name, 'Enabled') == true
137
+
138
+ derived_hash.replace(merge({ cop_name => { 'Enabled' => false } }, derived_hash))
139
+ end
140
+ end
141
+
125
142
  private
126
143
 
127
144
  def disabled?(hash, department)
128
- hash[department] && hash[department]['Enabled'] == false
145
+ hash[department].is_a?(Hash) && hash[department]['Enabled'] == false
129
146
  end
130
147
 
131
148
  def duplicate_setting?(base_hash, derived_hash, key, inherited_file)
@@ -4,233 +4,40 @@ module RuboCop
4
4
  # This class handles obsolete configuration.
5
5
  # @api private
6
6
  class ConfigObsoletion
7
- RENAMED_COPS = {
8
- 'Layout/AlignArguments' => 'Layout/ArgumentAlignment',
9
- 'Layout/AlignArray' => 'Layout/ArrayAlignment',
10
- 'Layout/AlignHash' => 'Layout/HashAlignment',
11
- 'Layout/AlignParameters' => 'Layout/ParameterAlignment',
12
- 'Layout/IndentArray' => 'Layout/FirstArrayElementIndentation',
13
- 'Layout/IndentAssignment' => 'Layout/AssignmentIndentation',
14
- 'Layout/IndentFirstArgument' => 'Layout/FirstArgumentIndentation',
15
- 'Layout/IndentFirstArrayElement' => 'Layout/FirstArrayElementIndentation',
16
- 'Layout/IndentFirstHashElement' => 'Layout/FirstHashElementIndentation',
17
- 'Layout/IndentFirstParameter' => 'Layout/FirstParameterIndentation',
18
- 'Layout/IndentHash' => 'Layout/FirstHashElementIndentation',
19
- 'Layout/IndentHeredoc' => 'Layout/HeredocIndentation',
20
- 'Layout/LeadingBlankLines' => 'Layout/LeadingEmptyLines',
21
- 'Layout/Tab' => 'Layout/IndentationStyle',
22
- 'Layout/TrailingBlankLines' => 'Layout/TrailingEmptyLines',
23
- 'Lint/DuplicatedKey' => 'Lint/DuplicateHashKey',
24
- 'Lint/EndInMethod' => 'Style/EndBlock',
25
- 'Lint/HandleExceptions' => 'Lint/SuppressedException',
26
- 'Lint/MultipleCompare' => 'Lint/MultipleComparison',
27
- 'Lint/StringConversionInInterpolation' => 'Lint/RedundantStringCoercion',
28
- 'Lint/UnneededCopDisableDirective' => 'Lint/RedundantCopDisableDirective',
29
- 'Lint/UnneededCopEnableDirective' => 'Lint/RedundantCopEnableDirective',
30
- 'Lint/UnneededRequireStatement' => 'Lint/RedundantRequireStatement',
31
- 'Lint/UnneededSplatExpansion' => 'Lint/RedundantSplatExpansion',
32
- 'Naming/UncommunicativeBlockParamName' => 'Naming/BlockParameterName',
33
- 'Naming/UncommunicativeMethodParamName' => 'Naming/MethodParameterName',
34
- 'Style/DeprecatedHashMethods' => 'Style/PreferredHashMethods',
35
- 'Style/MethodCallParentheses' => 'Style/MethodCallWithoutArgsParentheses',
36
- 'Style/OpMethod' => 'Naming/BinaryOperatorParameterName',
37
- 'Style/SingleSpaceBeforeFirstArg' => 'Layout/SpaceBeforeFirstArg',
38
- 'Style/UnneededCapitalW' => 'Style/RedundantCapitalW',
39
- 'Style/UnneededCondition' => 'Style/RedundantCondition',
40
- 'Style/UnneededInterpolation' => 'Style/RedundantInterpolation',
41
- 'Style/UnneededPercentQ' => 'Style/RedundantPercentQ',
42
- 'Style/UnneededSort' => 'Style/RedundantSort'
43
- }.map do |old_name, new_name|
44
- [old_name, "The `#{old_name}` cop has been renamed to `#{new_name}`."]
45
- end
46
-
47
- MOVED_COPS = {
48
- 'Security' => 'Lint/Eval',
49
- 'Naming' => %w[Style/ClassAndModuleCamelCase Style/ConstantName
50
- Style/FileName Style/MethodName Style/PredicateName
51
- Style/VariableName Style/VariableNumber
52
- Style/AccessorMethodName Style/AsciiIdentifiers],
53
- 'Layout' => %w[Lint/BlockAlignment Lint/EndAlignment
54
- Lint/DefEndAlignment Metrics/LineLength],
55
- 'Lint' => 'Style/FlipFlop'
56
- }.map do |new_department, old_names|
57
- Array(old_names).map do |old_name|
58
- [old_name, "The `#{old_name}` cop has been moved to " \
59
- "`#{new_department}/#{old_name.split('/').last}`."]
7
+ DEFAULT_RULES_FILE = File.join(ConfigLoader::RUBOCOP_HOME, 'config', 'obsoletion.yml')
8
+ COP_RULE_CLASSES = {
9
+ 'renamed' => RenamedCop,
10
+ 'removed' => RemovedCop,
11
+ 'split' => SplitCop,
12
+ 'extracted' => ExtractedCop
13
+ }.freeze
14
+ PARAMETER_RULE_CLASSES = {
15
+ 'changed_parameters' => ChangedParameter,
16
+ 'changed_enforced_styles' => ChangedEnforcedStyles
17
+ }.freeze
18
+
19
+ attr_reader :rules, :warnings
20
+
21
+ class << self
22
+ attr_accessor :files
23
+
24
+ def legacy_cop_names
25
+ # Used by DepartmentName#qualified_legacy_cop_name
26
+ new(Config.new).rules.select(&:cop_rule?).map(&:old_name)
60
27
  end
61
28
  end
62
29
 
63
- REMOVED_COPS = {
64
- 'Layout/SpaceAfterControlKeyword' => 'Layout/SpaceAroundKeyword',
65
- 'Layout/SpaceBeforeModifierKeyword' => 'Layout/SpaceAroundKeyword',
66
- 'Lint/RescueWithoutErrorClass' => 'Style/RescueStandardError',
67
- 'Style/SpaceAfterControlKeyword' => 'Layout/SpaceAroundKeyword',
68
- 'Style/SpaceBeforeModifierKeyword' => 'Layout/SpaceAroundKeyword',
69
- 'Style/TrailingComma' => 'Style/TrailingCommaInArguments, ' \
70
- 'Style/TrailingCommaInArrayLiteral, and/or ' \
71
- 'Style/TrailingCommaInHashLiteral',
72
- 'Style/TrailingCommaInLiteral' => 'Style/TrailingCommaInArrayLiteral ' \
73
- 'and/or ' \
74
- 'Style/TrailingCommaInHashLiteral',
75
- 'Style/BracesAroundHashParameters' => nil
76
- }.map do |old_name, other_cops|
77
- if other_cops
78
- more = ". Please use #{other_cops} instead".gsub(%r{[A-Z]\w+/\w+},
79
- '`\&`')
80
- end
81
- [old_name, "The `#{old_name}` cop has been removed#{more}."]
82
- end
83
-
84
- REMOVED_COPS_WITH_REASON = {
85
- 'Lint/InvalidCharacterLiteral' => 'it was never being actually triggered',
86
- 'Lint/SpaceBeforeFirstArg' =>
87
- 'it was a duplicate of `Layout/SpaceBeforeFirstArg`. Please use ' \
88
- '`Layout/SpaceBeforeFirstArg` instead',
89
- 'Style/MethodMissingSuper' => 'it has been superseded by `Lint/MissingSuper`. Please use ' \
90
- '`Lint/MissingSuper` instead',
91
- 'Lint/UselessComparison' => 'it has been superseded by '\
92
- '`Lint/BinaryOperatorWithIdenticalOperands`. Please use '\
93
- '`Lint/BinaryOperatorWithIdenticalOperands` instead'
94
- }.map do |cop_name, reason|
95
- [cop_name, "The `#{cop_name}` cop has been removed since #{reason}."]
96
- end
97
-
98
- SPLIT_COPS = {
99
- 'Style/MethodMissing' =>
100
- 'The `Style/MethodMissing` cop has been split into ' \
101
- '`Style/MethodMissingSuper` and `Style/MissingRespondToMissing`.'
102
- }.to_a
103
-
104
- OBSOLETE_COPS = Hash[*(RENAMED_COPS + MOVED_COPS + REMOVED_COPS +
105
- REMOVED_COPS_WITH_REASON + SPLIT_COPS).flatten]
106
-
107
- # Parameters can be deprecated but not disabled by setting `severity: :warning`
108
- OBSOLETE_PARAMETERS = [
109
- {
110
- cops: %w[Layout/SpaceAroundOperators Style/SpaceAroundOperators],
111
- parameters: 'MultiSpaceAllowedForOperators',
112
- alternative: 'If your intention was to allow extra spaces for ' \
113
- 'alignment, please use AllowForAlignment: true instead.'
114
- },
115
- {
116
- cops: 'Style/Encoding',
117
- parameters: %w[EnforcedStyle SupportedStyles
118
- AutoCorrectEncodingComment],
119
- alternative: 'Style/Encoding no longer supports styles. ' \
120
- 'The "never" behavior is always assumed.'
121
- },
122
- {
123
- cops: 'Style/IfUnlessModifier',
124
- parameters: 'MaxLineLength',
125
- alternative: '`Style/IfUnlessModifier: MaxLineLength` has been ' \
126
- 'removed. Use `Layout/LineLength: Max` instead'
127
- },
128
- {
129
- cops: 'Style/WhileUntilModifier',
130
- parameters: 'MaxLineLength',
131
- alternative: '`Style/WhileUntilModifier: MaxLineLength` has been ' \
132
- 'removed. Use `Layout/LineLength: Max` instead'
133
- },
134
- {
135
- cops: 'AllCops',
136
- parameters: 'RunRailsCops',
137
- alternative: "Use the following configuration instead:\n" \
138
- "Rails:\n Enabled: true"
139
- },
140
- {
141
- cops: 'Layout/CaseIndentation',
142
- parameters: 'IndentWhenRelativeTo',
143
- alternative: '`IndentWhenRelativeTo` has been renamed to ' \
144
- '`EnforcedStyle`'
145
- },
146
- {
147
- cops: %w[Lint/BlockAlignment Layout/BlockAlignment Lint/EndAlignment
148
- Layout/EndAlignment Lint/DefEndAlignment
149
- Layout/DefEndAlignment],
150
- parameters: 'AlignWith',
151
- alternative: '`AlignWith` has been renamed to `EnforcedStyleAlignWith`'
152
- },
153
- {
154
- cops: 'Rails/UniqBeforePluck',
155
- parameters: 'EnforcedMode',
156
- alternative: '`EnforcedMode` has been renamed to `EnforcedStyle`'
157
- },
158
- {
159
- cops: 'Style/MethodCallWithArgsParentheses',
160
- parameters: 'IgnoredMethodPatterns',
161
- alternative: '`IgnoredMethodPatterns` has been renamed to ' \
162
- '`IgnoredPatterns`'
163
- },
164
- {
165
- cops: %w[Performance/Count Performance/Detect],
166
- parameters: 'SafeMode',
167
- alternative: '`SafeMode` has been removed. ' \
168
- 'Use `SafeAutoCorrect` instead.'
169
- },
170
- {
171
- cops: 'Bundler/GemComment',
172
- parameters: 'Whitelist',
173
- alternative: '`Whitelist` has been renamed to `IgnoredGems`.'
174
- },
175
- {
176
- cops: %w[
177
- Lint/SafeNavigationChain Lint/SafeNavigationConsistency
178
- Style/NestedParenthesizedCalls Style/SafeNavigation
179
- Style/TrivialAccessors
180
- ],
181
- parameters: 'Whitelist',
182
- alternative: '`Whitelist` has been renamed to `AllowedMethods`.'
183
- },
184
- {
185
- cops: 'Style/IpAddresses',
186
- parameters: 'Whitelist',
187
- alternative: '`Whitelist` has been renamed to `AllowedAddresses`.'
188
- },
189
- {
190
- cops: 'Naming/HeredocDelimiterNaming',
191
- parameters: 'Blacklist',
192
- alternative: '`Blacklist` has been renamed to `ForbiddenDelimiters`.'
193
- },
194
- {
195
- cops: 'Naming/PredicateName',
196
- parameters: 'NamePrefixBlacklist',
197
- alternative: '`NamePrefixBlacklist` has been renamed to ' \
198
- '`ForbiddenPrefixes`.'
199
- },
200
- {
201
- cops: 'Naming/PredicateName',
202
- parameters: 'NameWhitelist',
203
- alternative: '`NameWhitelist` has been renamed to ' \
204
- '`AllowedMethods`.'
205
- },
206
- {
207
- cops: %w[Metrics/BlockLength Metrics/MethodLength],
208
- parameters: 'ExcludedMethods',
209
- alternative: '`ExcludedMethods` has been renamed to `IgnoredMethods`.',
210
- severity: :warning
211
- }
212
- ].freeze
213
-
214
- OBSOLETE_ENFORCED_STYLES = [
215
- {
216
- cop: 'Layout/IndentationConsistency',
217
- parameter: 'EnforcedStyle',
218
- enforced_style: 'rails',
219
- alternative: '`EnforcedStyle: rails` has been renamed to ' \
220
- '`EnforcedStyle: indented_internal_methods`'
221
- }
222
- ].freeze
223
-
224
- attr_reader :warnings
30
+ # Can be extended by extension libraries to add their own obsoletions
31
+ self.files = [DEFAULT_RULES_FILE]
225
32
 
226
33
  def initialize(config)
227
34
  @config = config
35
+ @rules = load_rules
228
36
  @warnings = []
229
37
  end
230
38
 
231
- def reject_obsolete_cops_and_parameters
232
- messages = [obsolete_cops, obsolete_parameters,
233
- obsolete_enforced_style].flatten.compact
39
+ def reject_obsolete!
40
+ messages = obsoletions.flatten.compact
234
41
  return if messages.empty?
235
42
 
236
43
  raise ValidationError, messages.join("\n")
@@ -238,64 +45,59 @@ module RuboCop
238
45
 
239
46
  private
240
47
 
241
- def obsolete_cops
242
- OBSOLETE_COPS.map do |cop_name, message|
243
- next unless @config.key?(cop_name) ||
244
- @config.key?(Cop::Badge.parse(cop_name).cop_name)
245
-
246
- message + "\n(obsolete configuration found in " \
247
- "#{smart_loaded_path}, please update it)"
48
+ # Default rules for obsoletions are in config/obsoletion.yml
49
+ # Additional rules files can be added with `RuboCop::ConfigObsoletion.files << filename`
50
+ def load_rules
51
+ rules = self.class.files.each_with_object({}) do |filename, hash|
52
+ hash.merge!(YAML.safe_load(File.read(filename))) do |_key, first, second|
53
+ first.merge(second)
54
+ end
248
55
  end
249
- end
250
56
 
251
- def obsolete_enforced_style
252
- OBSOLETE_ENFORCED_STYLES.map do |params|
253
- obsolete_enforced_style_message(params[:cop], params[:parameter],
254
- params[:enforced_style],
255
- params[:alternative])
256
- end
257
- end
57
+ cop_rules = rules.slice(*COP_RULE_CLASSES.keys)
58
+ parameter_rules = rules.slice(*PARAMETER_RULE_CLASSES.keys)
258
59
 
259
- def obsolete_enforced_style_message(cop, param, enforced_style, alternative)
260
- style = @config[cop]&.detect { |key, _| key.start_with?(param) }
60
+ load_cop_rules(cop_rules).concat(load_parameter_rules(parameter_rules))
61
+ end
261
62
 
262
- return unless style && style[1] == enforced_style
63
+ # Cop rules are keyed by the name of the original cop
64
+ def load_cop_rules(rules)
65
+ rules.flat_map do |rule_type, data|
66
+ data.map do |cop_name, configuration|
67
+ next unless configuration # allow configurations to be disabled with `CopName: ~`
263
68
 
264
- "obsolete `#{param}: #{enforced_style}` (for #{cop}) found in " \
265
- "#{smart_loaded_path}\n#{alternative}"
69
+ COP_RULE_CLASSES[rule_type].new(@config, cop_name, configuration)
70
+ end.compact
71
+ end
266
72
  end
267
73
 
268
- def obsolete_parameters
269
- OBSOLETE_PARAMETERS.collect do |params|
270
- messages = obsolete_parameter_message(params[:cops], params[:parameters],
271
- params[:alternative])
272
-
273
- # Warnings are collected separately and not added to the error message
274
- if messages && params.fetch(:severity, :error) == :warning
275
- @warnings.concat(messages)
276
- next
74
+ # Parameter rules may apply to multiple cops and multiple parameters
75
+ # and are given as an array. Each combination is turned into a separate
76
+ # rule object.
77
+ def load_parameter_rules(rules)
78
+ rules.flat_map do |rule_type, data|
79
+ data.flat_map do |configuration|
80
+ cops = Array(configuration['cops'])
81
+ parameters = Array(configuration['parameters'])
82
+
83
+ cops.product(parameters).map do |cop, parameter|
84
+ PARAMETER_RULE_CLASSES[rule_type].new(@config, cop, parameter, configuration)
85
+ end
277
86
  end
278
-
279
- messages
280
87
  end
281
88
  end
282
89
 
283
- def obsolete_parameter_message(cops, parameters, alternative)
284
- Array(cops).map do |cop|
285
- obsolete_parameters = Array(parameters).select do |param|
286
- @config[cop]&.key?(param)
287
- end
288
- next if obsolete_parameters.empty?
90
+ def obsoletions
91
+ rules.map do |rule|
92
+ next unless rule.violated?
289
93
 
290
- obsolete_parameters.map do |parameter|
291
- "obsolete parameter #{parameter} (for #{cop}) found in " \
292
- "#{smart_loaded_path}\n#{alternative}"
94
+ if rule.warning?
95
+ @warnings.push(rule.message)
96
+ next
293
97
  end
294
- end
295
- end
296
98
 
297
- def smart_loaded_path
298
- PathUtil.smart_path(@config.loaded_path)
99
+ rule.message
100
+ end
299
101
  end
300
102
  end
301
103
  end