rubocop 1.79.2 → 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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +35 -7
  4. data/config/obsoletion.yml +4 -0
  5. data/exe/rubocop +1 -8
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
  7. data/lib/rubocop/cli.rb +8 -3
  8. data/lib/rubocop/comment_config.rb +62 -17
  9. data/lib/rubocop/config_loader.rb +5 -2
  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 +1 -2
  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/gemspec/ordered_dependencies.rb +1 -2
  17. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
  18. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  19. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  20. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  21. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  22. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  23. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  24. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
  25. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  26. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  27. data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
  28. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
  29. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  30. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
  31. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  32. data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
  33. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  34. data/lib/rubocop/cop/layout/line_length.rb +17 -5
  35. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  36. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
  37. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  38. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  39. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  40. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  41. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  42. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  43. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  44. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  45. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  46. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  47. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  48. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  49. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  50. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  51. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  52. data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
  53. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  54. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +17 -8
  55. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  56. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  57. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  58. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
  59. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  60. data/lib/rubocop/cop/lint/self_assignment.rb +15 -6
  61. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  62. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
  63. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  64. data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
  65. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  66. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
  67. data/lib/rubocop/cop/lint/void.rb +7 -0
  68. data/lib/rubocop/cop/message_annotator.rb +1 -1
  69. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  70. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  71. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
  72. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  73. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  74. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
  75. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  76. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  77. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  78. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  79. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  80. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  81. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  82. data/lib/rubocop/cop/naming/method_name.rb +5 -3
  83. data/lib/rubocop/cop/naming/predicate_method.rb +19 -6
  84. data/lib/rubocop/cop/security/json_load.rb +33 -11
  85. data/lib/rubocop/cop/style/array_intersect.rb +46 -12
  86. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  87. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
  88. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  89. data/lib/rubocop/cop/style/case_equality.rb +11 -13
  90. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
  91. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -14
  92. data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
  93. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  94. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  95. data/lib/rubocop/cop/style/endless_method.rb +15 -2
  96. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  97. data/lib/rubocop/cop/style/float_division.rb +15 -1
  98. data/lib/rubocop/cop/style/guard_clause.rb +0 -11
  99. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  100. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  101. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  102. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
  103. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  104. data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
  105. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  106. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  107. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
  108. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  109. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  110. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  111. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  112. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  113. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  114. data/lib/rubocop/cop/style/redundant_format.rb +26 -5
  115. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  116. data/lib/rubocop/cop/style/redundant_parentheses.rb +14 -11
  117. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
  118. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  119. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  120. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  121. data/lib/rubocop/cop/style/safe_navigation.rb +18 -1
  122. data/lib/rubocop/cop/style/semicolon.rb +23 -7
  123. data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
  124. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  125. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  126. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  127. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  128. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  129. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  130. data/lib/rubocop/cop/util.rb +2 -3
  131. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  132. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  133. data/lib/rubocop/cop/variable_force.rb +9 -7
  134. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  135. data/lib/rubocop/directive_comment.rb +46 -3
  136. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -5
  137. data/lib/rubocop/lsp/diagnostic.rb +10 -14
  138. data/lib/rubocop/lsp/routes.rb +31 -2
  139. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  140. data/lib/rubocop/magic_comment.rb +20 -0
  141. data/lib/rubocop/rake_task.rb +1 -1
  142. data/lib/rubocop/remote_config.rb +7 -8
  143. data/lib/rubocop/result_cache.rb +39 -28
  144. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  145. data/lib/rubocop/rspec/support.rb +1 -1
  146. data/lib/rubocop/runner.rb +10 -4
  147. data/lib/rubocop/target_finder.rb +9 -9
  148. data/lib/rubocop/target_ruby.rb +11 -2
  149. data/lib/rubocop/version.rb +1 -1
  150. data/lib/rubocop.rb +2 -0
  151. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  152. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  153. metadata +9 -7
@@ -3,31 +3,36 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of the case equality operator(===).
6
+ # Checks for uses of the case equality operator (`===`).
7
7
  #
8
8
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
9
  # the case equality operator is a constant.
10
-
10
+ #
11
11
  # If `AllowOnSelfClass` option is enabled, the cop will ignore violations when the receiver of
12
12
  # the case equality operator is `self.class`. Note intermediate variables are not accepted.
13
13
  #
14
+ # NOTE: Regexp case equality (`/regexp/ === var`) is allowed because changing it to
15
+ # `/regexp/.match?(var)` needs to take into account `Regexp.last_match?`, `$~`, `$1`, etc.
16
+ # This potentially incompatible transformation is handled by `Performance/RegexpMatch` cop.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # (1..100) === 7
17
- # /something/ === some_string
18
21
  #
19
22
  # # good
20
- # something.is_a?(Array)
21
23
  # (1..100).include?(7)
