rubocop 1.8.0 → 1.11.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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -13
  3. data/assets/output.html.erb +1 -1
  4. data/config/default.yml +89 -22
  5. data/config/obsoletion.yml +4 -0
  6. data/lib/rubocop.rb +9 -0
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  8. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  9. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  10. data/lib/rubocop/config.rb +5 -2
  11. data/lib/rubocop/config_loader.rb +7 -14
  12. data/lib/rubocop/config_store.rb +12 -1
  13. data/lib/rubocop/cop/base.rb +2 -1
  14. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -1
  15. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -0
  16. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +1 -0
  17. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -0
  18. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  19. data/lib/rubocop/cop/gemspec/date_assignment.rb +57 -0
  20. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -0
  21. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -0
  22. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +2 -0
  23. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +2 -0
  24. data/lib/rubocop/cop/generator.rb +3 -5
  25. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  26. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  27. data/lib/rubocop/cop/internal_affairs/example_description.rb +90 -0
  28. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +1 -0
  29. data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +2 -0
  30. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +151 -0
  31. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +1 -0
  32. data/lib/rubocop/cop/internal_affairs/offense_location_keyword.rb +2 -0
  33. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +62 -0
  34. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +65 -0
  35. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -0
  36. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +3 -0
  37. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +4 -0
  38. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  39. data/lib/rubocop/cop/layout/block_alignment.rb +1 -0
  40. data/lib/rubocop/cop/layout/class_structure.rb +8 -2
  41. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +37 -17
  42. data/lib/rubocop/cop/layout/extra_spacing.rb +2 -2
  43. data/lib/rubocop/cop/layout/first_argument_indentation.rb +22 -3
  44. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  45. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  46. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +26 -0
  47. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
  48. data/lib/rubocop/cop/layout/space_before_brackets.rb +9 -4
  49. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -1
  50. data/lib/rubocop/cop/lint/big_decimal_new.rb +1 -0
  51. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -0
  52. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +2 -0
  53. data/lib/rubocop/cop/lint/constant_resolution.rb +1 -0
  54. data/lib/rubocop/cop/lint/debugger.rb +60 -14
  55. data/lib/rubocop/cop/lint/deprecated_constants.rb +5 -0
  56. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +14 -4
  57. data/lib/rubocop/cop/lint/duplicate_branch.rb +1 -1
  58. data/lib/rubocop/cop/lint/duplicate_methods.rb +3 -0
  59. data/lib/rubocop/cop/lint/duplicate_require.rb +3 -2
  60. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -0
  61. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  62. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -0
  63. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -0
  64. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +1 -0
  65. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -0
  66. data/lib/rubocop/cop/lint/inherit_exception.rb +1 -0
  67. data/lib/rubocop/cop/lint/multiple_comparison.rb +5 -4
  68. data/lib/rubocop/cop/lint/nested_method_definition.rb +3 -0
  69. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -0
  70. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +7 -0
  71. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -0
  72. data/lib/rubocop/cop/lint/number_conversion.rb +43 -6
  73. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  74. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  75. data/lib/rubocop/cop/lint/raise_exception.rb +2 -0
  76. data/lib/rubocop/cop/lint/rand_one.rb +1 -0
  77. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +5 -3
  78. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -0
  79. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -0
  80. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -3
  81. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -0
  82. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -0
  83. data/lib/rubocop/cop/lint/redundant_with_object.rb +1 -0
  84. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
  85. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -0
  86. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -0
  87. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  88. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -0
  89. data/lib/rubocop/cop/lint/struct_new_override.rb +1 -0
  90. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  91. data/lib/rubocop/cop/lint/to_enum_arguments.rb +3 -0
  92. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  93. data/lib/rubocop/cop/lint/unified_integer.rb +1 -0
  94. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +5 -0
  95. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
  96. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -0
  97. data/lib/rubocop/cop/lint/unused_method_argument.rb +1 -0
  98. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +1 -0
  99. data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -0
  100. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -0
  101. data/lib/rubocop/cop/lint/useless_times.rb +3 -0
  102. data/lib/rubocop/cop/message_annotator.rb +4 -1
  103. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  104. data/lib/rubocop/cop/metrics/module_length.rb +1 -0
  105. data/lib/rubocop/cop/metrics/parameter_lists.rb +6 -2
  106. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -4
  107. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +2 -0
  108. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  109. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  110. data/lib/rubocop/cop/mixin/comments_help.rb +0 -1
  111. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  112. data/lib/rubocop/cop/mixin/def_node.rb +1 -0
  113. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +3 -0
  114. data/lib/rubocop/cop/mixin/empty_parameter.rb +1 -0
  115. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -0
  116. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -0
  117. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -1
  118. data/lib/rubocop/cop/mixin/negative_conditional.rb +3 -0
  119. data/lib/rubocop/cop/mixin/preferred_delimiters.rb +3 -3
  120. data/lib/rubocop/cop/mixin/rational_literal.rb +1 -0
  121. data/lib/rubocop/cop/mixin/safe_assignment.rb +5 -0
  122. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  123. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -0
  124. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -0
  125. data/lib/rubocop/cop/naming/constant_name.rb +2 -0
  126. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -0
  127. data/lib/rubocop/cop/naming/method_name.rb +3 -0
  128. data/lib/rubocop/cop/naming/predicate_name.rb +1 -0
  129. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  130. data/lib/rubocop/cop/naming/variable_number.rb +1 -1
  131. data/lib/rubocop/cop/registry.rb +1 -1
  132. data/lib/rubocop/cop/security/eval.rb +1 -0
  133. data/lib/rubocop/cop/security/json_load.rb +1 -0
  134. data/lib/rubocop/cop/security/marshal_load.rb +1 -0
  135. data/lib/rubocop/cop/security/open.rb +1 -0
  136. data/lib/rubocop/cop/security/yaml_load.rb +1 -0
  137. data/lib/rubocop/cop/severity.rb +3 -3
  138. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -0
  139. data/lib/rubocop/cop/style/alias.rb +1 -0
  140. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -0
  141. data/lib/rubocop/cop/style/array_coercion.rb +2 -0
  142. data/lib/rubocop/cop/style/array_join.rb +1 -0
  143. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  144. data/lib/rubocop/cop/style/attr.rb +1 -0
  145. data/lib/rubocop/cop/style/case_equality.rb +2 -1
  146. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -0
  147. data/lib/rubocop/cop/style/collection_compact.rb +2 -0
  148. data/lib/rubocop/cop/style/colon_method_call.rb +1 -0
  149. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  150. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -0
  151. data/lib/rubocop/cop/style/constant_visibility.rb +28 -0
  152. data/lib/rubocop/cop/style/date_time.rb +3 -0
  153. data/lib/rubocop/cop/style/dir.rb +1 -0
  154. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  155. data/lib/rubocop/cop/style/documentation.rb +5 -0
  156. data/lib/rubocop/cop/style/documentation_method.rb +1 -0
  157. data/lib/rubocop/cop/style/double_negation.rb +3 -2
  158. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -0
  159. data/lib/rubocop/cop/style/each_with_object.rb +1 -0
  160. data/lib/rubocop/cop/style/empty_literal.rb +9 -0
  161. data/lib/rubocop/cop/style/endless_method.rb +1 -0
  162. data/lib/rubocop/cop/style/eval_with_location.rb +140 -49
  163. data/lib/rubocop/cop/style/even_odd.rb +1 -0
  164. data/lib/rubocop/cop/style/expand_path_arguments.rb +3 -0
  165. data/lib/rubocop/cop/style/explicit_block_argument.rb +12 -1
  166. data/lib/rubocop/cop/style/exponential_notation.rb +6 -7
  167. data/lib/rubocop/cop/style/float_division.rb +7 -0
  168. data/lib/rubocop/cop/style/format_string.rb +2 -0
  169. data/lib/rubocop/cop/style/format_string_token.rb +19 -2
  170. data/lib/rubocop/cop/style/global_std_stream.rb +1 -0
  171. data/lib/rubocop/cop/style/hash_conversion.rb +105 -0
  172. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -0
  173. data/lib/rubocop/cop/style/hash_except.rb +1 -0
  174. data/lib/rubocop/cop/style/hash_like_case.rb +1 -0
  175. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -0
  176. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -0
  177. data/lib/rubocop/cop/style/if_inside_else.rb +14 -7
  178. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +122 -0
  179. data/lib/rubocop/cop/style/implicit_runtime_error.rb +1 -0
  180. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  181. data/lib/rubocop/cop/style/min_max.rb +1 -0
  182. data/lib/rubocop/cop/style/mixin_usage.rb +2 -0
  183. data/lib/rubocop/cop/style/module_function.rb +5 -0
  184. data/lib/rubocop/cop/style/multiple_comparison.rb +21 -2
  185. data/lib/rubocop/cop/style/mutable_constant.rb +3 -0
  186. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -0
  187. data/lib/rubocop/cop/style/nil_comparison.rb +6 -0
  188. data/lib/rubocop/cop/style/nil_lambda.rb +1 -0
  189. data/lib/rubocop/cop/style/non_nil_check.rb +30 -13
  190. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  191. data/lib/rubocop/cop/style/numeric_predicate.rb +4 -1
  192. data/lib/rubocop/cop/style/option_hash.rb +1 -0
  193. data/lib/rubocop/cop/style/or_assignment.rb +2 -0
  194. data/lib/rubocop/cop/style/parallel_assignment.rb +6 -0
  195. data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -0
  196. data/lib/rubocop/cop/style/proc.rb +1 -0
  197. data/lib/rubocop/cop/style/random_with_offset.rb +5 -0
  198. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -0
  199. data/lib/rubocop/cop/style/redundant_begin.rb +7 -1
  200. data/lib/rubocop/cop/style/redundant_conditional.rb +2 -0
  201. data/lib/rubocop/cop/style/redundant_exception.rb +2 -0
  202. data/lib/rubocop/cop/style/redundant_fetch_block.rb +2 -0
  203. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -0
  204. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -0
  205. data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -0
  206. data/lib/rubocop/cop/style/redundant_self_assignment.rb +2 -0
  207. data/lib/rubocop/cop/style/redundant_sort.rb +1 -0
  208. data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -0
  209. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  210. data/lib/rubocop/cop/style/rescue_standard_error.rb +2 -0
  211. data/lib/rubocop/cop/style/return_nil.rb +6 -0
  212. data/lib/rubocop/cop/style/safe_navigation.rb +2 -0
  213. data/lib/rubocop/cop/style/sample.rb +1 -0
  214. data/lib/rubocop/cop/style/signal_exception.rb +3 -0
  215. data/lib/rubocop/cop/style/single_argument_dig.rb +1 -0
  216. data/lib/rubocop/cop/style/single_line_methods.rb +5 -2
  217. data/lib/rubocop/cop/style/slicing_with_range.rb +1 -0
  218. data/lib/rubocop/cop/style/sole_nested_conditional.rb +28 -4
  219. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  220. data/lib/rubocop/cop/style/stderr_puts.rb +1 -0
  221. data/lib/rubocop/cop/style/string_concatenation.rb +2 -1
  222. data/lib/rubocop/cop/style/string_hash_keys.rb +2 -0
  223. data/lib/rubocop/cop/style/strip.rb +1 -0
  224. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -0
  225. data/lib/rubocop/cop/style/symbol_proc.rb +25 -1
  226. data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -1
  227. data/lib/rubocop/cop/style/trailing_body_on_method_definition.rb +1 -0
  228. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -1
  229. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -0
  230. data/lib/rubocop/cop/style/unless_logical_operators.rb +99 -0
  231. data/lib/rubocop/cop/style/unpack_first.rb +1 -0
  232. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  233. data/lib/rubocop/cop/style/yoda_condition.rb +1 -0
  234. data/lib/rubocop/cop/style/zero_length_predicate.rb +5 -0
  235. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  236. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  237. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  238. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  239. data/lib/rubocop/magic_comment.rb +30 -1
  240. data/lib/rubocop/name_similarity.rb +1 -1
  241. data/lib/rubocop/options.rb +1 -1
  242. data/lib/rubocop/rspec/expect_offense.rb +5 -2
  243. data/lib/rubocop/runner.rb +1 -0
  244. data/lib/rubocop/target_ruby.rb +21 -13
  245. data/lib/rubocop/version.rb +2 -2
  246. metadata +21 -7
