rubocop 0.31.0 → 0.32.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +13 -0
  4. data/config/disabled.yml +4 -0
  5. data/config/enabled.yml +34 -8
  6. data/lib/rubocop.rb +4 -1
  7. data/lib/rubocop/cop/cop.rb +18 -12
  8. data/lib/rubocop/cop/lint/debugger.rb +7 -1
  9. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  10. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +10 -0
  11. data/lib/rubocop/cop/lint/nested_method_definition.rb +31 -0
  12. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +9 -0
  13. data/lib/rubocop/cop/lint/unneeded_disable.rb +53 -0
  14. data/lib/rubocop/cop/metrics/class_length.rb +1 -1
  15. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  16. data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
  17. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
  18. data/lib/rubocop/cop/mixin/if_node.rb +10 -0
  19. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +8 -1
  20. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +8 -1
  21. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -5
  22. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  23. data/lib/rubocop/cop/offense.rb +16 -3
  24. data/lib/rubocop/cop/performance/count.rb +33 -30
  25. data/lib/rubocop/cop/performance/sample.rb +103 -59
  26. data/lib/rubocop/cop/performance/size.rb +2 -1
  27. data/lib/rubocop/cop/rails/time_zone.rb +14 -6
  28. data/lib/rubocop/cop/style/align_hash.rb +7 -3
  29. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +39 -11
  30. data/lib/rubocop/cop/style/case_indentation.rb +18 -4
  31. data/lib/rubocop/cop/style/comment_annotation.rb +22 -7
  32. data/lib/rubocop/cop/style/documentation.rb +11 -5
  33. data/lib/rubocop/cop/style/empty_else.rb +25 -0
  34. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -5
  35. data/lib/rubocop/cop/style/indentation_width.rb +1 -5
  36. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +12 -10
  37. data/lib/rubocop/cop/style/next.rb +1 -1
  38. data/lib/rubocop/cop/style/parallel_assignment.rb +196 -0
  39. data/lib/rubocop/cop/style/single_line_methods.rb +1 -4
  40. data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +41 -0
  41. data/lib/rubocop/cop/style/struct_inheritance.rb +11 -10
  42. data/lib/rubocop/cop/style/trailing_blank_lines.rb +8 -0
  43. data/lib/rubocop/cop/style/trailing_comma.rb +1 -1
  44. data/lib/rubocop/cop/team.rb +8 -1
  45. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  46. data/lib/rubocop/formatter/formatter_set.rb +24 -1
  47. data/lib/rubocop/options.rb +4 -0
  48. data/lib/rubocop/processed_source.rb +4 -1
  49. data/lib/rubocop/runner.rb +12 -7
  50. data/lib/rubocop/target_finder.rb +3 -3
  51. data/lib/rubocop/version.rb +1 -1
  52. data/relnotes/v0.32.0.md +139 -0
  53. data/rubocop.gemspec +2 -2
  54. metadata +12 -8
  55. data/lib/rubocop/cop/performance/parallel_assignment.rb +0 -79
@@ -27,12 +27,13 @@ module RuboCop
27
27
  MSG = 'Use `size` instead of `count`.'
28
28
 
29
29
  def on_send(node)
30
- receiver, method = *node
30
+ receiver, method, args = *node
31
31
 
32
32
  return if receiver.nil?
33
33
  return unless method == :count
34
34
  return unless array?(receiver) || hash?(receiver)
35
35
  return if node.parent && node.parent.block_type?
36
+ return if args && args.block_pass_type?
36
37
 
37
38
  add_offense(node, node.loc.selector)
38
39
  end
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  DANGER_METHODS = [:now, :local, :new, :strftime, :parse, :at]
41
41
 
42
- ACCEPTED_METHODS = [:in_time_zone, :utc,
42
+ ACCEPTED_METHODS = [:in_time_zone, :utc, :getlocal,
43
43
  :iso8601, :jisx0301, :rfc3339,
44
44
  :to_i, :to_f]
45
45
 
@@ -68,13 +68,9 @@ module RuboCop
68
68
 
69
69
  def build_message(klass, method_name, node)
70
70
  if acceptable?
71
- accepted_methods = ACCEPTED_METHODS.map do |am|
72
- "`#{klass}.#{method_name}.#{am}`"
73
- end
74
-
75
71
  format(MSG_ACCEPTABLE,
76
72
  "#{klass}.#{method_name}",
77
- accepted_methods.join(', ')
73
+ acceptable_methods(klass, method_name, node).join(', ')
78
74
  )
79
75
  else
80
76
  safe_method_name = safe_method(method_name, node)
@@ -146,6 +142,18 @@ module RuboCop
146
142
  def good_methods
147
143
  style == :always ? [:zone] : [:zone] + ACCEPTED_METHODS
148
144
  end
145
+
146
+ def acceptable_methods(klass, method_name, node)
147
+ acceptable = [
148
+ "`#{klass}.zone.#{safe_method(method_name, node)}`"
149
+ ]
150
+
151
+ ACCEPTED_METHODS.each do |am|
152
+ acceptable << "`#{klass}.#{method_name}.#{am}`"
153
+ end
154
+
155
+ acceptable
156
+ end
149
157
  end
150
158
  end
151
159
  end
@@ -231,9 +231,13 @@ module RuboCop
231
231
  key, value = *node
232
232
 
233
233
  lambda do |corrector|
234
- adjust(corrector, key_delta, key.loc.expression)
235
- adjust(corrector, separator_delta, node.loc.operator)
236
- adjust(corrector, value_delta, value.loc.expression)
234
+ if value.nil?
235
+ adjust(corrector, key_delta, node.loc.expression)
236
+ else
237
+ adjust(corrector, key_delta, key.loc.expression)
238
+ adjust(corrector, separator_delta, node.loc.operator)
239
+ adjust(corrector, value_delta, value.loc.expression)
240
+ end
237
241
  end
238
242
  end
239
243
 
@@ -7,6 +7,7 @@ module RuboCop
7
7
  # if the last parameter is a hash.
8
8
  class BracesAroundHashParameters < Cop
9
9
  include ConfigurableEnforcedStyle
10
+ include AutocorrectUnlessChangingAST
10
11
 
11
12
  MSG = '%s curly braces around a hash parameter.'
12
13
 
@@ -28,9 +29,10 @@ module RuboCop
28
29
 
29
30
  def check(arg, args)
30
31
  if style == :braces && !braces?(arg)
31
- add_offense(arg, :expression, format(MSG, 'Missing'))
32
+ add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
32
33
  elsif style == :no_braces && braces?(arg)
33
- add_offense(arg, :expression, format(MSG, 'Redundant'))
34
+ add_offense(arg.parent, arg.loc.expression,
35
+ format(MSG, 'Redundant'))
34
36
  elsif style == :context_dependent
35
37
  check_context_dependent(arg, args)
36
38
  end
@@ -40,27 +42,53 @@ module RuboCop
40
42
  braces_around_2nd_from_end = args.length > 1 && args[-2].type == :hash
41
43
  if braces?(arg)
42
44
  unless braces_around_2nd_from_end
43
- add_offense(arg, :expression, format(MSG, 'Redundant'))
45
+ add_offense(arg.parent, arg.loc.expression,
46
+ format(MSG, 'Redundant'))
44
47
  end
45
48
  elsif braces_around_2nd_from_end
46
- add_offense(arg, :expression, format(MSG, 'Missing'))
49
+ add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
47
50
  end
48
51
  end
49
52
 
50
- def autocorrect(node)
53
+ # We let AutocorrectUnlessChangingAST#autocorrect work with the send
54
+ # node, becuase that context is needed. When parsing the code to see if
55
+ # the AST has changed, a braceless hash would not be parsed as a hash
56
+ # otherwise.
57
+ def correction(send_node)
58
+ _receiver, _method_name, *args = *send_node
59
+ node = args.last
51
60
  lambda do |corrector|
52
61
  if braces?(node)
53
- right_range = range_with_surrounding_space(node.loc.begin, :right)
54
- corrector.remove(right_range)
55
- left_range = range_with_surrounding_space(node.loc.end, :left)
56
- corrector.remove(left_range)
62
+ remove_braces(corrector, node)
57
63
  else
58
- corrector.insert_before(node.loc.expression, '{')
59
- corrector.insert_after(node.loc.expression, '}')
64
+ add_braces(corrector, node)
60
65
  end
61
66
  end
62
67
  end
63
68
 
69
+ def remove_braces(corrector, node)
70
+ comments = processed_source.comments
71
+ right_brace_and_space = range_with_surrounding_space(node.loc.end,
72
+ :left)
73
+ if comments.any? { |c| c.loc.line == right_brace_and_space.line }
74
+ # Removing a line break between a comment and the closing
75
+ # parenthesis would cause a syntax error, so we only remove the
76
+ # braces in that case.
77
+ corrector.remove(node.loc.begin)
78
+ corrector.remove(node.loc.end)
79
+ else
80
+ left_brace_and_space = range_with_surrounding_space(node.loc.begin,
81
+ :right)
82
+ corrector.remove(left_brace_and_space)
83
+ corrector.remove(right_brace_and_space)
84
+ end
85
+ end
86
+
87
+ def add_braces(corrector, node)
88
+ corrector.insert_before(node.loc.expression, '{')
89
+ corrector.insert_after(node.loc.expression, '}')
90
+ end
91
+
64
92
  def non_empty_hash?(arg)
65
93
  arg && arg.type == :hash && arg.children.any?
66
94
  end
@@ -8,6 +8,7 @@ module RuboCop
8
8
  #
9
9
  # It will register a separate offense for each misaligned *when*.
10
10
  class CaseIndentation < Cop
11
+ include AutocorrectAlignment
11
12
  include ConfigurableEnforcedStyle
12
13
 
13
14
  def on_case(case_node)
@@ -50,10 +51,6 @@ module RuboCop
50
51
  end
51
52
  end
52
53
 
53
- def configured_indentation_width
54
- config.for_cop('IndentationWidth')['Width']
55
- end
56
-
57
54
  def parameter_name
58
55
  'IndentWhenRelativeTo'
59
56
  end
@@ -64,6 +61,23 @@ module RuboCop
64
61
  when :end then case_node.location.end.column
65
62
  end
66
63
  end
64
+
65
+ def autocorrect(node)
66
+ when_column = node.location.keyword.column
67
+ source_buffer = node.loc.expression.source_buffer
68
+ begin_pos = node.loc.keyword.begin_pos
69
+ whitespace = Parser::Source::Range.new(source_buffer,
70
+ begin_pos - when_column,
71
+ begin_pos)
72
+ return false unless whitespace.source.strip.empty?
73
+
74
+ case_node = node.each_ancestor(:case).first
75
+ base_type = cop_config[parameter_name] == 'end' ? :end : :case
76
+ column = base_column(case_node, base_type)
77
+ column += configured_indentation_width if cop_config['IndentOneStep']
78
+
79
+ ->(corrector) { corrector.replace(whitespace, ' ' * column) }
80
+ end
67
81
  end
68
82
  end
69
83
  end
@@ -8,8 +8,11 @@ module RuboCop
8
8
  class CommentAnnotation < Cop
9
9
  include AnnotationComment
10
10
 
11
- MSG = 'Annotation keywords should be all upper case, followed by a ' \
12
- 'colon and a space, then a note describing the problem.'
11
+ MSG = 'Annotation keywords like `%s` should be all upper case, ' \
12
+ 'followed by a colon, and a space, ' \
13
+ 'then a note describing the problem.'
14
+ MISSING_NOTE = 'Annotation comment, with keyword `%s`, ' \
15
+ 'is missing a note.'
13
16
 
14
17
  def investigate(processed_source)
15
18
  processed_source.comments.each do |comment|
@@ -19,19 +22,31 @@ module RuboCop
19
22
 
20
23
  start = comment.loc.expression.begin_pos + margin.length
21
24
  length = first_word.length + colon.to_s.length + space.to_s.length
22
- range = Parser::Source::Range.new(processed_source.buffer,
25
+ source_buffer = comment.loc.expression.source_buffer
26
+ range = Parser::Source::Range.new(source_buffer,
23
27
  start,
24
28
  start + length)
25
- add_offense(range, range)
29
+ if note
30
+ add_offense(comment, range, format(MSG, first_word))
31
+ else
32
+ add_offense(comment, range, format(MISSING_NOTE, first_word))
33
+ end
26
34
  end
27
35
  end
28
36
 
29
37
  private
30
38
 
31
- def autocorrect(range)
39
+ def autocorrect(comment)
40
+ margin, first_word, colon, space, note = split_comment(comment)
41
+ start = comment.loc.expression.begin_pos + margin.length
42
+ return if note.nil?
43
+
32
44
  lambda do |corrector|
33
- annotation_keyword = range.source.split(/:?\s+/).first
34
- corrector.replace(range, annotation_keyword.upcase << ': ')
45
+ length = first_word.length + colon.to_s.length + space.to_s.length
46
+ range = Parser::Source::Range.new(comment.loc.expression.source,
47
+ start,
48
+ start + length)
49
+ corrector.replace(range, "#{first_word.upcase}: ")
35
50
  end
36
51
  end
37
52
 
@@ -65,16 +65,22 @@ module RuboCop
65
65
  # Returns true if the node has a comment on the line above it that
66
66
  # isn't an annotation.
67
67
  def associated_comment?(node, ast_with_comments)
68
- return false if ast_with_comments[node].empty?
68
+ preceding_comments = preceding_comments(node, ast_with_comments)
69
+ return false if preceding_comments.empty?
69
70
 
70
- preceding_comment = ast_with_comments[node].last
71
- distance = node.loc.keyword.line - preceding_comment.loc.line
71
+ distance = node.loc.keyword.line - preceding_comments.last.loc.line
72
72
  return false if distance > 1
73
- return false unless comment_line_only?(preceding_comment)
73
+ return false unless comment_line_only?(preceding_comments.last)
74
74
 
75
75
  # As long as there's at least one comment line that isn't an
76
76
  # annotation, it's OK.
77
- ast_with_comments[node].any? { |comment| !annotation?(comment) }
77
+ preceding_comments.any? { |comment| !annotation?(comment) }
78
+ end
79
+
80
+ def preceding_comments(node, ast_with_comments)
81
+ return [] unless node && ast_with_comments
82
+
83
+ ast_with_comments[node].select { |c| c.loc.line < node.loc.line }
78
84
  end
79
85
 
80
86
  def comment_line_only?(comment)
@@ -110,6 +110,31 @@ module RuboCop
110
110
  add_offense(node, :else, MSG)
111
111
  end
112
112
  end
113
+
114
+ def autocorrect(node)
115
+ return false if autocorrect_forbidden?(node.type.to_s)
116
+
117
+ lambda do |corrector|
118
+ end_pos = if node.loc.end
119
+ node.loc.end.begin_pos
120
+ else
121
+ node.loc.expression.end_pos + 1
122
+ end
123
+ range = Parser::Source::Range.new(node.loc.expression.source_buffer,
124
+ node.loc.else.begin_pos,
125
+ end_pos)
126
+ corrector.remove(range)
127
+ end
128
+ end
129
+
130
+ def autocorrect_forbidden?(type)
131
+ [type, 'both'].include? missing_else_style
132
+ end
133
+
134
+ def missing_else_style
135
+ missing_config = config.for_cop('Style/MissingElse')
136
+ missing_config['Enabled'] ? missing_config['EnforcedStyle'] : nil
137
+ end
113
138
  end
114
139
  end
115
140
  end
@@ -25,11 +25,7 @@ module RuboCop
25
25
  end
26
26
 
27
27
  def autocorrect(node)
28
- if node.loc.keyword.is?('if')
29
- cond, body = *node
30
- else
31
- cond, _else, body = *node
32
- end
28
+ cond, body, _else = if_node_parts(node)
33
29
 
34
30
  oneline =
35
31
  "#{body.loc.expression.source} #{node.loc.keyword.source} " +
@@ -157,11 +157,7 @@ module RuboCop
157
157
  return if ternary_op?(node)
158
158
  return if modifier_if?(node)
159
159
 
160
- case node.loc.keyword.source
161
- when 'if', 'elsif' then _condition, body, else_clause = *node
162
- when 'unless' then _condition, else_clause, body = *node
163
- else _condition, body = *node
164
- end
160
+ _condition, body, else_clause = if_node_parts(node)
165
161
 
166
162
  check_if(node, body, else_clause, base.loc) if body
167
163
  end
@@ -121,15 +121,17 @@ module RuboCop
121
121
  _, method_name, *args = *send_node
122
122
  if operator?(method_name) && args.any?
123
123
  args.first.loc.expression
124
- elsif send_node.loc.dot &&
125
- send_node.loc.selector &&
126
- send_node.loc.dot.line == send_node.loc.selector.line
127
- send_node.loc.dot.join(send_node.loc.selector)
128
- elsif send_node.loc.selector
129
- send_node.loc.selector
130
- elsif send_node.loc.dot.line == send_node.loc.begin.line
131
- # lambda.(args)
132
- send_node.loc.dot.join(send_node.loc.begin)
124
+ else
125
+ dot = send_node.loc.dot
126
+ selector = send_node.loc.selector
127
+ if dot && selector && dot.line == selector.line
128
+ dot.join(selector)
129
+ elsif selector
130
+ selector
131
+ elsif dot.line == send_node.loc.begin.line
132
+ # lambda.(args)
133
+ dot.join(send_node.loc.begin)
134
+ end
133
135
  end
134
136
  end
135
137
 
@@ -173,7 +175,7 @@ module RuboCop
173
175
  end
174
176
 
175
177
  def not_for_this_cop?(node)
176
- node.each_ancestor.find do |ancestor|
178
+ node.each_ancestor.any? do |ancestor|
177
179
  grouped_expression?(ancestor) ||
178
180
  inside_arg_list_parentheses?(node, ancestor)
179
181
  end
@@ -50,7 +50,7 @@ module RuboCop
50
50
 
51
51
  def on_for(node)
52
52
  _, _, body = *node
53
- return unless ends_with_condition?(body)
53
+ return unless body && ends_with_condition?(body)
54
54
 
55
55
  add_offense(node, :keyword, MSG)
56
56
  end
@@ -0,0 +1,196 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for simple usages of parallel assignment.
7
+ # This will only complain when the number of variables
8
+ # being assigned matched the number of assigning variables.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # a, b, c = 1, 2, 3
13
+ # a, b, c = [1, 2, 3]
14
+ #
15
+ # # good
16
+ # one, two = *foo
17
+ # a, b = foo()
18
+ # a, b = b, a
19
+ #
20
+ # a = 1
21
+ # b = 2
22
+ # c = 3
23
+ class ParallelAssignment < Cop
24
+ include AutocorrectAlignment
25
+ include IfNode
26
+
27
+ MSG = 'Do not use parallel assignment.'
28
+
29
+ def on_masgn(node)
30
+ left, right = *node
31
+ left_elements = *left
32
+ right_elements = [*right].compact # edge case for one constant
33
+
34
+ # only complain when the number of variables matches
35
+ return if left_elements.size != right_elements.size
36
+
37
+ # account for edge cases using one variable with a comma
38
+ return if left_elements.size == 1
39
+
40
+ # allow mass assignment as the return of a method call
41
+ return if right.block_type? || right.send_type?
42
+
43
+ # allow mass assignment when using splat
44
+ return if (left_elements + right_elements).any?(&:splat_type?)
45
+
46
+ # a, b = b, a
47
+ return if swapping_variables?(left_elements, right_elements)
48
+
49
+ add_offense(node, :expression)
50
+ end
51
+
52
+ def autocorrect(node)
53
+ lambda do |corrector|
54
+ assignment_corrector =
55
+ if modifier_statement?(node.parent)
56
+ ModifierCorrector.new(node, configured_indentation_width)
57
+ elsif rescue_modifier?(node.parent)
58
+ RescueCorrector.new(node, configured_indentation_width)
59
+ else
60
+ GenericCorrector.new(node, configured_indentation_width)
61
+ end
62
+
63
+ corrector.replace(assignment_corrector.correction_range,
64
+ assignment_corrector.correction)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def swapping_variables?(left_elements, right_elements)
71
+ left_elements.any? do |le|
72
+ right_elements.any? do |re|
73
+ re.loc.expression.is?(le.loc.expression.source)
74
+ end
75
+ end
76
+ end
77
+
78
+ def modifier_statement?(node)
79
+ node &&
80
+ ((node.if_type? && modifier_if?(node)) ||
81
+ ((node.while_type? || node.until_type?) && modifier_while?(node)))
82
+ end
83
+
84
+ def modifier_while?(node)
85
+ node.loc.respond_to?(:keyword) &&
86
+ %w(while until).include?(node.loc.keyword.source) &&
87
+ node.loc.respond_to?(:end) && node.loc.end.nil?
88
+ end
89
+
90
+ def rescue_modifier?(node)
91
+ node &&
92
+ node.rescue_type? &&
93
+ (node.parent.nil? || !node.parent.kwbegin_type?)
94
+ end
95
+
96
+ # An internal class for correcting parallel assignment
97
+ class GenericCorrector
98
+ attr_reader :correction, :correction_range
99
+
100
+ def initialize(node, indentation_width)
101
+ @node = node
102
+ @indentation_width = indentation_width
103
+ end
104
+
105
+ def correction
106
+ "#{assignment.join("\n#{indent}")}"
107
+ end
108
+
109
+ def correction_range
110
+ @node.loc.expression
111
+ end
112
+
113
+ protected
114
+
115
+ def space_offset
116
+ @node.loc.expression.column
117
+ end
118
+
119
+ def indent
120
+ ' ' * space_offset
121
+ end
122
+
123
+ attr_reader :indentation_width
124
+
125
+ def assignment
126
+ left, right = *@node
127
+ l_vars = extract_sources(left)
128
+ r_vars = extract_sources(right)
129
+ groups = l_vars.zip(r_vars)
130
+ groups.map { |pair| pair.join(' = ') }
131
+ end
132
+
133
+ private
134
+
135
+ def extract_sources(node)
136
+ node.children.map { |child| child.loc.expression.source }
137
+ end
138
+ end
139
+
140
+ # An internal class for correcting parallel assignment
141
+ # protected by rescue
142
+ class RescueCorrector < GenericCorrector
143
+ def correction
144
+ _node, rescue_clause = *@node.parent
145
+ _, _, rescue_result = *rescue_clause
146
+
147
+ "begin\n" <<
148
+ indent << assignment.join("\n#{indent}") <<
149
+ "\nrescue\n" <<
150
+ indent << rescue_result.loc.expression.source <<
151
+ "\nend"
152
+ end
153
+
154
+ def correction_range
155
+ @node.parent.loc.expression
156
+ end
157
+
158
+ protected
159
+
160
+ def space_offset
161
+ offset = super
162
+ offset + indentation_width
163
+ end
164
+ end
165
+
166
+ # An internal class for correcting parallel assignment
167
+ # guarded by if, unless, while, or until
168
+ class ModifierCorrector < GenericCorrector
169
+ def correction
170
+ parent = @node.parent
171
+
172
+ modifier_range =
173
+ Parser::Source::Range.new(parent.loc.expression.source_buffer,
174
+ parent.loc.keyword.begin_pos,
175
+ parent.loc.expression.end_pos)
176
+
177
+ "#{modifier_range.source}\n" <<
178
+ indent << assignment.join("\n#{indent}") <<
179
+ "\nend"
180
+ end
181
+
182
+ def correction_range
183
+ @node.parent.loc.expression
184
+ end
185
+
186
+ protected
187
+
188
+ def space_offset
189
+ offset = super
190
+ offset + indentation_width
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end