rubocop 1.75.8 → 1.81.7

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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +121 -28
  4. data/config/obsoletion.yml +6 -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 +18 -3
  8. data/lib/rubocop/config_loader.rb +4 -39
  9. data/lib/rubocop/config_loader_resolver.rb +5 -4
  10. data/lib/rubocop/config_store.rb +5 -0
  11. data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
  12. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  13. data/lib/rubocop/cop/correctors/alignment_corrector.rb +7 -4
  14. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  15. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  16. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  18. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  19. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  20. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  21. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  22. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +4 -1
  23. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  24. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  26. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  27. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  28. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  29. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  30. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  31. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  32. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  33. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  34. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
  35. data/lib/rubocop/cop/layout/line_length.rb +35 -6
  36. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  37. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
  38. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  39. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  40. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  41. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  42. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  43. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  44. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  45. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +13 -7
  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_methods.rb +25 -4
  49. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  50. data/lib/rubocop/cop/lint/empty_interpolation.rb +14 -1
  51. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  52. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  53. data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
  54. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +17 -8
  55. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  56. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  57. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  58. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  59. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  60. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  61. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  62. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  63. data/lib/rubocop/cop/lint/self_assignment.rb +31 -5
  64. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  65. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  66. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  67. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  68. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  69. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  70. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  71. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  72. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  73. data/lib/rubocop/cop/lint/void.rb +7 -0
  74. data/lib/rubocop/cop/message_annotator.rb +1 -1
  75. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  76. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  77. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  78. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  79. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  80. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  81. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  82. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  83. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  84. data/lib/rubocop/cop/naming/method_name.rb +129 -13
  85. data/lib/rubocop/cop/naming/predicate_method.rb +319 -0
  86. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  87. data/lib/rubocop/cop/security/eval.rb +2 -1
  88. data/lib/rubocop/cop/security/json_load.rb +33 -11
  89. data/lib/rubocop/cop/security/open.rb +1 -0
  90. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  91. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  92. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  93. data/lib/rubocop/cop/style/array_intersect.rb +99 -35
  94. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  95. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  96. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  97. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  98. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  99. data/lib/rubocop/cop/style/conditional_assignment.rb +11 -5
  100. data/lib/rubocop/cop/style/constant_visibility.rb +14 -9
  101. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  102. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  103. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  104. data/lib/rubocop/cop/style/endless_method.rb +15 -2
  105. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  106. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  107. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  108. data/lib/rubocop/cop/style/float_division.rb +15 -1
  109. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  110. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  111. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  112. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  113. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  114. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  115. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  116. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  117. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  118. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  119. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  120. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  121. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  122. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
  123. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  124. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  125. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  126. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  127. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  128. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  129. data/lib/rubocop/cop/style/redundant_format.rb +26 -5
  130. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  131. data/lib/rubocop/cop/style/redundant_interpolation.rb +12 -3
  132. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  133. data/lib/rubocop/cop/style/redundant_parentheses.rb +55 -16
  134. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  135. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  136. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  137. data/lib/rubocop/cop/style/safe_navigation.rb +44 -12
  138. data/lib/rubocop/cop/style/semicolon.rb +23 -7
  139. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  140. data/lib/rubocop/cop/style/sole_nested_conditional.rb +40 -3
  141. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  142. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  143. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  144. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  145. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  146. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  147. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  148. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  149. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  150. data/lib/rubocop/cop/variable_force.rb +25 -8
  151. data/lib/rubocop/cops_documentation_generator.rb +5 -4
  152. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  153. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  154. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  155. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  156. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  157. data/lib/rubocop/lsp/diagnostic.rb +25 -24
  158. data/lib/rubocop/lsp/routes.rb +65 -9
  159. data/lib/rubocop/lsp/runtime.rb +2 -2
  160. data/lib/rubocop/lsp/server.rb +2 -2
  161. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  162. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  163. data/lib/rubocop/result_cache.rb +14 -12
  164. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  165. data/lib/rubocop/runner.rb +6 -4
  166. data/lib/rubocop/server/cache.rb +4 -2
  167. data/lib/rubocop/server/client_command/base.rb +10 -0
  168. data/lib/rubocop/server/client_command/exec.rb +2 -1
  169. data/lib/rubocop/server/client_command/start.rb +11 -1
  170. data/lib/rubocop/target_finder.rb +9 -9
  171. data/lib/rubocop/target_ruby.rb +10 -1
  172. data/lib/rubocop/version.rb +1 -1
  173. data/lib/rubocop.rb +12 -1
  174. data/lib/ruby_lsp/rubocop/addon.rb +25 -10
  175. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  176. metadata +18 -7
