brakeman 1.8.3 → 1.9.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/README.md +3 -27
  2. data/lib/brakeman.rb +36 -38
  3. data/lib/brakeman/app_tree.rb +90 -0
  4. data/lib/brakeman/call_index.rb +5 -38
  5. data/lib/brakeman/checks.rb +11 -11
  6. data/lib/brakeman/checks/base_check.rb +53 -29
  7. data/lib/brakeman/checks/check_cross_site_scripting.rb +11 -9
  8. data/lib/brakeman/checks/check_evaluation.rb +1 -1
  9. data/lib/brakeman/checks/check_execute.rb +3 -3
  10. data/lib/brakeman/checks/check_link_to.rb +15 -13
  11. data/lib/brakeman/checks/check_link_to_href.rb +1 -1
  12. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  13. data/lib/brakeman/checks/check_mass_assignment.rb +27 -13
  14. data/lib/brakeman/checks/check_redirect.rb +4 -4
  15. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  16. data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
  17. data/lib/brakeman/checks/check_send.rb +2 -2
  18. data/lib/brakeman/checks/check_session_settings.rb +12 -5
  19. data/lib/brakeman/checks/check_single_quotes.rb +3 -3
  20. data/lib/brakeman/checks/check_skip_before_filter.rb +4 -3
  21. data/lib/brakeman/checks/check_sql.rb +30 -30
  22. data/lib/brakeman/checks/check_translate_bug.rb +11 -10
  23. data/lib/brakeman/checks/check_validation_regex.rb +36 -11
  24. data/lib/brakeman/checks/check_without_protection.rb +1 -1
  25. data/lib/brakeman/options.rb +6 -2
  26. data/lib/brakeman/processor.rb +6 -5
  27. data/lib/brakeman/processors/alias_processor.rb +153 -38
  28. data/lib/brakeman/processors/base_processor.rb +16 -21
  29. data/lib/brakeman/processors/controller_alias_processor.rb +24 -11
  30. data/lib/brakeman/processors/controller_processor.rb +25 -25
  31. data/lib/brakeman/processors/erb_template_processor.rb +6 -7
  32. data/lib/brakeman/processors/erubis_template_processor.rb +2 -3
  33. data/lib/brakeman/processors/gem_processor.rb +5 -4
  34. data/lib/brakeman/processors/haml_template_processor.rb +4 -6
  35. data/lib/brakeman/processors/lib/find_all_calls.rb +3 -3
  36. data/lib/brakeman/processors/lib/find_call.rb +2 -2
  37. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  38. data/lib/brakeman/processors/lib/processor_helper.rb +24 -2
  39. data/lib/brakeman/processors/lib/rails2_config_processor.rb +13 -14
  40. data/lib/brakeman/processors/lib/rails2_route_processor.rb +9 -4
  41. data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -8
  42. data/lib/brakeman/processors/lib/rails3_route_processor.rb +23 -21
  43. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  44. data/lib/brakeman/processors/library_processor.rb +2 -2
  45. data/lib/brakeman/processors/model_processor.rb +16 -12
  46. data/lib/brakeman/processors/output_processor.rb +2 -1
  47. data/lib/brakeman/processors/template_alias_processor.rb +12 -8
  48. data/lib/brakeman/report.rb +28 -14
  49. data/lib/brakeman/rescanner.rb +5 -5
  50. data/lib/brakeman/scanner.rb +56 -94
  51. data/lib/brakeman/templates/header.html.erb +7 -2
  52. data/lib/brakeman/tracker.rb +14 -4
  53. data/lib/brakeman/util.rb +38 -17
  54. data/lib/brakeman/version.rb +1 -1
  55. data/lib/brakeman/warning.rb +14 -6
  56. data/lib/ruby_parser/bm_sexp.rb +157 -57
  57. data/lib/ruby_parser/bm_sexp_processor.rb +1 -2
  58. metadata +26 -25
  59. data/lib/ruby_parser/ruby18_parser.rb +0 -5544
  60. data/lib/ruby_parser/ruby19_parser.rb +0 -5756
  61. data/lib/ruby_parser/ruby_lexer.rb +0 -1349
  62. data/lib/ruby_parser/ruby_parser.rb +0 -5
  63. data/lib/ruby_parser/ruby_parser_extras.rb +0 -1057
