rubocop 1.75.1 → 1.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +103 -25
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/config_validator.rb +6 -6
  6. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  7. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  8. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  9. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  10. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +37 -15
  11. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  13. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  15. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -0
  16. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
  17. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  18. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  19. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  20. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  21. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  22. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -2
  23. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
  24. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  25. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
  26. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  27. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  28. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
  29. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +2 -4
  30. data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
  31. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -38
  32. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +12 -3
  33. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  34. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  35. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  36. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  37. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
  38. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  39. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  40. data/lib/rubocop/cop/lint/duplicate_methods.rb +86 -5
  41. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  42. data/lib/rubocop/cop/lint/float_comparison.rb +31 -4
  43. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  44. data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
  45. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  46. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  47. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
  48. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  49. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  50. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -4
  51. data/lib/rubocop/cop/lint/return_in_void_context.rb +7 -2
  52. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  53. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  54. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  55. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  56. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  57. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  58. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  59. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  60. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  61. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  62. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  63. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  64. data/lib/rubocop/cop/lint/void.rb +2 -2
  65. data/lib/rubocop/cop/message_annotator.rb +7 -3
  66. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  67. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  68. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  69. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  70. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  71. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  72. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -2
  73. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  74. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  75. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  76. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
  77. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  78. data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
  79. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  80. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  81. data/lib/rubocop/cop/naming/method_name.rb +1 -1
  82. data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
  83. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  84. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  85. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
  86. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  87. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
  88. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  89. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  90. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  91. data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
  92. data/lib/rubocop/cop/style/comparable_between.rb +5 -2
  93. data/lib/rubocop/cop/style/conditional_assignment.rb +18 -4
  94. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  95. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  96. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  97. data/lib/rubocop/cop/style/empty_literal.rb +4 -0
  98. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  99. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  100. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  101. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  102. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  103. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  104. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  105. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  106. data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
  107. data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
  108. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  109. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  110. data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
  111. data/lib/rubocop/cop/style/if_unless_modifier.rb +33 -6
  112. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  113. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  114. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  115. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  116. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  117. data/lib/rubocop/cop/style/map_into_array.rb +3 -1
  118. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  119. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -3
  120. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  121. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  122. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  123. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  124. data/lib/rubocop/cop/style/redundant_condition.rb +13 -1
  125. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  126. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  127. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  128. data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
  129. data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -3
  130. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  131. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  132. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  133. data/lib/rubocop/cop/style/safe_navigation.rb +42 -14
  134. data/lib/rubocop/cop/style/sole_nested_conditional.rb +6 -3
  135. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  136. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  137. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  138. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  139. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
  140. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
  141. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  142. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
  143. data/lib/rubocop/cop/team.rb +1 -1
  144. data/lib/rubocop/cop/util.rb +1 -1
  145. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  146. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  147. data/lib/rubocop/cops_documentation_generator.rb +6 -2
  148. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  149. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  150. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  151. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  152. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  153. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  154. data/lib/rubocop/magic_comment.rb +8 -0
  155. data/lib/rubocop/rspec/cop_helper.rb +2 -2
  156. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  157. data/lib/rubocop/rspec/shared_contexts.rb +1 -2
  158. data/lib/rubocop/server/cache.rb +13 -10
  159. data/lib/rubocop/target_finder.rb +6 -2
  160. data/lib/rubocop/version.rb +1 -1
  161. data/lib/rubocop.rb +8 -1
  162. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  163. metadata +14 -7
@@ -6,6 +6,8 @@ module RuboCop
6
6
  # Checks that brackets used for array literals have or don't have
7
7
  # surrounding space depending on configuration.
8
8
  #
9
+ # Array pattern matching is handled in the same way.
10
+ #
9
11
  # @example EnforcedStyle: no_space (default)
10
12
  # # The `no_space` style enforces that array literals have
11
13
  # # no surrounding space.
@@ -82,9 +84,11 @@ module RuboCop
82
84
  EMPTY_MSG = '%<command>s space inside empty array brackets.'
83
85
 
84
86
  def on_array(node)
85
- return unless node.square_brackets?
87
+ return if node.array_type? && !node.square_brackets?
86
88
 
