rubocop 0.73.0 → 0.77.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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/bin/console +1 -0
  4. data/config/default.yml +332 -295
  5. data/lib/rubocop.rb +46 -30
  6. data/lib/rubocop/ast/builder.rb +1 -0
  7. data/lib/rubocop/ast/node.rb +6 -8
  8. data/lib/rubocop/ast/node/block_node.rb +2 -0
  9. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +1 -12
  10. data/lib/rubocop/ast/node/return_node.rb +24 -0
  11. data/lib/rubocop/cli.rb +11 -227
  12. data/lib/rubocop/cli/command.rb +21 -0
  13. data/lib/rubocop/cli/command/auto_genenerate_config.rb +105 -0
  14. data/lib/rubocop/cli/command/base.rb +33 -0
  15. data/lib/rubocop/cli/command/execute_runner.rb +76 -0
  16. data/lib/rubocop/cli/command/init_dotfile.rb +45 -0
  17. data/lib/rubocop/cli/command/show_cops.rb +73 -0
  18. data/lib/rubocop/cli/command/version.rb +17 -0
  19. data/lib/rubocop/cli/environment.rb +21 -0
  20. data/lib/rubocop/comment_config.rb +5 -4
  21. data/lib/rubocop/config.rb +28 -537
  22. data/lib/rubocop/config_loader.rb +21 -3
  23. data/lib/rubocop/config_loader_resolver.rb +4 -3
  24. data/lib/rubocop/config_obsoletion.rb +275 -0
  25. data/lib/rubocop/config_validator.rb +246 -0
  26. data/lib/rubocop/cop/autocorrect_logic.rb +2 -2
  27. data/lib/rubocop/cop/bundler/gem_comment.rb +4 -4
  28. data/lib/rubocop/cop/commissioner.rb +15 -7
  29. data/lib/rubocop/cop/cop.rb +33 -9
  30. data/lib/rubocop/cop/corrector.rb +8 -7
  31. data/lib/rubocop/cop/correctors/alignment_corrector.rb +43 -17
  32. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +2 -2
  33. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  34. data/lib/rubocop/cop/correctors/space_corrector.rb +1 -2
  35. data/lib/rubocop/cop/generator.rb +3 -3
  36. data/lib/rubocop/cop/generator/configuration_injector.rb +9 -4
  37. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  38. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  39. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +59 -0
  40. data/lib/rubocop/cop/layout/{align_arguments.rb → argument_alignment.rb} +1 -1
  41. data/lib/rubocop/cop/layout/{align_array.rb → array_alignment.rb} +1 -1
  42. data/lib/rubocop/cop/layout/{indent_assignment.rb → assignment_indentation.rb} +11 -2
  43. data/lib/rubocop/cop/layout/block_alignment.rb +2 -2
  44. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  45. data/lib/rubocop/cop/layout/comment_indentation.rb +10 -13
  46. data/lib/rubocop/cop/layout/empty_comment.rb +7 -16
  47. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +22 -7
  48. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +2 -2
  49. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +2 -2
  50. data/lib/rubocop/cop/layout/end_of_line.rb +8 -3
  51. data/lib/rubocop/cop/layout/extra_spacing.rb +15 -60
  52. data/lib/rubocop/cop/layout/{indent_first_argument.rb → first_argument_indentation.rb} +12 -10
  53. data/lib/rubocop/cop/layout/{indent_first_array_element.rb → first_array_element_indentation.rb} +4 -4
  54. data/lib/rubocop/cop/layout/{indent_first_hash_element.rb → first_hash_element_indentation.rb} +4 -4
  55. data/lib/rubocop/cop/layout/{indent_first_parameter.rb → first_parameter_indentation.rb} +3 -3
  56. data/lib/rubocop/cop/layout/{align_hash.rb → hash_alignment.rb} +8 -4
  57. data/lib/rubocop/cop/layout/{indent_heredoc.rb → heredoc_indentation.rb} +2 -2
  58. data/lib/rubocop/cop/layout/indentation_width.rb +19 -5
  59. data/lib/rubocop/cop/layout/{leading_blank_lines.rb → leading_empty_lines.rb} +1 -1
  60. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  61. data/lib/rubocop/cop/layout/multiline_block_layout.rb +24 -2
  62. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +1 -1
  63. data/lib/rubocop/cop/layout/{align_parameters.rb → parameter_alignment.rb} +1 -1
  64. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +2 -0
  65. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +5 -1
  66. data/lib/rubocop/cop/layout/space_around_keyword.rb +12 -0
  67. data/lib/rubocop/cop/layout/space_around_operators.rb +43 -24
  68. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -7
  69. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +8 -5
  70. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +7 -0
  71. data/lib/rubocop/cop/layout/space_inside_parens.rb +6 -6
  72. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +24 -40
  73. data/lib/rubocop/cop/layout/{trailing_blank_lines.rb → trailing_empty_lines.rb} +1 -1
  74. data/lib/rubocop/cop/layout/trailing_whitespace.rb +18 -2
  75. data/lib/rubocop/cop/lint/assignment_in_condition.rb +17 -4
  76. data/lib/rubocop/cop/lint/debugger.rb +1 -3
  77. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +1 -1
  78. data/lib/rubocop/cop/lint/{duplicated_key.rb → duplicate_hash_key.rb} +1 -1
  79. data/lib/rubocop/cop/lint/empty_interpolation.rb +4 -4
  80. data/lib/rubocop/cop/lint/erb_new_arguments.rb +61 -4
  81. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +10 -36
  82. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -1
  83. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +7 -8
  84. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +2 -2
  85. data/lib/rubocop/cop/lint/{multiple_compare.rb → multiple_comparison.rb} +1 -1
  86. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  87. data/lib/rubocop/cop/lint/{unneeded_cop_disable_directive.rb → redundant_cop_disable_directive.rb} +24 -24
  88. data/lib/rubocop/cop/lint/{unneeded_cop_enable_directive.rb → redundant_cop_enable_directive.rb} +6 -8
  89. data/lib/rubocop/cop/lint/{unneeded_require_statement.rb → redundant_require_statement.rb} +1 -1
  90. data/lib/rubocop/cop/lint/{unneeded_splat_expansion.rb → redundant_splat_expansion.rb} +12 -7
  91. data/lib/rubocop/cop/lint/{string_conversion_in_interpolation.rb → redundant_string_coercion.rb} +7 -7
  92. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  93. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  94. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +5 -6
  95. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +91 -0
  96. data/lib/rubocop/cop/lint/{handle_exceptions.rb → suppressed_exception.rb} +1 -1
  97. data/lib/rubocop/cop/lint/unused_block_argument.rb +22 -6
  98. data/lib/rubocop/cop/lint/unused_method_argument.rb +23 -5
  99. data/lib/rubocop/cop/lint/useless_access_modifier.rb +57 -23
  100. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -1
  101. data/lib/rubocop/cop/lint/void.rb +7 -26
  102. data/lib/rubocop/cop/message_annotator.rb +16 -7
  103. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  104. data/lib/rubocop/cop/metrics/line_length.rb +48 -42
  105. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  106. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +23 -6
  107. data/lib/rubocop/cop/migration/department_name.rb +44 -0
  108. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  109. data/lib/rubocop/cop/mixin/documentation_comment.rb +0 -2
  110. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  111. data/lib/rubocop/cop/mixin/{hash_alignment.rb → hash_alignment_styles.rb} +1 -1
  112. data/lib/rubocop/cop/mixin/interpolation.rb +27 -0
  113. data/lib/rubocop/cop/mixin/method_complexity.rb +2 -1
  114. data/lib/rubocop/cop/mixin/nil_methods.rb +4 -4
  115. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +87 -0
  116. data/lib/rubocop/cop/mixin/statement_modifier.rb +5 -2
  117. data/lib/rubocop/cop/mixin/surrounding_space.rb +7 -5
  118. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -6
  119. data/lib/rubocop/cop/naming/{uncommunicative_block_param_name.rb → block_parameter_name.rb} +3 -3
  120. data/lib/rubocop/cop/naming/file_name.rb +12 -5
  121. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -5
  122. data/lib/rubocop/cop/naming/method_name.rb +12 -1
  123. data/lib/rubocop/cop/naming/{uncommunicative_method_param_name.rb → method_parameter_name.rb} +3 -3
  124. data/lib/rubocop/cop/naming/predicate_name.rb +6 -6
  125. data/lib/rubocop/cop/naming/variable_name.rb +1 -0
  126. data/lib/rubocop/cop/offense.rb +18 -7
  127. data/lib/rubocop/cop/registry.rb +22 -1
  128. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -0
  129. data/lib/rubocop/cop/style/alias.rb +1 -1
  130. data/lib/rubocop/cop/style/array_join.rb +1 -1
  131. data/lib/rubocop/cop/style/attr.rb +2 -2
  132. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  133. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +35 -16
  134. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  135. data/lib/rubocop/cop/style/comment_annotation.rb +5 -5
  136. data/lib/rubocop/cop/style/commented_keyword.rb +16 -30
  137. data/lib/rubocop/cop/style/conditional_assignment.rb +5 -7
  138. data/lib/rubocop/cop/style/constant_visibility.rb +13 -2
  139. data/lib/rubocop/cop/style/copyright.rb +11 -7
  140. data/lib/rubocop/cop/style/documentation_method.rb +44 -0
  141. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +10 -4
  142. data/lib/rubocop/cop/style/empty_case_condition.rb +2 -2
  143. data/lib/rubocop/cop/style/empty_literal.rb +2 -2
  144. data/lib/rubocop/cop/style/empty_method.rb +5 -5
  145. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  146. data/lib/rubocop/cop/style/even_odd.rb +1 -1
  147. data/lib/rubocop/cop/style/expand_path_arguments.rb +1 -1
  148. data/lib/rubocop/cop/style/format_string.rb +10 -7
  149. data/lib/rubocop/cop/style/format_string_token.rb +19 -68
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +28 -33
  151. data/lib/rubocop/cop/style/guard_clause.rb +39 -10
  152. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  153. data/lib/rubocop/cop/style/if_unless_modifier.rb +58 -15
  154. data/lib/rubocop/cop/style/infinite_loop.rb +5 -4
  155. data/lib/rubocop/cop/style/inverse_methods.rb +19 -13
  156. data/lib/rubocop/cop/style/ip_addresses.rb +4 -4
  157. data/lib/rubocop/cop/style/lambda.rb +0 -2
  158. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -10
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -25
  160. data/lib/rubocop/cop/style/method_def_parentheses.rb +17 -9
  161. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  162. data/lib/rubocop/cop/style/mixin_usage.rb +11 -1
  163. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  164. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  165. data/lib/rubocop/cop/style/nested_modifier.rb +22 -4
  166. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +5 -5
  167. data/lib/rubocop/cop/style/next.rb +5 -5
  168. data/lib/rubocop/cop/style/non_nil_check.rb +21 -9
  169. data/lib/rubocop/cop/style/numeric_literals.rb +7 -3
  170. data/lib/rubocop/cop/style/option_hash.rb +3 -3
  171. data/lib/rubocop/cop/style/or_assignment.rb +6 -1
  172. data/lib/rubocop/cop/style/parentheses_around_condition.rb +14 -0
  173. data/lib/rubocop/cop/style/{unneeded_capital_w.rb → redundant_capital_w.rb} +1 -1
  174. data/lib/rubocop/cop/style/{unneeded_condition.rb → redundant_condition.rb} +3 -3
  175. data/lib/rubocop/cop/style/{unneeded_interpolation.rb → redundant_interpolation.rb} +1 -1
  176. data/lib/rubocop/cop/style/redundant_parentheses.rb +16 -7
  177. data/lib/rubocop/cop/style/{unneeded_percent_q.rb → redundant_percent_q.rb} +1 -1
  178. data/lib/rubocop/cop/style/redundant_return.rb +39 -29
  179. data/lib/rubocop/cop/style/redundant_self.rb +18 -1
  180. data/lib/rubocop/cop/style/{unneeded_sort.rb → redundant_sort.rb} +5 -5
  181. data/lib/rubocop/cop/style/rescue_modifier.rb +24 -0
  182. data/lib/rubocop/cop/style/safe_navigation.rb +23 -3
  183. data/lib/rubocop/cop/style/semicolon.rb +13 -2
  184. data/lib/rubocop/cop/style/single_line_methods.rb +8 -1
  185. data/lib/rubocop/cop/style/special_global_vars.rb +5 -7
  186. data/lib/rubocop/cop/style/ternary_parentheses.rb +19 -0
  187. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +4 -6
  188. data/lib/rubocop/cop/style/trivial_accessors.rb +5 -5
  189. data/lib/rubocop/cop/style/variable_interpolation.rb +6 -16
  190. data/lib/rubocop/cop/team.rb +5 -0
  191. data/lib/rubocop/cop/util.rb +1 -1
  192. data/lib/rubocop/cop/utils/format_string.rb +120 -0
  193. data/lib/rubocop/cop/variable_force.rb +7 -5
  194. data/lib/rubocop/cop/variable_force/variable.rb +15 -2
  195. data/lib/rubocop/core_ext/string.rb +0 -24
  196. data/lib/rubocop/formatter/clang_style_formatter.rb +9 -6
  197. data/lib/rubocop/formatter/emacs_style_formatter.rb +22 -9
  198. data/lib/rubocop/formatter/file_list_formatter.rb +1 -1
  199. data/lib/rubocop/formatter/formatter_set.rb +16 -15
  200. data/lib/rubocop/formatter/pacman_formatter.rb +80 -0
  201. data/lib/rubocop/formatter/simple_text_formatter.rb +16 -4
  202. data/lib/rubocop/formatter/tap_formatter.rb +18 -7
  203. data/lib/rubocop/magic_comment.rb +4 -0
  204. data/lib/rubocop/node_pattern.rb +3 -1
  205. data/lib/rubocop/options.rb +17 -22
  206. data/lib/rubocop/path_util.rb +1 -1
  207. data/lib/rubocop/processed_source.rb +5 -1
  208. data/lib/rubocop/rake_task.rb +1 -0
  209. data/lib/rubocop/result_cache.rb +22 -8
  210. data/lib/rubocop/rspec/expect_offense.rb +4 -1
  211. data/lib/rubocop/runner.rb +55 -32
  212. data/lib/rubocop/target_finder.rb +12 -6
  213. data/lib/rubocop/version.rb +1 -1
  214. metadata +47 -32
  215. data/lib/rubocop/cop/mixin/ignored_method_patterns.rb +0 -19
  216. data/lib/rubocop/cop/mixin/safe_mode.rb +0 -22
