rubocop 0.92.0 → 1.2.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -7
  3. data/config/default.yml +169 -59
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop.rb +15 -3
  6. data/lib/rubocop/cached_data.rb +2 -1
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  8. data/lib/rubocop/cli/command/version.rb +1 -1
  9. data/lib/rubocop/comment_config.rb +1 -1
  10. data/lib/rubocop/config.rb +4 -0
  11. data/lib/rubocop/config_loader.rb +19 -2
  12. data/lib/rubocop/config_loader_resolver.rb +7 -5
  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/class_structure.rb +7 -0
  24. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  25. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  26. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  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/end_alignment.rb +3 -3
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  31. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  32. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  33. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -11
  34. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  35. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  36. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  37. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  38. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  39. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  40. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  41. data/lib/rubocop/cop/lint/debugger.rb +2 -3
  42. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  43. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  44. data/lib/rubocop/cop/lint/empty_block.rb +59 -0
  45. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  46. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  47. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +17 -3
  48. data/lib/rubocop/cop/lint/loop.rb +0 -4
  49. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  50. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  51. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  52. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  53. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  54. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  55. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  56. data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
  57. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  58. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
  59. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  60. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  61. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  62. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  63. data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
  64. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
  65. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  66. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  67. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
  68. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  69. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  70. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  71. data/lib/rubocop/cop/naming/variable_number.rb +82 -8
  72. data/lib/rubocop/cop/offense.rb +18 -5
  73. data/lib/rubocop/cop/security/open.rb +12 -10
  74. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  75. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  76. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  77. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  78. data/lib/rubocop/cop/style/case_like_if.rb +18 -6
  79. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  80. data/lib/rubocop/cop/style/collection_compact.rb +85 -0
  81. data/lib/rubocop/cop/style/combinable_loops.rb +8 -1
  82. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  83. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +67 -0
  84. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  85. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  86. data/lib/rubocop/cop/style/for.rb +0 -4
  87. data/lib/rubocop/cop/style/format_string_token.rb +48 -3
  88. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  89. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  90. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  91. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
  92. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  93. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  94. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  95. data/lib/rubocop/cop/style/multiple_comparison.rb +54 -7
  96. data/lib/rubocop/cop/style/negated_if_else_condition.rb +99 -0
  97. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  98. data/lib/rubocop/cop/style/raise_args.rb +21 -9
  99. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  100. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  101. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  102. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  103. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +45 -24
  104. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  105. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  106. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  107. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  108. data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
  109. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  110. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  111. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  112. data/lib/rubocop/cop/team.rb +6 -1
  113. data/lib/rubocop/cop/util.rb +5 -1
  114. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  115. data/lib/rubocop/ext/regexp_node.rb +35 -11
  116. data/lib/rubocop/ext/regexp_parser.rb +84 -0
  117. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  118. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  119. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  120. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  121. data/lib/rubocop/magic_comment.rb +2 -2
  122. data/lib/rubocop/options.rb +6 -1
  123. data/lib/rubocop/result_cache.rb +8 -2
  124. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  125. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  126. data/lib/rubocop/runner.rb +4 -4
  127. data/lib/rubocop/target_finder.rb +23 -25
  128. data/lib/rubocop/version.rb +56 -6
  129. metadata +22 -8
  130. 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.start_index, !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`
@@ -123,6 +123,10 @@ module RuboCop
123
123
  node1.loc.line == node2.loc.line
124
124
  end
125
125
 
126
+ def indent(node)
127
+ ' ' * node.loc.column
128
+ end
129
+
126
130
  def to_supported_styles(enforced_style)
127
131
  enforced_style
128
132
  .sub(/^Enforced/, 'Supported')