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
@@ -9,6 +9,13 @@ module RuboCop
9
9
  # on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
10
10
  # appended after these methods will never evaluate.
11
11
  #
12
+ # @safety
13
+ # As shown in the examples below, there are generally two possible ways to correct the
14
+ # offense, but this cop’s autocorrection always chooses the option that preserves the
15
+ # current behavior. While this does not change how the code behaves, that option is not
16
+ # necessarily the appropriate fix in every situation. For this reason, the autocorrection
17
+ # provided by this cop is considered unsafe.
18
+ #
12
19
  # @example
13
20
  #
14
21
  # # bad
@@ -64,6 +71,8 @@ module RuboCop
64
71
  # x&.to_s or fallback
65
72
  #
66
73
  class UselessOr < Base
74
+ extend AutoCorrector
75
+
67
76
  MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
68
77
 
69
78
  TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
@@ -89,8 +98,12 @@ module RuboCop
89
98
  private
90
99
 
91
100
  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))
101
+ add_offense(
102
+ or_node.loc.operator.join(or_node.rhs.source_range),
103
+ message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source)
104
+ ) do |corrector|
105
+ corrector.replace(or_node, or_node.lhs.source)
106
+ end
94
107
  end
95
108
  end
96
109
  end
@@ -31,7 +31,7 @@ module RuboCop
31
31
  @checked_nodes[node] = true
32
32
 
33
33
  case node.type
34
- when :def, :class, :module, :sclass
34
+ when :def, :defs, :class, :module, :sclass
35
35
  return false
36
36
  when :send
37
37
  return non_nil_method?(node.method_name) if node.receiver == receiver
@@ -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?
@@ -84,7 +84,10 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def assignment?(node)
87
- return compound_assignment(node) if node.masgn_type? || node.shorthand_asgn?
87
+ if node.masgn_type? || node.shorthand_asgn?
88
+ compound_assignment(node)
89
+ return false
90
+ end
88
91
 
89
92
  node.for_type? ||
90
93
  (node.respond_to?(:setter_method?) && node.setter_method?) ||
@@ -101,8 +104,6 @@ module RuboCop
101
104
  child.respond_to?(:setter_method?) && !child.setter_method?
102
105
  end
103
106
  @assignment += will_be_miscounted
104
-
105
- false
106
107
  end
107
108
 
108
109
  def simple_assignment?(node)
@@ -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
@@ -14,6 +14,8 @@ module RuboCop
14
14
  private
15
15
 
16
16
  def too_long?(node)
17
+ return false unless max_line_length
18
+
17
19
  lines = processed_source.lines[(node.first_line - 1)...node.last_line]
18
20
  to_single_line(lines.join("\n")).length > max_line_length
19
21
  end
@@ -27,10 +29,6 @@ module RuboCop
27
29
  .gsub(/\s*\\?\n\s*/, ' ') # Any other line break, with or without backslash
28
30
  end
29
31
 
30
- def max_line_length
31
- config.for_cop('Layout/LineLength')['Max']
32
- end
33
-
34
32
  def comment_within?(node)
35
33
  comment_line_numbers = processed_source.comments.map { |comment| comment.loc.line }
36
34
 
@@ -59,7 +59,7 @@ module RuboCop
59
59
  return node.loc.name if node.casgn_type?
60
60
 
61
61
  if LSP.enabled?
62
- end_range = node.loc.respond_to?(:name) ? node.loc.name : node.loc.begin
62
+ end_range = node.loc?(:name) ? node.loc.name : node.loc.begin
63
63
  node.source_range.begin.join(end_range)
64
64
  else
65
65
  node.source_range
@@ -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
@@ -13,7 +13,7 @@ module RuboCop
13
13
 
14
14
  DefNode = Struct.new(:node) do
15
15
  def selector
16
- if node.loc.respond_to?(:selector)
16
+ if node.loc?(:selector)
17
17
  node.loc.selector
18
18
  else
19
19
  node.loc.keyword
@@ -8,8 +8,27 @@ module RuboCop
8
8
 
9
9
  private
10
10
 
