rubocop-ast 0.5.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rubocop/ast.rb +17 -0
- data/lib/rubocop/ast/builder.rb +1 -0
- data/lib/rubocop/ast/node.rb +44 -125
- data/lib/rubocop/ast/node/array_node.rb +1 -0
- data/lib/rubocop/ast/node/block_node.rb +1 -0
- data/lib/rubocop/ast/node/def_node.rb +5 -0
- data/lib/rubocop/ast/node/keyword_splat_node.rb +1 -0
- data/lib/rubocop/ast/node/mixin/collection_node.rb +1 -0
- data/lib/rubocop/ast/node/mixin/descendence.rb +116 -0
- data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +2 -0
- data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +9 -0
- data/lib/rubocop/ast/node/mixin/numeric_node.rb +1 -0
- data/lib/rubocop/ast/node/mixin/predicate_operator_node.rb +7 -3
- data/lib/rubocop/ast/node/pair_node.rb +4 -0
- data/lib/rubocop/ast/node/regexp_node.rb +9 -4
- data/lib/rubocop/ast/node_pattern.rb +44 -870
- data/lib/rubocop/ast/node_pattern/builder.rb +72 -0
- data/lib/rubocop/ast/node_pattern/comment.rb +45 -0
- data/lib/rubocop/ast/node_pattern/compiler.rb +104 -0
- data/lib/rubocop/ast/node_pattern/compiler/atom_subcompiler.rb +56 -0
- data/lib/rubocop/ast/node_pattern/compiler/binding.rb +78 -0
- data/lib/rubocop/ast/node_pattern/compiler/debug.rb +168 -0
- data/lib/rubocop/ast/node_pattern/compiler/node_pattern_subcompiler.rb +146 -0
- data/lib/rubocop/ast/node_pattern/compiler/sequence_subcompiler.rb +420 -0
- data/lib/rubocop/ast/node_pattern/compiler/subcompiler.rb +57 -0
- data/lib/rubocop/ast/node_pattern/lexer.rb +70 -0
- data/lib/rubocop/ast/node_pattern/lexer.rex +39 -0
- data/lib/rubocop/ast/node_pattern/lexer.rex.rb +182 -0
- data/lib/rubocop/ast/node_pattern/method_definer.rb +143 -0
- data/lib/rubocop/ast/node_pattern/node.rb +275 -0
- data/lib/rubocop/ast/node_pattern/parser.racc.rb +470 -0
- data/lib/rubocop/ast/node_pattern/parser.rb +66 -0
- data/lib/rubocop/ast/node_pattern/parser.y +103 -0
- data/lib/rubocop/ast/node_pattern/sets.rb +37 -0
- data/lib/rubocop/ast/node_pattern/with_meta.rb +111 -0
- data/lib/rubocop/ast/processed_source.rb +5 -1
- data/lib/rubocop/ast/traversal.rb +149 -172
- data/lib/rubocop/ast/version.rb +1 -1
- metadata +37 -3
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'parser.racc'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module AST
|
7
|
+
class NodePattern
|
8
|
+
# Parser for NodePattern
|
9
|
+
# Note: class reopened in `parser.racc`
|
10
|
+
#
|
11
|
+
# Doc on how this fits in the compiling process:
|
12
|
+
# /doc/modules/ROOT/pages/node_pattern.md
|
13
|
+
class Parser < Racc::Parser
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
Builder = NodePattern::Builder
|
17
|
+
Lexer = NodePattern::Lexer
|
18
|
+
|
19
|
+
def initialize(builder = self.class::Builder.new)
|
20
|
+
super()
|
21
|
+
@builder = builder
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# (Similar API to `parser` gem)
|
26
|
+
# Parses a source and returns the AST.
|
27
|
+
#
|
28
|
+
# @param [Parser::Source::Buffer, String] source_buffer The source buffer to parse.
|
29
|
+
# @return [NodePattern::Node]
|
30
|
+
#
|
31
|
+
def parse(source)
|
32
|
+
@lexer = self.class::Lexer.new(source)
|
33
|
+
do_parse
|
34
|
+
rescue Lexer::Error => e
|
35
|
+
raise NodePattern::Invalid, e.message
|
36
|
+
ensure
|
37
|
+
@lexer = nil # Don't keep references
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
"<##{self.class}>"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def_delegators :@builder, :emit_list, :emit_unary_op, :emit_atom, :emit_capture,
|
47
|
+
:emit_call, :emit_union
|
48
|
+
def_delegators :@lexer, :next_token
|
49
|
+
|
50
|
+
def enforce_unary(node)
|
51
|
+
return node if node.arity == 1
|
52
|
+
|
53
|
+
detail = node.loc&.expression&.source || node.to_s
|
54
|
+
raise NodePattern::Invalid, 'parse error, expected unary node pattern ' \
|
55
|
+
"but got expression matching multiple elements: #{detail}"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Overrides Racc::Parser's method:
|
59
|
+
def on_error(token, val, _vstack)
|
60
|
+
detail = token_to_str(token) || '?'
|
61
|
+
raise NodePattern::Invalid, "parse error on value #{val.inspect} (#{detail})"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class RuboCop::AST::NodePattern::Parser
|
2
|
+
options no_result_var
|
3
|
+
token tSYMBOL tNUMBER tSTRING tWILDCARD tPARAM_NAMED tPARAM_CONST tPARAM_NUMBER
|
4
|
+
tFUNCTION_CALL tPREDICATE tNODE_TYPE tARG_LIST tUNIFY tREGEXP
|
5
|
+
rule
|
6
|
+
node_pattern # @return Node
|
7
|
+
: node_pattern_no_union
|
8
|
+
| union { enforce_unary(val[0]) }
|
9
|
+
;
|
10
|
+
|
11
|
+
node_pattern_no_union # @return Node
|
12
|
+
: '(' variadic_pattern_list ')' { emit_list :sequence, *val }
|
13
|
+
| '[' node_pattern_list ']' { emit_list :intersection, *val }
|
14
|
+
| '!' node_pattern { emit_unary_op :negation, *val }
|
15
|
+
| '^' node_pattern { emit_unary_op :ascend, *val }
|
16
|
+
| '`' node_pattern { emit_unary_op :descend, *val }
|
17
|
+
| '$' node_pattern { emit_capture(*val) }
|
18
|
+
| tFUNCTION_CALL args { emit_call :function_call, *val }
|
19
|
+
| tPREDICATE args { emit_call :predicate, *val }
|
20
|
+
| tNODE_TYPE { emit_call :node_type, *val }
|
21
|
+
| atom
|
22
|
+
;
|
23
|
+
|
24
|
+
atom # @return Node
|
25
|
+
: tSYMBOL { emit_atom :symbol, *val }
|
26
|
+
| tNUMBER { emit_atom :number, *val }
|
27
|
+
| tSTRING { emit_atom :string, *val }
|
28
|
+
| tPARAM_CONST { emit_atom :const, *val }
|
29
|
+
| tPARAM_NAMED { emit_atom :named_parameter, *val }
|
30
|
+
| tPARAM_NUMBER { emit_atom :positional_parameter, *val }
|
31
|
+
| tREGEXP { emit_atom :regexp, *val }
|
32
|
+
| tWILDCARD { emit_atom :wildcard, *val }
|
33
|
+
| tUNIFY { emit_atom :unify, *val }
|
34
|
+
;
|
35
|
+
|
36
|
+
union # @return Node
|
37
|
+
: '{' separated_variadic_patterns '}' { emit_union(*val) }
|
38
|
+
;
|
39
|
+
|
40
|
+
variadic_pattern # @return Node
|
41
|
+
: node_pattern_no_union
|
42
|
+
| union
|
43
|
+
| node_pattern repetition
|
44
|
+
{
|
45
|
+
main, repeat_t = val
|
46
|
+
emit_unary_op(:repetition, repeat_t, main, repeat_t)
|
47
|
+
}
|
48
|
+
| opt_capture '<' node_pattern_list opt_rest '>'
|
49
|
+
{
|
50
|
+
opt_capture, bracket, node_pattern_list, opt_rest, close_bracket = val
|
51
|
+
node_pattern_list << opt_rest if opt_rest
|
52
|
+
main = emit_list :any_order, bracket, node_pattern_list, close_bracket
|
53
|
+
emit_capture(opt_capture, main)
|
54
|
+
}
|
55
|
+
| rest
|
56
|
+
;
|
57
|
+
|
58
|
+
repetition # @return Token
|
59
|
+
: '?'
|
60
|
+
| '*'
|
61
|
+
| '+'
|
62
|
+
;
|
63
|
+
|
64
|
+
opt_capture # @return Token | nil
|
65
|
+
:
|
66
|
+
| '$'
|
67
|
+
;
|
68
|
+
|
69
|
+
rest # @return Node
|
70
|
+
: opt_capture '...' { emit_capture(val[0], emit_atom(:rest, val[1])) }
|
71
|
+
;
|
72
|
+
|
73
|
+
opt_rest # @return Node | nil
|
74
|
+
:
|
75
|
+
| rest
|
76
|
+
;
|
77
|
+
|
78
|
+
args # @return [Token, Array<Node>, Token] | nil
|
79
|
+
:
|
80
|
+
| tARG_LIST arg_list ')' { val }
|
81
|
+
;
|
82
|
+
|
83
|
+
arg_list # @return Array<Node>
|
84
|
+
: node_pattern { val }
|
85
|
+
| arg_list ',' node_pattern { val[0] << val[2] }
|
86
|
+
;
|
87
|
+
|
88
|
+
node_pattern_list # @return Array<Node>
|
89
|
+
: node_pattern { val }
|
90
|
+
| node_pattern_list node_pattern { val[0] << val[1] }
|
91
|
+
;
|
92
|
+
|
93
|
+
variadic_pattern_list # @return Array<Node>
|
94
|
+
: variadic_pattern { val }
|
95
|
+
| variadic_pattern_list variadic_pattern { val[0] << val[1] }
|
96
|
+
;
|
97
|
+
|
98
|
+
separated_variadic_patterns # @return Array<Array<Node>>
|
99
|
+
: { [[]] }
|
100
|
+
| separated_variadic_patterns variadic_pattern { val[0].last << val[1]; val[0] }
|
101
|
+
| separated_variadic_patterns '|' { val[0] << [] }
|
102
|
+
;
|
103
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module AST
|
5
|
+
class NodePattern
|
6
|
+
# Utility to assign a set of values to a constant
|
7
|
+
module Sets
|
8
|
+
REGISTRY = Hash.new do |h, set|
|
9
|
+
name = Sets.name(set).freeze
|
10
|
+
Sets.const_set(name, set)
|
11
|
+
h[set] = "::RuboCop::AST::NodePattern::Sets::#{name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
MAX = 4
|
15
|
+
def self.name(set)
|
16
|
+
elements = set
|
17
|
+
elements = set.first(MAX - 1) << :etc if set.size > MAX
|
18
|
+
name = elements.to_a.join('_').upcase.gsub(/[^A-Z0-9_]/, '')
|
19
|
+
uniq("SET_#{name}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.uniq(name)
|
23
|
+
return name unless Sets.const_defined?(name)
|
24
|
+
|
25
|
+
(2..Float::INFINITY).each do |i|
|
26
|
+
uniq = "#{name}_#{i}"
|
27
|
+
return uniq unless Sets.const_defined?(uniq)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.[](set)
|
32
|
+
REGISTRY[set]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module AST
|
5
|
+
class NodePattern
|
6
|
+
class Parser
|
7
|
+
# Overrides Parser to use `WithMeta` variants and provide additional methods
|
8
|
+
class WithMeta < Parser
|
9
|
+
# Overrides Lexer to token locations and comments
|
10
|
+
class Lexer < NodePattern::Lexer
|
11
|
+
attr_reader :source_buffer
|
12
|
+
|
13
|
+
def initialize(str_or_buffer)
|
14
|
+
@source_buffer = if str_or_buffer.respond_to?(:source)
|
15
|
+
str_or_buffer
|
16
|
+
else
|
17
|
+
::Parser::Source::Buffer.new('(string)', source: str_or_buffer)
|
18
|
+
end
|
19
|
+
@comments = []
|
20
|
+
super(@source_buffer.source)
|
21
|
+
end
|
22
|
+
|
23
|
+
def token(type, value)
|
24
|
+
super(type, [value, pos])
|
25
|
+
end
|
26
|
+
|
27
|
+
def emit_comment
|
28
|
+
@comments << Comment.new(pos)
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [::Parser::Source::Range] last match's position
|
33
|
+
def pos
|
34
|
+
::Parser::Source::Range.new(source_buffer, ss.pos - ss.matched_size, ss.pos)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Overrides Builder to emit nodes with locations
|
39
|
+
class Builder < NodePattern::Builder
|
40
|
+
def emit_atom(type, token)
|
41
|
+
value, loc = token
|
42
|
+
begin_l = loc.resize(1)
|
43
|
+
end_l = loc.end.adjust(begin_pos: -1)
|
44
|
+
begin_l = nil if begin_l.source.match?(/\w/)
|
45
|
+
end_l = nil if end_l.source.match?(/\w/)
|
46
|
+
n(type, [value], source_map(token, begin_t: begin_l, end_t: end_l))
|
47
|
+
end
|
48
|
+
|
49
|
+
def emit_unary_op(type, operator_t = nil, *children)
|
50
|
+
children[-1] = children[-1].first if children[-1].is_a?(Array) # token?
|
51
|
+
map = source_map(children.first.loc.expression, operator_t: operator_t)
|
52
|
+
n(type, children, map)
|
53
|
+
end
|
54
|
+
|
55
|
+
def emit_list(type, begin_t, children, end_t)
|
56
|
+
expr = children.first.loc.expression.join(children.last.loc.expression)
|
57
|
+
map = source_map(expr, begin_t: begin_t, end_t: end_t)
|
58
|
+
n(type, children, map)
|
59
|
+
end
|
60
|
+
|
61
|
+
def emit_call(type, selector_t, args = nil)
|
62
|
+
selector, = selector_t
|
63
|
+
begin_t, arg_nodes, end_t = args
|
64
|
+
|
65
|
+
map = source_map(selector_t, begin_t: begin_t, end_t: end_t, selector_t: selector_t)
|
66
|
+
n(type, [selector, *arg_nodes], map)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def n(type, children, source_map)
|
72
|
+
super(type, children, { location: source_map })
|
73
|
+
end
|
74
|
+
|
75
|
+
def loc(token_or_range)
|
76
|
+
return token_or_range[1] if token_or_range.is_a?(Array)
|
77
|
+
|
78
|
+
token_or_range
|
79
|
+
end
|
80
|
+
|
81
|
+
def join_exprs(left_expr, right_expr)
|
82
|
+
left_expr.loc.expression
|
83
|
+
.join(right_expr.loc.expression)
|
84
|
+
end
|
85
|
+
|
86
|
+
def source_map(token_or_range, begin_t: nil, end_t: nil, operator_t: nil, selector_t: nil)
|
87
|
+
expression_l = loc(token_or_range)
|
88
|
+
expression_l = expression_l.expression if expression_l.respond_to?(:expression)
|
89
|
+
locs = [begin_t, end_t, operator_t, selector_t].map { |token| loc(token) }
|
90
|
+
begin_l, end_l, operator_l, selector_l = locs
|
91
|
+
|
92
|
+
expression_l = locs.compact.inject(expression_l, :join)
|
93
|
+
|
94
|
+
::Parser::Source::Map::Send.new(_dot_l = nil, selector_l, begin_l, end_l, expression_l)
|
95
|
+
.with_operator(operator_l)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
attr_reader :comments, :tokens
|
100
|
+
|
101
|
+
def do_parse
|
102
|
+
r = super
|
103
|
+
@comments = @lexer.comments
|
104
|
+
@tokens = @lexer.tokens
|
105
|
+
r
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -9,6 +9,7 @@ module RuboCop
|
|
9
9
|
# and other information such as disabled lines for cops.
|
10
10
|
# It also provides a convenient way to access source lines.
|
11
11
|
class ProcessedSource
|
12
|
+
# @api private
|
12
13
|
STRING_SOURCE_NAME = '(string)'
|
13
14
|
|
14
15
|
attr_reader :path, :buffer, :ast, :comments, :tokens, :diagnostics,
|
@@ -19,6 +20,9 @@ module RuboCop
|
|
19
20
|
new(file, ruby_version, path)
|
20
21
|
end
|
21
22
|
|
23
|
+
INVALID_LEVELS = %i[error fatal].freeze
|
24
|
+
private_constant :INVALID_LEVELS
|
25
|
+
|
22
26
|
def initialize(source, ruby_version, path = nil)
|
23
27
|
# Defaults source encoding to UTF-8, regardless of the encoding it has
|
24
28
|
# been read with, which could be non-utf8 depending on the default
|
@@ -63,7 +67,7 @@ module RuboCop
|
|
63
67
|
def valid_syntax?
|
64
68
|
return false if @parser_error
|
65
69
|
|
66
|
-
@diagnostics.none? { |d|
|
70
|
+
@diagnostics.none? { |d| INVALID_LEVELS.include?(d.level) }
|
67
71
|
end
|
68
72
|
|
69
73
|
# Raw source checksum for tracking infinite loops.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable Metrics/ModuleLength
|
4
3
|
module RuboCop
|
5
4
|
module AST
|
6
5
|
# Provides methods for traversing an AST.
|
@@ -8,197 +7,175 @@ module RuboCop
|
|
8
7
|
# Override methods to perform custom processing. Remember to call `super`
|
9
8
|
# if you want to recursively process descendant nodes.
|
10
9
|
module Traversal
|
10
|
+
# Only for debugging.
|
11
|
+
# @api private
|
12
|
+
class DebugError < RuntimeError
|
13
|
+
end
|
14
|
+
|
15
|
+
TYPE_TO_METHOD = Hash.new { |h, type| h[type] = :"on_#{type}" }
|
16
|
+
|
11
17
|
def walk(node)
|
12
18
|
return if node.nil?
|
13
19
|
|
14
|
-
send(
|
20
|
+
send(TYPE_TO_METHOD[node.type], node)
|
15
21
|
nil
|
16
22
|
end
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
|
40
|
-
kwoptarg].freeze
|
41
|
-
|
42
|
-
NO_CHILD_NODES.each do |type|
|
43
|
-
module_eval("def on_#{type}(node); end", __FILE__, __LINE__)
|
44
|
-
end
|
45
|
-
|
46
|
-
ONE_CHILD_NODE.each do |type|
|
47
|
-
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
48
|
-
def on_#{type}(node)
|
49
|
-
if (child = node.children[0])
|
50
|
-
send(:"on_\#{child.type}", child)
|
24
|
+
# @api private
|
25
|
+
module CallbackCompiler
|
26
|
+
SEND = 'send(TYPE_TO_METHOD[child.type], child)'
|
27
|
+
assign_code = 'child = node.children[%<index>i]'
|
28
|
+
code = "#{assign_code}\n#{SEND}"
|
29
|
+
TEMPLATE = {
|
30
|
+
skip: '',
|
31
|
+
always: code,
|
32
|
+
nil?: "#{code} if child"
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
def def_callback(type, *signature,
|
36
|
+
arity: signature.size..signature.size,
|
37
|
+
arity_check: ENV['RUBOCOP_DEBUG'] && self.arity_check(arity),
|
38
|
+
body: self.body(signature, arity_check))
|
39
|
+
type, *aliases = type
|
40
|
+
lineno = caller_locations(1, 1).first.lineno
|
41
|
+
module_eval(<<~RUBY, __FILE__, lineno) # rubocop:disable Style/EvalWithLocation
|
42
|
+
def on_#{type}(node)
|
43
|
+
#{body}
|
44
|
+
nil
|
51
45
|
end
|
46
|
+
RUBY
|
47
|
+
aliases.each do |m|
|
48
|
+
alias_method "on_#{m}", "on_#{type}"
|
52
49
|
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
MANY_CHILD_NODES.each do |type|
|
57
|
-
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
58
|
-
def on_#{type}(node)
|
59
|
-
node.children.each { |child| send(:"on_\#{child.type}", child) }
|
60
|
-
nil
|
61
|
-
end
|
62
|
-
RUBY
|
63
|
-
end
|
50
|
+
end
|
64
51
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
if (child = node.children[1])
|
70
|
-
send(:"on_\#{child.type}", child)
|
52
|
+
def body(signature, prelude)
|
53
|
+
signature
|
54
|
+
.map.with_index do |arg, i|
|
55
|
+
TEMPLATE[arg].gsub('%<index>i', i.to_s)
|
71
56
|
end
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
def on_const(node)
|
77
|
-
return unless (child = node.children[0])
|
78
|
-
|
79
|
-
send(:"on_#{child.type}", child)
|
80
|
-
end
|
81
|
-
|
82
|
-
def on_casgn(node)
|
83
|
-
children = node.children
|
84
|
-
if (child = children[0]) # always const???
|
85
|
-
send(:"on_#{child.type}", child)
|
57
|
+
.unshift(prelude)
|
58
|
+
.join("\n")
|
86
59
|
end
|
87
|
-
return unless (child = children[2])
|
88
|
-
|
89
|
-
send(:"on_#{child.type}", child)
|
90
|
-
end
|
91
60
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
61
|
+
def arity_check(range)
|
62
|
+
<<~RUBY
|
63
|
+
n = node.children.size
|
64
|
+
raise DebugError, [
|
65
|
+
'Expected #{range} children, got',
|
66
|
+
n, 'for', node.inspect
|
67
|
+
].join(' ') unless (#{range}).cover?(node.children.size)
|
68
|
+
RUBY
|
98
69
|
end
|
99
|
-
return unless (child = children[2])
|
100
|
-
|
101
|
-
send(:"on_#{child.type}", child)
|
102
|
-
end
|
103
|
-
|
104
|
-
def on_def(node)
|
105
|
-
children = node.children
|
106
|
-
on_args(children[1])
|
107
|
-
return unless (child = children[2])
|
108
|
-
|
109
|
-
send(:"on_#{child.type}", child)
|
110
70
|
end
|
111
|
-
|
112
|
-
|
71
|
+
private_constant :CallbackCompiler
|
72
|
+
extend CallbackCompiler
|
73
|
+
send_code = CallbackCompiler::SEND
|
74
|
+
|
75
|
+
### arity == 0
|
76
|
+
no_children = %i[true false nil self cbase zsuper redo retry
|
77
|
+
forward_args forwarded_args match_nil_pattern
|
78
|
+
forward_arg lambda empty_else kwnilarg
|
79
|
+
__FILE__ __LINE__ __ENCODING__]
|
80
|
+
|
81
|
+
### arity == 0..1
|
82
|
+
opt_symbol_child = %i[restarg kwrestarg]
|
83
|
+
opt_node_child = %i[splat kwsplat match_rest]
|
84
|
+
|
85
|
+
### arity == 1
|
86
|
+
literal_child = %i[int float complex
|
87
|
+
rational str sym lvar
|
88
|
+
ivar cvar gvar nth_ref back_ref
|
89
|
+
arg blockarg shadowarg
|
90
|
+
kwarg match_var]
|
91
|
+
|
92
|
+
many_symbol_children = %i[regopt]
|
93
|
+
|
94
|
+
node_child = %i[block_pass not
|
95
|
+
match_current_line defined?
|
96
|
+
arg_expr pin if_guard unless_guard
|
97
|
+
match_with_trailing_comma]
|
98
|
+
node_or_nil_child = %i[preexe postexe]
|
99
|
+
|
100
|
+
NO_CHILD_NODES = (no_children + opt_symbol_child + literal_child).to_set.freeze
|
101
|
+
private_constant :NO_CHILD_NODES # Used by Commissioner
|
102
|
+
|
103
|
+
### arity > 1
|
104
|
+
symbol_then_opt_node = %i[lvasgn ivasgn cvasgn gvasgn]
|
105
|
+
symbol_then_node_or_nil = %i[optarg kwoptarg]
|
106
|
+
node_then_opt_node = %i[while until module sclass]
|
107
|
+
|
108
|
+
### variable arity
|
109
|
+
many_node_children = %i[dstr dsym xstr regexp array hash pair
|
110
|
+
mlhs masgn or_asgn and_asgn rasgn mrasgn
|
111
|
+
undef alias args super yield or and
|
112
|
+
while_post until_post iflipflop eflipflop
|
113
|
+
match_with_lvasgn begin kwbegin return
|
114
|
+
in_match match_alt break next
|
115
|
+
match_as array_pattern array_pattern_with_tail
|
116
|
+
hash_pattern const_pattern find_pattern
|
117
|
+
index indexasgn procarg0]
|
118
|
+
many_opt_node_children = %i[case rescue resbody ensure for when
|
119
|
+
case_match in_pattern irange erange]
|
120
|
+
|
121
|
+
### Callbacks for above
|
122
|
+
def_callback no_children
|
123
|
+
def_callback opt_symbol_child, :skip, arity: 0..1
|
124
|
+
def_callback opt_node_child, :nil?, arity: 0..1
|
125
|
+
|
126
|
+
def_callback literal_child, :skip
|
127
|
+
def_callback node_child, :always
|
128
|
+
def_callback node_or_nil_child, :nil?
|
129
|
+
|
130
|
+
def_callback symbol_then_opt_node, :skip, :nil?, arity: 1..2
|
131
|
+
def_callback symbol_then_node_or_nil, :skip, :nil?
|
132
|
+
def_callback node_then_opt_node, :always, :nil?
|
133
|
+
|
134
|
+
def_callback many_symbol_children, :skip, arity_check: nil
|
135
|
+
def_callback many_node_children, body: <<~RUBY
|
136
|
+
node.children.each { |child| #{send_code} }
|
137
|
+
RUBY
|
138
|
+
def_callback many_opt_node_children,
|
139
|
+
body: <<~RUBY
|
140
|
+
node.children.each { |child| #{send_code} if child }
|
141
|
+
RUBY
|
142
|
+
|
143
|
+
### Other particular cases
|
144
|
+
def_callback :const, :nil?, :skip
|
145
|
+
def_callback :casgn, :nil?, :skip, :nil?, arity: 2..3
|
146
|
+
def_callback :class, :always, :nil?, :nil?
|
147
|
+
def_callback :def, :skip, :always, :nil?
|
148
|
+
def_callback :op_asgn, :always, :skip, :always
|
149
|
+
def_callback :if, :always, :nil?, :nil?
|
150
|
+
def_callback :block, :always, :always, :nil?
|
151
|
+
def_callback :numblock, :always, :skip, :nil?
|
152
|
+
def_callback :defs, :always, :skip, :always, :nil?
|
153
|
+
|
154
|
+
def_callback %i[send csend], body: <<~RUBY
|
113
155
|
node.children.each_with_index do |child, i|
|
114
156
|
next if i == 1
|
115
157
|
|
116
|
-
|
158
|
+
#{send_code} if child
|
117
159
|
end
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
def on_defs(node)
|
132
|
-
children = node.children
|
133
|
-
child = children[0]
|
134
|
-
send(:"on_#{child.type}", child)
|
135
|
-
on_args(children[2])
|
136
|
-
return unless (child = children[3])
|
137
|
-
|
138
|
-
send(:"on_#{child.type}", child)
|
139
|
-
end
|
140
|
-
|
141
|
-
def on_if(node)
|
142
|
-
children = node.children
|
143
|
-
child = children[0]
|
144
|
-
send(:"on_#{child.type}", child)
|
145
|
-
if (child = children[1])
|
146
|
-
send(:"on_#{child.type}", child)
|
147
|
-
end
|
148
|
-
return unless (child = children[2])
|
149
|
-
|
150
|
-
send(:"on_#{child.type}", child)
|
151
|
-
end
|
152
|
-
|
153
|
-
def on_while(node)
|
154
|
-
children = node.children
|
155
|
-
child = children[0]
|
156
|
-
send(:"on_#{child.type}", child)
|
157
|
-
return unless (child = children[1])
|
158
|
-
|
159
|
-
send(:"on_#{child.type}", child)
|
160
|
-
end
|
161
|
-
|
162
|
-
alias on_until on_while
|
163
|
-
alias on_module on_while
|
164
|
-
alias on_sclass on_while
|
165
|
-
|
166
|
-
def on_block(node)
|
167
|
-
children = node.children
|
168
|
-
child = children[0]
|
169
|
-
send(:"on_#{child.type}", child) # can be send, zsuper...
|
170
|
-
on_args(children[1])
|
171
|
-
return unless (child = children[2])
|
172
|
-
|
173
|
-
send(:"on_#{child.type}", child)
|
174
|
-
end
|
175
|
-
|
176
|
-
def on_case(node)
|
160
|
+
RUBY
|
161
|
+
|
162
|
+
### generic processing of any other node (forward compatibility)
|
163
|
+
defined = instance_methods(false)
|
164
|
+
.grep(/^on_/)
|
165
|
+
.map { |s| s.to_s[3..-1].to_sym } # :on_foo => :foo
|
166
|
+
|
167
|
+
to_define = ::Parser::Meta::NODE_TYPES.to_a
|
168
|
+
to_define -= defined
|
169
|
+
to_define -= %i[numargs ident] # transient
|
170
|
+
to_define -= %i[blockarg_expr restarg_expr] # obsolete
|
171
|
+
to_define -= %i[objc_kwarg objc_restarg objc_varargs] # mac_ruby
|
172
|
+
def_callback to_define, body: <<~RUBY
|
177
173
|
node.children.each do |child|
|
178
|
-
|
174
|
+
next unless child.class == Node
|
175
|
+
#{send_code}
|
179
176
|
end
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
alias on_rescue on_case
|
184
|
-
alias on_resbody on_case
|
185
|
-
alias on_ensure on_case
|
186
|
-
alias on_for on_case
|
187
|
-
alias on_when on_case
|
188
|
-
alias on_case_match on_case
|
189
|
-
alias on_in_pattern on_case
|
190
|
-
alias on_irange on_case
|
191
|
-
alias on_erange on_case
|
192
|
-
|
193
|
-
def on_numblock(node)
|
194
|
-
children = node.children
|
195
|
-
child = children[0]
|
196
|
-
send(:"on_#{child.type}", child)
|
197
|
-
return unless (child = children[2])
|
198
|
-
|
199
|
-
send(:"on_#{child.type}", child)
|
200
|
-
end
|
177
|
+
RUBY
|
178
|
+
MISSING = to_define if ENV['RUBOCOP_DEBUG']
|
201
179
|
end
|
202
180
|
end
|
203
181
|
end
|
204
|
-
# rubocop:enable Metrics/ModuleLength
|