synvert-core 0.63.1 → 1.0.1

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 (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