Class: Sexp

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_parser/bm_sexp.rb

Overview

Sexp changes from ruby_parser and some changes for caching hash value and tracking ‘original’ line number of a Sexp.

Constant Summary collapse

ASSIGNMENT_BOOL =
[:gasgn, :iasgn, :lasgn, :cvdecl, :cvasgn, :cdecl, :or, :and, :colon2, :op_asgn_or]
CALLS =
[:call, :attrasgn, :safe_call, :safe_attrasgn]

Instance Attribute Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/ruby_parser/bm_sexp.rb', line 9

def method_missing name, *args
  #Brakeman does not use this functionality,
  #so overriding it to raise a NoMethodError.
  #
  #The original functionality calls find_node and optionally
  #deletes the node if found.
  #
  #Defining a method named "return" seems like a bad idea, so we have to
  #check for it here instead
  if name == :return
    find_node name, *args
  else
    raise NoMethodError.new("No method '#{name}' for Sexp", name, args)
  end
end

Instance Attribute Details

#or_depthObject

Returns the value of attribute or_depth.



5
6
7
# File 'lib/ruby_parser/bm_sexp.rb', line 5

def or_depth
  @or_depth
end

#original_lineObject

Returns the value of attribute original_line.



5
6
7
# File 'lib/ruby_parser/bm_sexp.rb', line 5

def original_line
  @original_line
end

Instance Method Details

#<<(arg) ⇒ Object



96
97
98
99
# File 'lib/ruby_parser/bm_sexp.rb', line 96

def << arg
  @my_hash_value = nil
  old_push arg
end

#arglistObject

Returns arglist for method call. This differs from Sexp#args, as Sexp#args does not return a ‘real’ Sexp (it does not have a node type) but Sexp#arglist returns a s(:arglist, …)

s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
                                             ^------------ arglist ------------^


196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ruby_parser/bm_sexp.rb', line 196

def arglist
  expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper

  case self.node_type
  when :call, :attrasgn, :safe_call, :safe_attrasgn
    self.sexp_body(3).unshift :arglist
  when :super, :zsuper
    if self[1]
      self.sexp_body.unshift :arglist
    else
      Sexp.new(:arglist)
    end
  end
end

#arglist=(exp) ⇒ Object

Sets the arglist in a method call.



172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/ruby_parser/bm_sexp.rb', line 172

def arglist= exp
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  @my_hash_value = nil
  start_index = 3

  if exp.is_a? Sexp and exp.node_type == :arglist
    exp = exp.sexp_body
  end

  exp.each_with_index do |e, i|
    self[start_index + i] = e
  end
end

#argsObject

Returns arguments of a method call. This will be an ‘untyped’ Sexp.

s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
                                                         ^--------args--------^


215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/ruby_parser/bm_sexp.rb', line 215

def args
  expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper

  case self.node_type
  when :call, :attrasgn, :safe_call, :safe_attrasgn
    if self[3]
      self.sexp_body(3)
    else
      Sexp.new
    end
  when :super, :zsuper
    if self[1]
      self.sexp_body
    else
      Sexp.new
    end
  end
end

#block(delete = nil) ⇒ Object

Returns block of a call with a block. Could be a single expression or a block:

s(:iter,
 s(:call, nil, :x, s(:arglist)),
  s(:lasgn, :y),
   s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
   ^-------------------- block --------------------------^


390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/ruby_parser/bm_sexp.rb', line 390

def block delete = nil
  unless delete.nil? #this is from RubyParser
    return find_node :block, delete
  end

  expect :iter, :scope, :resbody

  case self.node_type
  when :iter
    self[3]
  when :scope
    self[1]
  when :resbody
    #This is for Ruby2Ruby ONLY
    find_node :block
  end
end

#block_argsObject

Returns parameters for a block

s(:iter,
 s(:call, nil, :x, s(:arglist)),
  s(:lasgn, :y), <- block_args
   s(:call, nil, :p, s(:arglist, s(:lvar, :y))))


414
415
416
417
418
419
420
421
# File 'lib/ruby_parser/bm_sexp.rb', line 414

def block_args
  expect :iter
  if self[2] == 0 # ?! See https://github.com/presidentbeef/brakeman/issues/331
    return Sexp.new(:args)
  else
    self[2]
  end
end

#block_callObject

Method call associated with a block:

s(:iter,
 s(:call, nil, :x, s(:arglist)), <- block_call
  s(:lasgn, :y),
   s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))


