rubocop 1.26.1 → 1.27.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +15 -2
  4. data/lib/rubocop/config_obsoletion/extracted_cop.rb +3 -1
  5. data/lib/rubocop/cop/autocorrect_logic.rb +4 -0
  6. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -5
  7. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -5
  8. data/lib/rubocop/cop/layout/indentation_width.rb +1 -2
  9. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -4
  10. data/lib/rubocop/cop/lint/ambiguous_operator.rb +6 -6
  11. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  12. data/lib/rubocop/cop/lint/empty_in_pattern.rb +3 -1
  13. data/lib/rubocop/cop/lint/empty_when.rb +3 -1
  14. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +11 -4
  15. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +8 -1
  16. data/lib/rubocop/cop/lint/refinement_import_methods.rb +51 -0
  17. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +10 -0
  18. data/lib/rubocop/cop/lint/syntax.rb +1 -2
  19. data/lib/rubocop/cop/lint/unused_method_argument.rb +1 -1
  20. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  21. data/lib/rubocop/cop/mixin/comments_help.rb +22 -2
  22. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
  23. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -2
  24. data/lib/rubocop/cop/mixin/surrounding_space.rb +4 -2
  25. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  26. data/lib/rubocop/cop/style/double_negation.rb +32 -1
  27. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  28. data/lib/rubocop/cop/style/file_write.rb +12 -0
  29. data/lib/rubocop/cop/style/raise_args.rb +5 -2
  30. data/lib/rubocop/cop/style/redundant_capital_w.rb +1 -2
  31. data/lib/rubocop/cop/style/redundant_initialize.rb +119 -0
  32. data/lib/rubocop/cop/style/safe_navigation.rb +12 -7
  33. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -2
  34. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -4
  35. data/lib/rubocop/formatter/offense_count_formatter.rb +6 -2
  36. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  37. data/lib/rubocop/result_cache.rb +9 -1
  38. data/lib/rubocop/version.rb +1 -1
  39. data/lib/rubocop.rb +2 -0
  40. metadata +5 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10e6b8905d81b2204fd3d0bf60d3278cd902d0cefc2ed387bf2c317062b46034
4
- data.tar.gz: f27011464d465728f1fa470c85c4779c2b148b3cdc872247c5a25d421f269076
3
+ metadata.gz: d48fd91bc57ba3589a331c3c0141d3e168c7631e5db47238a88cecbd10b12292
4
+ data.tar.gz: 9393d3cab7e09e86a9276ca4614f8f2c4fc2895f2070bd3364096569c2cd5828
5
5
  SHA512:
6
- metadata.gz: 215ee2bb6f0699fdedcd82c7795c40e2350c33fdfbd48117dbb2fdcaa957bcbff9a65ad9c95b9666d4b9c70d6a2e2d8b6ec6904c3c3f01cbbfa8fee2319c9518
7
- data.tar.gz: 0b33b7c3aecf2b7482e19cdbf1a94a56f21d3cb14035c68c62ce0572385edb34a59faa1ea5fb21d9504b04e18f797b0419c6ade76ff70604311b9f899e28b39d
6
+ metadata.gz: 13d85485214ca48904b07a6da11f89d2e2ef33fceab05912fb4c4f4c1e025da4be79139845d13724a749f97eb837b943eef5515639a5116299b6a164d7517944
7
+ data.tar.gz: 668abbcaedb1f06c43a2e5f61774909d684767659979bc3787c0827ca22adf42be0edf1d30544909a74fc6aac18bcd3758c7c5f77352cb97b51fd96acd24ab18
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.26', require: false
56
+ gem 'rubocop', '~> 1.27', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -2058,6 +2058,12 @@ Lint/RedundantWithObject:
2058
2058
  Enabled: true
2059
2059
  VersionAdded: '0.51'
2060
2060
 
2061
+ Lint/RefinementImportMethods:
2062
+ Description: 'Use `Refinement#import_methods` when using `include` or `prepend` in `refine` block.'
2063
+ Enabled: pending
2064
+ SafeAutoCorrect: false
2065
+ VersionAdded: '1.27'
2066
+
2061
2067
  Lint/RegexpAsCondition:
2062
2068
  Description: >-
2063
2069
  Do not use regexp literal as a condition.
@@ -2327,8 +2333,8 @@ Lint/UselessMethodDefinition:
2327
2333
  Description: 'Checks for useless method definitions.'
2328
2334
  Enabled: true
2329
2335
  VersionAdded: '0.90'
2336
+ VersionChanged: '0.91'
2330
2337
  Safe: false
2331
- AllowComments: true
2332
2338
 
2333
2339
  Lint/UselessRuby2Keywords:
2334
2340
  Description: 'Finds unnecessary uses of `ruby2_keywords`.'
@@ -4524,6 +4530,11 @@ Style/RedundantFreeze:
4524
4530
  VersionAdded: '0.34'
