rubocop 1.53.1 → 1.57.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +34 -8
  4. data/config/obsoletion.yml +5 -0
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config_finder.rb +2 -2
  8. data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
  9. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  10. data/lib/rubocop/cop/base.rb +1 -1
  11. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  12. data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
  13. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  14. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
  15. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  16. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -21
  18. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  19. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  20. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  21. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  22. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
  23. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +26 -3
  24. data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
  25. data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
  26. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  27. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  28. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
  29. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  30. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  31. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  32. data/lib/rubocop/cop/layout/redundant_line_break.rb +13 -3
  33. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  34. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  35. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  36. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -1
  37. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  38. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +17 -4
  40. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  42. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -1
  43. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
  44. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +10 -0
  45. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  46. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
  47. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
  48. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
  49. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  50. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  51. data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
  52. data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
  53. data/lib/rubocop/cop/lint/void.rb +32 -20
  54. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  55. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  56. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  57. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
  58. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  59. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  60. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  61. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  62. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  63. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
  64. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  65. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  66. data/lib/rubocop/cop/naming/file_name.rb +1 -1
  67. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
  68. data/lib/rubocop/cop/style/alias.rb +9 -8
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +280 -63
  70. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  71. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  72. data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -0
  73. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  74. data/lib/rubocop/cop/style/combinable_loops.rb +4 -2
  75. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  76. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  77. data/lib/rubocop/cop/style/for.rb +1 -1
  78. data/lib/rubocop/cop/style/format_string.rb +24 -3
  79. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
  80. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  81. data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
  82. data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -3
  83. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  84. data/lib/rubocop/cop/style/lambda.rb +3 -3
  85. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  86. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -1
  87. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  88. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  89. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  90. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  91. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  92. data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
  93. data/lib/rubocop/cop/style/redundant_argument.rb +6 -1
  94. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  95. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  97. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  98. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  99. data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -15
  100. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  101. data/lib/rubocop/cop/style/redundant_return.rb +7 -2
  102. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
  103. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  104. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +23 -9
  105. data/lib/rubocop/cop/style/semicolon.rb +0 -3
  106. data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
  107. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  108. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  109. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  110. data/lib/rubocop/cop/style/symbol_array.rb +35 -15
  111. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  112. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  113. data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
  114. data/lib/rubocop/cop/variable_force/assignment.rb +14 -5
  115. data/lib/rubocop/file_finder.rb +4 -7
  116. data/lib/rubocop/formatter/html_formatter.rb +4 -2
  117. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  118. data/lib/rubocop/lsp/routes.rb +41 -18
  119. data/lib/rubocop/lsp/runtime.rb +22 -2
  120. data/lib/rubocop/lsp/server.rb +10 -4
  121. data/lib/rubocop/magic_comment.rb +12 -10
  122. data/lib/rubocop/result_cache.rb +4 -0
  123. data/lib/rubocop/rspec/shared_contexts.rb +2 -3
  124. data/lib/rubocop/runner.rb +5 -3
  125. data/lib/rubocop/server/cache.rb +1 -0
  126. data/lib/rubocop/server/client_command/exec.rb +1 -1
  127. data/lib/rubocop/string_interpreter.rb +3 -3
  128. data/lib/rubocop/target_finder.rb +7 -3
  129. data/lib/rubocop/target_ruby.rb +9 -5
  130. data/lib/rubocop/version.rb +1 -1
  131. data/lib/rubocop.rb +2 -0
  132. metadata +16 -14
@@ -3,7 +3,23 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # Enforces empty line after guard clause
6
+ # Enforces empty line after guard clause.
7
+ #
8
+ # This cop allows `# :nocov:` directive after guard clause because
9
+ # SimpleCov excludes code from the coverage report by wrapping it in `# :nocov:`:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # def foo
14
+ # # :nocov:
15
+ # return if condition
16
+ # # :nocov:
17
+ # bar
18
+ # end
19
+ # ----
20
+ #
21
+ # Refer to SimpleCov's documentation for more details:
22
+ # https://github.com/simplecov-ruby/simplecov#ignoringskipping-code
7
23
  #
8
24
  # @example
9
25
  #
@@ -42,19 +58,22 @@ module RuboCop
42
58
 
