rubocop 1.75.8 → 1.82.1

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +142 -33
  4. data/config/obsoletion.yml +10 -3
  5. data/exe/rubocop +1 -8
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
  7. data/lib/rubocop/cli.rb +20 -4
  8. data/lib/rubocop/comment_config.rb +62 -17
  9. data/lib/rubocop/config_loader.rb +6 -40
  10. data/lib/rubocop/config_loader_resolver.rb +7 -6
  11. data/lib/rubocop/config_store.rb +5 -0
  12. data/lib/rubocop/cop/autocorrect_logic.rb +8 -4
  13. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -3
  14. data/lib/rubocop/cop/correctors/alignment_corrector.rb +8 -7
  15. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  16. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  17. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  18. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  19. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +2 -3
  20. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  21. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
  22. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  23. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  24. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  25. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  26. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +4 -1
  27. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  28. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  29. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  30. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  31. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  32. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  33. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
  34. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  35. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  36. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  37. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  38. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  39. data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
  40. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
  41. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  42. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
  43. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  44. data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
  45. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  46. data/lib/rubocop/cop/layout/line_length.rb +43 -10
  47. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  48. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
  49. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  50. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  51. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  52. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  53. data/lib/rubocop/cop/layout/space_around_keyword.rb +7 -2
  54. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  55. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  56. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  57. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  58. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  59. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  60. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  61. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  62. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  63. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  64. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  65. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  66. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  67. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  68. data/lib/rubocop/cop/lint/empty_interpolation.rb +14 -1
  69. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  70. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  71. data/lib/rubocop/cop/lint/literal_as_condition.rb +38 -28
  72. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  73. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +17 -8
  74. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  75. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  76. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  77. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  78. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  79. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  80. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
  81. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  82. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  83. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  84. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  85. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  86. data/lib/rubocop/cop/lint/self_assignment.rb +39 -5
  87. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  88. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  89. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
  90. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  92. data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
  93. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  94. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  95. data/lib/rubocop/cop/lint/useless_or.rb +111 -0
  96. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  97. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  98. data/lib/rubocop/cop/lint/void.rb +7 -0
  99. data/lib/rubocop/cop/message_annotator.rb +1 -1
  100. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  101. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  102. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  103. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
  104. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  105. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  106. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  107. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  108. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
  109. data/lib/rubocop/cop/mixin/line_length_help.rb +45 -10
  110. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  111. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  112. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  113. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  114. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  115. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  116. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  117. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  118. data/lib/rubocop/cop/naming/method_name.rb +129 -13
  119. data/lib/rubocop/cop/naming/predicate_method.rb +319 -0
  120. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  121. data/lib/rubocop/cop/security/eval.rb +2 -1
  122. data/lib/rubocop/cop/security/json_load.rb +33 -11
  123. data/lib/rubocop/cop/security/open.rb +1 -0
  124. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  125. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  126. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  127. data/lib/rubocop/cop/style/array_intersect.rb +99 -35
  128. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  129. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
  130. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  131. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  132. data/lib/rubocop/cop/style/case_equality.rb +11 -13
  133. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  134. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
  135. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  136. data/lib/rubocop/cop/style/conditional_assignment.rb +12 -16
  137. data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
  138. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  139. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  140. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  141. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  142. data/lib/rubocop/cop/style/endless_method.rb +15 -2
  143. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  144. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  145. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  146. data/lib/rubocop/cop/style/float_division.rb +15 -1
  147. data/lib/rubocop/cop/style/guard_clause.rb +0 -11
  148. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  149. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  150. data/lib/rubocop/cop/style/if_unless_modifier.rb +16 -9
  151. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  152. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  153. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  154. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  155. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  156. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  157. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  158. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +33 -4
  160. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  161. data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
  162. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  163. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  164. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
  165. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  166. data/lib/rubocop/cop/style/parallel_assignment.rb +34 -22
  167. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  168. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  169. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  170. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  171. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  172. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  173. data/lib/rubocop/cop/style/redundant_format.rb +26 -5
  174. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  175. data/lib/rubocop/cop/style/redundant_interpolation.rb +12 -3
  176. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  177. data/lib/rubocop/cop/style/redundant_parentheses.rb +55 -16
  178. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
  179. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  180. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  181. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  182. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  183. data/lib/rubocop/cop/style/safe_navigation.rb +44 -12
  184. data/lib/rubocop/cop/style/semicolon.rb +23 -7
  185. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  186. data/lib/rubocop/cop/style/sole_nested_conditional.rb +40 -3
  187. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  188. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  189. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  190. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  191. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  192. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  193. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  194. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  195. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  196. data/lib/rubocop/cop/util.rb +2 -3
  197. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  198. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  199. data/lib/rubocop/cop/variable_force.rb +25 -8
  200. data/lib/rubocop/cops_documentation_generator.rb +5 -4
  201. data/lib/rubocop/directive_comment.rb +46 -3
  202. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -5
  203. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  204. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  205. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  206. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  207. data/lib/rubocop/lsp/diagnostic.rb +14 -18
  208. data/lib/rubocop/lsp/routes.rb +35 -6
  209. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  210. data/lib/rubocop/magic_comment.rb +20 -0
  211. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  212. data/lib/rubocop/rake_task.rb +1 -1
  213. data/lib/rubocop/remote_config.rb +7 -8
  214. data/lib/rubocop/result_cache.rb +51 -38
  215. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  216. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  217. data/lib/rubocop/rspec/support.rb +1 -1
  218. data/lib/rubocop/runner.rb +10 -4
  219. data/lib/rubocop/server/cache.rb +4 -2
  220. data/lib/rubocop/server/client_command/base.rb +10 -0
  221. data/lib/rubocop/server/client_command/exec.rb +2 -1
  222. data/lib/rubocop/server/client_command/start.rb +11 -1
  223. data/lib/rubocop/target_finder.rb +9 -9
  224. data/lib/rubocop/target_ruby.rb +11 -2
  225. data/lib/rubocop/version.rb +1 -1
  226. data/lib/rubocop.rb +13 -1
  227. data/lib/ruby_lsp/rubocop/addon.rb +25 -10
  228. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  229. metadata +20 -8
