rubocop 0.46.0 → 0.47.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +77 -2
  4. data/config/default.yml +151 -74
  5. data/config/disabled.yml +9 -0
  6. data/config/enabled.yml +49 -9
  7. data/lib/rubocop.rb +36 -8
  8. data/lib/rubocop/ast/builder.rb +59 -0
  9. data/lib/rubocop/ast/node.rb +607 -0
  10. data/lib/rubocop/ast/node/array_node.rb +45 -0
  11. data/lib/rubocop/ast/node/case_node.rb +63 -0
  12. data/lib/rubocop/ast/node/for_node.rb +53 -0
  13. data/lib/rubocop/ast/node/hash_node.rb +102 -0
  14. data/lib/rubocop/ast/node/if_node.rb +136 -0
  15. data/lib/rubocop/ast/node/keyword_splat_node.rb +45 -0
  16. data/lib/rubocop/ast/node/mixin/conditional_node.rb +45 -0
  17. data/lib/rubocop/ast/node/mixin/hash_element_node.rb +125 -0
  18. data/lib/rubocop/ast/node/mixin/modifier_node.rb +17 -0
  19. data/lib/rubocop/ast/node/pair_node.rb +64 -0
  20. data/lib/rubocop/ast/node/until_node.rb +43 -0
  21. data/lib/rubocop/ast/node/when_node.rb +61 -0
  22. data/lib/rubocop/ast/node/while_node.rb +43 -0
  23. data/lib/rubocop/ast/sexp.rb +16 -0
  24. data/lib/rubocop/{ast_node → ast}/traversal.rb +1 -1
  25. data/lib/rubocop/cli.rb +18 -14
  26. data/lib/rubocop/comment_config.rb +1 -3
  27. data/lib/rubocop/config.rb +93 -35
  28. data/lib/rubocop/config_loader.rb +1 -1
  29. data/lib/rubocop/cop/badge.rb +73 -0
  30. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  31. data/lib/rubocop/cop/bundler/ordered_gems.rb +43 -3
  32. data/lib/rubocop/cop/commissioner.rb +17 -6
  33. data/lib/rubocop/cop/cop.rb +25 -112
  34. data/lib/rubocop/cop/lint/ambiguous_operator.rb +9 -4
  35. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +7 -0
  36. data/lib/rubocop/cop/lint/assignment_in_condition.rb +18 -4
  37. data/lib/rubocop/cop/lint/block_alignment.rb +40 -9
  38. data/lib/rubocop/cop/lint/circular_argument_reference.rb +14 -0
  39. data/lib/rubocop/cop/lint/condition_position.rb +14 -16
  40. data/lib/rubocop/cop/lint/debugger.rb +28 -0
  41. data/lib/rubocop/cop/lint/def_end_alignment.rb +21 -1
  42. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +13 -1
  43. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +26 -22
  44. data/lib/rubocop/cop/lint/duplicate_methods.rb +15 -1
  45. data/lib/rubocop/cop/lint/duplicated_key.rb +16 -8
  46. data/lib/rubocop/cop/lint/each_with_object_argument.rb +9 -0
  47. data/lib/rubocop/cop/lint/else_layout.rb +26 -29
  48. data/lib/rubocop/cop/lint/empty_ensure.rb +38 -0
  49. data/lib/rubocop/cop/lint/empty_expression.rb +11 -1
  50. data/lib/rubocop/cop/lint/empty_interpolation.rb +8 -0
  51. data/lib/rubocop/cop/lint/empty_when.rb +14 -16
  52. data/lib/rubocop/cop/lint/end_alignment.rb +48 -28
  53. data/lib/rubocop/cop/lint/end_in_method.rb +23 -0
  54. data/lib/rubocop/cop/lint/ensure_return.rb +21 -0
  55. data/lib/rubocop/cop/lint/float_out_of_range.rb +5 -0
  56. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +29 -4
  57. data/lib/rubocop/cop/lint/handle_exceptions.rb +40 -0
  58. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +7 -2
  59. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +11 -2
  60. data/lib/rubocop/cop/lint/invalid_character_literal.rb +3 -0
  61. data/lib/rubocop/cop/lint/literal_in_condition.rb +34 -36
  62. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -0
  63. data/lib/rubocop/cop/lint/loop.rb +36 -0
  64. data/lib/rubocop/cop/lint/multiple_compare.rb +46 -0
  65. data/lib/rubocop/cop/lint/nested_method_definition.rb +22 -0
  66. data/lib/rubocop/cop/lint/next_without_accumulator.rb +5 -0
  67. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -0
  68. data/lib/rubocop/cop/lint/percent_string_array.rb +27 -13
  69. data/lib/rubocop/cop/lint/percent_symbol_array.rb +14 -4
  70. data/lib/rubocop/cop/lint/rand_one.rb +7 -3
  71. data/lib/rubocop/cop/lint/require_parentheses.rb +20 -19
  72. data/lib/rubocop/cop/lint/rescue_exception.rb +20 -0
  73. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +66 -0
  74. data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -1
  75. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +24 -0
  76. data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +8 -0
  77. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +24 -0
  78. data/lib/rubocop/cop/lint/unified_integer.rb +5 -0
  79. data/lib/rubocop/cop/lint/unneeded_disable.rb +2 -2
  80. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +5 -0
  81. data/lib/rubocop/cop/lint/unreachable_code.rb +17 -0
  82. data/lib/rubocop/cop/lint/unused_block_argument.rb +2 -0
  83. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  84. data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -1
  85. data/lib/rubocop/cop/lint/useless_assignment.rb +18 -0
  86. data/lib/rubocop/cop/lint/useless_comparison.rb +3 -1
  87. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +16 -1
  88. data/lib/rubocop/cop/lint/useless_setter_call.rb +16 -4
  89. data/lib/rubocop/cop/lint/void.rb +52 -0
  90. data/lib/rubocop/cop/message_annotator.rb +102 -0
  91. data/lib/rubocop/cop/metrics/block_length.rb +6 -0
  92. data/lib/rubocop/cop/metrics/block_nesting.rb +17 -5
  93. data/lib/rubocop/cop/metrics/line_length.rb +11 -4
  94. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  95. data/lib/rubocop/cop/mixin/array_syntax.rb +2 -11
  96. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +12 -5
  97. data/lib/rubocop/cop/mixin/configurable_formatting.rb +48 -0
  98. data/lib/rubocop/cop/mixin/configurable_max.rb +3 -3
  99. data/lib/rubocop/cop/mixin/configurable_naming.rb +5 -33
  100. data/lib/rubocop/cop/mixin/configurable_numbering.rb +6 -47
  101. data/lib/rubocop/cop/mixin/documentation_comment.rb +7 -1
  102. data/lib/rubocop/cop/mixin/duplication.rb +46 -0
  103. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +2 -2
  104. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +14 -11
  105. data/lib/rubocop/cop/mixin/hash_alignment.rb +114 -0
  106. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -3
  107. data/lib/rubocop/cop/mixin/negative_conditional.rb +21 -7
  108. data/lib/rubocop/cop/mixin/on_method_def.rb +14 -0
  109. data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -24
  110. data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -13
  111. data/lib/rubocop/cop/mixin/target_ruby_version.rb +16 -0
  112. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -3
  113. data/lib/rubocop/cop/offense.rb +1 -1
  114. data/lib/rubocop/cop/performance/case_when_splat.rb +56 -59
  115. data/lib/rubocop/cop/performance/detect.rb +2 -2
  116. data/lib/rubocop/cop/performance/flat_map.rb +3 -3
  117. data/lib/rubocop/cop/performance/redundant_merge.rb +3 -6
  118. data/lib/rubocop/cop/performance/regexp_match.rb +201 -0
  119. data/lib/rubocop/cop/rails/delegate.rb +2 -2
  120. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +10 -19
  121. data/lib/rubocop/cop/rails/enum_uniqueness.rb +12 -40
  122. data/lib/rubocop/cop/rails/file_path.rb +80 -0
  123. data/lib/rubocop/cop/rails/find_each.rb +5 -14
  124. data/lib/rubocop/cop/rails/http_positional_arguments.rb +30 -24
  125. data/lib/rubocop/cop/rails/not_null_column.rb +23 -0
  126. data/lib/rubocop/cop/rails/reversible_migration.rb +217 -0
  127. data/lib/rubocop/cop/rails/safe_navigation.rb +4 -2
  128. data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -0
  129. data/lib/rubocop/cop/rails/time_zone.rb +1 -1
  130. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +7 -5
  131. data/lib/rubocop/cop/registry.rb +170 -0
  132. data/lib/rubocop/cop/{lint → security}/eval.rb +7 -1
  133. data/lib/rubocop/cop/security/marshal_load.rb +33 -0
  134. data/lib/rubocop/cop/security/yaml_load.rb +37 -0
  135. data/lib/rubocop/cop/style/align_hash.rb +138 -169
  136. data/lib/rubocop/cop/style/and_or.rb +1 -1
  137. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +10 -15
  138. data/lib/rubocop/cop/style/case_indentation.rb +36 -27
  139. data/lib/rubocop/cop/style/conditional_assignment.rb +64 -47
  140. data/lib/rubocop/cop/style/each_with_object.rb +4 -1
  141. data/lib/rubocop/cop/style/else_alignment.rb +14 -20
  142. data/lib/rubocop/cop/style/empty_case_condition.rb +16 -25
  143. data/lib/rubocop/cop/style/empty_else.rb +20 -22
  144. data/lib/rubocop/cop/style/empty_literal.rb +4 -4
  145. data/lib/rubocop/cop/style/empty_method.rb +12 -6
  146. data/lib/rubocop/cop/style/encoding.rb +1 -1
  147. data/lib/rubocop/cop/style/file_name.rb +24 -4
  148. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +1 -1
  149. data/lib/rubocop/cop/style/format_string.rb +17 -48
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +40 -11
  151. data/lib/rubocop/cop/style/guard_clause.rb +11 -17
  152. data/lib/rubocop/cop/style/hash_syntax.rb +24 -42
  153. data/lib/rubocop/cop/style/identical_conditional_branches.rb +40 -28
  154. data/lib/rubocop/cop/style/if_inside_else.rb +6 -9
  155. data/lib/rubocop/cop/style/if_unless_modifier.rb +16 -25
  156. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +3 -9
  157. data/lib/rubocop/cop/style/indent_array.rb +1 -1
  158. data/lib/rubocop/cop/style/indentation_width.rb +29 -60
  159. data/lib/rubocop/cop/style/infinite_loop.rb +21 -22
  160. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +86 -0
  161. data/lib/rubocop/cop/style/{method_call_parentheses.rb → method_call_without_args_parentheses.rb} +8 -1
  162. data/lib/rubocop/cop/style/missing_else.rb +40 -14
  163. data/lib/rubocop/cop/style/multiline_if_modifier.rb +5 -15
  164. data/lib/rubocop/cop/style/multiline_if_then.rb +14 -8
  165. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -3
  166. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -5
  167. data/lib/rubocop/cop/style/mutable_constant.rb +3 -2
  168. data/lib/rubocop/cop/style/negated_if.rb +3 -19
  169. data/lib/rubocop/cop/style/negated_while.rb +2 -17
  170. data/lib/rubocop/cop/style/nested_modifier.rb +16 -43
  171. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -5
  172. data/lib/rubocop/cop/style/next.rb +23 -21
  173. data/lib/rubocop/cop/style/non_nil_check.rb +2 -3
  174. data/lib/rubocop/cop/style/not.rb +1 -3
  175. data/lib/rubocop/cop/style/numeric_literals.rb +2 -2
  176. data/lib/rubocop/cop/style/one_line_conditional.rb +12 -22
  177. data/lib/rubocop/cop/style/option_hash.rb +4 -15
  178. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
  179. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -12
  180. data/lib/rubocop/cop/style/percent_q_literals.rb +15 -12
  181. data/lib/rubocop/cop/style/redundant_freeze.rb +3 -2
  182. data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -4
  183. data/lib/rubocop/cop/style/redundant_return.rb +4 -8
  184. data/lib/rubocop/cop/style/safe_navigation.rb +13 -6
  185. data/lib/rubocop/cop/style/space_after_colon.rb +2 -4
  186. data/lib/rubocop/cop/style/space_around_block_parameters.rb +1 -1
  187. data/lib/rubocop/cop/style/space_around_operators.rb +15 -13
  188. data/lib/rubocop/cop/style/string_methods.rb +1 -3
  189. data/lib/rubocop/cop/style/symbol_array.rb +1 -5
  190. data/lib/rubocop/cop/style/ternary_parentheses.rb +5 -6
  191. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +2 -5
  192. data/lib/rubocop/cop/style/trailing_comma_in_literal.rb +1 -1
  193. data/lib/rubocop/cop/style/unless_else.rb +1 -5
  194. data/lib/rubocop/cop/style/when_then.rb +4 -2
  195. data/lib/rubocop/cop/style/while_until_do.rb +9 -13
  196. data/lib/rubocop/cop/style/while_until_modifier.rb +12 -11
  197. data/lib/rubocop/cop/style/word_array.rb +5 -9
  198. data/lib/rubocop/cop/team.rb +16 -15
  199. data/lib/rubocop/cop/util.rb +13 -3
  200. data/lib/rubocop/formatter/clang_style_formatter.rb +2 -2
  201. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  202. data/lib/rubocop/magic_comment.rb +196 -0
  203. data/lib/rubocop/options.rb +5 -4
  204. data/lib/rubocop/processed_source.rb +1 -1
  205. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  206. data/lib/rubocop/rspec/shared_examples.rb +1 -1
  207. data/lib/rubocop/runner.rb +7 -2
  208. data/lib/rubocop/version.rb +1 -1
  209. metadata +41 -14
  210. data/lib/rubocop/ast_node.rb +0 -624
  211. data/lib/rubocop/ast_node/builder.rb +0 -30
  212. data/lib/rubocop/ast_node/sexp.rb +0 -13
  213. data/lib/rubocop/cop/mixin/hash_node.rb +0 -14
  214. data/lib/rubocop/cop/mixin/if_node.rb +0 -42
