rubocop 1.64.0 → 1.65.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/config/default.yml +11 -1
  4. data/lib/rubocop/config_loader.rb +1 -1
  5. data/lib/rubocop/config_loader_resolver.rb +9 -3
  6. data/lib/rubocop/cop/cop.rb +20 -2
  7. data/lib/rubocop/cop/force.rb +12 -0
  8. data/lib/rubocop/cop/gemspec/add_runtime_dependency.rb +38 -0
  9. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  10. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  11. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  12. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +1 -1
  13. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  14. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  15. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  16. data/lib/rubocop/cop/layout/line_length.rb +20 -20
  17. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -0
  18. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +3 -4
  19. data/lib/rubocop/cop/legacy/corrector.rb +12 -2
  20. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +1 -1
  21. data/lib/rubocop/cop/lint/empty_when.rb +1 -1
  22. data/lib/rubocop/cop/lint/erb_new_arguments.rb +21 -14
  23. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +14 -7
  24. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  25. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  26. data/lib/rubocop/cop/lint/to_enum_arguments.rb +2 -9
  27. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  28. data/lib/rubocop/cop/lint/void.rb +5 -0
  29. data/lib/rubocop/cop/metrics/block_nesting.rb +19 -7
  30. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  31. data/lib/rubocop/cop/mixin/allowed_methods.rb +7 -1
  32. data/lib/rubocop/cop/mixin/allowed_pattern.rb +15 -3
  33. data/lib/rubocop/cop/mixin/configurable_max.rb +5 -1
  34. data/lib/rubocop/cop/mixin/rescue_node.rb +4 -0
  35. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  36. data/lib/rubocop/cop/style/copyright.rb +5 -2
  37. data/lib/rubocop/cop/style/documentation.rb +24 -24
  38. data/lib/rubocop/cop/style/hash_except.rb +8 -5
  39. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +77 -43
  40. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +5 -0
  41. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  42. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  43. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -1
  44. data/lib/rubocop/cop/style/redundant_line_continuation.rb +2 -1
  45. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +23 -2
  46. data/lib/rubocop/cop/style/super_arguments.rb +52 -15
  47. data/lib/rubocop/cop/style/symbol_proc.rb +8 -1
  48. data/lib/rubocop/cop/style/zero_length_predicate.rb +28 -24
  49. data/lib/rubocop/cop/team.rb +10 -0
  50. data/lib/rubocop/cop/util.rb +7 -1
  51. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  52. data/lib/rubocop/ext/regexp_parser.rb +4 -21
  53. data/lib/rubocop/formatter/html_formatter.rb +3 -1
  54. data/lib/rubocop/lsp/routes.rb +1 -1
  55. data/lib/rubocop/rspec/shared_contexts.rb +20 -0
  56. data/lib/rubocop/rspec/support.rb +1 -0
  57. data/lib/rubocop/server/cache.rb +10 -0
  58. data/lib/rubocop/server/client_command/exec.rb +2 -2
  59. data/lib/rubocop/server/client_command/start.rb +1 -1
  60. data/lib/rubocop/server/core.rb +4 -0
  61. data/lib/rubocop/version.rb +1 -1
  62. data/lib/rubocop.rb +1 -0
  63. metadata +7 -6
@@ -3,12 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
- # Checks for excessive nesting of conditional and looping
7
- # constructs.
6
+ # Checks for excessive nesting of conditional and looping constructs.
8
7
  #
9
- # You can configure if blocks are considered using the `CountBlocks`
10
- # option. When set to `false` (the default) blocks are not counted
11
- # towards the nesting level. Set to `true` to count blocks as well.
8
+ # You can configure if blocks are considered using the `CountBlocks` and `CountModifierForms`
9
+ # options. When both are set to `false` (the default) blocks and modifier forms are not
10
+ # counted towards the nesting level. Set them to `true` to include these in the nesting level
11
+ # calculation as well.
12
12
  #
13
13
  # The maximum level of nesting allowed is configurable.
14
14
  class BlockNesting < Base
@@ -27,7 +27,7 @@ module RuboCop
27
27
 
28
28
  def check_nesting_level(node, max, current_level)
29
29
  if consider_node?(node)
