rubocop 1.50.2 → 1.51.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +18 -3
  4. data/lib/rubocop/config.rb +4 -0
  5. data/lib/rubocop/config_obsoletion.rb +2 -2
  6. data/lib/rubocop/cop/base.rb +4 -0
  7. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  8. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  9. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  10. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  11. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
  12. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +12 -1
  13. data/lib/rubocop/cop/lint/erb_new_arguments.rb +2 -2
  14. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -2
  15. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  16. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
  17. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
  18. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -1
  19. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
  20. data/lib/rubocop/cop/lint/useless_assignment.rb +56 -1
  21. data/lib/rubocop/cop/lint/void.rb +62 -6
  22. data/lib/rubocop/cop/mixin/comments_help.rb +6 -2
  23. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  24. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  25. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +22 -7
  26. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
  27. data/lib/rubocop/cop/style/attr.rb +11 -1
  28. data/lib/rubocop/cop/style/collection_compact.rb +10 -6
  29. data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
  30. data/lib/rubocop/cop/style/combinable_loops.rb +26 -6
  31. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  32. data/lib/rubocop/cop/style/copyright.rb +5 -2
  33. data/lib/rubocop/cop/style/documentation.rb +1 -1
  34. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  35. data/lib/rubocop/cop/style/exact_regexp_match.rb +62 -0
  36. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  37. data/lib/rubocop/cop/style/hash_except.rb +19 -8
  38. data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
  39. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -0
  40. data/lib/rubocop/cop/style/invertible_unless_condition.rb +9 -5
  41. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  42. data/lib/rubocop/cop/style/redundant_line_continuation.rb +5 -1
  43. data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
  44. data/lib/rubocop/cop/style/require_order.rb +9 -4
  45. data/lib/rubocop/cop/style/semicolon.rb +12 -1
  46. data/lib/rubocop/cop/style/special_global_vars.rb +2 -2
  47. data/lib/rubocop/cop/team.rb +1 -1
  48. data/lib/rubocop/cop/variable_force/assignment.rb +4 -0
  49. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  50. data/lib/rubocop/result_cache.rb +1 -1
  51. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  52. data/lib/rubocop/target_ruby.rb +3 -2
  53. data/lib/rubocop/version.rb +3 -3
  54. data/lib/rubocop.rb +1 -0
  55. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67accbc10c4c2ae80033ab2a3713fce8a190d772234cbd52c5584e5c69895915
4
- data.tar.gz: 69e7f99ee66c192619b2dc26df6d0ac73c794bb088bade8182f3eceb1cb8d8c0
3
+ metadata.gz: '0863dd1792c6296e51bcdbbc4503423cbc61f66c40a7120b405f4eaf999349be'
4
+ data.tar.gz: a4998dfeef1e7f82ffac34fb409494859b28afba9ce6bb007f6ca5870a5e371b
5
5
  SHA512:
6
- metadata.gz: 21005d0d16a207a998640686ee7dcf251683899ab8e746baa861888fd26f2e58791733b48869a085b1b365ce2b67e64b216b4cfe6c1a2e26a2ecc699da0ea085
7
- data.tar.gz: 461a588bb95269c194efee4ee8570c166ce009695664aa31181f1f0806220f451e4685d4feff8751d4135f196d3cb42ec100c171b905526e06ed968cd9bb47f9
6
+ metadata.gz: 1ff6067cf16502e16e5bf6691717f97b5f0cf34bdd03e0d893ee90185af1a26d5622c394b5b586530a9d596f8f92cdff4b5eb351d305fc8a2206906f903c9606
7
+ data.tar.gz: 2ab3c9a30731adbd8818bfce7fba9f346ac8f66a3b0a52fe2ee65094a7ed580f617fa776664f59df71d6229cf35e3e9ad74a557ec6a760dcbc6d85ae1008a2bb
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.50', require: false
56
+ gem 'rubocop', '~> 1.51', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
@@ -75,8 +75,8 @@ You can read a lot more about RuboCop in its [official docs](https://docs.ruboco
75
75
 
76
76
  RuboCop officially supports the following runtime Ruby implementations:
77
77
 
78
- * MRI 2.6+
79
- * JRuby 9.3+
78
+ * MRI 2.7+
79
+ * JRuby 9.4+
80
80
 
81
81
  Targets Ruby 2.0+ code analysis.
82
82
 
data/config/default.yml CHANGED
@@ -140,7 +140,7 @@ AllCops:
140
140
  # or gems.locked file. (Although the Ruby version is specified in the Gemfile
141
141
  # or gems.rb file, RuboCop reads the final value from the lock file.) If the
142
142
  # Ruby version is still unresolved, RuboCop will use the oldest officially
143
- # supported Ruby version (currently Ruby 2.6).
143
+ # supported Ruby version (currently Ruby 2.7).
144
144
  TargetRubyVersion: ~
145
145
  # Determines if a notification for extension libraries should be shown when
146
146
  # rubocop is run. Keys are the name of the extension, and values are an array
@@ -154,6 +154,7 @@ AllCops:
154
154
  rubocop-rake: [rake]
155
155
  rubocop-graphql: [graphql]
156
156
  rubocop-capybara: [capybara]
157
+ rubocop-factory_bot: [factory_bot, factory_bot_rails]
157
158
  # Enable/Disable checking the methods extended by Active Support.
158
159
  ActiveSupportExtensionsEnabled: false
159
160
 
@@ -2343,6 +2344,9 @@ Lint/TopLevelReturnWithArgument:
2343
2344
  Description: 'Detects top level return statements with argument.'
2344
2345
  Enabled: true
2345
2346
  VersionAdded: '0.89'
2347
+ # These codes are `eval`-ed in method and their return values may be used.
2348
+ Exclude:
2349
+ - '**/*.jb'
2346
2350
 
2347
2351
  Lint/TrailingCommaInAttributeDeclaration:
2348
2352
  Description: 'Checks for trailing commas in attribute declarations.'
@@ -2451,6 +2455,8 @@ Lint/UselessAssignment:
2451
2455
  StyleGuide: '#underscore-unused-vars'
2452
2456
  Enabled: true
2453
2457
  VersionAdded: '0.11'
2458
+ VersionChanged: '1.51'
2459
+ SafeAutoCorrect: false
2454
2460
 
2455
2461
  Lint/UselessElseWithoutRescue:
2456
2462
  Description: 'Checks for useless `else` in `begin..end` without `rescue`.'
@@ -2942,7 +2948,9 @@ Naming/VariableNumber:
2942
2948
  Security/CompoundHash:
2943
2949
  Description: 'When overwriting Object#hash to combine values, prefer delegating to Array#hash over writing a custom implementation.'
2944
2950
  Enabled: pending
2951
+ Safe: false
2945
2952
  VersionAdded: '1.28'
2953
+ VersionChanged: '1.51'
2946
2954
 
2947
2955
  Security/Eval:
2948
2956
  Description: 'The use of eval represents a serious security risk.'
@@ -3515,7 +3523,9 @@ Style/DataInheritance:
3515
3523
  Description: 'Checks for inheritance from Data.define.'
3516
3524
  StyleGuide: '#no-extend-data-define'
3517
3525
  Enabled: pending
3526
+ SafeAutoCorrect: false
3518
3527
  VersionAdded: '1.49'
3528
+ VersionChanged: '1.51'
3519
3529
 
3520
3530
  Style/DateTime:
3521
3531
  Description: 'Use Time over DateTime.'
@@ -3706,6 +3716,11 @@ Style/EvenOdd:
3706
3716
  VersionAdded: '0.12'
3707
3717
  VersionChanged: '0.29'
3708
3718
 
3719
+ Style/ExactRegexpMatch:
3720
+ Description: 'Checks for exact regexp match inside Regexp literals.'
3721
+ Enabled: pending
3722
+ VersionAdded: '1.51'
3723
+
3709
3724
  Style/ExpandPathArguments:
3710
3725
  Description: "Use `expand_path(__dir__)` instead of `expand_path('..', __FILE__)`."
3711
3726
  Enabled: true
@@ -4092,8 +4107,6 @@ Style/InvertibleUnlessCondition:
4092
4107
  # :blank?: :present?
4093
4108
  # :include?: :exclude?
4094
4109
  # :exclude?: :include?
4095
- # :one?: :many?
4096
- # :many?: :one?
4097
4110
 
4098
4111
  Style/IpAddresses:
4099
4112
  Description: "Don't include literal IP addresses in code."
@@ -4624,7 +4637,9 @@ Style/OpenStructUse:
4624
4637
  - https://docs.ruby-lang.org/en/3.0.0/OpenStruct.html#class-OpenStruct-label-Caveats
4625
4638
 
4626
4639
  Enabled: pending
4640
+ Safe: false
4627
4641
  VersionAdded: '1.23'
4642
+ VersionChanged: '1.51'
4628
4643
 
4629
4644
  Style/OperatorMethodCall:
4630
4645
  Description: 'Checks for redundant dot before operator method call.'
@@ -284,6 +284,10 @@ module RuboCop
284
284
  end
285
285
  end
286
286
 
287
+ def inspect # :nodoc:
288
+ "#<#{self.class.name}:#{object_id} @loaded_path=#{loaded_path}>"
289
+ end
290
+
287
291
  private
288
292
 
289
293
  def target_rails_version_from_bundler_lock_file
@@ -68,11 +68,11 @@ module RuboCop
68
68
  # Cop rules are keyed by the name of the original cop
69
69
  def load_cop_rules(rules)
70
70
  rules.flat_map do |rule_type, data|
71
- data.map do |cop_name, configuration|
71
+ data.filter_map do |cop_name, configuration|
72
72
  next unless configuration # allow configurations to be disabled with `CopName: ~`
73
73
 
74
74
  COP_RULE_CLASSES[rule_type].new(@config, cop_name, configuration)
75
- end.compact
75
+ end
76
76
  end
77
77
  end
78
78
 
@@ -305,6 +305,10 @@ module RuboCop
305
305
  @current_original = original
306
306
  end
307
307
 
308
+ def inspect # :nodoc:
309
+ "#<#{self.class.name}:#{object_id} @config=#{@config} @options=#{@options}>"
310
+ end
311
+
308
312
  private
309
313
 
310
314
  ### Reserved for Cop::Cop
@@ -54,7 +54,7 @@ module RuboCop
54
54
  def inside_string_ranges(node)
55
55
  return [] unless node.is_a?(Parser::AST::Node)
56
56
 
57
- node.each_node(:str, :dstr, :xstr).map { |n| inside_string_range(n) }.compact
57
+ node.each_node(:str, :dstr, :xstr).filter_map { |n| inside_string_range(n) }
58
58
  end
59
59
 
60
60
  def inside_string_range(node)
@@ -75,7 +75,7 @@ module RuboCop
75
75
 
76
76
  # @!method add_development_dependency?(node)
77
77
  def_node_matcher :add_development_dependency?, <<~PATTERN
78
- (send _ :add_development_dependency (str #forbidden_gem? ...))
78
+ (send _ :add_development_dependency (str #forbidden_gem? ...) _? _?)
79
79
  PATTERN
80
80
 
81
81
  # @!method gem?(node)
@@ -59,12 +59,12 @@ module RuboCop
59
59
  def method_directives(node)
60
60
  comments = processed_source.ast_with_comments[node]
61
61
 
62
- comments.map do |comment|
62
+ comments.filter_map do |comment|
63
63
  match = comment.text.match(REGEXP)
64
64
  next unless match
65
65
 
66
66
  { node: comment, method_name: match[:method_name], args: match[:args] }
67
- end.compact
67
+ end
68
68
  end
69
69
 
70
70
  def too_many_directives(node)
@@ -228,9 +228,9 @@ module RuboCop
228
228
  end
229
229
 
230
230
  def find_most_bottom_of_heredoc_end(arguments)
231
- arguments.map do |argument|
231
+ arguments.filter_map do |argument|
232
232
  argument.loc.heredoc_end.end_pos if argument.loc.respond_to?(:heredoc_end)
233
- end.compact.max
233
+ end.max
234
234
  end
235
235
 
236
236
  # Internal trailing comma helpers.
@@ -236,6 +236,8 @@ module RuboCop
236
236
  end
237
237
 
238
238
  def offense(begin_pos, end_pos, msg, style_param = 'EnforcedStyle')
239
+ return if begin_pos > end_pos
240
+
239
241
  range = range_between(begin_pos, end_pos)
240
242
  add_offense(range, message: msg) do |corrector|
241
243
  case range.source
@@ -52,6 +52,8 @@ module RuboCop
52
52
  # expect { do_something }.to not_change { object.attribute }
53
53
  #
54
54
  class AmbiguousBlockAssociation < Base
55
+ extend AutoCorrector
56
+
55
57
  include AllowedMethods
56
58
  include AllowedPattern
57
59
 
@@ -68,7 +70,9 @@ module RuboCop
68
70
 
69
71
  message = message(node)
70
72
 
71
- add_offense(node, message: message)
73
+ add_offense(node, message: message) do |corrector|
74
+ wrap_in_parentheses(corrector, node)
75
+ end
72
76
  end
73
77
  alias on_csend on_send
74
78
 
@@ -89,6 +93,13 @@ module RuboCop
89
93
 
90
94
  format(MSG, param: block_param.source, method: block_param.send_node.source)
91
95
  end
96
+
97
+ def wrap_in_parentheses(corrector, node)
98
+ range = node.loc.selector.end.join(node.first_argument.source_range.begin)
99
+
100
+ corrector.replace(range, '(')
101
+ corrector.insert_after(node.last_argument, ')')
102
+ end
92
103
  end
93
104
  end
94
105
  end
@@ -8,9 +8,9 @@ module RuboCop
8
8
  #
9
9
  # [source,console]
10
10
  # ----
11
- # % cat example.rb
11
+ # $ cat example.rb
12
12
  # ERB.new('hi', nil, '-', '@output_buffer')
13
- # % ruby -rerb example.rb
13
+ # $ ruby -rerb example.rb
14
14
  # example.rb:1: warning: Passing safe_level with the 2nd argument of ERB.new is
15
15
  # deprecated. Do not use it, and specify other arguments as keyword arguments.
16
16
  # example.rb:1: warning: Passing trim_mode with the 3rd argument of ERB.new is
@@ -6,6 +6,9 @@ module RuboCop
6
6
  #
7
7
  # This cop checks for `IO.select` that is incompatible with Fiber Scheduler since Ruby 3.0.
8
8
  #
9
+ # When an array of IO objects waiting for an exception (the third argument of `IO.select`)
10
+ # is used as an argument, there is no alternative API, so offenses are not registered.
11
+ #
9
12
  # NOTE: When the method is successful the return value of `IO.select` is `[[IO]]`,
10
13
  # and the return value of `io.wait_readable` and `io.wait_writable` are `self`.
11
14
  # They are not autocorrected when assigning a return value because these types are different.
@@ -42,8 +45,8 @@ module RuboCop
42
45
  PATTERN
43
46
 
44
47
  def on_send(node)
45
- read, write, _excepts, timeout = *io_select(node)
46
- return unless read
48
+ read, write, excepts, timeout = *io_select(node)
49
+ return if excepts && !excepts.children.empty?
47
50
  return unless scheduler_compatible?(read, write) || scheduler_compatible?(write, read)
48
51
 
49
52
  preferred = preferred_method(read, write, timeout)
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks uses of lambda without a literal block.
7
7
  # It emulates the following warning in Ruby 3.0:
8
8
  #
9
- # % ruby -vwe 'lambda(&proc {})'
9
+ # $ ruby -vwe 'lambda(&proc {})'
10
10
  # ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
11
11
  # -e:1: warning: lambda without a literal block is deprecated; use the proc without
12
12
  # lambda instead
@@ -6,13 +6,13 @@ module RuboCop
6
6
  # Checks for uses of numbered parameter assignment.
7
7
  # It emulates the following warning in Ruby 2.7:
8
8
  #
9
- # % ruby -ve '_1 = :value'
9
+ # $ ruby -ve '_1 = :value'
10
10
  # ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
11
11
  # -e:1: warning: `_1' is reserved for numbered parameter; consider another name
12
12
  #
13
13
  # Assigning to a numbered parameter (from `_1` to `_9`) causes an error in Ruby 3.0.
14
14
  #
15
- # % ruby -ve '_1 = :value'
15
+ # $ ruby -ve '_1 = :value'
16
16
  # ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
17
17
  # -e:1: _1 is reserved for numbered parameter
18
18
  #
@@ -65,13 +65,13 @@ module RuboCop
65
65
  def on_when(node)
66
66
  regexp_conditions = node.conditions.select(&:regexp_type?)
67
67
 
68
- @valid_ref = regexp_conditions.map { |condition| check_regexp(condition) }.compact.max
68
+ @valid_ref = regexp_conditions.filter_map { |condition| check_regexp(condition) }.max
69
69
  end
70
70
 
71
71
  def on_in_pattern(node)
72
72
  regexp_patterns = regexp_patterns(node)
73
73
 
74
- @valid_ref = regexp_patterns.map { |pattern| check_regexp(pattern) }.compact.max
74
+ @valid_ref = regexp_patterns.filter_map { |pattern| check_regexp(pattern) }.max
75
75
  end
76
76
 
77
77
  def on_nth_ref(node)
@@ -47,7 +47,7 @@ module RuboCop
47
47
  return if node.receiver
48
48
 
49
49
  node.each_child_node(:send) do |child|
50
- next unless child.method?(:to_s)
50
+ next if !child.method?(:to_s) || child.arguments.any?
51
51
 
52
52
  register_offense(child, "`#{node.method_name}`")
53
53
  end
@@ -8,25 +8,39 @@ module RuboCop
8
8
  # always ignored. This is detected automatically since Ruby 2.7.
9
9
  #
10
10
  # @example
11
+ # # bad
12
+ # return 1
11
13
  #
12
- # # Detected since Ruby 2.7
13
- # return 1 # 1 is always ignored.
14
+ # # good
15
+ # return
14
16
  class TopLevelReturnWithArgument < Base
15
- # This cop works by validating the ancestors of the return node. A
16
- # top-level return node's ancestors should not be of block, def, or
17
- # defs type.
17
+ extend AutoCorrector
18
18
 
19
19
  MSG = 'Top level return with argument detected.'
20
20
 
21
21
  def on_return(return_node)
22
- add_offense(return_node) if return_node.arguments? && ancestors_valid?(return_node)
22
+ return unless top_level_return_with_any_argument?(return_node)
23
+
24
+ add_offense(return_node) do |corrector|
25
+ remove_arguments(corrector, return_node)
26
+ end
23
27
  end
24
28
 
25
29
  private
26
30
 
27
- def ancestors_valid?(return_node)
28
- prohibited_ancestors = return_node.each_ancestor(:block, :def, :defs)
29
- prohibited_ancestors.none?
31
+ def top_level_return_with_any_argument?(return_node)
32
+ top_level_return?(return_node) && return_node.arguments?
33
+ end
34
+
35
+ def remove_arguments(corrector, return_node)
36
+ corrector.replace(return_node, 'return')
37
+ end
38
+
39
+ # This cop works by validating the ancestors of the return node. A
40
+ # top-level return node's ancestors should not be of block, def, or
41
+ # defs type.
42
+ def top_level_return?(return_node)
43
+ return_node.each_ancestor(:block, :def, :defs).none?
30
44
  end
31
45
  end
32
46
  end
@@ -13,6 +13,12 @@ module RuboCop
13
13
  # reassignments and properly handles varied cases such as branch, loop,
14
14
  # rescue, ensure, etc.
15
15
  #
16
+ # @safety
17
+ # This cop's autocorrection is unsafe because removing assignment from
18
+ # operator assignment can cause NameError if this assignment has been used to declare
19
+ # local variable. For example, replacing `a ||= 1` to `a || 1` may cause
20
+ # "undefined local variable or method `a' for main:Object (NameError)".
21
+ #
16
22
  # @example
17
23
  #
18
24
  # # bad
@@ -31,6 +37,10 @@ module RuboCop
31
37
  # do_something(some_var)
32
38
  # end
33
39
  class UselessAssignment < Base
40
+ extend AutoCorrector
41
+
42
+ include RangeHelp
43
+
34
44
  MSG = 'Useless assignment to variable - `%<variable>s`.'
35
45
 
36
46
  def self.joining_forces
@@ -55,7 +65,9 @@ module RuboCop
55
65
  assignment.node.loc.name
56
66
  end
57
67
 
58
- add_offense(location, message: message)
68
+ add_offense(location, message: message) do |corrector|
69
+ autocorrect(corrector, assignment)
70
+ end
59
71
  end
60
72
  end
61
73
 
@@ -119,6 +131,49 @@ module RuboCop
119
131
 
120
132
  node.receiver.nil? && !node.arguments?
121
133
  end
134
+
135
+ def autocorrect(corrector, assignment)
136
+ if assignment.exception_assignment?
137
+ remove_exception_assignment_part(corrector, assignment.node)
138
+ elsif assignment.multiple_assignment?
139
+ rename_variable_with_underscore(corrector, assignment.node)
140
+ elsif assignment.operator_assignment?
141
+ remove_trailing_character_from_operator(corrector, assignment.node)
142
+ elsif assignment.regexp_named_capture?
143
+ replace_named_capture_group_with_non_capturing_group(corrector, assignment.node,
144
+ assignment.variable.name)
145
+ else
146
+ remove_local_variable_assignment_part(corrector, assignment.node)
147
+ end
148
+ end
149
+
150
+ def remove_exception_assignment_part(corrector, node)
151
+ corrector.remove(
152
+ range_between(
153
+ (node.parent.children.first&.source_range || node.parent.location.keyword).end_pos,
154
+ node.source_range.end_pos
155
+ )
156
+ )
157
+ end
158
+
159
+ def rename_variable_with_underscore(corrector, node)
160
+ corrector.replace(node, '_')
161
+ end
162
+
163
+ def remove_trailing_character_from_operator(corrector, node)
164
+ corrector.remove(node.parent.location.operator.end.adjust(begin_pos: -1))
165
+ end
166
+
167
+ def replace_named_capture_group_with_non_capturing_group(corrector, node, variable_name)
168
+ corrector.replace(
169
+ node.children.first,
170
+ node.children.first.source.sub(/\(\?<#{variable_name}>/, '(?:')
171
+ )
172
+ end
173
+
174
+ def remove_local_variable_assignment_part(corrector, node)
175
+ corrector.replace(node, node.expression.source)
176
+ end
122
177
  end
123
178
  end
124
179
  end
@@ -41,6 +41,10 @@ module RuboCop
41
41
  # do_something(some_array)
42
42
  # end
43
43
  class Void < Base
44
+ extend AutoCorrector
45
+
46
+ include RangeHelp
47
+
44
48
  OP_MSG = 'Operator `%<op>s` used in void context.'
45
49
  VAR_MSG = 'Variable `%<var>s` used in void context.'
46
50
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
@@ -100,31 +104,43 @@ module RuboCop
100
104
  def check_void_op(node)
101
105
  return unless node.send_type? && OPERATORS.include?(node.method_name)
102
106
 
103
- add_offense(node.loc.selector, message: format(OP_MSG, op: node.method_name))
107
+ add_offense(node.loc.selector,
108
+ message: format(OP_MSG, op: node.method_name)) do |corrector|
109
+ autocorrect_void_op(corrector, node)
110
+ end
104
111
  end
105
112
 
106
113
  def check_var(node)
107
114
  return unless node.variable? || node.const_type?
108
115
 
109
- add_offense(node.loc.name, message: format(VAR_MSG, var: node.loc.name.source))
116
+ add_offense(node.loc.name,
117
+ message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
118
+ autocorrect_void_var(corrector, node)
119
+ end
110
120
  end
111
121
 
112
122
  def check_literal(node)
113
123
  return if !node.literal? || node.xstr_type? || node.range_type?
114
124
 
115
- add_offense(node, message: format(LIT_MSG, lit: node.source))
125
+ add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
126
+ autocorrect_void_literal(corrector, node)
127
+ end
116
128
  end
117
129
 
118
130
  def check_self(node)
119
131
  return unless node.self_type?
120
132
 
121
- add_offense(node, message: SELF_MSG)
133
+ add_offense(node, message: SELF_MSG) do |corrector|
134
+ autocorrect_void_self(corrector, node)
135
+ end
122
136
  end
123
137
 
124
138
  def check_void_expression(node)
125
139
  return unless node.defined_type? || node.lambda_or_proc?
126
140
 
127
- add_offense(node, message: format(EXPRESSION_MSG, expression: node.source))
141
+ add_offense(node, message: format(EXPRESSION_MSG, expression: node.source)) do |corrector|
142
+ autocorrect_void_expression(corrector, node)
143
+ end
128
144
  end
129
145
 
130
146
  def check_nonmutating(node)
@@ -139,7 +155,10 @@ module RuboCop
139
155
  "#{method_name}!"
140
156
  end
141
157
  add_offense(node,
142
- message: format(NONMUTATING_MSG, method: method_name, suggest: suggestion))
158
+ message: format(NONMUTATING_MSG, method: method_name,
159
+ suggest: suggestion)) do |corrector|
160
+ autocorrect_nonmutating_send(corrector, node, suggestion)
161
+ end
143
162
  end
144
163
 
145
164
  def in_void_context?(node)
@@ -149,6 +168,43 @@ module RuboCop
149
168
 
150
169
  VOID_CONTEXT_TYPES.include?(parent.type) && parent.void_context?
151
170
  end
171
+
172
+ def autocorrect_void_op(corrector, node)
173
+ if node.arguments.empty?
174
+ corrector.replace(node, node.receiver.source)
175
+ else
176
+ corrector.replace(
177
+ range_with_surrounding_space(range: node.loc.selector, side: :both,
178
+ newlines: false),
179
+ "\n"
180
+ )
181
+ end
182
+ end
183
+
184
+ def autocorrect_void_var(corrector, node)
185
+ corrector.remove(range_with_surrounding_space(range: node.loc.name, side: :left))
186
+ end
187
+
188
+ def autocorrect_void_literal(corrector, node)
189
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
190
+ end
191
+
192
+ def autocorrect_void_self(corrector, node)
193
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
194
+ end
195
+
196
+ def autocorrect_void_expression(corrector, node)
197
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
198
+ end
199
+
200
+ def autocorrect_nonmutating_send(corrector, node, suggestion)
201
+ send_node = if node.send_type?
202
+ node
203
+ else
204
+ node.send_node
205
+ end
206
+ corrector.replace(send_node.loc.selector, suggestion)
207
+ end
152
208
  end
153
209
  end
154
210
  end
@@ -62,12 +62,16 @@ module RuboCop
62
62
  # Returns the end line of a node, which might be a comment and not part of the AST
63
63
  # End line is considered either the line at which another node starts, or
64
64
  # the line at which the parent node ends.
65
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
66
66
  def find_end_line(node)
67
67
  if node.if_type? && node.else?
68
68
  node.loc.else.line
69
69
  elsif node.if_type? && node.ternary?
70
70
  node.else_branch.loc.line
71
+ elsif node.if_type? && node.elsif?
72
+ node.each_ancestor(:if).find(&:if?).loc.end.line
73
+ elsif node.block_type? || node.numblock_type?
74
+ node.loc.end.line
71
75
  elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
72
76
  next_sibling.loc.line
73
77
  elsif (parent = node.parent)
@@ -76,7 +80,7 @@ module RuboCop
76
80
  node.loc.end.line
77
81
  end
78
82
  end
79
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
80
84
  end
81
85
  end
82
86
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def allowed_type?(token)
39
- %i[tRPAREN tRBRACK tPIPE].include?(token.type)
39
+ %i[tRPAREN tRBRACK tPIPE tSTRING_DEND].include?(token.type)
40
40
  end
41
41
 
42
42
  def space_forbidden_before_rcurly?
@@ -76,7 +76,7 @@ module RuboCop
76
76
  end
77
77
 
78
78
  def contains_constant?(node)
79
- node.branches.any?(&:const_type?)
79
+ node.branches.compact.any?(&:const_type?)
80
80
  end
81
81
  end
82
82
  end