rubocop 1.48.0 → 1.50.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +22 -7
  4. data/lib/rubocop/cli/command/execute_runner.rb +7 -2
  5. data/lib/rubocop/cli.rb +6 -6
  6. data/lib/rubocop/config.rb +3 -3
  7. data/lib/rubocop/config_loader.rb +8 -8
  8. data/lib/rubocop/cop/autocorrect_logic.rb +28 -12
  9. data/lib/rubocop/cop/cop.rb +2 -2
  10. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  11. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  12. data/lib/rubocop/cop/gemspec/dependency_version.rb +1 -1
  13. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/cop_description.rb +1 -1
  15. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +2 -2
  16. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +29 -2
  18. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -21
  19. data/lib/rubocop/cop/layout/class_structure.rb +1 -0
  20. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  21. data/lib/rubocop/cop/layout/empty_lines.rb +1 -1
  22. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -0
  23. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  24. data/lib/rubocop/cop/layout/extra_spacing.rb +6 -1
  25. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
  26. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +25 -34
  27. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +7 -19
  28. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +42 -52
  29. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +38 -55
  30. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  31. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  32. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +8 -27
  33. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +7 -26
  34. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -21
  35. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +6 -30
  36. data/lib/rubocop/cop/layout/redundant_line_break.rb +6 -7
  37. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  38. data/lib/rubocop/cop/layout/space_inside_parens.rb +2 -2
  39. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +3 -3
  40. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +122 -0
  41. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  42. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  43. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -1
  44. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +35 -15
  45. data/lib/rubocop/cop/lint/syntax.rb +4 -0
  46. data/lib/rubocop/cop/lint/to_enum_arguments.rb +7 -1
  47. data/lib/rubocop/cop/lint/unreachable_loop.rb +3 -3
  48. data/lib/rubocop/cop/lint/useless_method_definition.rb +10 -2
  49. data/lib/rubocop/cop/lint/useless_rescue.rb +4 -1
  50. data/lib/rubocop/cop/lint/void.rb +7 -3
  51. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  52. data/lib/rubocop/cop/metrics/class_length.rb +1 -0
  53. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  54. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  55. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -0
  56. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  57. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  58. data/lib/rubocop/cop/naming/ascii_identifiers.rb +1 -1
  59. data/lib/rubocop/cop/naming/inclusive_language.rb +23 -4
  60. data/lib/rubocop/cop/naming/method_name.rb +2 -2
  61. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -4
  62. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  63. data/lib/rubocop/cop/style/class_equality_comparison.rb +42 -9
  64. data/lib/rubocop/cop/style/collection_compact.rb +3 -0
  65. data/lib/rubocop/cop/style/copyright.rb +1 -1
  66. data/lib/rubocop/cop/style/data_inheritance.rb +75 -0
  67. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +2 -2
  68. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  69. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  70. data/lib/rubocop/cop/style/file_empty.rb +3 -3
  71. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  72. data/lib/rubocop/cop/style/hash_except.rb +4 -4
  73. data/lib/rubocop/cop/style/hash_syntax.rb +4 -1
  74. data/lib/rubocop/cop/style/if_unless_modifier.rb +38 -12
  75. data/lib/rubocop/cop/style/map_to_hash.rb +4 -1
  76. data/lib/rubocop/cop/style/map_to_set.rb +4 -1
  77. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -7
  78. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +43 -36
  79. data/lib/rubocop/cop/style/multiline_method_signature.rb +6 -3
  80. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -18
  81. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -3
  82. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  83. data/lib/rubocop/cop/style/redundant_fetch_block.rb +6 -4
  84. data/lib/rubocop/cop/style/redundant_line_continuation.rb +179 -0
  85. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  86. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  87. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +2 -2
  88. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  89. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -3
  90. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
  91. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  92. data/lib/rubocop/cop/style/trailing_body_on_class.rb +1 -0
  93. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  94. data/lib/rubocop/cop/style/unless_logical_operators.rb +1 -0
  95. data/lib/rubocop/cops_documentation_generator.rb +10 -3
  96. data/lib/rubocop/ext/regexp_node.rb +1 -1
  97. data/lib/rubocop/ext/regexp_parser.rb +1 -1
  98. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  99. data/lib/rubocop/options.rb +4 -1
  100. data/lib/rubocop/result_cache.rb +1 -1
  101. data/lib/rubocop/rspec/support.rb +1 -0
  102. data/lib/rubocop/server/cache.rb +1 -1
  103. data/lib/rubocop/server/helper.rb +1 -1
  104. data/lib/rubocop/server/server_command/exec.rb +1 -1
  105. data/lib/rubocop/version.rb +1 -1
  106. data/lib/rubocop.rb +3 -0
  107. metadata +12 -9
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks that there are no repeated patterns used in `in` keywords.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # case x
12
+ # in 'first'
13
+ # do_something
14
+ # in 'first'
15
+ # do_something_else
16
+ # end
17
+ #
18
+ # # good
19
+ # case x
20
+ # in 'first'
21
+ # do_something
22
+ # in 'second'
23
+ # do_something_else
24
+ # end
25
+ #
26
+ # # bad - repeated alternate patterns with the same conditions don't depend on the order
27
+ # case x
28
+ # in foo | bar
29
+ # first_method
30
+ # in bar | foo
31
+ # second_method
32
+ # end
33
+ #
34
+ # # good
35
+ # case x
36
+ # in foo | bar
37
+ # first_method
38
+ # in bar | baz
39
+ # second_method
40
+ # end
41
+ #
42
+ # # bad - repeated hash patterns with the same conditions don't depend on the order
43
+ # case x
44
+ # in foo: a, bar: b
45
+ # first_method
46
+ # in bar: b, foo: a
47
+ # second_method
48
+ # end
49
+ #
50
+ # # good
51
+ # case x
52
+ # in foo: a, bar: b
53
+ # first_method
54
+ # in bar: b, baz: c
55
+ # second_method
56
+ # end
57
+ #
58
+ # # bad - repeated array patterns with elements in the same order
59
+ # case x
60
+ # in [foo, bar]
61
+ # first_method
62
+ # in [foo, bar]
63
+ # second_method
64
+ # end
65
+ #
66
+ # # good
67
+ # case x
68
+ # in [foo, bar]
69
+ # first_method
70
+ # in [bar, foo]
71
+ # second_method
72
+ # end
73
+ #
74
+ # # bad - repeated the same patterns and guard conditions
75
+ # case x
76
+ # in foo if bar
77
+ # first_method
78
+ # in foo if bar
79
+ # second_method
80
+ # end
81
+ #
82
+ # # good
83
+ # case x
84
+ # in foo if bar
85
+ # first_method
86
+ # in foo if baz
87
+ # second_method
88
+ # end
89
+ #
90
+ class DuplicateMatchPattern < Base
91
+ extend TargetRubyVersion
92
+
93
+ MSG = 'Duplicate `in` pattern detected.'
94
+
95
+ minimum_target_ruby_version 2.7
96
+
97
+ def on_case_match(case_node)
98
+ case_node.in_pattern_branches.each_with_object(Set.new) do |in_pattern_node, previous|
99
+ pattern = in_pattern_node.pattern
100
+ next if previous.add?(pattern_identity(pattern))
101
+
102
+ add_offense(pattern)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def pattern_identity(pattern)
109
+ pattern_source = if pattern.hash_pattern_type? || pattern.match_alt_type?
110
+ pattern.children.map(&:source).sort.to_s
111
+ else
112
+ pattern.source
113
+ end
114
+
115
+ return pattern_source unless (guard = pattern.parent.children[1])
116
+
117
+ pattern_source + guard.source
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  def on_interpolation(begin_node)
26
26
  return unless begin_node.children.empty?
