synvert-core 0.63.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +9 -1
  5. data/Guardfile +11 -2
  6. data/README.md +74 -34
  7. data/Rakefile +15 -1
  8. data/lib/synvert/core/array_ext.rb +41 -0
  9. data/lib/synvert/core/configuration.rb +12 -0
  10. data/lib/synvert/core/engine/erb.rb +9 -8
  11. data/lib/synvert/core/exceptions.rb +0 -4
  12. data/lib/synvert/core/node_ext.rb +232 -128
  13. data/lib/synvert/core/node_query/compiler/array.rb +34 -0
  14. data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
  15. data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
  16. data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
  17. data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
  18. data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
  19. data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
  20. data/lib/synvert/core/node_query/compiler/float.rb +23 -0
  21. data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
  22. data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
  23. data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
  24. data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
  25. data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
  26. data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
  27. data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
  28. data/lib/synvert/core/node_query/compiler/string.rb +34 -0
  29. data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
  30. data/lib/synvert/core/node_query/compiler.rb +24 -0
  31. data/lib/synvert/core/node_query/lexer.rex +96 -0
  32. data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
  33. data/lib/synvert/core/node_query/parser.racc.rb +518 -0
  34. data/lib/synvert/core/node_query/parser.y +84 -0
  35. data/lib/synvert/core/node_query.rb +36 -0
  36. data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
  37. data/lib/synvert/core/rewriter/action/delete_action.rb +17 -8
  38. data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
  39. data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
  40. data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
  41. data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
  42. data/lib/synvert/core/rewriter/action/replace_action.rb +15 -5
  43. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
  44. data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
  45. data/lib/synvert/core/rewriter/action/wrap_action.rb +16 -7
  46. data/lib/synvert/core/rewriter/action.rb +22 -10
  47. data/lib/synvert/core/rewriter/any_value.rb +1 -0
  48. data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
  49. data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
  50. data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
  51. data/lib/synvert/core/rewriter/condition.rb +11 -3
  52. data/lib/synvert/core/rewriter/gem_spec.rb +6 -3
  53. data/lib/synvert/core/rewriter/helper.rb +7 -4
  54. data/lib/synvert/core/rewriter/instance.rb +217 -104
  55. data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
  56. data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
  57. data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
  58. data/lib/synvert/core/rewriter/scope/within_scope.rb +10 -5
  59. data/lib/synvert/core/rewriter/scope.rb +8 -0
  60. data/lib/synvert/core/rewriter/warning.rb +1 -1
  61. data/lib/synvert/core/rewriter.rb +91 -43
  62. data/lib/synvert/core/version.rb +1 -1
  63. data/lib/synvert/core.rb +22 -6
  64. data/spec/synvert/core/engine/erb_spec.rb +2 -2
  65. data/spec/synvert/core/node_ext_spec.rb +36 -12
  66. data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
  67. data/spec/synvert/core/node_query/parser_spec.rb +270 -0
  68. data/spec/synvert/core/rewriter/action_spec.rb +0 -4
  69. data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
  70. data/spec/synvert/core/rewriter/gem_spec_spec.rb +1 -1
  71. data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
  72. data/spec/synvert/core/rewriter/instance_spec.rb +31 -20
  73. data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
  74. data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
  75. data/spec/synvert/core/rewriter_spec.rb +4 -2
  76. data/synvert-core-ruby.gemspec +7 -2
  77. metadata +91 -4
