rubocop-ast 0.4.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rubocop/ast.rb +18 -0
  3. data/lib/rubocop/ast/builder.rb +1 -0
  4. data/lib/rubocop/ast/node.rb +57 -126
  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 +16 -24
  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 +9 -4
  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 +6 -4
  37. data/lib/rubocop/ast/rubocop_compatibility.rb +31 -0
  38. data/lib/rubocop/ast/traversal.rb +1 -0
  39. data/lib/rubocop/ast/version.rb +1 -1
  40. metadata +39 -4
@@ -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,
@@ -229,9 +230,9 @@ module RuboCop
229
230
  when 2.7
230
231
  require 'parser/ruby27'
231
232
  Parser::Ruby27
232
- when 2.8
233
- require 'parser/ruby28'
234
- Parser::Ruby28
233
+ when 2.8, 3.0
234
+ require 'parser/ruby30'
235
+ Parser::Ruby30
235
236
  else
236
237
  raise ArgumentError,
237
238
  "RuboCop found unknown Ruby version: #{ruby_version.inspect}"
@@ -269,7 +270,8 @@ module RuboCop
269
270
  # is passed as a method argument. In this case tokens are interleaved by
270
271
  # heredoc contents' tokens.
271
272
  def sorted_tokens
272
- @sorted_tokens ||= tokens.sort_by(&:begin_pos)
273
+ # Use stable sort.
274
+ @sorted_tokens ||= tokens.sort_by.with_index { |token, i| [token.begin_pos, i] }
273
275
  end
274
276
 
275
277
  def source_range(range_or_node)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # ...
5
+ module AST
6
+ # Responsible for compatibility with main gem
7
+ # @api private
8
+ module RuboCopCompatibility
9
+ INCOMPATIBLE_COPS = {
10
+ '0.89.0' => 'Layout/LineLength',
11
+ '0.92.0' => 'Style/MixinUsage'
12
+ }.freeze
13
+ def rubocop_loaded
14
+ loaded = Gem::Version.new(RuboCop::Version::STRING)
15
+ incompatible = INCOMPATIBLE_COPS.select do |k, _v|
16
+ loaded < Gem::Version.new(k)
17
+ end.values
18
+ return if incompatible.empty?
19
+
20
+ warn <<~WARNING
21
+ *** WARNING – Incompatible versions of `rubocop` and `rubocop-ast`
22
+ You may encounter issues with the following \
23
+ Cop#{'s' if incompatible.size > 1}: #{incompatible.join(', ')}
24
+ Please upgrade rubocop to at least v#{INCOMPATIBLE_COPS.keys.last}
25
+ WARNING
26
+ end
27
+ end
28
+
29
+ extend RuboCopCompatibility
30
+ end
31
+ end
@@ -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.4.1'
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.4.1
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-16 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
@@ -18,14 +18,28 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 2.7.1.4
21
+ version: 2.7.1.5
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 2.7.1.4
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,7 +130,27 @@ 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
153
+ - lib/rubocop/ast/rubocop_compatibility.rb
119
154
  - lib/rubocop/ast/sexp.rb
120
155
  - lib/rubocop/ast/token.rb
121
156
  - lib/rubocop/ast/traversal.rb