rubocop 1.66.1 → 1.68.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +55 -6
  4. data/config/internal_affairs.yml +11 -0
  5. data/lib/rubocop/cached_data.rb +12 -4
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  7. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  8. data/lib/rubocop/cli/command/lsp.rb +2 -2
  9. data/lib/rubocop/cli/command/version.rb +2 -2
  10. data/lib/rubocop/config_loader_resolver.rb +3 -3
  11. data/lib/rubocop/config_validator.rb +2 -1
  12. data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
  13. data/lib/rubocop/cop/base.rb +6 -2
  14. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  15. data/lib/rubocop/cop/cop.rb +8 -0
  16. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  17. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  18. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  19. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  20. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  21. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  22. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  23. data/lib/rubocop/cop/internal_affairs.rb +16 -0
  24. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  25. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  26. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  27. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  28. data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
  29. data/lib/rubocop/cop/layout/leading_comment_space.rb +56 -1
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  31. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  32. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  33. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  34. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  35. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  36. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  37. data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
  38. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  39. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  40. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
  41. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  42. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
  43. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +7 -0
  44. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  45. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
  46. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
  47. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +107 -41
  48. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  49. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  50. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  51. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
  52. data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
  53. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  54. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
  55. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  56. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  57. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  58. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  59. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  60. data/lib/rubocop/cop/offense.rb +4 -5
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
  62. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  63. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  64. data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
  65. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  66. data/lib/rubocop/cop/style/block_delimiters.rb +31 -3
  67. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  68. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  69. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  70. data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
  71. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  72. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  73. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  74. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  75. data/lib/rubocop/cop/style/guard_clause.rb +15 -2
  76. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  77. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  78. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  79. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -3
  80. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  81. data/lib/rubocop/cop/style/lambda.rb +1 -1
  82. data/lib/rubocop/cop/style/map_into_array.rb +59 -8
  83. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
  84. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  85. data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
  86. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  87. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  88. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
  89. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  90. data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
  91. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  92. data/lib/rubocop/cop/style/redundant_line_continuation.rb +23 -5
  93. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -11
  94. data/lib/rubocop/cop/style/require_order.rb +1 -1
  95. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  96. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  97. data/lib/rubocop/cop/style/safe_navigation.rb +104 -50
  98. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  99. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  100. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  101. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  102. data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
  103. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  104. data/lib/rubocop/cop/team.rb +8 -1
  105. data/lib/rubocop/cop/util.rb +1 -1
  106. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  107. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  108. data/lib/rubocop/cop/variable_force/variable.rb +5 -1
  109. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  110. data/lib/rubocop/cops_documentation_generator.rb +81 -40
  111. data/lib/rubocop/file_finder.rb +9 -4
  112. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  113. data/lib/rubocop/lsp/runtime.rb +2 -0
  114. data/lib/rubocop/lsp/server.rb +0 -1
  115. data/lib/rubocop/rspec/expect_offense.rb +1 -0
  116. data/lib/rubocop/runner.rb +17 -6
  117. data/lib/rubocop/server/cache.rb +6 -1
  118. data/lib/rubocop/server/core.rb +1 -0
  119. data/lib/rubocop/target_ruby.rb +13 -13
  120. data/lib/rubocop/version.rb +28 -7
  121. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  122. data/lib/rubocop.rb +10 -0
  123. metadata +13 -4
@@ -3,13 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # Checks for indentation that doesn't use the specified number
7
- # of spaces.
6
+ # Checks for indentation that doesn't use the specified number of spaces.
7
+ # The indentation width can be configured using the `Width` setting. The default width is 2.
8
8
  #
9
- # See also the IndentationConsistency cop which is the companion to this
10
- # one.
9
+ # See also the `Layout/IndentationConsistency` cop which is the companion to this one.
11
10
  #
12
- # @example
11
+ # @example Width: 2 (default)
13
12
  # # bad
14
13
  # class A
15
14
  # def test
@@ -49,18 +49,57 @@ module RuboCop
49
49
  # #ruby=2.7.0
50
50
  # #ruby-gemset=myproject
51
51
  #
