rubocop 1.56.2 → 1.57.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +12 -6
  4. data/lib/rubocop/cli.rb +1 -1
  5. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  6. data/lib/rubocop/cop/bundler/duplicated_group.rb +1 -1
  7. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -22
  8. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  9. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  10. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  11. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +41 -8
  12. data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
  13. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  14. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  15. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  16. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  17. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  18. data/lib/rubocop/cop/lint/debugger.rb +10 -1
  19. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  20. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  21. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  22. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
  23. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  24. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
  25. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
  26. data/lib/rubocop/cop/lint/useless_assignment.rb +37 -10
  27. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  28. data/lib/rubocop/cop/metrics/class_length.rb +2 -2
  29. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  30. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  31. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  32. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
  33. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -6
  34. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  35. data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
  36. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  37. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  38. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  39. data/lib/rubocop/cop/style/for.rb +1 -1
  40. data/lib/rubocop/cop/style/format_string.rb +24 -3
  41. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  42. data/lib/rubocop/cop/style/identical_conditional_branches.rb +17 -3
  43. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  44. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  45. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  46. data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
  47. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  48. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  49. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +86 -5
  50. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  51. data/lib/rubocop/cop/style/redundant_filter_chain.rb +18 -2
  52. data/lib/rubocop/cop/style/redundant_parentheses.rb +21 -5
  53. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  54. data/lib/rubocop/cop/style/single_line_do_end_block.rb +65 -0
  55. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  56. data/lib/rubocop/cop/style/symbol_array.rb +10 -11
  57. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  58. data/lib/rubocop/file_finder.rb +4 -7
  59. data/lib/rubocop/magic_comment.rb +12 -10
  60. data/lib/rubocop/rspec/shared_contexts.rb +2 -3
  61. data/lib/rubocop/server/cache.rb +1 -0
  62. data/lib/rubocop/version.rb +1 -1
  63. data/lib/rubocop.rb +1 -0
  64. metadata +10 -9
@@ -7,12 +7,18 @@ module RuboCop
7
7
  # scope.
8
8
  # The basic idea for this cop was from the warning of `ruby -cw`:
9
9
  #
10
- # assigned but unused variable - foo
10
+ # [source,console]
11
+ # ----
12
+ # assigned but unused variable - foo
13
+ # ----
11
14
  #
12
15
  # Currently this cop has advanced logic that detects unreferenced
13
16
  # reassignments and properly handles varied cases such as branch, loop,
14
17
  # rescue, ensure, etc.
15
18
  #
19
+ # NOTE: Given the assignment `foo = 1, bar = 2`, removing unused variables
20
+ # can lead to a syntax error, so this case is not autocorrected.
21
+ #
16
22
  # @safety
17
23
  # This cop's autocorrection is unsafe because removing assignment from
18
24
  # operator assignment can cause NameError if this assignment has been used to declare
@@ -51,25 +57,24 @@ module RuboCop
51
57
  scope.variables.each_value { |variable| check_for_unused_assignments(variable) }
52
58
  end
53
59
 
60
+ # rubocop:disable Metrics/AbcSize
54
61
  def check_for_unused_assignments(variable)
55
62
  return if variable.should_be_unused?
56
63
 
57
64
  variable.assignments.each do |assignment|
58
- next if assignment.used?
65
+ next if assignment.used? || part_of_ignored_node?(assignment.node)
59
66
 
60
67
  message = message_for_useless_assignment(assignment)
68
+ range = offense_range(assignment)
61
69
 
62
- location = if assignment.regexp_named_capture?
63
- assignment.node.children.first.source_range
64
- else
65
- assignment.node.loc.name
66
- end
67
-
68
- add_offense(location, message: message) do |corrector|
69
- autocorrect(corrector, assignment)
70
+ add_offense(range, message: message) do |corrector|
71
+ autocorrect(corrector, assignment) unless sequential_assignment?(assignment.node)
70
72
  end
73
+
74
+ ignore_node(assignment.node) if chained_assignment?(assignment.node)
71
75
  end
