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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rubocop/ast.rb +17 -0
  3. data/lib/rubocop/ast/builder.rb +1 -0
  4. data/lib/rubocop/ast/node.rb +44 -125
  5. data/lib/rubocop/ast/node/array_node.rb +1 -0
  6. data/lib/rubocop/ast/node/block_node.rb +1 -0
  7. data/lib/rubocop/ast/node/keyword_splat_node.rb +1 -0
  8. data/lib/rubocop/ast/node/mixin/collection_node.rb +1 -0
  9. data/lib/rubocop/ast/node/mixin/descendence.rb +116 -0
  10. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +2 -0
  11. data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +9 -0
  12. data/lib/rubocop/ast/node/mixin/numeric_node.rb +1 -0
  13. data/lib/rubocop/ast/node/mixin/predicate_operator_node.rb +7 -3
  14. data/lib/rubocop/ast/node/pair_node.rb +4 -0
  15. data/lib/rubocop/ast/node/regexp_node.rb +1 -0
  16. data/lib/rubocop/ast/node_pattern.rb +44 -870
  17. data/lib/rubocop/ast/node_pattern/builder.rb +72 -0
  18. data/lib/rubocop/ast/node_pattern/comment.rb +45 -0
  19. data/lib/rubocop/ast/node_pattern/compiler.rb +104 -0
  20. data/lib/rubocop/ast/node_pattern/compiler/atom_subcompiler.rb +56 -0
  21. data/lib/rubocop/ast/node_pattern/compiler/binding.rb +78 -0
  22. data/lib/rubocop/ast/node_pattern/compiler/debug.rb +168 -0
  23. data/lib/rubocop/ast/node_pattern/compiler/node_pattern_subcompiler.rb +146 -0
  24. data/lib/rubocop/ast/node_pattern/compiler/sequence_subcompiler.rb +420 -0
  25. data/lib/rubocop/ast/node_pattern/compiler/subcompiler.rb +57 -0
  26. data/lib/rubocop/ast/node_pattern/lexer.rb +69 -0
  27. data/lib/rubocop/ast/node_pattern/lexer.rex +39 -0
  28. data/lib/rubocop/ast/node_pattern/lexer.rex.rb +182 -0
  29. data/lib/rubocop/ast/node_pattern/method_definer.rb +143 -0
  30. data/lib/rubocop/ast/node_pattern/node.rb +275 -0
  31. data/lib/rubocop/ast/node_pattern/parser.racc.rb +470 -0
  32. data/lib/rubocop/ast/node_pattern/parser.rb +66 -0
  33. data/lib/rubocop/ast/node_pattern/parser.y +103 -0
  34. data/lib/rubocop/ast/node_pattern/sets.rb +37 -0
  35. data/lib/rubocop/ast/node_pattern/with_meta.rb +111 -0
  36. data/lib/rubocop/ast/processed_source.rb +1 -0
  37. data/lib/rubocop/ast/traversal.rb +1 -0
  38. data/lib/rubocop/ast/version.rb +1 -1
  39. 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__)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module AST
5
5
  module Version
6
- STRING = '0.6.0'
6
+ STRING = '0.7.0'
7
7
  end
8
8
  end
9
9
  end
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.6.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-26 00:00:00.000000000 Z
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