@@ -20,6 +20,7 @@ module RuboCop
20
20
  '`method_name == %<method_name>s`.'
21
21
  RESTRICT_ON_SEND = %i[==].freeze
22
22
 
23
+ # @!method method_name?(node)
23
24
  def_node_matcher :method_name?, <<~PATTERN
24
25
  (send
25
26
  $(send
@@ -19,10 +19,12 @@ module RuboCop
19
19
  MSG = 'Use the methods provided with the node extensions instead ' \
20
20
  'of manually destructuring nodes.'
21
21
 
22
+ # @!method node_variable?(node)
22
23
  def_node_matcher :node_variable?, <<~PATTERN
23
24
  {(lvar [#node_suffix? _]) (send nil? [#node_suffix? _])}
24
25
  PATTERN
25
26
 
27
+ # @!method node_destructuring?(node)
26
28
  def_node_matcher :node_destructuring?, <<~PATTERN
27
29
  {(masgn (mlhs ...) {(send #node_variable? :children) (array (splat #node_variable?))})}
28
30
  PATTERN
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks that node matcher definitions are tagged with a YARD `@!method`
7
+ # directive so that editors are able to find the dynamically defined
8
+ # method.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # def_node_matcher :foo?, <<~PATTERN
13
+ # ...
14
+ # PATTERN
15
+ #
16
+ # # good
17
+ # # @!method foo?(node)
18
+ # def_node_matcher :foo?, <<~PATTERN
19
+ # ...
20
+ # PATTERN
21
+ #
22
+ class NodeMatcherDirective < Base
23
+ extend AutoCorrector
24
+ include RangeHelp
25
+
26
+ MSG = 'Preceed `%<method>s` with a `@!method` YARD directive.'
27
+ MSG_WRONG_NAME = '`@!method` YARD directive has invalid method name, ' \
28
+ 'use `%<expected>s` instead of `%<actual>s`.'
29
+ MSG_TOO_MANY = 'Multiple `@!method` YARD directives found for this matcher.'
30
+
31
+ RESTRICT_ON_SEND = %i[def_node_matcher def_node_search].to_set.freeze
32
+ REGEXP = /^\s*#\s*@!method\s+(?<method_name>[a-z0-9_]+[?!]?)(?:\((?<args>.*)\))?/.freeze
33
+
34
+ # @!method pattern_matcher?(node)
35
+ def_node_matcher :pattern_matcher?, <<~PATTERN
36
+ (send _ %RESTRICT_ON_SEND {str sym} {str dstr})
37
+ PATTERN
38
+
39
+ def on_send(node)
40
+ return if node.arguments.none?
41
+ return unless valid_method_name?(node)
42
+
43
+ actual_name = node.arguments.first.value
44
+ directives = method_directives(node)
45
+ return too_many_directives(node) if directives.size > 1
46
+
47
+ directive = directives.first
48
+ return if directive_correct?(directive, actual_name)
49
+
50
+ register_offense(node, directive, actual_name)
51
+ end
52
+
53
+ private
54
+
55
+ def valid_method_name?(node)
56
+ node.arguments.first.str_type? || node.arguments.first.sym_type?
57
+ end
58
+
59
+ def method_directives(node)
60
+ comments = processed_source.ast_with_comments[node]
61
+
62
+ comments.map do |comment|
63
+ match = comment.text.match(REGEXP)
64
+ next unless match
65
+
66
+ { node: comment, method_name: match[:method_name], args: match[:args] }
67
+ end.compact
68
+ end
69
+
70
+ def too_many_directives(node)
71
+ add_offense(node, message: MSG_TOO_MANY)
72
+ end
73
+
74
+ def directive_correct?(directive, actual_name)
75
+ directive && directive[:method_name] == actual_name.to_s
76
+ end
77
+
78
+ def register_offense(node, directive, actual_name)
79
+ message = formatted_message(directive, actual_name, node.method_name)
80
+
81
+ add_offense(node, message: message) do |corrector|
82
+ if directive
83
+ correct_directive(corrector, directive, actual_name)
84
+ else
85
+ insert_directive(corrector, node, actual_name)
86
+ end
87
+ end
88
+ end
89
+
90
+ def formatted_message(directive, actual_name, method_name)
91
+ if directive
92
+ format(MSG_WRONG_NAME, expected: actual_name, actual: directive[:method_name])
93
+ else
94
+ format(MSG, method: method_name)
95
+ end
96
+ end
97
+
98
+ def insert_directive(corrector, node, actual_name)
99
+ # If the pattern matcher uses arguments (`%1`, `%2`, etc.), include them in the directive
100
+ arguments = pattern_arguments(node.arguments[1].source)
101
+
102
+ range = range_with_surrounding_space(
103
+ range: node.loc.expression,
104
+ side: :left,
105
+ newlines: false
106
+ )
107
+ indentation = range.source.match(/^\s*/)[0]
108
+ directive = "#{indentation}# @!method #{actual_name}(#{arguments.join(', ')})\n"
109
+ directive = "\n#{directive}" if add_newline?(node)
110
+
111
+ corrector.insert_before(range, directive)
112
+ end
113
+
114
+ def pattern_arguments(pattern)
115
+ arguments = %w[node]
116
+ max_pattern_var = pattern.scan(/(?<=%)\d+/).map(&:to_i).max
117
+ max_pattern_var&.times { |i| arguments << "arg#{i + 1}" }
118
+ arguments
119
+ end
120
+
121
+ def add_newline?(node)
122
+ # Determine if a blank line should be inserted before the new directive
123
+ # in order to spread out pattern matchers
124
+ return if node.sibling_index&.zero?
125
+ return unless node.parent
126
+
127
+ prev_sibling = node.parent.child_nodes[node.sibling_index - 1]
128
+ return unless prev_sibling && pattern_matcher?(prev_sibling)
129
+
130
+ node.loc.line == last_line(prev_sibling) + 1
131
+ end
132
+
133
+ def last_line(node)
134
+ if node.last_argument.heredoc?
135
+ node.last_argument.loc.heredoc_end.line
136
+ else
137
+ node.loc.last_line
138
+ end
139
+ end
140
+
141
+ def correct_directive(corrector, directive, actual_name)
142
+ correct = "@!method #{actual_name}"
143
+ regexp = /@!method\s+#{Regexp.escape(directive[:method_name])}/
144
+
145
+ replacement = directive[:node].text.gsub(regexp, correct)
146
+ corrector.replace(directive[:node], replacement)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -19,6 +19,7 @@ module RuboCop
19
19
  MSG = 'Use `#%<type>s_type?` to check node type.'
20
20
  RESTRICT_ON_SEND = %i[==].freeze
21
21
 
22
+ # @!method node_type_check(node)
22
23
  def_node_matcher :node_type_check, <<~PATTERN
23
24
  (send (send $_ :type) :== (sym $_))
24
25
  PATTERN
@@ -34,10 +34,12 @@ module RuboCop
34
34
 
35
35
  private
36
36
 
37
+ # @!method node_type_check(node)
37
38
  def_node_matcher :node_type_check, <<~PATTERN
38
39
  (send nil? :add_offense $_node $hash)
39
40
  PATTERN
40
41
 
42
+ # @!method offending_location_argument(node)
41
43
  def_node_matcher :offending_location_argument, <<~PATTERN
42
44
  (pair (sym :location) $(send (send $_node :loc) $_keyword))
43
45
  PATTERN
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks for redundant `subject(:cop) { described_class.new }`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # RSpec.describe RuboCop::Cop::Department::Foo do
11
+ # subject(:cop) { described_class.new(config) }
12
+ # end
13
+ #
14
+ # # good
15
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
16
+ # end
17
+ #
18
+ class RedundantDescribedClassAsSubject < Base
19
+ include RangeHelp
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Remove the redundant `subject`%<additional_message>s.'
23
+
24
+ # @!method described_class_subject?(node)
25
+ def_node_matcher :described_class_subject?, <<~PATTERN
26
+ (block
27
+ (send nil? :subject
28
+ (sym :cop))
29
+ (args)
30
+ (send
31
+ (send nil? :described_class) :new
32
+ $...))
33
+ PATTERN
34
+
35
+ def on_block(node)
36
+ return unless (described_class_arguments = described_class_subject?(node))
37
+ return if described_class_arguments.count >= 2
38
+
39
+ describe = find_describe_method_node(node)
40
+
41
+ unless (exist_config = describe.last_argument.source == ':config')
42
+ additional_message = ' and specify `:config` in `describe`'
43
+ end
44
+
45
+ message = format(MSG, additional_message: additional_message)
46
+
47
+ add_offense(node, message: message) do |corrector|
48
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
49
+
50
+ corrector.insert_after(describe.last_argument, ', :config') unless exist_config
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def find_describe_method_node(block_node)
57
+ block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks that `let` is `RuboCop::Config.new` with no arguments.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
11
+ # let(:config) { RuboCop::Config.new }
12
+ # end
13
+ #
14
+ # # good
15
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
16
+ # end
17
+ #
18
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
19
+ # let(:config) { RuboCop::Config.new(argument) }
20
+ # end
21
+ #
22
+ class RedundantLetRuboCopConfigNew < Base
23
+ include RangeHelp
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Remove `let` that is `RuboCop::Config.new` with no arguments%<additional_message>s.'
27
+
28
+ # @!method let_rubocop_config_new?(node)
29
+ def_node_matcher :let_rubocop_config_new?, <<~PATTERN
30
+ (block
31
+ (send nil? :let
32
+ (sym :config))
33
+ (args)
34
+ (send
35
+ (const
36
+ (const nil? :RuboCop) :Config) :new))
37
+ PATTERN
38
+
39
+ def on_block(node)
40
+ return unless let_rubocop_config_new?(node)
41
+
42
+ describe = find_describe_method_node(node)
43
+
44
+ unless (exist_config = describe.last_argument.source == ':config')
45
+ additional_message = ' and specify `:config` in `describe`'
46
+ end
47
+
48
+ message = format(MSG, additional_message: additional_message)
49
+
50
+ add_offense(node, message: message) do |corrector|
51
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
52
+
53
+ corrector.insert_after(describe.last_argument, ', :config') unless exist_config
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def find_describe_method_node(block_node)
60
+ block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -23,6 +23,7 @@ module RuboCop
23
23
  MSG = 'Redundant location argument to `#add_offense`.'
24
24
  RESTRICT_ON_SEND = %i[add_offense].freeze
25
25
 
26
+ # @!method redundant_location_argument(node)
26
27
  def_node_matcher :redundant_location_argument, <<~PATTERN
27
28
  (send nil? :add_offense _
28
29
  (hash <$(pair (sym :location) (sym :expression)) ...>)
@@ -26,16 +26,19 @@ module RuboCop
26
26
  MSG = 'Redundant message argument to `#add_offense`.'
27
27
  RESTRICT_ON_SEND = %i[add_offense].freeze
28
28
 
29
+ # @!method node_type_check(node)
29
30
  def_node_matcher :node_type_check, <<~PATTERN
30
31
  (send nil? :add_offense $_node $hash)
31
32
  PATTERN
32
33
 
34
+ # @!method redundant_message_argument(node)
33
35
  def_node_matcher :redundant_message_argument, <<~PATTERN
34
36
  (pair
35
37
  (sym :message)
36
38
  ${(const nil? :MSG) (send nil? :message) (send nil? :message _)})
37
39
  PATTERN
38
40
 
41
+ # @!method message_method_call(node)
39
42
  def_node_matcher :message_method_call, '(send nil? :message $_node)'
40
43
 
41
44
  def on_send(node)
@@ -67,18 +67,22 @@ module RuboCop
67
67
  no_acceptable_style! style_detected
68
68
  ].freeze
69
69
 
70
+ # @!method correct_style_detected_check(node)
70
71
  def_node_matcher :correct_style_detected_check, <<~PATTERN
71
72
  (send nil? :correct_style_detected)
72
73
  PATTERN
73
74
 
75
+ # @!method negative_style_detected_method_check(node)
74
76
  def_node_matcher :negative_style_detected_method_check, <<~PATTERN
75
77
  (send nil? /(?:opposite|unexpected|ambiguous|unrecognized)_style_detected|conflicting_styles_detected/ ...)
76
78
  PATTERN
77
79
 
80
+ # @!method no_acceptable_style_check(node)
78
81
  def_node_matcher :no_acceptable_style_check, <<~PATTERN
79
82
  (send nil? :no_acceptable_style!)
80
83
  PATTERN
81
84
 
85
+ # @!method style_detected_check(node)
82
86
  def_node_matcher :style_detected_check, <<~PATTERN
83
87
  (send nil? :style_detected ...)
84
88
  PATTERN
@@ -16,10 +16,12 @@ module RuboCop
16
16
  class UselessMessageAssertion < Base
17
17
  MSG = 'Do not specify cop behavior using `described_class::MSG`.'
18
18
 
19
+ # @!method described_class_msg(node)
19
20
  def_node_search :described_class_msg, <<~PATTERN
20
21
  (const (send nil? :described_class) :MSG)
21
22
  PATTERN
22
23
 
24
+ # @!method rspec_expectation_on_msg?(node)
23
25
  def_node_matcher :rspec_expectation_on_msg?, <<~PATTERN
24
26
  (send (send nil? :expect #contains_described_class_msg?) :to ...)
25
27
  PATTERN
@@ -68,6 +68,7 @@ module RuboCop
68
68
 
69
69
  MSG = '%<current>s is not aligned with %<prefer>s%<alt_prefer>s.'
70
70
 
71
+ # @!method block_end_align_target?(node, child)
71
72
  def_node_matcher :block_end_align_target?, <<~PATTERN
72
73
  {assignment?
73
74
  splat
@@ -147,6 +147,7 @@ module RuboCop
147
147
  MSG = '`%<category>s` is supposed to appear before ' \
148
148
  '`%<previous>s`.'
149
149
 
150
+ # @!method dynamic_constant?(node)
150
151
  def_node_matcher :dynamic_constant?, <<~PATTERN
151
152
  (casgn nil? _ (send ...))
152
153
  PATTERN
@@ -213,7 +214,12 @@ module RuboCop
213
214
  name = node.method_name.to_s
214
215
  category, = categories.find { |_, names| names.include?(name) }
215
216
  key = category || name
216
- visibility_key = "#{node_visibility(node)}_#{key}"
217
+ visibility_key =
218
+ if node.def_modifier?
219
+ "#{name}_methods"
220
+ else
221
+ "#{node_visibility(node)}_#{key}"
222
+ end
217
223
  expected_order.include?(visibility_key) ? visibility_key : key
218
224
  end
219
225
 
@@ -264,7 +270,7 @@ module RuboCop
264
270
 
265
271
  def source_range_with_comment(node)
266
272
  begin_pos, end_pos =
267
- if node.def_type?
273
+ if node.def_type? && !node.method?(:initialize) || node.send_type? && node.def_modifier?
268
274
  start_node = find_visibility_start(node) || node
269
275
  end_node = find_visibility_end(node) || node
270
276
  [begin_pos_with_comment(start_node),
@@ -88,7 +88,7 @@ module RuboCop
88
88
  include RangeHelp
89
89
  extend AutoCorrector
90
90
 
91
- MSG = 'Use empty lines between %<type>s definitions.'
91
+ MSG = 'Expected %<expected>s between %<type>s definitions; found %<actual>d.'
92
92
 
93
93
  def self.autocorrect_incompatible_with
94
94
  [Layout::EmptyLines]
@@ -107,19 +107,21 @@ module RuboCop
107
107
  end
108
108
 
109
109
  def check_defs(nodes)
110
- return if blank_lines_between?(*nodes)
110
+ count = blank_lines_count_between(*nodes)
111
+
112
+ return if line_count_allowed?(count)
111
113
  return if multiple_blank_lines_groups?(*nodes)
112
114
  return if nodes.all?(&:single_line?) &&
113
115
  cop_config['AllowAdjacentOneLineDefs']
114
116
 
115
117
  correction_node = nodes.last
116
118
  location = correction_node.loc.keyword.join(correction_node.loc.name)
117
- add_offense(location, message: message(correction_node)) do |corrector|
118
- autocorrect(corrector, *nodes)
119
+ add_offense(location, message: message(correction_node, count: count)) do |corrector|
120
+ autocorrect(corrector, *nodes, count)
119
121
  end
120
122
  end
121
123
 
122
- def autocorrect(corrector, prev_def, node)
124
+ def autocorrect(corrector, prev_def, node, count)
123
125
  # finds position of first newline
124
126
  end_pos = end_loc(prev_def).end_pos
125
127
  source_buffer = end_loc(prev_def).source_buffer
@@ -128,8 +130,6 @@ module RuboCop
128
130
  # Handle the case when multiple one-liners are on the same line.
129
131
  newline_pos = end_pos + 1 if newline_pos > node.source_range.begin_pos
130
132
 
131
- count = blank_lines_count_between(prev_def, node)
132
-
133
133
  if count > maximum_empty_lines
134
134
  autocorrect_remove_lines(corrector, newline_pos, count)
135
135
  else
@@ -157,14 +157,22 @@ module RuboCop
157
157
  cop_config['EmptyLineBetweenModuleDefs'] && node.module_type?
158
158
  end
159
159
 
160
- def message(node)
161
- type = case node.type
162
- when :def, :defs
163
- :method
164
- else
165
- node.type
166
- end
167
- format(MSG, type: type)
160
+ def message(node, count: nil)
161
+ type = node_type(node)
162
+
163
+ format(MSG,
164
+ type: type,
165
+ expected: expected_lines,
166
+ actual: count)
167
+ end
168
+
169
+ def expected_lines
170
+ if allowance_range?
171
+ "#{minimum_empty_lines..maximum_empty_lines} empty lines"
172
+ else
173
+ lines = maximum_empty_lines == 1 ? 'line' : 'lines'
174
+ "#{maximum_empty_lines} empty #{lines}"
175
+ end
168
176
  end
169
177
 
170
178
  def multiple_blank_lines_groups?(first_def_node, second_def_node)
@@ -176,8 +184,7 @@ module RuboCop
176
184
  blank_start > non_blank_end
177
185
  end
178
186
 
179
- def blank_lines_between?(first_def_node, second_def_node)
180
- count = blank_lines_count_between(first_def_node, second_def_node)
187
+ def line_count_allowed?(count)
181
188
  (minimum_empty_lines..maximum_empty_lines).cover?(count)
182
189
  end
183
190
 
@@ -230,6 +237,19 @@ module RuboCop
230
237
 
231
238
  corrector.insert_after(where_to_insert, "\n" * difference)
232
239
  end
240
+
241
+ def node_type(node)
242
+ case node.type
243
+ when :def, :defs
244
+ :method
245
+ else
246
+ node.type
247
+ end
248
+ end
249
+
250
+ def allowance_range?
251
+ minimum_empty_lines != maximum_empty_lines
252
+ end
233
253
  end
234
254
  end
235
255
  end