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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +13 -0
- data/config/disabled.yml +4 -0
- data/config/enabled.yml +34 -8
- data/lib/rubocop.rb +4 -1
- data/lib/rubocop/cop/cop.rb +18 -12
- data/lib/rubocop/cop/lint/debugger.rb +7 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +10 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +31 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +9 -0
- data/lib/rubocop/cop/lint/unneeded_disable.rb +53 -0
- data/lib/rubocop/cop/metrics/class_length.rb +1 -1
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/if_node.rb +10 -0
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -5
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/offense.rb +16 -3
- data/lib/rubocop/cop/performance/count.rb +33 -30
- data/lib/rubocop/cop/performance/sample.rb +103 -59
- data/lib/rubocop/cop/performance/size.rb +2 -1
- data/lib/rubocop/cop/rails/time_zone.rb +14 -6
- data/lib/rubocop/cop/style/align_hash.rb +7 -3
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +39 -11
- data/lib/rubocop/cop/style/case_indentation.rb +18 -4
- data/lib/rubocop/cop/style/comment_annotation.rb +22 -7
- data/lib/rubocop/cop/style/documentation.rb +11 -5
- data/lib/rubocop/cop/style/empty_else.rb +25 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -5
- data/lib/rubocop/cop/style/indentation_width.rb +1 -5
- data/lib/rubocop/cop/style/multiline_operation_indentation.rb +12 -10
- data/lib/rubocop/cop/style/next.rb +1 -1
- data/lib/rubocop/cop/style/parallel_assignment.rb +196 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +1 -4
- data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +41 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +11 -10
- data/lib/rubocop/cop/style/trailing_blank_lines.rb +8 -0
- data/lib/rubocop/cop/style/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/formatter/formatter_set.rb +24 -1
- data/lib/rubocop/options.rb +4 -0
- data/lib/rubocop/processed_source.rb +4 -1
- data/lib/rubocop/runner.rb +12 -7
- data/lib/rubocop/target_finder.rb +3 -3
- data/lib/rubocop/version.rb +1 -1
- data/relnotes/v0.32.0.md +139 -0
- data/rubocop.gemspec +2 -2
- metadata +12 -8
- 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
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
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,
|
32
|
+
add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
|
32
33
|
elsif style == :no_braces && braces?(arg)
|
33
|
-
add_offense(arg,
|
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,
|
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,
|
49
|
+
add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
50
|
-
|
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
|
-
|
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
|
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,
|
12
|
-
'
|
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
|
-
|
25
|
+
source_buffer = comment.loc.expression.source_buffer
|
26
|
+
range = Parser::Source::Range.new(source_buffer,
|
23
27
|
start,
|
24
28
|
start + length)
|
25
|
-
|
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(
|
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
|
-
|
34
|
-
|
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
|
-
|
68
|
+
preceding_comments = preceding_comments(node, ast_with_comments)
|
69
|
+
return false if preceding_comments.empty?
|
69
70
|
|
70
|
-
|
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?(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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.
|
178
|
+
node.each_ancestor.any? do |ancestor|
|
177
179
|
grouped_expression?(ancestor) ||
|
178
180
|
inside_arg_list_parentheses?(node, ancestor)
|
179
181
|
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
|