89
+ node = find_node_with_brackets(node)
87
90
  tokens, left, right = array_brackets(node)
91
+ return unless left && right
88
92
 
89
93
  if empty_brackets?(left, right, tokens: tokens)
90
94
  return empty_offenses(node, left, right, EMPTY_MSG)
@@ -95,9 +99,14 @@ module RuboCop
95
99
 
96
100
  issue_offenses(node, left, right, start_ok, end_ok)
97
101
  end
102
+ alias on_array_pattern on_array
98
103
 
99
104
  private
100
105
 
106
+ def find_node_with_brackets(node)
107
+ node.ancestors.find(&:const_pattern_type?) || node
108
+ end
109
+
101
110
  def autocorrect(corrector, node)
102
111
  tokens, left, right = array_brackets(node)
103
112
 
@@ -115,7 +124,7 @@ module RuboCop
115
124
  def array_brackets(node)
116
125
  tokens = processed_source.tokens_within(node)
117
126
 
118
- left = tokens.find(&:left_array_bracket?)
127
+ left = tokens.find(&:left_bracket?)
119
128
  right = tokens.reverse_each.find(&:right_bracket?)
120
129
 
121
130
  [tokens, left, right]
@@ -188,7 +197,7 @@ module RuboCop
188
197
  if side == :right
189
198
  processed_source.tokens_within(node)[i].right_bracket?
190
199
  else
191
- processed_source.tokens_within(node)[i].left_array_bracket?
200
+ processed_source.tokens_within(node)[i].left_bracket?
192
201
  end
193
202
  end
194
203
 
@@ -6,6 +6,8 @@ module RuboCop
6
6
  # Checks that braces used for hash literals have or don't have
7
7
  # surrounding space depending on configuration.
8
8
  #
9
+ # Hash pattern matching is handled in the same way.
10
+ #
9
11
  # @example EnforcedStyle: space (default)
10
12
  # # The `space` style enforces that hash literals have
11
13
  # # surrounding space.
@@ -87,6 +89,7 @@ module RuboCop
87
89
  check(tokens[-2], tokens[-1]) if tokens.size > 2
88
90
  check_whitespace_only_hash(node) if enforce_no_space_style_for_empty_braces?
89
91
  end
92
+ alias on_hash_pattern on_hash
90
93
 
91
94
  private
92
95
 
@@ -27,7 +27,9 @@ module RuboCop
27
27
  # @example
28
28
  # # bad
29
29
  # x || 1..2
30
+ # x - 1..2
30
31
  # (x || 1..2)
32
+ # x || 1..y || 2
31
33
  # 1..2.to_a
32
34
  #
33
35
  # # good, unambiguous
@@ -41,6 +43,7 @@ module RuboCop
41
43
  #
42
44
  # # good, ambiguity removed
43
45
  # x || (1..2)
46
+ # (x - 1)..2
44
47
  # (x || 1)..2
45
48
  # (x || 1)..(y || 2)
46
49
  # (1..2).to_a
@@ -96,6 +99,8 @@ module RuboCop
96
99
  # to avoid the ambiguity of `1..2.to_a`.
97
100
  return false if node.receiver&.basic_literal?
98
101
 
102
+ return false if node.operator_method? && !node.method?(:[])
103
+
99
104
  require_parentheses_for_method_chain? || node.receiver.nil?
100
105
  end
101
106
 
@@ -51,10 +51,9 @@ module RuboCop
51
51
  'in a regexp.'
52
52
 
53
53
  def on_interpolation(begin_node)
54
- final_node = begin_node.children.last
55
-
56
- return unless begin_node.parent.regexp_type?
54
+ return unless (final_node = begin_node.children.last)
57
55
  return unless final_node.array_type?
56
+ return unless begin_node.parent.regexp_type?
58
57
 
59
58
  if array_of_literal_values?(final_node)
60
59
  register_array_of_literal_values(begin_node, final_node)
@@ -48,7 +48,7 @@ module RuboCop
48
48
  def autocorrect(corrector, node)
49
49
  boolean_literal = node.source.delete(':')
50
50
  parent = node.parent