@@ -5,15 +5,28 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for blocks with one argument where `it` block parameter can be used.
7
7
  #
8
- # It provides three `EnforcedStyle` options:
8
+ # It provides four `EnforcedStyle` options:
9
9
  #
10
- # 1. `only_numbered_parameters` (default) ... Detects only numbered block parameters.
11
- # 2. `always` ... Always uses the `it` block parameter.
12
- # 3. `disallow` ... Disallows the `it` block parameter.
10
+ # 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
11
+ # 2. `only_numbered_parameters` ... Detects only numbered block parameters.
12
+ # 3. `always` ... Always uses the `it` block parameter.
13
+ # 4. `disallow` ... Disallows the `it` block parameter.
13
14
  #
14
- # A single numbered parameter is detected when `only_numbered_parameters` or `always`.
15
+ # A single numbered parameter is detected when `allow_single_line`,
16
+ # `only_numbered_parameters`, or `always`.
15
17
  #
16
- # @example EnforcedStyle: only_numbered_parameters (default)
18
+ # @example EnforcedStyle: allow_single_line (default)
19
+ # # bad
20
+ # block do
21
+ # do_something(it)
22
+ # end
23
+ # block { do_something(_1) }
24
+ #
25
+ # # good
26
+ # block { do_something(it) }
27
+ # block { |named_param| do_something(named_param) }
28
+ #
29
+ # @example EnforcedStyle: only_numbered_parameters
17
30
  # # bad
18
31
  # block { do_something(_1) }
19
32
  #
@@ -42,8 +55,9 @@ module RuboCop
42
55
  extend TargetRubyVersion
43
56
  extend AutoCorrector
44
57
 
