rubocop 1.36.0 → 1.38.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +27 -1
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  8. data/lib/rubocop/comment_config.rb +36 -1
  9. data/lib/rubocop/cop/commissioner.rb +3 -1
  10. data/lib/rubocop/cop/generator.rb +1 -2
  11. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  12. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  13. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  15. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  17. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  18. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  19. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  20. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  21. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  22. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  23. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  24. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  25. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  26. data/lib/rubocop/cop/lint/empty_conditional_body.rb +20 -8
  27. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  28. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  29. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  30. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  31. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  32. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -1
  33. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  35. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  36. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  37. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  38. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  39. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  40. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  41. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  42. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  43. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  44. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  45. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  46. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  47. data/lib/rubocop/cop/registry.rb +10 -4
  48. data/lib/rubocop/cop/style/access_modifier_declarations.rb +24 -2
  49. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  50. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  51. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  52. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  53. data/lib/rubocop/cop/style/collection_compact.rb +11 -2
  54. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  55. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  56. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  57. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  58. data/lib/rubocop/cop/style/guard_clause.rb +62 -21
  59. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  60. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  61. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  62. data/lib/rubocop/cop/style/module_function.rb +28 -6
  63. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  64. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  65. data/lib/rubocop/cop/style/operator_method_call.rb +40 -0
  66. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  67. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  68. data/lib/rubocop/cop/style/redundant_each.rb +111 -0
  69. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  70. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  71. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  72. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  73. data/lib/rubocop/cop/style/static_class.rb +32 -1
  74. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  75. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  76. data/lib/rubocop/cop/style/word_array.rb +2 -0
  77. data/lib/rubocop/cop/team.rb +3 -4
  78. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  79. data/lib/rubocop/cops_documentation_generator.rb +4 -2
  80. data/lib/rubocop/ext/processed_source.rb +2 -0
  81. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  82. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  83. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  84. data/lib/rubocop/options.rb +19 -15
  85. data/lib/rubocop/rspec/cop_helper.rb +21 -1
  86. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  87. data/lib/rubocop/runner.rb +15 -11
  88. data/lib/rubocop/server/cache.rb +5 -1
  89. data/lib/rubocop/server/cli.rb +9 -2
  90. data/lib/rubocop/server/client_command/exec.rb +5 -0
  91. data/lib/rubocop/server/core.rb +3 -1
  92. data/lib/rubocop/server/socket_reader.rb +5 -1
  93. data/lib/rubocop/server.rb +1 -1
  94. data/lib/rubocop/version.rb +8 -3
  95. data/lib/rubocop.rb +4 -0
  96. metadata +13 -5
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for duplicated magic comments.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ #
12
+ # # encoding: ascii
13
+ # # encoding: ascii
14
+ #
15
+ # # good
16
+ #
17
+ # # encoding: ascii
18
+ #
19
+ # # bad
20
+ #
21
+ # # frozen_string_literal: true
22
+ # # frozen_string_literal: true
23
+ #
24
+ # # good
25
+ #
26
+ # # frozen_string_literal: true
27
+ #
28
+ class DuplicateMagicComment < Base
29
+ include FrozenStringLiteral
30
+ include RangeHelp
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Duplicate magic comment detected.'
34
+
35
+ def on_new_investigation
36
+ return if processed_source.buffer.source.empty?
37
+
38
+ magic_comment_lines.each_value do |comment_lines|
39
+ next if comment_lines.count <= 1
40
+
41
+ comment_lines[1..].each do |comment_line|
42
+ range = processed_source.buffer.line_range(comment_line + 1)
43
+
44
+ register_offense(range)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def magic_comment_lines
52
+ comment_lines = { encoding_magic_comments: [], frozen_string_literal_magic_comments: [] }
53
+
54
+ leading_magic_comments.each.with_index do |magic_comment, index|
55
+ if magic_comment.encoding_specified?
56
+ comment_lines[:encoding_magic_comments] << index
57
+ elsif magic_comment.frozen_string_literal_specified?
58
+ comment_lines[:frozen_string_literal_magic_comments] << index
59
+ end
60
+ end
61
+
62
+ comment_lines
63
+ end
64
+
65
+ def register_offense(range)
66
+ add_offense(range) do |corrector|
67
+ corrector.remove(range_by_whole_lines(range, include_final_newline: true))
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def found_instance_method(node, name)
136
- return unless (scope = node.parent_module_name)
136
+ return found_sclass_method(node, name) unless (scope = node.parent_module_name)
137
137
 