@@ -23,6 +23,14 @@ module RuboCop
23
23
  # # good (method calls possibly can return different results)
24
24
  # hash[foo] = hash[foo]
25
25
  #
26
+ # @example AllowRBSInlineAnnotation:true
27
+ # # good
28
+ # foo = foo #: Integer
29
+ # foo, bar = foo, bar #: Integer
30
+ # Foo = Foo #: Integer
31
+ # hash['foo'] = hash['foo'] #: Integer
32
+ # obj.attr = obj.attr #: Integer
33
+ #
26
34
  class SelfAssignment < Base
27
35
  MSG = 'Self-assignment detected.'
28
36
 
@@ -34,8 +42,10 @@ module RuboCop
34
42
  }.freeze
35
43
 
36
44
  def on_send(node)
45
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
46
+
37
47
  if node.method?(:[]=)
38
- handle_key_assignment(node) if node.arguments.size == 2
48
+ handle_key_assignment(node)
39
49
  elsif node.assignment_method?
40
50
  handle_attribute_assignment(node) if node.arguments.size == 1
41
51
  end
@@ -44,6 +54,7 @@ module RuboCop
44
54
 
45
55
  def on_lvasgn(node)
46
56
  return unless node.rhs
57
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
47
58
 
48
59
  rhs_type = ASSIGNMENT_TYPE_TO_RHS_TYPE[node.type]
49
60
 
@@ -55,16 +66,22 @@ module RuboCop
55
66
 
56
67
  def on_casgn(node)
57
68
  return unless node.rhs&.const_type?
69
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
58
70
 
59
71
  add_offense(node) if node.namespace == node.rhs.namespace &&
60
72
  node.short_name == node.rhs.short_name
61
73
  end
62
74
 
63
75
  def on_masgn(node)
76
+ first_lhs = node.lhs.assignments.first
77
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(first_lhs)
78
+
64
79
  add_offense(node) if multiple_self_assignment?(node)
65
80
  end
66
81
 
67
82
  def on_or_asgn(node)
83
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.lhs)
84
+
68
85
  add_offense(node) if rhs_matches_lhs?(node.rhs, node.lhs)
69
86
  end
70
87
  alias on_and_asgn on_or_asgn
@@ -88,12 +105,13 @@ module RuboCop
88
105
  end
89
106
 
90
107
  def handle_key_assignment(node)
91
- value_node = node.arguments[1]
108
+ value_node = node.last_argument
109
+ node_arguments = node.arguments[0...-1]
92
110
 
93
- if value_node.send_type? && value_node.method?(:[]) &&
111
+ if value_node.respond_to?(:method?) && value_node.method?(:[]) &&
94
112
  node.receiver == value_node.receiver &&
95
- !node.first_argument.call_type? &&
96
- node.first_argument == value_node.first_argument
113
+ node_arguments.none?(&:call_type?) &&
114
+ node_arguments == value_node.arguments
97
115
  add_offense(node)
98
116
  end
99
117
  end
@@ -108,6 +126,14 @@ module RuboCop
108
126
  add_offense(node)
109
127
  end
110
128
  end
129
+
130
+ def rbs_inline_annotation?(node)
131
+ processed_source.ast_with_comments[node].any? { |comment| comment.text.start_with?('#:') }
132
+ end
133
+
134
+ def allow_rbs_inline_annotation?
135
+ cop_config['AllowRBSInlineAnnotation']
136
+ end
111
137
  end
112
138
  end
113
139
  end
@@ -125,13 +125,13 @@ module RuboCop
125
125
  next false if assignment_node.shorthand_asgn?
126
126
  next false unless assignment_node.parent
127
127
 
128
- node_within_block_or_conditional =
129
- node_within_block_or_conditional?(assignment_node.parent, argument.scope.node)
128
+ conditional_assignment =
129
+ conditional_assignment?(assignment_node.parent, argument.scope.node)
130
130
 
131
131
  unless uses_var?(assignment_node, argument.name)