@@ -1,11 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser::AST
4
- # Parser::AST::Node monkey patch.
4
+ # Extend Parser::AST::Node.
5
+ # {https://github.com/whitequark/parser/blob/master/lib/parser/ast/node.rb}
6
+ #
7
+ # Rules
8
+ #
9
+ # Synvert compares ast nodes with key / value pairs, each ast node has
10
+ # multiple attributes, e.g. +receiver+, +message+ and +arguments+, it
11
+ # matches only when all of key / value pairs match.
12
+ #
13
+ # +type: 'send', message: :include, arguments: ['FactoryGirl::Syntax::Methods']+
14
+ #
15
+ # Synvert does comparison based on the value type
16
+ #
17
+ # 1. if value is a symbol, then compares ast node value as symbol, e.g. +message: :include+
18
+ # 2. if value is a string, then compares ast node original source code, e.g. +name: 'Synvert::Application'+
19
+ # 3. if value is a regexp, then compares ast node original source code, e.g. +message: /find_all_by_/+
20
+ # 4. if value is an array, then compares each ast node, e.g. +arguments: ['FactoryGirl::Syntax::Methods']+
21
+ # 5. if value is nil, then check if ast node is nil, e.g. +arguments: [nil]+
22
+ # 6. if value is true or false, then check if ast node is :true or :false, e.g. +arguments: [false]+
23
+ # 7. if value is ast, then compare ast node directly, e.g. +to_ast: Parser::CurrentRuby.parse("self.class.serialized_attributes")+
24
+ #
25
+ # It can also compare nested key / value pairs, like
26
+ #
27
+ # +type: 'send', receiver: { type: 'send', receiver: { type: 'send', message: 'config' }, message: 'active_record' }, message: 'identity_map='+
28
+ #
29
+ # Source Code to Ast Node
30
+ # {https://synvert-playground.xinminlabs.com?language=ruby}
5
31
  class Node
6
- # Get name node of :class, :module, :const, :mlhs, :def and :defs node.
32
+ # Initialize a Node.
7
33
  #
8
- # @return [Parser::AST::Node] name node.
34
+ # It extends Parser::AST::Node and set parent for its child nodes.
35
+ def initialize(type, children = [], properties = {})
36
+ @mutable_attributes = {}
37
+ super
38
+ # children could be nil for s(:array)
39
+ Array(children).each do |child_node|
40
+ if child_node.is_a?(Parser::AST::Node)
41
+ child_node.parent = self
42
+ end
43
+ end
44
+ end
45
+
46
+ # Get the parent node.
47
+ # @return [Parser::AST::Node] parent node.
48
+ def parent
49
+ @mutable_attributes[:parent]
50
+ end
51
+
52
+ # Set the parent node.
53
+ # @param node [Parser::AST::Node] parent node.
54
+ def parent=(node)
55
+ @mutable_attributes[:parent] = node
56
+ end
57
+
58
+ # Get the sibling nodes.
59
+ # @return [Array<Parser::AST::Node>] sibling nodes.
60
+ def siblings
61
+ index = parent.children.index(self)
62
+ parent.children[index + 1..]
63
+ end
64
+
65
+ # Get the name of node.
66
+ # It supports :arg, :blockarg, :class, :const, :cvar, :def, :defs, :ivar,
67
+ # :lvar, :mlhs, :module and :restarg nodes.
68
+ # @example
69
+ # node # => s(:class, s(:const, nil, :Synvert), nil, nil)
70
+ # node.name # => s(:const, nil, :Synvert)
71
+ # @return [Parser::AST::Node] name of node.
9
72
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
10
73
  def name
11
74
  case type
@@ -20,9 +83,12 @@ module Parser::AST
20
83
  end
21
84
  end
22
85
 
23
- # Get parent_class node of :class node.
24
- #
25
- # @return [Parser::AST::Node] parent_class node.
86
+ # Get parent_class of node.
87
+ # It supports :class node.
88
+ # @example
89
+ # node # s(:class, s(:const, nil, :Post), s(:const, s(:const, nil, :ActiveRecord), :Base), nil)
90
+ # node.parent_class # s(:const, s(:const, nil, :ActiveRecord), :Base)
91
+ # @return [Parser::AST::Node] parent_class of node.
26
92
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
27
93
  def parent_class
28
94
  if :class == type
@@ -32,9 +98,12 @@ module Parser::AST
32
98
  end
