rubocop 0.91.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -5
  3. data/config/default.yml +143 -56
  4. data/lib/rubocop.rb +17 -5
  5. data/lib/rubocop/cached_data.rb +2 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/version.rb +1 -1
  8. data/lib/rubocop/comment_config.rb +1 -1
  9. data/lib/rubocop/config.rb +4 -0
  10. data/lib/rubocop/config_loader.rb +19 -2
  11. data/lib/rubocop/config_loader_resolver.rb +7 -5
  12. data/lib/rubocop/config_regeneration.rb +33 -0
  13. data/lib/rubocop/config_validator.rb +7 -6
  14. data/lib/rubocop/cop/badge.rb +9 -24
  15. data/lib/rubocop/cop/base.rb +16 -1
  16. data/lib/rubocop/cop/bundler/duplicated_gem.rb +23 -3
  17. data/lib/rubocop/cop/commissioner.rb +36 -22
  18. data/lib/rubocop/cop/corrector.rb +3 -1
  19. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  20. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  21. data/lib/rubocop/cop/force.rb +1 -1
  22. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
  23. data/lib/rubocop/cop/layout/array_alignment.rb +1 -0
  24. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  25. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  26. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  27. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -7
  28. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  29. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  30. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -11
  31. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  32. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  33. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  34. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  35. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  36. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  37. data/lib/rubocop/cop/lint/debugger.rb +2 -3
  38. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  39. data/lib/rubocop/cop/lint/empty_block.rb +46 -0
  40. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  41. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  42. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +17 -3
  43. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  44. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  45. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  47. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  48. data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
  49. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  50. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
  51. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  52. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  53. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  54. data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
  55. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  56. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  57. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  58. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  59. data/lib/rubocop/cop/offense.rb +18 -5
  60. data/lib/rubocop/cop/security/open.rb +12 -10
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  62. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  63. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  64. data/lib/rubocop/cop/style/array_coercion.rb +4 -0
  65. data/lib/rubocop/cop/style/case_like_if.rb +20 -4
  66. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  67. data/lib/rubocop/cop/style/combinable_loops.rb +8 -1
  68. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  69. data/lib/rubocop/cop/style/date_time.rb +12 -1
  70. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +67 -0
  71. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  72. data/lib/rubocop/cop/style/for.rb +0 -4
  73. data/lib/rubocop/cop/style/format_string_token.rb +48 -3
  74. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  77. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  78. data/lib/rubocop/cop/style/mixin_usage.rb +7 -27
  79. data/lib/rubocop/cop/style/multiple_comparison.rb +54 -7
  80. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  81. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +11 -3
  82. data/lib/rubocop/cop/style/raise_args.rb +0 -3
  83. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  84. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  85. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  86. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  87. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +45 -24
  88. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  89. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  90. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  91. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  92. data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
  93. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  94. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  95. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  96. data/lib/rubocop/cop/team.rb +6 -1
  97. data/lib/rubocop/cop/util.rb +1 -1
  98. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  99. data/lib/rubocop/ext/regexp_node.rb +29 -10
  100. data/lib/rubocop/ext/regexp_parser.rb +77 -0
  101. data/lib/rubocop/formatter/disabled_config_formatter.rb +12 -5
  102. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  103. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  104. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  105. data/lib/rubocop/magic_comment.rb +2 -2
  106. data/lib/rubocop/options.rb +22 -17
  107. data/lib/rubocop/result_cache.rb +8 -2
  108. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  109. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  110. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  111. data/lib/rubocop/runner.rb +9 -5
  112. data/lib/rubocop/target_finder.rb +27 -26
  113. data/lib/rubocop/target_ruby.rb +1 -1
  114. data/lib/rubocop/version.rb +61 -6
  115. metadata +21 -16
  116. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
@@ -75,7 +75,7 @@ module RuboCop
75
75
  def offense?(node)
76
76
  condition, if_branch, else_branch = *node
77
77
 
78
- return false if use_if_branch?(else_branch)
78
+ return false if use_if_branch?(else_branch) || use_hash_key_assignment?(else_branch)
79
79
 
