rubocop 1.81.1 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +25 -7
  4. data/config/obsoletion.yml +4 -0
  5. data/lib/rubocop/cli.rb +2 -1
  6. data/lib/rubocop/comment_config.rb +62 -17
  7. data/lib/rubocop/config_loader.rb +2 -1
  8. data/lib/rubocop/config_loader_resolver.rb +7 -6
  9. data/lib/rubocop/cop/autocorrect_logic.rb +4 -0
  10. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  11. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -3
  12. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  13. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
  14. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  15. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  16. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
  17. data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
  18. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
  19. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  20. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
  21. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  22. data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
  23. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_length.rb +8 -4
  25. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  26. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +5 -3
  28. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  29. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  30. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  31. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  32. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  33. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  34. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  35. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  36. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  37. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  38. data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
  39. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  40. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +16 -6
  41. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  43. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  44. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
  45. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  46. data/lib/rubocop/cop/lint/self_assignment.rb +10 -2
  47. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
  48. data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
  49. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  50. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
  51. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  52. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
  53. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  54. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
  55. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  56. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  57. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  58. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  59. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  60. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  61. data/lib/rubocop/cop/mixin/trailing_comma.rb +7 -4
  62. data/lib/rubocop/cop/naming/method_name.rb +4 -2
  63. data/lib/rubocop/cop/naming/predicate_method.rb +4 -4
  64. data/lib/rubocop/cop/security/json_load.rb +33 -11
  65. data/lib/rubocop/cop/style/array_intersect.rb +2 -2
  66. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
  67. data/lib/rubocop/cop/style/case_equality.rb +11 -13
  68. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
  69. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -14
  70. data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
  71. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  72. data/lib/rubocop/cop/style/endless_method.rb +15 -2
  73. data/lib/rubocop/cop/style/float_division.rb +15 -1
  74. data/lib/rubocop/cop/style/guard_clause.rb +0 -11
  75. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
  77. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  78. data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
  79. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  80. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
  81. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  82. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  83. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  84. data/lib/rubocop/cop/style/redundant_format.rb +10 -4
  85. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  86. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
  87. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +5 -0
  88. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  89. data/lib/rubocop/cop/style/semicolon.rb +23 -7
  90. data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
  91. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  92. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  93. data/lib/rubocop/cop/util.rb +2 -3
  94. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  95. data/lib/rubocop/directive_comment.rb +46 -3
  96. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
  97. data/lib/rubocop/lsp/diagnostic.rb +12 -17
  98. data/lib/rubocop/lsp/routes.rb +9 -36
  99. data/lib/rubocop/lsp/runtime.rb +2 -2
  100. data/lib/rubocop/lsp/server.rb +2 -2
  101. data/lib/rubocop/magic_comment.rb +20 -0
  102. data/lib/rubocop/rake_task.rb +1 -1
  103. data/lib/rubocop/remote_config.rb +7 -8
  104. data/lib/rubocop/result_cache.rb +38 -27
  105. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  106. data/lib/rubocop/rspec/support.rb +1 -1
  107. data/lib/rubocop/runner.rb +4 -0
  108. data/lib/rubocop/target_ruby.rb +1 -1
  109. data/lib/rubocop/version.rb +1 -1
  110. data/lib/rubocop.rb +1 -0
  111. metadata +8 -10
@@ -52,32 +52,39 @@ module RuboCop
52
52
  scope.variables.each_value { |variable| check_for_unused_assignments(variable) }
53
53
  end
54
54
 
55
- # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
56
55
  def check_for_unused_assignments(variable)
57
56
  return if variable.should_be_unused?
58
57
 
59
58
  variable.assignments.reverse_each do |assignment|
60
- assignment_node = assignment.node
61
- next if assignment.used? || part_of_ignored_node?(assignment_node)
59
+ check_for_unused_assignment(variable, assignment)
60
+ end
61
+ end
62
62
 
63
- message = message_for_useless_assignment(assignment)
64
- range = offense_range(assignment)
63
+ def check_for_unused_assignment(variable, assignment)
64
+ assignment_node = assignment.node
65
65
 