30
- current_level += 1 unless node.if_type? && node.elsif?
30
+ current_level += 1 if count_if_block?(node)
31
31
  if current_level > max
32
32
  self.max = current_level
33
33
  unless part_of_ignored_node?(node)
@@ -41,6 +41,14 @@ module RuboCop
41
41
  end
42
42
  end
43
43
 
44
+ def count_if_block?(node)
45
+ return true unless node.if_type?
46
+ return false if node.elsif?
47
+ return count_modifier_forms? if node.modifier_form?
48
+
49
+ true
50
+ end
51
+
44
52
  def consider_node?(node)
45
53
  return true if NESTING_BLOCKS.include?(node.type)
46
54
 
@@ -52,7 +60,11 @@ module RuboCop
52
60
  end
53
61
 
54
62
  def count_blocks?
55
- cop_config['CountBlocks']
63
+ cop_config.fetch('CountBlocks', false)
64
+ end
65
+
66
+ def count_modifier_forms?
67
+ cop_config.fetch('CountModifierForms', false)
56
68
  end
57
69
  end
58
70
  end
@@ -65,8 +65,12 @@ module RuboCop
65
65
  inner.begin_pos >= outer.begin_pos && inner.end_pos <= outer.end_pos
66
66
  end
67
67
 
68
- # @deprecated Use processed_source.comment_at_line(line)
68
+ # @deprecated Use processed_source.line_with_comment?(line)
69
69
  def end_of_line_comment(line)
70
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
71
+ `end_of_line_comment` is deprecated. Use `processed_source.line_with_comment?` instead.
72
+ WARNING
73
+
70
74
  processed_source.line_with_comment?(line)
71
75
  end
72
76
 
@@ -15,7 +15,13 @@ module RuboCop
15
15
  end
16
16
 
17
17
  # @deprecated Use allowed_method? instead
18
- alias ignored_method? allowed_method?
18
+ def ignored_method?
19
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
20
+ `ignored_method?` is deprecated. Use `allowed_method?` instead.
21
+ WARNING
22
+
23
+ allowed_method?
24
+ end
19
25
 
20
26
  # @api public
21
27
  def allowed_methods
@@ -18,14 +18,26 @@ module RuboCop
18
18
  end
19
19
 
20
20
  # @deprecated Use allowed_line? instead
21
- alias ignored_line? allowed_line?
21
+ def ignored_line?
22
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
23
+ `ignored_line?` is deprecated. Use `allowed_line?` instead.
24
+ WARNING
25
+
26
+ allowed_line?
27
+ end
22
28
 
23
29
  def matches_allowed_pattern?(line)
24
30
  allowed_patterns.any? { |pattern| Regexp.new(pattern).match?(line) }
25
31
  end
26
32
 
27
- # @deprecated Use matches_allowed_pattern?? instead
28
- alias matches_ignored_pattern? matches_allowed_pattern?
33
+ # @deprecated Use matches_allowed_pattern? instead
34
+ def matches_ignored_pattern?
35
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
36
+ `matches_ignored_pattern?` is deprecated. Use `matches_allowed_pattern?` instead.
37
+ WARNING
38
+
39
+ matches_allowed_pattern?
40
+ end
29
41
 
30
42
  def allowed_patterns
31
43
  # Since there could be a pattern specified in the default config, merge the two
@@ -4,11 +4,15 @@ module RuboCop
4
4
  module Cop
5
5
  # Handles `Max` configuration parameters, especially setting them to an
6
6
  # appropriate value with --auto-gen-config.
7
- # @deprecated Use `exclude_limit ParameterName` instead.
7
+ # @deprecated Use `exclude_limit <ParameterName>` instead.
8
8
  module ConfigurableMax
9
9
  private
10
10
 
11
11
  def max=(value)
12
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
13
+ `max=` is deprecated. Use `exclude_limit <ParameterName>` instead.
14
+ WARNING
15
+
12
16
  cfg = config_to_allow_offenses
13
17
  cfg[:exclude_limit] ||= {}
14
18
  current_max = cfg[:exclude_limit][max_parameter_name]
@@ -18,6 +18,10 @@ module RuboCop
18
18
 