@@ -133,9 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def all_cop_names
136
- @all_cop_names ||= Cop::Cop.all.map(&:cop_name).reject do |cop_name|
137
- cop_name == UNNEEDED_DISABLE
138
- end
136
+ @all_cop_names ||= Cop::Cop.registry.names - [UNNEEDED_DISABLE]
139
137
  end
140
138
 
141
139
  def comment_only_line?(line_number)
@@ -27,7 +27,7 @@ module RuboCop
27
27
  'The `Rails/DefaultScope` cop no longer exists.',
28
28
  'Style/SingleSpaceBeforeFirstArg' =>
29
29
  'The `Style/SingleSpaceBeforeFirstArg` cop has been renamed to ' \
30
- '`Style/SpaceBeforeFirstArg. ',
30
+ '`Style/SpaceBeforeFirstArg.`',
31
31
  'Lint/SpaceBeforeFirstArg' =>
32
32
  'The `Lint/SpaceBeforeFirstArg` cop has been removed, since it was a ' \
33
33
  'duplicate of `Style/SpaceBeforeFirstArg`. Please use ' \
@@ -37,9 +37,60 @@ module RuboCop
37
37
  'use `Style/SpaceAroundKeyword` instead.',
38
38
  'Style/SpaceBeforeModifierKeyword' =>