72
76
  end
77
+ # rubocop:enable Metrics/AbcSize
73
78
 
74
79
  def message_for_useless_assignment(assignment)
75
80
  variable = assignment.variable
@@ -77,6 +82,28 @@ module RuboCop
77
82
  format(MSG, variable: variable.name) + message_specification(assignment, variable).to_s
78
83
  end
79
84
 
85
+ def offense_range(assignment)
86
+ if assignment.regexp_named_capture?
87
+ assignment.node.children.first.source_range
88
+ else
89
+ assignment.node.loc.name
90
+ end
91
+ end
92
+
93
+ def sequential_assignment?(node)
94
+ if node.lvasgn_type? && node.expression&.array_type? &&
95
+ node.each_descendant.any?(&:assignment?)
96
+ return true
97
+ end
98
+ return false unless node.parent
99
+
100
+ sequential_assignment?(node.parent)
101
+ end
102
+
103
+ def chained_assignment?(node)
104
+ node.respond_to?(:expression) && node.expression&.lvasgn_type?
105
+ end
106
+
80
107
  def message_specification(assignment, variable)
81
108
  if assignment.multiple_assignment?
82
109
  multiple_assignment_message(variable.name)
@@ -12,6 +12,7 @@ module RuboCop
12
12
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
13
13
  # will be counted as one line regardless of its actual size.
14
14
  #
15
+ # NOTE: This cop does not apply for `Struct` definitions.
15
16
  #
16
17
  # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
17
18
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
@@ -40,7 +41,6 @@ module RuboCop
40
41
  # )
41
42
  # end # 6 points
42
43
  #
43
- # NOTE: This cop does not apply for `Struct` definitions.
44
44
  class BlockLength < Base
45
45
  include CodeLength
46
46
  include AllowedMethods
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
+ # NOTE: This cop also applies for `Struct` definitions.
15
+ #
14
16
  # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
17
  #
16
18
  # class Foo
@@ -34,8 +36,6 @@ module RuboCop
34
36
  # )
35
37
  # end # 6 points
36
38
  #
37
- #
38
- # NOTE: This cop also applies for `Struct` definitions.
39
39
  class ClassLength < Base
40
40
  include CodeLength
41
41
 
@@ -54,7 +54,7 @@ module RuboCop
54
54
  alias on_defs on_def
55
55
 
56
56
  def on_block(node)
57
- return unless node.send_node.method?(:define_method)
57
+ return unless node.method?(:define_method)
58
58
 
59
59
  check_code_length(node)
60
60
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module sclass].freeze
13
+ CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -145,7 +145,7 @@ module RuboCop
145
145
 
146
146
  def extract_body(node)
147
147
  case node.type
148
- when :class, :module, :block, :numblock, :def, :defs
148
+ when :class, :module, :sclass, :block, :numblock, :def, :defs
149
149
  node.body
150
150
  when :casgn
151
151
  _scope, _name, value = *node
@@ -20,16 +20,17 @@ module RuboCop
20
20
  range = offending_range(node, lhs, rhs, style)
21
21
  check(range, node, lhs, rhs)
22
22
  end
23
+ alias on_csend on_send
23
24
 
24
25
  private
25
26
 
26
- # In a chain of method calls, we regard the top send node as the base
27
+ # In a chain of method calls, we regard the top call node as the base
27
28
  # for indentation of all lines following the first. For example:
28
29
  # a.
29
30
  # b c { block }. <-- b is indented relative to a
30
31
  # d <-- d is indented relative to a
31
32
  def left_hand_side(lhs)
32
- while lhs.parent&.send_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
33
+ while lhs.parent&.call_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
33
34
  lhs = lhs.parent
34
35
  end
35
36
  lhs
@@ -75,7 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def aligned_token?(range, line)
78
- aligned_words?(range, line) || aligned_dot?(range, line) || aligned_assignment?(range, line)
78
+ aligned_words?(range, line) || aligned_assignment?(range, line)
79
79
  end
80
80
 
81
81
  def aligned_operator?(range, line)
