rubocop 0.40.0 → 0.41.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -1014
  3. data/config/default.yml +61 -5
  4. data/config/disabled.yml +6 -0
  5. data/config/enabled.yml +63 -4
  6. data/lib/rubocop.rb +17 -1
  7. data/lib/rubocop/ast_node.rb +56 -42
  8. data/lib/rubocop/ast_node/traversal.rb +3 -3
  9. data/lib/rubocop/cli.rb +14 -9
  10. data/lib/rubocop/comment_config.rb +85 -32
  11. data/lib/rubocop/config.rb +29 -8
  12. data/lib/rubocop/config_loader.rb +1 -1
  13. data/lib/rubocop/cop/cop.rb +1 -1
  14. data/lib/rubocop/cop/corrector.rb +13 -0
  15. data/lib/rubocop/cop/lint/block_alignment.rb +25 -11
  16. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +5 -2
  17. data/lib/rubocop/cop/lint/inherit_exception.rb +69 -0
  18. data/lib/rubocop/cop/lint/percent_string_array.rb +60 -0
  19. data/lib/rubocop/cop/lint/percent_symbol_array.rb +57 -0
  20. data/lib/rubocop/cop/lint/shadowed_exception.rb +95 -0
  21. data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -13
  22. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +25 -19
  23. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +16 -8
  24. data/lib/rubocop/cop/mixin/if_node.rb +1 -2
  25. data/lib/rubocop/cop/mixin/integer_node.rb +13 -0
  26. data/lib/rubocop/cop/mixin/match_range.rb +26 -0
  27. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +16 -7
  28. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +18 -1
  29. data/lib/rubocop/cop/mixin/negative_conditional.rb +6 -4
  30. data/lib/rubocop/cop/mixin/percent_literal.rb +10 -0
  31. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +24 -6
  32. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +20 -7
  33. data/lib/rubocop/cop/mixin/string_literals_help.rb +2 -2
  34. data/lib/rubocop/cop/mixin/trailing_comma.rb +34 -20
  35. data/lib/rubocop/cop/performance/flat_map.rb +23 -10
  36. data/lib/rubocop/cop/performance/push_splat.rb +47 -0
  37. data/lib/rubocop/cop/performance/redundant_block_call.rb +24 -1
  38. data/lib/rubocop/cop/performance/redundant_merge.rb +3 -5
  39. data/lib/rubocop/cop/performance/sample.rb +15 -11
  40. data/lib/rubocop/cop/rails/exit.rb +62 -0
  41. data/lib/rubocop/cop/rails/output_safety.rb +45 -0
  42. data/lib/rubocop/cop/rails/pluralization_grammar.rb +12 -4
  43. data/lib/rubocop/cop/rails/request_referer.rb +40 -0
  44. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +63 -28
  45. data/lib/rubocop/cop/rails/validation.rb +37 -23
  46. data/lib/rubocop/cop/style/alias.rb +10 -6
  47. data/lib/rubocop/cop/style/bare_percent_literals.rb +18 -7
  48. data/lib/rubocop/cop/style/block_delimiters.rb +15 -22
  49. data/lib/rubocop/cop/style/closing_parenthesis_indentation.rb +19 -8
  50. data/lib/rubocop/cop/style/comment_indentation.rb +13 -5
  51. data/lib/rubocop/cop/style/conditional_assignment.rb +111 -59
  52. data/lib/rubocop/cop/style/documentation.rb +7 -1
  53. data/lib/rubocop/cop/style/each_for_simple_loop.rb +43 -0
  54. data/lib/rubocop/cop/style/each_with_object.rb +25 -14
  55. data/lib/rubocop/cop/style/empty_else.rb +6 -10
  56. data/lib/rubocop/cop/style/extra_spacing.rb +20 -3
  57. data/lib/rubocop/cop/style/file_name.rb +16 -4
  58. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  59. data/lib/rubocop/cop/style/hash_syntax.rb +9 -2
  60. data/lib/rubocop/cop/style/if_unless_modifier.rb +20 -13
  61. data/lib/rubocop/cop/style/implicit_runtime_error.rb +32 -0
  62. data/lib/rubocop/cop/style/infinite_loop.rb +42 -5
  63. data/lib/rubocop/cop/style/lambda.rb +22 -0
  64. data/lib/rubocop/cop/style/method_def_parentheses.rb +12 -4
  65. data/lib/rubocop/cop/style/module_function.rb +28 -6
  66. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +49 -12
  67. data/lib/rubocop/cop/style/mutable_constant.rb +8 -1
  68. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  69. data/lib/rubocop/cop/style/next.rb +43 -31
  70. data/lib/rubocop/cop/style/not.rb +33 -13
  71. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +92 -0
  72. data/lib/rubocop/cop/style/numeric_literals.rb +1 -4
  73. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -8
  74. data/lib/rubocop/cop/style/{deprecated_hash_methods.rb → preferred_hash_methods.rb} +8 -8
  75. data/lib/rubocop/cop/style/redundant_parentheses.rb +29 -19
  76. data/lib/rubocop/cop/style/redundant_self.rb +13 -6
  77. data/lib/rubocop/cop/style/space_after_not.rb +7 -5
  78. data/lib/rubocop/cop/style/space_around_keyword.rb +6 -0
  79. data/lib/rubocop/cop/style/space_around_operators.rb +5 -1
  80. data/lib/rubocop/cop/style/space_before_first_arg.rb +21 -9
  81. data/lib/rubocop/cop/style/space_inside_array_percent_literal.rb +53 -0
  82. data/lib/rubocop/cop/style/space_inside_block_braces.rb +2 -2
  83. data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +26 -6
  84. data/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb +64 -0
  85. data/lib/rubocop/cop/style/string_literals.rb +37 -8
  86. data/lib/rubocop/cop/style/symbol_array.rb +21 -12
  87. data/lib/rubocop/cop/style/symbol_proc.rb +26 -19
  88. data/lib/rubocop/cop/style/word_array.rb +1 -5
  89. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -6
  90. data/lib/rubocop/cop/team.rb +40 -27
  91. data/lib/rubocop/cop/util.rb +13 -42
  92. data/lib/rubocop/formatter/disabled_config_formatter.rb +37 -14
  93. data/lib/rubocop/formatter/html_formatter.rb +3 -7
  94. data/lib/rubocop/result_cache.rb +18 -4
  95. data/{spec/support → lib/rubocop/rspec}/cop_helper.rb +3 -0
  96. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +33 -0
  97. data/lib/rubocop/rspec/shared_contexts.rb +75 -0
  98. data/lib/rubocop/rspec/shared_examples.rb +101 -0
  99. data/lib/rubocop/rspec/support.rb +9 -0
  100. data/lib/rubocop/runner.rb +2 -2
  101. data/lib/rubocop/string_interpreter.rb +58 -0
  102. data/lib/rubocop/version.rb +1 -1
  103. metadata +27 -7