33
99
  end
34
100
 
35
- # Get parent constant node of :const node.
36
- #
37
- # @return [Parser::AST::Node] parent const node.
101
+ # Get parent constant of node.
102
+ # It supports :const node.
103
+ # @example
104
+ # node # s(:const, s(:const, nil, :Synvert), :Node)
105
+ # node.parent_const # s(:const, nil, :Synvert)
106
+ # @return [Parser::AST::Node] parent const of node.
38
107
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
39
108
  def parent_const
40
109
  if :const == type
@@ -44,9 +113,12 @@ module Parser::AST
44
113
  end
45
114
  end
46
115
 
47
- # Get receiver node of :send node.
48
- #
49
- # @return [Parser::AST::Node] receiver node.
116
+ # Get receiver of node.
117
+ # It support :csend and :send nodes.
118
+ # @example
119
+ # node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post))
120
+ # node.receiver # s(:const, nil, :FactoryGirl)
121
+ # @return [Parser::AST::Node] receiver of node.
50
122
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
51
123
  def receiver
52
124
  if %i[csend send].include?(type)
@@ -56,9 +128,12 @@ module Parser::AST
56
128
  end
57
129
  end
58
130
 
59
- # Get message node of :super or :send node.
60
- #
61
- # @return [Parser::AST::Node] mesage node.
131
+ # Get message of node.
132
+ # It support :csend, :send, :super and :zsuper nodes.
133
+ # @example
134
+ # node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post))
135
+ # node.message # :create
136
+ # @return [Symbol] mesage of node.
62
137
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
63
138
  def message
64
139
  case type
@@ -71,16 +146,19 @@ module Parser::AST
71
146
  end
72
147
  end
73
148
 
74
- # Get arguments node of :send, :block or :defined? node.
75
- #
76
- # @return [Array<Parser::AST::Node>] arguments node.
149
+ # Get arguments of node.
150
+ # It supports :block, :csend, :def, :defined?, :defs and :send nodes.
151
+ # @example
152
+ # node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post"))))
153
+ # node.arguments # [s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post")))]
154
+ # @return [Array<Parser::AST::Node>] arguments of node.
77
155
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
78
156
  def arguments
79
157
  case type
80
158
  when :def, :block
81
- children[1]
159
+ children[1].children
82
160
  when :defs
83
- children[2]
161
+ children[2].children
84
162
  when :send, :csend
85
163
  children[2..-1]
86
164
  when :defined?
@@ -90,9 +168,12 @@ module Parser::AST
90
168
  end
91
169
  end
92
170
 
93
- # Get caller node of :block node.
94
- #
95
- # @return [Parser::AST::Node] caller node.
171
+ # Get caller of node.
172
+ # It support :block node.
173
+ # @example
174
+ # node # s(:block, s(:send, s(:const, nil, :RSpec), :configure), s(:args, s(:arg, :config)), nil)
175
+ # node.caller # s(:send, s(:const, nil, :RSpec), :configure)
176
+ # @return [Parser::AST::Node] caller of node.
96
177
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
97
178
  def caller
98
179
  if :block == type
@@ -102,9 +183,12 @@ module Parser::AST
102
183
  end
103
184
  end
104
185
 
105
- # Get body node of :begin or :block node.
106
- #
107
- # @return [Array<Parser::AST::Node>] body node.
186
+ # Get body of node.
187
+ # It supports :begin, :block, :class, :def, :defs and :module node.
188
+ # @example
189
+ # node # s(:block, s(:send, s(:const, nil, :RSpec), :configure), s(:args, s(:arg, :config)), s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers)))
190
+ # node.body # [s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers))]
191
+ # @return [Array<Parser::AST::Node>] body of node.
108
192
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
109
193
  def body
110
194
  case type
@@ -123,9 +207,12 @@ module Parser::AST
123
207
  end
124
208
  end