27
27
 
28
- add_offense(begin_node) { |corrector| corrector.remove(begin_node.source_range) }
28
+ add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
29
29
  end
30
30
  end
31
31
  end
@@ -131,12 +131,12 @@ module RuboCop
131
131
 
132
132
  # @!method eval_call?(node)
133
133
  def_node_matcher :eval_call?, <<~PATTERN
134
- (block (send _ {:instance_eval :class_eval :module_eval} ...) ...)
134
+ ({block numblock} (send _ {:instance_eval :class_eval :module_eval} ...) ...)
135
135
  PATTERN
136
136
 
137
137
  # @!method exec_call?(node)
138
138
  def_node_matcher :exec_call?, <<~PATTERN
139
- (block (send _ {:instance_exec :class_exec :module_exec} ...) ...)
139
+ ({block numblock} (send _ {:instance_exec :class_exec :module_exec} ...) ...)
140
140
  PATTERN
141
141
  end
142
142
  end
@@ -281,7 +281,7 @@ module RuboCop
281
281
  .drop_while { |r| !r.equal?(range) }
282
282
  .each_cons(2)
283
283
  .map { |range1, range2| range1.end.join(range2.begin).source }
284
- .all? { |intervening| /\A\s*,\s*\Z/.match?(intervening) }
284
+ .all?(/\A\s*,\s*\z/)
285
285
  end