132
132
  # It's impossible to decide whether a branch or block is executed,
133
133
  # so the precise reassignment location is undecidable.
134
- next false if node_within_block_or_conditional
134
+ next false if conditional_assignment
135
135
 
136
136
  yield(assignment.node, location_known)
137
137
  break
@@ -147,13 +147,13 @@ module RuboCop
147
147
  node.source_range.begin_pos
148
148
  end
149
149
 
150
- # Check whether the given node is nested into block or conditional.
150
+ # Check whether the given node is always executed or not
151
151
  #
152
- def node_within_block_or_conditional?(node, stop_search_node)
152
+ def conditional_assignment?(node, stop_search_node)
153
153
  return false if node == stop_search_node
154
154
 
155
- node.conditional? || node.block_type? ||
156
- node_within_block_or_conditional?(node.parent, stop_search_node)
155
+ node.conditional? || node.type?(:block, :rescue) ||
156
+ conditional_assignment?(node.parent, stop_search_node)
157
157
  end
158
158
 
159
159
  # Get argument references without assignments' references
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # given by `ruby -cw` prior to Ruby 2.6:
9
9
  # "shadowing outer local variable - foo".
10
10
  #
11
+ # The cop is now disabled by default to match the upstream Ruby behavior.
12
+ # It's useful, however, if you'd like to avoid shadowing variables from outer
13
+ # scopes, which some people consider an anti-pattern that makes it harder
14
+ # to keep track of what's going on in a program.
15
+ #
11
16
  # NOTE: Shadowing of variables in block passed to `Ractor.new` is allowed
12
17
  # because `Ractor` should not access outer variables.
13
18
  # eg. following style is encouraged:
@@ -17,6 +17,7 @@ module RuboCop
17
17
  #
18
18
  # # good
19
19
  # CGI.escape('http://example.com')
20
+ # URI.encode_uri_component(uri) # Since Ruby 3.1
20
21
  # URI.encode_www_form([['example', 'param'], ['lang', 'en']])
21
22
  # URI.encode_www_form(page: 10, locale: 'en')
22
23
  # URI.encode_www_form_component('http://example.com')
@@ -27,6 +28,7 @@ module RuboCop
27
28
  #
28
29
  # # good
29
30
  # CGI.unescape(enc_uri)
31
+ # URI.decode_uri_component(uri) # Since Ruby 3.1
30
32
  # URI.decode_www_form(enc_uri)
31
33
  # URI.decode_www_form_component(enc_uri)
32
34
  class UriEscapeUnescape < Base
@@ -4,10 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for redundant access modifiers, including those with no
7
- # code, those which are repeated, and leading `public` modifiers in a
8
- # class or module body. Conditionally-defined methods are considered as
9
- # always being defined, and thus access modifiers guarding such methods
10
- # are not redundant.
7
+ # code, those which are repeated, those which are on top-level, and
8
+ # leading `public` modifiers in a class or module body.
9
+ # Conditionally-defined methods are considered as always being defined,
10
+ # and thus access modifiers guarding such methods are not redundant.
11
11
  #
12
12
  # This cop has `ContextCreatingMethods` option. The default setting value
13
13
  # is an empty array that means no method is specified.
@@ -58,6 +58,12 @@ module RuboCop
58
58
  # private # this is redundant (no following methods are defined)
59
59
  # end
60
60
  #
61
+ # # bad
62
+ # private # this is useless (access modifiers have no effect on top-level)
63
+ #
64
+ # def method
65
+ # end
66
+ #
61
67
  # # good
62
68
  # class Foo
63
69
  # private # this is not redundant (a method is defined)
@@ -145,6 +151,17 @@ module RuboCop
145
151
  alias on_numblock on_block
146
152
  alias on_itblock on_block
147
153
 
154
+ def on_begin(node)
155
+ return if node.parent
156
+
157
+ node.child_nodes.each do |child|
158
+ next unless child.send_type? && access_modifier?(child)
159
+
160
+ # This call always registers an offense for access modifier `child.method_name`
161
+ check_send_node(child, child.method_name, true)
162
+ end
163
+ end
164
+
148
165
  private
149
166
 
150
167
  def autocorrect(corrector, node)
@@ -257,6 +274,10 @@ module RuboCop
257
274
 
258
275
  def any_method_definition?(child)