39
39
  'The `Style/SpaceBeforeModifierKeyword` cop has been removed. Please ' \
40
- 'use `Style/SpaceAroundKeyword` instead.'
40
+ 'use `Style/SpaceAroundKeyword` instead.',
41
+ 'Style/MethodCallParentheses' =>
42
+ 'The `Style/MethodCallParentheses` cop has been renamed to ' \
43
+ '`Style/MethodCallWithoutArgsParentheses`.',
44
+ 'Lint/Eval' =>
45
+ 'The `Lint/Eval` cop has been renamed to `Security/Eval`.'
41
46
  }.freeze
42
47
 
48
+ OBSOLETE_PARAMETERS = [
49
+ {
50
+ cop: 'Style/SpaceAroundOperators',
51
+ parameter: 'MultiSpaceAllowedForOperators',
52
+ alternative: 'If your intention was to allow extra spaces ' \
53
+ 'for alignment, please use AllowForAlignment: ' \
54
+ 'true instead.'
55
+ },
56
+ {
57
+ cop: 'AllCops',
58
+ parameter: 'RunRailsCops',
59
+ alternative: "Use the following configuration instead:\n" \
60
+ "Rails:\n Enabled: true"
61
+ },
62
+ {
63
+ cop: 'Style/CaseIndentation',
64
+ parameter: 'IndentWhenRelativeTo',
65
+ alternative: '`IndentWhenRelativeTo` has been renamed to ' \
66
+ '`EnforcedStyle`'
67
+ },
68
+ {
69
+ cop: 'Lint/BlockAlignment',
70
+ parameter: 'AlignWith',
71
+ alternative: '`AlignWith` has been renamed to ' \
72
+ '`EnforcedStyleAlignWith`'
73
+ },
74
+ {
75
+ cop: 'Lint/EndAlignment',
76
+ parameter: 'AlignWith',
77
+ alternative: '`AlignWith` has been renamed to ' \
78
+ '`EnforcedStyleAlignWith`'
79
+ },
80
+ {
81
+ cop: 'Lint/DefEndAlignment',
82
+ parameter: 'AlignWith',
83
+ alternative: '`AlignWith` has been renamed to ' \
84
+ '`EnforcedStyleAlignWith`'
85
+ },
86
+ {
87
+ cop: 'Rails/UniqBeforePluck',
88
+ parameter: 'EnforcedMode',
89
+ alternative: '`EnforcedMode` has been renamed to ' \
90
+ '`EnforcedStyle`'
91
+ }
92
+ ].freeze
93
+
43
94
  attr_reader :loaded_path