286
286
 
287
287
  SIMILAR_COP_NAMES_CACHE = Hash.new do |hash, cop_name|
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for string conversion in string interpolation,
6
+ # Checks for string conversion in string interpolation, `print`, `puts`, and `warn` arguments,
7
7
  # which is redundant.
8
8
  #
9
9
  # @example
@@ -11,18 +11,26 @@ module RuboCop
11
11
  # # bad
12
12
  #
13
13
  # "result is #{something.to_s}"
14
+ # print something.to_s
15
+ # puts something.to_s
16
+ # warn something.to_s
14
17
  #
15
18
  # @example
16
19
  #
17
20
  # # good
18
21
  #
19
22
  # "result is #{something}"
23
+ # print something
24
+ # puts something
25
+ # warn something
26
+ #
20
27
  class RedundantStringCoercion < Base
21
28
  include Interpolation
22
29
  extend AutoCorrector
23
30
 
24
- MSG_DEFAULT = 'Redundant use of `Object#to_s` in interpolation.'
25
- MSG_SELF = 'Use `self` instead of `Object#to_s` in interpolation.'
31
+ MSG_DEFAULT = 'Redundant use of `Object#to_s` in %<context>s.'
32
+ MSG_SELF = 'Use `self` instead of `Object#to_s` in %<context>s.'
33
+ RESTRICT_ON_SEND = %i[print puts warn].freeze
26
34
 
27
35
  # @!method to_s_without_args?(node)
28
36
  def_node_matcher :to_s_without_args?, '(send _ :to_s)'
@@ -32,18 +40,30 @@ module RuboCop
32
40
 
33
41
  return unless to_s_without_args?(final_node)
34
42
 
35
- message = final_node.receiver ? MSG_DEFAULT : MSG_SELF
36
-
37
- add_offense(final_node.loc.selector, message: message) do |corrector|
38
- receiver = final_node.receiver
39
- corrector.replace(
40
- final_node,
41
- if receiver
42
- receiver.source
43
- else
44
- 'self'
45
- end
46
- )
43
+ register_offense(final_node, 'interpolation')
44
+ end
45
+
46
+ def on_send(node)
47
+ return if node.receiver
48
+
49
+ node.each_child_node(:send) do |child|
50
+ next unless child.method?(:to_s)
51
+
52
+ register_offense(child, "`#{node.method_name}`")
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def register_offense(node, context)
59
+ receiver = node.receiver
60
+ template = receiver ? MSG_DEFAULT : MSG_SELF
61
+ message = format(template, context: context)
62
+
63
+ add_offense(node.loc.selector, message: message) do |corrector|
64
+ replacement = receiver ? receiver.source : 'self'
65
+
66
+ corrector.replace(node, replacement)
47
67
  end
48
68
  end
49
69
  end
@@ -33,6 +33,10 @@ module RuboCop
33
33
  message << '.' unless message.end_with?('.')
34
34
  message
35
35
  end
36
+
37
+ def find_severity(_range, _severity)
38
+ :fatal
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -14,8 +14,14 @@ module RuboCop
14
14
  #
15
15
  # # good
16
16
  # def foo(x, y = 1)
17
+ # # Alternatives to `__callee__` are `__method__` and `:foo`.
17
18
  # return to_enum(__callee__, x, y)
18
- # # alternatives to `__callee__` are `__method__` and `:foo`
19
+ # end
20
+ #
21
+ # # good
22
+ # def foo(x, y = 1)
23
+ # # It is also allowed if it is wrapped in some method like Sorbet.
24
+ # return to_enum(T.must(__callee__), x, y)
19
25
  # end