51
- if parent&.pair_type? && node.equal?(parent.children[0])
51
+ if parent&.pair_type? && parent.colon? && node.equal?(parent.children[0])
52
52
  corrector.remove(parent.loc.operator)
53
53
  boolean_literal = "#{node.source} =>"
54
54
  end
@@ -6,9 +6,8 @@ module RuboCop
6
6
  # Checks for circular argument references in optional keyword
7
7
  # arguments and optional ordinal arguments.
8
8
  #
9
- # This cop mirrors a warning produced by MRI since 2.2.
10
- #
11
- # NOTE: This syntax is no longer valid on Ruby 2.7 or higher.
9
+ # NOTE: This syntax was made invalid on Ruby 2.7 - Ruby 3.3 but is allowed
10
+ # again since Ruby 3.4.
12
11
  #
13
12
  # @example
14
13
  #
@@ -41,8 +40,6 @@ module RuboCop
41
40
 
42
41
  MSG = 'Circular argument reference - `%<arg_name>s`.'
43
42
 
44
- maximum_target_ruby_version 2.6
45
-
46
43
  def on_kwoptarg(node)
47
44
  check_for_circular_argument_references(*node)
48
45
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  dup: 'to_h',
44
44
  exists?: 'exist?',
45
45
  gethostbyaddr: 'Addrinfo#getnameinfo',
46
- gethostbyname: 'Addrinfo#getaddrinfo',
46
+ gethostbyname: 'Addrinfo.getaddrinfo',
47
47
  iterator?: 'block_given?'
48
48
  }.freeze
49
49
 
@@ -134,7 +134,7 @@ module RuboCop
134
134
  if NO_ARG_ALGORITHM.include?(algorithm_parts.first.upcase) && no_arguments
135
135
  "'#{algorithm_parts.first}'"
136
136
  else
137
- mode = 'cbc' unless size_and_mode == ['cbc']
137
+ mode = 'cbc' if size_and_mode.empty?
138
138
 
139
139
  "'#{(algorithm_parts + size_and_mode + [mode]).compact.take(3).join('-')}'"
140
140
  end
@@ -39,10 +39,52 @@ module RuboCop
39
39
  # end
40
40
  #
41
41
  # alias bar foo
42
+ #
43
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
44
+ #
45
+ # # good
46
+ # def foo
47
+ # 1
48
+ # end
49
+ #
50
+ # delegate :foo, to: :bar
51
+ #
52
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
53
+ #
54
+ # # bad
55
+ # def foo
56
+ # 1
57
+ # end
58
+ #
59
+ # delegate :foo, to: :bar
60
+ #
61
+ # # good
62
+ # def foo
63
+ # 1
64
+ # end
65
+ #
66
+ # delegate :baz, to: :bar
67
+ #
68
+ # # good - delegate with splat arguments is ignored
69
+ # def foo
70
+ # 1
71
+ # end
72
+ #
73
+ # delegate :foo, **options
74
+ #
75
+ # # good - delegate inside a condition is ignored
76
+ # def foo
77
+ # 1
78
+ # end
79
+ #
80
+ # if cond
81
+ # delegate :foo, to: :bar
82
+ # end
83
+ #
42
84
  class DuplicateMethods < Base
43
85
  MSG = 'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
44
- RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr].freeze
45
- DEF_TYPES = %i[def defs].freeze
86
+ RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr
87
+ delegate].freeze
46
88
 
47
89
  def initialize(config = nil, options = nil)
48
90
  super
@@ -86,15 +128,28 @@ module RuboCop
86
128
  (send nil? :alias_method (sym $_name) _)
87
129
  PATTERN
88
130
 
131
+ # @!method delegate_method?(node)
132
+ def_node_matcher :delegate_method?, <<~PATTERN
133
+ (send nil? :delegate
134
+ ({sym str} $_)+
135
+ (hash <(pair (sym :to) {sym str}) ...>)
136
+ )
137
+ PATTERN
138
+
89
139
  # @!method sym_name(node)
90
140
  def_node_matcher :sym_name, '(sym $_name)'
91
- def on_send(node)
141
+
142
+ def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
143
  if (name = alias_method?(node))
93
144
  return if node.ancestors.any?(&:if_type?)
94
145
 
95
146
  found_instance_method(node, name)