4525
4531
  VersionChanged: '0.66'
4526
4532
 
4533
+ Style/RedundantInitialize:
4534
+ Description: 'Checks for redundant `initialize` methods.'
4535
+ Enabled: pending
4536
+ VersionAdded: '1.27'
4537
+
4527
4538
  Style/RedundantInterpolation:
4528
4539
  Description: 'Checks for strings that are just an interpolated expression.'
4529
4540
  Enabled: true
@@ -4645,7 +4656,7 @@ Style/SafeNavigation:
4645
4656
  be `nil` or truthy, but never `false`.
4646
4657
  Enabled: true
4647
4658
  VersionAdded: '0.43'
4648
- VersionChanged: '0.77'
4659
+ VersionChanged: '1.27'
4649
4660
  # Safe navigation may cause a statement to start returning `nil` in addition
4650
4661
  # to whatever it used to return.
4651
4662
  ConvertCodeThatCanStartToReturnNil: false
@@ -4656,6 +4667,8 @@ Style/SafeNavigation:
4656
4667
  - try
4657
4668
  - try!
4658
4669
  SafeAutoCorrect: false
4670
+ # Maximum length of method chains for register an offense.
4671
+ MaxChainLength: 2
4659
4672
 
4660
4673
  Style/Sample:
4661
4674
  Description: >-
@@ -33,7 +33,9 @@ module RuboCop
33
33
  return old_name unless old_name.end_with?('*')
34
34
 
35
35
  # Handle whole departments (expressed as `Department/*`)
36
- config.keys.grep(Regexp.new("^#{department}"))
36
+ config.keys.select do |key|
37
+ key == department || key.start_with?("#{department}/")
38
+ end
37
39
  end
38
40
 
39
41
  def feature_loaded?
@@ -8,6 +8,10 @@ module RuboCop
8
8
  autocorrect_requested? && correctable? && autocorrect_enabled?
9
9
  end
10
10
 
11
+ def autocorrect_with_disable_uncorrectable?
12
+ autocorrect_requested? && disable_uncorrectable? && autocorrect_enabled?
13
+ end
14
+
11
15
  def autocorrect_requested?
12
16
  @options.fetch(:auto_correct, false)
13
17
  end
@@ -46,11 +46,7 @@ module RuboCop
46
46
 
47
47
  duplicated_gem_nodes.each do |nodes|
48
48
  nodes[1..-1].each do |node|
49
- register_offense(
50
- node,
51
- node.first_argument.to_a.first,
52
- nodes.first.first_line
53
- )
49
+ register_offense(node, node.first_argument.to_a.first, nodes.first.first_line)
54
50
  end
55
51
  end
56
52
  end
@@ -52,11 +52,7 @@ module RuboCop
52
52
 
53
53
  duplicated_assignment_method_nodes.each do |nodes|
54
54
  nodes[1..-1].each do |node|
55
- register_offense(
56
- node,
57
- node.method_name,
58
- nodes.first.first_line
59
- )
55
+ register_offense(node, node.method_name, nodes.first.first_line)
60
56
  end
61
57
  end
62
58
  end
@@ -185,8 +185,7 @@ module RuboCop
185
185
 
186
186
  def check_members_for_indented_internal_methods_style(members)
187
187
  each_member(members) do |member, previous_modifier|
188
- check_indentation(previous_modifier, member,
189
- indentation_consistency_style)
188
+ check_indentation(previous_modifier, member, indentation_consistency_style)
190
189
  end
191
190
  end
192
191
 
@@ -105,10 +105,9 @@ module RuboCop
105
105
  end
106
106
 
107
107
  def convertible_block?(node)
108
- return false unless node.parent&.block_type?
109
-
110
- send_node = node.parent&.send_node
111
- send_node.parenthesized? || !send_node.arguments?
108
+ parent = node.parent
109
+ parent&.block_type? && node == parent.send_node &&
110
+ (node.parenthesized? || !node.arguments?)
112
111
  end
113
112
 
114
113
  def comment_within?(node)
@@ -24,11 +24,11 @@ module RuboCop
24
24
  extend AutoCorrector
25
25
 
26
26
  AMBIGUITIES = {
27
- '+' => { actual: 'positive number', possible: 'addition' },
28
- '-' => { actual: 'negative number', possible: 'subtraction' },
29
- '*' => { actual: 'splat', possible: 'multiplication' },
30
- '&' => { actual: 'block', possible: 'binary AND' },
31
- '**' => { actual: 'keyword splat', possible: 'exponent' }
27
+ '+' => { actual: 'positive number', possible: 'an addition' },
28
+ '-' => { actual: 'negative number', possible: 'a subtraction' },
29
+ '*' => { actual: 'splat', possible: 'a multiplication' },
30
+ '&' => { actual: 'block', possible: 'a binary AND' },
31
+ '**' => { actual: 'keyword splat', possible: 'an exponent' }
32
32
  }.each do |key, hash|
