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
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop detects instances of rubocop:disable comments that can be
|
7
|
+
# removed without causing any offenses to be reported. It's implemented
|
8
|
+
# as a cop in that it inherits from the Cop base class and calls
|
9
|
+
# add_offense. The unusual part of its implementation is that it doesn't
|
10
|
+
# have any on_* methods or an investigate method. This means that it
|
11
|
+
# doesn't take part in the investigation phase when the other cops do
|
12
|
+
# their work. Instead, it waits until it's called in a later stage of the
|
13
|
+
# execution. The reason it can't be implemented as a normal cop is that
|
14
|
+
# it depends on the results of all other cops to do its work.
|
15
|
+
class UnneededDisable < Cop
|
16
|
+
COP_NAME = 'Lint/UnneededDisable'
|
17
|
+
|
18
|
+
def check(file, offenses, cop_disabled_line_ranges, comments)
|
19
|
+
unneeded_cops = {}
|
20
|
+
|
21
|
+
cop_disabled_line_ranges[file].each do |cop, line_ranges|
|
22
|
+
cop_offenses = offenses.select { |o| o.cop_name == cop }
|
23
|
+
line_ranges.each do |line_range|
|
24
|
+
comment =
|
25
|
+
comments[file].find { |c| c.loc.line == line_range.begin }
|
26
|
+
unneeded_cop = find_unneeded(comment, offenses, cop, cop_offenses,
|
27
|
+
line_range)
|
28
|
+
if unneeded_cop
|
29
|
+
unneeded_cops[comment.loc.expression] ||= Set.new
|
30
|
+
unneeded_cops[comment.loc.expression].add(unneeded_cop)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
unneeded_cops.each do |range, cops|
|
36
|
+
add_offense(range, range,
|
37
|
+
"Unnecessary disabling of #{cops.sort.join(', ')}.")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def find_unneeded(comment, offenses, cop, cop_offenses, line_range)
|
44
|
+
if comment.loc.expression.source =~ /rubocop:disable\s+all\b/
|
45
|
+
'all cops' if offenses.none? { |o| line_range.include?(o.line) }
|
46
|
+
elsif cop_offenses.none? { |o| line_range.include?(o.line) }
|
47
|
+
cop
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -22,6 +22,16 @@ module RuboCop
|
|
22
22
|
def if_else?(node)
|
23
23
|
node.loc.respond_to?(:else) && node.loc.else
|
24
24
|
end
|
25
|
+
|
26
|
+
def if_node_parts(node)
|
27
|
+
case node.loc.keyword.source
|
28
|
+
when 'if', 'elsif' then condition, body, else_clause = *node
|
29
|
+
when 'unless' then condition, else_clause, body = *node
|
30
|
+
else condition, body = *node
|
31
|
+
end
|
32
|
+
|
33
|
+
[condition, body, else_clause]
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
@@ -11,12 +11,19 @@ module RuboCop
|
|
11
11
|
processed_source.tokens.each_cons(2) do |t1, t2|
|
12
12
|
next unless kind(t1) && t1.pos.line == t2.pos.line &&
|
13
13
|
t2.pos.column == t1.pos.column + offset &&
|
14
|
-
![:tRPAREN, :tRBRACK, :tPIPE].include?(t2.type)
|
14
|
+
![:tRPAREN, :tRBRACK, :tPIPE].include?(t2.type) &&
|
15
|
+
!(t2.type == :tRCURLY && space_forbidden_before_rcurly?)
|
15
16
|
|
16
17
|
add_offense(t1, t1.pos, format(MSG, kind(t1)))
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
21
|
+
def space_forbidden_before_rcurly?
|
22
|
+
cfg = config.for_cop('Style/SpaceInsideBlockBraces')
|
23
|
+
style = cfg['Enabled'] ? cfg['EnforcedStyle'] : 'space'
|
24
|
+
style == 'no_space'
|
25
|
+
end
|
26
|
+
|
20
27
|
# The normal offset, i.e., the distance from the punctuation
|
21
28
|
# token where a space should be, is 1.
|
22
29
|
def offset
|
@@ -10,7 +10,8 @@ module RuboCop
|
|
10
10
|
def investigate(processed_source)
|
11
11
|
processed_source.tokens.each_cons(2) do |t1, t2|
|
12
12
|
next unless kind(t2) && t1.pos.line == t2.pos.line &&
|
13
|
-
t2.pos.begin_pos > t1.pos.end_pos
|
13
|
+
t2.pos.begin_pos > t1.pos.end_pos &&
|
14
|
+
!(t1.type == :tLCURLY && space_required_after_lcurly?)
|
14
15
|
buffer = processed_source.buffer
|
15
16
|
pos_before_punctuation = Parser::Source::Range.new(buffer,
|
16
17
|
t1.pos.end_pos,
|
@@ -22,6 +23,12 @@ module RuboCop
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
26
|
+
def space_required_after_lcurly?
|
27
|
+
cfg = config.for_cop('Style/SpaceInsideBlockBraces')
|
28
|
+
style = cfg['Enabled'] ? cfg['EnforcedStyle'] : 'space'
|
29
|
+
style == 'space'
|
30
|
+
end
|
31
|
+
|
25
32
|
def autocorrect(pos_before_punctuation)
|
26
33
|
->(corrector) { corrector.remove(pos_before_punctuation) }
|
27
34
|
end
|
@@ -7,13 +7,10 @@ module RuboCop
|
|
7
7
|
include IfNode
|
8
8
|
|
9
9
|
def fit_within_line_as_modifier_form?(node)
|
10
|
-
|
11
|
-
when 'if' then cond, body, _else = *node
|
12
|
-
when 'unless' then cond, _else, body = *node
|
13
|
-
else cond, body = *node
|
14
|
-
end
|
10
|
+
cond, body, _else = if_node_parts(node)
|
15
11
|
|
16
12
|
return false if length(node) > 3
|
13
|
+
return false if body && body.begin_type? # multiple statements
|
17
14
|
|
18
15
|
body_length = body_length(body)
|
19
16
|
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
def inside_interpolation?(node)
|
32
32
|
# A :begin node inside a :dstr node is an interpolation.
|
33
33
|
begin_found = false
|
34
|
-
node.each_ancestor.
|
34
|
+
node.each_ancestor.any? do |a|
|
35
35
|
begin_found = true if a.type == :begin
|
36
36
|
begin_found && a.type == :dstr
|
37
37
|
end
|
data/lib/rubocop/cop/offense.rb
CHANGED
@@ -56,16 +56,29 @@ module RuboCop
|
|
56
56
|
#
|
57
57
|
# @return [Boolean]
|
58
58
|
# whether this offense is automatically corrected.
|
59
|
-
|
59
|
+
def corrected
|
60
|
+
@status == :unsupported ? nil : @status == :corrected
|
61
|
+
end
|
60
62
|
alias_method :corrected?, :corrected
|
61
63
|
|
64
|
+
# @api public
|
65
|
+
#
|
66
|
+
# @!attribute [r] disabled?
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
# whether this offense was locally disabled where it occurred
|
70
|
+
def disabled?
|
71
|
+
@status == :disabled
|
72
|
+
end
|
73
|
+
|
62
74
|
# @api private
|
63
|
-
def initialize(severity, location, message, cop_name,
|
75
|
+
def initialize(severity, location, message, cop_name,
|
76
|
+
status = :uncorrected)
|
64
77
|
@severity = RuboCop::Cop::Severity.new(severity)
|
65
78
|
@location = location
|
66
79
|
@message = message.freeze
|
67
80
|
@cop_name = cop_name.freeze
|
68
|
-
@
|
81
|
+
@status = status
|
69
82
|
freeze
|
70
83
|
end
|
71
84
|
|
@@ -14,12 +14,15 @@ module RuboCop
|
|
14
14
|
# [1, 2, 3].reject { |e| e > 2 }.length
|
15
15
|
# [1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? }
|
16
16
|
# [1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? }
|
17
|
+
# array.select(&:value).count
|
17
18
|
#
|
18
19
|
# # good
|
19
20
|
# [1, 2, 3].count { |e| e > 2 }
|
20
21
|
# [1, 2, 3].count { |e| e < 2 }
|
21
22
|
# [1, 2, 3].count { |e| e > 2 && e.odd? }
|
22
23
|
# [1, 2, 3].count { |e| e < 2 && e.even? }
|
24
|
+
# Model.select('field AS field_one').count
|
25
|
+
# Model.select(:value).count
|
23
26
|
class Count < Cop
|
24
27
|
MSG = 'Use `count` instead of `%s...%s`.'
|
25
28
|
|
@@ -27,56 +30,56 @@ module RuboCop
|
|
27
30
|
COUNTERS = [:count, :length, :size]
|
28
31
|
|
29
32
|
def on_send(node)
|
30
|
-
|
31
|
-
|
32
|
-
return unless
|
33
|
-
|
34
|
-
begin_pos = if SELECTORS.include?(first_method)
|
35
|
-
return if second_method.is_a?(Symbol)
|
36
|
-
expression.loc.selector.begin_pos
|
37
|
-
else
|
38
|
-
return unless SELECTORS.include?(second_method)
|
39
|
-
expression.parent.loc.selector.begin_pos
|
40
|
-
end
|
41
|
-
|
33
|
+
selector, selector_loc, params, counter = parse(node)
|
34
|
+
return unless COUNTERS.include?(counter)
|
35
|
+
return unless SELECTORS.include?(selector)
|
36
|
+
return if params && !params.block_pass_type?
|
42
37
|
return if node.parent && node.parent.block_type?
|
43
38
|
|
44
39
|
range = Parser::Source::Range.new(node.loc.expression.source_buffer,
|
45
|
-
begin_pos,
|
40
|
+
selector_loc.begin_pos,
|
46
41
|
node.loc.expression.end_pos)
|
47
42
|
|
48
|
-
add_offense(node, range,
|
49
|
-
format(MSG, first_method || second_method, third_method))
|
43
|
+
add_offense(node, range, format(MSG, selector, counter))
|
50
44
|
end
|
51
45
|
|
52
46
|
def autocorrect(node)
|
53
|
-
|
47
|
+
selector, selector_loc = parse(node)
|
54
48
|
|
55
|
-
return if
|
49
|
+
return if selector == :reject
|
56
50
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
expression.parent.loc.selector
|
61
|
-
end
|
51
|
+
range = Parser::Source::Range.new(node.loc.expression.source_buffer,
|
52
|
+
node.loc.dot.begin_pos,
|
53
|
+
node.loc.expression.end_pos)
|
62
54
|
|
63
55
|
lambda do |corrector|
|
64
|
-
range = Parser::Source::Range.new(node.loc.expression.source_buffer,
|
65
|
-
node.loc.dot.begin_pos,
|
66
|
-
node.loc.expression.end_pos)
|
67
56
|
corrector.remove(range)
|
68
|
-
corrector.replace(
|
57
|
+
corrector.replace(selector_loc, 'count')
|
69
58
|
end
|
70
59
|
end
|
71
60
|
|
72
61
|
private
|
73
62
|
|
74
63
|
def parse(node)
|
75
|
-
left,
|
76
|
-
expression,
|
77
|
-
|
64
|
+
left, counter = *node
|
65
|
+
expression, selector, params = *left
|
66
|
+
|
67
|
+
selector_loc =
|
68
|
+
if selector.is_a?(Symbol)
|
69
|
+
if expression && expression.parent.loc.respond_to?(:selector)
|
70
|
+
expression.parent.loc.selector
|
71
|
+
end
|
72
|
+
else
|
73
|
+
_enumerable, selector, params = *expression
|
74
|
+
|
75
|
+
expression.loc.selector if contains_selector?(expression)
|
76
|
+
end
|
77
|
+
|
78
|
+
[selector, selector_loc, params, counter]
|
79
|
+
end
|
78
80
|
|
79
|
-
|
81
|
+
def contains_selector?(node)
|
82
|
+
node.respond_to?(:loc) && node.loc.respond_to?(:selector)
|
80
83
|
end
|
81
84
|
end
|
82
85
|
end
|
@@ -1,89 +1,133 @@
|
|
1
|
-
# encoding:
|
1
|
+
# encoding: UTF-8
|
2
2
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
# This cop is used to identify usages of `shuffle.first`
|
7
|
-
# change them to use `sample` instead.
|
6
|
+
# This cop is used to identify usages of `shuffle.first`, `shuffle.last`
|
7
|
+
# and `shuffle[]` and change them to use `sample` instead.
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# [1, 2, 3].shuffle.first
|
12
|
+
# [1, 2, 3].shuffle.first(2)
|
12
13
|
# [1, 2, 3].shuffle.last
|
13
|
-
# [1, 2, 3].shuffle[
|
14
|
-
# [1, 2, 3].shuffle[0,
|
15
|
-
# [1, 2, 3].shuffle
|
14
|
+
# [1, 2, 3].shuffle[2]
|
15
|
+
# [1, 2, 3].shuffle[0, 2] # sample(2) will do the same
|
16
|
+
# [1, 2, 3].shuffle[0..2] # sample(3) will do the same
|
17
|
+
# [1, 2, 3].shuffle(random: Random.new).first
|
16
18
|
#
|
17
19
|
# # good
|
18
20
|
# [1, 2, 3].shuffle
|
19
21
|
# [1, 2, 3].sample
|
20
22
|
# [1, 2, 3].sample(3)
|
21
|
-
# [1, 2, 3].sample(
|
23
|
+
# [1, 2, 3].shuffle[1, 3] # sample(3) might return a longer Array
|
24
|
+
# [1, 2, 3].shuffle[1..3] # sample(3) might return a longer Array
|
25
|
+
# [1, 2, 3].shuffle[foo, bar]
|
26
|
+
# [1, 2, 3].shuffle(random: Random.new)
|
22
27
|
class Sample < Cop
|
23
|
-
MSG = 'Use `
|
24
|
-
RANGE_TYPES = [:irange, :erange]
|
25
|
-
VALID_ARRAY_SELECTORS = [:first, :last, :[], nil]
|
28
|
+
MSG = 'Use `%<correct>s` instead of `%<incorrect>s`.'
|
26
29
|
|
27
30
|
def on_send(node)
|
28
|
-
|
29
|
-
return unless
|
30
|
-
|
31
|
-
return unless VALID_ARRAY_SELECTORS.include?(second_method)
|
32
|
-
return if second_method.nil? && params.nil?
|
33
|
-
|
34
|
-
add_offense(node, range_of_shuffle(node), message(node, params))
|
31
|
+
analyzer = ShuffleAnalyzer.new(node)
|
32
|
+
return unless analyzer.offensive?
|
33
|
+
add_offense(node, analyzer.source_range, analyzer.message)
|
35
34
|
end
|
36
35
|
|
37
36
|
def autocorrect(node)
|
38
|
-
|
39
|
-
_receiver, _method, params, selector = *node.parent if params.nil?
|
40
|
-
|
41
|
-
return if params && RANGE_TYPES.include?(params.type)
|
42
|
-
|
43
|
-
range = if params && (params.hash_type? || params.lvar_type?)
|
44
|
-
range_of_shuffle(node)
|
45
|
-
else
|
46
|
-
Parser::Source::Range.new(node.loc.expression.source_buffer,
|
47
|
-
node.loc.selector.begin_pos,
|
48
|
-
node.parent.loc.selector.end_pos)
|
49
|
-
end
|
50
|
-
|
51
|
-
lambda do |corrector|
|
52
|
-
corrector.replace(range, 'sample')
|
53
|
-
return if selector.nil?
|
54
|
-
corrector.insert_after(range, "(#{selector.loc.expression.source})")
|
55
|
-
end
|
37
|
+
ShuffleAnalyzer.new(node).autocorrect
|
56
38
|
end
|
57
39
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
40
|
+
# An internal class for representing a shuffle + method node analyzer.
|
41
|
+
class ShuffleAnalyzer
|
42
|
+
def initialize(shuffle_node)
|
43
|
+
@shuffle_node = shuffle_node
|
44
|
+
@method_node = shuffle_node.parent
|
45
|
+
end
|
46
|
+
|
47
|
+
def autocorrect
|
48
|
+
->(corrector) { corrector.replace(source_range, correct) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def message
|
52
|
+
format(MSG, correct: correct, incorrect: source_range.source)
|
53
|
+
end
|
54
|
+
|
55
|
+
def offensive?
|
56
|
+
shuffle_node.to_a[1] == :shuffle && corrigible?
|
57
|
+
end
|
58
|
+
|
59
|
+
def source_range
|
60
|
+
Parser::Source::Range.new(shuffle_node.loc.expression.source_buffer,
|
61
|
+
shuffle_node.loc.selector.begin_pos,
|
62
|
+
method_node.loc.expression.end_pos)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
attr_reader :method_node, :shuffle_node
|
68
|
+
|
69
|
+
def correct
|
70
|
+
args = [sample_arg, shuffle_arg].compact.join(', ')
|
71
|
+
args.empty? ? 'sample' : "sample(#{args})"
|
72
|
+
end
|
73
|
+
|
74
|
+
def corrigible?
|
75
|
+
case method
|
76
|
+
when :first, :last then true
|
77
|
+
when :[] then sample_size != :unknown
|
78
|
+
else false
|
69
79
|
end
|
70
|
-
else
|
71
|
-
format(MSG, shuffle_params(node))
|
72
80
|
end
|
73
|
-
end
|
74
81
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
node.loc.selector.end_pos)
|
79
|
-
end
|
82
|
+
def method
|
83
|
+
method_node.to_a[1]
|
84
|
+
end
|
80
85
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
86
|
+
def method_arg
|
87
|
+
_, _, arg = *method_node
|
88
|
+
arg.loc.expression.source if arg
|
89
|
+
end
|
90
|
+
|
91
|
+
# FIXME: use Range#size once Ruby 1.9 support is dropped
|
92
|
+
def range_size(range_node)
|
93
|
+
vals = *range_node
|
94
|
+
return :unknown unless vals.all?(&:int_type?)
|
95
|
+
low, high = *vals.map(&:to_a).map(&:first)
|
96
|
+
return :unknown unless low.zero? && high >= 0
|
97
|
+
case range_node.type
|
98
|
+
when :erange then high - low
|
99
|
+
when :irange then high - low + 1
|
100
|
+
end
|
101
|
+
end
|
85
102
|
|
86
|
-
|
103
|
+
def sample_arg
|
104
|
+
case method
|
105
|
+
when :first, :last then method_arg
|
106
|
+
when :[] then sample_size
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def sample_size
|
111
|
+
_, _, *args = *method_node
|
112
|
+
case args.size
|
113
|
+
when 1
|
114
|
+
arg = args.first
|
115
|
+
case arg.type
|
116
|
+
when :erange, :irange then range_size(arg)
|
117
|
+
when :int then arg.to_a.first.zero? ? nil : :unknown
|
118
|
+
else :unknown
|
119
|
+
end
|
120
|
+
when 2
|
121
|
+
first, second = *args
|
122
|
+
return :unknown unless first.int_type? && first.to_a.first.zero?
|
123
|
+
second.int_type? ? second.to_a.first : :unknown
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def shuffle_arg
|
128
|
+
_, _, arg = *shuffle_node
|
129
|
+
arg.loc.expression.source if arg
|
130
|
+
end
|
87
131
|
end
|
88
132
|
end
|
89
133
|
end
|