@@ -83,13 +83,11 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def aligned_words?(range, line)
86
- /\s\S/.match?(line[range.column - 1, 2])
87
- end
88
-
89
- def aligned_dot?(range, line)
90
- char = line[range.column]
86
+ left_edge = range.column
87
+ return true if /\s\S/.match?(line[left_edge - 1, 2])
91
88
 
92
- char == '.' && char == range.source[0]
89
+ token = range.source
90
+ token == line[left_edge, token.length]
93
91
  end
94
92
 
95
93
  def aligned_assignment?(range, line)
@@ -116,11 +116,11 @@ module RuboCop
116
116
  end
117
117
 
118
118
  def only_forwards_all?(send_classifications)
119
- send_classifications.each_value.all? { |c, _, _| c == :all }
119
+ send_classifications.all? { |_, c, _, _| c == :all }
120
120
  end
121
121
 
122
122
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
123
- send_classifications.each do |send_node, (_c, forward_rest, _forward_kwrest)|
123
+ send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
124
124
  register_forward_all_offense(send_node, send_node, forward_rest)
125
125
  end
126
126
 
@@ -133,7 +133,7 @@ module RuboCop
133
133
 
134
134
  rest_arg, kwrest_arg, _block_arg = *forwardable_args
135
135
 
136
- send_classifications.each do |send_node, (_c, forward_rest, forward_kwrest)|
136
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
137
137
  if forward_rest
138
138
  register_forward_args_offense(def_node.arguments, rest_arg)
139
139
  register_forward_args_offense(send_node, forward_rest)
@@ -157,7 +157,7 @@ module RuboCop
157
157
  end
158
158
 
159
159
  def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
160
- send_nodes.to_h do |send_node|
160
+ send_nodes.filter_map do |send_node|
161
161
  classification_and_forwards = classification_and_forwards(
162
162
  def_node,
163
163
  send_node,
@@ -165,8 +165,10 @@ module RuboCop
165
165
  forwardable_args
166
166
  )
167
167
 
168
- [send_node, classification_and_forwards]
169
- end.compact
168
+ next unless classification_and_forwards
169
+
170
+ [send_node, *classification_and_forwards]
171
+ end
170
172
  end
171
173
 
172
174
  def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
@@ -11,6 +11,15 @@ module RuboCop
11
11
  # The `array1.intersect?(array2)` method is faster than
12
12
  # `(array1 & array2).any?` and is more readable.
13
13
  #
14
+ # In cases like the following, compatibility is not ensured,
15
+ # so it will not be detected when using block argument.
16
+ #
17
+ # [source,ruby]
18
+ # ----
19
+ # ([1] & [1,2]).any? { |x| false } # => false
20
+ # [1].intersect?([1,2]) { |x| false } # => true
21
+ # ----
22
+ #
14
23
  # @safety
15
24
  # This cop cannot guarantee that `array1` and `array2` are
16
25
  # actually arrays while method `intersect?` is for arrays only.
@@ -68,16 +77,15 @@ module RuboCop
68
77
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
69
78
 
70
79
  def on_send(node)
80
+ return if (parent = node.parent) && (parent.block_type? || parent.numblock_type?)
71
81
  return unless (receiver, argument, method_name = bad_intersection_check?(node))
72
82
 
73
83
  message = message(receiver.source, argument.source, method_name)
74
84
 
75
85
  add_offense(node, message: message) do |corrector|
76
- if straight?(method_name)
77
- corrector.replace(node, "#{receiver.source}.intersect?(#{argument.source})")
78
- else
79
- corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
80
- end
86
+ bang = straight?(method_name) ? '' : '!'
87
+
88
+ corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
81
89
  end
82
90
  end
83
91
 
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # `==`, `equal?`, and `eql?` custom method definitions are allowed by default.
9
9
  # These are customizable with `AllowedMethods` option.
10
10
  #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because there is no guarantee that
13
+ # the constant `Foo` exists when autocorrecting `var.class.name == 'Foo'` to
14
+ # `var.instance_of?(Foo)`.
15
+ #
11
16
  # @example