33
33
  hash[:operator] = key
34
34
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  MSG_FORMAT = 'Ambiguous %<actual>s operator. Parenthesize the method ' \
37
37
  "arguments if it's surely a %<actual>s operator, or add " \
38
38
  'a whitespace to the right of the `%<operator>s` if it ' \
39
- 'should be a %<possible>s.'
39
+ 'should be %<possible>s.'
40
40
 
41
41
  def on_new_investigation
42
42
  processed_source.diagnostics.each do |diagnostic|
@@ -53,11 +53,13 @@ module RuboCop
53
53
  # end
54
54
  #
55
55
  class EmptyConditionalBody < Base
56
+ include CommentsHelp
57
+
56
58
  MSG = 'Avoid `%<keyword>s` branches without a body.'
57
59
 
58
60
  def on_if(node)
59
61
  return if node.body
60
- return if cop_config['AllowComments'] && comment_lines?(node)
62
+ return if cop_config['AllowComments'] && contains_comments?(node)
61
63
 
62
64
  add_offense(node, message: format(MSG, keyword: node.keyword))
63
65
  end
@@ -44,6 +44,7 @@ module RuboCop
44
44
  #
45
45
  class EmptyInPattern < Base
46
46
  extend TargetRubyVersion
47
+ include CommentsHelp
47
48
 
48
49
  MSG = 'Avoid `in` branches without a body.'
49
50
 
@@ -51,7 +52,8 @@ module RuboCop
51
52
 
52
53
  def on_case_match(node)
53
54
  node.in_pattern_branches.each do |branch|
54
- next if branch.body || (cop_config['AllowComments'] && comment_lines?(node))
55
+ next if branch.body
56
+ next if cop_config['AllowComments'] && contains_comments?(branch)
55
57
 
56
58
  add_offense(branch)
57
59
  end
@@ -45,12 +45,14 @@ module RuboCop
45
45
  # end
46
46
  #
47
47
  class EmptyWhen < Base
48
+ include CommentsHelp
49
+
48
50
  MSG = 'Avoid `when` branches without a body.'
49
51
 
50
52
  def on_case(node)
51
53
  node.each_when do |when_node|
52
54
  next if when_node.body
53
- next if cop_config['AllowComments'] && comment_lines?(node)
55
+ next if cop_config['AllowComments'] && contains_comments?(when_node)
54
56
 
55
57
  add_offense(when_node)
56
58
  end
@@ -6,6 +6,15 @@ 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
+ # NOTE: When the method is successful the return value of `IO.select` is `[[IO]]`,
10
+ # and the return value of `io.wait_readable` and `io.wait_writable` are `self`.
11
+ # They are not auto-corrected when assigning a return value because these types are different.
12
+ # It's up to user how to handle the return value.
13
+ #
14
+ # @safety
15
+ # This cop's autocorrection is unsafe because `NoMethodError` occurs
16
+ # if `require 'io/wait'` is not called.
17
+ #
9
18
  # @example
10
19
  #
11
20
  # # bad
@@ -20,10 +29,6 @@ module RuboCop
20
29
  # # good
21
30
  # io.wait_writable(timeout)
22
31
  #
23
- # @safety
24
- # This cop's autocorrection is unsafe because `NoMethodError` occurs
25
- # if `require 'io/wait'` is not called.
26
- #
27
32
  class IncompatibleIoSelectWithFiberScheduler < Base
28
33
  extend AutoCorrector
29
34
 
@@ -45,6 +50,8 @@ module RuboCop
45
50
  message = format(MSG, preferred: preferred, current: node.source)
46
51
 
47
52
  add_offense(node, message: message) do |corrector|
53
+ next if node.parent&.assignment?
54
+
48
55
  corrector.replace(node, preferred)
49
56
  end
50
57
  end
@@ -31,8 +31,15 @@ module RuboCop
31
31
  MSG = 'lambda without a literal block is deprecated; use the proc without lambda instead.'
32
32
  RESTRICT_ON_SEND = %i[lambda].freeze
33
33
 
34
+ # @!method lambda_with_symbol_proc?(node)
35
+ def_node_matcher :lambda_with_symbol_proc?, <<~PATTERN
36
+ (send nil? :lambda (block_pass (sym _)))
37
+ PATTERN
38
+
34
39
  def on_send(node)
35
- return if node.parent&.block_type? || !node.first_argument
40
+ if node.parent&.block_type? || !node.first_argument || lambda_with_symbol_proc?(node)
41
+ return
42
+ end
36
43
 
37
44
  add_offense(node) do |corrector|