96
147
  elsif (attr = node.attribute_accessor?)
97
148
  on_attr(node, *attr)
149
+ elsif active_support_extensions_enabled? && (names = delegate_method?(node))
150
+ return if node.ancestors.any?(&:if_type?)
151
+
152
+ on_delegate(node, names)
98
153
  end
99
154
  end
100
155
 
@@ -119,6 +174,32 @@ module RuboCop
119
174
  current: source_location(node))
120
175
  end
121
176
 
177
+ def on_delegate(node, method_names)
178
+ name_prefix = delegate_prefix(node)
179
+
180
+ method_names.each do |name|
181
+ name = "#{name_prefix}_#{name}" if name_prefix
182
+
183
+ found_instance_method(node, name)
184
+ end
185
+ end
186
+
187
+ def delegate_prefix(node)
188
+ kwargs_node = node.last_argument
189
+
190
+ return unless (prefix = hash_value(kwargs_node, :prefix))
191
+
192
+ if prefix.true_type?
193
+ hash_value(kwargs_node, :to).value
194
+ elsif prefix.type?(:sym, :str)
195
+ prefix.value
196
+ end
197
+ end
198
+
199
+ def hash_value(node, key)
200
+ node.pairs.find { |pair| pair.key.value == key }&.value
201
+ end
202
+
122
203
  def found_instance_method(node, name)
123
204
  return found_sclass_method(node, name) unless (scope = node.parent_module_name)
124
205
 
@@ -162,7 +243,7 @@ module RuboCop
162
243
  end
163
244
 
164
245
  def method_key(node, method_name)
165
- if (ancestor_def = node.each_ancestor(*DEF_TYPES).first)
246
+ if (ancestor_def = node.each_ancestor(:any_def).first)
166
247
  "#{ancestor_def.method_name}.#{method_name}"
167
248
  else
168
249
  method_name
@@ -170,7 +251,7 @@ module RuboCop
170
251
  end
171
252
 
172
253
  def location(node)
173
- if DEF_TYPES.include?(node.type)
254
+ if node.any_def_type?
174
255
  node.loc.keyword.join(node.loc.name)
175
256
  else
176
257
  node.source_range
@@ -19,7 +19,9 @@ module RuboCop
19
19
  MSG = 'Empty interpolation detected.'
20
20
 
21
21
  def on_interpolation(begin_node)
22
- return unless begin_node.children.empty?
22
+ node_children = begin_node.children.dup
23
+ node_children.delete_if { |e| e.nil_type? || (e.basic_literal? && e.str_content&.empty?) }
24
+ return unless node_children.empty?
23
25
 
24
26
  add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
25
27
  end
@@ -15,6 +15,14 @@ module RuboCop
15
15
  # x == 0.1
16
16
  # x != 0.1
17
17
  #
18
+ # # bad
19
+ # case value
20
+ # when 1.0
21
+ # foo
22
+ # when 2.0
23
+ # bar
24
+ # end
25
+ #
18
26
  # # good - using BigDecimal
19
27
  # x.to_d == 0.1.to_d
20
28
  #
@@ -32,12 +40,21 @@ module RuboCop
32
40
  # # good - comparing against nil
33
41
  # Float(x, exception: false) == nil
34
42
  #
43
+ # # good - using epsilon comparison in case expression
44
+ # case
45
+ # when (value - 1.0).abs < Float::EPSILON
46
+ # foo
47
+ # when (value - 2.0).abs < Float::EPSILON
48
+ # bar
49
+ # end
50
+ #
35
51
  # # Or some other epsilon based type of comparison:
36
52
  # # https://www.embeddeduse.com/2019/08/26/qt-compare-two-floats/
37
53
  #
38
54
  class FloatComparison < Base
39
55
  MSG_EQUALITY = 'Avoid equality comparisons of floats as they are unreliable.'
40
56
  MSG_INEQUALITY = 'Avoid inequality comparisons of floats as they are unreliable.'
57
+ MSG_CASE = 'Avoid float literal comparisons in case statements as they are unreliable.'
41
58
 
42
59
  EQUALITY_METHODS = %i[== != eql? equal?].freeze
43
60
  FLOAT_RETURNING_METHODS = %i[to_f Float fdiv].freeze