52
+ # @example AllowRBSInlineAnnotation: false (default)
53
+ #
54
+ # # bad
55
+ #
56
+ # include Enumerable #[Integer]
57
+ #
58
+ # attr_reader :name #: String
59
+ # attr_reader :age #: Integer?
60
+ #
61
+ # @example AllowRBSInlineAnnotation: true
62
+ #
63
+ # # good
64
+ #
65
+ # include Enumerable #[Integer]
66
+ #
67
+ # attr_reader :name #: String
68
+ # attr_reader :age #: Integer?
69
+ #
70
+ # @example AllowSteepAnnotation: false (default)
71
+ #
72
+ # # bad
73
+ # [1, 2, 3].each_with_object([]) do |n, list| #$ Array[Integer]
74
+ # list << n
75
+ # end
76
+ #
77
+ # name = 'John' #: String
78
+ #
79
+ # @example AllowSteepAnnotation: true
80
+ #
81
+ # # good
82
+ #
83
+ # [1, 2, 3].each_with_object([]) do |n, list| #$ Array[Integer]
84
+ # list << n
85
+ # end
86
+ #
87
+ # name = 'John' #: String
88
+ #
52
89
  class LeadingCommentSpace < Base
53
90
  include RangeHelp
54
91
  extend AutoCorrector
55
92
 
56
93
  MSG = 'Missing space after `#`.'
57
94
 
58
- def on_new_investigation
95
+ def on_new_investigation # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
59
96
  processed_source.comments.each do |comment|