66
- add_offense(range, message: message) do |corrector|
67
- # In cases like `x = 1, y = 2`, where removing a variable would cause a syntax error,
68
- # and where changing `x ||= 1` to `x = 1` would cause `NameError`,
69
- # the autocorrect will be skipped, even if the variable is unused.
70
- if sequential_assignment?(assignment_node) || assignment_node.parent&.or_asgn_type?
71
- next
72
- end
66
+ return if ignored_assignment?(variable, assignment_node, assignment)
73
67
 
74
- autocorrect(corrector, assignment)
75
- end
68
+ message = message_for_useless_assignment(assignment)
69
+ range = offense_range(assignment)
76
70
 
77
- ignore_node(assignment_node) if chained_assignment?(assignment_node)
71
+ add_offense(range, message: message) do |corrector|
72
+ # In cases like `x = 1, y = 2`, where removing a variable would cause a syntax error,
73
+ # and where changing `x ||= 1` to `x = 1` would cause `NameError`,
74
+ # the autocorrect will be skipped, even if the variable is unused.
75
+ next if sequential_assignment?(assignment_node) ||
76
+ assignment_node.parent&.or_asgn_type?
77
+
78
+ autocorrect(corrector, assignment)
78
79
  end
80
+
81
+ ignore_node(assignment_node) if chained_assignment?(assignment_node)
82
+ end
83
+
84
+ def ignored_assignment?(variable, assignment_node, assignment)
85
+ assignment.used? || part_of_ignored_node?(assignment_node) ||
86
+ variable_in_loop_condition?(assignment_node, variable)
79
87
  end
80
- # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
81
88
 
82
89
  def message_for_useless_assignment(assignment)
83
90
  variable = assignment.variable
@@ -208,6 +215,27 @@ module RuboCop
208
215
  def remove_local_variable_assignment_part(corrector, node)
209
216
  corrector.replace(node, node.expression.source)
210
217
  end
218
+
219
+ def variable_in_loop_condition?(assignment_node, variable)
220
+ return false if assignment_node.each_ancestor(:any_def).any?
221
+
222
+ loop_node = assignment_node.each_ancestor.find do |ancestor|
223
+ ancestor.type?(*VariableForce::LOOP_TYPES)
224
+ end
225
+
226
+ return false unless loop_node.respond_to?(:condition)
227
+
228
+ condition_node = loop_node.condition
229
+ variable_name = variable.name
230
+
231
+ return true if condition_node.lvar_type? && condition_node.children.first == variable_name
232
+
233
+ condition_node.each_descendant(:lvar) do |lvar_node|
234
+ return true if lvar_node.children.first == variable_name
235
+ end
236
+
237
+ false
238
+ end
211
239
  end
212
240
  end
213
241
  end
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
 
@@ -193,8 +193,7 @@ module RuboCop
193
193
  return_values << extract_return_value(return_node)
194
194
  end
195
195
 
196
- last_value = last_value(node)
197
- return_values << last_value if last_value
196
+ return_values << last_value(node)
198
197
 
199
198
  process_return_values(return_values)
200
199
  end
@@ -247,8 +246,9 @@ module RuboCop
247
246
  end
248
247
 
249
248
  def last_value(node)
250
- value = node.begin_type? ? node.children.last : node
251
- 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
252
252
  end
253
253
 
254
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
@@ -95,7 +95,7 @@ module RuboCop
95
95
  $(call
96
96
  {
97
97
  (begin (send $_ :& $_))
98
- (call $_ :intersection $_)
98
+ (call $!nil? :intersection $_)
99
99
  }
100
100
  $%1
101
101
  )
@@ -107,7 +107,7 @@ module RuboCop
107
107
  $(call
108
108
  {
109
109
  (begin (send $_ :& $_))
110
- (call $_ :intersection $_)
110
+ (call $!nil? :intersection $_)
111
111
  }
112
112
  %ARRAY_SIZE_METHODS
113
113
  )