125
209
 
126
- # Get condition node of :if node.
127
- #
128
- # @return [Parser::AST::Node] condition node.
210
+ # Get condition of node.
211
+ # It supports :if node.
212
+ # @example
213
+ # node # s(:if, s(:defined?, s(:const, nil, :Bundler)), nil, nil)
214
+ # node.condition # s(:defined?, s(:const, nil, :Bundler))
215
+ # @return [Parser::AST::Node] condition of node.
129
216
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
130
217
  def condition
131
218
  if :if == type
@@ -135,9 +222,11 @@ module Parser::AST
135
222
  end
136
223
  end
137
224
 
138
- # Get keys node of :hash node.
139
- #
140
- # @return [Array<Parser::AST::Node>] keys node.
225
+ # Get keys of :hash node.
226
+ # @example
227
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
228
+ # node.keys # [s(:sym, :foo), s(:str, "foo")]
229
+ # @return [Array<Parser::AST::Node>] keys of node.
141
230
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
142
231
  def keys
143
232
  if :hash == type
@@ -147,9 +236,11 @@ module Parser::AST
147
236
  end
148
237
  end
149
238
 
150
- # Get values node of :hash node.
151
- #
152
- # @return [Array<Parser::AST::Node>] values node.
239
+ # Get values of :hash node.
240
+ # @example
241
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
242
+ # node.values # [s(:sym, :bar), s(:str, "bar")]
243
+ # @return [Array<Parser::AST::Node>] values of node.
153
244
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
154
245
  def values
155
246
  if :hash == type
@@ -159,9 +250,11 @@ module Parser::AST
159
250
  end
160
251
  end
161
252
 
162
- # Test if hash node contains specified key.
163
- #
164
- # @param [Object] key value.
253
+ # Check if :hash node contains specified key.
254
+ # @example
255
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
256
+ # node.key?(:foo) # true
257
+ # @param [Symbol, String] key value.
165
258
  # @return [Boolean] true if specified key exists.
166
259
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
167
260
  def key?(key)
@@ -172,10 +265,12 @@ module Parser::AST
172
265
  end
173
266
  end
174
267
 
175
- # Get hash value node according to specified key.
176
- #
177
- # @param [Object] key value.
178
- # @return [Parser::AST::Node] value node.
268
+ # Get :hash value node according to specified key.
269
+ # @example
270
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
271
+ # node.hash_value(:foo) # s(:sym, :bar)
272
+ # @param [Symbol, String] key value.
273
+ # @return [Parser::AST::Node] hash value of node.
179
274
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
180
275
  def hash_value(key)
181
276
  if :hash == type
@@ -187,8 +282,10 @@ module Parser::AST
187
282
  end
188
283
 
189
284
  # Get key node of hash :pair node.
190
- #
191
- # @return [Parser::AST::Node] key node.
285
+ # @example
286
+ # node # s(:pair, s(:sym, :foo), s(:str, "bar"))
287
+ # node.key # s(:sym, :foo)
288
+ # @return [Parser::AST::Node] key of node.
192
289
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
193
290
  def key
194
291
  if :pair == type
@@ -199,8 +296,10 @@ module Parser::AST
199
296
  end
200
297
 
201
298
  # Get value node of hash :pair node.
202
- #
203
- # @return [Parser::AST::Node] value node.
299
+ # @example
300
+ # node # s(:pair, s(:sym, :foo), s(:str, "bar"))
301
+ # node.value # s(:str, "bar")
302
+ # @return [Parser::AST::Node] value of node.
204
303
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
205
304
  def value
206
305
  if :pair == type
@@ -210,9 +309,12 @@ module Parser::AST
210
309
  end
211
310
  end
212
311
 