20
26
  #
21
27
  class ToEnumArguments < Base
@@ -111,9 +111,9 @@ module RuboCop
111
111
  return false unless node.block_type? || node.numblock_type?
112
112
 
113
113
  send_node = node.send_node
114
- return false if matches_allowed_pattern?(send_node.source)
115
-
116
- send_node.enumerable_method? || send_node.enumerator_method? || send_node.method?(:loop)
114
+ loopable = send_node.enumerable_method? || send_node.enumerator_method? ||
115
+ send_node.method?(:loop)
116
+ loopable && !matches_allowed_pattern?(send_node.source)
117
117
  end
118
118
 
119
119
  def check(node)
@@ -41,15 +41,23 @@ module RuboCop
41
41
  MSG = 'Useless method definition detected.'
42
42
 
43
43
  def on_def(node)
44
- return if use_rest_or_optional_args?(node)
44
+ return if method_definition_with_modifier?(node) || use_rest_or_optional_args?(node)
45
45
  return unless delegating?(node.body, node)
46
46
 
47
- add_offense(node) { |corrector| corrector.remove(node) }
47
+ add_offense(node) do |corrector|
48
+ range = node.parent&.send_type? ? node.parent : node
49
+
50
+ corrector.remove(range)
51
+ end
48
52
  end
49
53
  alias on_defs on_def
50
54
 
51
55
  private
52
56
 
57
+ def method_definition_with_modifier?(node)
58
+ node.parent&.send_type? && !node.parent&.non_bare_access_modifier?
59
+ end
60
+
53
61
  def use_rest_or_optional_args?(node)
54
62
  node.arguments.any? { |arg| arg.restarg_type? || arg.optarg_type? || arg.kwoptarg_type? }
55
63
  end
@@ -56,11 +56,13 @@ module RuboCop
56
56
 
57
57
  private
58
58
 
59
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
59
60
  def only_reraising?(resbody_node)
60
61
  return false if use_exception_variable_in_ensure?(resbody_node)
61
62
 
62
63
  body = resbody_node.body
63
- return false if body.nil? || !body.send_type? || !body.method?(:raise)
64
+
65
+ return false if body.nil? || !body.send_type? || !body.method?(:raise) || body.receiver
64
66
  return true unless body.arguments?
65
67
  return false if body.arguments.size > 1
66
68
 
@@ -68,6 +70,7 @@ module RuboCop
68
70
 
69
71
  exception_objects(resbody_node).include?(exception_name)
70
72
  end
73
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
71
74
 
72
75
  def use_exception_variable_in_ensure?(resbody_node)
73
76
  return false unless (exception_variable = resbody_node.exception_variable)
@@ -59,10 +59,10 @@ module RuboCop
59
59
  shuffle slice sort sort_by squeeze strip sub
60
60
  succ swapcase tr tr_s transform_values
61
61
  unicode_normalize uniq upcase].freeze
62
- METHODS_REPLACABLE_BY_EACH = %i[collect map].freeze
62
+ METHODS_REPLACEABLE_BY_EACH = %i[collect map].freeze
63
63
 
64
64
  NONMUTATING_METHODS = (NONMUTATING_METHODS_WITH_BANG_VERSION +
65
- METHODS_REPLACABLE_BY_EACH).freeze
65
+ METHODS_REPLACEABLE_BY_EACH).freeze
66
66
 
67
67
  def on_block(node)
68
68
  return unless node.body && !node.body.begin_type?
@@ -133,7 +133,11 @@ module RuboCop
133
133
  method_name = node.method_name
134
134
  return unless NONMUTATING_METHODS.include?(method_name)
135
135
 
136
- suggestion = METHODS_REPLACABLE_BY_EACH.include?(method_name) ? 'each' : "#{method_name}!"
136
+ suggestion = if METHODS_REPLACEABLE_BY_EACH.include?(method_name)
137
+ 'each'
138
+ else
139
+ "#{method_name}!"
140
+ end
137
141
  add_offense(node,
138
142
  message: format(NONMUTATING_MSG, method: method_name, suggest: suggestion))
139
143
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
  def consider_node?(node)