60
97
  next unless /\A(?!#\+\+|#--)(#+[^#\s=])/.match?(comment.text)
61
98
  next if comment.loc.line == 1 && allowed_on_first_line?(comment)
62
99
  next if doxygen_comment_style?(comment)
63
100
  next if gemfile_ruby_comment?(comment)
101
+ next if rbs_inline_annotation?(comment)
102
+ next if steep_annotation?(comment)
64
103
 
65
104
  add_offense(comment) do |corrector|
66
105
  expr = comment.source_range
@@ -115,6 +154,22 @@ module RuboCop
115
154
  def gemfile_ruby_comment?(comment)
116
155
  allow_gemfile_ruby_comment? && ruby_comment_in_gemfile?(comment)
117
156
  end
157
+
158
+ def allow_rbs_inline_annotation?
159
+ cop_config['AllowRBSInlineAnnotation']
160
+ end
161
+
162
+ def rbs_inline_annotation?(comment)
163
+ allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]/)
164
+ end
165
+
166
+ def allow_steep_annotation?
167
+ cop_config['AllowSteepAnnotation']
168
+ end
169
+
170
+ def steep_annotation?(comment)
171
+ allow_steep_annotation? && comment.text.start_with?(/#[$:]/)
172
+ end
118
173
  end
119
174
  end
120
175
  end
@@ -33,12 +33,12 @@ module RuboCop
33
33
  private
34
34
 
35
35
  def offense_range(node, begin_pos)
36
- if reference_variable_with_brackets?(node)
37
- receiver_end_pos = node.receiver.source_range.end_pos
38
- selector_begin_pos = node.loc.selector.begin_pos
39
- return if receiver_end_pos >= selector_begin_pos
40
- return if dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
36
+ receiver_end_pos = node.receiver.source_range.end_pos
37
+ selector_begin_pos = node.loc.selector.begin_pos
38
+ return if receiver_end_pos >= selector_begin_pos
39
+ return if dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
41
40
 
41
+ if reference_variable_with_brackets?(node)
42
42
  range_between(receiver_end_pos, selector_begin_pos)
43
43
  elsif node.method?(:[]=)
44
44
  offense_range_for_assignment(node, begin_pos)
@@ -82,6 +82,10 @@ module RuboCop
82
82
  include RangeHelp
83
83
  extend AutoCorrector
84
84
 
85
+ def self.autocorrect_incompatible_with
86
+ [Style::BlockDelimiters]
87
+ end
88
+
85
89
  def on_block(node)
86
90
  return if node.keywords?
87
91
 
@@ -57,6 +57,7 @@ module RuboCop
57
57
  # # good
58
58
  # (a.foo)..(b.bar)
59
59
  class AmbiguousRange < Base
60
+ include RationalLiteral
60
61
  extend AutoCorrector
61
62
 
62
63
  MSG = 'Wrap complex range boundaries with parentheses to avoid ambiguity.'
@@ -79,12 +80,14 @@ module RuboCop
79
80
  yield range.end if range.end
80
81
  end
81
82
 
83
+ # rubocop:disable Metrics/CyclomaticComplexity
82
84
  def acceptable?(node)
83
85
  node.begin_type? ||
84
- node.literal? ||
86
+ node.literal? || rational_literal?(node) ||
85
87
  node.variable? || node.const_type? || node.self_type? ||
86
88
  (node.call_type? && acceptable_call?(node))
87
89
  end
90
+ # rubocop:enable Metrics/CyclomaticComplexity
88
91
 
89
92
  def acceptable_call?(node)
90
93
  return true if node.unary_operation?
@@ -17,8 +17,7 @@ module RuboCop
17
17
  class BigDecimalNew < Base
18
18
  extend AutoCorrector
19
19
 
20
- MSG = '`%<double_colon>sBigDecimal.new()` is deprecated. ' \
21
- 'Use `%<double_colon>sBigDecimal()` instead.'
20
+ MSG = '`BigDecimal.new()` is deprecated. Use `BigDecimal()` instead.'
22
21
  RESTRICT_ON_SEND = %i[new].freeze
23
22
 
24
23
  # @!method big_decimal_new(node)
@@ -28,13 +27,11 @@ module RuboCop
28
27
  PATTERN
29
28
 
30
29
  def on_send(node)
31
- big_decimal_new(node) do |captured_value|
32
- double_colon = captured_value ? '::' : ''
33
- message = format(MSG, double_colon: double_colon)
34
-
35
- add_offense(node.loc.selector, message: message) do |corrector|
30
+ big_decimal_new(node) do |cbase|
31
+ add_offense(node.loc.selector) do |corrector|
36
32
  corrector.remove(node.loc.selector)
37
33
  corrector.remove(node.loc.dot)
34
+ corrector.remove(cbase) if cbase
38
35
  end
39
36
  end
40
37
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  return unless boolean_symbol?(node)
37
37
 
38
38
  parent = node.parent
39
- return if parent&.array_type? && parent&.percent_literal?(:symbol)
39
+ return if parent&.array_type? && parent.percent_literal?(:symbol)
40
40
 
41
41
  add_offense(node, message: format(MSG, boolean: node.value)) do |corrector|
42
42
  autocorrect(corrector, node)
@@ -15,6 +15,9 @@ module RuboCop
15
15
  # With `IgnoreConstantBranches: true`, branches are not registered
16
16
  # as offenses if they return a constant value.
17
17
  #
18
+ # With `IgnoreDuplicateElseBranch: true`, in conditionals with multiple branches,
19
+ # duplicate 'else' branches are not registered as offenses.
20
+ #
18
21
  # @example
19
22
  # # bad
20
23
  # if foo
@@ -83,21 +86,37 @@ module RuboCop
83
86
  # else MEDIUM_SIZE
84
87
  # end
85
88
  #
89
+ # @example IgnoreDuplicateElseBranch: true
90
+ # # good
91
+ # if foo
92
+ # do_foo
93
+ # elsif bar
94
+ # do_bar
95
+ # else
96
+ # do_foo
97
+ # end
98
+ #
86
99
  class DuplicateBranch < Base
87
100
  MSG = 'Duplicate branch body detected.'
88
101
 
89
102
  def on_branching_statement(node)
90
- branches(node).each_with_object(Set.new) do |branch, previous|
91
- next unless consider_branch?(branch)
103
+ branches = branches(node)
104
+ branches.each_with_object(Set.new) do |branch, previous|
105
+ next unless consider_branch?(branches, branch)
92
106
 
93
107
  add_offense(offense_range(branch)) unless previous.add?(branch)
94
108
  end
95
109
  end
96
- alias on_if on_branching_statement
97
110
  alias on_case on_branching_statement
98
111
  alias on_case_match on_branching_statement
99
112
  alias on_rescue on_branching_statement
100
113
 
114
+ def on_if(node)
115
+ # Ignore 'elsif' nodes, because we don't want to check them separately whether
116
+ # the 'else' branch is duplicated. We want to check only on the outermost conditional.
117
+ on_branching_statement(node) unless node.elsif?
118
+ end
119
+
101
120
  private
102
121
 
103
122
  def offense_range(duplicate_branch)
@@ -118,10 +137,14 @@ module RuboCop
118
137
  node.branches.compact
119
138
  end
120
139
 
121
- def consider_branch?(branch)
140
+ def consider_branch?(branches, branch)
122
141
  return false if ignore_literal_branches? && literal_branch?(branch)
123
142
  return false if ignore_constant_branches? && const_branch?(branch)
124
143
 
144
+ if ignore_duplicate_else_branches? && duplicate_else_branch?(branches, branch)
145
+ return false
146
+ end
147
+
125
148
  true
126
149
  end
127
150
 
@@ -133,6 +156,10 @@ module RuboCop
133
156
  cop_config.fetch('IgnoreConstantBranches', false)
134
157
  end
135
158
 
159
+ def ignore_duplicate_else_branches?
160
+ cop_config.fetch('IgnoreDuplicateElseBranch', false)
161
+ end
162
+
136
163
  def literal_branch?(branch) # rubocop:disable Metrics/CyclomaticComplexity
137
164
  return false if !branch.literal? || branch.xstr_type?
138
165
  return true if branch.basic_literal?
@@ -147,6 +174,14 @@ module RuboCop
147
174
  def const_branch?(branch)
148
175
  branch.const_type?
149
176
  end
177
+
178
+ def duplicate_else_branch?(branches, branch)
179
+ return false unless (parent = branch.parent)
180
+
181
+ branches.size > 2 &&
182
+ branch.equal?(branches.last) &&
183
+ parent.respond_to?(:else?) && parent.else?
184
+ end
150
185
  end
151
186
  end
152
187
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for duplicate literal, constant, or variable elements in Set.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # Set[:foo, :bar, :foo]
12
+ #
13
+ # # good
14
+ # Set[:foo, :bar]
15
+ #
16
+ # # bad
17
+ # Set.new([:foo, :bar, :foo])
18
+ #
19
+ # # good
20
+ # Set.new([:foo, :bar])
21
+ #
22
+ # # bad
23
+ # [:foo, :bar, :foo].to_set
24
+ #
25
+ # # good
26
+ # [:foo, :bar].to_set
27
+ #
28
+ class DuplicateSetElement < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Remove the duplicate element in Set.'
32
+ RESTRICT_ON_SEND = %i[\[\] new to_set].freeze
33
+
34
+ # @!method set_init_elements(node)
35
+ def_node_matcher :set_init_elements, <<~PATTERN
36
+ {
37
+ (send (const {nil? cbase} :Set) :[] $...)
38
+ (send (const {nil? cbase} :Set) :new (array $...))
39
+ (call (array $...) :to_set)
40
+ }
41
+ PATTERN
42
+
43
+ def on_send(node)
44
+ return unless (set_elements = set_init_elements(node))
45
+
46
+ seen_elements = Set[]
47
+
48
+ set_elements.each_with_index do |set_element, index|
49
+ # NOTE: Skip due to the possibility of corner cases where Set elements
50
+ # may have changing return values if they are not literals, constants, or variables.
51
+ next if !set_element.literal? && !set_element.const_type? && !set_element.variable?
52
+
53
+ if seen_elements.include?(set_element)
54
+ register_offense(set_element, set_elements[index - 1])
55
+ else
56
+ seen_elements << set_element
57
+ end
58
+ end
59
+ end
60
+ alias on_csend on_send
61
+
62
+ private
63
+
64
+ def register_offense(current_element, prev_element)
65
+ add_offense(current_element) do |corrector|
66
+ range = prev_element.source_range.end.join(current_element.source_range.end)
67
+
68
+ corrector.remove(range)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -40,9 +40,6 @@ module RuboCop
40
40
  # cleanup
41
41
  # end
42
42
  class EnsureReturn < Base
43
- extend AutoCorrector
44
- include RangeHelp
45
-
46
43
  MSG = 'Do not return from an `ensure` block.'
47
44
 
48
45
  def on_ensure(node)
@@ -91,7 +91,7 @@ module RuboCop
91
91
  next if !argument || argument.hash_type?
92
92
 
93
93
  add_offense(
94
- argument.source_range, message: message(i, argument.source)
94
+ argument, message: message(i, argument.source)
95
95
  ) do |corrector|
96
96
  autocorrect(corrector, node)
97
97
  end
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # # good - using BigDecimal
19
19
  # x.to_d == 0.1.to_d
20
20
  #
21
- # # good - comparing against zero
21
+ # # good - comparing against zero
22
22
  # x == 0.0
23
23
  # x != 0.0
24
24
  #
@@ -28,7 +28,7 @@ module RuboCop
28
28
  FOR_METHOD = ' Or, if they were intended to be separate method ' \
29
29
  'arguments, separate them with a comma.'
30
30
 
31
- # rubocop:disable Metrics/AbcSize
31
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
32
32
  def on_dstr(node)
33
33
  each_bad_cons(node) do |lhs_node, rhs_node|
34
34
  range = lhs_node.source_range.join(rhs_node.source_range)
@@ -40,13 +40,19 @@ module RuboCop
40
40
  end
41
41
 
42
42
  add_offense(range, message: message) do |corrector|
43
- range = lhs_node.source_range.end.join(rhs_node.source_range.begin)
43
+ if lhs_node.value == ''
44
+ corrector.remove(lhs_node)
45
+ elsif rhs_node.value == ''
46
+ corrector.remove(rhs_node)
47
+ else
48
+ range = lhs_node.source_range.end.join(rhs_node.source_range.begin)
44
49
 
45
- corrector.replace(range, ' + ')
50
+ corrector.replace(range, ' + ')
51
+ end
46
52
  end
47
53
  end
48
54
  end
49
- # rubocop:enable Metrics/AbcSize
55
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
50
56
 
51
57
  private
52
58
 
@@ -29,25 +29,16 @@ module RuboCop
29
29
 
30
30
  MSG = '`it` calls without arguments will refer to the first block param in Ruby 3.4; ' \
31
31
  'use `it()` or `self.it`.'
32
+ RESTRICT_ON_SEND = %i[it].freeze
32
33
 
33
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
34
- return unless (body = node.body)
35
- return unless node.arguments.empty_and_without_delimiters?
34
+ def on_send(node)
35
+ return unless (block_node = node.each_ancestor(:block).first)
36
+ return unless block_node.arguments.empty_and_without_delimiters?
36
37
 
37
- if body.send_type? && deprecated_it_method?(body)
38
- add_offense(body)
39
- else
40
- body.each_descendant(:send).each do |send_node|
41
- next unless deprecated_it_method?(send_node)
42
-
43
- add_offense(send_node)
44
- end
45
- end
38
+ add_offense(node) if deprecated_it_method?(node)
46
39
  end
47
40
 
48
41
  def deprecated_it_method?(node)
49
- return false unless node.method?(:it)
50
-
51
42
  !node.receiver && node.arguments.empty? && !node.parenthesized? && !node.block_literal?
52
43
  end
53
44
  end
@@ -30,6 +30,8 @@ module RuboCop
30
30
  # interpolation should not be removed if the expanded value
31
31
  # contains a space character.
32
32
  expanded_value = autocorrected_value(final_node)
33
+ expanded_value = handle_special_regexp_chars(begin_node, expanded_value)
34
+
33
35
  return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
34
36
 
35
37
  add_offense(final_node) do |corrector|
@@ -77,6 +79,27 @@ module RuboCop
77
79
  end
78
80
  # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
79
81
 
82
+ def handle_special_regexp_chars(begin_node, value)
83
+ parent_node = begin_node.parent
84
+
85
+ return value unless parent_node.regexp_type? && parent_node.slash_literal? && value['/']
86
+
87
+ # When a literal string containing a forward slash preceded by backslashes
88
+ # is interpolated inside a regexp, the number of resultant backslashes in the
89
+ # compiled Regexp is `(2(n+1) / 4)+1`, where `n` is the number of backslashes
90
+ # inside the interpolation.
91
+ # ie. 0-2 backslashes is compiled to 1, 3-6 is compiled to 3, etc.
92
+ # This maintains that same behavior in order to ensure the Regexp behavior
93
+ # does not change upon removing the interpolation.
94
+ value.gsub(%r{(\\*)/}) do
95
+ backslashes = Regexp.last_match[1]
96
+ backslash_count = backslashes.length
97
+ needed_backslashes = (2 * ((backslash_count + 1) / 4)) + 1
98
+
99
+ "#{'\\' * needed_backslashes}/"
100
+ end
101
+ end
102
+
80
103
  def autocorrected_value_for_string(node)
81
104
  if node.source.start_with?("'", '%q')
82
105
  node.children.last.inspect[1..-2]
@@ -150,7 +173,7 @@ module RuboCop
150
173
 
151
174
  def ends_heredoc_line?(node)
152
175
  grandparent = node.parent.parent
153
- return false unless grandparent&.dstr_type? && grandparent&.heredoc?
176
+ return false unless grandparent&.dstr_type? && grandparent.heredoc?
154
177
 
155
178
  line = processed_source.lines[node.last_line - 1]
156
179
  line.size == node.loc.last_column + 1
@@ -161,7 +184,7 @@ module RuboCop
161
184
  return false unless parent.dstr_type? || parent.dsym_type?
162
185
 
163
186
  grandparent = parent.parent
164
- grandparent&.array_type? && grandparent&.percent_literal?
187
+ grandparent&.array_type? && grandparent.percent_literal?
165
188
  end
166
189
  end
167
190
  end
@@ -134,6 +134,7 @@ module RuboCop
134
134
 
135
135
  corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
136
136
  corrector.replace(node.loc.selector, replacement_method(node))
137
+ corrector.insert_before(node.last_argument, 'mode: ') if require_mode_keyword?(node)
137
138
  end
138
139
 
139
140
  def replacement_method(node)
@@ -152,6 +153,12 @@ module RuboCop
152
153
  force_method_name?(node) || force_option?(node)
153
154
  end
154
155
 
156
+ def require_mode_keyword?(node)
157
+ return false unless node.receiver.const_name == 'Dir'
158
+
159
+ replacement_method(node) == 'mkdir_p' && node.arguments.length == 2
160
+ end
161
+
155
162
  def force_option?(node)
156
163
  node.arguments.any? { |arg| force?(arg) }
157
164
  end
@@ -37,9 +37,7 @@ module RuboCop
37
37
  private
38
38
 
39
39
  def valid_context?(node)
40
- unless node.arguments.one? && first_argument_starts_with_left_parenthesis?(node)
41
- return true
42
- end
40
+ return true unless node.arguments.one? && node.first_argument.parenthesized_call?
43
41
  return true if first_argument_block_type?(node.first_argument)
44
42
 
45
43
  node.operator_method? || node.setter_method? || chained_calls?(node) ||
@@ -51,11 +49,12 @@ module RuboCop
51
49
  end
52
50
 
53
51
  def valid_first_argument?(first_arg)
54
- first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg)
52
+ first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg) ||
53
+ compound_range?(first_arg)
55
54
  end