@@ -46,7 +46,7 @@ module RuboCop
46
46
 
47
47
  add_missing_namespaces(path, hash)
48
48
 
49
- resolver.resolve_inheritance_from_gems(hash, hash.delete('inherit_gem'))
49
+ resolver.resolve_inheritance_from_gems(hash)
50
50
  resolver.resolve_inheritance(path, hash, file, debug?)
51
51
 
52
52
  hash.delete('inherit_from')
@@ -199,6 +199,8 @@ module RuboCop
199
199
  raise(TypeError, "Malformed configuration in #{absolute_path}")
200
200
  end
201
201
 
202
+ check_cop_config_value(hash)
203
+
202
204
  hash
203
205
  end
204
206
 
@@ -220,6 +222,22 @@ module RuboCop
220
222
  end
221
223
  end
222
224
 
225
+ def check_cop_config_value(hash, parent = nil)
226
+ hash.each do |key, value|
227
+ check_cop_config_value(value, key) if value.is_a?(Hash)
228
+
229
+ next unless %w[Enabled
230
+ Safe
231
+ SafeAutoCorrect
232
+ AutoCorrect].include?(key) && value.is_a?(String)
233
+
234
+ abort(
235
+ "Property #{Rainbow(key).yellow} of cop #{Rainbow(parent).yellow}" \
236
+ " is supposed to be a boolean and #{Rainbow(value).yellow} is not."
237
+ )
238
+ end
239
+ end
240
+
223
241
  # Read the specified file, or exit with a friendly, concise message on