44
95
 
45
96
  def initialize(hash = {}, loaded_path = nil)
@@ -142,13 +193,11 @@ module RuboCop
142
193
  end
143
194
 
144
195
  def cop_enabled?(cop)
145
- department = cop.cop_type.to_s.capitalize!
146
-
147
- if (dept_config = self[department])
196
+ if (dept_config = self[cop.department.to_s])
148
197
  return false if dept_config['Enabled'] == false
149
198
  end
150
199
 
151
- for_cop(cop).empty? || for_cop(cop)['Enabled']
200
+ for_cop(cop).empty? || for_cop(cop).fetch('Enabled', true)
152
201
  end
153
202
 
154
203
  def validate
@@ -161,9 +210,8 @@ module RuboCop
161
210
  ConfigLoader.default_configuration.key?(key)
162
211
  end
163
212
 
164
- reject_obsolete_cops
213
+ reject_obsolete_cops_and_parameters
165
214
  warn_about_unrecognized_cops(invalid_cop_names)
166
- reject_obsolete_parameters
167
215
  check_target_ruby
168
216
  validate_parameter_names(valid_cop_names)
169
217
  validate_enforced_styles(valid_cop_names)
@@ -259,7 +307,7 @@ module RuboCop
259
307
  end
260
308
 
261
309
  # There could be a custom cop with this name. If so, don't warn