38
45
  corrector.replace(node, node.first_argument.source.delete('&'))
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks if `include` or `prepend` is called in `refine` block.
7
+ # These methods are deprecated and should be replaced with `Refinement#import_methods`.
8
+ #
9
+ # It emulates deprecation warnings in Ruby 3.1.
10
+ #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because `include M` will affect the included class
13
+ # if any changes are made to module `M`.
14
+ # On the other hand, `import_methods M` uses a snapshot of method definitions,
15
+ # thus it will not be affected if module `M` changes.
16
+ #
17
+ # @example
18
+ #
19
+ # # bad
20
+ # refine Foo do
21
+ # include Bar
22
+ # end
23
+ #
24
+ # # bad
25
+ # refine Foo do
26
+ # prepend Bar
27
+ # end
28
+ #
29
+ # # good
30
+ # refine Foo do
31
+ # import_methods Bar
32
+ # end
33
+ #
34
+ class RefinementImportMethods < Base
35
+ extend TargetRubyVersion
36
+
37
+ MSG = 'Use `import_methods` instead of `%<current>s` because it is deprecated in Ruby 3.1.'
38
+ RESTRICT_ON_SEND = %i[include prepend].freeze
39
+
40
+ minimum_target_ruby_version 3.1
41
+
42
+ def on_send(node)
43
+ return if node.receiver
44
+ return unless node.parent.block_type? && node.parent.method?(:refine)
45
+
46
+ add_offense(node.loc.selector, message: format(MSG, current: node.method_name))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -57,10 +57,20 @@ module RuboCop
57
57
 
58
58
  outer_local_variable = variable_table.find_variable(variable.name)
59
59
  return unless outer_local_variable
60
+ return if same_conditions_node_different_branch?(variable, outer_local_variable)
60
61
 
61
62
  message = format(MSG, variable: variable.name)
62
63
  add_offense(variable.declaration_node, message: message)
63
64
  end
65
+
66
+ def same_conditions_node_different_branch?(variable, outer_local_variable)
67
+ variable_node = variable.scope.node.parent
68
+ return false unless variable_node.conditional?
69
+
70
+ outer_local_variable_node = outer_local_variable.scope.node
71
+
72
+ outer_local_variable_node.conditional? && variable_node == outer_local_variable_node
73
+ end
64
74
  end
65
75
  end
66
76
  end
@@ -9,8 +9,7 @@ module RuboCop
9
9
  def on_other_file
10
10
  add_offense_from_error(processed_source.parser_error) if processed_source.parser_error
11
11
  processed_source.diagnostics.each do |diagnostic|
12
- add_offense_from_diagnostic(diagnostic,
13
- processed_source.ruby_version)
12
+ add_offense_from_diagnostic(diagnostic, processed_source.ruby_version)
14
13
  end
15
14
  super
16
15
  end
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  # @!method not_implemented?(node)
66
66
  def_node_matcher :not_implemented?, <<~PATTERN