22
- # /something/.match?(some_string)
23
24
  #
24
25
  # @example AllowOnConstant: false (default)
25
26
  # # bad
26
27
  # Array === something
27
28
  #
29
+ # # good
30
+ # something.is_a?(Array)
31
+ #
28
32
  # @example AllowOnConstant: true
29
33
  # # good
30
34
  # Array === something
35
+ # something.is_a?(Array)
31
36
  #
32
37
  # @example AllowOnSelfClass: false (default)
33
38
  # # bad
@@ -51,7 +56,7 @@ module RuboCop
51
56
 
52
57
  def on_send(node)
53
58
  case_equality?(node) do |lhs, rhs|
54
- return if lhs.const_type? && !lhs.module_name?
59
+ return if lhs.regexp_type? || (lhs.const_type? && !lhs.module_name?)
55
60
 
56
61
  add_offense(node.loc.selector) do |corrector|
57
62
  replacement = replacement(lhs, rhs)
@@ -71,13 +76,6 @@ module RuboCop
71
76
 
72
77
  def replacement(lhs, rhs)
73
78
  case lhs.type
74
- when :regexp
75
- # The automatic correction from `a === b` to `a.match?(b)` needs to
76
- # consider `Regexp.last_match?`, `$~`, `$1`, and etc.
77
- # This correction is expected to be supported by `Performance/Regexp` cop.
78
- # See: https://github.com/rubocop/rubocop-performance/issues/152
79
- #
80
- # So here is noop.
81
79
  when :begin
82
80
  begin_replacement(lhs, rhs)
83
81
  when :const
@@ -183,6 +183,7 @@ module RuboCop
183
183
 
184
184
  def check_nested_style(node)
185
185
  return unless compact_node_name?(node)
186
+ return if node.parent&.type?(:class, :module)
186
187
 
187
188
  add_offense(node.loc.name, message: NESTED_MSG) do |corrector|
188
189
  autocorrect(corrector, node)
@@ -213,9 +213,7 @@ module RuboCop
213
213
  ASSIGN_TO_CONDITION_MSG = 'Assign variables inside of conditionals.'
214
214
  VARIABLE_ASSIGNMENT_TYPES = %i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
215
215
  ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES + %i[and_asgn or_asgn op_asgn masgn].freeze
216
- LINE_LENGTH = 'Layout/LineLength'
217
216
  ENABLED = 'Enabled'
218
- MAX = 'Max'
219
217
  SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'
220
218
 
221
219
  # The shovel operator `<<` does not have its own type. It is a `send`
@@ -399,7 +397,7 @@ module RuboCop
399
397
  # of the longest line + the length of the corrected assignment is
400
398
  # greater than the max configured line length
401
399
  def correction_exceeds_line_limit?(node, branches)
402
- return false unless line_length_cop_enabled?
400
+ return false unless config.cop_enabled?('Layout/LineLength')
403
401
 
404
402
  assignment = lhs(tail(branches[0]))
405
403
 
@@ -417,14 +415,6 @@ module RuboCop
417
415
  assignment + longest_line
418
416
  end
419
417
 
420
- def line_length_cop_enabled?
421
- config.for_cop(LINE_LENGTH)[ENABLED]
422
- end
423
-
424
- def max_line_length
425
- config.for_cop(LINE_LENGTH)[MAX]
426
- end
427
-
428
418
  def single_line_conditions_only?
429
419
  cop_config[SINGLE_LINE_CONDITIONS_ONLY]
430
420
  end
@@ -444,7 +434,7 @@ module RuboCop
444
434
  next if child.parent.dstr_type?
445
435
 
446
436
  white_space = white_space_range(child, column)
447
- corrector.remove(white_space) if white_space.source.strip.empty?
437
+ corrector.remove(white_space) if white_space
448
438
  end
449
439
 
450
440
  if condition.loc.else && !same_line?(condition.else_branch, condition)
@@ -465,9 +455,13 @@ module RuboCop
465
455
 
466
456
  def white_space_range(node, column)
467
457
  expression = node.source_range
468
- begin_pos = expression.begin_pos - (expression.column - column - 2)
458
+ end_pos = expression.begin_pos
459
+ begin_pos = end_pos - (expression.column - column - 2)
460
+
461
+ return nil if begin_pos > end_pos
469
462
 
470
- Parser::Source::Range.new(expression.source_buffer, begin_pos, expression.begin_pos)
463
+ white_space = Parser::Source::Range.new(expression.source_buffer, begin_pos, end_pos)
464
+ white_space if white_space.source.strip.empty?
471
465
  end
472
466
 
473
467
  def assignment(node)
@@ -29,25 +29,30 @@ module RuboCop
29
29
  # @example IgnoreModules: false (default)