262
- next if Cop::Cop.all.any? { |c| c.match?([name]) }
310
+ next if Cop::Cop.registry.contains_cop_matching?([name])
263
311
 
264
312
  warn Rainbow("Warning: unrecognized cop #{name} found in " \
265
313
  "#{loaded_path}").yellow
@@ -286,42 +334,52 @@ module RuboCop
286
334
 
287
335
  def validate_enforced_styles(valid_cop_names)
288
336
  valid_cop_names.each do |name|
289
- next unless (style = self[name]['EnforcedStyle'])
290
- valid = ConfigLoader.default_configuration[name]['SupportedStyles']
291
- next if valid.include?(style)
292
-
293
- msg = "invalid EnforcedStyle '#{style}' for #{name} found in " \
294
- "#{loaded_path}\n" \
295
- "Valid choices are: #{valid.join(', ')}"
296
- raise ValidationError, msg
337
+ styles = self[name].select { |key, _| key.start_with?('Enforced') }
338
+
339
+ styles.each do |style_name, style|
340
+ supported_key = RuboCop::Cop::Util.to_supported_styles(style_name)
341
+ valid = ConfigLoader.default_configuration[name][supported_key]
342
+ next unless valid
343
+ next if valid.include?(style)
344
+
345
+ msg = "invalid #{style_name} '#{style}' for #{name} found in " \
346
+ "#{loaded_path}\n" \
347
+ "Valid choices are: #{valid.join(', ')}"
348
+ raise ValidationError, msg
349
+ end
297
350
  end
298
351
  end
299
352
 
300
- def reject_obsolete_parameters
301
- check_obsolete_parameter('Style/SpaceAroundOperators',
302
- 'MultiSpaceAllowedForOperators',
303
- 'If your intention was to allow extra spaces ' \
304
- 'for alignment, please use AllowForAlignment: ' \
305
- 'true instead.')
306
- check_obsolete_parameter('AllCops', 'RunRailsCops',
307
- "Use the following configuration instead:\n" \
308
- "Rails:\n Enabled: true")
353
+ def reject_obsolete_cops_and_parameters
354
+ messages = [
355
+ obsolete_cops,
356
+ obsolete_parameters
357
+ ].flatten.compact
358
+ return if messages.empty?
359
+
360
+ raise ValidationError, messages.join("\n")
361
+ end
362
+
363
+ def obsolete_parameters
364
+ OBSOLETE_PARAMETERS.map do |params|
365
+ obsolete_parameter_message(params[:cop], params[:parameter],
366
+ params[:alternative])
367
+ end
309
368
  end
310
369
 
311
- def check_obsolete_parameter(cop, parameter, alternative = nil)
370
+ def obsolete_parameter_message(cop, parameter, alternative)
312
371
  return unless self[cop] && self[cop].key?(parameter)
313
372
 
314
- raise ValidationError, "obsolete parameter #{parameter} (for #{cop}) " \
315
- "found in #{loaded_path}" \
316
- "#{"\n" if alternative}#{alternative}"
373
+ "obsolete parameter #{parameter} (for #{cop}) " \
374
+ "found in #{loaded_path}" \
375
+ "\n#{alternative}"
317
376
  end
318
377
 
319
- def reject_obsolete_cops
320
- OBSOLETE_COPS.each do |cop_name, message|
321
- next unless key?(cop_name) || key?(cop_name.split('/').last)
322
- message += "\n(obsolete configuration found in #{loaded_path}, please" \
378
+ def obsolete_cops
379
+ OBSOLETE_COPS.map do |cop_name, message|
380
+ next unless key?(cop_name) || key?(Cop::Badge.parse(cop_name).cop_name)
381
+ message + "\n(obsolete configuration found in #{loaded_path}, please" \
323
382
  ' update it)'
324
- raise ValidationError, message
325
383
  end
326
384
  end
327
385
 
@@ -169,7 +169,7 @@ module RuboCop
169
169
  YAML.safe_load(yaml_code, [Regexp], [], false, filename)
170
170
  end
171
171
  else
172
- YAML.load(yaml_code, filename)
172
+ YAML.load(yaml_code, filename) # rubocop:disable Security/YAMLLoad
173
173
  end
174
174
  end