372
373
374
375
376
377
378
379
380
# File 'lib/ruby_parser/bm_sexp.rb', line 372

def block_call
  expect :iter

  if self[1].node_type == :lambda
    s(:call, nil, :lambda).line(self.line)
  else
    self[1]
  end
end

#bodyObject

Returns body of a method definition, class, or module. This will be an untyped Sexp containing a list of Sexps from the body.



527
528
529
530
531
532
533
534
535
536
537
538
# File 'lib/ruby_parser/bm_sexp.rb', line 527

def body
  expect :defn, :defs, :class, :module

  case self.node_type
  when :defn, :class
    self.sexp_body(3)
  when :defs
    self.sexp_body(4)
  when :module
    self.sexp_body(2)
  end
end

#body=(exp) ⇒ Object

Sets body, which is now a complicated process because the body is no longer a separate Sexp, but just a list of Sexps.



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
# File 'lib/ruby_parser/bm_sexp.rb', line 499

def body= exp
  expect :defn, :defs, :class, :module
  @my_hash_value = nil

  case self.node_type
  when :defn, :class
    index = 3
  when :defs
    index = 4
  when :module
    index = 2
  end

  self.slice!(index..-1) #Remove old body

  if exp.first == :rlist
    exp = exp.sexp_body
  end

  #Insert new body
  exp.each do |e|
    self[index] = e
    index += 1
  end
end

#body_listObject

Like Sexp#body, except the returned Sexp is of type :rlist instead of untyped.



542
543
544
# File 'lib/ruby_parser/bm_sexp.rb', line 542

def body_list
  self.body.unshift :rlist
end

#callObject

Returns the call Sexp in a result returned from FindCall



578
579
580
581
582
# File 'lib/ruby_parser/bm_sexp.rb', line 578

def call
  expect :result

  self.last
end

#call_chainObject



311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/ruby_parser/bm_sexp.rb', line 311

def call_chain
  expect :call, :attrasgn, :safe_call, :safe_attrasgn

  chain = []
  call = self

  while call.class == Sexp and CALLS.include? call.first 
    chain << call.method
    call = call.target
  end

  chain.reverse!
  chain
end

#class_nameObject Also known as: module_name



565
566
567
568
# File 'lib/ruby_parser/bm_sexp.rb', line 565

def class_name
  expect :class, :module
  self[1]
end

#combine(exp, line = nil) ⇒ Object

Join self and exp into an :or Sexp. Sets or_depth. Used for combining “branched” values in AliasProcessor.



80
81
82
83
84
85
86
# File 'lib/ruby_parser/bm_sexp.rb', line 80

def combine exp, line = nil
  combined = Sexp.new(:or, self, exp).line(line || -2)

  combined.or_depth = [self.or_depth, exp.or_depth].compact.reduce(0, :+) + 1

  combined
end

#compactObject



109
110
111
112
# File 'lib/ruby_parser/bm_sexp.rb', line 109

def compact
  @my_hash_value = nil
  old_compact
end

#conditionObject

Returns condition of an if expression:

s(:if,
 s(:lvar, :condition), <-- condition
 s(:lvar, :then_val),
 s(:lvar, :else_val)))


332
333
334
335
# File 'lib/ruby_parser/bm_sexp.rb', line 332

def condition
  expect :if
  self[1]
end