259
276
  cop_config.fetch('MethodCreatingMethods', []).any? do |m|
277
+ # Some users still have `"included"` in their `MethodCreatingMethods` configurations,
278
+ # so to prevent Ruby method redefinition warnings let's just skip this value.
279
+ next if m == 'included'
280
+
260
281
  matcher_name = :"#{m}_method?"
261
282
  unless respond_to?(matcher_name)
262
283
  self.class.def_node_matcher matcher_name, <<~PATTERN
@@ -279,7 +300,11 @@ module RuboCop
279
300
  end
280
301
 
281
302
  def any_context_creating_methods?(child)
303
+ # Some users still have `"included"` in their `ContextCreatingMethods` configurations,
304
+ # so to prevent Ruby method redefinition warnings let's just skip this value.
282
305
  cop_config.fetch('ContextCreatingMethods', []).any? do |m|
306
+ next if m == 'included'
307
+
283
308
  matcher_name = :"#{m}_block?"
284
309
  unless respond_to?(matcher_name)
285
310
  self.class.def_node_matcher matcher_name, <<~PATTERN
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for usage of method `fetch` or `Array.new` with default value argument
7
+ # and block. In such cases, block will always be used as default value.
8
+ #
9
+ # This cop emulates Ruby warning "block supersedes default value argument" which
10
+ # applies to `Array.new`, `Array#fetch`, `Hash#fetch`, `ENV.fetch` and
11
+ # `Thread#fetch`.
12
+ #
13
+ # A `fetch` call without a receiver is considered a custom method and does not register
14
+ # an offense.
15
+ #
16
+ # @safety
17
+ # This cop is unsafe because the receiver could have nonstandard implementation
18
+ # of `fetch`, or be a class other than the one listed above.
19
+ #
20
+ # It is also unsafe because default value argument could have side effects:
21
+ #
22
+ # [source,ruby]
23
+ # ----
24
+ # def x(a) = puts "side effect"
25
+ # Array.new(5, x(1)) { 2 }
26
+ # ----
27
+ #
28
+ # so removing it would change behavior.
29
+ #
30
+ # @example
31
+ # # bad
32
+ # x.fetch(key, default_value) { block_value }
33
+ # Array.new(size, default_value) { block_value }
34
+ #
35
+ # # good
36
+ # x.fetch(key) { block_value }
37
+ # Array.new(size) { block_value }
38
+ #
39
+ # # also good - in case default value argument is desired instead
40
+ # x.fetch(key, default_value)
41
+ # Array.new(size, default_value)
42
+ #
43
+ # # good - keyword arguments aren't registered as offenses
44
+ # x.fetch(key, keyword: :arg) { block_value }
45
+ #
46
+ # @example AllowedReceivers: ['Rails.cache']
47
+ # # good
48
+ # Rails.cache.fetch(name, options) { block }
49
+ #
50
+ class UselessDefaultValueArgument < Base
51
+ include AllowedReceivers
52
+ extend AutoCorrector
53
+
54
+ MSG = 'Block supersedes default value argument.'
55
+
56
+ RESTRICT_ON_SEND = %i[fetch new].freeze
57
+
58
+ # @!method default_value_argument_and_block(node)
59
+ def_node_matcher :default_value_argument_and_block, <<~PATTERN
60
+ (any_block
61
+ {
62
+ (call !nil? :fetch $_key $_default_value)
63
+ (send (const _ :Array) :new $_size $_default_value)
64
+ }
65
+ _args
66
+ _block_body)
67
+ PATTERN
68
+
69
+ def on_send(node)
70
+ unless (prev_arg_node, default_value_node = default_value_argument_and_block(node.parent))
71
+ return
72
+ end
73
+ return if allowed_receiver?(node.receiver)
74
+ return if hash_without_braces?(default_value_node)
75
+
76
+ add_offense(default_value_node) do |corrector|
77
+ corrector.remove(prev_arg_node.source_range.end.join(default_value_node.source_range))
78
+ end
79
+ end
80
+ alias on_csend on_send
81
+
82
+ private
83
+
84
+ def hash_without_braces?(node)
85
+ node.hash_type? && !node.braces?
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -31,6 +31,7 @@ module RuboCop
31
31
  #
32
32
  class UselessNumericOperation < Base