45
45
  return true if NESTING_BLOCKS.include?(node.type)
46
46
 
47
- count_blocks? && node.block_type?
47
+ count_blocks? && (node.block_type? || node.numblock_type?)
48
48
  end
49
49
 
50
50
  def message(max)
@@ -42,6 +42,7 @@ module RuboCop
42
42
  def on_class(node)
43
43
  check_code_length(node)
44
44
  end
45
+ alias on_sclass on_class
45
46
 
46
47
  def on_casgn(node)
47
48
  parent = node.parent
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module].freeze
13
+ CLASSLIKE_TYPES = %i[class module sclass].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -71,7 +71,7 @@ module RuboCop
71
71
  elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
72
72
  next_sibling.loc.line
73
73
  elsif (parent = node.parent)
74
- parent.loc.end ? parent.loc.end.line : parent.loc.line
74
+ parent.loc.respond_to?(:end) && parent.loc.end ? parent.loc.end.line : parent.loc.line
75
75
  else
76
76
  node.loc.end.line
77
77
  end
@@ -100,6 +100,7 @@ module RuboCop
100
100
  last_pair = node.parent.pairs.last
101
101
  return unless last_pair.key.source == last_pair.value.source
102
102
  return unless (dispatch_node = find_ancestor_method_dispatch_node(node))
103
+ return if dispatch_node.assignment_method?
103
104
  return if dispatch_node.parenthesized?
104
105
  return if dispatch_node.parent && parentheses?(dispatch_node.parent)
105
106
  return if last_expression?(dispatch_node) && !method_dispatch_as_argument?(dispatch_node)
@@ -175,7 +175,7 @@ module RuboCop
175
175
  end
176
176
 
177
177
  def set_new_arg_name(transformed_argname, corrector)
178
- corrector.replace(block_node.arguments.source_range, "|#{transformed_argname}|")
178
+ corrector.replace(block_node.arguments, "|#{transformed_argname}|")
179
179
  end
180
180
 
181
181
  def set_new_body_expression(transforming_body_expr, corrector)
@@ -69,7 +69,7 @@ module RuboCop
69
69
  end
70
70
 
71
71
  def first_line_comment(node)
72
- comment = processed_source.find_comment { |c| same_line?(c, node) }
72
+ comment = processed_source.comments.find { |c| same_line?(c, node) }
73
73
  return unless comment
74
74
 
75
75
  comment_source = comment.source
@@ -57,7 +57,7 @@ module RuboCop
57
57
  CONSTANT_MSG = 'Use only ascii symbols in constants.'
58
58
 
59
59
  def on_new_investigation
60
- processed_source.each_token do |token|
60
+ processed_source.tokens.each do |token|
61
61
  next if !should_check?(token) || token.text.ascii_only?
62
62
 
63
63
  message = token.type == :tIDENTIFIER ? IDENTIFIER_MSG : CONSTANT_MSG
@@ -5,6 +5,7 @@ module RuboCop
5
5
  module Naming
6
6
  # Recommends the use of inclusive language instead of problematic terms.
7
7
  # The cop can check the following locations for offenses:
8
+ #
8
9
  # - identifiers
9
10
  # - constants
10
11
  # - variables
@@ -12,6 +13,7 @@ module RuboCop
12
13
  # - symbols
13
14
  # - comments
14
15
  # - file paths
16
+ #
15
17
  # Each of these locations can be individually enabled/disabled via configuration,
16
18
  # for example CheckIdentifiers = true/false.
17
19
  #
@@ -22,6 +24,9 @@ module RuboCop
22
24
  # `WholeWord: true` can be set on a flagged term to indicate the cop should only match when
23
25
  # a term matches the whole word (partial matches will not be offenses).
24
26
  #
27
+ # The cop supports autocorrection when there is only one suggestion. When there are multiple
28
+ # suggestions, the best suggestion cannot be identified and will not be autocorrected.
29
+ #
25
30
  # @example FlaggedTerms: { whitelist: { Suggestions: ['allowlist'] } }
26
31
  # # Suggest replacing identifier whitelist with allowlist
27
32
  #
@@ -68,6 +73,7 @@ module RuboCop
68
73
  # TeslaVehicle
69
74
  class InclusiveLanguage < Base