12
17
  # # bad
13
18
  # var.class == Date
@@ -25,6 +25,7 @@ module RuboCop
25
25
  # # bad
26
26
  # items.collect
27
27
  # items.collect!
28
+ # items.collect_concat
28
29
  # items.inject
29
30
  # items.detect
30
31
  # items.find_all
@@ -33,6 +34,7 @@ module RuboCop
33
34
  # # good
34
35
  # items.map
35
36
  # items.map!
37
+ # items.flat_map
36
38
  # items.reduce
37
39
  # items.find
38
40
  # items.select
@@ -93,8 +93,9 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def same_collection_looping_block?(node, sibling)
96
- (sibling&.block_type? || sibling&.numblock_type?) &&
97
- sibling.send_node.method?(node.method_name) &&
96
+ return false if sibling.nil? || (!sibling.block_type? && !sibling.numblock_type?)
97
+
98
+ sibling.method?(node.method_name) &&
98
99
  sibling.receiver == node.receiver &&
99
100
  sibling.send_node.arguments == node.send_node.arguments
100
101
  end
@@ -40,9 +40,13 @@ module RuboCop
40
40
  extend AutoCorrector
41
41
 
42
42
  MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
43
+ NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend].freeze
43
44
 
45
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
44
46
  def on_case(case_node)
45
- return if case_node.condition
47
+ if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
48
+ return
49
+ end
46
50
 
47
51
  branch_bodies = [*case_node.when_branches.map(&:body), case_node.else_branch].compact
48
52
 
@@ -52,6 +56,7 @@ module RuboCop
52
56
 
53
57
  add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
54
58
  end
59
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
55
60
 
56
61
  private
57
62
 
@@ -80,7 +80,7 @@ module RuboCop
80
80
  private
81
81
 
82
82
  def suspect_enumerable?(node)
83
- node.multiline? && node.send_node.method?(:each) && !node.send_node.arguments?
83
+ node.multiline? && node.method?(:each) && !node.send_node.arguments?
84
84
  end
85
85
  end
86
86
  end
@@ -4,13 +4,25 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Enforces the use of a single string formatting utility.
7
- # Valid options include Kernel#format, Kernel#sprintf and String#%.
7
+ # Valid options include `Kernel#format`, `Kernel#sprintf`, and `String#%`.
8
8
  #
9
- # The detection of String#% cannot be implemented in a reliable
9
+ # The detection of `String#%` cannot be implemented in a reliable
10
10
  # manner for all cases, so only two scenarios are considered -
11
11
  # if the first argument is a string literal and if the second
12
12
  # argument is an array literal.
13
13
  #
14
+ # Autocorrection will be applied when using argument is a literal or known built-in conversion
15
+ # methods such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`, and `to_sym` on variables,
16
+ # provided that their return value is not an array. For example, when using `to_s`,
17
+ # `'%s' % [1, 2, 3].to_s` can be autocorrected without any incompatibility:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # '%s' % [1, 2, 3] #=> '1'
22
+ # format('%s', [1, 2, 3]) #=> '[1, 2, 3]'
23
+ # '%s' % [1, 2, 3].to_s #=> '[1, 2, 3]'
24
+ # ----
25
+ #
14
26
  # @example EnforcedStyle: format (default)
15
27
  # # bad
16
28
  # puts sprintf('%10s', 'hoge')
@@ -42,6 +54,9 @@ module RuboCop
42
54
  MSG = 'Favor `%<prefer>s` over `%<current>s`.'
43
55
  RESTRICT_ON_SEND = %i[format sprintf %].freeze
44
56
 
57
+ # Known conversion methods whose return value is not an array.
58
+ AUTOCORRECTABLE_METHODS = %i[to_d to_f to_h to_i to_r to_s to_sym].freeze
59
+
45
60
  # @!method formatter(node)
46
61
  def_node_matcher :formatter, <<~PATTERN
47
62
  {
@@ -53,7 +68,7 @@ module RuboCop
53
68
 
54
69
  # @!method variable_argument?(node)
55
70
  def_node_matcher :variable_argument?, <<~PATTERN
56
- (send {str dstr} :% {send_type? lvar_type?})
71
+ (send {str dstr} :% #autocorrectable?)
57
72
  PATTERN
58
73
 
59
74
  def on_send(node)
@@ -70,6 +85,12 @@ module RuboCop
70
85
 
71
86
  private
72
87
 
88
+ def autocorrectable?(node)
89
+ return true if node.lvar_type?
90
+
91
+ node.send_type? && !AUTOCORRECTABLE_METHODS.include?(node.method_name)
92
+ end
93
+
73
94
  def message(detected_style)
74
95
  format(MSG, prefer: method_name(style), current: method_name(detected_style))
75
96
  end
@@ -55,6 +55,25 @@ module RuboCop
55
55
  # foo || raise('exception') if something
56
56
  # ok
57
57
  #
58
+ # # bad
59
+ # define_method(:test) do
60
+ # if something
61
+ # work
62
+ # end
63
+ # end
64
+ #
65
+ # # good
66
+ # define_method(:test) do
67
+ # return unless something
68
+ #
69
+ # work
70
+ # end
71
+ #
72
+ # # also good
73
+ # define_method(:test) do
74
+ # work if something
75
+ # end
76
+ #
58
77
  # @example AllowConsecutiveConditionals: false (default)
59
78
  # # bad
60
79
  # def test
@@ -110,6 +129,13 @@ module RuboCop
110
129
  end
111
130
  alias on_defs on_def
112
131
 
132
+ def on_block(node)
133
+ return unless node.method?(:define_method) || node.method?(:define_singleton_method)
134
+
135
+ on_def(node)
136
+ end
137
+ alias on_numblock on_block
138
+
113
139
  def on_if(node)
114
140
  return if accepted_form?(node)
115
141
 
@@ -136,7 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
140
140
  def check_branches(node, branches)
141
141
  # return if any branch is empty. An empty branch can be an `if`
142
142
  # without an `else` or a branch that contains only comments.
@@ -149,9 +149,15 @@ module RuboCop
149
149
  branches.any? { |branch| single_child_branch?(branch) }
150
150
 
151
151
  heads = branches.map { |branch| head(branch) }
152
- check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
152
+
153
+ return unless duplicated_expressions?(node, heads)
154
+
155
+ condition_variable = assignable_condition_value(node)
156
+ return if heads.first.assignment? && condition_variable == heads.first.name.to_s
157
+
158
+ check_expressions(node, heads, :before_condition)
153
159
  end
154
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
160
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
161
 
156
162
  def duplicated_expressions?(node, expressions)
157
163
  unique_expressions = expressions.uniq
@@ -164,6 +170,14 @@ module RuboCop
164
170
  node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
165
171
  end
166
172
 
173
+ def assignable_condition_value(node)
174
+ if node.condition.call_type?
175
+ (receiver = node.condition.receiver) ? receiver.source : node.condition.source
176
+ elsif node.condition.variable?
177
+ node.condition.source
178
+ end
179
+ end
180
+
167
181
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
168
182
  def check_expressions(node, expressions, insert_position)
169
183
  return if expressions.any?(&:nil?)
@@ -28,7 +28,7 @@ module RuboCop
28
28
  MSG = 'Avoid multi-line chains of blocks.'
29
29
 
30
30
  def on_block(node)
31
- node.send_node.each_node(:send) do |send_node|
31
+ node.send_node.each_node(:send, :csend) do |send_node|
32
32
  receiver = send_node.receiver
33
33
 
34
34
  next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
41
41
  MSG_SINGLE_LINE = 'Avoid multi-line ternary operators, use single-line instead.'
42
- SINGLE_LINE_TYPES = %i[return break next send].freeze
42
+ SINGLE_LINE_TYPES = %i[return break next send csend].freeze
43
43
 
44
44
  def on_if(node)
45
45
  return unless offense?(node)
@@ -27,24 +27,16 @@ module RuboCop
27
27
 
28
28
  node.each_descendant(:if).select(&:ternary?).each do |nested_ternary|
29
29
  add_offense(nested_ternary) do |corrector|
30
- if_node = if_node(nested_ternary)
31
- next if part_of_ignored_node?(if_node)
30
+ next if part_of_ignored_node?(node)
32
31
 
33
- autocorrect(corrector, if_node)
34
- ignore_node(if_node)
32
+ autocorrect(corrector, node)
33
+ ignore_node(node)
35
34
  end
36
35
  end
37
36
  end
38
37
 
39
38
  private
40
39
 
41
- def if_node(node)
42
- node = node.parent
43
- return node if node.if_type?
44
-
45
- if_node(node)
46
- end
47
-
48
40
  def autocorrect(corrector, if_node)
49
41
  replace_loc_and_whitespace(corrector, if_node.loc.question, "\n")
50
42
  replace_loc_and_whitespace(corrector, if_node.loc.colon, "\nelse\n")
@@ -23,6 +23,7 @@ module RuboCop
23
23
  MSG = 'Redundant dot detected.'
24
24
  RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
25
25
 
26
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
26
27
  def on_send(node)
27
28
  return unless (dot = node.loc.dot)
28
29
  return if node.receiver.const_type? || !node.arguments.one?
@@ -33,8 +34,12 @@ module RuboCop
33
34
  add_offense(dot) do |corrector|
34
35
  wrap_in_parentheses_if_chained(corrector, node)
35
36
  corrector.replace(dot, ' ')
37
+
38
+ selector = node.loc.selector
39
+ corrector.insert_after(selector, ' ') if selector.end_pos == rhs.source_range.begin_pos
36
40
  end
37
41
  end
42
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
38
43
 
39
44
  private
40
45
 
@@ -54,6 +59,7 @@ module RuboCop
54
59
 
55
60
  def wrap_in_parentheses_if_chained(corrector, node)
56
61
  return unless node.parent&.call_type?
62
+ return if node.parent.first_argument == node
57
63
 
58
64
  operator = node.loc.selector
59
65
 
@@ -114,7 +114,7 @@ module RuboCop
114
114
  if node.parent&.assignment?
115
115
  replace_begin_with_statement(corrector, offense_range, node)
116
116
  else
117
- corrector.remove(offense_range)
117
+ remove_begin(corrector, offense_range, node)
118
118
  end
119
119
 
120
120
  if use_modifier_form_after_multiline_begin_block?(node)
@@ -136,6 +136,14 @@ module RuboCop
136
136
  restore_removed_comments(corrector, offense_range, node, first_child)
137
137
  end
138
138
 
139
+ def remove_begin(corrector, offense_range, node)
140
+ if node.parent.respond_to?(:endless?) && node.parent.endless?
141
+ offense_range = range_with_surrounding_space(offense_range, newlines: true)
142
+ end
143
+
144
+ corrector.remove(offense_range)
145
+ end
146
+
139
147
  # Restore comments that occur between "begin" and "first_child".
140
148
  # These comments will be moved to above the assignment line.
141
149
  def restore_removed_comments(corrector, offense_range, node, first_child)
@@ -70,19 +70,11 @@ module RuboCop
70
70
 
71
71
  def replacement_condition(node)
72
72
  condition = node.condition.source
73
- expression = invert_expression?(node) ? "!(#{condition})" : condition
73
+ expression = redundant_condition_inverted?(node) ? "!(#{condition})" : condition
74
74
 
75
75
  node.elsif? ? indented_else_node(expression, node) : expression
76
76
  end
77
77
 
78
- def invert_expression?(node)
79
- (
80
- (node.if? || node.elsif? || node.ternary?) && redundant_condition_inverted?(node)
81
- ) || (
82
- node.unless? && redundant_condition?(node)
83
- )
84
- end
85
-
86
78
  def indented_else_node(expression, node)
87
79
  "else\n#{indentation(node)}#{expression}"
88
80
  end