224
242
  # stderr. Care is taken to use the standard OS exit code for a "file not
225
243
  # found" error.
@@ -240,11 +258,11 @@ module RuboCop
240
258
  yaml_code,
241
259
  permitted_classes: [Regexp, Symbol],
242
260
  permitted_symbols: [],
243
- aliases: false,
261
+ aliases: true,
244
262
  filename: filename
245
263
  )
246
264
  else
247
- YAML.safe_load(yaml_code, [Regexp, Symbol], [], false, filename)
265
+ YAML.safe_load(yaml_code, [Regexp, Symbol], [], true, filename)
248
266
  end
249
267
  end
250
268
  end
@@ -35,7 +35,8 @@ module RuboCop
35
35
  end
36
36
  end
37
37
 
38
- def resolve_inheritance_from_gems(hash, gems)
38
+ def resolve_inheritance_from_gems(hash)
39
+ gems = hash.delete('inherit_gem')
39
40
  (gems || {}).each_pair do |gem_name, config_path|
40
41
  if gem_name == 'rubocop'
41
42
  raise ArgumentError,
@@ -73,7 +74,7 @@ module RuboCop
73
74
 
74
75
  opts = { inherit_mode: config['inherit_mode'] || {},
75
76
  unset_nil: unset_nil }
76
- Config.new(merge(default_configuration, config, opts), config_file)
77
+ Config.new(merge(default_configuration, config, **opts), config_file)
77
78
  end
