rubocop 0.67.2 → 0.68.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +86 -233
- data/exe/rubocop +0 -12
- data/lib/rubocop.rb +13 -30
- data/lib/rubocop/ast/builder.rb +4 -0
- data/lib/rubocop/ast/node/alias_node.rb +24 -0
- data/lib/rubocop/ast/node/class_node.rb +31 -0
- data/lib/rubocop/ast/node/module_node.rb +24 -0
- data/lib/rubocop/ast/node/range_node.rb +7 -0
- data/lib/rubocop/ast/node/resbody_node.rb +12 -0
- data/lib/rubocop/ast/node/self_class_node.rb +24 -0
- data/lib/rubocop/cli.rb +40 -4
- data/lib/rubocop/config.rb +9 -7
- data/lib/rubocop/config_loader.rb +48 -7
- data/lib/rubocop/config_loader_resolver.rb +5 -4
- data/lib/rubocop/cop/commissioner.rb +24 -0
- data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +18 -6
- data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +12 -14
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +9 -20
- data/lib/rubocop/cop/layout/align_arguments.rb +93 -0
- data/lib/rubocop/cop/layout/align_parameters.rb +57 -33
- data/lib/rubocop/cop/layout/class_structure.rb +5 -5
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -8
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +3 -6
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +1 -2
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +1 -0
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +292 -0
- data/lib/rubocop/cop/layout/{first_parameter_indentation.rb → indent_first_argument.rb} +11 -12
- data/lib/rubocop/cop/layout/{indent_array.rb → indent_first_array_element.rb} +2 -2
- data/lib/rubocop/cop/layout/{indent_hash.rb → indent_first_hash_element.rb} +2 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +96 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -16
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -4
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -16
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +6 -8
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -8
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +157 -0
- data/lib/rubocop/cop/lint/inherit_exception.rb +3 -4
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +18 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -5
- data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +25 -5
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -6
- data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -2
- data/lib/rubocop/cop/message_annotator.rb +1 -0
- data/lib/rubocop/cop/metrics/line_length.rb +139 -28
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +3 -4
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +190 -0
- data/lib/rubocop/cop/mixin/{array_hash_indentation.rb → multiline_element_indentation.rb} +3 -2
- data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -7
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +33 -4
- data/lib/rubocop/cop/rails/active_record_override.rb +23 -8
- data/lib/rubocop/cop/rails/delegate.rb +5 -8
- data/lib/rubocop/cop/rails/environment_comparison.rb +5 -3
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/skips_model_validations.rb +6 -7
- data/lib/rubocop/cop/rails/time_zone.rb +3 -10
- data/lib/rubocop/cop/rails/validation.rb +3 -0
- data/lib/rubocop/cop/registry.rb +3 -3
- data/lib/rubocop/cop/style/alias.rb +13 -7
- data/lib/rubocop/cop/style/block_delimiters.rb +20 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -21
- data/lib/rubocop/cop/style/class_methods.rb +16 -24
- data/lib/rubocop/cop/style/conditional_assignment.rb +20 -49
- data/lib/rubocop/cop/style/documentation.rb +3 -7
- data/lib/rubocop/cop/style/format_string.rb +18 -21
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +4 -0
- data/lib/rubocop/cop/style/lambda.rb +12 -8
- data/lib/rubocop/cop/style/mixin_grouping.rb +8 -10
- data/lib/rubocop/cop/style/module_function.rb +2 -3
- data/lib/rubocop/cop/style/next.rb +10 -14
- data/lib/rubocop/cop/style/one_line_conditional.rb +5 -3
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -4
- data/lib/rubocop/cop/style/random_with_offset.rb +44 -47
- data/lib/rubocop/cop/style/redundant_return.rb +6 -14
- data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +20 -40
- data/lib/rubocop/cop/style/unless_else.rb +1 -2
- data/lib/rubocop/cop/style/yoda_condition.rb +8 -7
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/file_finder.rb +5 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -0
- data/lib/rubocop/node_pattern.rb +304 -170
- data/lib/rubocop/options.rb +4 -1
- data/lib/rubocop/rspec/shared_contexts.rb +3 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- metadata +26 -50
- data/lib/rubocop/cop/performance/caller.rb +0 -69
- data/lib/rubocop/cop/performance/case_when_splat.rb +0 -177
- data/lib/rubocop/cop/performance/casecmp.rb +0 -108
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +0 -78
- data/lib/rubocop/cop/performance/compare_with_block.rb +0 -122
- data/lib/rubocop/cop/performance/count.rb +0 -102
- data/lib/rubocop/cop/performance/detect.rb +0 -110
- data/lib/rubocop/cop/performance/double_start_end_with.rb +0 -94
- data/lib/rubocop/cop/performance/end_with.rb +0 -56
- data/lib/rubocop/cop/performance/fixed_size.rb +0 -97
- data/lib/rubocop/cop/performance/flat_map.rb +0 -78
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +0 -99
- data/lib/rubocop/cop/performance/open_struct.rb +0 -46
- data/lib/rubocop/cop/performance/range_include.rb +0 -50
- data/lib/rubocop/cop/performance/redundant_block_call.rb +0 -93
- data/lib/rubocop/cop/performance/redundant_match.rb +0 -56
- data/lib/rubocop/cop/performance/redundant_merge.rb +0 -183
- data/lib/rubocop/cop/performance/regexp_match.rb +0 -265
- data/lib/rubocop/cop/performance/reverse_each.rb +0 -42
- data/lib/rubocop/cop/performance/size.rb +0 -77
- data/lib/rubocop/cop/performance/start_with.rb +0 -59
- data/lib/rubocop/cop/performance/string_replacement.rb +0 -173
- data/lib/rubocop/cop/performance/times_map.rb +0 -71
- data/lib/rubocop/cop/performance/unfreeze_string.rb +0 -50
- data/lib/rubocop/cop/performance/uri_default_parser.rb +0 -47
@@ -90,6 +90,8 @@ module RuboCop
|
|
90
90
|
|
91
91
|
# rubocop:disable Metrics/CyclomaticComplexity
|
92
92
|
def check_branch(node)
|
93
|
+
return unless node
|
94
|
+
|
93
95
|
case node.type
|
94
96
|
when :return then check_return_node(node)
|
95
97
|
when :case then check_case_node(node)
|
@@ -111,25 +113,15 @@ module RuboCop
|
|
111
113
|
end
|
112
114
|
|
113
115
|
def check_case_node(node)
|
114
|
-
|
115
|
-
|
116
|
-
check_branch(else_node) if else_node
|
117
|
-
end
|
118
|
-
|
119
|
-
def check_when_node(node)
|
120
|
-
return unless node
|
121
|
-
|
122
|
-
_cond, body = *node
|
123
|
-
check_branch(body) if body
|
116
|
+
node.when_branches.each { |when_node| check_branch(when_node.body) }
|
117
|
+
check_branch(node.else_branch)
|
124
118
|
end
|
125
119
|
|
126
120
|
def check_if_node(node)
|
127
121
|
return if node.modifier_form? || node.ternary?
|
128
122
|
|
129
|
-
|
130
|
-
|
131
|
-
check_branch(if_node) if if_node
|
132
|
-
check_branch(else_node) if else_node
|
123
|
+
check_branch(node.if_branch)
|
124
|
+
check_branch(node.else_branch)
|
133
125
|
end
|
134
126
|
|
135
127
|
def check_rescue_node(node)
|
@@ -65,6 +65,8 @@ module RuboCop
|
|
65
65
|
|
66
66
|
MSG = 'Use safe navigation (`&.`) instead of checking if an object ' \
|
67
67
|
'exists before calling the method.'.freeze
|
68
|
+
LOGIC_JUMP_KEYWORDS = %i[break fail next raise
|
69
|
+
return throw yield].freeze
|
68
70
|
|
69
71
|
minimum_target_ruby_version 2.3
|
70
72
|
|
@@ -149,6 +151,7 @@ module RuboCop
|
|
149
151
|
|
150
152
|
checked_variable, matching_receiver, method =
|
151
153
|
extract_common_parts(receiver, variable)
|
154
|
+
matching_receiver = nil if LOGIC_JUMP_KEYWORDS.include?(receiver.type)
|
152
155
|
[checked_variable, matching_receiver, receiver, method]
|
153
156
|
end
|
154
157
|
|
@@ -24,10 +24,9 @@ module RuboCop
|
|
24
24
|
'Use a block to customize the struct.'.freeze
|
25
25
|
|
26
26
|
def on_class(node)
|
27
|
-
|
28
|
-
return unless struct_constructor?(superclass)
|
27
|
+
return unless struct_constructor?(node.parent_class)
|
29
28
|
|
30
|
-
add_offense(node, location:
|
29
|
+
add_offense(node, location: node.parent_class.source_range)
|
31
30
|
end
|
32
31
|
|
33
32
|
def_node_matcher :struct_constructor?, <<-PATTERN
|
@@ -32,46 +32,38 @@ module RuboCop
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def on_block(node)
|
35
|
-
symbol_proc?(node) do |
|
36
|
-
block_method_name = resolve_block_method_name(send_or_super)
|
37
|
-
|
35
|
+
symbol_proc?(node) do |dispatch_node, arguments_node, method_name|
|
38
36
|
# TODO: Rails-specific handling that we should probably make
|
39
37
|
# configurable - https://github.com/rubocop-hq/rubocop/issues/1485
|
40
38
|
# we should ignore lambdas & procs
|
41
|
-
return if proc_node?(
|
42
|
-
return if %i[lambda proc].include?(
|
43
|
-
return if ignored_method?(
|
44
|
-
return if
|
45
|
-
block_args.source.include?(',')
|
39
|
+
return if proc_node?(dispatch_node)
|
40
|
+
return if %i[lambda proc].include?(dispatch_node.method_name)
|
41
|
+
return if ignored_method?(dispatch_node.method_name)
|
42
|
+
return if destructuring_block_argument?(arguments_node)
|
46
43
|
|
47
|
-
|
44
|
+
register_offense(node, method_name, dispatch_node.method_name)
|
48
45
|
end
|
49
46
|
end
|
50
47
|
|
48
|
+
def destructuring_block_argument?(argument_node)
|
49
|
+
argument_node.one? && argument_node.source.include?(',')
|
50
|
+
end
|
51
|
+
|
51
52
|
def autocorrect(node)
|
52
53
|
lambda do |corrector|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
args = *block_send_or_super
|
54
|
+
if node.send_node.arguments?
|
55
|
+
autocorrect_with_args(corrector, node,
|
56
|
+
node.send_node.arguments,
|
57
|
+
node.body.method_name)
|
58
58
|
else
|
59
|
-
|
59
|
+
autocorrect_without_args(corrector, node)
|
60
60
|
end
|
61
|
-
autocorrect_method(corrector, node, args, method_name)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
65
64
|
private
|
66
65
|
|
67
|
-
def
|
68
|
-
return :super if super?(block_send_or_super)
|
69
|
-
|
70
|
-
_receiver, method_name, _args = *block_send_or_super
|
71
|
-
method_name
|
72
|
-
end
|
73
|
-
|
74
|
-
def offense(node, method_name, block_method_name)
|
66
|
+
def register_offense(node, method_name, block_method_name)
|
75
67
|
block_start = node.loc.begin.begin_pos
|
76
68
|
block_end = node.loc.end.end_pos
|
77
69
|
range = range_between(block_start, block_end)
|
@@ -83,16 +75,9 @@ module RuboCop
|
|
83
75
|
block_method: block_method_name))
|
84
76
|
end
|
85
77
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
autocorrect_with_args(corrector, node, args, method_name)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def autocorrect_no_args(corrector, node, method_name)
|
95
|
-
corrector.replace(block_range_with_space(node), "(&:#{method_name})")
|
78
|
+
def autocorrect_without_args(corrector, node)
|
79
|
+
corrector.replace(block_range_with_space(node),
|
80
|
+
"(&:#{node.body.method_name})")
|
96
81
|
end
|
97
82
|
|
98
83
|
def autocorrect_with_args(corrector, node, args, method_name)
|
@@ -111,8 +96,7 @@ module RuboCop
|
|
111
96
|
end
|
112
97
|
|
113
98
|
def begin_pos_for_replacement(node)
|
114
|
-
|
115
|
-
expr = block_send_or_super.source_range
|
99
|
+
expr = node.send_node.source_range
|
116
100
|
|
117
101
|
if (paren_pos = (expr.source =~ /\(\s*\)$/))
|
118
102
|
expr.begin_pos + paren_pos
|
@@ -120,10 +104,6 @@ module RuboCop
|
|
120
104
|
node.loc.begin.begin_pos
|
121
105
|
end
|
122
106
|
end
|
123
|
-
|
124
|
-
def super?(node)
|
125
|
-
SUPER_TYPES.include?(node.type)
|
126
|
-
end
|
127
107
|
end
|
128
108
|
end
|
129
109
|
end
|
@@ -32,8 +32,7 @@ module RuboCop
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def autocorrect(node)
|
35
|
-
|
36
|
-
body_range = range_between_condition_and_else(node, condition)
|
35
|
+
body_range = range_between_condition_and_else(node, node.condition)
|
37
36
|
else_range = range_between_else_and_end(node)
|
38
37
|
|
39
38
|
lambda do |corrector|
|
@@ -98,7 +98,8 @@ module RuboCop
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def valid_yoda?(node)
|
101
|
-
lhs
|
101
|
+
lhs = node.receiver
|
102
|
+
rhs = node.first_argument
|
102
103
|
|
103
104
|
return true if lhs.literal? && rhs.literal? ||
|
104
105
|
!lhs.literal? && !rhs.literal?
|
@@ -111,8 +112,10 @@ module RuboCop
|
|
111
112
|
end
|
112
113
|
|
113
114
|
def corrected_code(node)
|
114
|
-
lhs
|
115
|
-
|
115
|
+
lhs = node.receiver
|
116
|
+
rhs = node.first_argument
|
117
|
+
|
118
|
+
"#{rhs.source} #{reverse_comparison(node.method_name)} #{lhs.source}"
|
116
119
|
end
|
117
120
|
|
118
121
|
def actual_code_range(node)
|
@@ -126,13 +129,11 @@ module RuboCop
|
|
126
129
|
end
|
127
130
|
|
128
131
|
def non_equality_operator?(node)
|
129
|
-
|
130
|
-
!EQUALITY_OPERATORS.include?(operator)
|
132
|
+
!EQUALITY_OPERATORS.include?(node.method_name)
|
131
133
|
end
|
132
134
|
|
133
135
|
def noncommutative_operator?(node)
|
134
|
-
|
135
|
-
NONCOMMUTATIVE_OPERATORS.include?(operator)
|
136
|
+
NONCOMMUTATIVE_OPERATORS.include?(node.method_name)
|
136
137
|
end
|
137
138
|
end
|
138
139
|
end
|
data/lib/rubocop/cop/util.rb
CHANGED
@@ -45,11 +45,9 @@ module RuboCop
|
|
45
45
|
while node
|
46
46
|
case node.type
|
47
47
|
when :send
|
48
|
-
|
49
|
-
node = receiver
|
48
|
+
node = node.receiver
|
50
49
|
when :block
|
51
|
-
|
52
|
-
node = method
|
50
|
+
node = node.send_node
|
53
51
|
else
|
54
52
|
break
|
55
53
|
end
|
data/lib/rubocop/file_finder.rb
CHANGED
@@ -13,16 +13,16 @@ module RuboCop
|
|
13
13
|
@root_level == path.to_s
|
14
14
|
end
|
15
15
|
|
16
|
-
def find_file_upwards(filename, start_dir
|
17
|
-
traverse_files_upwards(filename, start_dir
|
16
|
+
def find_file_upwards(filename, start_dir)
|
17
|
+
traverse_files_upwards(filename, start_dir) do |file|
|
18
18
|
# minimize iteration for performance
|
19
19
|
return file if file
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def find_files_upwards(filename, start_dir
|
23
|
+
def find_files_upwards(filename, start_dir)
|
24
24
|
files = []
|
25
|
-
traverse_files_upwards(filename, start_dir
|
25
|
+
traverse_files_upwards(filename, start_dir) do |file|
|
26
26
|
files << file
|
27
27
|
end
|
28
28
|
files
|
@@ -30,18 +30,13 @@ module RuboCop
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def traverse_files_upwards(filename, start_dir
|
33
|
+
def traverse_files_upwards(filename, start_dir)
|
34
34
|
Pathname.new(start_dir).expand_path.ascend do |dir|
|
35
35
|
break if FileFinder.root_level?(dir)
|
36
36
|
|
37
37
|
file = dir + filename
|
38
38
|
yield(file.to_s) if file.exist?
|
39
39
|
end
|
40
|
-
|
41
|
-
return unless use_home && ENV.key?('HOME')
|
42
|
-
|
43
|
-
file = File.join(Dir.home, filename)
|
44
|
-
yield(file) if File.exist?(file)
|
45
40
|
end
|
46
41
|
end
|
47
42
|
end
|
data/lib/rubocop/node_pattern.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'delegate'
|
4
|
+
require 'erb'
|
5
|
+
|
3
6
|
# rubocop:disable Metrics/ClassLength, Metrics/CyclomaticComplexity
|
4
7
|
module RuboCop
|
5
8
|
# This class performs a pattern-matching operation on an AST node.
|
@@ -38,6 +41,12 @@ module RuboCop
|
|
38
41
|
# '$(send const ...)' # arbitrary matching can be performed on a capture
|
39
42
|
# '(send _recv _msg)' # wildcards can be named (for readability)
|
40
43
|
# '(send ... :new)' # you can match against the last children
|
44
|
+
# '(array <str sym>)' # you can match children in any order. This
|
45
|
+
# # would match `['x', :y]` as well as `[:y, 'x']
|
46
|
+
# '(_ <str sym ...>)' # will match if arguments have at least a `str` and
|
47
|
+
# # a `sym` node, but can have more.
|
48
|
+
# '(array <$str $_>)' # captures are in the order of the pattern,
|
49
|
+
# # irrespective of the actual order of the children
|
41
50
|
# '(send $...)' # capture all the children as an array
|
42
51
|
# '(send $... int)' # capture all children but the last as an array
|
43
52
|
# '(send _x :+ _x)' # unification is performed on named wildcards
|
@@ -97,7 +106,7 @@ module RuboCop
|
|
97
106
|
class Compiler
|
98
107
|
SYMBOL = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}.freeze
|
99
108
|
IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_-]*/.freeze
|
100
|
-
META =
|
109
|
+
META = Regexp.union(%w"( ) { } [ ] $< < > $... $ ! ^ ...").freeze
|
101
110
|
NUMBER = /-?\d+(?:\.\d+)?/.freeze
|
102
111
|
STRING = /".+?"/.freeze
|
103
112
|
METHOD_NAME = /\#?#{IDENTIFIER}[\!\?]?\(?/.freeze
|
@@ -121,10 +130,36 @@ module RuboCop
|
|
121
130
|
REST = '...'.freeze
|
122
131
|
CAPTURED_REST = '$...'.freeze
|
123
132
|
|
124
|
-
attr_reader :match_code
|
133
|
+
attr_reader :match_code, :tokens, :captures
|
125
134
|
|
126
135
|
SEQ_HEAD_INDEX = -1
|
127
136
|
|
137
|
+
# Placeholders while compiling, see with_..._context methods
|
138
|
+
CUR_PLACEHOLDER = '@@@cur'.freeze
|
139
|
+
CUR_NODE = "#{CUR_PLACEHOLDER} node@@@".freeze
|
140
|
+
CUR_ELEMENT = "#{CUR_PLACEHOLDER} element@@@".freeze
|
141
|
+
SEQ_HEAD_GUARD = '@@@seq guard head@@@'.freeze
|
142
|
+
|
143
|
+
line = __LINE__
|
144
|
+
ANY_ORDER_TEMPLATE = ERB.new <<-RUBY.strip_indent.gsub("-%>\n", '%>')
|
145
|
+
<% if capture_rest %>(<%= capture_rest %> = []) && <% end -%>
|
146
|
+
<% if capture_all %>(<%= capture_all %> = <% end -%>
|
147
|
+
<%= CUR_NODE %>.children[<%= range %>]<% if capture_all %>)<% end -%>
|
148
|
+
.each_with_object({}) { |<%= child %>, <%= matched %>|
|
149
|
+
case
|
150
|
+
<% patterns.each_with_index do |pattern, i| -%>
|
151
|
+
when !<%= matched %>[<%= i %>] && <%=
|
152
|
+
with_context(pattern, child, use_temp_node: false)
|
153
|
+
%> then <%= matched %>[<%= i %>] = true
|
154
|
+
<% end -%>
|
155
|
+
<% if !rest %> else break({})
|
156
|
+
<% elsif capture_rest %> else <%= capture_rest %> << <%= child %>
|
157
|
+
<% end -%>
|
158
|
+
end
|
159
|
+
}.size == <%= patterns.size -%>
|
160
|
+
RUBY
|
161
|
+
ANY_ORDER_TEMPLATE.location = [__FILE__, line + 1]
|
162
|
+
|
128
163
|
def initialize(str, node_var = 'node0')
|
129
164
|
@string = str
|
130
165
|
@root = node_var
|
@@ -133,40 +168,45 @@ module RuboCop
|
|
133
168
|
@captures = 0 # number of captures seen
|
134
169
|
@unify = {} # named wildcard -> temp variable number
|
135
170
|
@params = 0 # highest % (param) number seen
|
136
|
-
|
137
171
|
run(node_var)
|
138
172
|
end
|
139
173
|
|
140
174
|
def run(node_var)
|
141
|
-
tokens = Compiler.tokens(@string)
|
175
|
+
@tokens = Compiler.tokens(@string)
|
142
176
|
|
143
|
-
@match_code = compile_expr
|
177
|
+
@match_code = with_context(compile_expr, node_var, use_temp_node: false)
|
178
|
+
@match_code.prepend("(captures = Array.new(#{@captures})) && ") \
|
179
|
+
if @captures > 0
|
144
180
|
|
145
181
|
fail_due_to('unbalanced pattern') unless tokens.empty?
|
146
182
|
end
|
147
183
|
|
148
184
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
149
|
-
def compile_expr(
|
185
|
+
def compile_expr(token = tokens.shift)
|
150
186
|
# read a single pattern-matching expression from the token stream,
|
151
187
|
# return Ruby code which performs the corresponding matching operation
|
152
|
-
# on 'cur_node' (which is Ruby code which evaluates to an AST node)
|
153
188
|
#
|
154
189
|
# the 'pattern-matching' expression may be a composite which
|
155
|
-
# contains an arbitrary number of sub-expressions
|
156
|
-
|
190
|
+
# contains an arbitrary number of sub-expressions, but that composite
|
191
|
+
# must all have precedence higher or equal to that of `&&`
|
192
|
+
#
|
193
|
+
# Expressions may use placeholders like:
|
194
|
+
# CUR_NODE: Ruby code that evaluates to an AST node
|
195
|
+
# CUR_ELEMENT: Either the node or the type if in first element of
|
196
|
+
# a sequence (aka seq_head, e.g. "(seq_head first_node_arg ...")
|
157
197
|
case token
|
158
|
-
when '(' then compile_seq
|
159
|
-
when '{' then compile_union
|
160
|
-
when '[' then compile_intersect
|
161
|
-
when '!' then compile_negation
|
162
|
-
when '$' then compile_capture
|
163
|
-
when '^' then compile_ascend
|
164
|
-
when WILDCARD then compile_wildcard(
|
165
|
-
when FUNCALL then compile_funcall(
|
166
|
-
when LITERAL then compile_literal(
|
167
|
-
when PREDICATE then compile_predicate(
|
168
|
-
when NODE then compile_nodetype(
|
169
|
-
when PARAM then compile_param(
|
198
|
+
when '(' then compile_seq
|
199
|
+
when '{' then compile_union
|
200
|
+
when '[' then compile_intersect
|
201
|
+
when '!' then compile_negation
|
202
|
+
when '$' then compile_capture
|
203
|
+
when '^' then compile_ascend
|
204
|
+
when WILDCARD then compile_wildcard(token[1..-1])
|
205
|
+
when FUNCALL then compile_funcall(token)
|
206
|
+
when LITERAL then compile_literal(token)
|
207
|
+
when PREDICATE then compile_predicate(token)
|
208
|
+
when NODE then compile_nodetype(token)
|
209
|
+
when PARAM then compile_param(token[1..-1])
|
170
210
|
when CLOSING then fail_due_to("#{token} in invalid position")
|
171
211
|
when nil then fail_due_to('pattern ended prematurely')
|
172
212
|
else fail_due_to("invalid token #{token.inspect}")
|
@@ -174,211 +214,260 @@ module RuboCop
|
|
174
214
|
end
|
175
215
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
176
216
|
|
177
|
-
def
|
178
|
-
|
179
|
-
fail_due_to('parentheses at sequence head') if seq_head
|
217
|
+
def tokens_until(stop, what)
|
218
|
+
return to_enum __method__, stop, what unless block_given?
|
180
219
|
|
181
|
-
#
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
with_temp_node(cur_node) do |init, temp_node|
|
186
|
-
terms = compile_seq_terms(tokens, temp_node)
|
187
|
-
terms.unshift(compile_guard_clause(temp_node))
|
220
|
+
fail_due_to("empty #{what}") if tokens.first == stop && what
|
221
|
+
yield until tokens.first == stop
|
222
|
+
tokens.shift
|
223
|
+
end
|
188
224
|
|
189
|
-
|
190
|
-
|
225
|
+
def compile_seq
|
226
|
+
terms = tokens_until(')', 'sequence').map { variadic_seq_term }
|
227
|
+
Sequence.new(self, *terms).compile
|
191
228
|
end
|
192
229
|
|
193
|
-
def compile_guard_clause
|
194
|
-
"#{
|
230
|
+
def compile_guard_clause
|
231
|
+
"#{CUR_NODE}.is_a?(RuboCop::AST::Node)"
|
195
232
|
end
|
196
233
|
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
ret << "(#{cur_node}.children.size == #{ret.size - 1})"
|
234
|
+
def variadic_seq_term
|
235
|
+
token = tokens.shift
|
236
|
+
case token
|
237
|
+
when CAPTURED_REST then compile_captured_ellipsis
|
238
|
+
when REST then compile_ellipsis
|
239
|
+
when '$<' then compile_any_order(next_capture)
|
240
|
+
when '<' then compile_any_order
|
241
|
+
else [1, compile_expr(token)]
|
242
|
+
end
|
207
243
|
end
|
208
244
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
245
|
+
# @private
|
246
|
+
# Builds Ruby code for a sequence
|
247
|
+
# (head *first_terms variadic_term *last_terms)
|
248
|
+
class Sequence < SimpleDelegator
|
249
|
+
def initialize(compiler, *arity_term_list)
|
250
|
+
@arities, @terms = arity_term_list.transpose
|
251
|
+
|
252
|
+
super(compiler)
|
253
|
+
@variadic_index = @arities.find_index { |a| a.is_a?(Range) }
|
254
|
+
fail_due_to 'multiple variable patterns in same sequence' \
|
255
|
+
if @variadic_index && !@arities.one? { |a| a.is_a?(Range) }
|
217
256
|
end
|
218
257
|
|
219
|
-
|
220
|
-
|
221
|
-
|
258
|
+
def compile
|
259
|
+
[
|
260
|
+
compile_guard_clause,
|
261
|
+
compile_child_nb_guard,
|
262
|
+
compile_seq_head,
|
263
|
+
*compile_first_terms,
|
264
|
+
compile_variadic_term,
|
265
|
+
*compile_last_terms
|
266
|
+
].compact.join(" &&\n") << SEQ_HEAD_GUARD
|
267
|
+
end
|
222
268
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
# grep for seq_head to see where it makes a difference
|
228
|
-
compile_expr(tokens, cur_node, true)
|
229
|
-
else
|
230
|
-
child_node = "#{cur_node}.children[#{index}]"
|
231
|
-
compile_expr(tokens, child_node, false)
|
269
|
+
private
|
270
|
+
|
271
|
+
def first_terms_arity
|
272
|
+
first_terms_range { |r| @arities[r].inject(0, :+) } || 0
|
232
273
|
end
|
233
|
-
end
|
234
274
|
|
235
|
-
|
236
|
-
|
237
|
-
tail = compile_seq_tail(tokens, cur_node)
|
238
|
-
terms << "(#{cur_node}.children.size >= #{index + tail.size})"
|
239
|
-
terms.concat tail
|
240
|
-
if capture
|
241
|
-
range = index..-tail.size - 1
|
242
|
-
terms << "(#{capture} = #{cur_node}.children[#{range}])"
|
275
|
+
def last_terms_arity
|
276
|
+
last_terms_range { |r| @arities[r].inject(0, :+) } || 0
|
243
277
|
end
|
244
|
-
terms
|
245
|
-
end
|
246
278
|
|
247
|
-
|
248
|
-
|
249
|
-
terms = []
|
250
|
-
until tokens.first == ')'
|
251
|
-
terms << compile_expr(tokens, child_node, false)
|
279
|
+
def first_terms_range
|
280
|
+
yield 1..(@variadic_index || @terms.size) - 1 if seq_head?
|
252
281
|
end
|
253
|
-
tokens.shift # drop ')'
|
254
|
-
# E.g. for terms.size == 3, we want to replace the three [%<revindex>i]
|
255
|
-
# with [-3], [-2] and [-1]
|
256
|
-
terms.map.with_index { |term, i| format term, revindex: i - terms.size }
|
257
|
-
end
|
258
282
|
|
259
|
-
|
260
|
-
|
283
|
+
def last_terms_range
|
284
|
+
yield @variadic_index + 1...@terms.size if @variadic_index
|
285
|
+
end
|
261
286
|
|
262
|
-
|
263
|
-
|
264
|
-
join_terms(init, terms, ' || ')
|
287
|
+
def seq_head?
|
288
|
+
@variadic_index != 0
|
265
289
|
end
|
266
|
-
end
|
267
290
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
291
|
+
def compile_child_nb_guard
|
292
|
+
min = first_terms_arity + last_terms_arity
|
293
|
+
"#{CUR_NODE}.children.size #{@variadic_index ? '>' : '='}= #{min}"
|
294
|
+
end
|
295
|
+
|
296
|
+
def term(index, range)
|
297
|
+
t = @terms[index]
|
298
|
+
if t.respond_to? :call
|
299
|
+
t.call(range)
|
300
|
+
else
|
301
|
+
with_child_context(t, range.begin)
|
279
302
|
end
|
280
|
-
|
303
|
+
end
|
304
|
+
|
305
|
+
def compile_seq_head
|
306
|
+
return unless seq_head?
|
307
|
+
|
308
|
+
fail_due_to 'sequences can not start with <' \
|
309
|
+
if @terms[0].respond_to? :call
|
281
310
|
|
282
|
-
terms
|
311
|
+
with_seq_head_context(@terms[0])
|
283
312
|
end
|
284
|
-
end
|
285
313
|
|
286
|
-
|
287
|
-
|
288
|
-
|
314
|
+
def compile_first_terms
|
315
|
+
first_terms_range { |range| compile_terms(range, 0) }
|
316
|
+
end
|
289
317
|
|
290
|
-
|
291
|
-
|
318
|
+
def compile_last_terms
|
319
|
+
last_terms_range { |r| compile_terms(r, -last_terms_arity) }
|
320
|
+
end
|
292
321
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
322
|
+
def compile_terms(index_range, start)
|
323
|
+
index_range.map do |i|
|
324
|
+
current = start
|
325
|
+
start += @arities.fetch(i)
|
326
|
+
term(i, current..start - 1)
|
327
|
+
end
|
299
328
|
end
|
300
329
|
|
301
|
-
|
302
|
-
|
330
|
+
def compile_variadic_term
|
331
|
+
variadic_arity { |arity| term(@variadic_index, arity) }
|
332
|
+
end
|
303
333
|
|
304
|
-
|
305
|
-
|
334
|
+
def variadic_arity
|
335
|
+
return unless @variadic_index
|
306
336
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
337
|
+
first = @variadic_index > 0 ? first_terms_arity : SEQ_HEAD_INDEX
|
338
|
+
yield first..-last_terms_arity - 1
|
339
|
+
end
|
340
|
+
end
|
341
|
+
private_constant :Sequence
|
342
|
+
|
343
|
+
def compile_captured_ellipsis
|
344
|
+
capture = next_capture
|
345
|
+
block = lambda { |range|
|
346
|
+
# Consider ($...) like (_ $...):
|
347
|
+
range = 0..range.end if range.begin == SEQ_HEAD_INDEX
|
348
|
+
"(#{capture} = #{CUR_NODE}.children[#{range}])"
|
349
|
+
}
|
350
|
+
[0..Float::INFINITY, block]
|
351
|
+
end
|
352
|
+
|
353
|
+
def compile_ellipsis
|
354
|
+
[0..Float::INFINITY, 'true']
|
355
|
+
end
|
356
|
+
|
357
|
+
# rubocop:disable Metrics/MethodLength
|
358
|
+
def compile_any_order(capture_all = nil)
|
359
|
+
rest = capture_rest = nil
|
360
|
+
patterns = []
|
361
|
+
with_temp_variables do |child, matched|
|
362
|
+
tokens_until('>', 'any child').each do
|
363
|
+
fail_due_to 'ellipsis must be at the end of <>' if rest
|
364
|
+
token = tokens.shift
|
365
|
+
case token
|
366
|
+
when CAPTURED_REST then rest = capture_rest = next_capture
|
367
|
+
when REST then rest = true
|
368
|
+
else patterns << compile_expr(token)
|
369
|
+
end
|
311
370
|
end
|
312
|
-
|
371
|
+
[rest ? patterns.size..Float::INFINITY : patterns.size,
|
372
|
+
->(range) { ANY_ORDER_TEMPLATE.result(binding) }]
|
373
|
+
end
|
374
|
+
end
|
375
|
+
# rubocop:enable Metrics/MethodLength
|
376
|
+
|
377
|
+
def insure_same_captures(enum, what)
|
378
|
+
return to_enum __method__, enum, what unless block_given?
|
313
379
|
|
314
|
-
|
380
|
+
captures_before = captures_after = nil
|
381
|
+
enum.each do
|
382
|
+
captures_before ||= @captures
|
383
|
+
@captures = captures_before
|
384
|
+
yield
|
385
|
+
captures_after ||= @captures
|
386
|
+
if captures_after != @captures
|
387
|
+
fail_due_to("each #{what} must have same # of captures")
|
388
|
+
end
|
315
389
|
end
|
316
390
|
end
|
317
391
|
|
318
|
-
def
|
319
|
-
|
320
|
-
|
392
|
+
def compile_union
|
393
|
+
# we need to ensure that each branch of the {} contains the same
|
394
|
+
# number of captures (since only one branch of the {} can actually
|
395
|
+
# match, the same variables are used to hold the captures for each
|
396
|
+
# branch)
|
397
|
+
enum = tokens_until('}', 'union')
|
398
|
+
terms = insure_same_captures(enum, 'branch of {}')
|
399
|
+
.map { compile_expr }
|
400
|
+
|
401
|
+
"(#{terms.join(' || ')})"
|
402
|
+
end
|
403
|
+
|
404
|
+
def compile_intersect
|
405
|
+
tokens_until(']', 'intersection')
|
406
|
+
.map { compile_expr }
|
407
|
+
.join(' && ')
|
321
408
|
end
|
322
409
|
|
323
|
-
def
|
324
|
-
"(
|
410
|
+
def compile_capture
|
411
|
+
"(#{next_capture} = #{CUR_ELEMENT}; #{compile_expr})"
|
325
412
|
end
|
326
413
|
|
327
|
-
def
|
328
|
-
"(#{
|
329
|
-
"#{compile_expr(tokens, "#{cur_node}.parent", seq_head)})"
|
414
|
+
def compile_negation
|
415
|
+
"!(#{compile_expr})"
|
330
416
|
end
|
331
417
|
|
332
|
-
def
|
418
|
+
def compile_ascend
|
419
|
+
with_context("#{CUR_NODE} && #{compile_expr}", "#{CUR_NODE}.parent")
|
420
|
+
end
|
421
|
+
|
422
|
+
def compile_wildcard(name)
|
333
423
|
if name.empty?
|
334
424
|
'true'
|
335
425
|
elsif @unify.key?(name)
|
336
426
|
# we have already seen a wildcard with this name before
|
337
427
|
# so the value it matched the first time will already be stored
|
338
428
|
# in a temp. check if this value matches the one stored in the temp
|
339
|
-
"
|
429
|
+
"#{CUR_ELEMENT} == temp#{@unify[name]}"
|
340
430
|
else
|
341
431
|
n = @unify[name] = next_temp_value
|
342
432
|
# double assign to temp#{n} to avoid "assigned but unused variable"
|
343
|
-
"(temp#{n} = #{
|
433
|
+
"(temp#{n} = #{CUR_ELEMENT}; " \
|
344
434
|
"temp#{n} = temp#{n}; true)"
|
345
435
|
end
|
346
436
|
end
|
347
437
|
|
348
|
-
def compile_literal(
|
349
|
-
"
|
438
|
+
def compile_literal(literal)
|
439
|
+
"#{CUR_ELEMENT} == #{literal}"
|
350
440
|
end
|
351
441
|
|
352
|
-
def compile_predicate(
|
442
|
+
def compile_predicate(predicate)
|
353
443
|
if predicate.end_with?('(') # is there an arglist?
|
354
444
|
args = compile_args(tokens)
|
355
445
|
predicate = predicate[0..-2] # drop the trailing (
|
356
|
-
"
|
446
|
+
"#{CUR_ELEMENT}.#{predicate}(#{args.join(',')})"
|
357
447
|
else
|
358
|
-
"
|
448
|
+
"#{CUR_ELEMENT}.#{predicate}"
|
359
449
|
end
|
360
450
|
end
|
361
451
|
|
362
|
-
def compile_funcall(
|
452
|
+
def compile_funcall(method)
|
363
453
|
# call a method in the context which this pattern-matching
|
364
454
|
# code is used in. pass target value as an argument
|
365
455
|
method = method[1..-1] # drop the leading #
|
366
456
|
if method.end_with?('(') # is there an arglist?
|
367
457
|
args = compile_args(tokens)
|
368
458
|
method = method[0..-2] # drop the trailing (
|
369
|
-
"
|
459
|
+
"#{method}(#{CUR_ELEMENT},#{args.join(',')})"
|
370
460
|
else
|
371
|
-
"
|
461
|
+
"#{method}(#{CUR_ELEMENT})"
|
372
462
|
end
|
373
463
|
end
|
374
464
|
|
375
|
-
def compile_nodetype(
|
376
|
-
"
|
377
|
-
"#{cur_node}.#{type.tr('-', '_')}_type?)"
|
465
|
+
def compile_nodetype(type)
|
466
|
+
"#{compile_guard_clause} && #{CUR_NODE}.#{type.tr('-', '_')}_type?"
|
378
467
|
end
|
379
468
|
|
380
|
-
def compile_param(
|
381
|
-
"
|
469
|
+
def compile_param(number)
|
470
|
+
"#{CUR_ELEMENT} == #{get_param(number)}"
|
382
471
|
end
|
383
472
|
|
384
473
|
def compile_args(tokens)
|
@@ -406,7 +495,9 @@ module RuboCop
|
|
406
495
|
end
|
407
496
|
|
408
497
|
def next_capture
|
409
|
-
|
498
|
+
index = @captures
|
499
|
+
@captures += 1
|
500
|
+
"captures[#{index}]"
|
410
501
|
end
|
411
502
|
|
412
503
|
def get_param(number)
|
@@ -415,21 +506,24 @@ module RuboCop
|
|
415
506
|
number.zero? ? @root : "param#{number}"
|
416
507
|
end
|
417
508
|
|
418
|
-
def
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
509
|
+
def emit_yield_capture(when_no_capture = '')
|
510
|
+
yield_val = if @captures.zero?
|
511
|
+
when_no_capture
|
512
|
+
elsif @captures == 1
|
513
|
+
'captures[0]' # Circumvent https://github.com/jruby/jruby/issues/5710
|
514
|
+
else
|
515
|
+
'*captures'
|
516
|
+
end
|
517
|
+
"yield(#{yield_val})"
|
424
518
|
end
|
425
519
|
|
426
520
|
def emit_retval
|
427
521
|
if @captures.zero?
|
428
522
|
'true'
|
429
523
|
elsif @captures == 1
|
430
|
-
'
|
524
|
+
'captures[0]'
|
431
525
|
else
|
432
|
-
|
526
|
+
'captures'
|
433
527
|
end
|
434
528
|
end
|
435
529
|
|
@@ -445,7 +539,7 @@ module RuboCop
|
|
445
539
|
def emit_method_code
|
446
540
|
<<-RUBY
|
447
541
|
return unless #{@match_code}
|
448
|
-
block_given? ?
|
542
|
+
block_given? ? #{emit_yield_capture} : (return #{emit_retval})
|
449
543
|
RUBY
|
450
544
|
end
|
451
545
|
|
@@ -454,20 +548,62 @@ module RuboCop
|
|
454
548
|
end
|
455
549
|
|
456
550
|
def with_temp_node(cur_node)
|
457
|
-
|
458
|
-
#
|
459
|
-
yield "#{temp_var} = #{cur_node}; #{temp_var} = #{temp_var}", temp_var
|
551
|
+
with_temp_variables do |node|
|
552
|
+
yield "(#{node} = #{cur_node})", node
|
460
553
|
end
|
554
|
+
.gsub("\n", "\n ") # Nicer indent for debugging
|
461
555
|
end
|
462
556
|
|
463
|
-
def
|
464
|
-
|
557
|
+
def with_temp_variables(&block)
|
558
|
+
names = block.parameters.map { |_, name| "#{name}#{next_temp_value}" }
|
559
|
+
yield(*names)
|
465
560
|
end
|
466
561
|
|
467
562
|
def next_temp_value
|
468
563
|
@temps += 1
|
469
564
|
end
|
470
565
|
|
566
|
+
def auto_use_temp_node?(code)
|
567
|
+
code.scan(CUR_PLACEHOLDER).count > 1
|
568
|
+
end
|
569
|
+
|
570
|
+
# with_<...>_context methods are used whenever the context,
|
571
|
+
# i.e the current node or the current element can be determined.
|
572
|
+
|
573
|
+
def with_child_context(code, child_index)
|
574
|
+
with_context(code, "#{CUR_NODE}.children[#{child_index}]")
|
575
|
+
end
|
576
|
+
|
577
|
+
def with_context(code, cur_node,
|
578
|
+
use_temp_node: auto_use_temp_node?(code))
|
579
|
+
if use_temp_node
|
580
|
+
with_temp_node(cur_node) do |init, temp_var|
|
581
|
+
substitute_cur_node(code, temp_var, first_cur_node: init)
|
582
|
+
end
|
583
|
+
else
|
584
|
+
substitute_cur_node(code, cur_node)
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
def with_seq_head_context(code)
|
589
|
+
if code.include?(SEQ_HEAD_GUARD)
|
590
|
+
fail_due_to('parentheses at sequence head')
|
591
|
+
end
|
592
|
+
|
593
|
+
code.gsub CUR_ELEMENT, "#{CUR_NODE}.type"
|
594
|
+
end
|
595
|
+
|
596
|
+
def substitute_cur_node(code, cur_node, first_cur_node: cur_node)
|
597
|
+
iter = 0
|
598
|
+
code
|
599
|
+
.gsub(CUR_ELEMENT, CUR_NODE)
|
600
|
+
.gsub(CUR_NODE) do
|
601
|
+
iter += 1
|
602
|
+
iter == 1 ? first_cur_node : cur_node
|
603
|
+
end
|
604
|
+
.gsub(SEQ_HEAD_GUARD, '')
|
605
|
+
end
|
606
|
+
|
471
607
|
def self.tokens(pattern)
|
472
608
|
pattern.scan(TOKEN).reject { |token| token =~ /\A#{SEPARATORS}\Z/ }
|
473
609
|
end
|
@@ -515,13 +651,11 @@ module RuboCop
|
|
515
651
|
end
|
516
652
|
|
517
653
|
def node_search_all(method_name, compiler, called_from)
|
518
|
-
|
519
|
-
yieldval = 'node' if yieldval.empty?
|
654
|
+
yield_code = compiler.emit_yield_capture('node')
|
520
655
|
prelude = "return enum_for(:#{method_name}, node0" \
|
521
656
|
"#{compiler.emit_trailing_params}) unless block_given?"
|
522
657
|
|
523
|
-
node_search(method_name, compiler,
|
524
|
-
called_from)
|
658
|
+
node_search(method_name, compiler, yield_code, prelude, called_from)
|
525
659
|
end
|
526
660
|
|
527
661
|
def node_search(method_name, compiler, on_match, prelude, called_from)
|