rubocop 1.60.2 → 1.62.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/assets/output.css.erb +159 -0
  4. data/assets/output.html.erb +1 -160
  5. data/config/default.yml +41 -12
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +12 -3
  7. data/lib/rubocop/cli/command/lsp.rb +2 -2
  8. data/lib/rubocop/cli.rb +6 -1
  9. data/lib/rubocop/config.rb +4 -0
  10. data/lib/rubocop/config_finder.rb +12 -2
  11. data/lib/rubocop/config_validator.rb +14 -5
  12. data/lib/rubocop/cop/autocorrect_logic.rb +6 -1
  13. data/lib/rubocop/cop/base.rb +17 -8
  14. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  15. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  16. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +5 -1
  17. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  18. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +122 -28
  19. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  20. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  21. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +14 -7
  22. data/lib/rubocop/cop/layout/end_alignment.rb +3 -1
  23. data/lib/rubocop/cop/layout/redundant_line_break.rb +11 -3
  24. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  25. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  26. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  27. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -9
  28. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -0
  29. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  30. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  31. data/lib/rubocop/cop/lint/syntax.rb +1 -1
  32. data/lib/rubocop/cop/lint/to_enum_arguments.rb +7 -2
  33. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  34. data/lib/rubocop/cop/lint/void.rb +6 -1
  35. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  36. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  37. data/lib/rubocop/cop/registry.rb +1 -1
  38. data/lib/rubocop/cop/style/arguments_forwarding.rb +29 -8
  39. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  40. data/lib/rubocop/cop/style/class_vars.rb +3 -3
  41. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  42. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -5
  43. data/lib/rubocop/cop/style/for.rb +2 -0
  44. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -1
  45. data/lib/rubocop/cop/style/hash_syntax.rb +6 -2
  46. data/lib/rubocop/cop/style/inverse_methods.rb +8 -8
  47. data/lib/rubocop/cop/style/invertible_unless_condition.rb +10 -5
  48. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +5 -8
  49. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  50. data/lib/rubocop/cop/style/multiline_method_signature.rb +10 -1
  51. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +4 -0
  52. data/lib/rubocop/cop/style/nil_comparison.rb +2 -0
  53. data/lib/rubocop/cop/style/object_then.rb +5 -3
  54. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
  55. data/lib/rubocop/cop/style/raise_args.rb +4 -1
  56. data/lib/rubocop/cop/style/redundant_argument.rb +2 -2
  57. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  58. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +4 -3
  59. data/lib/rubocop/cop/style/redundant_line_continuation.rb +17 -6
  60. data/lib/rubocop/cop/style/redundant_return.rb +6 -0
  61. data/lib/rubocop/cop/style/sample.rb +1 -3
  62. data/lib/rubocop/cop/utils/regexp_ranges.rb +1 -1
  63. data/lib/rubocop/cops_documentation_generator.rb +4 -2
  64. data/lib/rubocop/directive_comment.rb +10 -8
  65. data/lib/rubocop/formatter/html_formatter.rb +30 -10
  66. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  67. data/lib/rubocop/lsp/logger.rb +1 -1
  68. data/lib/rubocop/lsp/routes.rb +1 -1
  69. data/lib/rubocop/lsp/runtime.rb +1 -1
  70. data/lib/rubocop/lsp/server.rb +5 -2
  71. data/lib/rubocop/lsp/severity.rb +1 -1
  72. data/lib/rubocop/lsp.rb +29 -0
  73. data/lib/rubocop/magic_comment.rb +1 -1
  74. data/lib/rubocop/options.rb +11 -0
  75. data/lib/rubocop/path_util.rb +6 -2
  76. data/lib/rubocop/rspec/cop_helper.rb +8 -2
  77. data/lib/rubocop/rspec/expect_offense.rb +8 -8
  78. data/lib/rubocop/rspec/shared_contexts.rb +36 -17
  79. data/lib/rubocop/rspec/support.rb +2 -1
  80. data/lib/rubocop/runner.rb +9 -2
  81. data/lib/rubocop/target_finder.rb +84 -78
  82. data/lib/rubocop/target_ruby.rb +82 -80
  83. data/lib/rubocop/version.rb +18 -3
  84. metadata +9 -6
@@ -5,7 +5,8 @@ module RuboCop
5
5
  module Lint