138
138
  # Humanize the scope
139
139
  scope = scope.sub(
@@ -145,6 +145,16 @@ module RuboCop
145
145
  found_method(node, "#{scope}#{name}")
146
146
  end
147
147
 
148
+ def found_sclass_method(node, name)
149
+ singleton_ancestor = node.each_ancestor.find(&:sclass_type?)
150
+ return unless singleton_ancestor
151
+
152
+ singleton_receiver_node = singleton_ancestor.children[0]
153
+ return unless singleton_receiver_node.send_type?
154
+
155
+ found_method(node, "#{singleton_receiver_node.method_name}.#{name}")
156
+ end
157
+
148
158
  def found_method(node, method_name)
149
159
  if @definitions.key?(method_name)
150
160
  loc = case node.type
@@ -32,26 +32,40 @@ module RuboCop
32
32
  end
33
33
  end
34
34
 
35
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
35
36
  def each_repeated_character_class_element_loc(node)
36
37
  node.parsed_tree&.each_expression do |expr|
37
- next if expr.type != :set || expr.token == :intersection
38
+ next if skip_expression?(expr)
38
39
 
39
40
  seen = Set.new
41
+ enum = expr.expressions.to_enum
42
+ expression_count = expr.expressions.count
40
43
 
41
- expr.expressions.each do |child|
42
- next if within_interpolation?(node, child)
44
+ expression_count.times do |current_number|
45
+ current_child = enum.next
46
+ next if within_interpolation?(node, current_child)
43
47
 
44
- child_source = child.to_s
48
+ current_child_source = current_child.to_s
49
+ next_child = enum.peek if current_number + 1 < expression_count
45
50
 
46
- yield child.expression if seen.include?(child_source)
51
+ if seen.include?(current_child_source)
52
+ next if start_with_escaped_zero_number?(current_child_source, next_child.to_s)
47
53
 
48
- seen << child_source
54
+ yield current_child.expression
55
+ end
56
+
57
+ seen << current_child_source
49
58
  end
50
59
  end
51
60
  end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
52
62
 
53
63
  private
54
64
 
65
+ def skip_expression?(expr)
66
+ expr.type != :set || expr.token == :intersection
67
+ end
68
+
55
69
  # Since we blank interpolations with a space for every char of the interpolation, we would
56
70
  # mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
57
71
  # that are within an interpolation.
@@ -61,6 +75,11 @@ module RuboCop
61
75
  interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
62
76
  end
63
77
 
78
+ def start_with_escaped_zero_number?(current_child, next_child)
79
+ # Represents escaped code from `"\00"` (`"\u0000"`) to `"\07"` (`"\a"`).
80
+ current_child == '\\0' && next_child.match?(/[0-7]/)
81
+ end
82
+
64
83
  def interpolation_locs(node)
65
84
  @interpolation_locs ||= {}
66
85
 
@@ -85,7 +85,9 @@ module RuboCop
85
85
  private
86
86
 
87
87
  def body_or_allowed_comment_lines?(node)
88
- node.body || (cop_config['AllowComments'] && comment_lines?(node))
88
+ return true if node.body
89
+
90
+ cop_config['AllowComments'] && processed_source.contains_comment?(node.source_range)
89
91
  end
90
92
  end
91
93
  end
@@ -92,13 +92,17 @@ module RuboCop
92
92
  end
93
93
 
94
94
  def remove_empty_branch(corrector, node)
95
- corrector.remove(deletion_range(branch_range(node)))
95
+ if empty_if_branch?(node) && else_branch?(node)
96
+ corrector.remove(branch_range(node))
97
+ else
98
+ corrector.remove(deletion_range(branch_range(node)))
99
+ end
96
100
  end
97
101
 
98
102
  def correct_other_branches(corrector, node)
99
103
  return unless require_other_branches_correction?(node)
100
104
 
101
- if node.else_branch.if_type?
105
+ if node.else_branch&.if_type?
102
106
  # Replace an orphaned `elsif` with `if`
103
107
  corrector.replace(node.else_branch.loc.keyword, 'if')
104
108
  else
@@ -108,10 +112,10 @@ module RuboCop
108
112
  end
109
113
 
110
114
  def require_other_branches_correction?(node)
111
- return false unless node.if_type? && node.else_branch
115
+ return false unless node.if_type? && node.else?
112
116
  return false if !empty_if_branch?(node) && node.elsif?
113
117
 
114
- !empty_else_branch?(node)
118
+ !empty_elsif_branch?(node)
115
119
  end
116
120
 
117
121
  def empty_if_branch?(node)
@@ -122,14 +126,22 @@ module RuboCop
122
126
  if_branch.if_type? && !if_branch.body
123
127
  end
124
128
 
125
- def empty_else_branch?(node)
126
- node.else_branch.if_type? && !node.else_branch.body
129
+ def empty_elsif_branch?(node)
130
+ return false unless (else_branch = node.else_branch)
131
+
132
+ else_branch.if_type? && !else_branch.body
133
+ end
134
+
135
+ def else_branch?(node)
136
+ node.else_branch && !node.else_branch.if_type?
127
137
  end
128
138
 
129
139
  # rubocop:disable Metrics/AbcSize
130
140
  def branch_range(node)
131
- if node.loc.else
132
- node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
141
+ if empty_if_branch?(node) && else_branch?(node)
142
+ node.source_range.with(end_pos: node.loc.else.begin_pos)
143
+ elsif node.loc.else
144
+ node.source_range.with(end_pos: node.condition.loc.expression.end_pos)
133
145
  elsif all_branches_body_missing?(node)
134
146
  if_node = node.ancestors.detect(&:if?)
135
147
  node.source_range.with(end_pos: if_node.loc.end.end_pos)
@@ -50,9 +50,7 @@ module RuboCop
50
50
 
51
51
  def on_new_investigation
52
52
  each_missing_enable do |cop, line_range|
53
- # This has to remain a strict inequality to handle
54
- # the case when max_range is Float::INFINITY
55
- next if line_range.max - line_range.min < max_range + 2
53
+ next if acceptable_range?(cop, line_range)
56
54
 
57
55
  range = source_range(processed_source.buffer, line_range.min, (0..0))
58
56
  comment = processed_source.comment_at_line(line_range.begin)
@@ -69,6 +67,23 @@ module RuboCop
69
67
  end
70
68
  end
71
69
 
70
+ def acceptable_range?(cop, line_range)
71
+ # This has to remain a strict inequality to handle
72
+ # the case when max_range is Float::INFINITY
73
+ return true if line_range.max - line_range.min < max_range + 2
74
+ # This cop is disabled in the config, it is not expected to be re-enabled
75
+ return true if line_range.min == CommentConfig::CONFIG_DISABLED_LINE_RANGE_MIN
76
+
77
+ cop_class = RuboCop::Cop::Registry.global.find_by_cop_name cop
78
+ if cop_class &&
79
+ !processed_source.registry.enabled?(cop_class, config) &&
80
+ line_range.max == Float::INFINITY
81
+ return true
82
+ end
83
+
84
+ false
85
+ end
86
+
72
87
  def max_range
73
88
  @max_range ||= cop_config['MaximumRangeSize']
74
89
  end
@@ -30,6 +30,9 @@ module RuboCop
30
30
  #
31
31
  # # good
32
32
  #
33
+ # # `class_eval`, `instance_eval`, `module_eval`, `class_exec`, `instance_exec`, and
34
+ # # `module_exec` blocks are allowed by default.
35
+ #
33
36
  # def foo
34
37
  # self.class.class_eval do
35
38
  # def bar
@@ -54,7 +57,47 @@ module RuboCop
54
57
  # end
55
58
  # end
56
59
  # end
60
+ #
61
+ # @example AllowedMethods: [] (default)
62
+ # # bad
63
+ # def do_something
64
+ # has_many :articles do
65
+ # def find_or_create_by_name(name)
66
+ # end
67
+ # end
68
+ # end
69
+ #
70
+ # @example AllowedMethods: ['has_many']
71
+ # # bad
72
+ # def do_something
73
+ # has_many :articles do
74
+ # def find_or_create_by_name(name)
75
+ # end
76
+ # end
77
+ # end
78
+ #
79
+ # @example AllowedPatterns: [] (default)
80
+ # # bad
81
+ # def foo(obj)
82
+ # obj.do_baz do
83
+ # def bar
84
+ # end
85
+ # end
86
+ # end
87
+ #
88
+ # @example AllowedPatterns: ['baz']
89
+ # # good
90
+ # def foo(obj)
91
+ # obj.do_baz do
92
+ # def bar
93
+ # end
94
+ # end
95
+ # end
96
+ #
57
97
  class NestedMethodDefinition < Base
98
+ include AllowedMethods
99
+ include AllowedPattern
100
+
58
101
  MSG = 'Method definitions must not be nested. Use `lambda` instead.'
59
102
 
60
103
  def on_def(node)
@@ -77,7 +120,13 @@ module RuboCop
77
120
 
78
121
  def scoping_method_call?(child)
79
122
  child.sclass_type? || eval_call?(child) || exec_call?(child) ||
80
- class_or_module_or_struct_new_call?(child)
123
+ class_or_module_or_struct_new_call?(child) || allowed_method_name?(child)
124
+ end
125
+
126
+ def allowed_method_name?(node)
127
+ name = node.method_name
128
+
129
+ allowed_method?(name) || matches_allowed_pattern?(name)
81
130
  end
82
131
 
83
132
  # @!method eval_call?(node)
@@ -61,7 +61,7 @@ module RuboCop
61
61
  # # bad
62
62
  # 10.minutes.to_i
63
63
  #
64
- # @example AllowedPatterns: [/min*/]
64
+ # @example AllowedPatterns: ['min*']
65
65
  #
66
66
  # # good
67
67
  # 10.minutes.to_i
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # Checks the proper ordering of magic comments and whether
8
8
  # a magic comment is not placed before a shebang.
9
9
  #
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because file encoding may change.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  #
@@ -61,7 +64,7 @@ module RuboCop
61
64
  def magic_comment_lines
62
65
  lines = [nil, nil]
63
66
 
64
- magic_comments.each.with_index do |comment, index|
67
+ leading_magic_comments.each.with_index do |comment, index|
65
68
  if comment.encoding_specified?
66
69
  lines[0] = index
67
70
  elsif comment.frozen_string_literal_specified?
@@ -73,10 +76,6 @@ module RuboCop
73
76
 
74
77
  lines
75
78
  end
76
-
77
- def magic_comments
78
- leading_comment_lines.map { |line| MagicComment.parse(line) }
79
- end
80
79
  end
81
80
  end
82
81
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cops looks for references of Regexp captures that are out of range
6
+ # Looks for references of Regexp captures that are out of range
7
7
  # and thus always returns nil.
8
8
  #
9
9
  # @safety
@@ -113,6 +113,7 @@ module RuboCop
113
113
  def each_line_range(cop, line_ranges)
114
114
  line_ranges.each_with_index do |line_range, line_range_index|
115
115
  next if ignore_offense?(line_range)
116
+ next if expected_final_disable?(cop, line_range)
116
117
 
117
118
  comment = processed_source.comment_at_line(line_range.begin)
118
119
  redundant = if all_disabled?(comment)
@@ -179,11 +180,21 @@ module RuboCop
179
180
  end
180
181
 
181
182
  def ignore_offense?(line_range)
183
+ return true if line_range.min == CommentConfig::CONFIG_DISABLED_LINE_RANGE_MIN
184
+
182
185
  disabled_ranges.any? do |range|
183
186
  range.cover?(line_range.min) && range.cover?(line_range.max)
184
187
  end
185
188
  end
186
189
 
190
+ def expected_final_disable?(cop, line_range)
191
+ # A cop which is disabled in the config is being re-disabled until end of file
192
+ cop_class = RuboCop::Cop::Registry.global.find_by_cop_name cop
193
+ cop_class &&
194
+ !processed_source.registry.enabled?(cop_class, config) &&
195
+ line_range.max == Float::INFINITY
196
+ end
197
+
187
198
  def department_disabled?(cop, comment)
188
199
  directive = DirectiveComment.new(comment)
189
200
  directive.in_directive_department?(cop) && !directive.overridden_by_department?(cop)
@@ -209,7 +220,12 @@ module RuboCop
209
220
 
210
221
  add_offense(location, message: message(cop_names)) do |corrector|
211
222
  range = comment_range_with_surrounding_space(location, comment.loc.expression)
212
- corrector.remove(range)
223
+
224
+ if leave_free_comment?(comment, range)
225
+ corrector.replace(range, ' # ')
226
+ else
227
+ corrector.remove(range)
228
+ end
213
229
  end
214
230
  end
215
231
 
@@ -227,6 +243,12 @@ module RuboCop
227
243
  end
228
244
  end
229
245
 
246
+ def leave_free_comment?(comment, range)
247
+ free_comment = comment.text.gsub(range.source.strip, '')
248
+
249
+ !free_comment.empty? && !free_comment.start_with?('#')
250
+ end
251
+
230
252
  def cop_range(comment, cop)
231
253
  cop = remove_department_marker(cop)
232
254
  matching_range(comment.loc.expression, cop) ||
@@ -41,6 +41,7 @@ module RuboCop
41
41
  return unless (receiver = node.receiver)
42
42
  return unless receiver.receiver&.const_type? && receiver.receiver.short_name == :Dir
43
43
  return unless GLOB_METHODS.include?(receiver.method_name)
44
+ return if multiple_argument?(receiver)
44
45
 
45
46
  selector = node.loc.selector
46
47
 
@@ -49,6 +50,12 @@ module RuboCop
49
50
  corrector.remove(node.loc.dot)
50
51
  end
51
52
  end
53
+
54
+ private
55
+
56
+ def multiple_argument?(glob_method)
57
+ glob_method.arguments.count >= 2 || glob_method.first_argument&.splat_type?
58
+ end
52
59
  end
53
60
  end
54
61
  end
@@ -6,13 +6,23 @@ module RuboCop
6
6
  # Checks for unnecessary `require` statement.
7
7
  #
8
8
  # The following features are unnecessary `require` statement because
9
- # they are already loaded.
9
+ # they are already loaded. e.g. Ruby 2.2:
10
10
  #
11
11
  # ruby -ve 'p $LOADED_FEATURES.reject { |feature| %r|/| =~ feature }'
12
12
  # ruby 2.2.8p477 (2017-09-14 revision 59906) [x86_64-darwin13]
13
13
  # ["enumerator.so", "rational.so", "complex.so", "thread.rb"]
14
14
  #
15
- # This cop targets Ruby 2.2 or higher containing these 4 features.
15
+ # Below are the features that each `TargetRubyVersion` targets.
16
+ #
17
+ # * 2.0+ ... `enumerator`
18
+ # * 2.1+ ... `thread`
19
+ # * 2.2+ ... Add `rational` and `complex` above
20
+ # * 2.5+ ... Add `pp` above
21
+ # * 2.7+ ... Add `ruby2_keywords` above
22
+ # * 3.1+ ... Add `fiber` above
23
+ # * 3.2+ ... `set`
24
+ #
25
+ # This cop target those features.
16
26
  #
17
27
  # @example
18
28
  # # bad
@@ -24,28 +34,46 @@ module RuboCop
24
34
  class RedundantRequireStatement < Base
25
35
  include RangeHelp
26
36
  extend AutoCorrector
27
- extend TargetRubyVersion
28
-
29
- minimum_target_ruby_version 2.2
30
37
 
31
38
  MSG = 'Remove unnecessary `require` statement.'
32
39
  RESTRICT_ON_SEND = %i[require].freeze
40
+ RUBY_22_LOADED_FEATURES = %w[rational complex].freeze
33
41
 
34
- # @!method unnecessary_require_statement?(node)
35
- def_node_matcher :unnecessary_require_statement?, <<~PATTERN
42
+ # @!method redundant_require_statement?(node)
43
+ def_node_matcher :redundant_require_statement?, <<~PATTERN
36
44
  (send nil? :require
37
- (str {"enumerator" "rational" "complex" "thread"}))
45
+ (str #redundant_feature?))
38
46
  PATTERN
39
47
 
40
48
  def on_send(node)
41
- return unless unnecessary_require_statement?(node)
49
+ return unless redundant_require_statement?(node)
42
50
 
43
51
  add_offense(node) do |corrector|
44
- range = range_with_surrounding_space(node.loc.expression, side: :right)
52
+ if node.parent.respond_to?(:modifier_form?) && node.parent.modifier_form?
53
+ corrector.insert_after(node.parent, "\nend")
54
+
55
+ range = range_with_surrounding_space(node.loc.expression, side: :right)
56
+ else
57
+ range = range_by_whole_lines(node.source_range, include_final_newline: true)
58
+ end
45
59
 
46
60
  corrector.remove(range)
47
61
  end
48
62
  end
63
+
64
+ private
65
+
66
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
67
+ def redundant_feature?(feature_name)
68
+ feature_name == 'enumerator' ||
69
+ (target_ruby_version >= 2.1 && feature_name == 'thread') ||
70
+ (target_ruby_version >= 2.2 && RUBY_22_LOADED_FEATURES.include?(feature_name)) ||
71
+ (target_ruby_version >= 2.5 && feature_name == 'pp') ||
72
+ (target_ruby_version >= 2.7 && feature_name == 'ruby2_keywords') ||
73
+ (target_ruby_version >= 3.1 && feature_name == 'fiber') ||
74
+ (target_ruby_version >= 3.2 && feature_name == 'set')
75
+ end
76
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
49
77
  end
50
78
  end
51
79
  end
@@ -46,7 +46,7 @@ module RuboCop
46
46
  private
47
47
 
48
48
  def check_ternary(ternary, node)
49
- return unless ternary.condition.operator_keyword?
49
+ return if node.method?(:[]) || !ternary.condition.operator_keyword?
50
50
 
51
51
  range = range_between(node.source_range.begin_pos, ternary.condition.source_range.end_pos)
52
52
 
@@ -31,6 +31,7 @@ module RuboCop
31
31
  minimum_target_ruby_version 2.3
32
32
 
33
33
  MSG = 'Do not chain ordinary method call after safe navigation operator.'
34
+ PLUS_MINUS_METHODS = %i[+@ -@].freeze
34
35
 
35
36
  # @!method bad_method?(node)
36
37
  def_node_matcher :bad_method?, <<~PATTERN
@@ -42,7 +43,7 @@ module RuboCop
42
43
 
43
44
  def on_send(node)
44
45
  bad_method?(node) do |safe_nav, method|
45
- return if nil_methods.include?(method)
46
+ return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
46
47
 
47
48
  method_chain = method_chain(node)
48
49
  location =
@@ -71,7 +72,7 @@ module RuboCop
71
72
  else
72
73
  offense_range.source.dup
73
74
  end
74
- source.prepend('.') unless send_node.dot?
75
+ source.prepend('.') unless source.start_with?('.')
75
76
  source.prepend('&')
76
77
  end
77
78
 
@@ -155,16 +155,6 @@ module RuboCop
155
155
  end
156
156
  end
157
157
 
158
- # @param [RuboCop::AST::Node] rescue_group is a node of array_type
159
- def rescued_exceptions(rescue_group)
160
- klasses = *rescue_group
161
- klasses.map do |klass|
162
- next unless klass.const_type?
163
-
164
- klass.source
165
- end.compact
166
- end
167
-
168
158
  def find_shadowing_rescue(rescues)
169
159
  rescued_groups = rescued_groups_for(rescues)
170
160
  rescued_groups.zip(rescues).each do |group, res|
@@ -12,9 +12,12 @@ module RuboCop
12
12
  # because `Ractor` should not access outer variables.
13
13
  # eg. following style is encouraged:
14
14
  #
15
+ # [source,ruby]
16
+ # ----
15
17
  # worker_id, pipe = env
16
18
  # Ractor.new(worker_id, pipe) do |worker_id, pipe|
17
19
  # end
20
+ # ----
18
21
  #
19
22
  # @example
20
23
  #
@@ -79,7 +79,7 @@ module RuboCop
79
79
  # # bad
80
80
  # 2.times { raise ArgumentError }
81
81
  #
82
- # @example AllowedPatterns: [/(exactly|at_least|at_most)\(\d+\)\.times/] (default)
82
+ # @example AllowedPatterns: ['(exactly|at_least|at_most)\(\d+\)\.times'] (default)
83
83
  #
84
84
  # # good
85
85
  # exactly(2).times { raise StandardError }
@@ -68,6 +68,10 @@ module RuboCop
68
68
  (send nil? :fail ...)}
69
69
  PATTERN
70
70
 
71
+ def self.autocorrect_incompatible_with
72
+ [Style::ExplicitBlockArgument]
73
+ end
74
+
71
75
  def self.joining_forces
72
76
  VariableForce
73
77
  end
@@ -22,6 +22,18 @@ module RuboCop
22
22
  processed_source.each_comment_in_lines(start_line...end_line)
23
23
  end
24
24
 
25
+ def comments_contain_disables?(node, cop_name)
26
+ disabled_ranges = processed_source.disabled_line_ranges[cop_name]
27
+
28
+ return unless disabled_ranges
29
+
30
+ node_range = node.source_range.line...find_end_line(node)
31
+
32
+ disabled_ranges.any? do |disable_range|
33
+ disable_range.cover?(node_range) || node_range.cover?(disable_range)
34
+ end
35
+ end
36
+
25
37
  private
26
38
 
27
39
  def end_position_for(node)
@@ -69,6 +69,10 @@ module RuboCop
69
69
  end
70
70
  end
71
71
 
72
+ def leading_magic_comments
73
+ leading_comment_lines.map { |line| MagicComment.parse(line) }
74
+ end
75
+
72
76
  def leading_comment_lines
73
77
  first_non_comment_token = processed_source.tokens.find { |token| !token.comment? }
74
78