80
80
  condition == if_branch && !node.elsif? && (
81
81
  node.ternary? ||
@@ -88,6 +88,10 @@ module RuboCop
88
88
  else_branch&.if_type?
89
89
  end
90
90
 
91
+ def use_hash_key_assignment?(else_branch)
92
+ else_branch&.send_type? && else_branch&.method?(:[]=)
93
+ end
94
+
91
95
  def else_source(else_branch)
92
96
  if require_parentheses?(else_branch)
93
97
  "(#{else_branch.source})"
@@ -51,7 +51,12 @@ module RuboCop
51
51
  end
52
52
 
53
53
  def single_variable_interpolation?(node)
54
- node.children.one? && variable_interpolation?(node.children.first)
54
+ return false unless node.children.one?
55
+
56
+ first_child = node.children.first
57
+
58
+ variable_interpolation?(first_child) ||
59
+ first_child.send_type? && !first_child.operator_method?
55
60
  end
56
61
 
57
62
  def interpolation?(node)
@@ -104,9 +104,13 @@ module RuboCop
104
104
  return offense(begin_node, 'a variable') if node.variable?
105
105
  return offense(begin_node, 'a constant') if node.const_type?
106
106
 
107
+ return offense(begin_node, 'an interpolated expression') if interpolation?(begin_node)
108
+
107
109
  check_send(begin_node, node) if node.call_type?
108
110
  end
109
111
 
112
+ def_node_matcher :interpolation?, '[^begin ^^dstr]'
113
+
110
114
  def check_send(begin_node, node)
111
115
  return check_unary(begin_node, node) if node.unary_operation?
112
116
 
@@ -19,35 +19,23 @@ module RuboCop
19
19
  # # good
20
20
  # r = /\s/
21
21
  #
22
+ # # bad
23
+ # r = %r{/[b]}
24
+ #
25
+ # # good
26
+ # r = %r{/b}
27
+ #
22
28
  # # good
23
29
  # r = /[ab]/
24
30
  class RedundantRegexpCharacterClass < Base
25
- include MatchRange
26
- include RegexpLiteralHelp
27
31
  extend AutoCorrector
28
32
 
33
+ REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS = '.*+?{}()|$'.chars.freeze
29
34
  MSG_REDUNDANT_CHARACTER_CLASS = 'Redundant single-element character class, ' \
30
35
  '`%<char_class>s` can be replaced with `%<element>s`.'
31
36
 
32
- PATTERN = /
33
- (
34
- (?<!\\) # No \-prefix (i.e. not escaped)
35
- \[ # Literal [
36
- (?!\#\{) # Not (the start of) an interpolation
37
- (?: # Either...
38
- \\[^b] | # Any escaped character except b (which would change behaviour)
39
- [^.*+?{}()|$] | # or one that doesn't require escaping outside the character class
40
- \\[upP]\{[^}]+\} # or a unicode code-point or property
41
- )
42
- (?<!\\) # No \-prefix (i.e. not escaped)
43
- \] # Literal ]
44
- )
45
- /x.freeze
46
-
47
37
  def on_regexp(node)
48
38
  each_redundant_character_class(node) do |loc|
49
- next if whitespace_in_free_space_mode?(node, loc)
50
-
51
39
  add_offense(
52
40
  loc, message: format(
53
41
  MSG_REDUNDANT_CHARACTER_CLASS,
@@ -63,19 +51,52 @@ module RuboCop
63
51
  private
64
52
 
65
53
  def each_redundant_character_class(node)
66
- pattern_source(node).scan(PATTERN) do
67
- yield match_range(node.loc.begin.end, Regexp.last_match)
54
+ each_single_element_character_class(node) do |char_class|
55
+ next unless redundant_single_element_character_class?(node, char_class)
56
+
57
+ yield char_class.loc.body
68
58
  end
69
59
  end
70
60
 
61
+ def each_single_element_character_class(node)
62
+ node.parsed_tree&.each_expression do |expr|
63
+ next if expr.type != :set || expr.expressions.size != 1
64
+ next if expr.negative?
65
+ next if %i[set posixclass nonposixclass].include?(expr.expressions.first.type)
66
+
67
+ yield expr
68
+ end
69
+ end
70
+
71
+ def redundant_single_element_character_class?(node, char_class)
72
+ class_elem = char_class.expressions.first.text
73
+
74
+ non_redundant =
75
+ whitespace_in_free_space_mode?(node, class_elem) ||
76
+ backslash_b?(class_elem) ||
77
+ requires_escape_outside_char_class?(class_elem)
78
+
79
+ !non_redundant
80
+ end
81
+
71
82
  def without_character_class(loc)
72
83
  loc.source[1..-2]
73
84
  end
74
85
 
75
- def whitespace_in_free_space_mode?(node, loc)
76
- return false unless freespace_mode_regexp?(node)
86
+ def whitespace_in_free_space_mode?(node, elem)
87
+ return false unless node.extended?
88
+
89
+ /\s/.match?(elem)
90
+ end
91
+
92
+ def backslash_b?(elem)
93
+ # \b's behaviour is different inside and outside of a character class, matching word
94
+ # boundaries outside but backspace (0x08) when inside.
95
+ elem == '\b'
96
+ end
77
97
 
78
- /\[\s\]/.match?(loc.source)
98
+ def requires_escape_outside_char_class?(elem)
99
+ REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS.include?(elem)
79
100
  end
80
101
  end
81
102
  end
@@ -34,7 +34,6 @@ module RuboCop
34
34
  # /[+\-]\d/
35
35
  class RedundantRegexpEscape < Base
36
36
  include RangeHelp
37
- include RegexpLiteralHelp
38
37
  extend AutoCorrector
39
38
 
40
39
  MSG_REDUNDANT_ESCAPE = 'Redundant escape inside regexp literal'
@@ -59,9 +58,9 @@ module RuboCop
59
58
 
60
59
  def allowed_escape?(node, char, within_character_class)
61
60
  # Strictly speaking a few single-letter metachars are currently
62
- # unnecessary to "escape", e.g. g, i, E, F, but enumerating them is
61
+ # unnecessary to "escape", e.g. i, E, F, but enumerating them is
63
62
  # rather difficult, and their behaviour could change over time with
64
- # different versions of Ruby so that e.g. /\g/ != /g/
63
+ # different versions of Ruby so that e.g. /\i/ != /i/
65
64
  return true if /[[:alnum:]]/.match?(char)
66
65
  return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
67
66
 
@@ -82,19 +81,13 @@ module RuboCop
82
81
  end
83
82
 
84
83
  def each_escape(node)
85
- pattern_source(node).each_char.with_index.reduce(
86
- [nil, 0]
87
- ) do |(previous, char_class_depth), (current, index)|
88
- if previous == '\\'
89
- yield [current, index - 1, !char_class_depth.zero?]
90
-
91
- [nil, char_class_depth]
92
- elsif previous == '['
93
- [current, char_class_depth + 1]
94
- elsif current == ']'
95
- [current, char_class_depth - 1]
84
+ node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
85
+ yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
86
+
87
+ if expr.type == :set
88
+ char_class_depth + (event == :enter ? 1 : -1)
96
89
  else
97
- [current, char_class_depth]
90
+ char_class_depth
98
91
  end
99
92
  end
100
93
  end
@@ -129,6 +129,9 @@ module RuboCop
129
129
  def allowed_send_node?(node)
130
130
  @allowed_send_nodes.include?(node) ||
131
131
  @local_variables_scopes[node].include?(node.method_name) ||
132
+ node.each_ancestor.any? do |ancestor|
133
+ @local_variables_scopes[ancestor].include?(node.method_name)
134
+ end ||
132
135
  KERNEL_METHODS.include?(node.method_name)
133
136
  end
134
137
 
@@ -142,10 +142,22 @@ module RuboCop
142
142
  end
143
143
 
144
144
  def comments(node)
145
- processed_source.each_comment_in_lines(
146
- node.loc.first_line...
147
- node.loc.last_line
148
- ).to_a
145
+ relevant_comment_ranges(node).each.with_object([]) do |range, comments|
146
+ comments.concat(processed_source.each_comment_in_lines(range).to_a)
147
+ end
148
+ end
149
+
150
+ def relevant_comment_ranges(node)
151
+ # Get source lines ranges inside the if node that aren't inside an inner node
152
+ # Comments inside an inner node should remain attached to that node, and not
153
+ # moved.
154
+ begin_pos = node.loc.first_line
155
+ end_pos = node.loc.last_line
156
+
157
+ node.child_nodes.each.with_object([]) do |child, ranges|
158
+ ranges << (begin_pos...child.loc.first_line)
159
+ begin_pos = child.loc.last_line
160
+ end << (begin_pos...end_pos)
149
161
  end
150
162
 
151
163
  def allowed_if_condition?(node)
@@ -70,6 +70,9 @@ module RuboCop
70
70
  private
71
71
 
72
72
  def check_for_line_terminator_or_opener
73
+ # Make the obvious check first
74
+ return unless @processed_source.raw_source.include?(';')
75
+
73
76
  each_semicolon { |line, column| convention_on(line, column, true) }
74
77
  end
75
78
 
@@ -33,6 +33,10 @@ module RuboCop
33
33
  }
34
34
  PATTERN
35
35
 
36
+ def on_new_investigation
37
+ @corrected_nodes = nil
38
+ end
39
+
36
40
  def on_send(node)
37
41
  return unless string_concatenation?(node)
38
42
 
@@ -42,8 +46,12 @@ module RuboCop
42
46
  collect_parts(topmost_plus_node, parts)
43
47
 
44
48
  add_offense(topmost_plus_node) do |corrector|
45
- if parts.none? { |part| uncorrectable?(part) }
49
+ correctable_parts = parts.none? { |part| uncorrectable?(part) }
50
+ if correctable_parts && !corrected_ancestor?(topmost_plus_node)
46
51
  corrector.replace(topmost_plus_node, replacement(parts))
52
+
53
+ @corrected_nodes ||= Set.new.compare_by_identity
54
+ @corrected_nodes.add(topmost_plus_node)
47
55
  end
48
56
  end
49
57
  end
@@ -80,6 +88,10 @@ module RuboCop
80
88
  part.each_descendant(:block).any?
81
89
  end
82
90
 
91
+ def corrected_ancestor?(node)
92
+ node.each_ancestor(:send).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
93
+ end
94
+
83
95
  def replacement(parts)
84
96
  interpolated_parts =
85
97
  parts.map do |part|
@@ -87,7 +99,7 @@ module RuboCop
87
99
  if single_quoted?(part)
88
100
  part.value.gsub('\\') { '\\\\' }
89
101
  else
90
- escape_string(part.value)
102
+ part.value.inspect[1..-2]
91
103
  end
92
104
  else
93
105
  "\#{#{part.source}}"
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop enforces the use of shorthand-style swapping of 2 variables.
7
+ # Its autocorrection is marked as unsafe, because it can erroneously remove
8
+ # the temporary variable which is used later.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # tmp = x
13
+ # x = y
14
+ # y = tmp
15
+ #
16
+ # # good
17
+ # x, y = y, x
18
+ #
19
+ class SwapValues < Base
20
+ include RangeHelp
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Replace this and assignments at lines %<x_line>d '\
24
+ 'and %<y_line>d with `%<replacement>s`.'
25
+
26
+ SIMPLE_ASSIGNMENT_TYPES = %i[lvasgn ivasgn cvasgn gvasgn casgn].to_set.freeze
27
+
28
+ def on_asgn(node)
29
+ return if allowed_assignment?(node)
30
+
31
+ tmp_assign = node
32
+ x_assign, y_assign = *node.right_siblings.take(2)
33
+ return unless x_assign && y_assign && swapping_values?(tmp_assign, x_assign, y_assign)
34
+
35
+ add_offense(node, message: message(x_assign, y_assign)) do |corrector|
36
+ range = correction_range(tmp_assign, y_assign)
37
+ corrector.replace(range, replacement(x_assign))
38
+ end
39
+ end
40
+
41
+ SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
42
+
43
+ private
44
+
45
+ def allowed_assignment?(node)
46
+ node.parent&.mlhs_type? || node.parent&.shorthand_asgn?
47
+ end
48
+
49
+ def swapping_values?(tmp_assign, x_assign, y_assign)
50
+ simple_assignment?(tmp_assign) &&
51
+ simple_assignment?(x_assign) &&
52
+ simple_assignment?(y_assign) &&
53
+ lhs(x_assign) == rhs(tmp_assign) &&
54
+ lhs(y_assign) == rhs(x_assign) &&
55
+ rhs(y_assign) == lhs(tmp_assign)
56
+ end
57
+
58
+ def simple_assignment?(node)
59
+ SIMPLE_ASSIGNMENT_TYPES.include?(node.type)
60
+ end
61
+
62
+ def message(x_assign, y_assign)
63
+ format(
64
+ MSG,
65
+ x_line: x_assign.first_line,
66
+ y_line: y_assign.first_line,
67
+ replacement: replacement(x_assign)
68
+ )
69
+ end
70
+
71
+ def replacement(x_assign)
72
+ x = lhs(x_assign)
73
+ y = rhs(x_assign)
74
+ "#{x}, #{y} = #{y}, #{x}"
75
+ end
76
+
77
+ def lhs(node)
78
+ case node.type
79
+ when :casgn
80
+ namespace, name, = *node
81
+ if namespace
82
+ "#{namespace.const_name}::#{name}"
83
+ else
84
+ name.to_s
85
+ end
86
+ else
87
+ node.children[0].to_s
88
+ end
89
+ end
90
+
91
+ def rhs(node)
92
+ case node.type
93
+ when :casgn
94
+ node.children[2].source
95
+ else
96
+ node.children[1].source
97
+ end
98
+ end
99
+
100
+ def correction_range(tmp_assign, y_assign)
101
+ range_by_whole_lines(
102
+ range_between(tmp_assign.source_range.begin_pos, y_assign.source_range.end_pos)
103
+ )
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -192,7 +192,7 @@ module RuboCop
192
192
  end
193
193
 
194
194
  def_node_matcher :method_name, <<~PATTERN
195
- {($:defined? (send nil? _) ...)
195
+ {($:defined? _ ...)
196
196
  (send {_ nil?} $_ _ ...)}
197
197
  PATTERN
198
198
 
@@ -36,6 +36,8 @@ module RuboCop
36
36
  MSG = 'Do not use trailing `_`s in parallel assignment. ' \
37
37
  'Prefer `%<code>s`.'
38
38
  UNDERSCORE = '_'
39
+ DISALLOW = %i[lvasgn splat].freeze
40
+ private_constant :DISALLOW
39
41
 
40
42
  def on_masgn(node)
41
43
  ranges = unneeded_ranges(node)
@@ -64,7 +66,7 @@ module RuboCop
64
66
 
65
67
  def find_first_possible_offense(variables)
66
68
  variables.reduce(nil) do |offense, variable|
67
- break offense unless %i[lvasgn splat].include?(variable.type)
69
+ break offense unless DISALLOW.include?(variable.type)
68
70
 
69
71
  var, = *variable
70
72
  var, = *var
@@ -99,7 +99,12 @@ module RuboCop
99
99
  def self.forces_for(cops)
100
100
  needed = Hash.new { |h, k| h[k] = [] }
101
101
  cops.each do |cop|
102
- Array(cop.class.joining_forces).each { |force| needed[force] << cop }
102
+ forces = cop.class.joining_forces
103
+ if forces.is_a?(Array)
104
+ forces.each { |force| needed[force] << cop }
105
+ elsif forces
106
+ needed[forces] << cop
107
+ end
103
108
  end
104
109
 
105
110
  needed.map do |force_class, joining_cops|
@@ -62,7 +62,7 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def begins_its_line?(range)
65
- (range.source_line =~ /\S/) == range.column
65
+ range.source_line.index(/\S/) == range.column
66
66
  end
67
67
 
68
68
  # Returns, for example, a bare `if` node if the given node is an `if`