@@ -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)
@@ -3,31 +3,36 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of the case equality operator(===).
6
+ # Checks for uses of the case equality operator (`===`).
7
7
  #
8
8
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
9
  # the case equality operator is a constant.
10
-
10
+ #
11
11
  # If `AllowOnSelfClass` option is enabled, the cop will ignore violations when the receiver of
12
12
  # the case equality operator is `self.class`. Note intermediate variables are not accepted.
13
13
  #
14
+ # NOTE: Regexp case equality (`/regexp/ === var`) is allowed because changing it to
15
+ # `/regexp/.match?(var)` needs to take into account `Regexp.last_match?`, `$~`, `$1`, etc.
16
+ # This potentially incompatible transformation is handled by `Performance/RegexpMatch` cop.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # (1..100) === 7
17
- # /something/ === some_string
18
21
  #
19
22
  # # good
20
- # something.is_a?(Array)
21
23
  # (1..100).include?(7)
22
- # /something/.match?(some_string)
23
24
  #
24
25
  # @example AllowOnConstant: false (default)
25
26
  # # bad
26
27
  # Array === something
27
28
  #
29
+ # # good
30
+ # something.is_a?(Array)
31
+ #
28
32
  # @example AllowOnConstant: true
29
33
  # # good
30
34
  # Array === something
35
+ # something.is_a?(Array)
31
36
  #
32
37
  # @example AllowOnSelfClass: false (default)
33
38
  # # bad
@@ -51,7 +56,7 @@ module RuboCop
51
56
 
52
57
  def on_send(node)
53
58
  case_equality?(node) do |lhs, rhs|
54
- return if lhs.const_type? && !lhs.module_name?
59
+ return if lhs.regexp_type? || (lhs.const_type? && !lhs.module_name?)
55
60
 
56
61
  add_offense(node.loc.selector) do |corrector|
57
62
  replacement = replacement(lhs, rhs)
@@ -71,13 +76,6 @@ module RuboCop
71
76
 
72
77
  def replacement(lhs, rhs)
73
78
  case lhs.type
74
- when :regexp
75
- # The automatic correction from `a === b` to `a.match?(b)` needs to
76
- # consider `Regexp.last_match?`, `$~`, `$1`, and etc.
77
- # This correction is expected to be supported by `Performance/Regexp` cop.
78
- # See: https://github.com/rubocop/rubocop-performance/issues/152
79
- #
80
- # So here is noop.
81
79
  when :begin
82
80
  begin_replacement(lhs, rhs)
83
81
  when :const
@@ -183,6 +183,7 @@ module RuboCop
183
183
 
184
184
  def check_nested_style(node)
185
185
  return unless compact_node_name?(node)
186
+ return if node.parent&.type?(:class, :module)
186
187
 
187
188
  add_offense(node.loc.name, message: NESTED_MSG) do |corrector|
188
189
  autocorrect(corrector, node)
@@ -213,9 +213,7 @@ module RuboCop
213
213
  ASSIGN_TO_CONDITION_MSG = 'Assign variables inside of conditionals.'
214
214
  VARIABLE_ASSIGNMENT_TYPES = %i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
215
215
  ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES + %i[and_asgn or_asgn op_asgn masgn].freeze
216
- LINE_LENGTH = 'Layout/LineLength'
217
216
  ENABLED = 'Enabled'
218
- MAX = 'Max'
219
217
  SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'
220
218
 
221
219
  # The shovel operator `<<` does not have its own type. It is a `send`
@@ -399,7 +397,7 @@ module RuboCop
399
397
  # of the longest line + the length of the corrected assignment is
400
398
  # greater than the max configured line length
401
399
  def correction_exceeds_line_limit?(node, branches)
402
- return false unless line_length_cop_enabled?
400
+ return false unless config.cop_enabled?('Layout/LineLength')
403
401
 
404
402
  assignment = lhs(tail(branches[0]))
405
403
 
@@ -417,14 +415,6 @@ module RuboCop
417
415
  assignment + longest_line
418
416
  end
419
417
 