6
6
  # Checks for redundant safe navigation calls.
7
7
  # Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
8
- # and an offense is not detected when the receiver is a snake case constant.
8
+ # and an offense is not detected when the receiver is a constant. The detection also applies
9
+ # to literal receivers, except for `nil`.
9
10
  #
10
11
  # For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
11
12
  # and `equal?` methods are checked by default.
@@ -76,10 +77,9 @@ module RuboCop
76
77
  #
77
78
  class RedundantSafeNavigation < Base
78
79
  include AllowedMethods
79
- include RangeHelp
80
80
  extend AutoCorrector
81
81
 
82
- MSG = 'Redundant safe navigation detected.'
82
+ MSG = 'Redundant safe navigation detected, use `.` instead.'
83
83
  MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
84
84
 
85
85
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
@@ -105,24 +105,23 @@ module RuboCop
105
105
 
106
106
  # rubocop:disable Metrics/AbcSize
107
107
  def on_csend(node)
108
- unless node.receiver.const_type? && !node.receiver.source.match?(SNAKE_CASE)
108
+ unless assume_receiver_instance_exists?(node.receiver)
109
109
  return unless check?(node) && allowed_method?(node.method_name)
110
110
  return if respond_to_nil_specific_method?(node)
111
111
  end
112
112
 
113
- range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
114
- add_offense(range) { |corrector| corrector.replace(node.loc.dot, '.') }
113
+ range = node.loc.dot
114
+ add_offense(range) { |corrector| corrector.replace(range, '.') }
115
115
  end
116
116
 
117
117
  def on_or(node)
118
118
  conversion_with_default?(node) do |send_node|
119
- range = range_between(send_node.loc.dot.begin_pos, node.source_range.end_pos)
119
+ range = send_node.loc.dot.begin.join(node.source_range.end)
120
120
 
121
121
  add_offense(range, message: MSG_LITERAL) do |corrector|
122
122
  corrector.replace(send_node.loc.dot, '.')
123
123
 
124
- range_with_default = range_between(node.lhs.source_range.end.begin_pos,
125
- node.source_range.end.end_pos)
124
+ range_with_default = node.lhs.source_range.end.begin.join(node.source_range.end)
126
125
  corrector.remove(range_with_default)
127
126
  end
128
127
  end
@@ -131,6 +130,12 @@ module RuboCop
131
130
 
132
131
  private
133
132
 
133
+ def assume_receiver_instance_exists?(receiver)
134
+ return true if receiver.const_type? && !receiver.source.match?(SNAKE_CASE)
135
+
136
+ receiver.literal? && !receiver.nil_type?
137
+ end
138
+
134
139
  def check?(node)
135
140
  parent = node.parent
136
141
  return false unless parent
@@ -34,6 +34,7 @@ module RuboCop
34
34
  MSG_WITH_INDEX = 'Remove redundant `with_index`.'
35
35
 
36
36
  def on_block(node)
37
+ return unless node.receiver
37
38
  return unless (send = redundant_with_index?(node))
38
39
 
39
40
  range = with_index_range(send)
@@ -59,9 +59,7 @@ module RuboCop
59
59
 
60
60
  def autocorrect(corrector, node)
61
61
  rescued, _, _body = *node
62
- range = Parser::Source::Range.new(node.source_range.source_buffer,
63
- node.loc.keyword.end_pos,
64
- rescued.source_range.end_pos)
62
+ range = node.loc.keyword.end.join(rescued.source_range.end)
65
63
 
66
64
  corrector.replace(range, correction(*rescued))
67
65
  end
@@ -46,14 +46,14 @@ module RuboCop
46
46
  message = format_message_from(processed_source)
47
47
 
48
48
  add_offense(comment, message: message) do
49
- autocorrect(comment) if autocorrect_requested?
49
+ autocorrect if autocorrect_requested?
50
50
  end
51
51
  end
52
52
 
53
53
  private
54
54
 
55
- def autocorrect(comment)
56
- FileUtils.chmod('+x', comment.source_range.source_buffer.name)
55
+ def autocorrect
56
+ FileUtils.chmod('+x', processed_source.file_path)
57
57
  end
58
58
 
59
59
  def executable?(processed_source)
@@ -17,7 +17,7 @@ module RuboCop
17
17
  private
18
18
 