@@ -58,6 +75,16 @@ module RuboCop
58
75
  end
59
76
  alias on_csend on_send
60
77
 
78
+ def on_case(node)
79
+ node.when_branches.each do |when_branch|
80
+ when_branch.each_condition do |condition|
81
+ next if !float?(condition) || literal_safe?(condition)
82
+
83
+ add_offense(condition, message: MSG_CASE)
84
+ end
85
+ end
86
+ end
87
+
61
88
  private
62
89
 
63
90
  def float?(node)
@@ -67,7 +94,7 @@ module RuboCop
67
94
  when :float
68
95
  true
69
96
  when :send
70
- check_send(node)
97
+ float_send?(node)
71
98
  when :begin
72
99
  float?(node.children.first)
73
100
  else
@@ -81,18 +108,18 @@ module RuboCop
81
108
  (node.numeric_type? && node.value.zero?) || node.nil_type?
82
109
  end
83
110
 
84
- def check_send(node)
111
+ def float_send?(node)
85
112
  if node.arithmetic_operation?
86
113
  float?(node.receiver) || float?(node.first_argument)
87
114
  elsif FLOAT_RETURNING_METHODS.include?(node.method_name)
88
115
  true
89
116
  elsif node.receiver&.float_type?
90
117
  FLOAT_INSTANCE_METHODS.include?(node.method_name) ||
91
- check_numeric_returning_method(node)
118
+ numeric_returning_method?(node)
92
119
  end
93
120
  end
94
121
 
95
- def check_numeric_returning_method(node)
122
+ def numeric_returning_method?(node)
96
123
  return false unless node.receiver
97
124
 
98
125
  case node.method_name
@@ -11,39 +11,43 @@ module RuboCop
11
11
  # @example
12
12
  # # bad
13
13
  # foo.object_id == bar.object_id
14
+ # foo.object_id != baz.object_id
14
15
  #
15
16
  # # good
16
17
  # foo.equal?(bar)
18
+ # !foo.equal?(baz)
17
19
  #
18
20
  class IdentityComparison < Base
19
21
  extend AutoCorrector
20
22
 
21
- MSG = 'Use `equal?` instead `==` when comparing `object_id`.'
22
- RESTRICT_ON_SEND = %i[==].freeze
23
+ MSG = 'Use `%<bang>sequal?` instead of `%<comparison_method>s` when comparing `object_id`.'
24
+ RESTRICT_ON_SEND = %i[== !=].freeze
25
+
26
+ # @!method object_id_comparison(node)
27
+ def_node_matcher :object_id_comparison, <<~PATTERN
28
+ (send
29
+ (send
30
+ _lhs_receiver :object_id) ${:== :!=}
31
+ (send
32
+ _rhs_receiver :object_id))
33
+ PATTERN
23
34
 
24
35
  def on_send(node)
25
- return unless compare_between_object_id_by_double_equal?(node)
36
+ return unless (comparison_method = object_id_comparison(node))
26
37
 
27
- add_offense(node) do |corrector|
38
+ bang = comparison_method == :== ? '' : '!'
39
+ add_offense(node,
40
+ message: format(MSG, comparison_method: comparison_method,
41
+ bang: bang)) do |corrector|
28
42
  receiver = node.receiver.receiver
29
43
  argument = node.first_argument.receiver
30
44
  return unless receiver && argument
31
45
 
32
- replacement = "#{receiver.source}.equal?(#{argument.source})"
46
+ replacement = "#{bang}#{receiver.source}.equal?(#{argument.source})"
33
47
 
34
48
  corrector.replace(node, replacement)
35
49
  end
36
50
  end
37
-
38
- private
39
-
40
- def compare_between_object_id_by_double_equal?(node)
41
- object_id_method?(node.receiver) && object_id_method?(node.first_argument)
42
- end
43
-
44
- def object_id_method?(node)
45
- node.send_type? && node.method?(:object_id)
46
- end
47
51
  end
48
52
  end
49
53
  end
@@ -57,13 +57,9 @@ module RuboCop
57
57
  def on_if(node)
58
58
  cond = condition(node)
59
59
 