30
30
  # # bad
31
31
  # class Foo
32
- # MyClass = Struct.new()
32
+ # MyClass = Struct.new
33
33
  # end
34
34
  #
35
35
  # # good
36
36
  # class Foo
37
- # MyClass = Struct.new()
37
+ # MyClass = Struct.new
38
38
  # public_constant :MyClass
39
39
  # end
40
40
  #
41
41
  # @example IgnoreModules: true
42
42
  # # good
43
43
  # class Foo
44
- # MyClass = Struct.new()
44
+ # MyClass = Struct.new
45
45
  # end
46
46
  #
47
47
  class ConstantVisibility < Base
48
48
  MSG = 'Explicitly make `%<constant_name>s` public or private using ' \
49
49
  'either `#public_constant` or `#private_constant`.'
50
50
 
51
+ # @!method visibility_declaration_for(node)
52
+ def_node_matcher :visibility_declaration_for, <<~PATTERN
53
+ (send nil? {:public_constant :private_constant} $...)
54
+ PATTERN
55
+
51
56
  def on_casgn(node)
52
57
  return unless class_or_module_scope?(node)
53
58
  return if visibility_declaration?(node)
@@ -77,20 +82,20 @@ module RuboCop
77
82
  end
78
83
  end
79
84
 
85
+ # rubocop:disable Metrics/AbcSize
80
86
  def visibility_declaration?(node)
81
87
  node.parent.each_child_node(:send).any? do |child|
82
- visibility_declaration_for?(child, node.name)
83
- end
84
- end
88
+ next false unless (arguments = visibility_declaration_for(child))
85
89
 
