rubocop 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
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