rubocop-ast 0.6.0 → 0.7.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/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 +1 -0
- 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 +69 -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 +1 -0
- data/lib/rubocop/ast/traversal.rb +1 -0
- data/lib/rubocop/ast/version.rb +1 -1
- metadata +36 -2
@@ -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,
|
@@ -38,6 +38,7 @@ module RuboCop
|
|
38
38
|
index indexasgn].freeze
|
39
39
|
SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
|
40
40
|
kwoptarg].freeze
|
41
|
+
private_constant :NO_CHILD_NODES, :ONE_CHILD_NODE, :MANY_CHILD_NODES, :SECOND_CHILD_ONLY
|
41
42
|
|
42
43
|
NO_CHILD_NODES.each do |type|
|
43
44
|
module_eval("def on_#{type}(node); end", __FILE__, __LINE__)
|
data/lib/rubocop/ast/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-ast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2020-09-
|
13
|
+
date: 2020-09-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: parser
|
@@ -26,6 +26,20 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: 2.7.1.5
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: strscan
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 1.0.0
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.0.0
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: bundler
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -89,6 +103,7 @@ files:
|
|
89
103
|
- lib/rubocop/ast/node/mixin/binary_operator_node.rb
|
90
104
|
- lib/rubocop/ast/node/mixin/collection_node.rb
|
91
105
|
- lib/rubocop/ast/node/mixin/conditional_node.rb
|
106
|
+
- lib/rubocop/ast/node/mixin/descendence.rb
|
92
107
|
- lib/rubocop/ast/node/mixin/hash_element_node.rb
|
93
108
|
- lib/rubocop/ast/node/mixin/method_dispatch_node.rb
|
94
109
|
- lib/rubocop/ast/node/mixin/method_identifier_predicates.rb
|
@@ -115,6 +130,25 @@ files:
|
|
115
130
|
- lib/rubocop/ast/node/while_node.rb
|
116
131
|
- lib/rubocop/ast/node/yield_node.rb
|
117
132
|
- lib/rubocop/ast/node_pattern.rb
|
133
|
+
- lib/rubocop/ast/node_pattern/builder.rb
|
134
|
+
- lib/rubocop/ast/node_pattern/comment.rb
|
135
|
+
- lib/rubocop/ast/node_pattern/compiler.rb
|
136
|
+
- lib/rubocop/ast/node_pattern/compiler/atom_subcompiler.rb
|
137
|
+
- lib/rubocop/ast/node_pattern/compiler/binding.rb
|
138
|
+
- lib/rubocop/ast/node_pattern/compiler/debug.rb
|
139
|
+
- lib/rubocop/ast/node_pattern/compiler/node_pattern_subcompiler.rb
|
140
|
+
- lib/rubocop/ast/node_pattern/compiler/sequence_subcompiler.rb
|
141
|
+
- lib/rubocop/ast/node_pattern/compiler/subcompiler.rb
|
142
|
+
- lib/rubocop/ast/node_pattern/lexer.rb
|
143
|
+
- lib/rubocop/ast/node_pattern/lexer.rex
|
144
|
+
- lib/rubocop/ast/node_pattern/lexer.rex.rb
|
145
|
+
- lib/rubocop/ast/node_pattern/method_definer.rb
|
146
|
+
- lib/rubocop/ast/node_pattern/node.rb
|
147
|
+
- lib/rubocop/ast/node_pattern/parser.racc.rb
|
148
|
+
- lib/rubocop/ast/node_pattern/parser.rb
|
149
|
+
- lib/rubocop/ast/node_pattern/parser.y
|
150
|
+
- lib/rubocop/ast/node_pattern/sets.rb
|
151
|
+
- lib/rubocop/ast/node_pattern/with_meta.rb
|
118
152
|
- lib/rubocop/ast/processed_source.rb
|
119
153
|
- lib/rubocop/ast/rubocop_compatibility.rb
|
120
154
|
- lib/rubocop/ast/sexp.rb
|