213
- # Return the left value.
214
- #
215
- # @return [Parser::AST::Node] variable nodes.
312
+ # Return the left value of node.
313
+ # It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
314
+ # @example
315
+ # node # s(:masgn, s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b)), s(:array, s(:int, 1), s(:int, 2)))
316
+ # node.left_value # s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b))
317
+ # @return [Parser::AST::Node] left value of node.
216
318
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
217
319
  def left_value
218
320
  if %i[masgn lvasgn ivasgn cvasgn and or].include? type
@@ -224,9 +326,12 @@ module Parser::AST
224
326
  end
225
327
  end
226
328
 
227
- # Return the right value.
228
- #
229
- # @return [Array<Parser::AST::Node>] variable nodes.
329
+ # Return the right value of node.
330
+ # It supports :cvasgn, :ivasgn, :lvasgn, :masgn, :or and :or_asgn nodes.
331
+ # @example
332
+ # node # s(:masgn, s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b)), s(:array, s(:int, 1), s(:int, 2)))
333
+ # node.right_value # s(:array, s(:int, 1), s(:int, 2))
334
+ # @return [Array<Parser::AST::Node>] right value of node.
230
335
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
231
336
  def right_value
232
337
  if %i[masgn lvasgn ivasgn cvasgn or_asgn and or].include? type
@@ -236,8 +341,11 @@ module Parser::AST
236
341
  end
237
342
  end
238
343
 
239
- # Return the exact value.
240
- #
344
+ # Return the exact value of node.
345
+ # It supports :array, :begin, :erange, :false, :float, :irange, :int, :str, :sym and :true nodes.
346
+ # @example
347
+ # node # s(:array, s(:str, "str"), s(:sym, :str))
348
+ # node.to_value # ['str', :str]
241
349
  # @return [Object] exact value.
242
350
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
243
351
  def to_value
@@ -248,6 +356,8 @@ module Parser::AST
248
356
  true
249
357
  when :false
250
358
  false
359
+ when :nil
360
+ nil
251
361
  when :array
252
362
  children.map(&:to_value)
253
363
  when :irange
@@ -261,10 +371,11 @@ module Parser::AST
261
371
  end
262
372
  end
263
373
 
264
- # Respond key value for hash node, e.g.
265
- #
266
- # Current node is s(:hash, s(:pair, s(:sym, :number), s(:int, 10)))
267
- # node.number_value is 10
374
+ # Respond key value and source for hash node, e.g.
375
+ # @example
376
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
377
+ # node.foo_value # :bar
378
+ # node.foo_source # ":bar"
268
379
  def method_missing(method_name, *args, &block)
269
380
  if :args == type && children.respond_to?(method_name)
270
381
  return children.send(method_name, *args, &block)
@@ -299,12 +410,9 @@ module Parser::AST
299
410
  super
300
411
  end
301
412
 
302
- def to_s
303
- if :mlhs == type
304
- "(#{children.map(&:name).join(', ')})"
305
- end
306
- end
307
-
413
+ # Return the debug info.
414
+ #
415
+ # @return [String] file, line, source and node.
308
416
  def debug_info
309
417
  "\n" +
310
418
  [
@@ -315,64 +423,49 @@ module Parser::AST
315
423
  ].join("\n")
316
424
  end
317
425
 
318
- # Get the file name of the current node.
426
+ # Get the file name of node.
319
427
  #
320
428
  # @return [String] file name.
321
429
  def filename
322
430
  loc.expression&.source_buffer.name
323
431
  end
324
432
 
325
- # Get the source code of current node.
433
+ # Get the source code of node.
326
434
  #
327
435
  # @return [String] source code.
328
436
  def to_source
329
437
  loc.expression&.source
330
438
  end
331
439
 
332
- # Get the column of current node.
440
+ # Get the column of node.
333
441
  #
334
442
  # @return [Integer] column.
335
443
  def column
336
444
  loc.expression.column
337
445
  end
338
446
 
339
- # Get the line of current node.
447
+ # Get the line of node.
340
448
  #
341
449
  # @return [Integer] line.
342
450
  def line