86
- # @!method visibility_declaration_for?(node, const_name)
87
- def_node_matcher :visibility_declaration_for?, <<~PATTERN
88
- (send nil? {:public_constant :private_constant} ({sym str} #match_name?(%1)))
89
- PATTERN
90
+ arguments = arguments.first.children.first.to_a if arguments.first&.splat_type?
91
+ constant_values = arguments.map do |argument|
92
+ argument.value.to_sym if argument.respond_to?(:value)
93
+ end
90
94
 
91
- def match_name?(name, constant_name)
92
- name.to_sym == constant_name.to_sym
95
+ constant_values.include?(node.name)
96
+ end
93
97
  end
98
+ # rubocop:enable Metrics/AbcSize
94
99
  end
95
100
  end
96
101
  end
@@ -96,7 +96,7 @@ module RuboCop
96
96
  elsif last_child.type?(:pair, :hash) || last_child.parent.array_type?
97
97
  false
98
98
  else
99
- last_child.last_line <= node.last_line
99
+ last_child.first_line <= node.first_line
100
100
  end
101
101
  end
102
102
 
@@ -107,12 +107,6 @@ module RuboCop
107
107
  def expanded_style?
108
108
  style == :expanded
109
109
  end
110
-
111
- def max_line_length
112
- return unless config.cop_enabled?('Layout/LineLength')
113
-
114
- config.for_cop('Layout/LineLength')['Max']
115
- end
116
110
  end
117
111
  end
118
112
  end
@@ -144,7 +144,7 @@ module RuboCop
144
144
  MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
145
145
 
146
146
  def on_def(node)
147
- return if node.assignment_method?
147
+ return if node.assignment_method? || use_heredoc?(node)
148
148
 
149
149
  case style
150
150
  when :allow_single_line, :allow_always
@@ -198,6 +198,13 @@ module RuboCop
198
198
  add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
199
199
  end
200
200
 
201
+ def use_heredoc?(node)
202
+ return false unless (body = node.body)
203
+ return true if body.any_str_type? && body.heredoc?
204
+
205
+ body.each_descendant(:str).any?(&:heredoc?)
206
+ end
207
+
201
208
  def correct_to_multiline(corrector, node)
202
209
  replacement = <<~RUBY.strip
203
210
  def #{node.method_name}#{arguments(node)}
@@ -225,7 +232,13 @@ module RuboCop
225
232
  def too_long_when_made_endless?(node)
226
233
  return false unless config.cop_enabled?('Layout/LineLength')
227
234
 
228
- endless_replacement(node).length > config.for_cop('Layout/LineLength')['Max']
235
+ offset = modifier_offset(node)
236
+
237
+ endless_replacement(node).length + offset > max_line_length
238
+ end
239
+
240
+ def modifier_offset(node)
241
+ same_line?(node.parent, node) ? node.loc.column - node.parent.loc.column : 0
229
242
  end
230
243
  end
231
244
  end
@@ -56,7 +56,7 @@ module RuboCop
56
56
 
57
57
  def initialize(config = nil, options = nil)
58
58
  super
59
- @def_nodes = Set.new
59
+ @def_nodes = Set.new.compare_by_identity
60
60
  end
61
61
 
62
62
  def on_yield(node)
@@ -7,9 +7,12 @@ module RuboCop
7
7
  # It is recommended to either always use `fdiv` or coerce one side only.
8
8
  # This cop also provides other options for code consistency.
9
9
  #
10
+ # For `Regexp.last_match` and nth reference (e.g., `$1`), it assumes that the value
11
+ # is a string matched by a regular expression, and allows conversion with `#to_f`.
12
+ #
10
13
  # @safety
11
14
  # This cop is unsafe, because if the operand variable is a string object
12
- # then `.to_f` will be removed and an error will occur.
15
+ # then `#to_f` will be removed and an error will occur.
13
16
  #
14
17
  # [source,ruby]
15
18
  # ----
@@ -84,6 +87,14 @@ module RuboCop
84
87
  (send !nil? :to_f)
85
88
  PATTERN
86
89
 
90
+ # @!method regexp_last_match?(node)
91
+ def_node_matcher :regexp_last_match?, <<~PATTERN
92
+ {
93
+ (send (const {nil? cbase} :Regexp) :last_match int)
94
+ (:nth_ref _)
95
+ }
96
+ PATTERN
97
+
87
98
  def on_send(node)
88
99
  return unless offense_condition?(node)
89
100
 
@@ -104,6 +115,9 @@ module RuboCop
104
115
  private
105
116
 
106
117
  def offense_condition?(node)
118
+ return false if regexp_last_match?(node.receiver.receiver) ||
119
+ regexp_last_match?(node.first_argument.receiver)
120
+
107
121
  case style
108
122
  when :left_coerce
109
123
  right_coerce?(node)
@@ -45,17 +45,6 @@ module RuboCop
45
45
  # ok
46
46
  #
47
47
  # # bad
48
- # if something
49
- # foo || raise('exception')
50
- # else
51
- # ok
52
- # end
53
- #
54
- # # good
55
- # foo || raise('exception') if something
56
- # ok
57
- #
58
- # # bad
59
48
  # define_method(:test) do
60
49
  # if something
61
50
  # work
@@ -212,7 +212,7 @@ module RuboCop
212
212
  end
213
213
 
214
214
  def word_symbol_pair?(pair)
215
- return false unless pair.key.type?(:sym, :dsym)
215
+ return false unless pair.key.any_sym_type?
216
216
 
217
217
  acceptable_19_syntax_symbol?(pair.key.source)
218
218
  end
@@ -207,14 +207,14 @@ module RuboCop
207
207
  def too_long_line_based_on_config?(range, line)
208
208
  return false if matches_allowed_pattern?(line)
209
209
 
210
- too_long = too_long_line_based_on_ignore_cop_directives?(range, line)
210
+ too_long = too_long_line_based_on_allow_cop_directives?(range, line)
211
211
  return too_long unless too_long == :undetermined
212
212
 
213
213
  too_long_line_based_on_allow_uri?(line)
214
214
  end
215
215
 
216
- def too_long_line_based_on_ignore_cop_directives?(range, line)
217
- if ignore_cop_directives? && directive_on_source_line?(range.line - 1)
216
+ def too_long_line_based_on_allow_cop_directives?(range, line)
217
+ if allow_cop_directives? && directive_on_source_line?(range.line - 1)
218
218
  return line_length_without_directive(line) > max_line_length
219
219
  end
220
220
 
@@ -68,7 +68,7 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def autocorrect(corrector, node)
71
- if node.type?(:while_post, :until_post)
71
+ if node.post_condition_loop?
72
72
  replace_begin_end_with_modifier(corrector, node)
73
73
  elsif node.modifier_form?
74
74
  replace_source(corrector, node.source_range, modifier_replacement(node))
@@ -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.
@@ -148,6 +151,16 @@ module RuboCop
148
151
  # # still enforces parentheses on other methods
149
152
  # array.delete(e)
150
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
+ #
151
164
  # @example AllowParenthesesInMultilineCall: false (default)
152
165
  #
153
166
  # # bad
@@ -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?
@@ -14,7 +14,7 @@ module RuboCop
14
14
  #
15
15
  # # good
16
16
  # one, two = *foo
17
- # a, b = foo()
17
+ # a, b = foo
18
18
  # a, b = b, a
19
19
  #
20
20
  # a = 1
@@ -223,7 +223,7 @@ module RuboCop
223
223
  # __FILE__ is treated as a StrNode but has no begin
224
224
  if node.str_type? && loc.respond_to?(:begin) && loc.begin.nil?
225
225
  "'#{node.source}'"
226
- elsif node.sym_type? && loc.begin.nil?
226
+ elsif node.sym_type? && !node.loc?(:begin)
227
227
  ":#{node.source}"
228
228
  else
229
229
  node.source