45
- MSG_USE_IT_BLOCK_PARAMETER = 'Use `it` block parameter.'
46
- MSG_AVOID_IT_BLOCK_PARAMETER = 'Avoid using `it` block parameter.'
58
+ MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
59
+ MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
60
+ MSG_AVOID_IT_PARAMETER_MULTILINE = 'Avoid using `it` block parameter for multi-line blocks.'
47
61
 
48
62
  minimum_target_ruby_version 3.4
49
63
 
@@ -57,7 +71,7 @@ module RuboCop
57
71
  variables = find_block_variables(node, node.first_argument.source)
58
72
 
59
73
  variables.each do |variable|
60
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
74
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
61
75
  corrector.remove(node.arguments)
62
76
  corrector.replace(variable, 'it')
63
77
  end
@@ -71,26 +85,33 @@ module RuboCop
71
85
  variables = find_block_variables(node, '_1')
72
86
 
73
87
  variables.each do |variable|
74
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
88
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
75
89
  corrector.replace(variable, 'it')
76
90
  end
77
91
  end
78
92
  end
79
93
 
80
94
  def on_itblock(node)
81
- return unless style == :disallow
95
+ case style
96
+ when :allow_single_line
97
+ return if node.single_line?
82
98
 
83
- variables = find_block_variables(node, 'it')
99
+ add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
100
+ when :disallow
101
+ variables = find_block_variables(node, 'it')
84
102
 
85
- variables.each do |variable|
86
- add_offense(variable, message: MSG_AVOID_IT_BLOCK_PARAMETER)
103
+ variables.each do |variable|
104
+ add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
105
+ end
87
106
  end
88
107
  end
89
108
 
90
109
  private
91
110
 
92
111
  def find_block_variables(node, block_argument_name)
93
- node.each_descendant(:lvar).select do |descendant|
112
+ return [] unless node.body
113
+
114
+ node.body.each_descendant(:lvar).select do |descendant|
94
115
  descendant.source == block_argument_name
95
116
  end
96
117
  end
@@ -56,12 +56,10 @@ module RuboCop
56
56
 
57
57
  def on_send(node)
58
58
  return unless (to_h_node, map_node = map_to_h(node))
59
+ return if to_h_node.block_literal?
59
60
 
60
61
  message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
61
62
  add_offense(map_node.loc.selector, message: message) do |corrector|
62
- # If the `to_h` call already has a block, do not autocorrect.
63
- next if to_h_node.block_literal?
64
-
65
63
  autocorrect(corrector, to_h_node, map_node)
66
64
  end
67
65
  end
@@ -40,12 +40,10 @@ module RuboCop
40
40
 
41
41
  def on_send(node)
42
42
  return unless (to_set_node, map_node = map_to_set?(node))
43
+ return if to_set_node.block_literal?
43
44
 
44
45
  message = format(MSG, method: map_node.loc.selector.source)
45
46
  add_offense(map_node.loc.selector, message: message) do |corrector|
46
- # If the `to_set` call already has a block, do not autocorrect.
47
- next if to_set_node.block_literal?
48
-
49
47
  autocorrect(corrector, to_set_node, map_node)
50
48
  end
51
49
  end
@@ -167,7 +167,7 @@ module RuboCop
167
167
  def call_in_match_pattern?(node)
168
168
  return false unless (parent = node.parent)
169
169
 
170
- parent.type?(:match_pattern, :match_pattern_p)
170
+ parent.any_match_pattern_type?
171
171
  end
172
172
 
173
173
  def hash_literal_in_arguments?(node)
@@ -222,11 +222,9 @@ module RuboCop
222
222
  end
223
223
 
224
224
  def unary_literal?(node)
225
- # NOTE: should be removed after releasing https://github.com/rubocop/rubocop-ast/pull/379
226
- return node.source.match?(/\A[+-]/) if node.complex_type?
225
+ return true if node.numeric_type? && node.sign?
227
226
 
228
- (node.numeric_type? && node.sign?) ||
229
- (node.parent&.send_type? && node.parent.unary_operation?)
227
+ node.parent&.send_type? && node.parent.unary_operation?
230
228
  end