19
19
  def add_offense_from_diagnostic(diagnostic, ruby_version)
20
- message = if lsp_mode?
20
+ message = if LSP.enabled?
21
21
  diagnostic.message
22
22
  else
23
23
  "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
@@ -51,8 +51,13 @@ module RuboCop
51
51
  enum_conversion_call?(node) do |method_node, arguments|
52
52
  next if method_node.call_type? &&
53
53
  !method_node.method?(:__method__) && !method_node.method?(:__callee__)
54
- next if method_name?(method_node, def_node.method_name) &&
55
- arguments_match?(arguments, def_node)
54
+
55
+ valid = if method_name?(method_node, def_node.method_name)
56
+ arguments_match?(arguments, def_node)
57
+ else
58
+ def_node.arguments.empty?
59
+ end
60
+ return if valid
56
61
 
57
62
  add_offense(node)
58
63
  end
@@ -64,7 +64,7 @@ module RuboCop
64
64
  remove_node(corrector, node)
65
65
  elsif !proc_name.empty?
66
66
  autocorrect_block_pass(corrector, node, proc_name)
67
- else
67
+ elsif node.block_type?
68
68
  autocorrect_block(corrector, node)
69
69
  end
70
70
  end
@@ -100,7 +100,12 @@ module RuboCop
100
100
  expressions = *node
101
101
  expressions.pop unless in_void_context?(node)
102
102
  expressions.each do |expr|
103
- check_void_op(expr)
103
+ check_void_op(expr) do
104
+ block_node = node.each_ancestor(:block).first
105
+
106
+ block_node&.method?(:each)
107
+ end
108
+
104
109
  check_expression(expr)
105
110
  end
106
111
  end
@@ -189,7 +189,7 @@ module RuboCop
189
189
  case node.type
190
190
  when :casgn then _scope, _lhs, rhs = *node
191
191
  when :op_asgn then _lhs, _op, rhs = *node
192
- when :send then rhs = node.last_argument
192
+ when :send, :csend then rhs = node.last_argument
193
193
  else _lhs, rhs = *node
194
194
  end
195
195
  rhs