56
55
 
57
- def first_argument_starts_with_left_parenthesis?(node)
58
- node.first_argument.source.start_with?('(')
56
+ def compound_range?(first_arg)
57
+ first_arg.range_type? && first_arg.parenthesized_call?
59
58
  end
60
59
 
61
60
  def chained_calls?(node)
@@ -131,7 +131,7 @@ module RuboCop
131
131
  private
132
132
 
133
133
  def assume_receiver_instance_exists?(receiver)
134
- return true if receiver.const_type? && !receiver.source.match?(SNAKE_CASE)
134
+ return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
135
135
 
136
136
  receiver.literal? && !receiver.nil_type?
137
137
  end
@@ -38,6 +38,8 @@ module RuboCop
38
38
  PATTERN
39
39
 
40
40
  def on_send(node)
41
+ return unless require_safe_navigation?(node)
42
+
41
43
  bad_method?(node) do |safe_nav, method|
42
44
  return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
43
45
 
@@ -52,6 +54,13 @@ module RuboCop
52
54
 
53
55
  private
54
56
 
57
+ def require_safe_navigation?(node)
58
+ parent = node.parent
59
+ return true unless parent&.and_type?
60
+
61
+ parent.rhs != node || parent.lhs.receiver != parent.rhs.receiver
62
+ end
63
+
55
64
  # @param [Parser::Source::Range] offense_range
56
65
  # @param [RuboCop::AST::SendNode] send_node
57
66
  # @return [String]