60
- if node.unless?
61
- correct_if_node(node, cond, true) if cond.falsey_literal?
62
- correct_if_node(node, cond, false) if cond.truthy_literal?
63
- else
64
- correct_if_node(node, cond, true) if cond.truthy_literal?
65
- correct_if_node(node, cond, false) if cond.falsey_literal?
66
- end
60
+ return unless cond.falsey_literal? || cond.truthy_literal?
61
+
62
+ correct_if_node(node, cond)
67
63
  end
68
64
 
69
65
  def on_while(node)
@@ -232,27 +228,37 @@ module RuboCop
232
228
  )
233
229
  end
234
230
 
235
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
236
- def correct_if_node(node, cond, result)
237
- if result
238
- add_offense(cond) do |corrector|
239
- corrector.replace(node, node.if_branch.source)
240
- end
241
- elsif node.elsif_conditional?
242
- add_offense(cond) do |corrector|
243
- corrector.replace(node, "#{node.else_branch.source.sub('elsif', 'if')}\nend")
244
- end
245
- elsif node.else? || node.ternary?
246
- add_offense(cond) do |corrector|
247
- corrector.replace(node, node.else_branch.source)
248
- end
231
+ def condition_evaluation?(node, cond)
232
+ if node.unless?
233
+ cond.falsey_literal?
249
234
  else
250
- add_offense(cond) do |corrector|
251
- corrector.remove(node)
252
- end
235
+ cond.truthy_literal?
236
+ end
237
+ end
238
+
239
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
240
+ def correct_if_node(node, cond)
241
+ result = condition_evaluation?(node, cond)
242
+
243
+ new_node = if node.elsif? && result
244
+ "else\n #{range_with_comments(node.if_branch).source}"
245
+ elsif node.elsif? && !result
246
+ "else\n #{node.else_branch.source}"
247
+ elsif node.if_branch && result
248
+ node.if_branch.source
249
+ elsif node.elsif_conditional?
250
+ "#{node.else_branch.source.sub('elsif', 'if')}\nend"
251
+ elsif node.else? || node.ternary?
252
+ node.else_branch.source
253
+ else
254
+ '' # Equivalent to removing the node
255
+ end
256
+
257
+ add_offense(cond) do |corrector|
258
+ corrector.replace(node, new_node)
253
259
  end
254
260
  end
255
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
261
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
256
262
  end
257
263
  end
258
264
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  each_missing_enable do |cop, line_range|
53
53
  next if acceptable_range?(cop, line_range)
54
54
 
55
- range = source_range(processed_source.buffer, line_range.min, (0..0))
55
+ range = source_range(processed_source.buffer, line_range.min, 0..0)
56
56
  comment = processed_source.comment_at_line(line_range.begin)
57
57
 
58
58
  add_offense(range, message: message(cop, comment))
@@ -98,7 +98,7 @@ module RuboCop
98
98
  subject, = *node # rubocop:disable InternalAffairs/NodeDestructuring
99
99
  return if node.defs_type? && allowed_subject_type?(subject)
100
100
 
101
- def_ancestor = node.each_ancestor(:def, :defs).first
101
+ def_ancestor = node.each_ancestor(:any_def).first
102
102
  return unless def_ancestor
103
103
 
104
104
  within_scoping_def =
@@ -46,7 +46,7 @@ module RuboCop
46
46
  def on_return(return_node)
47
47
  return if return_value?(return_node)
48
48
 
49
- return_node.each_ancestor(:any_block, :def, :defs) do |node|
49
+ return_node.each_ancestor(:any_block, :any_def) do |node|
50
50
  break if scoped_node?(node)
51
51
 
52
52
  # if a proc is passed to `Module#define_method` or
@@ -66,7 +66,7 @@ module RuboCop
66
66
  private
67
67
 
68
68
  def scoped_node?(node)
69
- node.type?(:def, :defs) || node.lambda?
69
+ node.any_def_type? || node.lambda?
70
70
  end
71
71
 
72
72
  def return_value?(return_node)
@@ -30,7 +30,7 @@ module RuboCop
30
30
  return unless node.lhs&.casgn_type?
31
31
 
32
32
  add_offense(node.loc.operator) do |corrector|