19
19
  # @deprecated Use ResbodyNode#exceptions instead
20
20
  def rescued_exceptions(resbody)
21
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
22
+ `rescued_exceptions` is deprecated. Use `ResbodyNode#exceptions` instead.
23
+ WARNING
24
+
21
25
  rescue_group, = *resbody
22
26
  if rescue_group
23
27
  rescue_group.values
@@ -148,7 +148,7 @@ module RuboCop
148
148
 
149
149
  restarg, kwrestarg, blockarg = extract_forwardable_args(node.arguments)
150
150
  forwardable_args = redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
151
- send_nodes = node.each_descendant(:send, :csend, :super).to_a
151
+ send_nodes = node.each_descendant(:send, :csend, :super, :yield).to_a
152
152
 
153
153
  send_classifications = classify_send_nodes(
154
154
  node, send_nodes, non_splat_or_block_pass_lvar_references(node.body), forwardable_args
@@ -58,12 +58,15 @@ module RuboCop
58
58
  end
59
59
 
60
60
  def verify_autocorrect_notice!
61
- raise Warning, AUTOCORRECT_EMPTY_WARNING if autocorrect_notice.empty?
61
+ if autocorrect_notice.nil? || autocorrect_notice.empty?
62
+ raise Warning, "#{cop_name}: #{AUTOCORRECT_EMPTY_WARNING}"
63
+ end
62
64
 
63
65
  regex = Regexp.new(notice)
64
66
  return if autocorrect_notice.gsub(/^# */, '').match?(regex)
65
67
 
66
- raise Warning, "AutocorrectNotice '#{autocorrect_notice}' must match Notice /#{notice}/"
68
+ message = "AutocorrectNotice '#{autocorrect_notice}' must match Notice /#{notice}/"
69
+ raise Warning, "#{cop_name}: #{message}"
67
70
  end
68
71
 
69
72
  def insert_notice_before(processed_source)
@@ -29,36 +29,36 @@ module RuboCop
29
29
  # end
30
30
  #
31
31
  # # allowed
32
- # # Class without body
32
+ # # Class without body
33
+ # class Person
34
+ # end
35
+ #
36
+ # # Namespace - A namespace can be a class or a module
37
+ # # Containing a class
38
+ # module Namespace
39
+ # # Description/Explanation of Person class
33
40
  # class Person
41
+ # # ...
34
42
  # end
43
+ # end
35
44
  #
36
- # # Namespace - A namespace can be a class or a module
37
- # # Containing a class
38
- # module Namespace
39
- # # Description/Explanation of Person class
40
- # class Person
41
- # # ...
42
- # end
45
+ # # Containing constant visibility declaration
46
+ # module Namespace
47
+ # class Private
43
48
  # end
44
49
  #
45
- # # Containing constant visibility declaration
46
- # module Namespace
47
- # class Private
48
- # end
49
- #
50
- # private_constant :Private
51
- # end
50
+ # private_constant :Private
51
+ # end
52
52
  #
53
- # # Containing constant definition
54
- # module Namespace
55
- # Public = Class.new
56
- # end
53
+ # # Containing constant definition
54
+ # module Namespace
55
+ # Public = Class.new
56
+ # end
57
57
  #
58
- # # Macro calls
59
- # module Namespace
60
- # extend Foo
61
- # end
58
+ # # Macro calls
59
+ # module Namespace
60
+ # extend Foo
61
+ # end
62
62
  #
63
63
  # @example AllowedConstants: ['ClassMethods']
64
64
  #
@@ -67,7 +67,7 @@ module RuboCop
67
67
  # module ClassMethods
68
68
  # # ...
69
69
  # end
70
- # end
70
+ # end
71
71
  #
72
72
  class Documentation < Base
73
73
  include DocumentationComment
@@ -23,9 +23,9 @@ module RuboCop
23
23
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
24
24
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
25
25
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
26
- # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[foo bar].include?(k) }
27
- # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[foo bar].include?(k) }
28
- # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[foo bar].include?(k) }
26
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].include?(k) }
27
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].include?(k) }
28
+ # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[bar].include?(k) }
29
29
  #
30
30
  # # good