@@ -99,7 +99,7 @@ module RuboCop
99
99
  !(method_name.start_with?(prefix) && # cheap check to avoid allocating Regexp
100
100
  method_name.match?(/^#{prefix}[^0-9]/)) ||
101
101
  method_name == expected_name(method_name, prefix) ||
102
- method_name.end_with?('=') || # rubocop:todo InternalAffairs/MethodNameEndWith
102
+ method_name.end_with?('=') ||
103
103
  allowed_method?(method_name)
104
104
  end
105
105
 
@@ -109,7 +109,7 @@ module RuboCop
109
109
  else
110
110
  method_name.dup
111
111
  end
112
- new_name << '?' unless method_name.end_with?('?') # rubocop:todo InternalAffairs/MethodNameEndWith
112
+ new_name << '?' unless method_name.end_with?('?')
113
113
  new_name
114
114
  end
115
115
 
@@ -300,7 +300,7 @@ module RuboCop
300
300
  unless given_badge.match?(real_badge)
301
301
  path = PathUtil.smart_path(source_path)
302
302
  warn "#{path}: #{given_badge} has the wrong namespace - " \
303
- "should be #{real_badge.department}"
303
+ "replace it with #{given_badge.with_department(real_badge.department)}"
304
304
  end
305
305
 
306
306
  real_badge.to_s
@@ -8,6 +8,12 @@ module RuboCop
8
8
  # This cop identifies places where `do_something(*args, &block)`
9
9
  # can be replaced by `do_something(...)`.
10
10
  #
11
+ # In Ruby 3.1, anonymous block forwarding has been added.
12
+ #
13
+ # This cop identifies places where `do_something(&block)` can be replaced
14
+ # by `do_something(&)`; if desired, this functionality can be disabled
15
+ # by setting `UseAnonymousForwarding: false`.
16
+ #
11
17
  # In Ruby 3.2, anonymous args/kwargs forwarding has been added.
12
18
  #
13
19
  # This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
@@ -41,22 +47,25 @@ module RuboCop
41
47
  #
42
48
  # @example UseAnonymousForwarding: true (default, only relevant for Ruby >= 3.2)
43
49
  # # bad
44
- # def foo(*args, **kwargs)
50
+ # def foo(*args, **kwargs, &block)
45
51
  # args_only(*args)
46
52
  # kwargs_only(**kwargs)
53
+ # block_only(&block)
47
54
  # end
48
55
  #
49
56
  # # good
50
- # def foo(*, **)
57
+ # def foo(*, **, &)
51
58
  # args_only(*)
52
59
  # kwargs_only(**)
60
+ # block_only(&)
53
61
  # end
54
62
  #
55
63
  # @example UseAnonymousForwarding: false (only relevant for Ruby >= 3.2)
56
64
  # # good
57
- # def foo(*args, **kwargs)
65
+ # def foo(*args, **kwargs, &block)
58
66
  # args_only(*args)
59
67
  # kwargs_only(**kwargs)
68
+ # block_only(&block)
60
69
  # end
61
70
  #
62
71
  # @example AllowOnlyRestArgument: true (default, only relevant for Ruby < 3.2)
@@ -179,9 +188,12 @@ module RuboCop
179
188
 
180
189
  send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
181
190
  if !forward_rest && !forward_kwrest
182
- register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
183
- register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
184
-
191
+ # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
192
+ # in Ruby 3.3.0.
193
+ if outside_block?(forward_block_arg)
194
+ register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
195
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
196
+ end
185
197
  registered_block_arg_offense = true
186
198
  break
187
199
  else
@@ -196,12 +208,13 @@ module RuboCop
196
208
  end
197
209
  # rubocop:enable Metrics/MethodLength
198
210
 
211
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
199
212
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
200
213
  return unless use_anonymous_forwarding?
201
214
 
202
- rest_arg, kwrest_arg, _block_arg = *forwardable_args
215
+ rest_arg, kwrest_arg, block_arg = *forwardable_args
203
216
 
204
- send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, _forward_block_arg| # rubocop:disable Layout/LineLength
217
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
205
218
  if outside_block?(forward_rest)
206
219
  register_forward_args_offense(def_node.arguments, rest_arg)
207
220
  register_forward_args_offense(send_node, forward_rest)
@@ -211,8 +224,16 @@ module RuboCop
211
224
  register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
212
225
  register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
213
226
  end
227
+
228
+ # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
229
+ # in Ruby 3.3.0.
230
+ if outside_block?(forward_block_arg)
231
+ register_forward_block_arg_offense(!forward_rest, def_node.arguments, block_arg)
232
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
233
+ end
214
234
  end
215
235
  end
236
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
216
237
 
217
238
  def non_splat_or_block_pass_lvar_references(body)
218
239
  body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
@@ -230,7 +230,7 @@ module RuboCop
230
230
 
231
231
  def branch_conditions(node)
232
232
  conditions = []
233
- while node&.if_type?
233
+ while node&.if_type? && !node.ternary?
234
234
  conditions << node.condition
235
235
  node = node.else_branch
236
236
  end
@@ -54,9 +54,9 @@ module RuboCop
54
54
  end
55
55
 
56
56
  def on_send(node)
57
- add_offense(
58
- node.first_argument, message: format(MSG, class_var: node.first_argument.source)
59
- )
57
+ return unless (first_argument = node.first_argument)
58
+
59
+ add_offense(first_argument, message: format(MSG, class_var: first_argument.source))
60
60
  end
61
61
  end
62
62
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../directive_comment'
4
+
3
5
  module RuboCop
4
6
  module Cop
5
7
  module Style
@@ -49,8 +51,9 @@ module RuboCop
49
51
  KEYWORDS = %w[begin class def end module].freeze
50
52
  KEYWORD_REGEXES = KEYWORDS.map { |w| /^\s*#{w}\s/ }.freeze
51
53
 
52
- ALLOWED_COMMENTS = %w[:nodoc: :yields: rubocop:disable rubocop:todo].freeze
53
- ALLOWED_COMMENT_REGEXES = ALLOWED_COMMENTS.map { |c| /#\s*#{c}/ }.freeze
54
+ ALLOWED_COMMENTS = %w[:nodoc: :yields:].freeze
55
+ ALLOWED_COMMENT_REGEXES = (ALLOWED_COMMENTS.map { |c| /#\s*#{c}/ } +
56
+ [DirectiveComment::DIRECTIVE_COMMENT_REGEXP]).freeze
54
57
 
55
58
  REGEXP = /(?<keyword>\S+).*#/.freeze
56
59
 
@@ -115,8 +115,8 @@ module RuboCop
115
115
  end
116
116
 
117
117
  # Check for `if` and `case` statements where each branch is used for
118
- # assignment to the same variable when using the return of the
119
- # condition can be used instead.
118
+ # both the assignment and comparison of the same variable
119
+ # when using the return of the condition can be used instead.
120
120
  #
121
121
  # @example EnforcedStyle: assign_to_condition (default)
122
122
  # # bad
@@ -460,9 +460,8 @@ module RuboCop
460
460
 
461
461
  def assignment(node)
462
462
  *_, condition = *node
463
- Parser::Source::Range.new(node.source_range.source_buffer,
464
- node.source_range.begin_pos,
465
- condition.source_range.begin_pos)
463
+
464
+ node.source_range.begin.join(condition.source_range.begin)
466
465
  end
467
466
 
468
467
  def correct_if_branches(corrector, cop, node)
@@ -66,6 +66,8 @@ module RuboCop
66
66
  return unless suspect_enumerable?(node)
67
67
 
68
68
  if style == :for
69
+ return unless node.receiver
70
+
69
71
  add_offense(node, message: PREFER_FOR) do |corrector|
70
72
  EachToForCorrector.new(node).call(corrector)
71
73
  opposite_style_detected
@@ -40,7 +40,7 @@ module RuboCop
40
40
 
41
41
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
42
42
  UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
43
- ARRAY_CONVERTER_METHODS = %i[assoc flatten rassoc sort sort_by to_a].freeze
43
+ ARRAY_CONVERTER_METHODS = %i[assoc chunk flatten rassoc sort sort_by to_a].freeze
44
44
 
45
45
  # @!method kv_each(node)
46
46
  def_node_matcher :kv_each, <<~PATTERN
@@ -195,6 +195,7 @@ module RuboCop
195
195
  acceptable_19_syntax_symbol?(pair.key.source)
196
196
  end
197
197
 
198
+ # rubocop:disable Metrics/CyclomaticComplexity
198
199
  def acceptable_19_syntax_symbol?(sym_name)
199
200
  sym_name.delete_prefix!(':')
200
201
 
@@ -209,9 +210,12 @@ module RuboCop
209
210
  # Most hash keys can be matched against a simple regex.
210
211
  return true if /\A[_a-z]\w*[?!]?\z/i.match?(sym_name)
211
212
 
212
- # For more complicated hash keys, let the parser validate the syntax.
213
- parse("{ #{sym_name}: :foo }").valid_syntax?
213
+ return false if target_ruby_version <= 2.1
214
+
215
+ (sym_name.start_with?("'") && sym_name.end_with?("'")) ||
216
+ (sym_name.start_with?('"') && sym_name.end_with?('"'))
214
217
  end
218
+ # rubocop:enable Metrics/CyclomaticComplexity
215
219
 
216
220
  def check(pairs, delim, msg)
217
221
  pairs.each do |pair|
@@ -76,9 +76,9 @@ module RuboCop
76
76
  PATTERN
77
77
 
78
78
  def on_send(node)
79
- inverse_candidate?(node) do |_method_call, lhs, method, rhs|
79
+ inverse_candidate?(node) do |method_call, lhs, method, rhs|
80
80
  return unless inverse_methods.key?(method)
81
- return if negated?(node)
81
+ return if negated?(node) || relational_comparison_with_safe_navigation?(method_call)
82
82
  return if part_of_ignored_node?(node)
83
83
  return if possible_class_hierarchy_check?(lhs, rhs, method)
84
84
 
@@ -155,16 +155,16 @@ module RuboCop
155
155
  node.parent.respond_to?(:method?) && node.parent.method?(:!)
156
156
  end
157
157
 
158
+ def relational_comparison_with_safe_navigation?(node)
159
+ node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name)
160
+ end
161
+
158
162
  def not_to_receiver(node, method_call)
159
- Parser::Source::Range.new(node.source_range.source_buffer,
160
- node.loc.selector.begin_pos,
161
- method_call.source_range.begin_pos)
163
+ node.loc.selector.begin.join(method_call.source_range.begin)
162
164
  end
163
165
 
164
166
  def end_parentheses(node, method_call)
165
- Parser::Source::Range.new(node.source_range.source_buffer,
166
- method_call.source_range.end_pos,
167
- node.source_range.end_pos)
167
+ method_call.source_range.end.join(node.source_range.end)
168
168
  end
169
169
 
170
170
  # When comparing classes, `!(Integer < Numeric)` is not the same as
@@ -32,12 +32,14 @@ module RuboCop
32
32
  # foo unless x != y
33
33
  # foo unless x >= 10
34
34
  # foo unless x.even?
35
+ # foo unless odd?
35
36
  #
36
37
  # # good
37
38
  # foo if bar
38
39
  # foo if x == y
39
40
  # foo if x < 10
40
41
  # foo if x.odd?
42
+ # foo if even?
41
43
  #
42
44
  # # bad (complex condition)
43
45
  # foo unless x != y || x.even?
@@ -99,12 +101,15 @@ module RuboCop
99
101
  end
100
102
  end
101
103
 
102
- def preferred_send_condition(node)
103
- receiver_source = node.receiver.source
104
+ def preferred_send_condition(node) # rubocop:disable Metrics/CyclomaticComplexity
105
+ receiver_source = node.receiver&.source
104
106
  return receiver_source if node.method?(:!)
105
107
 
108
+ # receiver may be implicit (self)
109
+ dotted_receiver_source = receiver_source ? "#{receiver_source}." : ''
110
+
106
111
  inverse_method_name = inverse_methods[node.method_name]
107
- return "#{receiver_source}.#{inverse_method_name}" unless node.arguments?
112
+ return "#{dotted_receiver_source}#{inverse_method_name}" unless node.arguments?
108
113
 
109
114
  argument_list = node.arguments.map(&:source).join(', ')
110
115
  if node.operator_method?
@@ -112,10 +117,10 @@ module RuboCop
112
117
  end
113
118
 
114
119
  if node.parenthesized?
115
- return "#{receiver_source}.#{inverse_method_name}(#{argument_list})"
120
+ return "#{dotted_receiver_source}#{inverse_method_name}(#{argument_list})"
116
121
  end
117
122
 
118
- "#{receiver_source}.#{inverse_method_name} #{argument_list}"
123
+ "#{dotted_receiver_source}#{inverse_method_name} #{argument_list}"
119
124
  end
120
125
 
121
126
  def preferred_logical_condition(node)
@@ -116,20 +116,17 @@ module RuboCop
116
116
  def truthy_branch_for_guard?(node)
117
117
  if_node = node.left_sibling
118
118
 
119
- if if_node.if? || if_node.ternary?
120
- if_node.else_branch.nil?
121
- elsif if_node.unless?
122
- if_node.if_branch.nil?
119
+ if if_node.if?
120
+ if_node.if_branch.arguments.any?
121
+ else
122
+ if_node.if_branch.arguments.none?
123
123
  end
124
124
  end
125
125
 
126
126
  def range(node)
127
- buffer = node.source_range.source_buffer
128
127
  map_node = node.receiver.send_node
129
- begin_pos = map_node.loc.selector.begin_pos
130
- end_pos = node.source_range.end_pos
131
128
 
132
- Parser::Source::Range.new(buffer, begin_pos, end_pos)
129
+ map_node.loc.selector.join(node.source_range.end)
133
130
  end
134
131
  end
135
132
  end
@@ -132,7 +132,7 @@ module RuboCop
132
132
  call_in_match_pattern?(node) ||
133
133
  hash_literal_in_arguments?(node) ||
134
134
  node.descendants.any? do |n|
135
- n.forwarded_args_type? || n.block_type? ||
135
+ n.forwarded_args_type? || n.block_type? || n.numblock_type? ||
136
136
  ambiguous_literal?(n) || logical_operator?(n)
137
137
  end
138
138
  end
@@ -38,6 +38,7 @@ module RuboCop
38
38
 
39
39
  private
40
40
 
41
+ # rubocop:disable Metrics/AbcSize
41
42
  def autocorrect(corrector, node, begin_of_arguments)
42
43
  arguments = node.arguments
43
44
  joined_arguments = arguments.map(&:source).join(', ')
@@ -49,9 +50,17 @@ module RuboCop
49
50
  corrector.remove(range_by_whole_lines(arguments.loc.end, include_final_newline: true))
50
51
  end
51
52
 
52
- corrector.remove(arguments_range(node))
53
+ arguments_range = arguments_range(node)
54
+ # If the method name isn't on the same line as def, move it directly after def
55
+ if arguments_range.first_line != opening_line(node)
56
+ corrector.remove(node.loc.name)
57
+ corrector.insert_after(node.loc.keyword, " #{node.loc.name.source}")
58
+ end
59
+
60
+ corrector.remove(arguments_range)
53
61
  corrector.insert_after(begin_of_arguments, joined_arguments)
54
62
  end
63
+ # rubocop:enable Metrics/AbcSize
55
64
 
56
65
  def last_line_source_of_arguments(arguments)
57
66
  processed_source[arguments.last_line - 1].strip
@@ -47,7 +47,11 @@ module RuboCop
47
47
  message = enforce_single_line_ternary_operator?(node) ? MSG_SINGLE_LINE : MSG_IF
48
48
 
49
49
  add_offense(node, message: message) do |corrector|
50
+ next if part_of_ignored_node?(node)
51
+
50
52
  autocorrect(corrector, node)
53
+
54
+ ignore_node(node)
51
55
  end
52
56
  end
53
57
 
@@ -44,6 +44,8 @@ module RuboCop
44
44
  def_node_matcher :nil_check?, '(send _ :nil?)'
45
45
 
46
46
  def on_send(node)
47
+ return unless node.receiver
48
+
47
49
  style_check?(node) do
48
50
  add_offense(node.loc.selector) do |corrector|
49
51
  new_code = if prefer_comparison?
@@ -46,15 +46,17 @@ module RuboCop
46
46
  private
47
47
 
48
48
  def check_method_node(node)
49
- return unless preferred_method(node)
49
+ return unless preferred_method?(node)
50
50
 
51
51
  message = message(node)
52
52
  add_offense(node.loc.selector, message: message) do |corrector|
53
- corrector.replace(node.loc.selector, style.to_s)
53
+ prefer = style == :then && node.receiver.nil? ? 'self.then' : style
54
+
55
+ corrector.replace(node.loc.selector, prefer)
54
56
  end
55
57
  end
56
58
 
57
- def preferred_method(node)
59
+ def preferred_method?(node)
58
60
  case style
59
61
  when :then
60
62
  node.method?(:yield_self)
@@ -289,9 +289,7 @@ module RuboCop
289
289
  private
290
290
 
291
291
  def modifier_range(node)
292
- Parser::Source::Range.new(node.source_range.source_buffer,
293
- node.loc.keyword.begin_pos,
294
- node.source_range.end_pos)
292
+ node.loc.keyword.join(node.source_range.end)
295
293
  end
296
294
  end
297
295
  end
@@ -16,6 +16,9 @@ module RuboCop
16
16
  # The exploded style has an `AllowedCompactTypes` configuration
17
17
  # option that takes an Array of exception name Strings.
18
18
  #
19
+ # @safety
20
+ # This cop is unsafe because `raise Foo` calls `Foo.exception`, not `Foo.new`.
21
+ #
19
22
  # @example EnforcedStyle: exploded (default)
20
23
  # # bad
21
24
  # raise StandardError.new('message')
@@ -77,7 +80,7 @@ module RuboCop
77
80
 
78
81
  def correction_exploded_to_compact(node)
79
82
  exception_node, *message_nodes = *node.arguments
80
- return node.source if message_nodes.size > 1
83
+ return if message_nodes.size > 1
81
84
 
82
85
  argument = message_nodes.first.source
83
86
  exception_class = exception_node.receiver&.source || exception_node.source
@@ -81,14 +81,14 @@ module RuboCop
81
81
  redundant_argument = redundant_arg_for_method(node.method_name.to_s)
82
82
  return false if redundant_argument.nil?
83
83
 
84
- node.first_argument == redundant_argument
84
+ node.first_argument.source.sub(/\A'/, '"').sub(/'\z/, '"') == redundant_argument
85
85
  end
86
86
 
87
87
  def redundant_arg_for_method(method_name)
88
88
  arg = cop_config['Methods'].fetch(method_name) { return }
89
89
 
90
90
  @mem ||= {}
91
- @mem[method_name] ||= parse(arg.inspect).ast
91
+ @mem[method_name] ||= arg.inspect
92
92
  end
93
93
 
94
94
  def argument_range(node)