43
59
  MSG = 'Add empty line after guard clause.'
44
60
  END_OF_HEREDOC_LINE = 1
61
+ SIMPLE_DIRECTIVE_COMMENT_PATTERN = /\A# *:nocov:\z/.freeze
45
62
 
46
63
  def on_if(node)
47
64
  return if correct_style?(node)
48
65
  return if multiple_statements_on_line?(node)
49
66
 
50
67
  if node.modifier_form? && (heredoc_node = last_heredoc_argument(node))
51
- return if next_line_empty_or_enable_directive_comment?(heredoc_line(node, heredoc_node))
68
+ if next_line_empty_or_allowed_directive_comment?(heredoc_line(node, heredoc_node))
69
+ return
70
+ end
52
71
 
53
72
  add_offense(heredoc_node.loc.heredoc_end) do |corrector|
54
73
  autocorrect(corrector, heredoc_node)
55
74
  end
56
75
  else
57
- return if next_line_empty_or_enable_directive_comment?(node.last_line)
76
+ return if next_line_empty_or_allowed_directive_comment?(node.last_line)
58
77
 
59
78
  add_offense(offense_location(node)) { |corrector| autocorrect(corrector, node) }
60
79
  end
@@ -70,7 +89,7 @@ module RuboCop
70
89
  end
71
90
 
72
91
  next_line = node_range.last_line + 1
73
- if next_line_enable_directive_comment?(next_line)
92
+ if next_line_allowed_directive_comment?(next_line)
74
93
  node_range = processed_source.comment_at_line(next_line)
75
94
  end
76
95
 
@@ -88,21 +107,21 @@ module RuboCop
88
107
  node.if_branch&.guard_clause?
89
108
  end
90
109
 
91
- def next_line_empty_or_enable_directive_comment?(line)
110
+ def next_line_empty_or_allowed_directive_comment?(line)
92
111
  return true if next_line_empty?(line)
93
112
 
94
113
  next_line = line + 1
95
- next_line_enable_directive_comment?(next_line) && next_line_empty?(next_line)
114
+ next_line_allowed_directive_comment?(next_line) && next_line_empty?(next_line)
96
115
  end
97
116
 
98
117
  def next_line_empty?(line)
99
118
  processed_source[line].blank?
100
119
  end
101
120
 
102
- def next_line_enable_directive_comment?(line)
121
+ def next_line_allowed_directive_comment?(line)
103
122
  return false unless (comment = processed_source.comment_at_line(line))
104
123
 
105
- DirectiveComment.new(comment).enabled?
124
+ DirectiveComment.new(comment).enabled? || simplecov_directive_comment?(comment)
106
125
  end
107
126
 
108
127
  def next_line_rescue_or_ensure?(node)
@@ -137,7 +156,7 @@ module RuboCop
137
156
  return node if node
138
157
  end
139
158
 
140
- return last_heredoc_argument(n.receiver) if n.respond_to?(:receiver)
159
+ last_heredoc_argument(n.receiver) if n.respond_to?(:receiver)
141
160
  end
142
161
 
143
162
  def last_heredoc_argument_node(node)
@@ -145,6 +164,8 @@ module RuboCop
145
164
 
146
165
  if node.if_branch.and_type?
147
166
  node.if_branch.children.first
167
+ elsif use_heredoc_in_condition?(node.condition)
168
+ node.condition
148
169
  else
149
170
  node.if_branch.children.last
150
171
  end
@@ -161,6 +182,12 @@ module RuboCop
161
182
  node.respond_to?(:heredoc?) && node.heredoc?
162
183
  end
163
184
 
185
+ def use_heredoc_in_condition?(condition)
186
+ condition.descendants.any? do |descendant|
187
+ descendant.respond_to?(:heredoc?) && descendant.heredoc?
188
+ end
189
+ end
190
+
164
191
  def offense_location(node)
165
192
  if node.loc.respond_to?(:end) && node.loc.end
166
193
  node.loc.end
@@ -175,6 +202,12 @@ module RuboCop
175
202
 
176
203
  parent.begin_type? && parent.single_line?
177
204
  end