343
451
  loc.expression.line
344
452
  end
345
453
 
346
- # Get child node by child name.
454
+ # Get child node by the name.
347
455
  #
348
- # @param [String] name of child node.
456
+ # @param child_name [String] name of child node.
349
457
  # @return [Parser::AST::Node] the child node.
350
458
  def child_node_by_name(child_name)
351
459
  direct_child_name, nested_child_name = child_name.to_s.split('.', 2)
352
460
  if respond_to?(direct_child_name)
353
461
  child_node = send(direct_child_name)
354
462
 
355
- if nested_child_name
356
- if child_node.is_a?(Array)
357
- child_direct_child_name, child_nested_child_name = nested_child_name.split('.', 2)
358
- child_direct_child_node = child_direct_child_name =~ /\A\d+\z/ ? child_node[child_direct_child_name.to_i - 1] : child_node.send(child_direct_child_name)
359
- return child_direct_child_node.child_node_by_name(child_nested_child_name) if child_nested_child_name
360
- return child_direct_child_node if child_direct_child_node
361
-
362
- raise Synvert::Core::MethodNotSupported,
363
- "child_node_by_name is not handled for #{debug_info}, child_name: #{child_name}"
364
- end
365
-
366
- return child_node.child_node_by_name(nested_child_name)
367
- end
463
+ return child_node.child_node_by_name(nested_child_name) if nested_child_name
368
464
 
369
465
  return nil if child_node.nil?
370
466
 
371
467
  return child_node if child_node.is_a?(Parser::AST::Node)
372
468
 
373
- # arguments
374
- return nil if child_node.empty?
375
-
376
469
  return child_node
377
470
  end
378
471
 
@@ -382,12 +475,16 @@ module Parser::AST
382
475
 
383
476
  # Get the source range of child node.
384
477
  #
385
- # @param [String] name of child node.
478
+ # @param child_name [String] name of child node.
386
479
  # @return [Parser::Source::Range] source range of child node.
387
480
  def child_node_range(child_name)
388
481
  case [type, child_name.to_sym]
389
482
  when %i[block pipes], %i[def parentheses], %i[defs parentheses]
390
- Parser::Source::Range.new('(string)', arguments.loc.expression.begin_pos, arguments.loc.expression.end_pos)
483
+ Parser::Source::Range.new(
484
+ '(string)',
485
+ arguments.first.loc.expression.begin_pos - 1,
486
+ arguments.last.loc.expression.end_pos + 1
487
+ )
391
488
  when %i[block arguments], %i[def arguments], %i[defs arguments]
