rubocop 1.60.2 → 1.61.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (62) 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 +35 -12
  6. data/lib/rubocop/cli/command/lsp.rb +2 -2
  7. data/lib/rubocop/cli.rb +6 -1
  8. data/lib/rubocop/config_finder.rb +12 -2
  9. data/lib/rubocop/config_validator.rb +14 -5
  10. data/lib/rubocop/cop/autocorrect_logic.rb +6 -1
  11. data/lib/rubocop/cop/base.rb +12 -7
  12. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  13. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  14. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +2 -0
  15. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  16. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  17. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  18. data/lib/rubocop/cop/layout/end_alignment.rb +3 -1
  19. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  20. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  21. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  22. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -9
  23. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  24. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  25. data/lib/rubocop/cop/lint/syntax.rb +1 -1
  26. data/lib/rubocop/cop/lint/void.rb +6 -1
  27. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  28. data/lib/rubocop/cop/registry.rb +1 -1
  29. data/lib/rubocop/cop/style/arguments_forwarding.rb +29 -8
  30. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  31. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  32. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -5
  33. data/lib/rubocop/cop/style/hash_syntax.rb +6 -2
  34. data/lib/rubocop/cop/style/inverse_methods.rb +8 -8
  35. data/lib/rubocop/cop/style/invertible_unless_condition.rb +10 -5
  36. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +5 -8
  37. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  38. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +4 -0
  39. data/lib/rubocop/cop/style/object_then.rb +5 -3
  40. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
  41. data/lib/rubocop/cop/style/raise_args.rb +3 -0
  42. data/lib/rubocop/cop/style/redundant_argument.rb +2 -2
  43. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  44. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -5
  45. data/lib/rubocop/cop/style/redundant_return.rb +6 -0
  46. data/lib/rubocop/cop/style/sample.rb +1 -3
  47. data/lib/rubocop/cops_documentation_generator.rb +4 -2
  48. data/lib/rubocop/formatter/html_formatter.rb +30 -10
  49. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  50. data/lib/rubocop/lsp/logger.rb +1 -1
  51. data/lib/rubocop/lsp/routes.rb +1 -1
  52. data/lib/rubocop/lsp/runtime.rb +1 -1
  53. data/lib/rubocop/lsp/server.rb +5 -2
  54. data/lib/rubocop/lsp/severity.rb +1 -1
  55. data/lib/rubocop/lsp.rb +29 -0
  56. data/lib/rubocop/options.rb +11 -0
  57. data/lib/rubocop/path_util.rb +6 -2
  58. data/lib/rubocop/rspec/shared_contexts.rb +10 -6
  59. data/lib/rubocop/rspec/support.rb +1 -1
  60. data/lib/rubocop/target_ruby.rb +79 -79
  61. data/lib/rubocop/version.rb +1 -1
  62. metadata +11 -8
@@ -34,18 +34,14 @@ module RuboCop
34
34
  end
35
35
 
36
36
  def offending_range
37
+ begin_range = block_node.source_range.begin
38
+
37
39
  if block_node.arguments?
38
- replacement_range(argument_node.source_range.end_pos)
40
+ begin_range.join(argument_node.source_range.end)
39
41
  else
40
- replacement_range(block_node.loc.begin.end_pos)
42
+ begin_range.join(block_node.loc.begin.end)
41
43
  end
42
44
  end
43
-
44
- def replacement_range(end_pos)
45
- Parser::Source::Range.new(block_node.source_range.source_buffer,
46
- block_node.source_range.begin_pos,
47
- end_pos)
48
- end
49
45
  end
50
46
  end
51
47
  end
@@ -15,6 +15,8 @@ module RuboCop
15
15
  end
16
16
 
17
17
  def call(corrector)
18
+ offending_range = for_node.source_range.begin.join(end_range)
19
+
18
20
  corrector.replace(offending_range, correction)
19
21
  end
20
22
 
@@ -40,11 +42,11 @@ module RuboCop
40
42
  collection_node.range_type? || collection_node.or_type? || collection_node.and_type?
41
43
  end
42
44
 
43
- def end_position
45
+ def end_range
44
46
  if for_node.do?
45
- keyword_begin.end_pos
47
+ keyword_begin.end
46
48
  else
47
- collection_end.end_pos
49
+ collection_end.end
48
50
  end
49
51
  end
50
52
 
@@ -59,16 +61,6 @@ module RuboCop
59
61
  collection_node.source_range
60
62
  end
61
63
  end
62
-
63
- def offending_range
64
- replacement_range(end_position)
65
- end
66
-
67
- def replacement_range(end_pos)
68
- Parser::Source::Range.new(for_node.source_range.source_buffer,
69
- for_node.source_range.begin_pos,
70
- end_pos)
71
- end
72
64
  end
73
65
  end
74
66
  end
@@ -110,6 +110,8 @@ module RuboCop
110
110
  end
111
111
  end
112
112
 
113
+ return unless required_ruby_version
114
+
113
115
  required_ruby_version.str_content.scan(/\d/).first(2).join('.')
114
116
  end
115
117
 
@@ -30,6 +30,7 @@ module RuboCop
30
30
  extend AutoCorrector
31
31
 
32
32
  MSG = 'Use `%<method_name>s` instead of `%<method_suffix>s`.'