31
31
  # {foo: 1, bar: 2, baz: 3}.except(:bar)
@@ -73,8 +73,9 @@ module RuboCop
73
73
  PATTERN
74
74
 
75
75
  def on_send(node)
76
+ method_name = node.method_name
76
77
  block = node.parent
77
- return unless bad_method?(block) && semantically_except_method?(node, block)
78
+ return unless bad_method?(method_name, block) && semantically_except_method?(node, block)
78
79
 
79
80
  except_key = except_key(block)
80
81
  return if except_key.nil? || !safe_to_register_offense?(block, except_key)
@@ -91,7 +92,7 @@ module RuboCop
91
92
  private
92
93
 
93
94
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
94
- def bad_method?(block)
95
+ def bad_method?(method_name, block)
95
96
  if active_support_extensions_enabled?
96
97
  bad_method_with_active_support?(block) do |key_arg, send_node|
97
98
  if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
@@ -103,6 +104,8 @@ module RuboCop
103
104
  end
104
105
  else
105
106
  bad_method_with_poro?(block) do |key_arg, send_node|
107
+ return false if method_name == :reject && block.body.method?(:!)
108
+
106
109
  !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
107
110
  end
108
111
  end
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Prefer `select` or `reject` over `map { ... }.compact`.
7
+ # This cop also handles `filter_map { ... }`, similar to `map { ... }.compact`.
7
8
  #
8
9
  # @example
9
10
  #
@@ -11,6 +12,9 @@ module RuboCop
11
12
  # array.map { |e| some_condition? ? e : next }.compact
12
13
  #
13
14
  # # bad
15
+ # array.filter_map { |e| some_condition? ? e : next }
16
+ #
17
+ # # bad
14
18
  # array.map do |e|
15
19
  # if some_condition?
16
20
  # e
@@ -40,55 +44,73 @@ module RuboCop
40
44
  class MapCompactWithConditionalBlock < Base
41
45
  extend AutoCorrector
42
46
 
43
- MSG = 'Replace `map { ... }.compact` with `%<method>s`.'
44
-
45
- # @!method map_and_compact?(node)
46
- def_node_matcher :map_and_compact?, <<~RUBY
47
- (call
48
- (block
49
- (call _ :map)
50
- (args
51
- $(arg _))
52
- {
53
- (if $_ $(lvar _) {next nil?})
54
- (if $_ {next nil?} $(lvar _))
55
- (if $_ (next $(lvar _)) {next nil nil?})
56
- (if $_ {next nil nil?} (next $(lvar _)))
57
- (begin
58
- {
59
- (if $_ next nil?)
60
- (if $_ nil? next)
61
- }
62
- $(lvar _))
63
- (begin
64
- {
65
- (if $_ (next $(lvar _)) nil?)
66
- (if $_ nil? (next $(lvar _)))
67
- }
68
- (nil))
69
- }) :compact)
47
+ MSG = 'Replace `%<current>s` with `%<method>s`.'
48
+ RESTRICT_ON_SEND = %i[compact filter_map].freeze
49
+
50
+ # @!method conditional_block(node)
51
+ def_node_matcher :conditional_block, <<~RUBY
52
+ (block
53
+ (call _ {:map :filter_map})
54
+ (args
55
+ $(arg _))
56
+ {
57
+ (if $_ $(lvar _) {next nil?})
58
+ (if $_ {next nil?} $(lvar _))
59
+ (if $_ (next $(lvar _)) {next nil nil?})
60
+ (if $_ {next nil nil?} (next $(lvar _)))
61
+ (begin
62
+ {
63
+ (if $_ next nil?)
64
+ (if $_ nil? next)
65
+ }
66
+ $(lvar _))
67
+ (begin
68
+ {
69
+ (if $_ (next $(lvar _)) nil?)
70
+ (if $_ nil? (next $(lvar _)))
71
+ }
72
+ (nil))
73
+ })
70
74
  RUBY
71
75
 
72
76
  def on_send(node)