205
+
206
+ # SimpleCov excludes code from the coverage report by wrapping it in `# :nocov:`:
207
+ # https://github.com/simplecov-ruby/simplecov#ignoringskipping-code
208
+ def simplecov_directive_comment?(comment)
209
+ SIMPLE_DIRECTIVE_COMMENT_PATTERN.match?(comment.text)
210
+ end
178
211
  end
179
212
  end
180
213
  end
@@ -135,7 +135,8 @@ module RuboCop
135
135
  return if nodes.all?(&:single_line?) && cop_config['AllowAdjacentOneLineDefs']
136
136
 
137
137
  correction_node = nodes.last
138
- location = correction_node.loc.keyword.join(correction_node.loc.name)
138
+
139
+ location = def_location(correction_node)
139
140
  add_offense(location, message: message(correction_node, count: count)) do |corrector|
140
141
  autocorrect(corrector, *nodes, count)
141
142
  end
@@ -159,10 +160,28 @@ module RuboCop
159
160
 
160
161
  private
161
162
 
163
+ def def_location(correction_node)
164
+ if correction_node.block_type?
165
+ correction_node.source_range.join(correction_node.children.first.source_range)
166
+ else
167
+ correction_node.loc.keyword.join(correction_node.loc.name)
168
+ end
169
+ end
170
+
162
171
  def candidate?(node)
163
172
  return false unless node
164
173
 
165
- method_candidate?(node) || class_candidate?(node) || module_candidate?(node)
174
+ method_candidate?(node) || class_candidate?(node) || module_candidate?(node) ||
175
+ macro_candidate?(node)
176
+ end
177
+
178
+ def empty_line_between_macros
179
+ cop_config.fetch('DefLikeMacros', []).map(&:to_sym)
180
+ end
181
+
182
+ def macro_candidate?(node)
183
+ node.block_type? && node.children.first.macro? &&
184
+ empty_line_between_macros.include?(node.children.first.method_name)
166
185
  end
167
186
 
168
187
  def method_candidate?(node)
@@ -226,7 +245,11 @@ module RuboCop
226
245
  end
227
246
 
228
247
  def def_start(node)
229
- node.loc.keyword.line
248
+ if node.block_type? && node.children.first.send_type?
249
+ node.source_range.line
250
+ else
251
+ node.loc.keyword.line
252
+ end
230
253
  end
231
254
 
232
255
  def def_end(node)
@@ -163,7 +163,13 @@ module RuboCop
163
163
  when :keyword
164
164
  node
165
165
  when :variable
166
- alignment_node_for_variable_style(node)
166
+ align_to = alignment_node_for_variable_style(node)
167
+
168
+ while (parent = align_to.parent) && parent.send_type? && same_line?(align_to, parent)
169
+ align_to = parent
170
+ end
171
+
172
+ align_to
167
173
  else
168
174
  start_line_range(node)
169
175
  end
@@ -25,6 +25,9 @@ module RuboCop
25
25
  include Alignment
26
26
  include Heredoc
27
27
  extend AutoCorrector
28
+ extend TargetRubyVersion
29
+
30
+ minimum_target_ruby_version 2.3
28
31
 
29
32
  TYPE_MSG = 'Use %<indentation_width>d spaces for indentation in a ' \
30
33
  'heredoc by using `<<~` instead of `%<current_indent_type>s`.'
@@ -354,7 +354,7 @@ module RuboCop
354
354
  # Don't check indentation if the line doesn't start with the body.
355
355
  # For example, lines like "else do_something".
356
356
  first_char_pos_on_line = body_node.source_range.source_line =~ /\S/
357
- return true unless body_node.loc.column == first_char_pos_on_line
357
+ body_node.loc.column != first_char_pos_on_line
358
358
  end
359
359
 
360
360
  def offending_range(body_node, indentation)
@@ -57,7 +57,7 @@ module RuboCop
57
57
 
58
58
  def on_new_investigation
59
59
  processed_source.comments.each do |comment|