78
79
 
79
80
  # Return a recursive merge of two hashes. That is, a normal hash merge,
@@ -92,7 +93,7 @@ module RuboCop
92
93
  elsif should_union?(base_hash, key, opts[:inherit_mode])
93
94
  result[key] = base_hash[key] | derived_hash[key]
94
95
  elsif opts[:debug]
95
- warn_on_duplicate_setting(base_hash, derived_hash, key, opts)
96
+ warn_on_duplicate_setting(base_hash, derived_hash, key, **opts)
96
97
  end
97
98
  end
98
99
  result
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This class handles obsolete configuration.
5
+ class ConfigObsoletion
6
+ RENAMED_COPS = {
7
+ 'Layout/AlignArguments' => 'Layout/ArgumentAlignment',
8
+ 'Layout/AlignArray' => 'Layout/ArrayAlignment',
9
+ 'Layout/AlignHash' => 'Layout/HashAlignment',
10
+ 'Layout/AlignParameters' => 'Layout/ParameterAlignment',
11
+ 'Layout/IndentArray' => 'Layout/FirstArrayElementIndentation',
12
+ 'Layout/IndentAssignment' => 'Layout/AssignmentIndentation',
13
+ 'Layout/IndentFirstArgument' => 'Layout/FirstArgumentIndentation',
14
+ 'Layout/IndentFirstArrayElement' => 'Layout/FirstArrayElementIndentation',
15
+ 'Layout/IndentFirstHashElement' => 'Layout/FirstHashElementIndentation',
16
+ 'Layout/IndentFirstParameter' => 'Layout/FirstParameterIndentation',
17
+ 'Layout/IndentHash' => 'Layout/FirstHashElementIndentation',
18
+ 'Layout/IndentHeredoc' => 'Layout/HeredocIndentation',
19
+ 'Layout/LeadingBlankLines' => 'Layout/LeadingEmptyLines',
20
+ 'Layout/TrailingBlankLines' => 'Layout/TrailingEmptyLines',
21
+ 'Lint/DuplicatedKey' => 'Lint/DuplicateHashKey',
22
+ 'Lint/HandleExceptions' => 'Lint/SuppressedException',
23
+ 'Lint/MultipleCompare' => 'Lint/MultipleComparison',
24
+ 'Lint/StringConversionInInterpolation' => 'Lint/RedundantStringCoercion',
25
+ 'Lint/UnneededCopDisableDirective' => 'Lint/RedundantCopDisableDirective',
26
+ 'Lint/UnneededCopEnableDirective' => 'Lint/RedundantCopEnableDirective',
27
+ 'Lint/UnneededRequireStatement' => 'Lint/RedundantRequireStatement',
28
+ 'Lint/UnneededSplatExpansion' => 'Lint/RedundantSplatExpansion',
29
+ 'Naming/UncommunicativeBlockParamName' => 'Naming/BlockParameterName',
30
+ 'Naming/UncommunicativeMethodParamName' => 'Naming/MethodParameterName',
31
+ 'Style/DeprecatedHashMethods' => 'Style/PreferredHashMethods',
32
+ 'Style/MethodCallParentheses' => 'Style/MethodCallWithoutArgsParentheses',
33
+ 'Style/OpMethod' => 'Naming/BinaryOperatorParameterName',
34
+ 'Style/SingleSpaceBeforeFirstArg' => 'Layout/SpaceBeforeFirstArg',
35
+ 'Style/UnneededCapitalW' => 'Style/RedundantCapitalW',
36
+ 'Style/UnneededCondition' => 'Style/RedundantCondition',
37
+ 'Style/UnneededInterpolation' => 'Style/RedundantInterpolation',
38
+ 'Style/UnneededPercentQ' => 'Style/RedundantPercentQ',
39
+ 'Style/UnneededSort' => 'Style/RedundantSort'
40
+ }.map do |old_name, new_name|
41
+ [old_name, "The `#{old_name}` cop has been renamed to `#{new_name}`."]
42
+ end
43
+
44
+ MOVED_COPS = {
45
+ 'Security' => 'Lint/Eval',
46
+ 'Naming' => %w[Style/ClassAndModuleCamelCase Style/ConstantName
47
+ Style/FileName Style/MethodName Style/PredicateName
48
+ Style/VariableName Style/VariableNumber
49
+ Style/AccessorMethodName Style/AsciiIdentifiers],
50
+ 'Layout' => %w[Lint/BlockAlignment Lint/EndAlignment
51
+ Lint/DefEndAlignment],
52
+ 'Lint' => 'Style/FlipFlop'
53
+ }.map do |new_department, old_names|
54
+ Array(old_names).map do |old_name|
55
+ [old_name, "The `#{old_name}` cop has been moved to " \
56
+ "`#{new_department}/#{old_name.split('/').last}`."]
57
+ end
58
+ end
59
+
60
+ REMOVED_COPS = {
61
+ 'Layout/SpaceAfterControlKeyword' => 'Layout/SpaceAroundKeyword',
62
+ 'Layout/SpaceBeforeModifierKeyword' => 'Layout/SpaceAroundKeyword',
63
+ 'Lint/RescueWithoutErrorClass' => 'Style/RescueStandardError',
64
+ 'Rails/DefaultScope' => nil,
65
+ 'Style/SpaceAfterControlKeyword' => 'Layout/SpaceAroundKeyword',
66
+ 'Style/SpaceBeforeModifierKeyword' => 'Layout/SpaceAroundKeyword',
67
+ 'Style/TrailingComma' => 'Style/TrailingCommaInArguments, ' \
68
+ 'Style/TrailingCommaInArrayLiteral, and/or ' \
69
+ 'Style/TrailingCommaInHashLiteral',
70
+ 'Style/TrailingCommaInLiteral' => 'Style/TrailingCommaInArrayLiteral ' \
71
+ 'and/or ' \
72
+ 'Style/TrailingCommaInHashLiteral'
73
+ }.map do |old_name, other_cops|
74
+ if other_cops
75
+ more = ". Please use #{other_cops} instead".gsub(%r{[A-Z]\w+/\w+},
76
+ '`\&`')
77
+ end
78
+ [old_name, "The `#{old_name}` cop has been removed#{more}."]
79
+ end
80
+
81
+ REMOVED_COPS_WITH_REASON = {
82
+ 'Lint/InvalidCharacterLiteral' => 'it was never being actually triggered',
83
+ 'Lint/SpaceBeforeFirstArg' =>
84
+ 'it was a duplicate of `Layout/SpaceBeforeFirstArg`. Please use ' \
85
+ '`Layout/SpaceBeforeFirstArg` instead'
86
+ }.map do |cop_name, reason|
87
+ [cop_name, "The `#{cop_name}` cop has been removed since #{reason}."]
88
+ end
89
+
90
+ SPLIT_COPS = {
91
+ 'Style/MethodMissing' =>
92
+ 'The `Style/MethodMissing` cop has been split into ' \
93
+ '`Style/MethodMissingSuper` and `Style/MissingRespondToMissing`.'
94
+ }.to_a
95
+
96
+ OBSOLETE_COPS = Hash[*(RENAMED_COPS + MOVED_COPS + REMOVED_COPS +
97
+ REMOVED_COPS_WITH_REASON + SPLIT_COPS).flatten]
98
+
99
+ OBSOLETE_PARAMETERS = [
100
+ {
101
+ cops: %w[Layout/SpaceAroundOperators Style/SpaceAroundOperators],
102
+ parameters: 'MultiSpaceAllowedForOperators',
103
+ alternative: 'If your intention was to allow extra spaces for ' \
104
+ 'alignment, please use AllowForAlignment: true instead.'
105
+ },
106
+ {
107
+ cops: 'Style/Encoding',
108
+ parameters: %w[EnforcedStyle SupportedStyles
109
+ AutoCorrectEncodingComment],
110
+ alternative: 'Style/Encoding no longer supports styles. ' \
111
+ 'The "never" behavior is always assumed.'
112
+ },
113
+ {
114
+ cops: 'Style/IfUnlessModifier',
115
+ parameters: 'MaxLineLength',
116
+ alternative: '`Style/IfUnlessModifier: MaxLineLength` has been ' \
117
+ 'removed. Use `Metrics/LineLength: Max` instead'
118
+ },
119
+ {
120
+ cops: 'Style/WhileUntilModifier',
121
+ parameters: 'MaxLineLength',
122
+ alternative: '`Style/WhileUntilModifier: MaxLineLength` has been ' \
123
+ 'removed. Use `Metrics/LineLength: Max` instead'
124
+ },
125
+ {
126
+ cops: 'AllCops',
127
+ parameters: 'RunRailsCops',
128
+ alternative: "Use the following configuration instead:\n" \
129
+ "Rails:\n Enabled: true"
130
+ },
131
+ {
132
+ cops: 'Layout/CaseIndentation',
133
+ parameters: 'IndentWhenRelativeTo',
134
+ alternative: '`IndentWhenRelativeTo` has been renamed to ' \
135
+ '`EnforcedStyle`'
136
+ },
137
+ {
138
+ cops: %w[Lint/BlockAlignment Layout/BlockAlignment Lint/EndAlignment
139
+ Layout/EndAlignment Lint/DefEndAlignment
140
+ Layout/DefEndAlignment],
141
+ parameters: 'AlignWith',
142
+ alternative: '`AlignWith` has been renamed to `EnforcedStyleAlignWith`'
143
+ },
144
+ {
145
+ cops: 'Rails/UniqBeforePluck',
146
+ parameters: 'EnforcedMode',
147
+ alternative: '`EnforcedMode` has been renamed to `EnforcedStyle`'
148
+ },
149
+ {
150
+ cops: 'Style/MethodCallWithArgsParentheses',
151
+ parameters: 'IgnoredMethodPatterns',
152
+ alternative: '`IgnoredMethodPatterns` has been renamed to ' \
153
+ '`IgnoredPatterns`'
154
+ },
155
+ {
156
+ cops: %w[Performance/Count Performance/Detect],
157
+ parameters: 'SafeMode',
158
+ alternative: '`SafeMode` has been removed. ' \
159
+ 'Use `SafeAutoCorrect` instead.'
160
+ },
161
+ {
162
+ cops: 'Bundler/GemComment',
163
+ parameters: 'Whitelist',
164
+ alternative: '`Whitelist` has been renamed to `IgnoredGems`.'
165
+ },
166
+ {
167
+ cops: %w[
168
+ Lint/SafeNavigationChain Lint/SafeNavigationConsistency
169
+ Style/NestedParenthesizedCalls Style/SafeNavigation
170
+ Style/TrivialAccessors
171
+ ],
172
+ parameters: 'Whitelist',
173
+ alternative: '`Whitelist` has been renamed to `AllowedMethods`.'
174
+ },
175
+ {
176
+ cops: 'Style/IpAddresses',
177
+ parameters: 'Whitelist',
178
+ alternative: '`Whitelist` has been renamed to `AllowedAddresses`.'
179
+ },
180
+ {
181
+ cops: 'Naming/HeredocDelimiterNaming',
182
+ parameters: 'Blacklist',
183
+ alternative: '`Blacklist` has been renamed to `ForbiddenDelimiters`.'
184
+ },
185
+ {
186
+ cops: 'Naming/PredicateName',
187
+ parameters: 'NamePrefixBlacklist',
188
+ alternative: '`NamePrefixBlacklist` has been renamed to ' \
189
+ '`ForbiddenPrefixes`.'
190
+ },
191
+ {
192
+ cops: 'Naming/PredicateName',
193
+ parameters: 'NameWhitelist',
194
+ alternative: '`NameWhitelist` has been renamed to ' \
195
+ '`AllowedMethods`.'
196
+ }
197
+ ].freeze
198
+
199
+ OBSOLETE_ENFORCED_STYLES = [
200
+ {
201
+ cop: 'Layout/IndentationConsistency',
202
+ parameter: 'EnforcedStyle',
203
+ enforced_style: 'rails',
204
+ alternative: '`EnforcedStyle: rails` has been renamed to ' \
205
+ '`EnforcedStyle: indented_internal_methods`'
206
+ }
207
+ ].freeze
208
+
209
+ def initialize(config)
210
+ @config = config
211
+ end
212
+
213
+ def reject_obsolete_cops_and_parameters
214
+ messages = [obsolete_cops, obsolete_parameters,
215
+ obsolete_enforced_style].flatten.compact
216
+ return if messages.empty?
217
+
218
+ raise ValidationError, messages.join("\n")
219
+ end
220
+
221
+ private
222
+
223
+ def obsolete_cops
224
+ OBSOLETE_COPS.map do |cop_name, message|
225
+ next unless @config.key?(cop_name) ||
226
+ @config.key?(Cop::Badge.parse(cop_name).cop_name)
227
+
228
+ message + "\n(obsolete configuration found in " \
229
+ "#{smart_loaded_path}, please update it)"
230
+ end
231
+ end
232
+
233
+ def obsolete_enforced_style
234
+ OBSOLETE_ENFORCED_STYLES.map do |params|
235
+ obsolete_enforced_style_message(params[:cop], params[:parameter],
236
+ params[:enforced_style],
237
+ params[:alternative])
238
+ end
239
+ end
240
+
241
+ def obsolete_enforced_style_message(cop, param, enforced_style, alternative)
242
+ style = @config[cop]&.detect { |key, _| key.start_with?(param) }
243
+
244
+ return unless style && style[1] == enforced_style
245
+
246
+ "obsolete `#{param}: #{enforced_style}` (for #{cop}) found in " \
247
+ "#{smart_loaded_path}\n#{alternative}"
248
+ end
249
+
250
+ def obsolete_parameters
251
+ OBSOLETE_PARAMETERS.map do |params|
252
+ obsolete_parameter_message(params[:cops], params[:parameters],
253
+ params[:alternative])
254
+ end
255
+ end
256
+
257
+ def obsolete_parameter_message(cops, parameters, alternative)
258
+ Array(cops).map do |cop|
259
+ obsolete_parameters = Array(parameters).select do |param|
260
+ @config[cop]&.key?(param)
261
+ end
262
+ next if obsolete_parameters.empty?
263
+
264
+ obsolete_parameters.map do |parameter|
265
+ "obsolete parameter #{parameter} (for #{cop}) found in " \
266
+ "#{smart_loaded_path}\n#{alternative}"
267
+ end
268
+ end
269
+ end
270
+
271
+ def smart_loaded_path
272
+ PathUtil.smart_path(@config.loaded_path)
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module RuboCop
6
+ # Handles validation of configuration, for example cop names, parameter
7
+ # names, and Ruby versions.
8
+ class ConfigValidator
9
+ extend Forwardable
10
+
11
+ COMMON_PARAMS = %w[Exclude Include Severity inherit_mode
12
+ AutoCorrect StyleGuide Details].freeze
13
+ INTERNAL_PARAMS = %w[Description StyleGuide
14
+ VersionAdded VersionChanged VersionRemoved
15
+ Reference Safe SafeAutoCorrect].freeze
16
+
17
+ # 2.3 is the oldest officially supported Ruby version.
18
+ DEFAULT_RUBY_VERSION = 2.3
19
+ KNOWN_RUBIES = [2.3, 2.4, 2.5, 2.6, 2.7].freeze
20
+ OBSOLETE_RUBIES = {
21
+ 1.9 => '0.50', 2.0 => '0.50', 2.1 => '0.58', 2.2 => '0.69'
22
+ }.freeze
23
+ RUBY_VERSION_FILENAME = '.ruby-version'
24
+
25
+ def_delegators :@config,
26
+ :smart_loaded_path, :for_all_cops, :find_file_upwards,
27
+ :base_dir_for_path_parameters, :bundler_lock_file_path
28
+
29
+ def initialize(config)
30
+ @config = config
31
+ @config_obsoletion = ConfigObsoletion.new(config)
32
+ end
33
+
34
+ def validate
35
+ # Don't validate RuboCop's own files. Avoids infinite recursion.
36
+ return if @config.internal?
37
+
38
+ valid_cop_names, invalid_cop_names = @config.keys.partition do |key|
39
+ ConfigLoader.default_configuration.key?(key)
40
+ end
41
+
42
+ @config_obsoletion.reject_obsolete_cops_and_parameters
43
+
44
+ warn_about_unrecognized_cops(invalid_cop_names)
45
+ check_target_ruby
46
+ validate_parameter_names(valid_cop_names)
47
+ validate_enforced_styles(valid_cop_names)
48
+ validate_syntax_cop
49
+ reject_mutually_exclusive_defaults
50
+ end
51
+
52
+ def target_ruby_version
53
+ @target_ruby_version ||= begin
54
+ if for_all_cops['TargetRubyVersion']
55
+ @target_ruby_version_source = :rubocop_yml
56
+
57
+ for_all_cops['TargetRubyVersion'].to_f
58
+ elsif target_ruby_version_from_version_file
59
+ @target_ruby_version_source = :ruby_version_file
60
+
61
+ target_ruby_version_from_version_file
62
+ elsif target_ruby_version_from_bundler_lock_file
63
+ @target_ruby_version_source = :bundler_lock_file
64
+
65
+ target_ruby_version_from_bundler_lock_file
66
+ else
67
+ DEFAULT_RUBY_VERSION
68
+ end
69
+ end
70
+ end
71
+
72
+ def validate_section_presence(name)
73
+ return unless @config.key?(name) && @config[name].nil?
74
+
75
+ raise ValidationError,
76
+ "empty section #{name} found in #{smart_loaded_path}"
77
+ end
78
+
79
+ private
80
+
81
+ def check_target_ruby
82
+ return if KNOWN_RUBIES.include?(target_ruby_version)
83
+
84
+ msg = if OBSOLETE_RUBIES.include?(target_ruby_version)
85
+ "RuboCop found unsupported Ruby version #{target_ruby_version} " \
86
+ "in #{target_ruby_source}. #{target_ruby_version}-compatible " \
87
+ 'analysis was dropped after version ' \
88
+ "#{OBSOLETE_RUBIES[target_ruby_version]}."
89
+ else
90
+ 'RuboCop found unknown Ruby version ' \
91
+ "#{target_ruby_version.inspect} in #{target_ruby_source}."
92
+ end
93
+
94
+ msg += "\nSupported versions: #{KNOWN_RUBIES.join(', ')}"
95
+
96
+ raise ValidationError, msg
97
+ end
98
+
99
+ def warn_about_unrecognized_cops(invalid_cop_names)
100
+ invalid_cop_names.each do |name|
101
+ # There could be a custom cop with this name. If so, don't warn
102
+ next if Cop::Cop.registry.contains_cop_matching?([name])
103
+
104
+ # Special case for inherit_mode, which is a directive that we keep in
105
+ # the configuration (even though it's not a cop), because it's easier
106
+ # to do so than to pass the value around to various methods.
107
+ next if name == 'inherit_mode'
108
+
109
+ warn Rainbow("Warning: unrecognized cop #{name} found in " \
110
+ "#{smart_loaded_path}").yellow
111
+ end
112
+ end
113
+
114
+ def validate_syntax_cop
115
+ syntax_config = @config['Lint/Syntax']
116
+ default_config = ConfigLoader.default_configuration['Lint/Syntax']
117
+
118
+ return unless syntax_config &&
119
+ default_config.merge(syntax_config) != default_config
120
+
121
+ raise ValidationError,
122
+ "configuration for Syntax cop found in #{smart_loaded_path}\n" \
123
+ 'It\'s not possible to disable this cop.'
124
+ end
125
+
126
+ def validate_parameter_names(valid_cop_names)
127
+ valid_cop_names.each do |name|
128
+ validate_section_presence(name)
129
+ each_invalid_parameter(name) do |param, supported_params|
130
+ warn Rainbow(<<~MESSAGE).yellow
131
+ Warning: #{name} does not support #{param} parameter.
132
+
133
+ Supported parameters are:
134
+
135
+ - #{supported_params.join("\n - ")}
136
+ MESSAGE
137
+ end
138
+ end
139
+ end
140
+
141
+ def each_invalid_parameter(cop_name)
142
+ default_config = ConfigLoader.default_configuration[cop_name]
143
+
144
+ @config[cop_name].each_key do |param|
145
+ next if COMMON_PARAMS.include?(param) || default_config.key?(param)
146
+
147
+ supported_params = default_config.keys - INTERNAL_PARAMS
148
+
149
+ yield param, supported_params
150
+ end
151
+ end
152
+
153
+ def validate_enforced_styles(valid_cop_names)
154
+ valid_cop_names.each do |name|
155
+ styles = @config[name].select { |key, _| key.start_with?('Enforced') }
156
+
157
+ styles.each do |style_name, style|
158
+ supported_key = RuboCop::Cop::Util.to_supported_styles(style_name)
159
+ valid = ConfigLoader.default_configuration[name][supported_key]
160
+
161
+ next unless valid
162
+ next if valid.include?(style)
163
+ next if validate_support_and_has_list(name, style, valid)
164
+
165
+ msg = "invalid #{style_name} '#{style}' for #{name} found in " \
166
+ "#{smart_loaded_path}\n" \
167
+ "Valid choices are: #{valid.join(', ')}"
168
+ raise ValidationError, msg
169
+ end
170
+ end
171
+ end
172
+
173
+ def validate_support_and_has_list(name, formats, valid)
174
+ ConfigLoader.default_configuration[name]['AllowMultipleStyles'] &&
175
+ formats.is_a?(Array) &&
176
+ formats.all? { |format| valid.include?(format) }
177
+ end
178
+
179
+ def target_ruby_source
180
+ case @target_ruby_version_source
181
+ when :ruby_version_file
182
+ "`#{RUBY_VERSION_FILENAME}`"
183
+ when :bundler_lock_file
184
+ "`#{bundler_lock_file_path}`"
185
+ when :rubocop_yml
186
+ "`TargetRubyVersion` parameter (in #{smart_loaded_path})"
187
+ end
188
+ end
189
+
190
+ def ruby_version_file
191
+ @ruby_version_file ||=
192
+ find_file_upwards(RUBY_VERSION_FILENAME, base_dir_for_path_parameters)
193
+ end
194
+
195
+ def target_ruby_version_from_version_file
196
+ file = ruby_version_file
197
+ return unless file && File.file?(file)
198
+
199
+ @target_ruby_version_from_version_file ||=
200
+ File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
201
+ md[:version].to_f
202
+ end
203
+ end
204
+
205
+ def target_ruby_version_from_bundler_lock_file
206
+ @target_ruby_version_from_bundler_lock_file ||=
207
+ read_ruby_version_from_bundler_lock_file
208
+ end
209
+
210
+ def read_ruby_version_from_bundler_lock_file
211
+ lock_file_path = bundler_lock_file_path
212
+ return nil unless lock_file_path
213
+
214
+ in_ruby_section = false
215
+ File.foreach(lock_file_path) do |line|
216
+ # If ruby is in Gemfile.lock or gems.lock, there should be two lines
217
+ # towards the bottom of the file that look like:
218
+ # RUBY VERSION
219
+ # ruby W.X.YpZ
220
+ # We ultimately want to match the "ruby W.X.Y.pZ" line, but there's
221
+ # extra logic to make sure we only start looking once we've seen the
222
+ # "RUBY VERSION" line.
223
+ in_ruby_section ||= line.match(/^\s*RUBY\s*VERSION\s*$/)
224
+ next unless in_ruby_section
225
+
226
+ # We currently only allow this feature to work with MRI ruby. If jruby
227
+ # (or something else) is used by the project, it's lock file will have a
228
+ # line that looks like:
229
+ # RUBY VERSION
230
+ # ruby W.X.YpZ (jruby x.x.x.x)
231
+ # The regex won't match in this situation.
232
+ result = line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)
233
+ return result.captures.first.to_f if result
234
+ end
235
+ end
236
+
237
+ def reject_mutually_exclusive_defaults
238
+ disabled_by_default = for_all_cops['DisabledByDefault']
239
+ enabled_by_default = for_all_cops['EnabledByDefault']
240
+ return unless disabled_by_default && enabled_by_default
241
+
242
+ msg = 'Cops cannot be both enabled by default and disabled by default'
243
+ raise ValidationError, msg
244
+ end
245
+ end
246
+ end