@@ -1,5 +0,0 @@
1
- $LOAD_PATH.unshift File.dirname(File.expand_path(__FILE__))
2
-
3
- require 'ruby18_parser'
4
- require 'ruby19_parser'
5
- require 'ruby_parser_extras'
@@ -1,1057 +0,0 @@
1
- require 'stringio'
2
- require 'racc/parser'
3
- require 'sexp'
4
- require 'strscan'
5
-
6
- # WHY do I have to do this?!?
7
- class Regexp
8
- ONCE = 0 unless defined? ONCE # FIX: remove this - it makes no sense
9
-
10
- unless defined? ENC_NONE then
11
- ENC_NONE = /x/n.options
12
- ENC_EUC = /x/e.options
13
- ENC_SJIS = /x/s.options
14
- ENC_UTF8 = /x/u.options
15
- end
16
- end
17
-
18
- # I hate ruby 1.9 string changes
19
- class Fixnum
20
- def ord
21
- self
22
- end
23
- end unless "a"[0] == "a"
24
-
25
- class RPStringScanner < StringScanner
26
- # if ENV['TALLY'] then
27
- # alias :old_getch :getch
28
- # def getch
29
- # warn({:getch => caller[0]}.inspect)
30
- # old_getch
31
- # end
32
- # end
33
- def current_line # HAHA fuck you (HACK)
34
- string[0..pos][/\A.*__LINE__/m].split(/\n/).size
35
- end
36
-
37
- def extra_lines_added
38
- @extra_lines_added ||= 0
39
- end
40
-
41
- def extra_lines_added= val
42
- @extra_lines_added = val
43
- end
44
-
45
- def lineno
46
- string[0...pos].count("\n") + 1 - extra_lines_added
47
- end
48
-
49
- # TODO: once we get rid of these, we can make things like
50
- # TODO: current_line and lineno much more accurate and easy to do
51
-
52
- def unread_many str # TODO: remove this entirely - we should not need it
53
- warn({:unread_many => caller[0]}.inspect) if ENV['TALLY']
54
- self.extra_lines_added += str.count("\n")
55
- string[pos, 0] = str
56
- end
57
-
58
- if ENV['DEBUG'] then
59
- alias :old_getch :getch
60
- def getch
61
- c = self.old_getch
62
- p :getch => [c, caller.first]
63
- c
64
- end
65
-
66
- alias :old_scan :scan
67
- def scan re
68
- s = old_scan re
69
- p :scan => [s, caller.first] if s
70
- s
71
- end
72
- end
73
-
74
- # TODO:
75
- # def last_line(src)
76
- # if n = src.rindex("\n")
77
- # src[(n+1) .. -1]
78
- # else
79
- # src
80
- # end
81
- # end
82
- # private :last_line
83
-
84
- # def next_words_on_error
85
- # if n = @src.rest.index("\n")
86
- # @src.rest[0 .. (n-1)]
87
- # else
88
- # @src.rest
89
- # end
90
- # end
91
-
92
- # def prev_words_on_error(ev)
93
- # pre = @pre
94
- # if ev and /#{Regexp.quote(ev)}$/ =~ pre
95
- # pre = $`
96
- # end
97
- # last_line(pre)
98
- # end
99
-
100
- # def on_error(et, ev, values)
101
- # lines_of_rest = @src.rest.to_a.length
102
- # prev_words = prev_words_on_error(ev)
103
- # at = 4 + prev_words.length
104
- # message = <<-MSG
105
- # RD syntax error: line #{@blockp.line_index - lines_of_rest}:
106
- # ...#{prev_words} #{(ev||'')} #{next_words_on_error()} ...
107
- # MSG
108
- # message << " " * at + "^" * (ev ? ev.length : 0) + "\n"
109
- # raise ParseError, message
110
- # end
111
- end
112
-
113
- module RubyParserStuff
114
- VERSION = '2.3.1' unless constants.include? "VERSION" # SIGH
115
-
116
- attr_accessor :lexer, :in_def, :in_single, :file
117
- attr_reader :env, :comments
118
-
119
- def arg_add(node1, node2) # TODO: nuke
120
- return s(:arglist, node2) unless node1
121
-
122
- node1[0] = :arglist if node1[0] == :array
123
- return node1 << node2 if node1[0] == :arglist
124
-
125
- return s(:arglist, node1, node2)
126
- end
127
-
128
- def arg_blk_pass node1, node2 # TODO: nuke
129
- node1 = s(:arglist, node1) unless [:arglist, :array].include? node1.first
130
- node1 << node2 if node2
131
- node1
132
- end
133
-
134
- def arg_concat node1, node2 # TODO: nuke
135
- raise "huh" unless node2
136
- node1 << s(:splat, node2).compact
137
- node1
138
- end
139
-
140
- def args arg, optarg, rest_arg, block_arg, post_arg = nil
141
- arg ||= s(:args)
142
-
143
- result = arg
144
- if optarg then
145
- optarg[1..-1].each do |lasgn| # FIX clean sexp iter
146
- raise "wtf? #{lasgn.inspect}" unless lasgn[0] == :lasgn
147
- result << lasgn[1]
148
- end
149
- end
150
-
151
- result << rest_arg if rest_arg
152
-
153
- result << :"&#{block_arg.last}" if block_arg
154
- result << optarg if optarg # TODO? huh - processed above as well
155
- post_arg[1..-1].each {|pa| result << pa } if post_arg
156
-
157
- result
158
- end
159
-
160
- def args19 vals # TODO: migrate to args once 1.8 tests pass as well
161
- result = s(:args)
162
- block = nil
163
-
164
- vals.each do |val|
165
- case val
166
- when Sexp then
167
- case val.first
168
- when :args then
169
- val[1..-1].each do |name|
170
- result << name
171
- end
172
- when :block_arg then
173
- result << :"&#{val.last}"
174
- when :block then
175
- block = val
176
- val[1..-1].each do |lasgn| # FIX clean sexp iter
177
- raise "wtf? #{val.inspect}" unless lasgn[0] == :lasgn
178
- result << lasgn[1]
179
- end
180
- else
181
- raise "unhandled sexp: #{val.inspect}"
182
- end
183
- when Symbol then
184
- result << val
185
- when ",", nil then
186
- # ignore
187
- else
188
- raise "unhandled val: #{val.inspect} in #{vals.inspect}"
189
- end
190
- end
191
-
192
- result << block if block
193
-
194
- result
195
- end
196
-
197
- def aryset receiver, index
198
- index[0] = :arglist if index[0] == :array
199
- s(:attrasgn, receiver, :"[]=", index)
200
- end
201
-
202
- def assignable(lhs, value = nil)
203
- id = lhs.to_sym
204
- id = id.to_sym if Sexp === id
205
-
206
- raise SyntaxError, "Can't change the value of #{id}" if
207
- id.to_s =~ /^(?:self|nil|true|false|__LINE__|__FILE__)$/
208
-
209
- result = case id.to_s
210
- when /^@@/ then
211
- asgn = in_def || in_single > 0
212
- s((asgn ? :cvasgn : :cvdecl), id)
213
- when /^@/ then
214
- s(:iasgn, id)
215
- when /^\$/ then
216
- s(:gasgn, id)
217
- when /^[A-Z]/ then
218
- s(:cdecl, id)
219
- else
220
- case self.env[id]
221
- when :lvar then
222
- s(:lasgn, id)
223
- when :dvar, nil then
224
- if self.env.current[id] == :dvar then
225
- s(:lasgn, id)
226
- elsif self.env[id] == :dvar then
227
- self.env.use(id)
228
- s(:lasgn, id)
229
- elsif ! self.env.dynamic? then
230
- s(:lasgn, id)
231
- else
232
- s(:lasgn, id)
233
- end
234
- else
235
- raise "wtf? unknown type: #{self.env[id]}"
236
- end
237
- end
238
-
239
- self.env[id] ||= :lvar
240
-
241
- result << value if value
242
-
243
- return result
244
- end
245
-
246
- def block_append(head, tail)
247
- return head if tail.nil?
248
- return tail if head.nil?
249
-
250
- case head[0]
251
- when :lit, :str then
252
- return tail
253
- end
254
-
255
- line = [head.line, tail.line].compact.min
256
-
257
- head = remove_begin(head)
258
- head = s(:block, head) unless head.node_type == :block
259
-
260
- head.line = line
261
- head << tail
262
- end
263
-
264
- def cond node
265
- return nil if node.nil?
266
- node = value_expr node
267
-
268
- case node.first
269
- when :lit then
270
- if Regexp === node.last then
271
- return s(:match, node)
272
- else
273
- return node
274
- end
275
- when :and then
276
- return s(:and, cond(node[1]), cond(node[2]))
277
- when :or then
278
- return s(:or, cond(node[1]), cond(node[2]))
279
- when :dot2 then
280
- label = "flip#{node.hash}"
281
- env[label] = :lvar
282
- return s(:flip2, node[1], node[2])
283
- when :dot3 then
284
- label = "flip#{node.hash}"
285
- env[label] = :lvar
286
- return s(:flip3, node[1], node[2])
287
- else
288
- return node
289
- end
290
- end
291
-
292
- ##
293
- # for pure ruby systems only
294
-
295
- def do_parse
296
- _racc_do_parse_rb(_racc_setup, false)
297
- end if ENV['PURE_RUBY']
298
-
299
- def get_match_node lhs, rhs # TODO: rename to new_match
300
- if lhs then
301
- case lhs[0]
302
- when :dregx, :dregx_once then
303
- return s(:match2, lhs, rhs).line(lhs.line)
304
- when :lit then
305
- return s(:match2, lhs, rhs).line(lhs.line) if Regexp === lhs.last
306
- end
307
- end
308
-
309
- if rhs then
310
- case rhs[0]
311
- when :dregx, :dregx_once then
312
- return s(:match3, rhs, lhs).line(lhs.line)
313
- when :lit then
314
- return s(:match3, rhs, lhs).line(lhs.line) if Regexp === rhs.last
315
- end
316
- end
317
-
318
- return s(:call, lhs, :"=~", s(:arglist, rhs)).line(lhs.line)
319
- end
320
-
321
- def gettable(id)
322
- id = id.to_sym if String === id
323
-
324
- result = case id.to_s
325
- when /^@@/ then
326
- s(:cvar, id)
327
- when /^@/ then
328
- s(:ivar, id)
329
- when /^\$/ then
330
- s(:gvar, id)
331
- when /^[A-Z]/ then
332
- s(:const, id)
333
- else
334
- type = env[id]
335
- if type then
336
- s(type, id)
337
- elsif env.dynamic? and :dvar == env[id] then
338
- s(:lvar, id)
339
- else
340
- s(:call, nil, id, s(:arglist))
341
- end
342
- end
343
-
344
- raise "identifier #{id.inspect} is not valid" unless result
345
-
346
- result
347
- end
348
-
349
- ##
350
- # Canonicalize conditionals. Eg:
351
- #
352
- # not x ? a : b
353
- #
354
- # becomes:
355
- #
356
- # x ? b : a
357
-
358
- attr_accessor :canonicalize_conditions
359
-
360
- def initialize(options = {})
361
- super()
362
-
363
- v = self.class.name[/1[89]/]
364
- self.lexer = RubyLexer.new v && v.to_i
365
- self.lexer.parser = self
366
- @env = Environment.new
367
- @comments = []
368
-
369
- @canonicalize_conditions = true
370
-
371
- self.reset
372
- end
373
-
374
- def list_append list, item # TODO: nuke me *sigh*
375
- return s(:array, item) unless list
376
- list = s(:array, list) unless Sexp === list && list.first == :array
377
- list << item
378
- end
379
-
380
- def list_prepend item, list # TODO: nuke me *sigh*
381
- list = s(:array, list) unless Sexp === list && list[0] == :array
382
- list.insert 1, item
383
- list
384
- end
385
-
386
- def literal_concat head, tail
387
- return tail unless head
388
- return head unless tail
389
-
390
- htype, ttype = head[0], tail[0]
391
-
392
- head = s(:dstr, '', head) if htype == :evstr
393
-
394
- case ttype
395
- when :str then
396
- if htype == :str
397
- head[-1] << tail[-1]
398
- elsif htype == :dstr and head.size == 2 then
399
- head[-1] << tail[-1]
400
- else
401
- head << tail
402
- end
403
- when :dstr then
404
- if htype == :str then
405
- tail[1] = head[-1] + tail[1]
406
- head = tail
407
- else
408
- tail[0] = :array
409
- tail[1] = s(:str, tail[1])
410
- tail.delete_at 1 if tail[1] == s(:str, '')
411
-
412
- head.push(*tail[1..-1])
413
- end
414
- when :evstr then
415
- head[0] = :dstr if htype == :str
416
- if head.size == 2 and tail.size > 1 and tail[1][0] == :str then
417
- head[-1] << tail[1][-1]
418
- head[0] = :str if head.size == 2 # HACK ?
419
- else
420
- head.push(tail)
421
- end
422
- else
423
- x = [head, tail]
424
- raise "unknown type: #{x.inspect}"
425
- end
426
-
427
- return head
428
- end
429
-
430
- def logop(type, left, right) # TODO: rename logical_op
431
- left = value_expr left
432
-
433
- if left and left[0] == type and not left.paren then
434
- node, second = left, nil
435
-
436
- while (second = node[2]) && second[0] == type and not second.paren do
437
- node = second
438
- end
439
-
440
- node[2] = s(type, second, right)
441
-
442
- return left
443
- end
444
-
445
- return s(type, left, right)
446
- end
447
-
448
- def new_aref val
449
- val[2] ||= s(:arglist)
450
- val[2][0] = :arglist if val[2][0] == :array # REFACTOR
451
- if val[0].node_type == :self then
452
- result = new_call nil, :"[]", val[2]
453
- else
454
- result = new_call val[0], :"[]", val[2]
455
- end
456
- result
457
- end
458
-
459
- def new_body val
460
- result = val[0]
461
-
462
- if val[1] then
463
- result = s(:rescue)
464
- result << val[0] if val[0]
465
-
466
- resbody = val[1]
467
-
468
- while resbody do
469
- result << resbody
470
- resbody = resbody.resbody(true)
471
- end
472
-
473
- result << val[2] if val[2]
474
-
475
- result.line = (val[0] || val[1]).line
476
- elsif not val[2].nil? then
477
- warning("else without rescue is useless")
478
- result = block_append(result, val[2])
479
- end
480
-
481
- result = s(:ensure, result, val[3]).compact if val[3]
482
- return result
483
- end
484
-
485
- def new_call recv, meth, args = nil
486
- result = s(:call, recv, meth)
487
- result.line = recv.line if recv
488
-
489
- args ||= s(:arglist)
490
- args[0] = :arglist if args.first == :array
491
- args = s(:arglist, args) unless args.first == :arglist
492
- result << args
493
- result
494
- end
495
-
496
- def new_case expr, body
497
- result = s(:case, expr)
498
- line = (expr || body).line
499
-
500
- while body and body.node_type == :when
501
- result << body
502
- body = body.delete_at 3
503
- end
504
-
505
- # else
506
- body = nil if body == s(:block)
507
- result << body
508
-
509
- result.line = line
510
- result
511
- end
512
-
513
- def new_class val
514
- line, path, superclass, body = val[1], val[2], val[3], val[5]
515
- scope = s(:scope, body).compact
516
- result = s(:class, path, superclass, scope)
517
- result.line = line
518
- result.comments = self.comments.pop
519
- result
520
- end
521
-
522
- def new_compstmt val
523
- result = void_stmts(val[0])
524
- result = remove_begin(result) if result
525
- result
526
- end
527
-
528
- def new_defn val
529
- (_, line), name, args, body = val[0], val[1], val[3], val[4]
530
- body ||= s(:nil)
531
-
532
- body ||= s(:block)
533
- body = s(:block, body) unless body.first == :block
534
-
535
- result = s(:defn, name.to_sym, args, s(:scope, body))
536
- result.line = line
537
- result.comments = self.comments.pop
538
- result
539
- end
540
-
541
- def new_defs val
542
- recv, name, args, body = val[1], val[4], val[6], val[7]
543
-
544
- body ||= s(:block)
545
- body = s(:block, body) unless body.first == :block
546
-
547
- result = s(:defs, recv, name.to_sym, args, s(:scope, body))
548
- result.line = recv.line
549
- result.comments = self.comments.pop
550
- result
551
- end
552
-
553
- def new_for expr, var, body
554
- result = s(:for, expr, var).line(var.line)
555
- result << body if body
556
- result
557
- end
558
-
559
- def new_if c, t, f
560
- l = [c.line, t && t.line, f && f.line].compact.min
561
- c = cond c
562
- c, t, f = c.last, f, t if c[0] == :not and canonicalize_conditions
563
- s(:if, c, t, f).line(l)
564
- end
565
-
566
- def new_iter call, args, body
567
- result = s(:iter)
568
- result << call if call
569
- result << args
570
- result << body if body
571
- result
572
- end
573
-
574
- def new_masgn lhs, rhs, wrap = false
575
- rhs = value_expr(rhs)
576
- rhs = lhs[1] ? s(:to_ary, rhs) : s(:array, rhs) if wrap
577
-
578
- lhs.delete_at 1 if lhs[1].nil?
579
- lhs << rhs
580
-
581
- lhs
582
- end
583
-
584
- def new_module val
585
- line, path, body = val[1], val[2], val[4]
586
- body = s(:scope, body).compact
587
- result = s(:module, path, body)
588
- result.line = line
589
- result.comments = self.comments.pop
590
- result
591
- end
592
-
593
- def new_op_asgn val
594
- lhs, asgn_op, arg = val[0], val[1].to_sym, val[2]
595
- name = lhs.value
596
- arg = remove_begin(arg)
597
- result = case asgn_op # REFACTOR
598
- when :"||" then
599
- lhs << arg
600
- s(:op_asgn_or, self.gettable(name), lhs)
601
- when :"&&" then
602
- lhs << arg
603
- s(:op_asgn_and, self.gettable(name), lhs)
604
- else
605
- # TODO: why [2] ?
606
- lhs[2] = new_call(self.gettable(name), asgn_op,
607
- s(:arglist, arg))
608
- lhs
609
- end
610
- result.line = lhs.line
611
- result
612
- end
613
-
614
- def new_regexp val
615
- node = val[1] || s(:str, '')
616
- options = val[2]
617
-
618
- o, k = 0, nil
619
- options.split(//).uniq.each do |c| # FIX: this has a better home
620
- v = {
621
- 'x' => Regexp::EXTENDED,
622
- 'i' => Regexp::IGNORECASE,
623
- 'm' => Regexp::MULTILINE,
624
- 'o' => Regexp::ONCE,
625
- 'n' => Regexp::ENC_NONE,
626
- 'e' => Regexp::ENC_EUC,
627
- 's' => Regexp::ENC_SJIS,
628
- 'u' => Regexp::ENC_UTF8,
629
- }[c]
630
- raise "unknown regexp option: #{c}" unless v
631
- o += v
632
- k = c if c =~ /[esu]/
633
- end
634
-
635
- case node[0]
636
- when :str then
637
- node[0] = :lit
638
- node[1] = if k then
639
- Regexp.new(node[1], o, k)
640
- else
641
- Regexp.new(node[1], o)
642
- end
643
- when :dstr then
644
- if options =~ /o/ then
645
- node[0] = :dregx_once
646
- else
647
- node[0] = :dregx
648
- end
649
- node << o if o and o != 0
650
- else
651
- node = s(:dregx, '', node);
652
- node[0] = :dregx_once if options =~ /o/
653
- node << o if o and o != 0
654
- end
655
-
656
- node
657
- end
658
-
659
- def new_sclass val
660
- recv, in_def, in_single, body = val[3], val[4], val[6], val[7]
661
- scope = s(:scope, body).compact
662
- result = s(:sclass, recv, scope)
663
- result.line = val[2]
664
- self.in_def = in_def
665
- self.in_single = in_single
666
- result
667
- end
668
-
669
- def new_super args
670
- if args && args.node_type == :block_pass then
671
- s(:super, args)
672
- else
673
- args ||= s(:arglist)
674
- s(:super, *args[1..-1])
675
- end
676
- end
677
-
678
- def new_undef n, m = nil
679
- if m then
680
- block_append(n, s(:undef, m))
681
- else
682
- s(:undef, n)
683
- end
684
- end
685
-
686
- def new_until_or_while type, block, expr, pre
687
- other = type == :until ? :while : :until
688
- line = [block && block.line, expr.line].compact.min
689
- block, pre = block.last, false if block && block[0] == :begin
690
-
691
- expr = cond expr
692
-
693
- result = unless expr.first == :not and canonicalize_conditions then
694
- s(type, expr, block, pre)
695
- else
696
- s(other, expr.last, block, pre)
697
- end
698
-
699
- result.line = line
700
- result
701
- end
702
-
703
- def new_until block, expr, pre
704
- new_until_or_while :until, block, expr, pre
705
- end
706
-
707
- def new_while block, expr, pre
708
- new_until_or_while :while, block, expr, pre
709
- end
710
-
711
- def new_xstring str
712
- if str then
713
- case str[0]
714
- when :str
715
- str[0] = :xstr
716
- when :dstr
717
- str[0] = :dxstr
718
- else
719
- str = s(:dxstr, '', str)
720
- end
721
- str
722
- else
723
- s(:xstr, '')
724
- end
725
- end
726
-
727
- def new_yield args = nil
728
- # TODO: raise args.inspect unless [:arglist].include? args.first # HACK
729
- raise SyntaxError, "Block argument should not be given." if
730
- args && args.node_type == :block_pass
731
-
732
- args ||= s(:arglist)
733
-
734
- # TODO: I can prolly clean this up
735
- args[0] = :arglist if args.first == :array
736
- args = s(:arglist, args) unless args.first == :arglist
737
-
738
- return s(:yield, *args[1..-1])
739
- end
740
-
741
- def next_token
742
- if self.lexer.advance then
743
- return self.lexer.token, self.lexer.yacc_value
744
- else
745
- return [false, '$end']
746
- end
747
- end
748
-
749
- def node_assign(lhs, rhs) # TODO: rename new_assign
750
- return nil unless lhs
751
-
752
- rhs = value_expr rhs
753
-
754
- case lhs[0]
755
- when :gasgn, :iasgn, :lasgn, :dasgn, :dasgn_curr,
756
- :masgn, :cdecl, :cvdecl, :cvasgn then
757
- lhs << rhs
758
- when :attrasgn, :call then
759
- args = lhs.pop unless Symbol === lhs.last
760
- lhs << arg_add(args, rhs)
761
- when :const then
762
- lhs[0] = :cdecl
763
- lhs << rhs
764
- else
765
- raise "unknown lhs #{lhs.inspect}"
766
- end
767
-
768
- lhs
769
- end
770
-
771
- def process(str, file = "(string)")
772
- raise "bad val: #{str.inspect}" unless String === str
773
-
774
- self.file = file
775
- self.lexer.src = str.dup
776
-
777
- @yydebug = ENV.has_key? 'DEBUG'
778
-
779
- do_parse
780
- end
781
- alias :parse :process
782
-
783
- def remove_begin node
784
- oldnode = node
785
- if node and :begin == node[0] and node.size == 2 then
786
- node = node[-1]
787
- node.line = oldnode.line
788
- end
789
- node
790
- end
791
-
792
- def reset
793
- lexer.reset
794
- self.in_def = false
795
- self.in_single = 0
796
- self.env.reset
797
- self.comments.clear
798
- end
799
-
800
- def ret_args node
801
- if node then
802
- raise SyntaxError, "block argument should not be given" if
803
- node[0] == :block_pass
804
-
805
- node = node.last if node[0] == :array && node.size == 2
806
- # HACK matz wraps ONE of the FOUR splats in a newline to
807
- # distinguish. I use paren for now. ugh
808
- node = s(:svalue, node) if node[0] == :splat and not node.paren
809
- node[0] = :svalue if node[0] == :arglist && node[1][0] == :splat
810
- end
811
-
812
- node
813
- end
814
-
815
- def s(*args)
816
- result = Sexp.new(*args)
817
- result.line ||= lexer.lineno if lexer.src # otherwise...
818
- result.file = self.file
819
- result
820
- end
821
-
822
- def value_expr oldnode # HACK
823
- node = remove_begin oldnode
824
- node.line = oldnode.line if oldnode
825
- node[2] = value_expr(node[2]) if node and node[0] == :if
826
- node
827
- end
828
-
829
- def void_stmts node
830
- return nil unless node
831
- return node unless node[0] == :block
832
-
833
- node[1..-1] = node[1..-1].map { |n| remove_begin(n) }
834
- node
835
- end
836
-
837
- def warning s
838
- # do nothing for now
839
- end
840
-
841
- def yyerror msg
842
- # for now do nothing with the msg
843
- super
844
- end
845
-
846
- class Keyword
847
- class KWtable
848
- attr_accessor :name, :state, :id0, :id1
849
- def initialize(name, id=[], state=nil)
850
- @name = name
851
- @id0, @id1 = id
852
- @state = state
853
- end
854
- end
855
-
856
- ##
857
- # :stopdoc:
858
- #
859
- # :expr_beg = ignore newline, +/- is a sign.
860
- # :expr_end = newline significant, +/- is a operator.
861
- # :expr_arg = newline significant, +/- is a operator.
862
- # :expr_cmdarg = newline significant, +/- is a operator.
863
- # :expr_endarg = newline significant, +/- is a operator.
864
- # :expr_mid = newline significant, +/- is a operator.
865
- # :expr_fname = ignore newline, no reserved words.
866
- # :expr_dot = right after . or ::, no reserved words.
867
- # :expr_class = immediate after class, no here document.
868
-
869
- wordlist = [
870
- ["end", [:kEND, :kEND ], :expr_end ],
871
- ["else", [:kELSE, :kELSE ], :expr_beg ],
872
- ["case", [:kCASE, :kCASE ], :expr_beg ],
873
- ["ensure", [:kENSURE, :kENSURE ], :expr_beg ],
874
- ["module", [:kMODULE, :kMODULE ], :expr_beg ],
875
- ["elsif", [:kELSIF, :kELSIF ], :expr_beg ],
876
- ["def", [:kDEF, :kDEF ], :expr_fname ],
877
- ["rescue", [:kRESCUE, :kRESCUE_MOD ], :expr_mid ],
878
- ["not", [:kNOT, :kNOT ], :expr_beg ],
879
- ["then", [:kTHEN, :kTHEN ], :expr_beg ],
880
- ["yield", [:kYIELD, :kYIELD ], :expr_arg ],
881
- ["for", [:kFOR, :kFOR ], :expr_beg ],
882
- ["self", [:kSELF, :kSELF ], :expr_end ],
883
- ["false", [:kFALSE, :kFALSE ], :expr_end ],
884
- ["retry", [:kRETRY, :kRETRY ], :expr_end ],
885
- ["return", [:kRETURN, :kRETURN ], :expr_mid ],
886
- ["true", [:kTRUE, :kTRUE ], :expr_end ],
887
- ["if", [:kIF, :kIF_MOD ], :expr_beg ],
888
- ["defined?", [:kDEFINED, :kDEFINED ], :expr_arg ],
889
- ["super", [:kSUPER, :kSUPER ], :expr_arg ],
890
- ["undef", [:kUNDEF, :kUNDEF ], :expr_fname ],
891
- ["break", [:kBREAK, :kBREAK ], :expr_mid ],
892
- ["in", [:kIN, :kIN ], :expr_beg ],
893
- ["do", [:kDO, :kDO ], :expr_beg ],
894
- ["nil", [:kNIL, :kNIL ], :expr_end ],
895
- ["until", [:kUNTIL, :kUNTIL_MOD ], :expr_beg ],
896
- ["unless", [:kUNLESS, :kUNLESS_MOD ], :expr_beg ],
897
- ["or", [:kOR, :kOR ], :expr_beg ],
898
- ["next", [:kNEXT, :kNEXT ], :expr_mid ],
899
- ["when", [:kWHEN, :kWHEN ], :expr_beg ],
900
- ["redo", [:kREDO, :kREDO ], :expr_end ],
901
- ["and", [:kAND, :kAND ], :expr_beg ],
902
- ["begin", [:kBEGIN, :kBEGIN ], :expr_beg ],
903
- ["__LINE__", [:k__LINE__, :k__LINE__ ], :expr_end ],
904
- ["class", [:kCLASS, :kCLASS ], :expr_class ],
905
- ["__FILE__", [:k__FILE__, :k__FILE__ ], :expr_end ],
906
- ["END", [:klEND, :klEND ], :expr_end ],
907
- ["BEGIN", [:klBEGIN, :klBEGIN ], :expr_end ],
908
- ["while", [:kWHILE, :kWHILE_MOD ], :expr_beg ],
909
- ["alias", [:kALIAS, :kALIAS ], :expr_fname ],
910
- ].map { |args| KWtable.new(*args) }
911
-
912
- # :startdoc:
913
-
914
- WORDLIST = Hash[*wordlist.map { |o| [o.name, o] }.flatten] unless
915
- defined? WORDLIST
916
-
917
- def self.keyword str
918
- WORDLIST[str]
919
- end
920
- end
921
-
922
- class Environment
923
- attr_reader :env, :dyn
924
-
925
- def [] k
926
- self.all[k]
927
- end
928
-
929
- def []= k, v
930
- raise "no" if v == true
931
- self.current[k] = v
932
- end
933
-
934
- def all
935
- idx = @dyn.index(false) || 0
936
- @env[0..idx].reverse.inject { |env, scope| env.merge scope }
937
- end
938
-
939
- def current
940
- @env.first
941
- end
942
-
943
- def dynamic
944
- idx = @dyn.index false
945
- @env[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
946
- end
947
-
948
- def dynamic?
949
- @dyn[0] != false
950
- end
951
-
952
- def extend dyn = false
953
- @dyn.unshift dyn
954
- @env.unshift({})
955
- @use.unshift({})
956
- end
957
-
958
- def initialize dyn = false
959
- @dyn = []
960
- @env = []
961
- @use = []
962
- self.reset
963
- end
964
-
965
- def reset
966
- @dyn.clear
967
- @env.clear
968
- @use.clear
969
- self.extend
970
- end
971
-
972
- def unextend
973
- @dyn.shift
974
- @env.shift
975
- @use.shift
976
- raise "You went too far unextending env" if @env.empty?
977
- end
978
-
979
- def use id
980
- @env.each_with_index do |env, i|
981
- if env[id] then
982
- @use[i][id] = true
983
- end
984
- end
985
- end
986
-
987
- def used? id
988
- idx = @dyn.index false # REFACTOR
989
- u = @use[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
990
- u[id]
991
- end
992
- end
993
-
994
- class StackState
995
- attr_reader :stack
996
-
997
- def initialize(name)
998
- @name = name
999
- @stack = [false]
1000
- end
1001
-
1002
- def inspect
1003
- "StackState(#{@name}, #{@stack.inspect})"
1004
- end
1005
-
1006
- def is_in_state
1007
- @stack.last
1008
- end
1009
-
1010
- def lexpop
1011
- raise if @stack.size == 0
1012
- a = @stack.pop
1013
- b = @stack.pop
1014
- @stack.push(a || b)
1015
- end
1016
-
1017
- def pop
1018
- r = @stack.pop
1019
- @stack.push false if @stack.size == 0
1020
- r
1021
- end
1022
-
1023
- def push val
1024
- @stack.push val
1025
- end
1026
- end
1027
- end
1028
-
1029
- class Ruby19Parser < Racc::Parser
1030
- include RubyParserStuff
1031
- end
1032
-
1033
- class Ruby18Parser < Racc::Parser
1034
- include RubyParserStuff
1035
- end
1036
-
1037
- class RubyParser < Ruby18Parser
1038
- def initialize
1039
- super
1040
- warn "WA\RNING: Deprecated: RubyParser. Use Ruby18Parser or Ruby19Parser"
1041
- warn " from #{caller.first}"
1042
- end
1043
- end
1044
-
1045
- ############################################################
1046
- # HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
1047
-
1048
- class Symbol
1049
- def is_argument # TODO: phase this out
1050
- return self == :expr_arg || self == :expr_cmdarg
1051
- end
1052
- end
1053
-
1054
- require 'bm_sexp'
1055
-
1056
- # END HACK
1057
- ############################################################