#condition=(exp) ⇒ Object



337
338
339
340
# File 'lib/ruby_parser/bm_sexp.rb', line 337

def condition= exp
  expect :if
  self[1] = exp
end

#deep_clone(line = nil) ⇒ Object

Create clone of Sexp and nested Sexps but not their non-Sexp contents. If a line number is provided, also sets line/original_line on all Sexps.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/ruby_parser/bm_sexp.rb', line 27

def deep_clone line = nil
  s = Sexp.new

  self.each do |e|
    if e.is_a? Sexp
      s << e.deep_clone(line)
    else
      s << e
    end
  end

  if line
    s.original_line = self.original_line || self.line
    s.line(line)
  else
    s.original_line = self.original_line
    s.line(self.line) if self.line
  end

  s
end

#each_arg(replace = false) ⇒ Object



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/ruby_parser/bm_sexp.rb', line 234

def each_arg replace = false
  expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
  range = nil

  case self.node_type
  when :call, :attrasgn, :safe_call, :safe_attrasgn
    if self[3]
      range = (3...self.length)
    end
  when :super, :zsuper
    if self[1]
      range = (1...self.length)
    end
  end

  if range
    range.each do |i|
      res = yield self[i]
      self[i] = res if replace
    end
  end

  self
end

#each_arg!(&block) ⇒ Object



259
260
261
262
# File 'lib/ruby_parser/bm_sexp.rb', line 259

def each_arg! &block
  @my_hash_value = nil
  self.each_arg true, &block
end

#else_clauseObject

Returns ‘else’ clause of an if expression:

s(:if,
 s(:lvar, :condition),
 s(:lvar, :then_val),
 s(:lvar, :else_val)))
 ^---else caluse---^


361
362
363
364
# File 'lib/ruby_parser/bm_sexp.rb', line 361

def else_clause
  expect :if
  self[3]
end

#expect(*types) ⇒ Object

Raise a WrongSexpError if the nodes type does not match one of the expected types.



126
127
128
129
130
# File 'lib/ruby_parser/bm_sexp.rb', line 126

def expect *types
  unless types.include? self.node_type
    raise WrongSexpError, "Expected #{types.join ' or '} but given #{self.inspect}", caller[1..-1]
  end
end

#find_and_replace_all(*args) ⇒ Object



114
115
116
117
# File 'lib/ruby_parser/bm_sexp.rb', line 114

def find_and_replace_all *args
  @my_hash_value = nil
  old_fara(*args)
end

#find_node(*args) ⇒ Object



119
120
121
122
# File 'lib/ruby_parser/bm_sexp.rb', line 119

def find_node *args
  @my_hash_value = nil
  old_find_node(*args)
end

#first_argObject

Returns first argument of a method call.



265
266
267
268
# File 'lib/ruby_parser/bm_sexp.rb', line 265

def first_arg
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  self[3]
end

#first_arg=(exp) ⇒ Object

Sets first argument of a method call.



271
272
273
274
275
# File 'lib/ruby_parser/bm_sexp.rb', line 271

def first_arg= exp
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  @my_hash_value = nil
  self[3] = exp
end

#first_paramObject



423
424
425
426
# File 'lib/ruby_parser/bm_sexp.rb', line 423

def first_param
  expect :args
  self[1]
end

#formal_argsObject



486
487
488
489
490
491
492
493
494
495
# File 'lib/ruby_parser/bm_sexp.rb', line 486

def formal_args
  expect :defn, :defs

  case self.node_type
  when :defn
    self[2]
  when :defs
    self[3]
  end
end

#hashObject



101
102
103
104
105
106
107
# File 'lib/ruby_parser/bm_sexp.rb', line 101

def hash
  #There still seems to be some instances in which the hash of the
  #Sexp changes, but I have not found what method call is doing it.
  #Of course, Sexp is subclasses from Array, so who knows what might
  #be going on.
  @my_hash_value ||= super