73
- map_and_compact?(node) do |block_argument_node, condition_node, return_value_node|
74
- return unless returns_block_argument?(block_argument_node, return_value_node)
75
- return if condition_node.parent.elsif?
76
-
77
- method = truthy_branch?(return_value_node) ? 'select' : 'reject'
78
- range = range(node)
79
-
80
- add_offense(range, message: format(MSG, method: method)) do |corrector|
81
- corrector.replace(
82
- range,
83
- "#{method} { |#{block_argument_node.source}| #{condition_node.source} }"
84
- )
85
- end
77
+ map_candidate = node.children.first
78
+ if (block_argument, condition, return_value = conditional_block(map_candidate))
79
+ return unless node.method?(:compact) && node.arguments.empty?
80
+
81
+ range = map_with_compact_range(node)
82
+ elsif (block_argument, condition, return_value = conditional_block(node.parent))
83
+ return unless node.method?(:filter_map)
84
+
85
+ range = filter_map_range(node)
86
+ else
87
+ return
86
88
  end
89
+
90
+ inspect(node, block_argument, condition, return_value, range)
87
91
  end
88
92
  alias on_csend on_send
89
93
 
90
94
  private
91
95
 
96
+ def inspect(node, block_argument_node, condition_node, return_value_node, range)
97
+ return unless returns_block_argument?(block_argument_node, return_value_node)
98
+ return if condition_node.parent.elsif?
99
+
100
+ method = truthy_branch?(return_value_node) ? 'select' : 'reject'
101
+ current = current(node)
102
+
103
+ add_offense(range, message: format(MSG, current: current, method: method)) do |corrector|
104
+ return if part_of_ignored_node?(node) || ignored_node?(node)
105
+
106
+ corrector.replace(
107
+ range, "#{method} { |#{block_argument_node.source}| #{condition_node.source} }"
108
+ )
109
+
110
+ ignore_node(node)
111
+ end
112
+ end
113
+
92
114
  def returns_block_argument?(block_argument_node, return_value_node)
93
115
  block_argument_node.name == return_value_node.children.first
94
116
  end
@@ -123,10 +145,22 @@ module RuboCop
123
145
  end
124
146
  end
125
147
 
126
- def range(node)
127
- map_node = node.receiver.send_node
148
+ def current(node)
149
+ if node.method?(:compact)
150
+ map_or_filter_map_method = node.children.first
151
+
152
+ "#{map_or_filter_map_method.method_name} { ... }.compact"
153
+ else
154
+ 'filter_map { ... }'
155
+ end
156
+ end
157
+
158
+ def map_with_compact_range(node)
159
+ node.receiver.send_node.loc.selector.begin.join(node.source_range.end)
160
+ end
128
161
 
129
- map_node.loc.selector.join(node.source_range.end)
162
+ def filter_map_range(node)
163
+ node.loc.selector.join(node.parent.source_range.end)
130
164
  end
131
165
  end
132
166
  end
@@ -18,6 +18,7 @@ module RuboCop
18
18
  return if inside_endless_method_def?(node)
19
19
  return if require_parentheses_for_hash_value_omission?(node)
20
20
  return if syntax_like_method_call?(node)
21
+ return if method_call_before_constant_resolution?(node)
21
22
  return if super_call_without_arguments?(node)
22
23
  return if legitimate_call_with_parentheses?(node)
23
24
  return if allowed_camel_case_method_call?(node)
@@ -63,6 +64,10 @@ module RuboCop
63
64
  node.implicit_call? || node.operator_method?
64
65
  end
65
66
 
67
+ def method_call_before_constant_resolution?(node)
68
+ node.parent&.const_type?
69
+ end
70
+
66
71
  def super_call_without_arguments?(node)
67
72
  node.super_type? && node.arguments.none?
68
73
  end
@@ -9,7 +9,7 @@ module RuboCop
9
9
  #
10
10
  # String interpolation is always kept in double quotes.
11
11
  #
12
- # Note: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
12
+ # NOTE: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
13
13
  # are not quoted that don't need to be. This cop is for configuring the quoting
14
14
  # style to use for symbols that require quotes.
15
15
  #
@@ -75,7 +75,7 @@ module RuboCop
75
75
 
76
76
  def on_def(node)
77
77
  return unless node.body&.kwbegin_type?
78
- return if node.endless? && !node.body.children.one?
78
+ return if node.endless?
79
79
 