175
175
 
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Identifier of all cops containing a department and cop name.
6
+ #
7
+ # All cops are identified by their badge. For example, the badge
8
+ # for `RuboCop::Cop::Style::Tab` is `Style/Tab`. Badges can be
9
+ # parsed as either `Department/CopName` or just `CopName` to allow
10
+ # for badge references in source files that omit the department
11
+ # for RuboCop to infer.
12
+ class Badge
13
+ # Error raised when a badge parse fails.
14
+ class InvalidBadge < Error
15
+ MSG = 'Invalid badge %<badge>p. ' \
16
+ 'Expected `Department/CopName` or `CopName`.'.freeze
17
+
18
+ def initialize(token)
19
+ super(format(MSG, badge: token))
20
+ end
21
+ end
22
+
23
+ attr_reader :department, :cop_name
24
+
25
+ def self.for(class_name)
26
+ new(*class_name.split('::').last(2))
27
+ end
28
+
29
+ def self.parse(identifier)
30
+ parts = identifier.split('/', 2)
31
+
32
+ raise InvalidBadge, identifier if parts.size > 2
33
+
34
+ if parts.one?
35
+ new(nil, *parts)
36
+ else
37
+ new(*parts)
38
+ end
39
+ end
40
+
41
+ def initialize(department, cop_name)
42
+ @department = department.to_sym if department
43
+ @cop_name = cop_name
44
+ end
45
+
46
+ def ==(other)
47
+ hash == other.hash
48
+ end
49
+ alias eql? ==
50
+
51
+ def hash
52
+ [department, cop_name].hash
53
+ end
54
+
55
+ def match?(other)
56
+ cop_name == other.cop_name &&
57
+ (!qualified? || department == other.department)
58
+ end
59
+
60
+ def to_s
61
+ qualified? ? "#{department}/#{cop_name}" : cop_name
62
+ end
63
+
64
+ def qualified?
65
+ !department.nil?
66
+ end
67
+
68
+ def with_department(department)
69
+ self.class.new(department, cop_name)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -34,7 +34,7 @@ module RuboCop
34
34
 
35
35
  duplicated_gem_nodes.each do |nodes|
36
36
  nodes[1..-1].each do |node|