11
- def ignore_cop_directives?
12
- config.for_cop('Layout/LineLength')['IgnoreCopDirectives']
11
+ def allow_rbs_inline_annotation?
12
+ config.for_cop('Layout/LineLength')['AllowRBSInlineAnnotation']
13
+ end
14
+
15
+ def rbs_inline_annotation_on_source_line?(line_index)
16
+ source_line_number = line_index + processed_source.buffer.first_line
17
+ comment = processed_source.comment_at_line(source_line_number)
18
+
19
+ return false unless comment
20
+
21
+ comment.text.start_with?(/#:|#\[.+\]|#\|/)
22
+ end
23
+
24
+ def allow_cop_directives?
25
+ # TODO: This logic for backward compatibility with deprecated `IgnoreCopDirectives` option.
26
+ # The following three lines will be removed in RuboCop 2.0.
27
+ ignore_cop_directives = config.for_cop('Layout/LineLength')['IgnoreCopDirectives']
28
+ return true if ignore_cop_directives
29
+ return false if ignore_cop_directives == false
30
+
31
+ config.for_cop('Layout/LineLength')['AllowCopDirectives']
13
32
  end
14
33
 
15
34
  def directive_on_source_line?(line_index)
@@ -73,7 +73,7 @@ module RuboCop
73
73
 
74
74
  def location(node)
75
75
  if LSP.enabled?
76
- end_range = node.loc.respond_to?(:name) ? node.loc.name : node.loc.begin
76
+ end_range = node.loc?(:name) ? node.loc.name : node.loc.begin
77
77
  node.source_range.begin.join(end_range)
78
78
  else
79
79
  node.source_range
@@ -200,7 +200,7 @@ module RuboCop
200
200
  end
201
201
 
202
202
  def grouped_expression?(node)
203
- node.begin_type? && node.loc.respond_to?(:begin) && node.loc.begin
203
+ node.begin_type? && node.loc?(:begin) && node.loc.begin
204
204
  end
205
205
 
206
206
  def inside_arg_list_parentheses?(node, ancestor)
@@ -127,7 +127,7 @@ module RuboCop
127
127
  parent ||= node
128
128
 
129
129
  if node.respond_to?(:loc) &&
130
- node.loc.respond_to?(:heredoc_end) &&
130
+ node.loc?(:heredoc_end) &&
131
131
  node.loc.heredoc_end.last_line >= parent.last_line
132
132
  return true
133
133
  end
@@ -8,8 +8,8 @@ module RuboCop
8
8
  MSG = 'Space missing after %<token>s.'
9
9
 
10
10
  def on_new_investigation
11
- each_missing_space(processed_source.tokens) do |token|
12
- add_offense(token.pos, message: format(MSG, token: kind(token))) do |corrector|
11
+ each_missing_space(processed_source.tokens) do |token, kind|
12
+ add_offense(token.pos, message: format(MSG, token: kind)) do |corrector|
13
13
  PunctuationCorrector.add_space(corrector, token)
14
14
  end
15
15
  end
@@ -19,11 +19,12 @@ module RuboCop
19
19
 
20
20
  def each_missing_space(tokens)
21
21
  tokens.each_cons(2) do |token1, token2|
22
- next unless kind(token1)
22
+ kind = kind(token1, token2)
23
+ next unless kind
23
24
  next unless space_missing?(token1, token2)
24
25
  next unless space_required_before?(token2)
25
26
 
26
- yield token1
27
+ yield token1, kind
27
28
  end
28
29
  end
29
30
 
@@ -100,12 +100,6 @@ module RuboCop
100
100
  node.parent.send_type?
101
101
  end
102
102
 
103
- def max_line_length
104
- return unless config.cop_enabled?('Layout/LineLength')
105
-
106
- config.for_cop('Layout/LineLength')['Max']
107
- end
108
-
109
103
  def comment_disables_cop?(comment)
110
104
  regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
111
105
  Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
@@ -95,13 +95,16 @@ module RuboCop
95
95
  node.multiline? && !allowed_multiline_argument?(node)
96
96
  end
97
97
 
98
+ # rubocop:disable Metrics/AbcSize
98
99
  def method_name_and_arguments_on_same_line?(node)
99
- return false unless node.call_type?
100
+ return false if !node.call_type? || node.last_line != node.last_argument.last_line
101
+ return true if node.last_argument.hash_type? && node.last_argument.braces?
100
102
 
101
- line = node.loc.selector.nil? ? node.loc.line : node.loc.selector.line
103
+ line = node.loc.selector&.line || node.loc.line
102
104
 
103
- line == node.last_argument.last_line && node.last_line == node.last_argument.last_line
105
+ line == node.last_argument.last_line
104
106
  end
107
+ # rubocop:enable Metrics/AbcSize
105
108
 
106
109
  # A single argument with the closing bracket on the same line as the end
107
110
  # of the argument is not considered multiline, even if the argument
@@ -140,7 +143,7 @@ module RuboCop
140
143
  end
141
144
 
142
145
  def last_item_precedes_newline?(node)
143
- after_last_item = node.children.last.source_range.end.join(node.loc.end.begin)
146
+ after_last_item = node.children.last.source_range.end.join(node.source_range.end)
144
147
 
145
148
  after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
146
149
  end
@@ -185,7 +188,7 @@ module RuboCop
185
188
 
186
189
  def heredoc?(node)
187
190
  return false unless node.is_a?(RuboCop::AST::Node)
188
- return true if node.loc.respond_to?(:heredoc_body)
191
+ return true if node.loc?(:heredoc_body)
189
192
 
190
193
  return heredoc_send?(node) if node.send_type?
191
194
 
@@ -147,7 +147,9 @@ module RuboCop
147
147
  alias on_defs on_def
148
148
 
149
149
  def on_alias(node)
150
- handle_method_name(node.new_identifier, node.new_identifier.value)
150
+ return unless (new_identifier = node.new_identifier).sym_type?
151
+
152
+ handle_method_name(new_identifier, new_identifier.value)
151
153
  end
152
154
 
153
155
  private
@@ -198,7 +200,7 @@ module RuboCop
198
200
 
199
201
  if forbidden_name?(name.to_s)
200
202
  register_forbidden_name(node)
201
- elsif !OPERATOR_METHODS.include?(name)
203
+ elsif !OPERATOR_METHODS.include?(name.to_sym)
202
204
  check_name(node, name, range_position(node))
203
205
  end
204
206
  end
@@ -232,7 +234,7 @@ module RuboCop
232
234
  end
233
235
 
234
236
  def range_position(node)
235
- if node.loc.respond_to?(:selector)
237
+ if node.loc?(:selector)
236
238
  selector_end_pos = node.loc.selector.end_pos + 1
237
239
  expr_end_pos = node.source_range.end_pos
238
240
 
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # method calls are assumed to return boolean values. The cop does not make an assessment
15
15
  # if the return type is unknown (non-predicate method calls, variables, etc.).
16
16
  #
17
- # NOTE: Operator methods (`def ==`, etc.) are ignored.
17
+ # NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
18
18
  #
19
19
  # By default, the cop runs in `conservative` mode, which allows a method to be named
20
20
  # with a question mark as long as at least one return value is boolean. In `aggressive`
@@ -113,6 +113,18 @@ module RuboCop
113
113
  # true
114
114
  # end
115
115
  #
116
+ # @example AllowedMethods: [call] (default)
117
+ # # good
118
+ # def call
119
+ # foo == bar
120
+ # end
121
+ #
122
+ # @example AllowedPatterns: [\Afoo]
123
+ # # good
124
+ # def foo?
125
+ # 'foo'
126
+ # end
127
+ #
116
128
  # @example AllowBangMethods: false (default)
117
129
  # # bad
118
130
  # def save!
@@ -149,7 +161,8 @@ module RuboCop
149
161
  private
150
162
 
151
163
  def allowed?(node)
152
- allowed_method?(node.method_name) ||
164
+ node.method?(:initialize) ||
165
+ allowed_method?(node.method_name) ||
153
166
  matches_allowed_pattern?(node.method_name) ||
154
167
  allowed_bang_method?(node) ||
155
168
  node.operator_method? ||
@@ -180,8 +193,7 @@ module RuboCop
180
193
  return_values << extract_return_value(return_node)
181
194
  end
182
195
 
183
- last_value = last_value(node)
184
- return_values << last_value if last_value
196
+ return_values << last_value(node)
185
197
 
186
198
  process_return_values(return_values)
187
199
  end
@@ -234,8 +246,9 @@ module RuboCop
234
246
  end
235
247
 
236
248
  def last_value(node)
237
- value = node.begin_type? ? node.children.last : node
238
- value&.return_type? ? extract_return_value(value) : value
249
+ value = node.begin_type? ? node.children.last || s(:nil) : node
250
+
251
+ value.return_type? ? extract_return_value(value) : value
239
252
  end
240
253
 
241
254
  def process_return_values(return_values)
@@ -6,22 +6,40 @@ module RuboCop
6
6
  # Checks for the use of JSON class methods which have potential
7
7
  # security issues.
8
8
  #
9
+ # `JSON.load` and similar methods allow deserialization of arbitrary ruby objects:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # require 'json/add/string'
14
+ # result = JSON.load('{ "json_class": "String", "raw": [72, 101, 108, 108, 111] }')
15
+ # pp result # => "Hello"
16
+ # ----
17
+ #
18
+ # Never use `JSON.load` for untrusted user input. Prefer `JSON.parse` unless you have
19
+ # a concrete use-case for `JSON.load`.
20
+ #
21
+ # NOTE: Starting with `json` gem version 2.8.0, triggering this behavior without explicitly
22
+ # passing the `create_additions` keyword argument emits a deprecation warning, with the
23
+ # goal of being secure by default in the next major version 3.0.0.
24
+ #
9
25
  # @safety
10
26
  # This cop's autocorrection is unsafe because it's potentially dangerous.
11
- # If using a stream, like `JSON.load(open('file'))`, it will need to call
27
+ # If using a stream, like `JSON.load(open('file'))`, you will need to call
12
28
  # `#read` manually, like `JSON.parse(open('file').read)`.
13
- # If reading single values (rather than proper JSON objects), like
14
- # `JSON.load('false')`, it will need to pass the `quirks_mode: true`
15
- # option, like `JSON.parse('false', quirks_mode: true)`.
16
29
  # Other similar issues may apply.
17
30
  #
18
31
  # @example
19
32
  # # bad
20
- # JSON.load("{}")
21
- # JSON.restore("{}")
33
+ # JSON.load('{}')
34
+ # JSON.restore('{}')
22
35
  #
23
36
  # # good
24
- # JSON.parse("{}")
37
+ # JSON.parse('{}')
38
+ # JSON.unsafe_load('{}')
39
+ #
40
+ # # good - explicit use of `create_additions` option
41
+ # JSON.load('{}', create_additions: true)
42
+ # JSON.load('{}', create_additions: false)
25
43
  #
26
44
  class JSONLoad < Base
27
45
  extend AutoCorrector
@@ -29,13 +47,17 @@ module RuboCop
29
47
  MSG = 'Prefer `JSON.parse` over `JSON.%<method>s`.'
30
48
  RESTRICT_ON_SEND = %i[load restore].freeze
31
49
 
32
- # @!method json_load(node)
33
- def_node_matcher :json_load, <<~PATTERN
34
- (send (const {nil? cbase} :JSON) ${:load :restore} ...)
50
+ # @!method insecure_json_load(node)
51
+ def_node_matcher :insecure_json_load, <<~PATTERN
52
+ (
53
+ send (const {nil? cbase} :JSON) ${:load :restore}
54
+ ...
55
+ !(hash `(sym $:create_additions))
56
+ )
35
57
  PATTERN
36
58
 
37
59
  def on_send(node)
38
- json_load(node) do |method|
60
+ insecure_json_load(node) do |method|
39
61
  add_offense(node.loc.selector, message: format(MSG, method: method)) do |corrector|
40
62
  corrector.replace(node.loc.selector, 'parse')
41
63
  end
@@ -10,6 +10,8 @@ module RuboCop
10
10
  # * `(array1 & array2).any?`
11
11
  # * `(array1.intersection(array2)).any?`
12
12
  # * `array1.any? { |elem| array2.member?(elem) }`
13
+ # * `(array1 & array2).count > 0`
14
+ # * `(array1 & array2).size > 0`
13
15
  #
14
16
  # can be replaced with `array1.intersect?(array2)`.
15
17
  #
@@ -51,6 +53,19 @@ module RuboCop
51
53
  # array1.intersect?(array2)
52
54
  # !array1.intersect?(array2)
53
55
  #
56
+ # # bad
57
+ # (array1 & array2).count > 0
58
+ # (array1 & array2).count.positive?
59
+ # (array1 & array2).count != 0
60
+ #
61
+ # (array1 & array2).count == 0
62
+ # (array1 & array2).count.zero?
63
+ #
64
+ # # good
65
+ # array1.intersect?(array2)
66
+ #
67
+ # !array1.intersect?(array2)
68
+ #
54
69
  # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
55
70
  # # good
56
71
  # (array1 & array2).present?
@@ -73,17 +88,33 @@ module RuboCop
73
88
  PREDICATES = %i[any? empty? none?].to_set.freeze
74
89
  ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
75
90
 
91
+ ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
92
+
76
93
  # @!method bad_intersection_check?(node, predicates)
77
94
  def_node_matcher :bad_intersection_check?, <<~PATTERN
78
- (call
95
+ $(call
79
96
  {
80
97
  (begin (send $_ :& $_))
81
- (call $_ :intersection $_)
98
+ (call $!nil? :intersection $_)
82
99
  }
83
100
  $%1
84
101
  )
85
102
  PATTERN
86
103
 
104
+ # @!method intersection_size_check?(node, predicates)
105
+ def_node_matcher :intersection_size_check?, <<~PATTERN
106
+ (call
107
+ $(call
108
+ {
109
+ (begin (send $_ :& $_))
110
+ (call $!nil? :intersection $_)
111
+ }
112
+ %ARRAY_SIZE_METHODS
113
+ )
114
+ {$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
115
+ )
116
+ PATTERN
117
+
87
118
  # @!method any_none_block_intersection(node)
88
119
  def_node_matcher :any_none_block_intersection, <<~PATTERN
89
120
  {
@@ -104,15 +135,15 @@ module RuboCop
104
135
  PATTERN
105
136
 
106
137
  MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
107
- STRAIGHT_METHODS = %i[present? any?].freeze
108
- NEGATED_METHODS = %i[blank? empty? none?].freeze
138
+ STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
139
+ NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
109
140
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
110
141
 
111
142
  def on_send(node)
112
143
  return if node.block_literal?
113
- return unless (receiver, argument, method_name = bad_intersection?(node))
144
+ return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
114
145
 
115
- dot = node.loc.dot.source
146
+ dot = dot_node.loc.dot.source
116
147
  bang = straight?(method_name) ? '' : '!'
117
148
  replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
118
149
 
@@ -135,13 +166,16 @@ module RuboCop
135
166
  private
136
167
 
137
168
  def bad_intersection?(node)
138
- predicates = if active_support_extensions_enabled?
139
- ACTIVE_SUPPORT_PREDICATES
140
- else
141
- PREDICATES
142
- end
169
+ bad_intersection_check?(node, bad_intersection_predicates) ||
170
+ intersection_size_check?(node)
171
+ end
143
172
 
144
- bad_intersection_check?(node, predicates)
173
+ def bad_intersection_predicates
174
+ if active_support_extensions_enabled?
175
+ ACTIVE_SUPPORT_PREDICATES
176
+ else
177
+ PREDICATES
178
+ end
145
179
  end
146
180
 
147
181
  def straight?(method_name)
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Use `include?(element)` instead of `intersect?([element])`.
7
+ #
8
+ # @safety
9
+ # The receiver might not be an array.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # array.intersect?([element])
14
+ #
15
+ # # good
16
+ # array.include?(element)
17
+ class ArrayIntersectWithSingleElement < Base
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `include?(element)` instead of `intersect?([element])`.'
21
+
22
+ RESTRICT_ON_SEND = %i[intersect?].freeze
23
+
24
+ # @!method single_element(node)
25
+ def_node_matcher :single_element, <<~PATTERN
26
+ (send _ _ $(array $_))
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ array, element = single_element(node)
31
+ return unless array
32
+
33
+ add_offense(
34
+ node.source_range.with(begin_pos: node.loc.selector.begin_pos)
35
+ ) do |corrector|
36
+ corrector.replace(node.loc.selector, 'include?')
37
+ corrector.replace(
38
+ array,
39
+ array.percent_literal? ? element.value.inspect : element.source
40
+ )
41
+ end
42
+ end
43
+ alias on_csend on_send
44
+ end
45
+ end
46
+ end
47
+ end
@@ -41,8 +41,7 @@ module RuboCop
41
41
 
42
42
  def check(node)
43
43
  return if node.heredoc?
44
- return unless node.loc.respond_to?(:begin)
45
- return unless node.loc.begin
44
+ return unless node.loc?(:begin)
46
45
 
47
46
  source = node.loc.begin.source
48
47
  if requires_percent_q?(source)
@@ -70,18 +70,25 @@ module RuboCop
70
70
  (send _ :& _))
71
71
  PATTERN
72
72
 
73
+ # rubocop:disable Metrics/AbcSize
73
74
  def on_send(node)
74
75
  return unless node.receiver&.begin_type?
75
76
  return unless (preferred_method = preferred_method(node))
76
77
 
77
78
  bit_operation = node.receiver.children.first
78
79
  lhs, _operator, rhs = *bit_operation
79
- preferred = "#{lhs.source}.#{preferred_method}(#{rhs.source})"
80
+
81
+ preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
82
+ "#{rhs.source}.allbits?(#{lhs.source})"
83
+ else
84
+ "#{lhs.source}.#{preferred_method}(#{rhs.source})"
85
+ end
80
86
 
81
87
  add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
82
88
  corrector.replace(node, preferred)
83
89
  end
84
90
  end
91
+ # rubocop:enable Metrics/AbcSize
85
92
 
86
93
  private
87
94