end

#inspect(seen = Set.new) ⇒ Object



599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
# File 'lib/ruby_parser/bm_sexp.rb', line 599

def inspect seen = Set.new
  if seen.include? self.object_id
    's(...)'
  else
    seen << self.object_id
    sexp_str = self.map do |x|
      if x.is_a? Sexp
        x.inspect seen
      else
        x.inspect
      end
    end.join(', ')

    "s(#{sexp_str})"
  end
end

#last_argObject



301
302
303
304
305
306
307
308
309
# File 'lib/ruby_parser/bm_sexp.rb', line 301

def last_arg
  expect :call, :attrasgn, :safe_call, :safe_attrasgn

  if self[3]
    self[-1]
  else
    nil
  end
end

#lhsObject

Returns the left hand side of assignment or boolean:

s(:lasgn, :x, s(:lit, 1))
           ^--lhs


432
433
434
435
# File 'lib/ruby_parser/bm_sexp.rb', line 432

def lhs
  expect(*ASSIGNMENT_BOOL)
  self[1]
end

#lhs=(exp) ⇒ Object

Sets the left hand side of assignment or boolean.



438
439
440
441
442
# File 'lib/ruby_parser/bm_sexp.rb', line 438

def lhs= exp
  expect(*ASSIGNMENT_BOOL)
  @my_hash_value = nil
  self[1] = exp
end

#methodObject

Returns method of a method call:

s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))

^- method


152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/ruby_parser/bm_sexp.rb', line 152

def method
  expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper, :result

  case self.node_type
  when :call, :attrasgn, :safe_call, :safe_attrasgn
    self[2]
  when :super, :zsuper
    :super
  when :result
    self.last
  end
end

#method=(name) ⇒ Object



165
166
167
168
169
# File 'lib/ruby_parser/bm_sexp.rb', line 165

def method= name
  expect :call, :safe_call

  self[2] = name
end

#method_lengthObject

Number of “statements” in a method. This is more efficient than ‘Sexp#body.length` because `Sexp#body` creates a new Sexp.



549
550
551
552
553
554
555
556
557
558
# File 'lib/ruby_parser/bm_sexp.rb', line 549

def method_length
  expect :defn, :defs

  case self.node_type
  when :defn
    self.length - 3
  when :defs
    self.length - 4
  end
end

#method_nameObject

Returns name of method being defined in a method definition.



475
476
477
478
479
480
481
482
483
484
# File 'lib/ruby_parser/bm_sexp.rb', line 475

def method_name
  expect :defn, :defs

  case self.node_type
  when :defn
    self[1]
  when :defs
    self[2]
  end
end

#moduleObject

Returns the module the call is inside



585
586
587
588
589
# File 'lib/ruby_parser/bm_sexp.rb', line 585

def module
  expect :result

  self[1]
end

#node_type=(type) ⇒ Object



72
73
74
75
# File 'lib/ruby_parser/bm_sexp.rb', line 72

def node_type= type
  @my_hash_value = nil
  self[0] = type
end

#old_compactObject



92
# File 'lib/ruby_parser/bm_sexp.rb', line 92

alias :old_compact :compact

#old_faraObject



93
# File 'lib/ruby_parser/bm_sexp.rb', line 93

alias :old_fara :find_and_replace_all

#old_find_nodeObject



94
# File 'lib/ruby_parser/bm_sexp.rb', line 94

alias :old_find_node :find_node

#old_pushObject



91
# File 'lib/ruby_parser/bm_sexp.rb', line 91

alias :old_push :<<

#parenObject



49
50
51
# File 'lib/ruby_parser/bm_sexp.rb', line 49

def paren
  @paren ||= false
end

#parent_nameObject



572
573
574
575
# File 'lib/ruby_parser/bm_sexp.rb', line 572

def parent_name
  expect :class
  self[2]
end

#render_typeObject



