brakeman 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/brakeman +3 -0
- data/lib/brakeman.rb +2 -0
- data/lib/brakeman/brakeman.rake +4 -3
- data/lib/brakeman/checks/base_check.rb +40 -37
- data/lib/brakeman/checks/check_basic_auth.rb +3 -3
- data/lib/brakeman/checks/check_content_tag.rb +179 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +41 -17
- data/lib/brakeman/checks/check_execute.rb +1 -1
- data/lib/brakeman/checks/check_file_access.rb +2 -2
- data/lib/brakeman/checks/check_link_to.rb +9 -7
- data/lib/brakeman/checks/check_link_to_href.rb +1 -1
- data/lib/brakeman/checks/check_mail_to.rb +1 -1
- data/lib/brakeman/checks/check_mass_assignment.rb +6 -5
- data/lib/brakeman/checks/check_redirect.rb +18 -17
- data/lib/brakeman/checks/check_render.rb +3 -1
- data/lib/brakeman/checks/check_select_tag.rb +2 -2
- data/lib/brakeman/checks/check_select_vulnerability.rb +3 -3
- data/lib/brakeman/checks/check_send.rb +3 -3
- data/lib/brakeman/checks/check_session_settings.rb +5 -5
- data/lib/brakeman/checks/check_single_quotes.rb +8 -8
- data/lib/brakeman/checks/check_skip_before_filter.rb +2 -2
- data/lib/brakeman/checks/check_sql.rb +36 -39
- data/lib/brakeman/checks/check_validation_regex.rb +3 -3
- data/lib/brakeman/checks/check_without_protection.rb +2 -2
- data/lib/brakeman/format/style.css +15 -0
- data/lib/brakeman/options.rb +4 -0
- data/lib/brakeman/processor.rb +1 -1
- data/lib/brakeman/processors/alias_processor.rb +63 -61
- data/lib/brakeman/processors/base_processor.rb +31 -45
- data/lib/brakeman/processors/controller_alias_processor.rb +11 -9
- data/lib/brakeman/processors/controller_processor.rb +26 -25
- data/lib/brakeman/processors/erb_template_processor.rb +12 -12
- data/lib/brakeman/processors/erubis_template_processor.rb +19 -17
- data/lib/brakeman/processors/gem_processor.rb +5 -5
- data/lib/brakeman/processors/haml_template_processor.rb +16 -12
- data/lib/brakeman/processors/lib/find_all_calls.rb +11 -17
- data/lib/brakeman/processors/lib/find_call.rb +16 -23
- data/lib/brakeman/processors/lib/processor_helper.rb +11 -5
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +21 -20
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +38 -34
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +17 -17
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +42 -40
- data/lib/brakeman/processors/lib/render_helper.rb +6 -6
- data/lib/brakeman/processors/lib/route_helper.rb +1 -1
- data/lib/brakeman/processors/library_processor.rb +11 -11
- data/lib/brakeman/processors/model_processor.rb +18 -16
- data/lib/brakeman/processors/template_alias_processor.rb +36 -29
- data/lib/brakeman/processors/template_processor.rb +4 -4
- data/lib/brakeman/report.rb +23 -4
- data/lib/brakeman/templates/error_overview.html.erb +9 -1
- data/lib/brakeman/templates/view_warnings.html.erb +16 -3
- data/lib/brakeman/tracker.rb +3 -0
- data/lib/brakeman/util.rb +5 -1
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +1 -1
- data/lib/ruby_parser/bm_sexp.rb +302 -2
- metadata +6 -5
@@ -17,12 +17,16 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
|
|
17
17
|
|
18
18
|
#Process template
|
19
19
|
def process_template name, args
|
20
|
-
if @called_from
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
if @called_from
|
21
|
+
unless @called_from.grep(/Template:#{name}$/).empty?
|
22
|
+
Brakeman.debug "Skipping circular render from #{@template[:name]} to #{name}"
|
23
|
+
return
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
+
super name, args, @called_from + ["Template:#{@template[:name]}"]
|
27
|
+
else
|
28
|
+
super name, args, ["Template:#{@template[:name]}"]
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
#Determine template name
|
@@ -37,28 +41,31 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
|
|
37
41
|
def process_call_with_block exp
|
38
42
|
process_default exp
|
39
43
|
|
40
|
-
call = exp
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
call = exp.block_call
|
45
|
+
|
46
|
+
if call? call
|
47
|
+
target = call.target
|
48
|
+
method = call.method
|
49
|
+
args = exp.block_args
|
50
|
+
block = exp.block
|
51
|
+
|
52
|
+
#Check for e.g. Model.find.each do ... end
|
53
|
+
if method == :each and args and block and model = get_model_target(target)
|
54
|
+
if node_type? args, :lasgn
|
55
|
+
if model == target.target
|
56
|
+
env[Sexp.new(:lvar, args.lhs)] = Sexp.new(:call, model, :new, Sexp.new(:arglist))
|
57
|
+
else
|
58
|
+
env[Sexp.new(:lvar, args.lhs)] = Sexp.new(:call, Sexp.new(:const, Brakeman::Tracker::UNKNOWN_MODEL), :new, Sexp.new(:arglist))
|
59
|
+
end
|
60
|
+
|
61
|
+
process block if sexp? block
|
53
62
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
elsif FORM_METHODS.include? method
|
58
|
-
if node_type? args, :lasgn
|
59
|
-
env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
|
63
|
+
elsif FORM_METHODS.include? method
|
64
|
+
if node_type? args, :lasgn
|
65
|
+
env[Sexp.new(:lvar, args.lhs)] = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
|
60
66
|
|
61
|
-
|
67
|
+
process block if sexp? block
|
68
|
+
end
|
62
69
|
end
|
63
70
|
end
|
64
71
|
|
@@ -70,9 +77,9 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
|
|
70
77
|
#Checks if +exp+ is a call to Model.all or Model.find*
|
71
78
|
def get_model_target exp
|
72
79
|
if call? exp
|
73
|
-
target = exp
|
80
|
+
target = exp.target
|
74
81
|
|
75
|
-
if exp
|
82
|
+
if exp.method == :all or exp.method.to_s[0,4] == "find"
|
76
83
|
models = Set.new @tracker.models.keys
|
77
84
|
|
78
85
|
begin
|
@@ -91,9 +98,9 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
|
|
91
98
|
|
92
99
|
def find_push_target exp
|
93
100
|
if sexp? exp
|
94
|
-
if exp.node_type == :lvar and (exp
|
101
|
+
if exp.node_type == :lvar and (exp.value == :_buf or exp.value == :_erbout)
|
95
102
|
return nil
|
96
|
-
elsif exp.node_type == :ivar and exp
|
103
|
+
elsif exp.node_type == :ivar and exp.value == :@output_buffer
|
97
104
|
return nil
|
98
105
|
end
|
99
106
|
end
|
@@ -35,19 +35,19 @@ class Brakeman::TemplateProcessor < Brakeman::BaseProcessor
|
|
35
35
|
|
36
36
|
#Ignore initial variable assignment
|
37
37
|
def process_lasgn exp
|
38
|
-
if exp
|
38
|
+
if exp.lhs == :_erbout and exp.rhs.node_type == :str #ignore
|
39
39
|
ignore
|
40
|
-
elsif exp
|
40
|
+
elsif exp.lhs == :_buf and exp.rhs.node_type == :str
|
41
41
|
ignore
|
42
42
|
else
|
43
|
-
exp
|
43
|
+
exp.rhs = process exp.rhs
|
44
44
|
exp
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
#Adds output to the list of outputs.
|
49
49
|
def process_output exp
|
50
|
-
process exp
|
50
|
+
process exp.value
|
51
51
|
@current_template[:outputs] << exp
|
52
52
|
exp
|
53
53
|
end
|
data/lib/brakeman/report.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
require 'set'
|
3
|
+
require 'pathname'
|
3
4
|
require 'brakeman/processors/output_processor'
|
4
5
|
require 'brakeman/util'
|
5
6
|
require 'terminal-table'
|
@@ -136,6 +137,8 @@ class Brakeman::Report
|
|
136
137
|
w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]]
|
137
138
|
w["Message"] = with_context warning, w["Message"]
|
138
139
|
w["Warning Type"] = with_link warning, w["Warning Type"]
|
140
|
+
w["Called From"] = warning.called_from
|
141
|
+
w["Template Name"] = warning.template[:name]
|
139
142
|
else
|
140
143
|
w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
|
141
144
|
w["Message"] = text_message warning, w["Message"]
|
@@ -549,7 +552,7 @@ class Brakeman::Report
|
|
549
552
|
message
|
550
553
|
end <<
|
551
554
|
"<table id='#{code_id}' class='context' style='display:none'>" <<
|
552
|
-
"<caption>#{(warning
|
555
|
+
"<caption>#{warning_file(warning, :relative) || ''}</caption>"
|
553
556
|
|
554
557
|
unless context.empty?
|
555
558
|
if warning.line - 1 == 1 or warning.line + 1 == 1
|
@@ -612,7 +615,7 @@ class Brakeman::Report
|
|
612
615
|
checks.send(meth).map do |w|
|
613
616
|
line = w.line || 0
|
614
617
|
w.warning_type.gsub!(/[^\w\s]/, ' ')
|
615
|
-
"#{
|
618
|
+
"#{warning_file w}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
|
616
619
|
end.join "\n"
|
617
620
|
|
618
621
|
end.join "\n"
|
@@ -635,7 +638,6 @@ class Brakeman::Report
|
|
635
638
|
w.code = ""
|
636
639
|
end
|
637
640
|
w.context = context_for(w).join("\n")
|
638
|
-
w.file = file_for w
|
639
641
|
end
|
640
642
|
end
|
641
643
|
|
@@ -648,7 +650,14 @@ class Brakeman::Report
|
|
648
650
|
require 'json'
|
649
651
|
|
650
652
|
errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
|
651
|
-
|
653
|
+
app_path = tracker.options[:app_path]
|
654
|
+
|
655
|
+
warnings = all_warnings.map do |w|
|
656
|
+
hash = w.to_hash
|
657
|
+
hash[:file] = warning_file w
|
658
|
+
hash
|
659
|
+
end.sort_by { |w| w[:file] }
|
660
|
+
|
652
661
|
scan_info = {
|
653
662
|
:app_path => File.expand_path(tracker.options[:app_path]),
|
654
663
|
:rails_version => rails_version,
|
@@ -678,6 +687,16 @@ class Brakeman::Report
|
|
678
687
|
Set.new(tracker.templates.map {|k,v| v[:name].to_s[/[^.]+/]}).length
|
679
688
|
end
|
680
689
|
|
690
|
+
def warning_file warning, relative = false
|
691
|
+
return nil if warning.file.nil?
|
692
|
+
|
693
|
+
if @tracker.options[:relative_paths] or relative
|
694
|
+
Pathname.new(warning.file).relative_path_from(Pathname.new(tracker.options[:app_path])).to_s
|
695
|
+
else
|
696
|
+
warning.file
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
681
700
|
private
|
682
701
|
|
683
702
|
def load_and_render_erb file, bind
|
@@ -7,7 +7,15 @@
|
|
7
7
|
<% tracker.errors.each do |warning| %>
|
8
8
|
<tr>
|
9
9
|
<td><%= CGI.escapeHTML warning[:error] %></td>
|
10
|
-
<td
|
10
|
+
<td>
|
11
|
+
<% if tracker.options[:debug] %>
|
12
|
+
<% warning[:backtrace].each do |line| %>
|
13
|
+
<%= line %><br/>
|
14
|
+
<% end %>
|
15
|
+
<% else %>
|
16
|
+
<%= warning[:backtrace][0] %>
|
17
|
+
<% end %>
|
18
|
+
</td>
|
11
19
|
</tr>
|
12
20
|
<% end %>
|
13
21
|
</table>
|
@@ -6,12 +6,25 @@
|
|
6
6
|
<th>Warning Type</th>
|
7
7
|
<th>Message</th>
|
8
8
|
</tr>
|
9
|
-
<% warnings.
|
9
|
+
<% warnings.each_with_index do |warning, i| %>
|
10
10
|
<tr>
|
11
11
|
<td><%= warning['Confidence']%></td>
|
12
|
-
<td
|
12
|
+
<td>
|
13
|
+
<% if warning['Called From'] and warning['Called From'].length > 1 %>
|
14
|
+
<div class="template_name" onClick="toggle('callers<%= i %>')" >
|
15
|
+
<div>
|
16
|
+
<%= warning['Template'] %>
|
17
|
+
</div>
|
18
|
+
<div class="render_path" id="callers<%= i %>" >
|
19
|
+
<%= warning['Called From'].join(' → ') %> → <%= warning['Template Name'] %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<% else %>
|
23
|
+
<%= warning['Template']%>
|
24
|
+
<% end %>
|
25
|
+
</td>
|
13
26
|
<td><%= warning['Warning Type']%></td>
|
14
27
|
<td><%= warning['Message']%></td>
|
15
28
|
</tr>
|
16
29
|
<% end %>
|
17
|
-
</table>
|
30
|
+
</table>
|
data/lib/brakeman/tracker.rb
CHANGED
data/lib/brakeman/util.rb
CHANGED
@@ -346,7 +346,11 @@ module Brakeman::Util
|
|
346
346
|
end
|
347
347
|
|
348
348
|
def truncate_table str
|
349
|
-
@terminal_width ||=
|
349
|
+
@terminal_width ||= if $stdin && $stdin.tty?
|
350
|
+
::HighLine::SystemExtensions::terminal_size[0]
|
351
|
+
else
|
352
|
+
80
|
353
|
+
end
|
350
354
|
lines = str.lines
|
351
355
|
|
352
356
|
lines.map do |line|
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman/warning.rb
CHANGED
data/lib/ruby_parser/bm_sexp.rb
CHANGED
@@ -3,20 +3,44 @@
|
|
3
3
|
#of a Sexp.
|
4
4
|
class Sexp
|
5
5
|
attr_reader :paren
|
6
|
+
ASSIGNMENT_BOOL = [:gasgn, :iasgn, :lasgn, :cvdecl, :cdecl, :or, :and]
|
7
|
+
|
8
|
+
def method_missing name, *args
|
9
|
+
#Brakeman does not use this functionality,
|
10
|
+
#so overriding it to raise a NoMethodError.
|
11
|
+
#
|
12
|
+
#The original functionality calls find_node and optionally
|
13
|
+
#deletes the node if found.
|
14
|
+
raise NoMethodError.new("No method '#{name}' for Sexp", name, args)
|
15
|
+
end
|
6
16
|
|
7
17
|
def paren
|
8
18
|
@paren ||= false
|
9
19
|
end
|
10
20
|
|
11
21
|
def value
|
12
|
-
raise "multi
|
22
|
+
raise WrongSexpError, "Sexp#value called on multi-item Sexp", caller[1..-1] if size > 2
|
13
23
|
last
|
14
24
|
end
|
15
25
|
|
26
|
+
def second
|
27
|
+
self[1]
|
28
|
+
end
|
29
|
+
|
16
30
|
def to_sym
|
17
31
|
self.value.to_sym
|
18
32
|
end
|
19
33
|
|
34
|
+
def node_type= type
|
35
|
+
self[0] = type
|
36
|
+
end
|
37
|
+
|
38
|
+
def resbody delete = false
|
39
|
+
#RubyParser relies on method_missing for this, but since we don't want to use
|
40
|
+
#method_missing, here's a real method.
|
41
|
+
find_node :resbody, delete
|
42
|
+
end
|
43
|
+
|
20
44
|
alias :node_type :sexp_type
|
21
45
|
alias :values :sexp_body # TODO: retire
|
22
46
|
|
@@ -86,6 +110,282 @@ class Sexp
|
|
86
110
|
@my_hash_value = nil
|
87
111
|
old_comments_set(*args)
|
88
112
|
end
|
113
|
+
|
114
|
+
#Iterates over the Sexps in an Sexp, skipping values that are not
|
115
|
+
#an Sexp.
|
116
|
+
def each_sexp
|
117
|
+
self.each do |e|
|
118
|
+
yield e if Sexp === e
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
#Raise a WrongSexpError if the nodes type does not match one of the expected
|
123
|
+
#types.
|
124
|
+
def expect *types
|
125
|
+
unless types.include? self.node_type
|
126
|
+
raise WrongSexpError, "Expected #{types.join ' or '} but given #{self.inspect}", caller[1..-1]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
#Returns target of a method call:
|
131
|
+
#
|
132
|
+
#s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
|
133
|
+
# ^-----------target-----------^
|
134
|
+
def target
|
135
|
+
expect :call, :attrasgn
|
136
|
+
self[1]
|
137
|
+
end
|
138
|
+
|
139
|
+
#Sets the target of a method call:
|
140
|
+
def target= exp
|
141
|
+
expect :call, :attrasgn
|
142
|
+
self[1] = exp
|
143
|
+
end
|
144
|
+
|
145
|
+
#Returns method of a method call:
|
146
|
+
#
|
147
|
+
#s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
|
148
|
+
# ^- method
|
149
|
+
def method
|
150
|
+
expect :call, :attrasgn
|
151
|
+
self[2]
|
152
|
+
end
|
153
|
+
|
154
|
+
#Sets the arglist in a method call.
|
155
|
+
def arglist= exp
|
156
|
+
expect :call, :attrasgn
|
157
|
+
self[3] = exp
|
158
|
+
#RP 3 TODO
|
159
|
+
end
|
160
|
+
|
161
|
+
#Returns arglist for method call. This differs from Sexp#args, as Sexp#args
|
162
|
+
#does not return a 'real' Sexp (it does not have a node type) but
|
163
|
+
#Sexp#arglist returns a s(:arglist, ...)
|
164
|
+
#
|
165
|
+
# s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
|
166
|
+
# ^------------ arglist ------------^
|
167
|
+
def arglist
|
168
|
+
expect :call, :attrasgn
|
169
|
+
self[3]
|
170
|
+
|
171
|
+
#For new ruby_parser
|
172
|
+
#Sexp.new(:arglist, *self[3..-1])
|
173
|
+
end
|
174
|
+
|
175
|
+
#Returns arguments of a method call. This will be an 'untyped' Sexp.
|
176
|
+
#
|
177
|
+
# s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
|
178
|
+
# ^--------args--------^
|
179
|
+
def args
|
180
|
+
expect :call, :attrasgn
|
181
|
+
#For new ruby_parser
|
182
|
+
#if self[3]
|
183
|
+
# self[3..-1]
|
184
|
+
#else
|
185
|
+
# []
|
186
|
+
#end
|
187
|
+
|
188
|
+
#For old ruby_parser
|
189
|
+
if self[3]
|
190
|
+
self[3][1..-1]
|
191
|
+
else
|
192
|
+
[]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
#Returns first argument of a method call.
|
197
|
+
def first_arg
|
198
|
+
expect :call, :attrasgn
|
199
|
+
if self[3]
|
200
|
+
self[3][1]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
#Sets first argument of a method call.
|
205
|
+
def first_arg= exp
|
206
|
+
expect :call, :attrasgn
|
207
|
+
if self[3]
|
208
|
+
self[3][1] = exp
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
#Returns second argument of a method call.
|
213
|
+
def second_arg
|
214
|
+
expect :call, :attrasgn
|
215
|
+
if self[3]
|
216
|
+
self[3][2]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
#Sets second argument of a method call.
|
221
|
+
def second_arg= exp
|
222
|
+
expect :call, :attrasgn
|
223
|
+
if self[3]
|
224
|
+
self[3][2] = exp
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
#Returns condition of an if expression:
|
229
|
+
#
|
230
|
+
# s(:if,
|
231
|
+
# s(:lvar, :condition), <-- condition
|
232
|
+
# s(:lvar, :then_val),
|
233
|
+
# s(:lvar, :else_val)))
|
234
|
+
def condition
|
235
|
+
expect :if
|
236
|
+
self[1]
|
237
|
+
end
|
238
|
+
|
239
|
+
#Returns 'then' clause of an if expression:
|
240
|
+
#
|
241
|
+
# s(:if,
|
242
|
+
# s(:lvar, :condition),
|
243
|
+
# s(:lvar, :then_val), <-- then clause
|
244
|
+
# s(:lvar, :else_val)))
|
245
|
+
def then_clause
|
246
|
+
expect :if
|
247
|
+
self[2]
|
248
|
+
end
|
249
|
+
|
250
|
+
#Returns 'else' clause of an if expression:
|
251
|
+
#
|
252
|
+
# s(:if,
|
253
|
+
# s(:lvar, :condition),
|
254
|
+
# s(:lvar, :then_val),
|
255
|
+
# s(:lvar, :else_val)))
|
256
|
+
# ^---else caluse---^
|
257
|
+
def else_clause
|
258
|
+
expect :if
|
259
|
+
self[3]
|
260
|
+
end
|
261
|
+
|
262
|
+
#Method call associated with a block:
|
263
|
+
#
|
264
|
+
# s(:iter,
|
265
|
+
# s(:call, nil, :x, s(:arglist)), <- block_call
|
266
|
+
# s(:lasgn, :y),
|
267
|
+
# s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
|
268
|
+
def block_call
|
269
|
+
expect :iter, :call_with_block
|
270
|
+
self[1]
|
271
|
+
end
|
272
|
+
|
273
|
+
#Returns block of a call with a block.
|
274
|
+
#Could be a single expression or a block:
|
275
|
+
#
|
276
|
+
# s(:iter,
|
277
|
+
# s(:call, nil, :x, s(:arglist)),
|
278
|
+
# s(:lasgn, :y),
|
279
|
+
# s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
|
280
|
+
# ^-------------------- block --------------------------^
|
281
|
+
def block
|
282
|
+
expect :iter, :call_with_block, :scope
|
283
|
+
|
284
|
+
case self.node_type
|
285
|
+
when :iter, :call_with_block
|
286
|
+
self[3]
|
287
|
+
when :scope
|
288
|
+
self[1]
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
#Returns parameters for a block
|
293
|
+
#
|
294
|
+
# s(:iter,
|
295
|
+
# s(:call, nil, :x, s(:arglist)),
|
296
|
+
# s(:lasgn, :y), <- block_args
|
297
|
+
# s(:call, nil, :p, s(:arglist, s(:lvar, :y))))
|
298
|
+
def block_args
|
299
|
+
expect :iter, :call_with_block
|
300
|
+
self[2]
|
301
|
+
end
|
302
|
+
|
303
|
+
#Returns the left hand side of assignment or boolean:
|
304
|
+
#
|
305
|
+
# s(:lasgn, :x, s(:lit, 1))
|
306
|
+
# ^--lhs
|
307
|
+
def lhs
|
308
|
+
expect *ASSIGNMENT_BOOL
|
309
|
+
self[1]
|
310
|
+
end
|
311
|
+
|
312
|
+
#Sets the left hand side of assignment or boolean.
|
313
|
+
def lhs= exp
|
314
|
+
expect *ASSIGNMENT_BOOL
|
315
|
+
self[1] = exp
|
316
|
+
end
|
317
|
+
|
318
|
+
#Returns right side (value) of assignment or boolean:
|
319
|
+
#
|
320
|
+
# s(:lasgn, :x, s(:lit, 1))
|
321
|
+
# ^--rhs---^
|
322
|
+
def rhs
|
323
|
+
expect *ASSIGNMENT_BOOL
|
324
|
+
self[2]
|
325
|
+
end
|
326
|
+
|
327
|
+
#Sets the right hand side of assignment or boolean.
|
328
|
+
def rhs= exp
|
329
|
+
expect *ASSIGNMENT_BOOL
|
330
|
+
self[2] = exp
|
331
|
+
end
|
332
|
+
|
333
|
+
#Returns name of method being defined in a method definition.
|
334
|
+
def method_name
|
335
|
+
expect :defn, :defs, :methdef, :selfdef
|
336
|
+
|
337
|
+
case self.node_type
|
338
|
+
when :defn, :methdef
|
339
|
+
self[1]
|
340
|
+
when :defs, :selfdef
|
341
|
+
self[2]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
#Sets body
|
346
|
+
def body= exp
|
347
|
+
expect :defn, :defs, :methdef, :selfdef, :class, :module
|
348
|
+
|
349
|
+
case self.node_type
|
350
|
+
when :defn, :methdef, :class
|
351
|
+
self[3] = exp
|
352
|
+
when :defs, :selfdef
|
353
|
+
self[4] = exp
|
354
|
+
when :module
|
355
|
+
self[2] = exp
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
#Returns body of a method definition, class, or module.
|
360
|
+
def body
|
361
|
+
expect :defn, :defs, :methdef, :selfdef, :class, :module
|
362
|
+
|
363
|
+
case self.node_type
|
364
|
+
when :defn, :methdef, :class
|
365
|
+
self[3]
|
366
|
+
when :defs, :selfdef
|
367
|
+
self[4]
|
368
|
+
when :module
|
369
|
+
self[2]
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def render_type
|
374
|
+
expect :render
|
375
|
+
self[1]
|
376
|
+
end
|
377
|
+
|
378
|
+
def class_name
|
379
|
+
expect :class, :module
|
380
|
+
self[1]
|
381
|
+
end
|
382
|
+
|
383
|
+
alias module_name class_name
|
384
|
+
|
385
|
+
def parent_name
|
386
|
+
expect :class
|
387
|
+
self[2]
|
388
|
+
end
|
89
389
|
end
|
90
390
|
|
91
391
|
#Invalidate hash cache if the Sexp changes
|
@@ -103,4 +403,4 @@ end
|
|
103
403
|
RUBY
|
104
404
|
end
|
105
405
|
|
106
|
-
|
406
|
+
class WrongSexpError < RuntimeError; end
|