33
33
  extend AutoCorrector
34
+
34
35
  MSG = 'Do not apply inconsequential numeric operations to variables.'
35
36
  RESTRICT_ON_SEND = %i[+ - * / **].freeze
36
37
 
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for useless OR (`||` and `or`) expressions.
7
+ #
8
+ # Some methods always return a truthy value, even when called
9
+ # on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
10
+ # appended after these methods will never evaluate.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # x.to_a || fallback
16
+ # x.to_c || fallback
17
+ # x.to_d || fallback
18
+ # x.to_i || fallback
19
+ # x.to_f || fallback
20
+ # x.to_h || fallback
21
+ # x.to_r || fallback
22
+ # x.to_s || fallback
23
+ # x.to_sym || fallback
24
+ # x.intern || fallback
25
+ # x.inspect || fallback
26
+ # x.hash || fallback
27
+ # x.object_id || fallback
28
+ # x.__id__ || fallback
29
+ #
30
+ # x.to_s or fallback
31
+ #
32
+ # # good - if fallback is same as return value of method called on nil
33
+ # x.to_a # nil.to_a returns []
34
+ # x.to_c # nil.to_c returns (0+0i)
35
+ # x.to_d # nil.to_d returns 0.0
36
+ # x.to_i # nil.to_i returns 0
37
+ # x.to_f # nil.to_f returns 0.0
38
+ # x.to_h # nil.to_h returns {}
39
+ # x.to_r # nil.to_r returns (0/1)
40
+ # x.to_s # nil.to_s returns ''
41
+ # x.to_sym # nil.to_sym raises an error
42
+ # x.intern # nil.intern raises an error
43
+ # x.inspect # nil.inspect returns "nil"
44
+ # x.hash # nil.hash returns an Integer
45
+ # x.object_id # nil.object_id returns an Integer
46
+ # x.__id__ # nil.object_id returns an Integer
47
+ #
48
+ # # good - if the intention is not to call the method on nil
49
+ # x&.to_a || fallback
50
+ # x&.to_c || fallback
51
+ # x&.to_d || fallback
52
+ # x&.to_i || fallback
53
+ # x&.to_f || fallback
54
+ # x&.to_h || fallback
55
+ # x&.to_r || fallback
56
+ # x&.to_s || fallback
57
+ # x&.to_sym || fallback
58
+ # x&.intern || fallback
59
+ # x&.inspect || fallback
60
+ # x&.hash || fallback
61
+ # x&.object_id || fallback
62
+ # x&.__id__ || fallback
63
+ #
64
+ # x&.to_s or fallback
65
+ #
66
+ class UselessOr < Base
67
+ MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
68
+
69
+ TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
70
+ :to_s, :to_sym, :intern, :inspect, :hash, :object_id,
71
+ :__id__].freeze
72
+
73
+ # @!method truthy_return_value_method?(node)
74
+ def_node_matcher :truthy_return_value_method?, <<~PATTERN
75
+ (send _ %TRUTHY_RETURN_VALUE_METHODS)
76
+ PATTERN
77
+
78
+ def on_or(node)
79
+ if truthy_return_value_method?(node.lhs)
80
+ report_offense(node, node.lhs)
81
+ elsif truthy_return_value_method?(node.rhs)
82
+ parent = node.parent
83
+ parent = parent.parent if parent&.begin_type?
84
+
85
+ report_offense(parent, node.rhs) if parent&.or_type?
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def report_offense(or_node, truthy_node)
92
+ add_offense(or_node.loc.operator.join(or_node.rhs.source_range),
93
+ message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source))
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -89,7 +89,7 @@ module RuboCop
89
89
  private
90
90
 
91
91
  def inspect_def(node, def_node)
92
- return if allowed_arguments(def_node.arguments)
92
+ return if allowed_arguments?(def_node.arguments)
93
93
 
94
94
  add_offense(node.loc.selector, message: format(MSG, method_name: def_node.method_name))
95
95
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  definition = find_method_definition(node, method_name)
102
102
 
103
103
  return unless definition
104
- return if allowed_arguments(definition.arguments)
104
+ return if allowed_arguments?(definition.arguments)
105
105
 
106
106
  add_offense(node, message: format(MSG, method_name: method_name))
107
107
  end
@@ -115,7 +115,7 @@ module RuboCop
115
115
  end