33
- next if node.each_ancestor(:def, :defs).any?
33
+ next if node.each_ancestor(:any_def).any?
34
34
 
35
35
  corrector.replace(node.loc.operator, '=')
36
36
  end
@@ -73,7 +73,7 @@ module RuboCop
73
73
  end
74
74
 
75
75
  def redundant_group?(expr)
76
- expr.is?(:passive, :group) && expr.count { |child| child.type != :free_space } == 1
76
+ expr.is?(:passive, :group) && expr.one? { |child| child.type != :free_space }
77
77
  end
78
78
 
79
79
  def redundantly_quantifiable?(node)
@@ -27,7 +27,8 @@ module RuboCop
27
27
  # In all cases, chaining one same `to_*` conversion methods listed above is redundant.
28
28
  #
29
29
  # The cop can also register an offense for chaining conversion methods on methods that are
30
- # expected to return a specific type regardless of receiver (eg. `foo.inspect.to_s`).
30
+ # expected to return a specific type regardless of receiver (eg. `foo.inspect.to_s` and
31
+ # `foo.to_json.to_s`).
31
32
  #
32
33
  # @example
33
34
  # # bad
@@ -69,10 +70,12 @@ module RuboCop
69
70
  # foo.to_s
70
71
  #
71
72
  # # bad - chaining a conversion to a method that is expected to return the same type
72
- # inspect.to_s
73
+ # foo.inspect.to_s
74
+ # foo.to_json.to_s
73
75
  #
74
76
  # # good
75
- # inspect
77
+ # foo.inspect
78
+ # foo.to_json
76
79
  #
77
80
  class RedundantTypeConversion < Base
78
81
  extend AutoCorrector
@@ -108,7 +111,7 @@ module RuboCop
108
111
 
109
112
  # Methods that already are expected to return a given type, which makes a further
110
113
  # conversion redundant.
111
- TYPED_METHODS = { to_s: %i[inspect] }.freeze
114
+ TYPED_METHODS = { to_s: %i[inspect to_json] }.freeze
112
115
 
113
116
  CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze
114
117
  RESTRICT_ON_SEND = CONVERSION_METHODS + [:to_d]
@@ -32,12 +32,17 @@ module RuboCop
32
32
  class ReturnInVoidContext < Base
33
33
  MSG = 'Do not return a value in `%<method>s`.'
34
34
 
35
+ # Returning out of these methods only exits the block itself.
36
+ SCOPE_CHANGING_METHODS = %i[lambda define_method define_singleton_method].freeze
37
+
35
38
  def on_return(return_node)
36
39
  return unless return_node.descendants.any?
37
40
 
38
- def_node = return_node.each_ancestor(:def).first
41
+ def_node = return_node.each_ancestor(:any_def).first
39
42
  return unless def_node&.void_context?
40
- return if return_node.each_ancestor(:any_block).any?(&:lambda?)
43
+ return if return_node.each_ancestor(:any_block).any? do |block_node|
44
+ SCOPE_CHANGING_METHODS.include?(block_node.method_name)
45
+ end
41
46
 
42
47
  add_offense(
43
48
  return_node.loc.keyword,
@@ -97,7 +97,7 @@ module RuboCop
97
97
  end
98
98
 
99
99
  def require_parentheses?(send_node)
100
- return true if operator_inside_hash?(send_node)
100
+ return true if operator_inside_collection_literal?(send_node)
101
101
  return false unless send_node.comparison_method?
102
102
  return false unless (node = send_node.parent)
103
103
 
@@ -105,10 +105,10 @@ module RuboCop
105
105
  (node.respond_to?(:comparison_method?) && node.comparison_method?)
106
106
  end
107
107
 
108
- def operator_inside_hash?(send_node)
109
- # If an operator call (without a dot) is inside a hash, it needs
108
+ def operator_inside_collection_literal?(send_node)
109
+ # If an operator call (without a dot) is inside an array or a hash, it needs
110
110
  # to be parenthesized when converted to safe navigation.
111
- send_node.parent&.pair_type? && !send_node.loc.dot
111
+ send_node.parent&.type?(:array, :pair) && !send_node.loc.dot
112
112
  end
113
113
  end
114
114
  end