60
- next unless /\A#+[^#\s=+-]/.match?(comment.text)
60
+ next unless /\A(?!#\+\+|#--)(#+[^#\s=])/.match?(comment.text)
61
61
  next if comment.loc.line == 1 && allowed_on_first_line?(comment)
62
62
  next if doxygen_comment_style?(comment)
63
63
  next if gemfile_ruby_comment?(comment)
@@ -57,16 +57,13 @@ module RuboCop
57
57
 
58
58
  end_of_first_line = node.source_range.begin_pos - node.source_range.column
59
59
 
60
- raw_lines(node).each_cons(2) do |raw_line_one, raw_line_two|
60
+ lines = raw_lines(node)
61
+ lines.each_cons(2).with_index(node.first_line) do |(raw_line_one, raw_line_two), line_num|
61
62
  end_of_first_line += raw_line_one.length
62
63
 
63
- next unless continuation?(raw_line_one)
64
+ next unless continuation?(raw_line_one, line_num, node)
64
65
 
65
- if enforced_style_leading?
66
- investigate_leading_style(raw_line_one, raw_line_two, end_of_first_line)
67
- else
68
- investigate_trailing_style(raw_line_one, raw_line_two, end_of_first_line)
69
- end
66
+ investigate(raw_line_one, raw_line_two, end_of_first_line)
70
67
  end
71
68
  end
72
69
 
@@ -76,6 +73,14 @@ module RuboCop
76
73
  processed_source.raw_source.lines[node.first_line - 1, line_range(node).size]
77
74
  end
78
75
 
76
+ def investigate(first_line, second_line, end_of_first_line)
77
+ if enforced_style_leading?
78
+ investigate_leading_style(first_line, second_line, end_of_first_line)
79
+ else
80
+ investigate_trailing_style(first_line, second_line, end_of_first_line)
81
+ end
82
+ end
83
+
79
84
  def investigate_leading_style(first_line, second_line, end_of_first_line)
80
85
  matches = first_line.match(LEADING_STYLE_OFFENSE)
81
86
  return if matches.nil?
@@ -98,8 +103,11 @@ module RuboCop
98
103
  end
99
104
  end
100
105
 
101
- def continuation?(line)
102
- line.end_with?("\\\n")
106
+ def continuation?(line, line_num, node)
107
+ return false unless line.end_with?("\\\n")
108
+
109
+ # Ensure backslash isn't part of a token spanning to the next line.
110
+ node.children.none? { |c| c.first_line == line_num && c.multiline? }
103
111
  end
104
112
 
105
113
  def autocorrect(corrector, offense_range, insert_pos, spaces)
@@ -109,7 +109,7 @@ module RuboCop
109
109
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
110
110
 
111
111
  def comment_ranges(comments)
112
- comments.map(&:loc).map(&:expression)
112
+ comments.map(&:source_range)
113
113
  end
114
114
 
115
115
  def last_line(processed_source)
@@ -84,6 +84,8 @@ module RuboCop
84
84
  return unless strings_concatenated_with_backslash?(node)
85
85
 
86
86
  children = node.children
87
+ return if children.empty?
88
+
87
89
  if style == :aligned && !always_indented?(node)
88
90
  check_aligned(children, 1)
89
91
  else
@@ -75,7 +75,7 @@ module RuboCop
75
75
  def right_hand_side(send_node)
76
76
  dot = send_node.loc.dot
77
77
  selector = send_node.loc.selector
78
- if send_node.dot? && selector && same_line?(dot, selector)
78
+ if (send_node.dot? || send_node.safe_navigation?) && selector && same_line?(dot, selector)
79
79
  dot.join(selector)
80
80
  elsif selector
81
81
  selector
@@ -179,10 +179,10 @@ module RuboCop
179
179
  # a.b
180
180
  # .c
181
181
  def semantic_alignment_base(node, rhs)
182
- return unless rhs.source.start_with?('.')
182
+ return unless rhs.source.start_with?('.', '&.')
183
183
 
184
184
  node = semantic_alignment_node(node)
185
- return unless node&.loc&.selector
185
+ return unless node&.loc&.selector && node.loc.dot
186
186
 
187
187
  node.loc.dot.join(node.loc.selector)
188
188
  end
@@ -204,6 +204,10 @@ module RuboCop
204
204
  dot_right_above = get_dot_right_above(node)
205
205
  return dot_right_above if dot_right_above
206
206
 
207
+ if (multiline_block_chain_node = find_multiline_block_chain_node(node))
208
+ return multiline_block_chain_node
209
+ end
210
+
207
211
  node = first_call_has_a_dot(node)
208
212
  return if node.loc.dot.line != node.first_line
209
213
 
@@ -219,6 +223,17 @@ module RuboCop
219
223
  end
220
224
  end
221
225
 
226
+ def find_multiline_block_chain_node(node)
227
+ return unless (block_node = node.each_descendant(:block, :numblock).first)
228
+ return unless block_node.multiline? && block_node.parent.call_type?
229
+
230
+ if node.receiver.call_type?
231
+ node.receiver
232
+ else
233
+ block_node.parent
234
+ end
235
+ end
236
+
222
237
  def first_call_has_a_dot(node)
223
238
  # descend to root of method chain
224
239
  node = node.receiver while node.receiver
@@ -48,6 +48,10 @@ module RuboCop
48
48
 
49
49
  MSG = 'Redundant line break detected.'
50
50
 
51
+ def on_lvasgn(node)
52
+ super unless end_with_percent_blank_string?(processed_source)
53
+ end
54
+
51
55
  def on_send(node)
52
56
  # Include "the whole expression".
53
57
  node = node.parent while node.parent&.send_type? ||
@@ -61,6 +65,10 @@ module RuboCop
61
65
 
62
66
  private
63
67
 
68
+ def end_with_percent_blank_string?(processed_source)
69
+ processed_source.buffer.source.end_with?("%\n\n")
70
+ end
71
+
64
72
  def check_assignment(node, _rhs)
65
73
  return unless offense?(node)
66
74
 
@@ -98,9 +106,9 @@ module RuboCop
98
106
 
99
107
  def suitable_as_single_line?(node)
100
108
  !comment_within?(node) &&
101
- node.each_descendant(:if, :case, :kwbegin, :def).none? &&
109
+ node.each_descendant(:if, :case, :kwbegin, :def, :defs).none? &&
102
110
  node.each_descendant(:dstr, :str).none? { |n| n.heredoc? || n.value.include?("\n") } &&
103
- node.each_descendant(:begin).none? { |b| !b.single_line? }
111
+ node.each_descendant(:begin, :sym).none? { |b| !b.single_line? }
104
112
  end
105
113
 
106
114
  def convertible_block?(node)
@@ -110,7 +118,9 @@ module RuboCop
110
118
  end
111
119
 
112
120
  def comment_within?(node)
113
- processed_source.comments.map(&:loc).map(&:line).any? do |comment_line_number|
121
+ comment_line_numbers = processed_source.comments.map { |comment| comment.loc.line }
122
+
123
+ comment_line_numbers.any? do |comment_line_number|
114
124
  comment_line_number >= node.first_line && comment_line_number <= node.last_line
115
125
  end
116
126
  end
@@ -24,7 +24,15 @@ module RuboCop
24
24
  end
25
25
 
26
26
  def kind(token)
27
- 'comma' if token.comma?
27
+ 'comma' if token.comma? && !before_semicolon?(token)
28
+ end
29
+
30
+ private
31
+
32
+ def before_semicolon?(token)
33
+ tokens = processed_source.tokens
34
+
35
+ tokens[tokens.index(token) + 1].semicolon?
28
36
  end
29
37
  end
30
38
  end
@@ -31,7 +31,7 @@ module RuboCop
31
31
  private
32
32
 
33
33
  def whitespace_after_operator?(node)
34
- node.receiver.loc.column - node.loc.column > 1
34
+ node.receiver.source_range.begin_pos - node.source_range.begin_pos > 1
35
35
  end
36
36
  end
37
37
  end
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # foo &. bar
20
20
  # foo &. bar&. buzz
21
21
  # RuboCop:: Cop
22
- # RuboCop:: Cop:: Cop
22
+ # RuboCop:: Cop:: Base
23
23
  # :: RuboCop::Cop
24
24
  #
25
25
  # # good
@@ -31,7 +31,7 @@ module RuboCop
31
31
  # foo&.bar
32
32
  # foo&.bar&.buzz
33
33
  # RuboCop::Cop
34
- # RuboCop::Cop::Cop
34
+ # RuboCop::Cop::Base
35
35
  # ::RuboCop::Cop
36
36
  #
37
37
  class SpaceAroundMethodCallOperator < Base
@@ -153,7 +153,9 @@ module RuboCop
153
153
  private
154
154
 
155
155
  def regular_operator?(send_node)
156
- !send_node.unary_operation? && !send_node.dot? && operator_with_regular_syntax?(send_node)
156
+ return false if send_node.unary_operation? || send_node.dot? || send_node.double_colon?
157
+
158
+ operator_with_regular_syntax?(send_node)
157
159
  end
158
160
 
159
161
  def operator_with_regular_syntax?(send_node)
@@ -168,7 +168,7 @@ module RuboCop
168
168
  # follows, and that the rules for space inside don't apply.
169
169
  return true if token2.comment?
170
170
 
171
- return true unless same_line?(token1, token2) && !token1.space_after?
171
+ !same_line?(token1, token2) || token1.space_after?
172
172
  end
173
173
  end
174
174
  end
@@ -51,6 +51,7 @@ module RuboCop
51
51
  # there could be good reasons why it needs to end with a certain
52
52
  # number of newlines.
53
53
  return if ends_in_end?(processed_source)
54
+ return if end_with_percent_blank_string?(processed_source)
54
55
 
55
56
  whitespace_at_end = buffer.source[/\s*\Z/]
56
57
  blank_lines = whitespace_at_end.count("\n") - 1
@@ -86,6 +87,10 @@ module RuboCop
86
87
  extra&.strip&.start_with?('__END__')
87
88
  end
88
89
 
90
+ def end_with_percent_blank_string?(processed_source)
91
+ processed_source.buffer.source.end_with?("%\n\n")
92
+ end
93
+
89
94
  def message(wanted_blank_lines, blank_lines)
90
95
  case blank_lines
91
96
  when -1
@@ -68,10 +68,7 @@ module RuboCop
68
68
  MSG = 'Remove debugger entry point `%<source>s`.'
69
69
 
70
70
  def on_send(node)
71
- return unless debugger_method?(node)
72
-
73
- # Basically, debugger methods are not used as a method argument without arguments.
74
- return if node.arguments.empty? && node.each_ancestor(:send, :csend).any?
71
+ return if !debugger_method?(node) || assumed_usage_context?(node)
75
72
 
76
73
  add_offense(node)
77
74
  end
@@ -95,6 +92,16 @@ module RuboCop
95
92
  debugger_methods.include?(chained_method_name(send_node))
96
93
  end
97
94
 
95
+ def assumed_usage_context?(node)
96
+ # Basically, debugger methods are not used as a method argument without arguments.
97
+ return false unless node.arguments.empty? && node.each_ancestor(:send, :csend).any?
98
+ return true if assumed_argument?(node)
99
+
100
+ node.each_ancestor.none? do |ancestor|
101
+ ancestor.block_type? || ancestor.numblock_type? || ancestor.lambda_or_proc?
102
+ end
103
+ end
104
+
98
105
  def chained_method_name(send_node)
99
106
  chained_method_name = send_node.method_name.to_s
100
107
  receiver = send_node.receiver
@@ -105,6 +112,12 @@ module RuboCop
105
112
  end
106
113
  chained_method_name
107
114
  end
115
+
116
+ def assumed_argument?(node)
117
+ parent = node.parent
118
+
119
+ parent.call_type? || parent.literal? || parent.pair_type?
120
+ end
108
121
  end
109
122
  end
110
123
  end
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module Lint
6
6
  # Checks for blocks without a body.
7
7
  # Such empty blocks are typically an oversight or we should provide a comment
8
- # be clearer what we're aiming for.
8
+ # to clarify what we're aiming for.
9
9
  #
10
10
  # Empty lambdas and procs are ignored by default.
11
11
  #
@@ -34,7 +34,7 @@ module RuboCop
34
34
  # interpolation should not be removed if the expanded value
35
35
  # contains a space character.
36
36
  expanded_value = autocorrected_value(final_node)
37
- return if in_array_percent_literal?(begin_node) && /\s/.match?(expanded_value)
37
+ return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
38
38
 
39
39
  add_offense(final_node) do |corrector|
40
40
  return if final_node.dstr_type? # nested, fixed in next iteration
@@ -84,6 +84,8 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def unsafe_range?(range_start, range_end)
87
+ return false if range_start.length != 1 || range_end.length != 1
88
+
87
89
  range_for(range_start) != range_for(range_end)
88
90
  end
89
91
 
@@ -93,7 +95,7 @@ module RuboCop
93
95
 
94
96
  def skip_range?(range_start, range_end)
95
97
  [range_start, range_end].any? do |bound|
96
- bound.type == :escape
98
+ bound.type != :literal
97
99
  end
98
100
  end
99
101
 
@@ -43,7 +43,6 @@ module RuboCop
43
43
  #
44
44
  class NonAtomicFileOperation < Base
45
45
  extend AutoCorrector
46
- include Alignment
47
46
 
48
47
  MSG_REMOVE_FILE_EXIST_CHECK = 'Remove unnecessary existence check ' \
49
48
  '`%<receiver>s.%<method_name>s`.'
@@ -51,18 +50,20 @@ module RuboCop
51
50
  MAKE_FORCE_METHODS = %i[makedirs mkdir_p mkpath].freeze
52
51
  MAKE_METHODS = %i[mkdir].freeze
53
52
  REMOVE_FORCE_METHODS = %i[rm_f rm_rf].freeze
54
- REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure
55
- delete unlink remove_file rm rmdir safe_unlink].freeze
56
- RESTRICT_ON_SEND = (MAKE_METHODS + MAKE_FORCE_METHODS + REMOVE_METHODS +
57
- REMOVE_FORCE_METHODS).freeze
53
+ REMOVE_METHODS = %i[remove delete unlink remove_file rm rmdir safe_unlink].freeze
54
+ RECURSIVE_REMOVE_METHODS = %i[remove_dir remove_entry remove_entry_secure].freeze
55
+ RESTRICT_ON_SEND = (
56
+ MAKE_METHODS + MAKE_FORCE_METHODS + REMOVE_METHODS + RECURSIVE_REMOVE_METHODS +
57
+ REMOVE_FORCE_METHODS
58
+ ).freeze
58
59
 