231
229
 
232
230
  def assigned_before?(node, target)
@@ -251,7 +249,7 @@ module RuboCop
251
249
  return false unless (last_argument = node.last_argument)
252
250
  return true if last_argument.forwarded_restarg_type?
253
251
 
254
- last_argument.hash_type? && last_argument.children.first&.forwarded_kwrestarg_type?
252
+ last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
255
253
  end
256
254
  end
257
255
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
@@ -36,10 +36,21 @@ module RuboCop
36
36
  cop_config.fetch('IncludedMacros', []).map(&:to_sym)
37
37
  end
38
38
 
39
+ def included_macro_patterns
40
+ cop_config.fetch('IncludedMacroPatterns', [])
41
+ end
42
+
43
+ def matches_included_macro_pattern?(method_name)
44
+ included_macro_patterns.any? do |pattern|
45
+ Regexp.new(pattern).match?(method_name.to_s)
46
+ end
47
+ end
48
+
39
49
  def ignored_macro?(node)
40
50
  cop_config['IgnoreMacros'] &&
41
51
  node.macro? &&
42
- !included_macros_list.include?(node.method_name)
52
+ !included_macros_list.include?(node.method_name) &&
53
+ !matches_included_macro_pattern?(node.method_name)
43
54
  end
44
55
  end
45
56
  end
@@ -9,17 +9,20 @@ module RuboCop
9
9
  # In the default style (require_parentheses), macro methods are allowed.
10
10
  # Additional methods can be added to the `AllowedMethods` or
11
11
  # `AllowedPatterns` list. These options are valid only in the default
12
- # style. Macros can be included by either setting `IgnoreMacros` to false
13
- # or adding specific macros to the `IncludedMacros` list.
12
+ # style. Macros can be included by either setting `IgnoreMacros` to false,
13
+ # adding specific macros to the `IncludedMacros` list, or using
14
+ # `IncludedMacroPatterns` for pattern-based matching.
14
15
  #
15
16
  # Precedence of options is as follows:
16
17
  #
17
18
  # 1. `AllowedMethods`
18
19
  # 2. `AllowedPatterns`
19
20
  # 3. `IncludedMacros`
21
+ # 4. `IncludedMacroPatterns`
20
22
  #
21
- # If a method is listed in both `IncludedMacros` and `AllowedMethods`,
22
- # then the latter takes precedence (that is, the method is allowed).
23
+ # If a method is listed in both `IncludedMacros`/`IncludedMacroPatterns`
24
+ # and `AllowedMethods`, then the latter takes precedence (that is, the
25
+ # method is allowed).
23
26
  #
24
27
  # In the alternative style (omit_parentheses), there are three additional
25
28
  # options.
@@ -132,6 +135,32 @@ module RuboCop
132
135
  # bar :baz
133
136
  # end
134
137
  #
138
+ # @example AllowedMethods: ["puts", "print"]
139
+ #
140
+ # # good
141
+ # puts "Hello world"
142
+ # print "Hello world"
143
+ # # still enforces parentheses on other methods
144
+ # array.delete(e)
145
+ #
146
+ # @example AllowedPatterns: ["^assert"]
147
+ #
148
+ # # good
149
+ # assert_equal 'test', x
150
+ # assert_match(/foo/, bar)
151
+ # # still enforces parentheses on other methods
152
+ # array.delete(e)
153
+ #
154
+ # @example IncludedMacroPatterns: ["^assert", "^refute"]
155
+ #
156
+ # # bad
157
+ # assert_equal 'test', x
158
+ # refute_nil value
159
+ #
160
+ # # good
161
+ # assert_equal('test', x)
162
+ # refute_nil(value)
163
+ #
135
164
  # @example AllowParenthesesInMultilineCall: false (default)
136
165
  #
137
166
  # # bad
@@ -39,13 +39,21 @@ module RuboCop
39
39
  include RangeHelp
40
40
 
41
41
  MSG = 'Use `%<prefer>s` instead.'