70
75
  include RangeHelp
76
+ extend AutoCorrector
71
77
 
72
78
  EMPTY_ARRAY = [].freeze
73
79
  MSG = "Consider replacing '%<term>s'%<suffix>s."
@@ -92,7 +98,7 @@ module RuboCop
92
98
  private
93
99
 
94
100
  def investigate_tokens
95
- processed_source.each_token do |token|
101
+ processed_source.tokens.each do |token|
96
102
  next unless check_token?(token.type)
97
103
 
98
104
  word_locations = scan_for_words(token.text)
@@ -104,9 +110,16 @@ module RuboCop
104
110
 
105
111
  def add_offenses_for_token(token, word_locations)
106
112
  word_locations.each do |word_location|
107
- start_position = token.pos.begin_pos + token.pos.source.index(word_location.word)
108
- range = range_between(start_position, start_position + word_location.word.length)
109
- add_offense(range, message: create_message(word_location.word))
113
+ word = word_location.word
114
+ range = offense_range(token, word)
115
+
116
+ add_offense(range, message: create_message(word)) do |corrector|
117
+ suggestions = find_flagged_term(word)['Suggestions']
118
+
119
+ next unless suggestions.is_a?(String)
120
+
121
+ corrector.replace(range, suggestions)
122
+ end
110
123
  end
111
124
  end
112
125
 
@@ -264,6 +277,12 @@ module RuboCop
264
277
  end
265
278
  " with #{suggestion_str}"
266
279
  end
280
+
281
+ def offense_range(token, word)
282
+ start_position = token.pos.begin_pos + token.pos.source.index(word)
283
+
284
+ range_between(start_position, start_position + word.length)
285
+ end
267
286
  end
268
287
  end
269
288
  end
@@ -10,8 +10,8 @@ module RuboCop
10
10
  #
11
11
  # Naming/MethodName:
12
12
  # AllowedPatterns:
13
- # - '\A\s*onSelectionBulkChange\s*'
14
- # - '\A\s*onSelectionCleared\s*'
13
+ # - '\AonSelectionBulkChange\z'
14
+ # - '\AonSelectionCleared\z'
15
15
  #
16
16
  # Method names matching patterns are always allowed.
17
17
  #
@@ -70,7 +70,7 @@ module RuboCop
70
70
 
71
71
  def check(send_node)
72
72
  return if previous_line_comment?(send_node) || !groupable_accessor?(send_node)
73
- return unless (grouped_style? && sibling_accessors(send_node).size > 1) ||
73
+ return unless (grouped_style? && groupable_sibling_accessors(send_node).size > 1) ||
74
74
  (separated_style? && send_node.arguments.size > 1)
75
75
 
76
76
  message = message(send_node)
@@ -127,12 +127,12 @@ module RuboCop
127
127
  style == :separated
128
128
  end
129
129
 
130
- def sibling_accessors(send_node)
130
+ def groupable_sibling_accessors(send_node)
131
131
  send_node.parent.each_child_node(:send).select do |sibling|
132
132
  sibling.attribute_accessor? &&
133
133
  sibling.method?(send_node.method_name) &&
134
134
  node_visibility(sibling) == node_visibility(send_node) &&
135
- !previous_line_comment?(sibling)
135
+ groupable_accessor?(sibling) && !previous_line_comment?(sibling)
136
136
  end
137
137
  end
138
138
 
@@ -143,7 +143,7 @@ module RuboCop
143
143
 
144
144
  def preferred_accessors(node)
145
145
  if grouped_style?
146
- accessors = sibling_accessors(node)
146
+ accessors = groupable_sibling_accessors(node)
147
147
  group_accessors(node, accessors) if node.loc == accessors.first.loc
148
148
  else
149
149
  separate_accessors(node)
@@ -11,7 +11,7 @@ module RuboCop
11
11
  #
12
12
  # Moving from compact to nested children requires knowledge of whether the
13
13
  # outer parent is a module or a class. Moving from nested to compact requires
14
- # verification that the outer parent is defined elsewhere. Rubocop does not
14
+ # verification that the outer parent is defined elsewhere. RuboCop does not
15
15
  # have the knowledge to perform either operation safely and thus requires
16
16
  # manual oversight.
17
17
  #