560
561
562
563
# File 'lib/ruby_parser/bm_sexp.rb', line 560

def render_type
  expect :render
  self[1]
end

#result_classObject

Return the class the call is inside



592
593
594
595
596
# File 'lib/ruby_parser/bm_sexp.rb', line 592

def result_class
  expect :result

  self[2]
end

#rhsObject

Returns right side (value) of assignment or boolean:

s(:lasgn, :x, s(:lit, 1))
              ^--rhs---^


448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/ruby_parser/bm_sexp.rb', line 448

def rhs
  expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL

  if self.node_type == :attrasgn or self.node_type == :safe_attrasgn
    if self[2] == :[]=
      self[4]
    else
      self[3]
    end
  else
    self[2]
  end
end

#rhs=(exp) ⇒ Object

Sets the right hand side of assignment or boolean.



463
464
465
466
467
468
469
470
471
472
# File 'lib/ruby_parser/bm_sexp.rb', line 463

def rhs= exp
  expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL
  @my_hash_value = nil

  if self.node_type == :attrasgn or self.node_type == :safe_attrasgn
    self[3] = exp
  else
    self[2] = exp
  end
end

#secondObject



64
65
66
# File 'lib/ruby_parser/bm_sexp.rb', line 64

def second
  self[1]
end

#second_argObject

Returns second argument of a method call.



278
279
280
281
# File 'lib/ruby_parser/bm_sexp.rb', line 278

def second_arg
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  self[4]
end

#second_arg=(exp) ⇒ Object

Sets second argument of a method call.



284
285
286
287
288
# File 'lib/ruby_parser/bm_sexp.rb', line 284

def second_arg= exp
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  @my_hash_value = nil
  self[4] = exp
end

#set_args(*exp) ⇒ Object



186
187
188
# File 'lib/ruby_parser/bm_sexp.rb', line 186

def set_args *exp
  self.arglist = exp
end

#targetObject

Returns target of a method call:

s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))

^-----------target-----------^


136
137
138
139
# File 'lib/ruby_parser/bm_sexp.rb', line 136

def target
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  self[1]
end

#target=(exp) ⇒ Object

Sets the target of a method call:



142
143
144
145
146
# File 'lib/ruby_parser/bm_sexp.rb', line 142

def target= exp
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  @my_hash_value = nil
  self[1] = exp
end

#then_clauseObject

Returns ‘then’ clause of an if expression:

s(:if,
 s(:lvar, :condition),
 s(:lvar, :then_val), <-- then clause
 s(:lvar, :else_val)))


349
350
351
352
# File 'lib/ruby_parser/bm_sexp.rb', line 349

def then_clause
  expect :if
  self[2]
end

#third_argObject



290
291
292
293
# File 'lib/ruby_parser/bm_sexp.rb', line 290

def third_arg
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  self[5]
end

#third_arg=(exp) ⇒ Object



295
296
297
298
299
# File 'lib/ruby_parser/bm_sexp.rb', line 295

def third_arg= exp
  expect :call, :attrasgn, :safe_call, :safe_attrasgn
  @my_hash_value = nil
  self[5] = exp
end

#to_symObject



68
69
70
# File 'lib/ruby_parser/bm_sexp.rb', line 68

def to_sym
  self.value.to_sym
end

#valueObject

Raises:



53
54
55
56
# File 'lib/ruby_parser/bm_sexp.rb', line 53

def value
  raise WrongSexpError, "Sexp#value called on multi-item Sexp: `#{self.inspect}`" if size > 2
  self[1]
end

#value=(exp) ⇒ Object

Raises:



58
59
60
61
62
# File 'lib/ruby_parser/bm_sexp.rb', line 58

def value= exp
  raise WrongSexpError, "Sexp#value= called on multi-item Sexp: `#{self.inspect}`" if size > 2
  @my_hash_value = nil
  self[1] = exp
end