42
- GRATER_OPERATORS = %i[> >=].freeze
42
+ GREATER_OPERATORS = %i[> >=].freeze
43
43
  LESS_OPERATORS = %i[< <=].freeze
44
- COMPARISON_OPERATORS = GRATER_OPERATORS + LESS_OPERATORS
44
+ COMPARISON_OPERATORS = (GREATER_OPERATORS + LESS_OPERATORS).to_set.freeze
45
+
46
+ # @!method comparison_condition(node, name)
47
+ def_node_matcher :comparison_condition, <<~PATTERN
48
+ {
49
+ (send $_lhs $COMPARISON_OPERATORS $_rhs)
50
+ (begin (send $_lhs $COMPARISON_OPERATORS $_rhs))
51
+ }
52
+ PATTERN
45
53
 
46
54
  def on_if(node)
47
- lhs, operator, rhs = *node.condition
48
- return unless COMPARISON_OPERATORS.include?(operator)
55
+ lhs, operator, rhs = comparison_condition(node.condition)
56
+ return unless operator
49
57
 
50
58
  if_branch = node.if_branch
51
59
  else_branch = node.else_branch
@@ -63,7 +71,7 @@ module RuboCop
63
71
 
64
72
  def preferred_method(operator, lhs, rhs, if_branch, else_branch)
65
73
  if lhs == if_branch && rhs == else_branch
66
- GRATER_OPERATORS.include?(operator) ? 'max' : 'min'
74
+ GREATER_OPERATORS.include?(operator) ? 'max' : 'min'
67
75
  elsif lhs == else_branch && rhs == if_branch
68
76
  LESS_OPERATORS.include?(operator) ? 'max' : 'min'
69
77
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for usage of `Module` methods returning arrays that can be replaced
7
+ # with equivalent predicates.
8
+ #
9
+ # Calling a method returning an array then checking if an element is inside
10
+ # it is much slower than using an equivalent predicate method. For example,
11
+ # `instance_methods.include?` will return an array of all public and protected
12
+ # instance methods in the module, then check if a given method is inside that
13
+ # array, while `method_defined?` will do direct method lookup, which is much
14
+ # faster and consumes less memory.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # Array.instance_methods.include?(:size)
19
+ # Array.instance_methods.member?(:size)
20
+ # Array.instance_methods(true).include?(:size)
21
+ #
22
+ # Array.instance_methods(false).include?(:find)
23
+ #
24
+ # # good
25
+ # Array.method_defined?(:size)
26
+ #
27
+ # Array.method_defined?(:find, false)
28
+ #
29
+ class ModuleMemberExistenceCheck < Base
30
+ extend AutoCorrector
31
+
32
+ MSG = 'Use `%<replacement>s` instead.'
33
+
34
+ RESTRICT_ON_SEND = %i[instance_methods].freeze
35
+
36
+ # @!method instance_methods_inclusion?(node)
37
+ def_node_matcher :instance_methods_inclusion?, <<~PATTERN
38
+ (call
39
+ (call _ :instance_methods _?)
40
+ {:include? :member?}
41
+ _)
42
+ PATTERN
43
+
44
+ def on_send(node) # rubocop:disable Metrics/AbcSize
45
+ return unless (parent = node.parent)
46
+ return unless instance_methods_inclusion?(parent)
47
+ return unless simple_method_argument?(node) && simple_method_argument?(parent)
48
+
49
+ offense_range = node.location.selector.join(parent.source_range.end)
50
+ replacement =
51
+ if node.first_argument.nil? || node.first_argument.true_type?
52
+ "method_defined?(#{parent.first_argument.source})"
53
+ else
54
+ "method_defined?(#{parent.first_argument.source}, #{node.first_argument.source})"
55
+ end
56
+
57
+ add_offense(offense_range, message: format(MSG, replacement: replacement)) do |corrector|
58
+ corrector.replace(offense_range, replacement)
59
+ end
60
+ end
61
+ alias on_csend on_send
62
+
63
+ private
64
+
65
+ def simple_method_argument?(node)
66
+ return false if node.splat_argument? || node.block_argument?
67
+ return false if node.first_argument&.hash_type?
68
+
69
+ true
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -75,6 +75,8 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def correction_exceeds_max_line_length?(node)
78
+ return false unless max_line_length
79
+
78
80
  indentation_width(node) + definition_width(node) > max_line_length