33
+ RESTRICT_ON_SEND = %i[end_with?].freeze
33
34
  SUGGEST_METHOD_FOR_SUFFIX = {
34
35
  '=' => 'assignment_method?',
35
36
  '!' => 'bang_method?',
@@ -51,14 +52,15 @@ module RuboCop
51
52
 
52
53
  def on_send(node)
53
54
  method_name_end_with?(node) do |method_name_node, end_with_arg|
55
+ next unless method_name_node.receiver
56
+
57
+ preferred_method = SUGGEST_METHOD_FOR_SUFFIX[end_with_arg.value]
54
58
  range = range(method_name_node, node)
55
- message = format(
56
- MSG,
57
- method_name: SUGGEST_METHOD_FOR_SUFFIX[end_with_arg.value],
58
- method_suffix: range.source
59
- )
59
+ message = format(MSG, method_name: preferred_method, method_suffix: range.source)
60
60
 
61
- add_offense(range, message: message)
61
+ add_offense(range, message: message) do |corrector|
62
+ corrector.replace(range, preferred_method)
63
+ end
62
64
  end
63
65
  end
64
66
  alias on_csend on_send
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks for redundant arguments of `RuboCop::RSpec::ExpectOffense`'s methods.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # expect_no_offenses('code', keyword: keyword)
12
+ #
13
+ # # good
14
+ # expect_no_offenses('code')
15
+ #
16
+ class RedundantExpectOffenseArguments < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Remove the redundant arguments.'
20
+ RESTRICT_ON_SEND = %i[expect_no_offenses].freeze
21
+
22
+ def on_send(node)
23
+ return if node.arguments.one? || !node.arguments[1]&.hash_type?
24
+
25
+ range = node.first_argument.source_range.end.join(node.last_argument.source_range.end)
26
+
27
+ add_offense(range) do |corrector|
28
+ corrector.remove(range)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -20,6 +20,7 @@ require_relative 'internal_affairs/offense_location_keyword'
20
20
  require_relative 'internal_affairs/processed_source_buffer_name'
21
21
  require_relative 'internal_affairs/redundant_context_config_parameter'
22
22
  require_relative 'internal_affairs/redundant_described_class_as_subject'
23
+ require_relative 'internal_affairs/redundant_expect_offense_arguments'
23
24
  require_relative 'internal_affairs/redundant_let_rubocop_config_new'
24
25
  require_relative 'internal_affairs/redundant_location_argument'
25
26
  require_relative 'internal_affairs/redundant_message_argument'
@@ -20,7 +20,9 @@ module RuboCop
20
20
  # This `Layout/EndAlignment` cop aligns with keywords (e.g. `if`, `while`, `case`)
21
21
  # by default. On the other hand, `Layout/BeginEndAlignment` cop aligns with
22
22
  # `EnforcedStyleAlignWith: start_of_line` by default due to `||= begin` tends
23
- # to align with the start of the line. These style can be configured by each cop.
23
+ # to align with the start of the line. `Layout/DefEndAlignment` cop also aligns with
24
+ # `EnforcedStyleAlignWith: start_of_line` by default.
25
+ # These style can be configured by each cop.
24
26
  #
25
27
  # @example EnforcedStyleAlignWith: keyword (default)
26
28
  # # bad
@@ -89,7 +89,9 @@ module RuboCop
89
89
  end
90
90
 
91
91
  def index_access_call_chained?(node)
92
- node.send_type? && node.method?(:[]) && node.children.first.method?(:[])
92
+ return false unless node.send_type? && node.method?(:[])
93
+
94
+ node.children.first.send_type? && node.children.first.method?(:[])
93
95
  end
94
96
 
95
97
  def configured_to_not_be_inspected?(node)
@@ -81,19 +81,28 @@ module RuboCop
81
81
  private
82
82
 
83
83
  def check_empty(left_brace, space_plus_brace, used_style)
84
- return if style_for_empty_braces == used_style
85
-
86
- config_to_allow_offenses['EnforcedStyleForEmptyBraces'] = used_style.to_s
84
+ if style_for_empty_braces == used_style
85
+ handle_different_styles_for_empty_braces(used_style)
86
+ return
87
+ elsif !config_to_allow_offenses.key?('Enabled')
88
+ config_to_allow_offenses['EnforcedStyleForEmptyBraces'] = used_style.to_s
89
+ end
87
90
 
88
91
  if style_for_empty_braces == :space
89
- add_offense(left_brace, message: MISSING_MSG) do |corrector|
90
- autocorrect(corrector, left_brace)
91
- end
92
+ range = left_brace
93
+ msg = MISSING_MSG
92
94
  else
93
- space = range_between(space_plus_brace.begin_pos, left_brace.begin_pos)
94
- add_offense(space, message: DETECTED_MSG) do |corrector|
95
- autocorrect(corrector, space)
96
- end
95
+ range = range_between(space_plus_brace.begin_pos, left_brace.begin_pos)
96
+ msg = DETECTED_MSG
97
+ end
98
+ add_offense(range, message: msg) { |corrector| autocorrect(corrector, range) }
99
+ end
100
+
101
+ def handle_different_styles_for_empty_braces(used_style)
102
+ if config_to_allow_offenses['EnforcedStyleForEmptyBraces'] &&
103
+ config_to_allow_offenses['EnforcedStyleForEmptyBraces'].to_sym != used_style
104
+ config_to_allow_offenses.clear
105
+ config_to_allow_offenses['Enabled'] = false
97
106
  end
98
107
  end
99
108
 
@@ -104,7 +104,7 @@ module RuboCop
104
104
  def correct_other_branches(corrector, node)
105
105
  return unless require_other_branches_correction?(node)
106
106
 
107
- if node.else_branch&.if_type?
107
+ if node.else_branch&.if_type? && !node.else_branch.modifier_form?
108
108
  # Replace an orphaned `elsif` with `if`
109
109
  corrector.replace(node.else_branch.loc.keyword, 'if')
110
110
  else
@@ -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
@@ -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; " \
@@ -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
@@ -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
@@ -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)
@@ -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