116
116
 
117
117
  # `ruby2_keywords` is only allowed if there's a `restarg` and no keyword arguments
118
- def allowed_arguments(arguments)
118
+ def allowed_arguments?(arguments)
119
119
  return false if arguments.empty?
120
120
 
121
121
  arguments.each_child_node(:restarg).any? &&
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ module Utils
7
+ # Utility class that checks if the receiver can't be nil.
8
+ class NilReceiverChecker
9
+ NIL_METHODS = (nil.methods + %i[!]).to_set.freeze
10
+
11
+ def initialize(receiver, additional_nil_methods)
12
+ @receiver = receiver
13
+ @additional_nil_methods = additional_nil_methods
14
+ @checked_nodes = {}.compare_by_identity
15
+ end
16
+
17
+ def cant_be_nil?
18
+ sole_condition_of_parent_if?(@receiver) || _cant_be_nil?(@receiver.parent, @receiver)
19
+ end
20
+
21
+ private
22
+
23
+ # rubocop:disable Metrics
24
+ def _cant_be_nil?(node, receiver)
25
+ return false unless node
26
+
27
+ # For some nodes, we check their parent and then some children for these parents.
28
+ # This is added to avoid infinite loops.
29
+ return false if @checked_nodes.key?(node)
30
+
31
+ @checked_nodes[node] = true
32
+
33
+ case node.type
34
+ when :def, :class, :module, :sclass
35
+ return false
36
+ when :send
37
+ return non_nil_method?(node.method_name) if node.receiver == receiver
38
+
39
+ node.arguments.each do |argument|
40
+ return true if _cant_be_nil?(argument, receiver)
41
+ end
42
+
43
+ return true if _cant_be_nil?(node.receiver, receiver)
44
+ when :begin
45
+ return true if _cant_be_nil?(node.children.first, receiver)
46
+ when :if, :case
47
+ return true if _cant_be_nil?(node.condition, receiver)
48
+ when :and, :or
49
+ return true if _cant_be_nil?(node.lhs, receiver)
50
+ when :pair
51
+ if _cant_be_nil?(node.key, receiver) ||
52
+ _cant_be_nil?(node.value, receiver)
53
+ return true
54
+ end
55
+ when :when
56
+ node.each_condition do |condition|
57
+ return true if _cant_be_nil?(condition, receiver)
58
+ end
59
+ when :lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn
60
+ return true if _cant_be_nil?(node.expression, receiver)
61
+ end
62
+
63
+ # Due to how `if/else` are implemented (`elsif` is a child of `if` or another `elsif`),
64
+ # using left_siblings will not work correctly for them.
65
+ if !else_branch?(node) || (node.if_type? && !node.elsif?)
66
+ node.left_siblings.reverse_each do |sibling|
67
+ next unless sibling.is_a?(AST::Node)
68
+
69
+ return true if _cant_be_nil?(sibling, receiver)
70
+ end
71
+ end
72
+
73
+ if node.parent
74
+ _cant_be_nil?(node.parent, receiver)
75
+ else
76
+ false
77
+ end
78
+ end
79
+ # rubocop:enable Metrics
80
+
81
+ def non_nil_method?(method_name)
82
+ !NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
83
+ end
84
+
85
+ # rubocop:disable Metrics/PerceivedComplexity
86
+ def sole_condition_of_parent_if?(node)
87
+ parent = node.parent
88
+
89
+ while parent
90
+ if parent.if_type?
91
+ if parent.condition == node
92
+ return true
93
+ elsif parent.elsif?
94
+ parent = find_top_if(parent)
95
+ end
96
+ elsif else_branch?(parent)
97
+ # Find the top `if` for `else`.
98
+ parent = parent.parent
99
+ end
100
+
101
+ parent = parent&.parent
102
+ end
103
+
104
+ false
105
+ end
106
+ # rubocop:enable Metrics/PerceivedComplexity
107
+
108
+ def else_branch?(node)
109
+ node.parent&.if_type? && node.parent.else_branch == node
110
+ end
111
+
112
+ def find_top_if(node)
113
+ node = node.parent while node.elsif?
114
+
115
+ node
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -16,6 +16,12 @@ module RuboCop
16
16
  # enumerator.each { |item| item >= 2 } #=> [2, 3]
17
17
  # ----
18
18
  #