79
81
  end
80
82
 
@@ -85,10 +87,6 @@ module RuboCop
85
87
  def definition_width(node)
86
88
  node.source_range.begin.join(node.arguments.source_range.end).length
87
89
  end
88
-
89
- def max_line_length
90
- config.for_cop('Layout/LineLength')['Max'] || 120
91
- end
92
90
  end
93
91
  end
94
92
  end
@@ -43,24 +43,26 @@ module RuboCop
43
43
  # @!method nil_check?(node)
44
44
  def_node_matcher :nil_check?, '(send _ :nil?)'
45
45
 
46
+ # rubocop:disable Metrics/AbcSize
46
47
  def on_send(node)
47
48
  return unless node.receiver
48
49
 
49
50
  style_check?(node) do
50
51
  add_offense(node.loc.selector) do |corrector|
51
- new_code = if prefer_comparison?
52
- node.source.sub('.nil?', ' == nil')
53
- else
54
- node.source.sub(/\s*={2,3}\s*nil/, '.nil?')
55
- end
56
-
57
- corrector.replace(node, new_code)
52
+ if prefer_comparison?
53
+ range = node.loc.dot.join(node.loc.selector.end)
54
+ corrector.replace(range, ' == nil')
55
+ else
56
+ range = node.receiver.source_range.end.join(node.source_range.end)
57
+ corrector.replace(range, '.nil?')
58
+ end
58
59
 
59
60
  parent = node.parent
60
61
  corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
61
62
  end
62
63
  end
63
64
  end
65
+ # rubocop:enable Metrics/AbcSize
64
66
 
65
67
  private
66
68
 
@@ -55,19 +55,21 @@ module RuboCop
55
55
  include OnNormalIfUnless
56
56
  extend AutoCorrector
57
57
 
58
- MSG = 'Favor the ternary operator (`?:`) or multi-line constructs ' \
59
- 'over single-line `%<keyword>s/then/else/end` constructs.'
58
+ MSG_SUFFIX = 'over single-line `%<keyword>s/then/else/end` constructs.'
59
+ MSG_TERNARY = "Favor the ternary operator (`?:`) #{MSG_SUFFIX}"
60
+ MSG_MULTILINE = "Favor multi-line `%<keyword>s` #{MSG_SUFFIX}"
60
61
 
61
62
  def on_normal_if_unless(node)
62
63
  return unless node.single_line?
63
64
  return unless node.else_branch
64
65
  return if node.elsif? || node.if_branch&.begin_type?
65
66
 
66
- message = message(node)
67
- add_offense(node, message: message) do |corrector|
67
+ multiline = multiline?(node)
68
+
69
+ add_offense(node, message: message(node, multiline)) do |corrector|
68
70
  next if part_of_ignored_node?(node)
69
71
 
70
- autocorrect(corrector, node)
72
+ autocorrect(corrector, node, multiline)
71
73
 
72
74
  ignore_node(node)
73
75
  end
@@ -75,12 +77,18 @@ module RuboCop
75
77
 
76
78
  private
77
79
 
78
- def message(node)
79
- format(MSG, keyword: node.keyword)
80
+ def multiline?(node)
81
+ always_multiline? || cannot_replace_to_ternary?(node)
82
+ end
83
+
84
+ def message(node, multiline)
85
+ template = multiline ? MSG_MULTILINE : MSG_TERNARY
86
+
87
+ format(template, keyword: node.keyword)
80
88
  end
81
89
 
82
- def autocorrect(corrector, node)
83
- if always_multiline? || cannot_replace_to_ternary?(node)
90
+ def autocorrect(corrector, node, multiline)
91
+ if multiline
84
92
  IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