@@ -11,6 +11,7 @@ module RuboCop
11
11
  def check_brace_layout(node)
12
12
  return unless node.loc.begin # Ignore implicit literals.
13
13
  return if children(node).empty? # Ignore empty literals.
14
+ return if node.single_line? # Ignore single-line literals.
14
15
 
15
16
  # If the last node is or contains a conflicting HEREDOC, we don't want
16
17
  # to adjust the brace layout because this will result in invalid code.
@@ -32,7 +33,8 @@ module RuboCop
32
33
  lambda do |corrector|
33
34
  corrector.remove(range_with_surrounding_space(node.loc.end,
34
35
  :left))
35
- corrector.insert_after(children(node).last.source_range,
36
+
37
+ corrector.insert_after(last_element_range_with_trailing_comma(node),
36
38
  node.loc.end.source)
37
39
  end
38
40
  end
@@ -40,6 +42,21 @@ module RuboCop
40
42
 
41
43
  private
42
44
 
45
+ def last_element_range_with_trailing_comma(node)
46
+ trailing_comma_range = last_element_trailing_comma_range(node)
47
+ if trailing_comma_range
48
+ children(node).last.source_range.join(trailing_comma_range)
49
+ else
50
+ children(node).last.source_range
51
+ end
52
+ end
53
+
54
+ def last_element_trailing_comma_range(node)
55
+ range = range_with_surrounding_space(children(node).last.source_range,
56
+ :right).end.resize(1)
57
+ range.source == ',' ? range : nil
58
+ end
59
+
43
60
  def handle_new_line(node)
44
61
  return unless closing_brace_on_same_line?(node)
45
62
 
@@ -6,17 +6,19 @@ module RuboCop
6
6
  # Some common code shared between FavorUnlessOverNegatedIf and
7
7
  # FavorUntilOverNegatedWhile.
8
8
  module NegativeConditional
9
+ def self.included(mod)
10
+ mod.def_node_matcher :single_negative?, '(send !(send _ :!) :!)'
11
+ end
12
+
9
13
  def check_negative_conditional(node)
10
14
  condition, _body, _rest = *node
11
15
 
12
16
  # Look at last expression of contents if there are parentheses
13
17
  # around condition.
14
18
  condition = condition.children.last while condition.type == :begin
15
- return unless condition.type == :send
16
19
 
17
- _object, method = *condition
18
- return unless method == :! && !(node.loc.respond_to?(:else) &&
19
- node.loc.else)
20
+ return unless single_negative?(condition) &&
21
+ !(node.loc.respond_to?(:else) && node.loc.else)
20
22
 
21
23
  add_offense(node, :expression)
22
24
  end
@@ -22,6 +22,16 @@ module RuboCop
22
22
  def type(node)
23
23
  node.loc.begin.source[0..-2]
24
24
  end
25
+
26
+ # A range containing only the contents of the percent literal (e.g. in
27
+ # %i{1 2 3} this will be the range covering '1 2 3' only)
28
+ def contents_range(node)
29
+ Parser::Source::Range.new(
30
+ node.loc.expression.source_buffer,
31
+ node.loc.begin.end_pos,
32
+ node.loc.end.begin_pos
33
+ )
34
+ end
25
35
  end
26
36
  end
27
37
  end
@@ -9,16 +9,34 @@ module RuboCop
9
9
  MSG = 'Space missing after %s.'.freeze
10
10
 
11
11
  def investigate(processed_source)
12
- processed_source.tokens.each_cons(2) do |t1, t2|
13
- next unless kind(t1) && t1.pos.line == t2.pos.line &&
14
- t2.pos.column == t1.pos.column + offset &&
15
- ![:tRPAREN, :tRBRACK, :tPIPE].include?(t2.type) &&
16
- !(t2.type == :tRCURLY && space_forbidden_before_rcurly?)
12
+ each_missing_space(processed_source.tokens) do |token|
13
+ add_offense(token, token.pos, format(MSG, kind(token)))
14
+ end
15
+ end
16
+
17
+ def each_missing_space(tokens)
18
+ tokens.each_cons(2) do |t1, t2|
19
+ next unless kind(t1)
20
+ next unless space_missing?(t1, t2)
21
+ next unless space_required_before?(t2)
17
22
 
18
- add_offense(t1, t1.pos, format(MSG, kind(t1)))
23
+ yield t1
19
24
  end
20
25
  end
21
26
 
27
+ def space_missing?(t1, t2)
28
+ t1.pos.line == t2.pos.line && t2.pos.column == t1.pos.column + offset
29
+ end
30
+
31
+ def space_required_before?(token)
32
+ !(allowed_type?(token) ||
33
+ (token.type == :tRCURLY && space_forbidden_before_rcurly?))
34
+ end
35
+
36
+ def allowed_type?(token)
37
+ [:tRPAREN, :tRBRACK, :tPIPE].include?(token.type)
38
+ end
39
+
22
40
  def space_forbidden_before_rcurly?
23
41
  style = space_style_before_rcurly
24
42
  style == 'no_space'
@@ -9,21 +9,34 @@ module RuboCop
9
9
  MSG = 'Space found before %s.'.freeze
10
10
 
11
11
  def investigate(processed_source)
12
- processed_source.tokens.each_cons(2) do |t1, t2|
13
- next unless kind(t2) && t1.pos.line == t2.pos.line &&
14
- t2.pos.begin_pos > t1.pos.end_pos &&
15
- !(t1.type == :tLCURLY && space_required_after_lcurly?)
12
+ each_missing_space(processed_source.tokens) do |token, pos_before|
13
+ add_offense(pos_before, pos_before, format(MSG, kind(token)))
14
+ end
15
+ end
16
+
17
+ def each_missing_space(tokens)
18
+ tokens.each_cons(2) do |t1, t2|
19
+ next unless kind(t2)
20
+ next unless space_missing?(t1, t2)
21
+ next if space_required_after?(t1)
22
+
16
23
  buffer = processed_source.buffer
17
24
  pos_before_punctuation = Parser::Source::Range.new(buffer,
18
25
  t1.pos.end_pos,
19
26
  t2.pos.begin_pos)
20
27
 
21
- add_offense(pos_before_punctuation,
22
- pos_before_punctuation,
23
- format(MSG, kind(t2)))
28
+ yield t2, pos_before_punctuation
24
29
  end
25
30
  end
26
31
 
32
+ def space_missing?(t1, t2)
33
+ t1.pos.line == t2.pos.line && t2.pos.begin_pos > t1.pos.end_pos
34
+ end
35
+
36
+ def space_required_after?(token)
37
+ token.type == :tLCURLY && space_required_after_lcurly?
38
+ end
39
+
27
40
  def space_required_after_lcurly?
28
41
  cfg = config.for_cop('Style/SpaceInsideBlockBraces')
29
42
  style = cfg['EnforcedStyle'] || 'space'
@@ -11,9 +11,9 @@ module RuboCop
11
11
  src = node.source
12
12
  return false if src.start_with?('%', '?')
13
13
  if style == :single_quotes
14
- src !~ /'/ && !double_quotes_acceptable?(node.str_content)
14
+ !double_quotes_required?(src)
15
15
  else
16
- src !~ /" | \\ | \#/x
16
+ src !~ /" | \\ | \#(@|\{)/x
17
17
  end
18
18
  end
19
19
 
@@ -23,26 +23,39 @@ module RuboCop
23
23
  comma_offset = after_last_item.source =~ /,/
24
24
 
25
25
  if comma_offset && !inside_comment?(after_last_item, comma_offset)
26
- unless should_have_comma?(style, node)
27
- extra_info = case style
28
- when :comma
29
- ', unless each item is on its own line'
30
- when :consistent_comma
31
- ', unless items are split onto multiple lines'
32
- else
33
- ''
34
- end
35
- avoid_comma(kind, after_last_item.begin_pos + comma_offset, sb,
36
- extra_info)
37
- end
26
+ check_comma(node, kind, after_last_item.begin_pos + comma_offset)
38
27
  elsif should_have_comma?(style, node)
39
28
  put_comma(node, items, kind, sb)
40
29
  end
41
30
  end
42
31
 
32
+ def check_comma(node, kind, comma_pos)
33
+ return if should_have_comma?(style, node)
34
+
35
+ avoid_comma(kind, comma_pos, node.source_range.source_buffer,
36
+ extra_avoid_comma_info)
37
+ end
38
+
39
+ def extra_avoid_comma_info
40
+ case style
41
+ when :comma
42
+ ', unless each item is on its own line'
43
+ when :consistent_comma
44
+ ', unless items are split onto multiple lines'
45
+ else
46
+ ''
47
+ end
48
+ end
49
+
43
50
  def should_have_comma?(style, node)
44
- [:comma, :consistent_comma].include?(style) &&
51
+ case style
52
+ when :comma
53
+ multiline?(node) && no_elements_on_same_line?(node)
54
+ when :consistent_comma
45
55
  multiline?(node)
56
+ else
57
+ false
58
+ end
46
59
  end
47
60
 
48
61
  def inside_comment?(range, comma_offset)
@@ -73,13 +86,8 @@ module RuboCop
73
86
 
74
87
  items = elements(node).map(&:source_range)
75
88
  return false if items.empty?
76
-
77
- # If brackets are on different lines and there is one item at least,
78
- # then comma is needed anytime for consistent_comma.
79
- return true if style == :consistent_comma
80
-
81
- items << node.loc.end
82
- items.each_cons(2).all? { |a, b| !on_same_line?(a, b) }
89
+ items << node.loc.begin << node.loc.end
90
+ (items.map(&:first_line) + items.map(&:last_line)).uniq.count > 1
83
91
  end
84
92
 
85
93
  def elements(node)
@@ -99,6 +107,12 @@ module RuboCop
99
107
  end
100
108
  end
101
109
 
110
+ def no_elements_on_same_line?(node)
111
+ items = elements(node).map(&:source_range)
112
+ items << node.loc.end
113
+ items.each_cons(2).none? { |a, b| on_same_line?(a, b) }
114
+ end
115
+
102
116
  def on_same_line?(a, b)
103
117
  a.last_line == b.line
104
118
  end
@@ -25,23 +25,17 @@ module RuboCop
25
25
  def on_send(node)
26
26
  left, second_method, flatten_param = *node
27
27
  return unless FLATTEN.include?(second_method)
28
+
28
29
  flatten_level, = *flatten_param
29
30
  expression, = *left
30
31
  _array, first_method = *expression
31
32
  return unless first_method == :map || first_method == :collect
32
33
 
33
- message = MSG
34
34
  if cop_config['EnabledForFlattenWithoutParams'] && flatten_level.nil?
35
- message = MSG + FLATTEN_MULTIPLE_LEVELS
36
- elsif flatten_level != 1
37
- return
35
+ offense_for_levels(node, expression, first_method, second_method)
36
+ elsif flatten_level == 1
37
+ offense_for_method(node, expression, first_method, second_method)
38
38
  end
39
-
40
- range = Parser::Source::Range.new(node.source_range.source_buffer,
41
- expression.loc.selector.begin_pos,
42
- node.loc.selector.end_pos)
43
-
44
- add_offense(node, range, format(message, first_method, second_method))
45
39
  end
46
40
 
47
41
  def autocorrect(node)
@@ -60,6 +54,25 @@ module RuboCop
60
54
  corrector.replace(array.loc.selector, 'flat_map')
61
55
  end
62
56
  end
57
+
58
+ private
59
+
60
+ def offense_for_levels(node, expression, first_method, second_method)
61
+ message = MSG + FLATTEN_MULTIPLE_LEVELS
62
+ offense(node, expression, first_method, second_method, message)
63
+ end
64
+
65
+ def offense_for_method(node, expression, first_method, second_method)
66
+ offense(node, expression, first_method, second_method, MSG)
67
+ end
68
+
69
+ def offense(node, expression, first_method, second_method, message)
70
+ range = Parser::Source::Range.new(node.source_range.source_buffer,
71
+ expression.loc.selector.begin_pos,
72
+ node.loc.selector.end_pos)
73
+
74
+ add_offense(node, range, format(message, first_method, second_method))
75
+ end
63
76
  end
64
77
  end
65
78
  end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Performance
7
+ # This cop is used to identify usages of
8
+ #
9
+ # @example
10
+ # # bad
11
+ # [].push(*a)
12
+ #
13
+ # # good
14
+ # [].concat(a)
15
+ class PushSplat < Cop
16
+ include Parentheses
17
+
18
+ MSG = 'Use `concat` instead of `push(*)`.'.freeze
19
+
20
+ def_node_matcher :push_splat, <<-END
21
+ (send _ :push (splat ...))
22
+ END
23
+
24
+ def on_send(node)
25
+ push_splat(node) do
26
+ add_offense(node, :expression, MSG)
27
+ end
28
+ end
29
+
30
+ def autocorrect(node)
31
+ _receiver, _method, splat = *node
32
+ body, = *splat
33
+ lambda do |corrector|
34
+ corrector.replace(node.location.selector, 'concat')
35
+
36
+ source = if parens_required?(splat)
37
+ "(#{body.source})"
38
+ else
39
+ body.source
40
+ end
41
+ corrector.replace(splat.loc.expression, source)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -39,15 +39,38 @@ module RuboCop
39
39
  (send (lvar %1) :call ...)
40
40
  END
41
41
 
42
+ def_node_search :blockarg_assigned?, <<-END
43
+ (lvasgn %1 ...)
44
+ END
45
+
42
46
  def on_def(node)
43
47
  blockarg_def(node) do |argname, body|
44
48
  next unless body
45
- blockarg_calls(body, argname) do |blockcall|
49
+
50
+ calls_to_report(argname, body).each do |blockcall|
46
51
  add_offense(blockcall, :expression, format(MSG, argname))
47
52
  end
48
53
  end
49
54
  end
50
55
 
56
+ private
57
+
58
+ def calls_to_report(argname, body)
59
+ return [] if blockarg_assigned?(body, argname)
60
+
61
+ calls = to_enum(:blockarg_calls, body, argname)
62
+
63
+ return [] if calls.any? { |call| args_include_block_pass?(call) }
64
+
65
+ calls
66
+ end
67
+
68
+ def args_include_block_pass?(blockcall)
69
+ _receiver, _call, *args = *blockcall
70
+
71
+ args.any?(&:block_pass_type?)
72
+ end
73
+
51
74
  # offenses are registered on the `block.call` nodes
52
75
  def autocorrect(node)
53
76
  _receiver, _method, *args = *node
@@ -16,7 +16,9 @@ module RuboCop
16
16
  MSG = 'Use `%s` instead of `%s`.'.freeze
17
17
 
18
18
  def_node_matcher :redundant_merge, '(send $_ :merge! (hash $...))'
19
- def_node_matcher :modifier_flow_control, '[{if while until} #modifier?]'
19
+ def_node_matcher :modifier_flow_control, <<-END
20
+ [{if while until} modifier_form?]
21
+ END
20
22
  def_node_matcher :each_with_object_node, <<-END
21
23
  (block (send _ :each_with_object _) (args _ $_) ...)
22
24
  END
@@ -110,10 +112,6 @@ module RuboCop
110
112
  @config.for_cop('IndentationWidth')['Width'] || 2
111
113
  end
112
114
 
113
- def modifier?(node)
114
- node.loc.respond_to?(:end) && node.loc.end.nil?
115
- end
116
-
117
115
  def max_key_value_pairs
118
116
  cop_config['MaxKeyValuePairs'].to_i
119
117
  end
@@ -111,20 +111,24 @@ module RuboCop
111
111
  def sample_size
112
112
  _, _, *args = *method_node
113
113
  case args.size
114
- when 1
115
- arg = args.first
116
- case arg.type
117
- when :erange, :irange then range_size(arg)
118
- when :int then arg.to_a.first.zero? ? nil : :unknown
119
- else :unknown
120
- end
121
- when 2
122
- first, second = *args
123
- return :unknown unless first.int_type? && first.to_a.first.zero?
124
- second.int_type? ? second.to_a.first : :unknown
114
+ when 1 then sample_size_for_one_arg(args.first)
115
+ when 2 then sample_size_for_two_args(*args)
125
116
  end
126
117
  end
127
118
 
119
+ def sample_size_for_one_arg(arg)
120
+ case arg.type
121
+ when :erange, :irange then range_size(arg)
122
+ when :int then arg.to_a.first.zero? ? nil : :unknown
123
+ else :unknown
124
+ end
125
+ end
126
+
127
+ def sample_size_for_two_args(first, second)
128
+ return :unknown unless first.int_type? && first.to_a.first.zero?
129
+ second.int_type? ? second.to_a.first : :unknown
130
+ end
131
+
128
132
  def shuffle_arg
129
133
  _, _, arg = *shuffle_node
130
134
  arg.source if arg