59
60
  # @!method send_exist_node(node)
60
- def_node_search :send_exist_node, <<-PATTERN
61
+ def_node_search :send_exist_node, <<~PATTERN
61
62
  $(send (const nil? {:FileTest :File :Dir :Shell}) {:exist? :exists?} ...)
62
63
  PATTERN
63
64
 
64
65
  # @!method receiver_and_method_name(node)
65
- def_node_matcher :receiver_and_method_name, <<-PATTERN
66
+ def_node_matcher :receiver_and_method_name, <<~PATTERN
66
67
  (send (const nil? $_) $_ ...)
67
68
  PATTERN
68
69
 
@@ -140,6 +141,8 @@ module RuboCop
140
141
  'mkdir_p'
141
142
  elsif REMOVE_METHODS.include?(node.method_name)
142
143
  'rm_f'
144
+ elsif RECURSIVE_REMOVE_METHODS.include?(node.method_name)
145
+ 'rm_rf'
143
146
  else
144
147
  node.method_name
145
148
  end
@@ -5,6 +5,14 @@ module RuboCop
5
5
  module Lint
6
6
  # Checks for redundant quantifiers inside Regexp literals.
7
7
  #
8
+ # It is always allowed when interpolation is used in a regexp literal,
9
+ # because it's unknown what kind of string will be expanded as a result:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # /(?:a*#{interpolation})?/x
14
+ # ----
15
+ #
8
16
  # @example
9
17
  # # bad
10
18
  # /(?:x+)+/
@@ -32,6 +40,8 @@ module RuboCop
32
40
  'with a single `%<replacement>s`.'
33
41
 
34
42
  def on_regexp(node)
43
+ return if node.interpolation?
44
+
35
45
  each_redundantly_quantified_pair(node) do |group, child|
36
46
  replacement = merged_quantifier(group, child)
37
47
  add_offense(
@@ -24,6 +24,10 @@ module RuboCop
24
24
  #
25
25
  # This cop target those features.
26
26
  #
27
+ # @safety
28
+ # This cop's autocorrection is unsafe because if `require 'pp'` is removed from one file,
29
+ # `NameError` can be encountered when another file uses `PP.pp`.
30
+ #
27
31
  # @example
28
32
  # # bad
29
33
  # require 'unloaded_feature'