392
489
  Parser::Source::Range.new(
393
490
  '(string)',
@@ -417,28 +514,7 @@ module Parser::AST
417
514
  if respond_to?(direct_child_name)
418
515
  child_node = send(direct_child_name)
419
516
 
420
- if nested_child_name
421
- if child_node.is_a?(Array)
422
- child_direct_child_name, child_nested_child_name = nested_child_name.split('.', 2)
423
- child_direct_child_node = child_direct_child_name =~ /\A\d+\z/ ? child_node[child_direct_child_name.to_i - 1] : child_node.send(child_direct_child_name)
424
- if child_nested_child_name
425
- return child_direct_child_node.child_node_range(child_nested_child_name)
426
- elsif child_direct_child_node
427
- return (
428
- Parser::Source::Range.new(
429
- '(string)',
430
- child_direct_child_node.loc.expression.begin_pos,
431
- child_direct_child_node.loc.expression.end_pos
432
- )
433
- )
434
- else
435
- raise Synvert::Core::MethodNotSupported,
436
- "child_node_range is not handled for #{debug_info}, child_name: #{child_name}"
437
- end
438
- end
439
-
440
- return child_node.child_node_range(nested_child_name)
441
- end
517
+ return child_node.child_node_range(nested_child_name) if nested_child_name
442
518
 
443
519
  return nil if child_node.nil?
444
520
 
@@ -469,7 +545,7 @@ module Parser::AST
469
545
  end
470
546
  end
471
547
 
472
- # Recursively iterate all child nodes of current node.
548
+ # Recursively iterate all child nodes of node.
473
549
  #
474
550
  # @yield [child] Gives a child node.
475
551
  # @yieldparam child [Parser::AST::Node] child node
@@ -482,8 +558,12 @@ module Parser::AST
482
558
  end
483
559
  end
484
560
 
485
- # Match current node with rules.
486
- #
561
+ # Match node with rules.
562
+ # It provides some additional keywords to match rules, +any+, +contain+, +not+, +in+, +not_in+, +gt+, +gte+, +lt+, +lte+.
563
+ # @example
564
+ # type: 'send', arguments: { any: 'Lifo::ShowExceptions' }
565
+ # type: { in: ['send', 'csend'] }
566
+ # type: :send, arguments: { length: { gt: 2 } }
487
567
  # @param rules [Hash] rules to match.
488
568
  # @return true if matches.
489
569
  def match?(rules)
@@ -517,11 +597,10 @@ module Parser::AST
517
597
 
518
598
  # Get rewritten source code.
519
599
  # @example
520
- # node.rewritten_source("create({{arguments}})") #=> "create(:post)"
521
- #
600
+ # node.rewritten_source("create({{arguments}})") # "create(:post)"
522
601
  # @param code [String] raw code.
523
- # @return [String] rewritten code, replace string in block {{ }} in raw code.
524
- # @raise [Synvert::Core::MethodNotSupported] if string in block {{ }} does not support.
602
+ # @return [String] rewritten code, replace string in block !{{ }} in raw code.
603
+ # @raise [Synvert::Core::MethodNotSupported] if string in block !{{ }} does not support.
525
604
  def rewritten_source(code)
526
605
  code.gsub(/{{(.*?)}}/m) do
527
606
  old_code = Regexp.last_match(1)
@@ -563,47 +642,71 @@ module Parser::AST
563
642
  end
564
643
  end
565
644
 
566
- # strip curly braces for hash
645
+ # Strip curly braces for hash.
646
+ # @example
647
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:str, "bar")))
648
+ # node.strip_curly_braces # "foo: 'bar'"
649
+ # @return [String]
567
650
  def strip_curly_braces
568
651
  return to_source unless type == :hash
569
652
 
570
653
  to_source.sub(/^{(.*)}$/) { Regexp.last_match(1).strip }
571
654
  end
572
655
 
573
- # wrap curly braces for hash
656
+ # Wrap curly braces for hash.
657
+ # @example
658
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:str, "bar")))
659
+ # node.wrap_curly_braces # "{ foo: 'bar' }"
660
+ # @return [String]
574
661
  def wrap_curly_braces
575
662
  return to_source unless type == :hash
576
663
 
577
664
  "{ #{to_source} }"
578
665
  end
579
666
 
580
- # get single quote string
667
+ # Get single quote string.
668
+ # @example
669
+ # node # s(:str, "foobar")
670
+ # node.to_single_quote # "'foobar'"
671
+ # @return [String]
581
672
  def to_single_quote
582
673
  return to_source unless type == :str
583
674
 
584
675
  "'#{to_value}'"
585
676
  end
586
677
 
587
- # convert string to symbol
678
+ # Convert string to symbol.
679
+ # @example
680
+ # node # s(:str, "foobar")
681
+ # node.to_symbol # ":foobar"
682
+ # @return [String]
588
683
  def to_symbol
589
684
  return to_source unless type == :str
590
685
 
591
686
  ":#{to_value}"
592
687
  end
593
688
 
594
- # convert symbol to string
689
+ # Convert symbol to string.
690
+ # @example
691
+ # node # s(:sym, :foobar)
692
+ # node.to_string # "foobar"
693
+ # @return [String]
595
694
  def to_string