19
+ # NOTE: Return values in assignment method definitions such as `def foo=(arg)` are
20
+ # detected because they are in a void context. However, autocorrection does not remove
21
+ # the return value, as that would change behavior. In such cases, whether to remove
22
+ # the return value or rename the method to something more appropriate should be left to
23
+ # the user.
24
+ #
19
25
  # @example CheckForMethodsWithNoSideEffects: false (default)
20
26
  # # bad
21
27
  # def some_method
@@ -233,6 +239,7 @@ module RuboCop
233
239
 
234
240
  def autocorrect_void_expression(corrector, node)
235
241
  return if node.parent.if_type?
242
+ return if (def_node = node.each_ancestor(:any_def).first) && def_node.assignment_method?
236
243
 
237
244
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
238
245
  end
@@ -102,7 +102,7 @@ module RuboCop
102
102
  def reference_urls
103
103
  urls = cop_config
104
104
  .values_at('References', 'Reference') # Support legacy Reference key
105
- .flat_map { Array(_1) }
105
+ .flat_map { |url| Array(url) }
106
106
  .reject(&:empty?)
107
107
 
108
108
  urls unless urls.empty?
@@ -66,7 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  # @deprecated Use processed_source.line_with_comment?(line)
69
- def end_of_line_comment(line)
69
+ def end_of_line_comment(line) # rubocop:disable Naming/PredicateMethod
70
70
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
71
71
  `end_of_line_comment` is deprecated. Use `processed_source.line_with_comment?` instead.
72
72
  WARNING
@@ -227,7 +227,7 @@ module RuboCop
227
227
 
228
228
  def chained_to_heredoc?(node)
229
229
  while (node = node.receiver)
230
- return true if node.type?(:str, :dstr, :xstr) && node.heredoc?
230
+ return true if node.any_str_type? && node.heredoc?
231
231
  end
232
232
 
233
233
  false
@@ -19,8 +19,7 @@ module RuboCop
19
19
  def check_end_kw_alignment(node, align_ranges)
20
20
  return if ignored_node?(node)
21
21
 
22
- end_loc = node.loc.end
23
- return if accept_end_kw_alignment?(end_loc)
22
+ return unless (end_loc = node.loc.end)
24
23
 
25
24
  matching = matching_ranges(end_loc, align_ranges)
26
25
 
@@ -57,11 +56,6 @@ module RuboCop
57
56
  add_offense(end_loc, message: msg) { |corrector| autocorrect(corrector, node) }
58
57
  end
59
58
 
60
- def accept_end_kw_alignment?(end_loc)
61
- end_loc.nil? || # Discard modifier forms of if/while/until.
62
- !/\A[ \t]*end/.match?(processed_source.lines[end_loc.line - 1])
63
- end
64
-
65
59
  def style_parameter_name
66
60
  'EnforcedStyleAlignWith'
67
61
  end
@@ -89,7 +89,7 @@ module RuboCop
89
89
 
90
90
  if first_non_comment_token
91
91
  # `line` is 1-indexed so we need to subtract 1 to get the array index
92
- processed_source.lines[0...first_non_comment_token.line - 1]
92
+ processed_source.lines[0...(first_non_comment_token.line - 1)]
93
93
  else
94
94
  processed_source.lines
95
95
  end
@@ -25,6 +25,28 @@ module RuboCop
25
25
  (args
26
26
  (arg $_)) ...)
27
27
  PATTERN
28
+
29
+ # @!method assignment_method_declarations(node)
30
+ def_node_search :assignment_method_declarations, <<~PATTERN
31
+ (send
32
+ (lvar {#match_block_variable_name? :_1 :it}) _ ...)
33
+ PATTERN
34
+
35
+ # @!method indexed_assignment_method_declarations(node)
36
+ def_node_search :indexed_assignment_method_declarations, <<~PATTERN
37
+ (send
38
+ (send (lvar {#match_block_variable_name? :_1 :it}) _)
39
+ :[]=
40
+ literal?
41
+ _
42
+ )
43
+ PATTERN
44
+
45
+ def match_block_variable_name?(receiver_name)
46
+ gem_specification(processed_source.ast) do |block_variable_name|
47
+ return block_variable_name == receiver_name
48
+ end
49
+ end
28
50
  end
29
51
  end
30
52
  end