85
93
  else
86
94
  corrector.replace(node, ternary_correction(node))
@@ -26,9 +26,10 @@ module RuboCop
26
26
  splat kwsplat forwarded_args forwarded_restarg forwarded_kwrestarg block_pass
27
27
  ].freeze
28
28
 
29
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
29
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
30
30
  def on_send(node)
31
31
  return unless (dot = node.loc.dot)
32
+ return if unary_method_no_operator?(node)
32
33
  return if node.receiver.const_type? || !node.arguments.one?
33
34
 
34
35
  return unless (rhs = node.first_argument)
@@ -43,10 +44,18 @@ module RuboCop
43
44
  corrector.insert_after(selector, ' ') if insert_space_after?(node)
44
45
  end
45
46
  end
46
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
47
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
47
48
 
48
49
  private
49
50
 
51
+ # `foo.~@` and `foo.!@` call the method `~` and `!` respectively. While those
52
+ # are operator methods, we don't want to actually consider them as such.
53
+ def unary_method_no_operator?(node)
54
+ return false unless node.nonmutating_unary_operator_method?
55
+
56
+ node.method_name.to_s != node.selector.source
57
+ end
58
+
50
59
  # Checks for an acceptable case of `foo.+(bar).baz`.
51
60
  def method_call_with_parenthesized_arg?(argument)
52
61
  return false unless argument.parent.parent&.send_type?
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tsort'
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -16,7 +14,7 @@ module RuboCop
16
14
  #
17
15
  # # good
18
16
  # one, two = *foo
19
- # a, b = foo()
17
+ # a, b = foo
20
18
  # a, b = b, a
21
19
  #
22
20
  # a = 1
@@ -29,6 +27,8 @@ module RuboCop
29
27
  MSG = 'Do not use parallel assignment.'
30
28
 
31
29
  def on_masgn(node) # rubocop:disable Metrics/AbcSize
30
+ return if part_of_ignored_node?(node)
31
+
32
32
  rhs = node.rhs
33
33
  rhs = rhs.body if rhs.rescue_type?
34
34
  rhs_elements = Array(rhs).compact # edge case for one constant
@@ -41,6 +41,7 @@ module RuboCop
41
41
  add_offense(range) do |corrector|
42
42
  autocorrect(corrector, node, rhs)
43
43
  end
44
+ ignore_node(node)
44
45
  end
45
46
 
46
47
  private
@@ -91,15 +92,9 @@ module RuboCop
91
92
  def find_valid_order(left_elements, right_elements)
92
93
  # arrange left_elements in an order such that no corresponding right
93
94
  # element refers to a left element earlier in the sequence
94
- # this can be done using an algorithm called a "topological sort"
95
- # fortunately for us, Ruby's stdlib contains an implementation
96
95
  assignments = left_elements.zip(right_elements)
97
96
 
98
- begin
99
- AssignmentSorter.new(assignments).tsort
100
- rescue TSort::Cyclic
101
- nil
102
- end
97
+ AssignmentSorter.new(assignments).tsort
103
98
  end
104
99
 
105
100
  # Converts (send nil :something) nodes to (send (:self) :something).
@@ -114,10 +109,9 @@ module RuboCop
114
109
  # @!method implicit_self_getter?(node)
115
110
  def_node_matcher :implicit_self_getter?, '(send nil? $_)'
116
111
 
117
- # Helper class necessitated by silly design of TSort prior to Ruby 2.1
118
- # Newer versions have a better API, but that doesn't help us
112
+ # Topologically sorts the assignments with Kahn's algorithm.
113
+ # https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
119
114
  class AssignmentSorter
120
- include TSort
121
115
  extend RuboCop::NodePattern::Macros
122
116
 
123
117
  # @!method var_name(node)
@@ -133,21 +127,39 @@ module RuboCop
133
127
  @assignments = assignments
134
128
  end
135
129
 