80
80
  register_offense(node.body)
81
81
  end
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for the presence of superfluous `.rb` extension in
7
7
  # the filename provided to `require` and `require_relative`.
8
8
  #
9
- # Note: If the extension is omitted, Ruby tries adding '.rb', '.so',
9
+ # NOTE: If the extension is omitted, Ruby tries adding '.rb', '.so',
10
10
  # and so on to the name until found. If the file named cannot be found,
11
11
  # a `LoadError` will be raised.
12
12
  # There is an edge case where `foo.so` file is loaded instead of a `LoadError`
@@ -74,6 +74,7 @@ module RuboCop
74
74
  kFALSE kNIL kSELF kTRUE tCONSTANT tCVAR tFLOAT tGVAR tIDENTIFIER tINTEGER tIVAR
75
75
  tLBRACK tLCURLY tLPAREN_ARG tSTRING tSTRING_BEG tSYMBOL tXSTRING_BEG
76
76
  ].freeze
77
+ ARGUMENT_TAKING_FLOW_TOKEN_TYPES = %i[tIDENTIFIER kRETURN kBREAK kNEXT kYIELD].freeze
77
78
 
78
79
  def on_new_investigation
79
80
  return unless processed_source.ast
@@ -137,7 +138,7 @@ module RuboCop
137
138
  # do_something \
138
139
  # argument
139
140
  def method_with_argument?(current_token, next_token)
140
- return false if current_token.type != :tIDENTIFIER && current_token.type != :kRETURN
141
+ return false unless ARGUMENT_TAKING_FLOW_TOKEN_TYPES.include?(current_token.type)
141
142
 
142
143
  ARGUMENT_TYPES.include?(next_token.type)
143
144
  end
@@ -7,6 +7,20 @@ module RuboCop
7
7
  # Since the `send` method can be used to call private methods, by default,
8
8
  # only the `public_send` method is detected.
9
9
  #
10
+ # NOTE: Writer methods with names ending in `=` are always permitted because their
11
+ # behavior differs as follows:
12
+ #
13
+ # [source,ruby]
14
+ # ----
15
+ # def foo=(foo)
16
+ # @foo = foo
17
+ # 42
18
+ # end
19
+ #
20
+ # self.foo = 1 # => 1
21
+ # send(:foo=, 1) # => 42
22
+ # ----
23
+ #
10
24
  # @safety
11
25
  # This cop is not safe because it can incorrectly detect based on the receiver.
12
26
  # Additionally, when `AllowSend` is set to `true`, it cannot determine whether
@@ -43,8 +57,14 @@ module RuboCop
43
57
  MSG = 'Use `%<method_name>s` method call directly instead.'
44
58
  RESTRICT_ON_SEND = %i[public_send send __send__].freeze
45
59
  STATIC_METHOD_NAME_NODE_TYPES = %i[sym str].freeze
60
+ METHOD_NAME_PATTERN = /\A[a-zA-Z_][a-zA-Z0-9_]*[!?]?\z/.freeze
61
+ RESERVED_WORDS = %i[
62
+ BEGIN END alias and begin break case class def defined? do else elsif end ensure
63
+ false for if in module next nil not or redo rescue retry return self super then true
64
+ undef unless until when while yield
65
+ ].freeze
46
66
 
47
- # rubocop:disable Metrics/AbcSize
67
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
48
68
  def on_send(node)
49
69
  return if allow_send? && !node.method?(:public_send)
50
70
  return unless (first_argument = node.first_argument)
@@ -52,6 +72,7 @@ module RuboCop
52
72
 
53
73
  offense_range = offense_range(node)
54
74
  method_name = first_argument.value
75
+ return if !METHOD_NAME_PATTERN.match?(method_name) || RESERVED_WORDS.include?(method_name)
55
76
 
56
77
  add_offense(offense_range, message: format(MSG, method_name: method_name)) do |corrector|
57
78
  if node.arguments.one?
@@ -62,7 +83,7 @@ module RuboCop
62
83
  end
63
84
  end
64
85
  end
65
- # rubocop:enable Metrics/AbcSize
86
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
66
87
 
67
88
  private
68
89