420
- def line_length_cop_enabled?
421
- config.for_cop(LINE_LENGTH)[ENABLED]
422
- end
423
-
424
- def max_line_length
425
- config.for_cop(LINE_LENGTH)[MAX]
426
- end
427
-
428
418
  def single_line_conditions_only?
429
419
  cop_config[SINGLE_LINE_CONDITIONS_ONLY]
430
420
  end
@@ -444,7 +434,7 @@ module RuboCop
444
434
  next if child.parent.dstr_type?
445
435
 
446
436
  white_space = white_space_range(child, column)
447
- corrector.remove(white_space) if white_space.source.strip.empty?
437
+ corrector.remove(white_space) if white_space
448
438
  end
449
439
 
450
440
  if condition.loc.else && !same_line?(condition.else_branch, condition)
@@ -465,9 +455,13 @@ module RuboCop
465
455
 
466
456
  def white_space_range(node, column)
467
457
  expression = node.source_range
468
- begin_pos = expression.begin_pos - (expression.column - column - 2)
458
+ end_pos = expression.begin_pos
459
+ begin_pos = end_pos - (expression.column - column - 2)
460
+
461
+ return nil if begin_pos > end_pos
469
462
 
470
- Parser::Source::Range.new(expression.source_buffer, begin_pos, expression.begin_pos)
463
+ white_space = Parser::Source::Range.new(expression.source_buffer, begin_pos, end_pos)
464
+ white_space if white_space.source.strip.empty?
471
465
  end
472
466
 
473
467
  def assignment(node)
@@ -29,25 +29,30 @@ module RuboCop
29
29
  # @example IgnoreModules: false (default)
30
30
  # # bad
31
31
  # class Foo
32
- # MyClass = Struct.new()
32
+ # MyClass = Struct.new
33
33
  # end
34
34
  #
35
35
  # # good
36
36
  # class Foo
37
- # MyClass = Struct.new()
37
+ # MyClass = Struct.new
38
38
  # public_constant :MyClass
39
39
  # end
40
40
  #
41
41
  # @example IgnoreModules: true
42
42
  # # good
43
43
  # class Foo
44
- # MyClass = Struct.new()
44
+ # MyClass = Struct.new
45
45
  # end
46
46
  #
47
47
  class ConstantVisibility < Base
48
48
  MSG = 'Explicitly make `%<constant_name>s` public or private using ' \
49
49
  'either `#public_constant` or `#private_constant`.'
50
50
 
51
+ # @!method visibility_declaration_for(node)
52
+ def_node_matcher :visibility_declaration_for, <<~PATTERN
53
+ (send nil? {:public_constant :private_constant} $...)
54
+ PATTERN
55
+
51
56
  def on_casgn(node)
52
57
  return unless class_or_module_scope?(node)
53
58
  return if visibility_declaration?(node)
@@ -77,20 +82,20 @@ module RuboCop
77
82
  end
78
83
  end
79
84
 
85
+ # rubocop:disable Metrics/AbcSize
80
86
  def visibility_declaration?(node)
81
87
  node.parent.each_child_node(:send).any? do |child|
82
- visibility_declaration_for?(child, node.name)
83
- end
84
- end
88
+ next false unless (arguments = visibility_declaration_for(child))
85
89
 
86
- # @!method visibility_declaration_for?(node, const_name)
87
- def_node_matcher :visibility_declaration_for?, <<~PATTERN
88
- (send nil? {:public_constant :private_constant} ({sym str} #match_name?(%1)))
89
- PATTERN
90
+ arguments = arguments.first.children.first.to_a if arguments.first&.splat_type?
91
+ constant_values = arguments.map do |argument|
92
+ argument.value.to_sym if argument.respond_to?(:value)
93
+ end
90
94
 
91
- def match_name?(name, constant_name)
92
- name.to_sym == constant_name.to_sym
95
+ constant_values.include?(node.name)
96
+ end
93
97
  end
98
+ # rubocop:enable Metrics/AbcSize
94
99
  end
95
100
  end
96
101
  end