596
695
  return to_source unless type == :sym
597
696
 
598
697
  to_value.to_s
599
698
  end
600
699
 
601
- # convert lambda {} to -> {}
700
+ # Convert lambda {} to -> {}
701
+ # @example
702
+ # node # s(:block, s(:send, nil, :lambda), s(:args), s(:send, nil, :foobar))
703
+ # node.to_lambda_literal # "-> { foobar }"
704
+ # @return [String]
602
705
  def to_lambda_literal
603
706
  if type == :block && caller.type == :send && caller.receiver.nil? && caller.message == :lambda
604
707
  new_source = to_source
605
708
  if arguments.size > 1
606
- new_source = new_source[0...arguments.loc.begin.to_range.begin - 1] + new_source[arguments.loc.end.to_range.end..-1]
709
+ new_source = new_source[0...arguments.first.loc.expression.begin_pos - 2] + new_source[arguments.last.loc.expression.end_pos + 1..-1]
607
710
  new_source = new_source.sub('lambda', "->(#{arguments.map(&:to_source).join(', ')})")
608
711
  else
609
712
  new_source = new_source.sub('lambda', '->')
@@ -620,7 +723,7 @@ module Parser::AST
620
723
  #
621
724
  # @param actual [Object] actual value.
622
725
  # @param expected [Object] expected value.
623
- # @return [Integer] -1, 0 or 1.
726
+ # @return [Boolean]
624
727
  # @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
625
728
  def match_value?(actual, expected)
626
729
  return true if actual == expected
@@ -678,7 +781,7 @@ module Parser::AST
678
781
  #
679
782
  # @example
680
783
  # flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
681
- # #=> {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
784
+ # # {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
682
785
  # @param h [Hash] original hash.
683
786
  # @return flatten hash.
684
787
  def flat_hash(h, k = [])
@@ -695,8 +798,7 @@ module Parser::AST
695
798
 
696
799
  # Get actual value from the node.
697
800
  #
698
- # @param node [Parser::AST::Node]
699
- # @param multi_keys [Array<Symbol>]
801
+ # @param multi_keys [Array<Symbol, String>]
700
802
  # @return [Object] actual value.
701
803
  def actual_value(multi_keys)
702
804
  multi_keys.inject(self) { |n, key| n.send(key) if n }
@@ -711,6 +813,7 @@ module Parser::AST
711
813
  multi_keys.inject(rules) { |o, key| o[key] }
712
814
  end
713
815
 
816
+ # Wrap the string with single or double quote.
714
817
  def wrap_quote(string)
715
818
  if string.include?("'")
716
819
  "\"#{string}\""
@@ -719,6 +822,7 @@ module Parser::AST
719
822
  end
720
823
  end
721
824
 
825
+ # Unwrap the quote from the string.
722
826
  def unwrap_quote(string)
723
827
  if (string[0] == '"' && string[-1] == '"') || (string[0] == "'" && string[-1] == "'")
724
828
  string[1...-1]
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synvert::Core::NodeQuery::Compiler
4
+ # Array represents a ruby array value.
5
+ class Array
6
+ include Comparable
7
+
8
+ # Initialize an Array.
9
+ # @param value the first value of the array
10
+ # @param rest the rest value of the array
11
+ def initialize(value: nil, rest: nil)
12
+ @value = value
13
+ @rest = rest
14
+ end
15
+
16
+ # Get the expected value.
17
+ # @return [Array]
18
+ def expected_value
19
+ expected = []
20
+ expected.push(@value) if @value
21
+ expected += @rest.expected_value if @rest
22
+ expected
23
+ end
24
+
25
+ # Get valid operators.
26
+ def valid_operators
27
+ ARRAY_VALID_OPERATORS
28
+ end
29
+
30
+ def to_s
31
+ [@value, @rest].compact.join(', ')
32
+ end
33
+ end
34
+ end