67
- {(send nil? :raise (const {nil? cbase} :NotImplementedError))
67
+ {(send nil? :raise (const {nil? cbase} :NotImplementedError) ...)
68
68
  (send nil? :fail ...)}
69
69
  PATTERN
70
70
 
@@ -101,8 +101,7 @@ module RuboCop
101
101
  children = node.masgn_type? ? node.children[0].children : node.children
102
102
 
103
103
  will_be_miscounted = children.count do |child|
104
- child.respond_to?(:setter_method?) &&
105
- !child.setter_method?
104
+ child.respond_to?(:setter_method?) && !child.setter_method?
106
105
  end
107
106
  @assignment += will_be_miscounted
108
107
 
@@ -4,8 +4,6 @@ module RuboCop
4
4
  module Cop
5
5
  # Help methods for working with nodes containing comments.
6
6
  module CommentsHelp
7
- include VisibilityHelp
8
-
9
7
  def source_range_with_comment(node)
10
8
  begin_pos = begin_pos_with_comment(node)
11
9
  end_pos = end_position_for(node)
@@ -13,6 +11,13 @@ module RuboCop
13
11
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
14
12
  end
15
13
 
14
+ def contains_comments?(node)
15
+ start_line = node.source_range.line
16
+ end_line = find_end_line(node)
17
+
18
+ processed_source.each_comment_in_lines(start_line...end_line).any?
19
+ end
20
+
16
21
  private
17
22
 
18
23
  def end_position_for(node)
@@ -37,6 +42,21 @@ module RuboCop
37
42
  def buffer
38
43
  processed_source.buffer
39
44
  end
45
+
46
+ # Returns the end line of a node, which might be a comment and not part of the AST
47
+ # End line is considered either the line at which another node starts, or
48
+ # the line at which the parent node ends.
49
+ def find_end_line(node)
50
+ if node.if_type? && node.loc.else
51
+ node.loc.else.line
52
+ elsif (next_sibling = node.right_sibling)
53
+ next_sibling.loc.line
54
+ elsif (parent = node.parent)
55
+ parent.loc.end.line
56
+ else
57
+ node.loc.end.line
58
+ end
59
+ end
40
60
  end
41
61
  end
42
62
  end
@@ -65,9 +65,8 @@ module RuboCop
65
65
 
66
66
  def use_modifier_form_without_parenthesized_method_call?(ancestor)
67
67
  return false if ancestor.respond_to?(:parenthesized?) && ancestor.parenthesized?
68
- return false unless (parent = ancestor.parent)
69
68
 
70
- parent.respond_to?(:modifier_form?) && parent.modifier_form?
69
+ ancestor.ancestors.any? { |node| node.respond_to?(:modifier_form?) && node.modifier_form? }
71
70
  end
72
71
 
73
72
  def without_parentheses_call_expr_follows?(ancestor)
@@ -196,8 +196,7 @@ module RuboCop
196
196
 
197
197
  def not_for_this_cop?(node)
198
198
  node.ancestors.any? do |ancestor|
199
- grouped_expression?(ancestor) ||
200
- inside_arg_list_parentheses?(node, ancestor)
199
+ grouped_expression?(ancestor) || inside_arg_list_parentheses?(node, ancestor)
201
200
  end
202
201
  end
203
202
 
@@ -44,7 +44,8 @@ module RuboCop
44
44
  if extra_space?(left_token, :left) && !start_ok
45
45
  space_offense(node, left_token, :right, message, NO_SPACE_COMMAND)
46
46
  end
47
- return if !extra_space?(right_token, :right) || end_ok
47
+ return if (!extra_space?(right_token, :right) || end_ok) ||
48
+ (autocorrect_with_disable_uncorrectable? && !start_ok)
48
49
 
49
50
  space_offense(node, right_token, :left, message, NO_SPACE_COMMAND)
50
51
  end
@@ -58,7 +59,8 @@ module RuboCop
58
59
  unless extra_space?(left_token, :left) || start_ok
59
60
  space_offense(node, left_token, :none, message, SPACE_COMMAND)
60
61
  end
61
- return if extra_space?(right_token, :right) || end_ok
62
+ return if (extra_space?(right_token, :right) || end_ok) ||
63
+ (autocorrect_with_disable_uncorrectable? && !start_ok)
62
64
 
63
65
  space_offense(node, right_token, :none, message, SPACE_COMMAND)
64
66
  end
@@ -107,7 +107,7 @@ module RuboCop
107
107
  def use_block_argument_as_local_variable?(node, last_argument)
108
108
  return if node.body.nil?
109
109
 
110
- node.body.each_descendant(:lvar).any? do |lvar|
110
+ node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
111
111
  !lvar.parent.block_pass_type? && lvar.source == last_argument
112
112
  end
113
113
  end
@@ -72,9 +72,14 @@ module RuboCop
72
72
  def end_of_method_definition?(node)
73
73
  return false unless (def_node = find_def_node_from_ascendant(node))
74
74
 
75
+ conditional_node = find_conditional_node_from_ascendant(node)
75
76
  last_child = find_last_child(def_node.body)
76
77
 
77
- last_child.last_line == node.last_line
78
+ if conditional_node
79
+ double_negative_condition_return_value?(node, last_child, conditional_node)
80
+ else
81
+ last_child.last_line == node.last_line
82
+ end
78
83
  end
79
84
 
80
85
  def find_def_node_from_ascendant(node)
@@ -84,6 +89,13 @@ module RuboCop
84
89
  find_def_node_from_ascendant(node.parent)
85
90
  end
86
91
 
92
+ def find_conditional_node_from_ascendant(node)
93
+ return unless (parent = node.parent)
94
+ return parent if parent.conditional?
95
+
96
+ find_conditional_node_from_ascendant(parent)
97
+ end
98
+
87
99
  def find_last_child(node)
88
100
  case node.type
89
101
  when :rescue
@@ -94,6 +106,25 @@ module RuboCop
94
106
  node.child_nodes.last
95
107
  end
96
108
  end
109
+
110
+ def double_negative_condition_return_value?(node, last_child, conditional_node)
111
+ parent = find_parent_not_enumerable(node)
112
+ if parent.begin_type?
113
+ node.loc.line == parent.loc.last_line
114
+ else
115
+ last_child.last_line <= conditional_node.last_line
116
+ end
117
+ end
118
+
119
+ def find_parent_not_enumerable(node)
120
+ return unless (parent = node.parent)
121
+
122
+ if parent.pair_type? || parent.hash_type? || parent.array_type?
123
+ find_parent_not_enumerable(parent)
124
+ else
125
+ parent
126
+ end
127
+ end
97
128
  end
98
129
  end
99
130
  end
@@ -47,8 +47,7 @@ module RuboCop
47
47
  branch_bodies = [*case_node.when_branches.map(&:body), case_node.else_branch].compact
48
48
 
49
49
  return if branch_bodies.any? do |body|
50
- body.return_type? ||
51
- body.each_descendant.any?(&:return_type?)
50
+ body.return_type? || body.each_descendant.any?(&:return_type?)
52
51
  end
53
52
 
54
53
  add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
@@ -5,6 +5,17 @@ module RuboCop
5
5
  module Style
6
6
  # Favor `File.(bin)write` convenience methods.
7
7
  #
8
+ # NOTE: There are different method signatures between `File.write` (class method)
9
+ # and `File#write` (instance method). The following case will be allowed because
10
+ # static analysis does not know the contents of the splat argument:
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # File.open(filename, 'w') do |f|
15
+ # f.write(*objects)
16
+ # end
17
+ # ----
18
+ #
8
19
  # @example
9
20
  # ## text mode
10
21
  # # bad
@@ -85,6 +96,7 @@ module RuboCop
85
96
  content = send_write?(node) || block_write?(node) do |block_arg, lvar, write_arg|
86
97
  write_arg if block_arg == lvar
87
98
  end
99
+ return false if content&.splat_type?
88
100
 
89
101
  yield(content) if content
90
102
  end
@@ -107,8 +107,7 @@ module RuboCop
107
107
 
108
108
  first_arg = node.first_argument
109
109
 
110
- return unless first_arg.send_type? && first_arg.method?(:new)
111
- return if acceptable_exploded_args?(first_arg.arguments)
110
+ return if !use_new_method?(first_arg) || acceptable_exploded_args?(first_arg.arguments)
112
111
 
113
112
  return if allowed_non_exploded_type?(first_arg)
114
113
 
@@ -120,6 +119,10 @@ module RuboCop
120
119
  end
121
120
  end
122
121
 
122
+ def use_new_method?(first_arg)
123
+ first_arg.send_type? && first_arg.receiver && first_arg.method?(:new)
124
+ end
125
+
123
126
  def acceptable_exploded_args?(args)
124
127
  # Allow code like `raise Ex.new(arg1, arg2)`.
125
128
  return true if args.size > 1
@@ -37,8 +37,7 @@ module RuboCop
37
37
 
38
38
  def requires_interpolation?(node)
39
39
  node.child_nodes.any? do |string|
40
- string.dstr_type? ||
41
- double_quotes_required?(string.source)
40
+ string.dstr_type? || double_quotes_required?(string.source)
42
41
  end
43
42
  end
44
43
  end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for `initialize` methods that are redundant.
7
+ #
8
+ # An initializer is redundant if it does not do anything, or if it only
9
+ # calls `super` with the same arguments given to it. If the initializer takes
10
+ # an argument that accepts multiple values (`restarg`, `kwrestarg`, etc.) it
11
+ # will not register an offense, because it allows the initializer to take a different
12
+ # number of arguments as its superclass potentially does.
13
+ #
14
+ # NOTE: If an initializer argument has a default value, RuboCop assumes it
15
+ # to *not* be redundant.
16
+ #
17
+ # NOTE: Empty initializers are registered as offenses, but it is possible
18
+ # to purposely create an empty `initialize` method to override a superclass's
19
+ # initializer.
20
+ #
21
+ # @example
22
+ # # bad
23
+ # def initialize
24
+ # end
25
+ #
26
+ # # bad
27
+ # def initialize
28
+ # super
29
+ # end
30
+ #
31
+ # # bad
32
+ # def initialize(a, b)
33
+ # super
34
+ # end
35
+ #
36
+ # # bad
37
+ # def initialize(a, b)
38
+ # super(a, b)
39
+ # end
40
+ #
41
+ # # good
42
+ # def initialize
43
+ # do_something
44
+ # end
45
+ #
46
+ # # good
47
+ # def initialize
48
+ # do_something
49
+ # super
50
+ # end
51
+ #
52
+ # # good (different number of parameters)
53
+ # def initialize(a, b)
54
+ # super(a)
55
+ # end
56
+ #
57
+ # # good (default value)
58
+ # def initialize(a, b = 5)
59
+ # super
60
+ # end
61
+ #
62
+ # # good (default value)
63
+ # def initialize(a, b: 5)
64
+ # super
65
+ # end
66
+ #
67
+ # # good (changes the parameter requirements)
68
+ # def initialize(*)
69
+ # end
70
+ #
71
+ # # good (changes the parameter requirements)
72
+ # def initialize(**)
73
+ # end
74
+ #
75
+ # # good (changes the parameter requirements)
76
+ # def initialize(...)
77
+ # end
78
+ #
79
+ class RedundantInitialize < Base
80
+ MSG = 'Remove unnecessary `initialize` method.'
81
+ MSG_EMPTY = 'Remove unnecessary empty `initialize` method.'
82
+
83
+ # @!method initialize_forwards?(node)
84
+ def_node_matcher :initialize_forwards?, <<~PATTERN
85
+ (def _ (args $arg*) $({super zsuper} ...))
86
+ PATTERN
87
+
88
+ def on_def(node)
89
+ return unless node.method?(:initialize)
90
+ return if forwards?(node)
91
+
92
+ if node.body.nil?
93
+ add_offense(node, message: MSG_EMPTY)
94
+ else
95
+ return if node.body.begin_type?
96
+
97
+ if (args, super_node = initialize_forwards?(node))
98
+ return unless same_args?(super_node, args)
99
+
100
+ add_offense(node)
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def forwards?(node)
108
+ node.arguments.each_child_node(:restarg, :kwrestarg, :forward_args, :forward_arg).any?
109
+ end
110
+
111
+ def same_args?(super_node, args)
112
+ return true if super_node.zsuper_type?
113
+
114
+ args.map(&:name) == super_node.arguments.map { |a| a.children[0] }
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -7,8 +7,7 @@ module RuboCop
7
7
  # check for the variable whose method is being called to
8
8
  # safe navigation (`&.`). If there is a method chain, all of the methods
9
9
  # in the chain need to be checked for safety, and all of the methods will
10
- # need to be changed to use safe navigation. We have limited the cop to
11
- # not register an offense for method chains that exceed 2 methods.
10
+ # need to be changed to use safe navigation.
12
11
  #
13
12
  # The default for `ConvertCodeThatCanStartToReturnNil` is `false`.
14
13
  # When configured to `true`, this will
@@ -18,6 +17,10 @@ module RuboCop
18
17
  # `foo&.bar` can start returning `nil` as well as what the method
19
18
  # returns.
20
19
  #
20
+ # The default for `MaxChainLength` is `2`
21
+ # We have limited the cop to not register an offense for method chains
22
+ # that exceed this option is set.
23
+ #
21
24
  # @safety
22
25
  # Autocorrection is unsafe because if a value is `false`, the resulting
23
26
  # code will have different behaviour or raise an error.
@@ -116,9 +119,7 @@ module RuboCop
116
119
  checked_variable, receiver, method_chain, method = extract_parts(node)
117
120
  return unless receiver == checked_variable
118
121
  return if use_var_only_in_unless_modifier?(node, checked_variable)
119
- # method is already a method call so this is actually checking for a
120
- # chain greater than 2
121
- return if chain_size(method_chain, method) > 1
122
+ return if chain_length(method_chain, method) > max_chain_length
122
123
  return if unsafe_method_used?(method_chain, method)
123
124
  return if method_chain.method?(:empty?)
124
125
 
@@ -225,8 +226,8 @@ module RuboCop
225
226
  find_matching_receiver_invocation(receiver, checked_variable)
226
227
  end
227
228
 
228
- def chain_size(method_chain, method)
229
- method.each_ancestor(:send).inject(0) do |total, ancestor|
229
+ def chain_length(method_chain, method)
230
+ method.each_ancestor(:send).inject(1) do |total, ancestor|
230
231
  break total + 1 if ancestor == method_chain
231
232
 
232
233
  total + 1
@@ -281,6 +282,10 @@ module RuboCop
281
282
  break if ancestor == method_chain
282
283
  end
283
284
  end
285
+
286
+ def max_chain_length
287
+ cop_config.fetch('MaxChainLength', 2)
288
+ end
284
289
  end
285
290
  end
286
291
  end
@@ -184,8 +184,7 @@ module RuboCop
184
184
 
185
185
  def unsafe_autocorrect?(condition)
186
186
  condition.children.any? do |child|
187
- unparenthesized_method_call?(child) ||
188
- below_ternary_precedence?(child)
187
+ unparenthesized_method_call?(child) || below_ternary_precedence?(child)
189
188
  end
190
189
  end
191
190
 
@@ -42,10 +42,7 @@ module RuboCop
42
42
  return if node.endless? || !trailing_end?(node)
43
43
 
44
44
  add_offense(node.loc.end) do |corrector|
45
- corrector.insert_before(
46
- node.loc.end,
47
- "\n#{' ' * node.loc.keyword.column}"
48
- )
45
+ corrector.insert_before(node.loc.end, "\n#{' ' * node.loc.keyword.column}")
49
46
  end
50
47
  end
51
48
 
@@ -17,6 +17,7 @@ module RuboCop
17
17
  def started(target_files)
18
18
  super
19
19
  @offense_counts = Hash.new(0)
20
+ @style_guide_links = {}
20
21
 
21
22
  return unless output.tty?
22
23
 
@@ -37,6 +38,9 @@ module RuboCop
37
38
 
38
39
  def file_finished(_file, offenses)
39
40
  offenses.each { |o| @offense_counts[o.cop_name] += 1 }
41
+ if options[:display_style_guide]
42
+ offenses.each { |o| @style_guide_links[o.cop_name] ||= o.message[/ \(http\S+\)\Z/] }
43
+ end
40
44
  @progressbar.increment if instance_variable_defined?(:@progressbar)
41
45
  end
42
46
 
@@ -52,8 +56,8 @@ module RuboCop
52
56
  output.puts
53
57
 
54
58
  per_cop_counts.each do |cop_name, count|
55
- output.puts "#{count.to_s.ljust(total_count.to_s.length + 2)}" \
56
- "#{cop_name}\n"
59
+ output.puts "#{count.to_s.ljust(total_count.to_s.length + 2)}#{cop_name}" \
60
+ "#{@style_guide_links[cop_name]}\n"
57
61
  end
58
62
  output.puts '--'
59
63
  output.puts "#{total_count} Total"
@@ -40,8 +40,7 @@ module RuboCop
40
40
  output.puts
41
41
 
42
42
  per_file_counts.each do |file_name, count|
43
- output.puts "#{count.to_s.ljust(total_count.to_s.length + 2)}" \
44
- "#{file_name}\n"
43
+ output.puts "#{count.to_s.ljust(total_count.to_s.length + 2)}#{file_name}\n"
45
44
  end
46
45
  output.puts '--'
47
46
  output.puts "#{total_count} Total"
@@ -73,7 +73,15 @@ module RuboCop
73
73
  # access.
74
74
  File.join(ENV['XDG_CACHE_HOME'], Process.uid.to_s)
75
75
  else
76
- File.join(ENV['HOME'], '.cache')
76
+ # On FreeBSD, the /home path is a symbolic link to /usr/home
77
+ # and the $HOME environment variable returns the /home path.
78
+ #
79
+ # As $HOME is a built-in environment variable, FreeBSD users
80
+ # always get a warning message.
81
+ #
82
+ # To avoid raising warn log messages on FreeBSD, we retrieve
83
+ # the real path of the home folder.
84
+ File.join(File.realpath(ENV['HOME']), '.cache')
77
85
  end
78
86
  File.join(root, 'rubocop_cache')
79
87
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.26.1'
6
+ STRING = '1.27.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -348,6 +348,7 @@ require_relative 'rubocop/cop/lint/redundant_splat_expansion'
348
348
  require_relative 'rubocop/cop/lint/redundant_string_coercion'
349
349
  require_relative 'rubocop/cop/lint/redundant_with_index'
350
350
  require_relative 'rubocop/cop/lint/redundant_with_object'
351
+ require_relative 'rubocop/cop/lint/refinement_import_methods'
351
352
  require_relative 'rubocop/cop/lint/regexp_as_condition'
352
353
  require_relative 'rubocop/cop/lint/require_parentheses'
353
354
  require_relative 'rubocop/cop/lint/require_relative_self_path'
@@ -527,6 +528,7 @@ require_relative 'rubocop/cop/style/open_struct_use'
527
528
  require_relative 'rubocop/cop/style/redundant_assignment'
528
529
  require_relative 'rubocop/cop/style/redundant_fetch_block'
529
530
  require_relative 'rubocop/cop/style/redundant_file_extension_in_require'
531
+ require_relative 'rubocop/cop/style/redundant_initialize'
530
532
  require_relative 'rubocop/cop/style/redundant_self_assignment'
531
533
  require_relative 'rubocop/cop/style/redundant_self_assignment_branch'
532
534
  require_relative 'rubocop/cop/style/sole_nested_conditional'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.26.1
4
+ version: 1.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-03-22 00:00:00.000000000 Z
13
+ date: 2022-04-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parallel
@@ -460,6 +460,7 @@ files:
460
460
  - lib/rubocop/cop/lint/redundant_string_coercion.rb
461
461
  - lib/rubocop/cop/lint/redundant_with_index.rb
462
462
  - lib/rubocop/cop/lint/redundant_with_object.rb
463
+ - lib/rubocop/cop/lint/refinement_import_methods.rb
463
464
  - lib/rubocop/cop/lint/regexp_as_condition.rb
464
465
  - lib/rubocop/cop/lint/require_parentheses.rb
465
466
  - lib/rubocop/cop/lint/require_relative_self_path.rb
@@ -778,6 +779,7 @@ files:
778
779
  - lib/rubocop/cop/style/redundant_fetch_block.rb
779
780
  - lib/rubocop/cop/style/redundant_file_extension_in_require.rb
780
781
  - lib/rubocop/cop/style/redundant_freeze.rb
782
+ - lib/rubocop/cop/style/redundant_initialize.rb
781
783
  - lib/rubocop/cop/style/redundant_interpolation.rb
782
784
  - lib/rubocop/cop/style/redundant_parentheses.rb
783
785
  - lib/rubocop/cop/style/redundant_percent_q.rb
@@ -913,7 +915,7 @@ metadata:
913
915
  homepage_uri: https://rubocop.org/
914
916
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
915
917
  source_code_uri: https://github.com/rubocop/rubocop/
916
- documentation_uri: https://docs.rubocop.org/rubocop/1.26/
918
+ documentation_uri: https://docs.rubocop.org/rubocop/1.27/
917
919
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
918
920
  rubygems_mfa_required: 'true'
919
921
  post_install_message: