rubocop 1.57.1 → 1.58.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +37 -2
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  5. data/lib/rubocop/config_obsoletion.rb +11 -8
  6. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  7. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  8. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  9. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  10. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  11. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  12. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  13. data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
  14. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  15. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +6 -6
  16. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  17. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  18. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  19. data/lib/rubocop/cop/layout/single_line_block_chain.rb +4 -0
  20. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  21. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  22. data/lib/rubocop/cop/lint/debugger.rb +2 -1
  23. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  24. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  25. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  26. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +64 -0
  27. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  28. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
  29. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  30. data/lib/rubocop/cop/lint/self_assignment.rb +37 -0
  31. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  32. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  33. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  34. data/lib/rubocop/cop/lint/void.rb +43 -12
  35. data/lib/rubocop/cop/metrics/class_length.rb +6 -1
  36. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  37. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  38. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  39. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  40. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  41. data/lib/rubocop/cop/naming/block_forwarding.rb +2 -2
  42. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  43. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  44. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  45. data/lib/rubocop/cop/style/arguments_forwarding.rb +68 -6
  46. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  47. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  48. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  49. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  50. data/lib/rubocop/cop/style/combinable_loops.rb +2 -7
  51. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  52. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  53. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  54. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  55. data/lib/rubocop/cop/style/hash_each_methods.rb +58 -10
  56. data/lib/rubocop/cop/style/identical_conditional_branches.rb +9 -1
  57. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  58. data/lib/rubocop/cop/style/map_to_hash.rb +9 -4
  59. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
  60. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  61. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  62. data/lib/rubocop/cop/style/redundant_argument.rb +2 -2
  63. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +17 -10
  64. data/lib/rubocop/cop/style/redundant_filter_chain.rb +4 -3
  65. data/lib/rubocop/cop/style/redundant_line_continuation.rb +2 -0
  66. data/lib/rubocop/cop/style/redundant_parentheses.rb +28 -12
  67. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  68. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  69. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  70. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -1
  71. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  72. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  73. data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
  74. data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
  75. data/lib/rubocop/cop/style/slicing_with_range.rb +1 -1
  76. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  77. data/lib/rubocop/formatter/html_formatter.rb +5 -4
  78. data/lib/rubocop/result_cache.rb +0 -1
  79. data/lib/rubocop/runner.rb +1 -1
  80. data/lib/rubocop/version.rb +1 -1
  81. data/lib/rubocop.rb +3 -0
  82. metadata +13 -23
@@ -17,10 +17,16 @@ module RuboCop
17
17
  # @example
18
18
  # # bad
19
19
  # hash.keys.each { |k| p k }
20
- # hash.values.each { |v| p v }
20
+ # hash.each { |k, unused_value| p k }
21
21
  #
22
22
  # # good
23
23
  # hash.each_key { |k| p k }
24
+ #
25
+ # # bad
26
+ # hash.values.each { |v| p v }
27
+ # hash.each { |unused_key, v| p v }
28
+ #
29
+ # # good
24
30
  # hash.each_value { |v| p v }
25
31
  #
26
32
  # @example AllowedReceivers: ['execute']
@@ -33,22 +39,44 @@ module RuboCop
33
39
  extend AutoCorrector
34
40
 
35
41
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
42
+ UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
36
43
 
37
44
  # @!method kv_each(node)
38
45
  def_node_matcher :kv_each, <<~PATTERN
39
- ({block numblock} $(send (send _ ${:keys :values}) :each) ...)
46
+ ({block numblock} $(call (call _ ${:keys :values}) :each) ...)
47
+ PATTERN
48
+
49
+ # @!method each_arguments(node)
50
+ def_node_matcher :each_arguments, <<~PATTERN
51
+ (block (call _ :each)(args $_key $_value) ...)
40
52
  PATTERN
41
53
 
42
54
  # @!method kv_each_with_block_pass(node)
43
55
  def_node_matcher :kv_each_with_block_pass, <<~PATTERN
44
- (send $(send _ ${:keys :values}) :each (block_pass (sym _)))
56
+ (call $(call _ ${:keys :values}) :each (block_pass (sym _)))
45
57
  PATTERN
46
58
 
59
+ # rubocop:disable Metrics/AbcSize
47
60
  def on_block(node)
48
61
  kv_each(node) do |target, method|
49
- register_kv_offense(target, method)
62
+ register_kv_offense(target, method) and return
63
+ end
64
+
65
+ return unless (key, value = each_arguments(node))
66
+
67
+ if unused_block_arg_exist?(node, value.source)
68
+ message = message('each_key', node.method_name, value.source)
69
+ unused_range = key.source_range.end.join(value.source_range.end)
70
+
71
+ register_each_args_offense(node, message, 'each_key', unused_range)
72
+ elsif unused_block_arg_exist?(node, key.source)
73
+ message = message('each_value', node.method_name, key.source)
74
+ unused_range = key.source_range.begin.join(value.source_range.begin)
75
+
76
+ register_each_args_offense(node, message, 'each_value', unused_range)
50
77
  end
51
78
  end
79
+ # rubocop:enable Metrics/AbcSize
52
80
 
53
81
  alias on_numblock on_block
54
82
 
@@ -64,23 +92,43 @@ module RuboCop
64
92
  return unless (parent_receiver = target.receiver.receiver)
65
93
  return if allowed_receiver?(parent_receiver)
66
94
 
67
- add_offense(kv_range(target), message: format_message(method)) do |corrector|
95
+ current = target.receiver.loc.selector.join(target.source_range.end).source
96
+
97
+ add_offense(kv_range(target), message: format_message(method, current)) do |corrector|
68
98
  correct_key_value_each(target, corrector)
69
99
  end
70
100
  end
71
101
 
102
+ def unused_block_arg_exist?(node, block_arg_source)
103
+ node.body.each_descendant(:lvar).map(&:source).none?(block_arg_source)
104
+ end
105
+
106
+ def message(prefer, method_name, unused_code)
107
+ format(
108
+ UNUSED_BLOCK_ARG_MSG, prefer: prefer, current: method_name, unused_code: unused_code
109
+ )
110
+ end
111
+
112
+ def register_each_args_offense(node, message, prefer, unused_range)
113
+ add_offense(node, message: message) do |corrector|
114
+ corrector.replace(node.send_node.loc.selector, prefer)
115
+ corrector.remove(unused_range)
116
+ end
117
+ end
118
+
72
119
  def register_kv_with_block_pass_offense(node, target, method)
73
120
  return unless (parent_receiver = node.parent.receiver.receiver)
74
121
  return if allowed_receiver?(parent_receiver)
75
122
 
76
- range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
77
- add_offense(range, message: format_message(method)) do |corrector|
123
+ range = target.loc.selector.join(node.parent.loc.selector.end)
124
+
125
+ add_offense(range, message: format_message(method, range.source)) do |corrector|
78
126
  corrector.replace(range, "each_#{method[0..-2]}")
79
127
  end
80
128
  end
81
129
 
82
- def format_message(method_name)
83
- format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
130
+ def format_message(method_name, current)
131
+ format(MSG, prefer: "each_#{method_name[0..-2]}", current: current)
84
132
  end
85
133
 
86
134
  def check_argument(variable)
@@ -103,7 +151,7 @@ module RuboCop
103
151
  name = "each_#{node.receiver.method_name.to_s.chop}"
104
152
  return correct_implicit(node, corrector, name) unless receiver
105
153
 
106
- new_source = receiver.source + ".#{name}"
154
+ new_source = receiver.source + "#{node.loc.dot.source}#{name}"
107
155
  corrector.replace(node, new_source)
108
156
  end
109
157
 
@@ -153,7 +153,15 @@ module RuboCop
153
153
  return unless duplicated_expressions?(node, heads)
154
154
 
155
155
  condition_variable = assignable_condition_value(node)
156
- return if heads.first.assignment? && condition_variable == heads.first.name.to_s
156
+
157
+ head = heads.first
158
+ if head.assignment?
159
+ # The `send` node is used instead of the `indexasgn` node, so `name` cannot be used.
160
+ # https://github.com/rubocop/rubocop-ast/blob/v1.29.0/lib/rubocop/ast/node/indexasgn_node.rb
161
+ assigned_value = head.send_type? ? head.receiver.source : head.name.to_s
162
+
163
+ return if condition_variable == assigned_value
164
+ end
157
165
 
158
166
  check_expressions(node, heads, :before_condition)
159
167
  end
@@ -60,17 +60,17 @@ module RuboCop
60
60
  # @!method inverse_candidate?(node)
61
61
  def_node_matcher :inverse_candidate?, <<~PATTERN
62
62
  {
63
- (send $(send $(...) $_ $...) :!)
64
- (send ({block numblock} $(send $(...) $_) $...) :!)
65
- (send (begin $(send $(...) $_ $...)) :!)
63
+ (send $(call $(...) $_ $...) :!)
64
+ (send ({block numblock} $(call $(...) $_) $...) :!)
65
+ (send (begin $(call $(...) $_ $...)) :!)
66
66
  }
67
67
  PATTERN
68
68
 
69
69
  # @!method inverse_block?(node)
70
70
  def_node_matcher :inverse_block?, <<~PATTERN
71
- ({block numblock} $(send (...) $_) ... { $(send ... :!)
71
+ ({block numblock} $(call (...) $_) ... { $(call ... :!)
72
72
  $(send (...) {:!= :!~} ...)
73
- (begin ... $(send ... :!))
73
+ (begin ... $(call ... :!))
74
74
  (begin ... $(send (...) {:!= :!~} ...))
75
75
  })
76
76
  PATTERN
@@ -87,6 +87,7 @@ module RuboCop
87
87
  end
88
88
  end
89
89
  end
90
+ alias on_csend on_send
90
91
 
91
92
  def on_block(node)
92
93
  inverse_block?(node) do |_method_call, method, block|
@@ -34,21 +34,25 @@ module RuboCop
34
34
 
35
35
  minimum_target_ruby_version 2.6
36
36
 
37
- MSG = 'Pass a block to `to_h` instead of calling `%<method>s.to_h`.'
37
+ MSG = 'Pass a block to `to_h` instead of calling `%<method>s%<dot>sto_h`.'
38
38
  RESTRICT_ON_SEND = %i[to_h].freeze
39
39
 
40
40
  # @!method map_to_h?(node)
41
41
  def_node_matcher :map_to_h?, <<~PATTERN
42
42
  {
43
- $(send ({block numblock} $(send _ {:map :collect}) ...) :to_h)
44
- $(send $(send _ {:map :collect} (block_pass sym)) :to_h)
43
+ $(call ({block numblock} $(call _ {:map :collect}) ...) :to_h)
44
+ $(call $(call _ {:map :collect} (block_pass sym)) :to_h)
45
45
  }
46
46
  PATTERN
47
47
 
48
+ def self.autocorrect_incompatible_with
49
+ [Layout::SingleLineBlockChain]
50
+ end
51
+
48
52
  def on_send(node)
49
53
  return unless (to_h_node, map_node = map_to_h?(node))
50
54
 
51
- message = format(MSG, method: map_node.loc.selector.source)
55
+ message = format(MSG, method: map_node.loc.selector.source, dot: map_node.loc.dot.source)
52
56
  add_offense(map_node.loc.selector, message: message) do |corrector|
53
57
  # If the `to_h` call already has a block, do not autocorrect.
54
58
  next if to_h_node.block_node
@@ -56,6 +60,7 @@ module RuboCop
56
60
  autocorrect(corrector, to_h_node, map_node)
57
61
  end
58
62
  end
63
+ alias on_csend on_send
59
64
 
60
65
  private
61
66
 
@@ -239,7 +239,7 @@ module RuboCop
239
239
  def args_parenthesized?(node)
240
240
  return false unless node.arguments.one?
241
241
 
242
- first_node = node.arguments.first
242
+ first_node = node.first_argument
243
243
  first_node.begin_type? && first_node.parenthesized_call?
244
244
  end
245
245
  end
@@ -170,7 +170,7 @@ module RuboCop
170
170
  return true if node.arguments.any? do |arg|
171
171
  arg.forward_arg_type? || arg.restarg_type? || arg.kwrestarg_type?
172
172
  end
173
- return false unless (last_argument = node.arguments.last)
173
+ return false unless (last_argument = node.last_argument)
174
174
 
175
175
  last_argument.blockarg_type? && last_argument.name.nil?
176
176
  end
@@ -7,12 +7,12 @@ module RuboCop
7
7
  # defining `respond_to_missing?`.
8
8
  #
9
9
  # @example
10
- # #bad
10
+ # # bad
11
11
  # def method_missing(name, *args)
12
12
  # # ...
13
13
  # end
14
14
  #
15
- # #good
15
+ # # good
16
16
  # def respond_to_missing?(name, include_private)
17
17
  # # ...
18
18
  # end
@@ -67,7 +67,7 @@ module RuboCop
67
67
  return unless redundant_argument?(node)
68
68
 
69
69
  offense_range = argument_range(node)
70
- message = format(MSG, arg: node.arguments.first.source)
70
+ message = format(MSG, arg: node.first_argument.source)
71
71
 
72
72
  add_offense(offense_range, message: message) do |corrector|
73
73
  corrector.remove(offense_range)
@@ -80,7 +80,7 @@ module RuboCop
80
80
  redundant_argument = redundant_arg_for_method(node.method_name.to_s)
81
81
  return false if redundant_argument.nil?
82
82
 
83
- node.arguments.first == redundant_argument
83
+ node.first_argument == redundant_argument
84
84
  end
85
85
 
86
86
  def redundant_arg_for_method(method_name)
@@ -25,26 +25,29 @@ module RuboCop
25
25
  MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
26
26
  MERGE_METHODS = %i[merge merge!].freeze
27
27
 
28
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
28
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
29
29
  def on_hash(node)
30
30
  return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
31
31
  return unless (parent = node.parent)
32
- return if parent.call_type? && !merge_method?(parent)
32
+ return unless parent.call_type? || parent.kwsplat_type?
33
+ return unless mergeable?(parent)
33
34
  return unless (kwsplat = node.each_ancestor(:kwsplat).first)
34
- return if allowed_double_splat_receiver?(kwsplat)
35
+ return if !node.braces? || allowed_double_splat_receiver?(kwsplat)
35
36
 
36
37
  add_offense(kwsplat) do |corrector|
37
38
  autocorrect(corrector, node, kwsplat)
38
39
  end
39
40
  end
40
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
41
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
41
42
 
42
43
  private
43
44
 
44
45
  def allowed_double_splat_receiver?(kwsplat)
45
- return false unless kwsplat.children.first.call_type?
46
+ first_child = kwsplat.children.first
47
+ return true if first_child.block_type? || first_child.numblock_type?
48
+ return false unless first_child.call_type?
46
49
 
47
- root_receiver = root_receiver(kwsplat.children.first)
50
+ root_receiver = root_receiver(first_child)
48
51
 
49
52
  !root_receiver&.hash_type?
50
53
  end
@@ -71,7 +74,7 @@ module RuboCop
71
74
 
72
75
  def select_merge_method_nodes(kwsplat)
73
76
  extract_send_methods(kwsplat).select do |node|
74
- merge_method?(node)
77
+ mergeable?(node)
75
78
  end
76
79
  end
77
80
 
@@ -106,7 +109,7 @@ module RuboCop
106
109
  end
107
110
 
108
111
  def convert_to_new_arguments(node)
109
- return unless merge_method?(node)
112
+ return unless mergeable?(node)
110
113
 
111
114
  node.arguments.map do |arg|
112
115
  if arg.hash_type?
@@ -117,8 +120,12 @@ module RuboCop
117
120
  end
118
121
  end
119
122
 
120
- def merge_method?(node)
121
- MERGE_METHODS.include?(node.method_name)
123
+ def mergeable?(node)
124
+ return true unless node.call_type?
125
+ return false unless MERGE_METHODS.include?(node.method_name)
126
+ return true unless (parent = node.parent)
127
+
128
+ mergeable?(parent)
122
129
  end
123
130
  end
124
131
  end
@@ -60,10 +60,10 @@ module RuboCop
60
60
 
61
61
  # @!method select_predicate?(node)
62
62
  def_node_matcher :select_predicate?, <<~PATTERN
63
- (send
63
+ (call
64
64
  {
65
- (block $(send _ {:select :filter :find_all}) ...)
66
- $(send _ {:select :filter :find_all} block_pass_type?)
65
+ (block $(call _ {:select :filter :find_all}) ...)
66
+ $(call _ {:select :filter :find_all} block_pass_type?)
67
67
  }
68
68
  ${:#{RESTRICT_ON_SEND.join(' :')}})
69
69
  PATTERN
@@ -87,6 +87,7 @@ module RuboCop
87
87
  register_offense(select_node, node)
88
88
  end
89
89
  end
90
+ alias on_csend on_send
90
91
 
91
92
  private
92
93
 
@@ -107,6 +107,8 @@ module RuboCop
107
107
 
108
108
  def inside_string_literal_or_method_with_argument?(range)
109
109
  processed_source.tokens.each_cons(2).any? do |token, next_token|
110
+ next if token.line == next_token.line
111
+
110
112
  inside_string_literal?(range, token) || method_with_argument?(token, next_token)
111
113
  end
112
114
  end
@@ -17,6 +17,8 @@ module RuboCop
17
17
  include Parentheses
18
18
  extend AutoCorrector
19
19
 
20
+ ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
21
+
20
22
  # @!method square_brackets?(node)
21
23
  def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
22
24
 
@@ -111,17 +113,19 @@ module RuboCop
111
113
 
112
114
  def first_arg_begins_with_hash_literal?(node)
113
115
  # Don't flag `method ({key: value})` or `method ({key: value}.method)`
114
- method_chain_begins_with_hash_literal?(node.children.first) &&
115
- first_argument?(node) &&
116
- !parentheses?(node.parent)
116
+ hash_literal = method_chain_begins_with_hash_literal(node.children.first)
117
+ if (root_method = node.each_ancestor(:send).to_a.last)
118
+ parenthesized = root_method.parenthesized_call?
119
+ end
120
+ hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
117
121
  end
118
122
 
119
- def method_chain_begins_with_hash_literal?(node)
120
- return false if node.nil?
121
- return true if node.hash_type?
122
- return false unless node.send_type?
123
+ def method_chain_begins_with_hash_literal(node)
124
+ return if node.nil?
125
+ return node if node.hash_type?
126
+ return unless node.send_type?
123
127
 
124
- method_chain_begins_with_hash_literal?(node.children.first)
128
+ method_chain_begins_with_hash_literal(node.children.first)
125
129
  end
126
130
 
127
131
  def check(begin_node)
@@ -134,23 +138,29 @@ module RuboCop
134
138
  check_send(begin_node, node) if node.call_type?
135
139
  end
136
140
 
137
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
141
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
138
142
  def find_offense_message(begin_node, node)
139
143
  return 'a keyword' if keyword_with_redundant_parentheses?(node)
140
144
  return 'a literal' if disallowed_literal?(begin_node, node)
141
145
  return 'a variable' if node.variable?
142
146
  return 'a constant' if node.const_type?
147
+ return 'an expression' if node.lambda_or_proc?
143
148
  return 'an interpolated expression' if interpolation?(begin_node)
144
149
 
145
- return if begin_node.chained? || !begin_node.parent.nil?
150
+ return if begin_node.chained?
146
151
 
147
152
  if node.and_type? || node.or_type?
153
+ return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
154
+ return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
155
+
148
156
  'a logical expression'
149
157
  elsif node.respond_to?(:comparison_method?) && node.comparison_method?
158
+ return unless begin_node.parent.nil?
159
+
150
160
  'a comparison expression'
151
161
  end
152
162
  end
153
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
163
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
154
164
 
155
165
  # @!method interpolation?(node)
156
166
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
@@ -231,7 +241,13 @@ module RuboCop
231
241
  end
232
242
 
233
243
  def first_argument?(node)
234
- first_send_argument?(node) || first_super_argument?(node) || first_yield_argument?(node)
244
+ if first_send_argument?(node) ||
245
+ first_super_argument?(node) ||
246
+ first_yield_argument?(node)
247
+ return true
248
+ end
249
+
250
+ node.each_ancestor.any? { |ancestor| first_argument?(ancestor) }
235
251
  end
236
252
 
237
253
  # @!method first_send_argument?(node)
@@ -58,7 +58,7 @@ module RuboCop
58
58
 
59
59
  MSG = 'Redundant `return` detected.'
60
60
  MULTI_RETURN_MSG = 'To return multiple values, use an array.'
61
- RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
61
+ RESTRICT_ON_SEND = %i[define_method define_singleton_method lambda].freeze
62
62
 
63
63
  def on_send(node)
64
64
  return unless (parent = node.parent) && parent.block_type?
@@ -180,7 +180,7 @@ module RuboCop
180
180
  end
181
181
 
182
182
  def arg_node(node)
183
- node.arguments.first
183
+ node.first_argument
184
184
  end
185
185
 
186
186
  def arg_value(node)
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def percent_array_literal?(node)
136
- (percent_w_literal?(node) || percent_w_upper_literal?(node))
136
+ percent_w_literal?(node) || percent_w_upper_literal?(node)
137
137
  end
138
138
 
139
139
  def heredoc_with_disabled_interpolation?(node)
@@ -146,7 +146,7 @@ module RuboCop
146
146
  return node.child_nodes.first if node.match_with_lvasgn_type?
147
147
 
148
148
  if node.receiver.lvar_type? &&
149
- (block.numblock_type? || node.receiver.source == block.arguments.first.source)
149
+ (block.numblock_type? || node.receiver.source == block.first_argument.source)
150
150
  node.first_argument
151
151
  elsif node.first_argument.lvar_type?
152
152
  node.receiver
@@ -16,7 +16,7 @@ module RuboCop
16
16
  extend AutoCorrector
17
17
 
18
18
  MSG = 'Use self-assignment shorthand `%<method>s=`.'
19
- OPS = %i[+ - * ** / | &].freeze
19
+ OPS = %i[+ - * ** / % ^ << >> | &].freeze
20
20
 
21
21
  def self.autocorrect_incompatible_with
22
22
  [Layout::SpaceAroundOperators]
@@ -80,6 +80,7 @@ module RuboCop
80
80
  processed_source.tokens.group_by(&:line)
81
81
  end
82
82
 
83
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
84
  def semicolon_position(tokens)
84
85
  if tokens.last.semicolon?
85
86
  -1
@@ -90,10 +91,13 @@ module RuboCop
90
91
  elsif exist_semicolon_after_left_curly_brace?(tokens) ||
91
92
  exist_semicolon_after_left_string_interpolation_brace?(tokens)
92
93
  2
94
+ elsif exist_semicolon_after_left_lambda_curly_brace?(tokens)
95
+ 3
93
96
  elsif exist_semicolon_before_right_string_interpolation_brace?(tokens)
94
97
  -4
95
98
  end
96
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
101
 
98
102
  def exist_semicolon_before_right_curly_brace?(tokens)
99
103
  tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
@@ -103,6 +107,10 @@ module RuboCop
103
107
  tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
108
  end
105
109
 
110
+ def exist_semicolon_after_left_lambda_curly_brace?(tokens)
111
+ tokens[2]&.type == :tLAMBEG && tokens[3]&.semicolon?
112
+ end
113
+
106
114
  def exist_semicolon_before_right_string_interpolation_brace?(tokens)
107
115
  tokens[-3]&.type == :tSTRING_DEND && tokens[-4]&.semicolon?
108
116
  end
@@ -33,6 +33,7 @@ module RuboCop
33
33
 
34
34
  MSG = 'Use `%<receiver>s[%<argument>s]` instead of `%<original>s`.'
35
35
  RESTRICT_ON_SEND = %i[dig].freeze
36
+ IGNORED_ARGUMENT_TYPES = %i[block_pass forwarded_restarg forwarded_args hash].freeze
36
37
 
37
38
  # @!method single_argument_dig?(node)
38
39
  def_node_matcher :single_argument_dig?, <<~PATTERN
@@ -44,7 +45,7 @@ module RuboCop
44
45
 
45
46
  expression = single_argument_dig?(node)
46
47
  return unless expression
47
- return if expression.forwarded_args_type?
48
+ return if IGNORED_ARGUMENT_TYPES.include?(expression.type)
48
49
 
49
50
  receiver = node.receiver.source
50
51
  argument = expression.source
@@ -30,6 +30,7 @@ module RuboCop
30
30
 
31
31
  MSG = 'Prefer multiline `do`...`end` block.'
32
32
 
33
+ # rubocop:disable Metrics/AbcSize
33
34
  def on_block(node)
34
35
  return if !node.single_line? || node.braces?
35
36
 
@@ -42,10 +43,11 @@ module RuboCop
42
43
  corrector.remove(node.loc.end)
43
44
  corrector.insert_after(node_body.loc.heredoc_end, "\nend")
44
45
  else
45
- corrector.insert_after(node_body, "\n")
46
+ corrector.insert_before(node.loc.end, "\n")
46
47
  end
47
48
  end
48
49
  end
50
+ # rubocop:enable Metrics/AbcSize
49
51
  alias on_numblock on_block
50
52
 
51
53
  private
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  def on_send(node)
41
41
  return unless node.arguments.count == 1
42
- return unless range_till_minus_one?(node.arguments.first)
42
+ return unless range_till_minus_one?(node.first_argument)
43
43
 
44
44
  add_offense(node.first_argument) do |corrector|
45
45
  corrector.remove(node.first_argument.end)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Enforces the presence of parentheses in `super` containing arguments.
7
+ #
8
+ # `super` is a keyword and is provided as a distinct cop from those designed for method call.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # super name, age
14
+ #
15
+ # # good
16
+ # super(name, age)
17
+ #
18
+ class SuperWithArgsParentheses < Base
19
+ extend AutoCorrector
20
+
21
+ MSG = 'Use parentheses for `super` with arguments.'
22
+
23
+ def on_super(node)
24
+ return if node.parenthesized?
25
+
26
+ add_offense(node) do |corrector|
27
+ range = node.loc.keyword.end.join(node.first_argument.source_range.begin)
28
+ corrector.replace(range, '(')
29
+ corrector.insert_after(node.last_argument, ')')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
3
  require 'cgi'
5
4
  require 'erb'
6
- require 'ostruct'
7
5
 
8
6
  module RuboCop
9
7
  module Formatter
@@ -88,7 +86,7 @@ module RuboCop
88
86
  # rubocop:enable Lint/UselessMethodDefinition
89
87
 
90
88
  def decorated_message(offense)
91
- offense.message.gsub(/`(.+?)`/) { "<code>#{Regexp.last_match(1)}</code>" }
89
+ offense.message.gsub(/`(.+?)`/) { "<code>#{escape(Regexp.last_match(1))}</code>" }
92
90
  end
93
91
 
94
92
  def highlighted_source_line(offense)
@@ -124,7 +122,10 @@ module RuboCop
124
122
 
125
123
  def base64_encoded_logo_image
126
124
  image = File.read(LOGO_IMAGE_PATH, binmode: true)
127
- Base64.encode64(image)
125
+
126
+ # `Base64.encode64` compatible:
127
+ # https://github.com/ruby/base64/blob/v0.1.1/lib/base64.rb#L27-L40
128
+ [image].pack('m')
128
129
  end
129
130
  end
130
131
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'digest/sha1'
4
- require 'etc'
5
4
  require 'find'
6
5
  require 'zlib'
7
6
  require_relative 'cache_config'