136
- def tsort_each_node(...)
137
- @assignments.each(...)
130
+ def tsort
131
+ dependencies = @assignments.to_h do |assignment|
132
+ [assignment, dependencies_for_assignment(assignment)]
133
+ end
134
+ result = []
135
+
136
+ while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
137
+ dependencies.delete(matched_node)
138
+ result.push(matched_node)
139
+
140
+ dependencies.each do |node, edges|
141
+ dependencies[node].delete(matched_node) if edges.include?(matched_node)
142
+ end
143
+ end
144
+ # Cyclic dependency
145
+ return nil if dependencies.any?
146
+
147
+ result
138
148
  end
139
149
 
140
- def tsort_each_child(assignment)
141
- # yield all the assignments which must come after `assignment`
142
- # (due to dependencies on the previous value of the assigned var)
150
+ # Returns all the assignments which must come after `assignment`
151
+ # (due to dependencies on the previous value of the assigned var)
152
+ def dependencies_for_assignment(assignment)
143
153
  my_lhs, _my_rhs = *assignment
144
154
 
145
- @assignments.each do |other|
146
- _other_lhs, other_rhs = *other
155
+ @assignments.filter_map do |other|
156
+ # Exclude self, there are no dependencies in cases such as `a, b = a, b`.
157
+ next if other == assignment
147
158
 
159
+ _other_lhs, other_rhs = *other
148
160
  next unless dependency?(my_lhs, other_rhs)
149
161
 
150
- yield other
162
+ other
151
163
  end
152
164
  end
153
165
 
@@ -211,7 +223,7 @@ module RuboCop
211
223
  # __FILE__ is treated as a StrNode but has no begin
212
224
  if node.str_type? && loc.respond_to?(:begin) && loc.begin.nil?
213
225
  "'#{node.source}'"
214
- elsif node.sym_type? && loc.begin.nil?
226
+ elsif node.sym_type? && !node.loc?(:begin)
215
227
  ":#{node.source}"
216
228
  else
217
229
  node.source
@@ -37,6 +37,7 @@ module RuboCop
37
37
  # array.sum(0)
38
38
  # exit(true)
39
39
  # exit!(false)
40
+ # string.to_i(10)
40
41
  # string.split(" ")
41
42
  # "first\nsecond".split(" ")
42
43
  # string.chomp("\n")
@@ -49,6 +50,7 @@ module RuboCop
49
50
  # array.sum
50
51
  # exit
51
52
  # exit!
53
+ # string.to_i
52
54
  # string.split
53
55
  # "first second".split
54
56
  # string.chomp
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant calls of `Array#flatten`.
7
+ #
8
+ # `Array#join` joins nested arrays recursively, so flattening an array
9
+ # beforehand is redundant.
10
+ #
11
+ # @safety
12
+ # Cop is unsafe because the receiver of `flatten` method might not
13
+ # be an `Array`, so it's possible it won't respond to `join` method,
14
+ # or the end result would be different.
15
+ # Also, if the global variable `$,` is set to a value other than the default `nil`,
16
+ # false positives may occur.
17
+ #
18
+ # @example
19
+ # # bad
20
+ # x.flatten.join
21
+ # x.flatten(1).join
22
+ #
23
+ # # good
24
+ # x.join
25
+ #
26
+ class RedundantArrayFlatten < Base
27
+ extend AutoCorrector
28
+
29
+ MSG = 'Remove the redundant `flatten`.'
30
+
31
+ RESTRICT_ON_SEND = %i[flatten].freeze
32
+
33
+ # @!method flatten_join?(node)
34
+ def_node_matcher :flatten_join?, <<~PATTERN
35
+ (call (call !nil? :flatten _?) :join (nil)?)
36
+ PATTERN
37
+
38
+ def on_send(node)
39
+ return unless flatten_join?(node.parent)
40
+
41
+ range = node.loc.dot.begin.join(node.source_range.end)
42
+ add_offense(range) do |corrector|
43
+ corrector.remove(range)
44
+ end
45
+ end
46
+ alias on_csend on_send
47
+ end
48
+ end
49
+ end
50
+ end