37
- offense(
37
+ register_offense(
38
38
  node,
39
39
  node.method_args.first.to_a.first,
40
40
  nodes.first.loc.line
@@ -54,7 +54,7 @@ module RuboCop
54
54
  .values
55
55
  end
56
56
 
57
- def offense(node, gem_name, line_of_first_occurence)
57
+ def register_offense(node, gem_name, line_of_first_occurence)
58
58
  line_range = node.loc.column...node.loc.last_column
59
59
 
60
60
  add_offense(
@@ -17,18 +17,26 @@ module RuboCop
17
17
  #
18
18
  # gem 'rspec'
19
19
  class OrderedGems < Cop
20
- MSG = 'Gem `%s` should appear before `%s` in their gem group.'.freeze
20
+ MSG = 'Gems should be sorted in an alphabetical order within their '\
21
+ 'section of the Gemfile. '\
22
+ 'Gem `%s` should appear before `%s`.'.freeze
21
23
  def investigate(processed_source)
22
24
  return if processed_source.ast.nil?
23
25
  gem_declarations(processed_source.ast)
24
26
  .each_cons(2) do |previous, current|
25
27
  next unless consecutive_lines(previous, current)
26
- next unless current.children[2].children.first.to_s <
27
- previous.children[2].children.first.to_s
28
+ next unless case_insensitive_out_of_order?(
29
+ current.children[2].children.first.to_s,
30
+ previous.children[2].children.first.to_s
31
+ )
28
32
  register_offense(previous, current)
29
33
  end
30
34
  end
31
35
 
36
+ def case_insensitive_out_of_order?(string_a, string_b)
37
+ 1 > string_a.casecmp(string_b)
38
+ end
39
+
32
40
  def consecutive_lines(previous, current)
33
41
  previous.source_range.last_line == current.source_range.first_line - 1
34
42
  end
@@ -45,6 +53,38 @@ module RuboCop
45
53
  )
46
54
  end
47
55
 
56
+ def autocorrect(node)
57
+ previous = previous_declaration(node)
58
+
59
+ current_range = declaration_with_comment(node)
60
+ previous_range = declaration_with_comment(previous)
61
+
62
+ lambda do |corrector|
63
+ swap_range(corrector, current_range, previous_range)
64
+ end
65
+ end
66
+
67
+ def declaration_with_comment(node)
68
+ buffer = processed_source.buffer
69
+ begin_pos = node.loc.expression.begin_pos
70
+ end_line = buffer.line_for_position(node.loc.expression.end_pos)
71
+ end_pos = buffer.line_range(end_line).end_pos
72
+ Parser::Source::Range.new(buffer, begin_pos, end_pos)
73
+ end
74
+
75
+ def swap_range(corrector, range1, range2)
76
+ src1 = range1.source
77
+ src2 = range2.source
78
+ corrector.replace(range1, src2)
79
+ corrector.replace(range2, src1)
80
+ end
81
+
82
+ def previous_declaration(node)
83
+ declarations = gem_declarations(processed_source.ast)
84
+ node_index = declarations.find_index(node)
85
+ declarations.to_a[node_index - 1]
86
+ end
87
+
48
88
  def_node_search :gem_declarations, <<-PATTERN
49
89
  (:send, nil, :gem, ...)
50
90
  PATTERN
@@ -5,7 +5,9 @@ module RuboCop
5
5
  # Commissioner class is responsible for processing the AST and delegating
6
6
  # work to the specified cops.
7
7
  class Commissioner
8
- include RuboCop::Node::Traversal
8
+ include RuboCop::AST::Traversal
9
+
10
+ CopError = Struct.new(:error, :line, :column)
9
11
 
10
12
  attr_reader :errors
11
13
 
@@ -26,19 +28,19 @@ module RuboCop
26
28
  # to continue iterating over the children of a node.
27
29
  # However, if we know that a certain node type (like `int`) never has
28
30
  # child nodes, there is no reason to pay the cost of calling `super`.
29
- no_child_callbacks = Node::Traversal::NO_CHILD_NODES.map do |type|
31
+ no_child_callbacks = NO_CHILD_NODES.map do |type|
30
32
  :"on_#{type}"
31
33
  end
32
34
 
33
35
  callback_methods.each do |callback|
34
- next unless RuboCop::Node::Traversal.method_defined?(callback)
36
+ next unless method_defined?(callback)
35
37
  class_eval <<-EOS, __FILE__, __LINE__
36
38
  def #{callback}(node)
37
39
  @callbacks[:"#{callback}"] ||= @cops.select do |cop|
38
40
  cop.respond_to?(:"#{callback}")
39
41
  end
40
42
  @callbacks[:#{callback}].each do |cop|
41
- with_cop_error_handling(cop) do
43
+ with_cop_error_handling(cop, node) do
42
44
  cop.send(:#{callback}, node)
43
45
  end
44
46
  end
@@ -67,6 +69,10 @@ module RuboCop
67
69
 
68
70
  def remove_irrelevant_cops(filename)
69
71
  @cops.reject! { |cop| cop.excluded_file?(filename) }
72
+ @cops.reject! do |cop|
73
+ cop.class.respond_to?(:support_target_ruby_version?) &&
74
+ !cop.class.support_target_ruby_version?(cop.target_ruby_version)
75
+ end
70
76
  end
71
77
 
72
78
  def reset_callbacks
@@ -92,11 +98,16 @@ module RuboCop
92
98
  end
93
99
  end
94
100
 
95
- def with_cop_error_handling(cop)
101
+ def with_cop_error_handling(cop, node = nil)
96
102
  yield
97
103
  rescue => e
98
104
  raise e if @options[:raise_error]
99
- @errors[cop] << e
105
+ if node
106
+ line = node.loc.line
107
+ column = node.loc.column
108
+ end
109
+ error = CopError.new(e, line, column)
110
+ @errors[cop] << error
100
111
  end
101
112
  end
102
113
  end
@@ -3,58 +3,6 @@ require 'uri'
3
3
 
4
4
  module RuboCop
5
5
  module Cop
6
- class AmbiguousCopName < RuboCop::Error; end
7
-
8
- # Store for all cops with helper functions
9
- class CopStore < ::Array
10
- # @return [Array<String>] list of types for current cops.
11
- def types
12
- @types ||= map(&:cop_type).uniq!
13
- end
14
-
15
- # @return [Array<Cop>] Cops for that specific type.
16
- def with_type(type)
17
- CopStore.new(select { |c| c.cop_type == type })
18
- end
19
-
20
- # @return [Array<Cop>] Cops not for a specific type.
21
- def without_type(type)
22
- CopStore.new(reject { |c| c.cop_type == type })
23
- end
24
-
25
- def qualified_cop_name(name, origin)
26
- return name if cop_names.include?(name)
27
-
28
- basename = File.basename(name)
29
- found_ns = types.map(&:capitalize).select do |ns|
30
- cop_names.include?("#{ns}/#{basename}")
31
- end
32
-
33
- case found_ns.size
34
- when 0 then name # No namespace found. Deal with it later in caller.
35
- when 1 then cop_name_with_namespace(name, origin, basename, found_ns[0])
36
- else raise AmbiguousCopName,
37
- "Ambiguous cop name `#{name}` used in #{origin} needs " \
38
- 'namespace qualifier. Did you mean ' \
39
- "#{found_ns.map { |ns| "#{ns}/#{basename}" }.join(' or ')}"
40
- end
41
- end
42
-
43
- def cop_name_with_namespace(name, origin, basename, found_ns)
44
- if name != basename && found_ns != File.dirname(name).to_sym
45
- warn "#{origin}: #{name} has the wrong namespace - should be " \
46
- "#{found_ns}"
47
- end
48
- "#{found_ns}/#{basename}"
49
- end
50
-
51
- private
52
-
53
- def cop_names
54
- @cop_names ||= Set.new(map(&:cop_name))
55
- end
56
- end
57
-
58
6
  # A scaffold for concrete cops.
59
7
  #
60
8
  # The Cop class is meant to be extended.
@@ -75,9 +23,9 @@ module RuboCop
75
23
  # end
76
24
  # end
77
25
  class Cop
78
- extend RuboCop::Sexp
26
+ extend RuboCop::AST::Sexp
79
27
  extend NodePattern::Macros
80
- include RuboCop::Sexp
28
+ include RuboCop::AST::Sexp
81
29
  include Util
82
30
  include IgnoredNode
83
31
  include AutocorrectLogic
@@ -85,34 +33,42 @@ module RuboCop
85
33
  attr_reader :config, :offenses, :corrections
86
34
  attr_accessor :processed_source # TODO: Bad design.
87
35
 
88
- @all = CopStore.new
36
+ @registry = Registry.new
37
+
38
+ class << self
39
+ attr_reader :registry
40
+ end
89
41
 
90
42
  def self.all
91
- @all.without_type(:test)
43
+ registry.without_department(:Test).cops
92
44
  end
93
45
 
94
46
  def self.qualified_cop_name(name, origin)
95
- @all.qualified_cop_name(name, origin)
47
+ registry.qualified_cop_name(name, origin)
96
48
  end
97
49
 
98
50
  def self.non_rails
99
- all.without_type(:rails)
51
+ registry.without_department(:Rails)
100
52
  end
101
53
 
102
54
  def self.inherited(subclass)
103
- @all << subclass
55
+ registry.enlist(subclass)
56
+ end
57
+
58
+ def self.badge
59
+ @badge ||= Badge.for(name)
104
60
  end
105
61
 
106
62
  def self.cop_name
107
- @cop_name ||= name.split('::').last(2).join('/')
63
+ badge.to_s
108
64
  end
109
65
 
110
- def self.cop_type
111
- @cop_type ||= name.split('::')[-2].downcase.to_sym
66
+ def self.department
67
+ badge.department
112
68
  end
113
69
 
114
70
  def self.lint?
115
- cop_type == :lint
71
+ department == :Lint
116
72
  end
117
73
 
118
74
  # Returns true if the cop name or the cop namespace matches any of the
@@ -121,7 +77,7 @@ module RuboCop
121
77
  return false unless given_names
122
78
 
123
79
  given_names.include?(cop_name) ||
124
- given_names.include?(cop_type.to_s.capitalize)
80
+ given_names.include?(department.to_s)
125
81
  end
126
82
 
127
83
  def initialize(config = nil, options = nil)
@@ -141,25 +97,6 @@ module RuboCop
141
97
  @cop_config ||= @config.for_cop(self)
142
98
  end
143
99
 
144
- def debug?
145
- @options[:debug]
146
- end
147
-
148
- def display_cop_names?
149
- debug? || @options[:display_cop_names] ||
150
- @config.for_all_cops['DisplayCopNames']
151
- end
152
-
153
- def display_style_guide?
154
- (style_guide_url || reference_url) &&
155
- (@options[:display_style_guide] ||
156
- config.for_all_cops['DisplayStyleGuide'])
157
- end
158
-
159
- def extra_details?
160
- @options[:extra_details] || config.for_all_cops['ExtraDetails']
161
- end
162
-
163
100
  def message(_node = nil)
164
101
  self.class::MSG
165
102
  end
@@ -172,7 +109,7 @@ module RuboCop
172
109
  severity = custom_severity || severity || default_severity
173
110
 
174
111
  message ||= message(node)
175
- message = annotate_message(message)
112
+ message = annotate(message)
176
113
 
177
114
  status = enabled_line?(location.line) ? correct(node) : :disabled
178
115
 
@@ -232,36 +169,12 @@ module RuboCop
232
169
  !relevant_file?(file)
233
170
  end
234
171
 
235
- def style_guide_url
236
- url = cop_config['StyleGuide']
237
- return nil if url.nil? || url.empty?
238
-
239
- base_url = config.for_all_cops['StyleGuideBaseURL']
240
- return url if base_url.nil? || base_url.empty?
241
-
242
- URI.join(base_url, url).to_s
243
- end
244
-
245
- def reference_url
246
- url = cop_config['Reference']
247
- url.nil? || url.empty? ? nil : url
248
- end
249
-
250
- def details
251
- details = cop_config && cop_config['Details']
252
- details.nil? || details.empty? ? nil : details
253
- end
254
-
255
172
  private
256
173
 
257
- def annotate_message(message)
258
- message = "#{name}: #{message}" if display_cop_names?
259
- message += " #{details}" if extra_details?
260
- if display_style_guide?
261
- links = [style_guide_url, reference_url].compact.join(', ')
262
- message = "#{message} (#{links})"
263
- end
264
- message
174
+ def annotate(message)
175
+ RuboCop::Cop::MessageAnnotator.new(
176
+ config, cop_config, @options
177
+ ).annotate(message, name)
265
178
  end
266
179
 
267
180
  def file_name_matches_any?(file, parameter, default_result)