code_analyzer 0.1.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/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rvmrc +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +32 -0
- data/Rakefile +2 -0
- data/code_analyzer.gemspec +20 -0
- data/lib/code_analyzer.rb +11 -0
- data/lib/code_analyzer/analyzer_exception.rb +3 -0
- data/lib/code_analyzer/checker.rb +90 -0
- data/lib/code_analyzer/checking_visitor.rb +7 -0
- data/lib/code_analyzer/checking_visitor/default.rb +59 -0
- data/lib/code_analyzer/checking_visitor/plain.rb +17 -0
- data/lib/code_analyzer/nil.rb +35 -0
- data/lib/code_analyzer/sexp.rb +836 -0
- data/lib/code_analyzer/version.rb +3 -0
- data/lib/code_analyzer/warning.rb +19 -0
- data/spec/code_analyzer/checker_spec.rb +64 -0
- data/spec/code_analyzer/checking_visitor/default_spec.rb +42 -0
- data/spec/code_analyzer/checking_visitor/plain_spec.rb +18 -0
- data/spec/code_analyzer/nil_spec.rb +37 -0
- data/spec/code_analyzer/sexp_spec.rb +622 -0
- data/spec/code_analyzer/warning_spec.rb +12 -0
- data/spec/spec_helper.rb +14 -0
- metadata +110 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Richard Huang
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# CodeAnalyzer
|
2
|
+
|
3
|
+
code_analyzer is extracted from [rails_best_practices][0], it helps you
|
4
|
+
easily build your own code analyzer tool.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'code_analyzer'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install code_analyzer
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
TODO: I will write instructions later.
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
|
26
|
+
1. Fork it
|
27
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
28
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
29
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
30
|
+
5. Create new Pull Request
|
31
|
+
|
32
|
+
[0]: https://github.com/railsbp/rails_best_practices
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/code_analyzer/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Richard Huang"]
|
6
|
+
gem.email = ["flyerhzm@gmail.com"]
|
7
|
+
gem.description = %q{a code analyzer tool which extracted from rails_best_practices, it helps you easily build your own code analyzer tool.}
|
8
|
+
gem.summary = %q{a code analyzer helps you build your own code analyzer tool.}
|
9
|
+
gem.homepage = "https://github.com/flyerhzm/code_analyzer"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "code_analyzer"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = CodeAnalyzer::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "sexp_processor"
|
19
|
+
gem.add_development_dependency "rspec"
|
20
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "ripper"
|
2
|
+
require "code_analyzer/version"
|
3
|
+
require "code_analyzer/nil"
|
4
|
+
require "code_analyzer/sexp"
|
5
|
+
|
6
|
+
module CodeAnalyzer
|
7
|
+
autoload :AnalyzerException, "code_analyzer/analyzer_exception"
|
8
|
+
autoload :Checker, "code_analyzer/checker"
|
9
|
+
autoload :CheckingVisitor, "code_analyzer/checking_visitor"
|
10
|
+
autoload :Warning, "code_analyzer/warning"
|
11
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module CodeAnalyzer
|
3
|
+
# A checker class that takes charge of checking the sexp.
|
4
|
+
class Checker
|
5
|
+
# interesting nodes that the check will parse.
|
6
|
+
def interesting_nodes
|
7
|
+
self.class.interesting_nodes
|
8
|
+
end
|
9
|
+
|
10
|
+
# interesting files that the check will parse.
|
11
|
+
def interesting_files
|
12
|
+
self.class.interesting_files
|
13
|
+
end
|
14
|
+
|
15
|
+
# check if the checker will parse the node file.
|
16
|
+
#
|
17
|
+
# @param [String] the file name of node.
|
18
|
+
# @return [Boolean] true if the checker will parse the file.
|
19
|
+
def parse_file?(node_file)
|
20
|
+
interesting_files.any? { |pattern| node_file =~ pattern }
|
21
|
+
end
|
22
|
+
|
23
|
+
# delegate to start_### according to the sexp_type, like
|
24
|
+
#
|
25
|
+
# start_call
|
26
|
+
# start_def
|
27
|
+
#
|
28
|
+
# @param [Sexp] node
|
29
|
+
def node_start(node)
|
30
|
+
@node = node
|
31
|
+
self.class.get_callbacks("start_#{node.sexp_type}".to_sym).each do |block|
|
32
|
+
self.instance_exec(node, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# delegate to end_### according to the sexp_type, like
|
37
|
+
#
|
38
|
+
# end_call
|
39
|
+
# end_def
|
40
|
+
#
|
41
|
+
# @param [Sexp] node
|
42
|
+
def node_end(node)
|
43
|
+
@node = node
|
44
|
+
self.class.get_callbacks("end_#{node.sexp_type}".to_sym).each do |block|
|
45
|
+
self.instance_exec(node, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# add an warning.
|
50
|
+
#
|
51
|
+
# @param [String] message, is the warning message
|
52
|
+
# @param [String] filename, is the filename of source code
|
53
|
+
# @param [Integer] line_number, is the line number of the source code which is reviewing
|
54
|
+
def add_warning(message, filename = @node.file, line_number = @node.line)
|
55
|
+
warnings << Warning.new(filename: filename, line_number: line_number, message: message)
|
56
|
+
end
|
57
|
+
|
58
|
+
def warnings
|
59
|
+
@warnings ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
class <<self
|
63
|
+
def interesting_nodes(*nodes)
|
64
|
+
@interesting_nodes ||= []
|
65
|
+
@interesting_nodes += nodes
|
66
|
+
end
|
67
|
+
|
68
|
+
def interesting_files(*file_patterns)
|
69
|
+
@interesting_files ||= []
|
70
|
+
@interesting_files += file_patterns
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_callbacks(name)
|
74
|
+
callbacks[name] ||= []
|
75
|
+
callbacks[name]
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_callback(*names, &block)
|
79
|
+
names.each do |name|
|
80
|
+
callbacks[name] ||= []
|
81
|
+
callbacks[name] << block
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def callbacks
|
86
|
+
@callbacks ||= {}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module CodeAnalyzer::CheckingVisitor
|
3
|
+
class Default
|
4
|
+
def initialize(options={})
|
5
|
+
@checks = {}
|
6
|
+
@checkers = options[:checkers]
|
7
|
+
@checkers.each do |checker|
|
8
|
+
checker.interesting_nodes.each do |node|
|
9
|
+
@checks[node] ||= []
|
10
|
+
@checks[node] << checker
|
11
|
+
@checks[node].uniq!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def check(filename, content)
|
17
|
+
node = parse(filename, content)
|
18
|
+
node.file = filename
|
19
|
+
check_node(node)
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_check
|
23
|
+
@checkers.each do |checker|
|
24
|
+
after_check_callbacks = checker.class.get_callbacks(:after_check)
|
25
|
+
after_check_callbacks.each do |block|
|
26
|
+
checker.instance_exec &block
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# parse ruby code.
|
32
|
+
#
|
33
|
+
# @param [String] filename is the filename of ruby file.
|
34
|
+
# @param [String] content is the source code of ruby file.
|
35
|
+
def parse(filename, content)
|
36
|
+
Sexp.from_array(Ripper::SexpBuilder.new(content).parse)
|
37
|
+
rescue Exception
|
38
|
+
raise AnalyzerException.new("#{filename} looks like it's not a valid Ruby file. Skipping...")
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_node(node)
|
42
|
+
checkers = @checks[node.sexp_type]
|
43
|
+
if checkers
|
44
|
+
checkers.each { |checker|
|
45
|
+
checker.node_start(node) if checker.parse_file?(node.file)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
node.children.each { |child_node|
|
49
|
+
child_node.file = node.file
|
50
|
+
child_node.check(self)
|
51
|
+
}
|
52
|
+
if checkers
|
53
|
+
checkers.each { |checker|
|
54
|
+
checker.node_end(node) if checker.parse_file?(node.file)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module CodeAnalyzer::CheckingVisitor
|
3
|
+
class Plain
|
4
|
+
def initialize(options={})
|
5
|
+
@checkers = options[:checkers]
|
6
|
+
end
|
7
|
+
|
8
|
+
def check(filename, content)
|
9
|
+
@checkers.each do |checker|
|
10
|
+
checker.check(filename, content)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def after_check
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module CodeAnalyzer
|
3
|
+
# Fake nil.
|
4
|
+
class Nil
|
5
|
+
# hash_size is 0.
|
6
|
+
def hash_size
|
7
|
+
0
|
8
|
+
end
|
9
|
+
|
10
|
+
# array_size is 0.
|
11
|
+
def array_size
|
12
|
+
0
|
13
|
+
end
|
14
|
+
|
15
|
+
# return self for to_s.
|
16
|
+
def to_s
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
# false
|
21
|
+
def present?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
# true
|
26
|
+
def blank?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
# return self.
|
31
|
+
def method_missing(method_sym, *arguments, &block)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,836 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'sexp'
|
3
|
+
|
4
|
+
class Sexp
|
5
|
+
# check current node.
|
6
|
+
#
|
7
|
+
# @param [CodeAnalyzer::CheckingVisitor::Default] visitor the visitor to check current node
|
8
|
+
def check(visitor)
|
9
|
+
visitor.check_node(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
# return the line number of a sexp node.
|
13
|
+
#
|
14
|
+
# s(:@ident, "test", s(2, 12)
|
15
|
+
# => 2
|
16
|
+
def line
|
17
|
+
case sexp_type
|
18
|
+
when :def, :defs, :command, :command_call, :call, :fcall, :method_add_arg, :method_add_block,
|
19
|
+
:var_ref, :vcall, :const_ref, :const_path_ref, :class, :module, :if, :unless, :elsif, :ifop, :binary,
|
20
|
+
:alias, :symbol_literal, :symbol, :aref, :hash, :assoc_new, :string_literal
|
21
|
+
self[1].line
|
22
|
+
when :assoclist_from_args, :bare_assoc_hash
|
23
|
+
self[1][0].line
|
24
|
+
when :string_add
|
25
|
+
self[2].line
|
26
|
+
when :array
|
27
|
+
array_values.first.line
|
28
|
+
else
|
29
|
+
self.last.first if self.last.is_a? Array
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# return child nodes of a sexp node.
|
34
|
+
#
|
35
|
+
# @return [Array] child nodes.
|
36
|
+
def children
|
37
|
+
find_all { | sexp | Sexp === sexp }
|
38
|
+
end
|
39
|
+
|
40
|
+
# recursively find all child nodes, and yeild each child node.
|
41
|
+
def recursive_children
|
42
|
+
children.each do |child|
|
43
|
+
yield child
|
44
|
+
child.recursive_children { |c| yield c }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# grep all the recursive child nodes with conditions, and yield each match node.
|
49
|
+
#
|
50
|
+
# @param [Hash] options grep conditions
|
51
|
+
#
|
52
|
+
# options is the grep conditions, like
|
53
|
+
#
|
54
|
+
# sexp_type: :call,
|
55
|
+
# receiver: "Post",
|
56
|
+
# message: ["find", "new"]
|
57
|
+
# to_s: "devise"
|
58
|
+
#
|
59
|
+
# the condition key is one of :sexp_type, :receiver, :message, :to_s,
|
60
|
+
# the condition value can be Symbol, Array or Sexp.
|
61
|
+
def grep_nodes(options)
|
62
|
+
sexp_type = options[:sexp_type]
|
63
|
+
receiver = options[:receiver]
|
64
|
+
message = options[:message]
|
65
|
+
to_s = options[:to_s]
|
66
|
+
self.recursive_children do |child|
|
67
|
+
if (!sexp_type || (sexp_type.is_a?(Array) ? sexp_type.include?(child.sexp_type) : sexp_type == child.sexp_type)) &&
|
68
|
+
(!receiver || (receiver.is_a?(Array) ? receiver.include?(child.receiver.to_s) : receiver == child.receiver.to_s)) &&
|
69
|
+
(!message || (message.is_a?(Array) ? message.include?(child.message.to_s) : message == child.message.to_s)) &&
|
70
|
+
(!to_s || (to_s.is_a?(Array) ? to_s.include?(child.to_s) : to_s == child.to_s))
|
71
|
+
yield child
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# grep all the recursive child nodes with conditions, and yield the first match node.
|
77
|
+
#
|
78
|
+
# @param [Hash] options grep conditions
|
79
|
+
#
|
80
|
+
# options is the grep conditions, like
|
81
|
+
#
|
82
|
+
# sexp_type: :call,
|
83
|
+
# receiver: s(:const, Post),
|
84
|
+
# message: [:find, :new]
|
85
|
+
#
|
86
|
+
# the condition key is one of :sexp_type, :receiver, :message, and to_s,
|
87
|
+
# the condition value can be Symbol, Array or Sexp.
|
88
|
+
def grep_node(options)
|
89
|
+
result = CodeAnalyzer::Nil.new
|
90
|
+
grep_nodes(options) { |node| result = node; break; }
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
# grep all the recursive child nodes with conditions, and get the count of match nodes.
|
95
|
+
#
|
96
|
+
# @param [Hash] options grep conditions
|
97
|
+
# @return [Integer] the count of metch nodes
|
98
|
+
def grep_nodes_count(options)
|
99
|
+
count = 0
|
100
|
+
grep_nodes(options) { |node| count += 1 }
|
101
|
+
count
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get receiver node.
|
105
|
+
#
|
106
|
+
# s(:call,
|
107
|
+
# s(:var_ref,
|
108
|
+
# s(:@ident, "user", s(1, 0))
|
109
|
+
# ),
|
110
|
+
# :".",
|
111
|
+
# s(:@ident, "name", s(1, 5))
|
112
|
+
# )
|
113
|
+
# => s(:var_ref,
|
114
|
+
# s(:@ident, "user", s(1, 0))
|
115
|
+
# )
|
116
|
+
#
|
117
|
+
# @return [Sexp] receiver node
|
118
|
+
def receiver
|
119
|
+
case sexp_type
|
120
|
+
when :assign, :field, :call, :binary, :command_call
|
121
|
+
self[1]
|
122
|
+
when :method_add_arg, :method_add_block
|
123
|
+
self[1].receiver
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get the module name of the module node.
|
128
|
+
#
|
129
|
+
# s(:module,
|
130
|
+
# s(:const_ref, s(:@const, "Admin", s(1, 7))),
|
131
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
132
|
+
# )
|
133
|
+
# => s(:const_ref, s(:@const, "Admin", s(1, 7))),
|
134
|
+
#
|
135
|
+
# @return [Sexp] module name node
|
136
|
+
def module_name
|
137
|
+
if :module == sexp_type
|
138
|
+
self[1]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get the class name of the class node.
|
143
|
+
#
|
144
|
+
# s(:class,
|
145
|
+
# s(:const_ref, s(:@const, "User", s(1, 6))),
|
146
|
+
# nil,
|
147
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
148
|
+
# )
|
149
|
+
# => s(:const_ref, s(:@const, "User", s(1, 6))),
|
150
|
+
#
|
151
|
+
# @return [Sexp] class name node
|
152
|
+
def class_name
|
153
|
+
if :class == sexp_type
|
154
|
+
self[1]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get the base class of the class node.
|
159
|
+
#
|
160
|
+
# s(:class,
|
161
|
+
# s(:const_ref, s(:@const, "User", s(1, 6))),
|
162
|
+
# s(:const_path_ref, s(:var_ref, s(:@const, "ActiveRecord", s(1, 13))), s(:@const, "Base", s(1, 27))),
|
163
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
164
|
+
# )
|
165
|
+
# => s(:const_path_ref, s(:var_ref, s(:@const, "ActiveRecord", s(1, 13))), s(:@const, "Base", s(1, 27))),
|
166
|
+
#
|
167
|
+
# @return [Sexp] base class of class node
|
168
|
+
def base_class
|
169
|
+
if :class == sexp_type
|
170
|
+
self[2]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Get the left value of the assign node.
|
175
|
+
#
|
176
|
+
# s(:assign,
|
177
|
+
# s(:var_field, s(:@ident, "user", s(1, 0))),
|
178
|
+
# s(:var_ref, s(:@ident, "current_user", s(1, 7)))
|
179
|
+
# )
|
180
|
+
# => s(:var_field, s(:@ident, "user", s(1, 0))),
|
181
|
+
#
|
182
|
+
# @return [Symbol] left value of lasgn or iasgn node
|
183
|
+
def left_value
|
184
|
+
if :assign == sexp_type
|
185
|
+
self[1]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Get the right value of assign node.
|
190
|
+
#
|
191
|
+
# s(:assign,
|
192
|
+
# s(:var_field, s(:@ident, "user", s(1, 0))),
|
193
|
+
# s(:var_ref, s(:@ident, "current_user", s(1, 7)))
|
194
|
+
# )
|
195
|
+
# => s(:var_ref, s(:@ident, "current_user", s(1, 7)))
|
196
|
+
#
|
197
|
+
# @return [Sexp] right value of assign node
|
198
|
+
def right_value
|
199
|
+
if :assign == sexp_type
|
200
|
+
self[2]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Get the message node.
|
205
|
+
#
|
206
|
+
# s(:command,
|
207
|
+
# s(:@ident, "has_many", s(1, 0)),
|
208
|
+
# s(:args_add_block,
|
209
|
+
# s(:args_add, s(:args_new),
|
210
|
+
# s(:symbol_literal, s(:symbol, s(:@ident, "projects", s(1, 10))))
|
211
|
+
# ),
|
212
|
+
# false
|
213
|
+
# )
|
214
|
+
# )
|
215
|
+
# => s(:@ident, "has_many", s(1, 0)),
|
216
|
+
#
|
217
|
+
# @return [Symbol] message node
|
218
|
+
def message
|
219
|
+
case sexp_type
|
220
|
+
when :command, :fcall
|
221
|
+
self[1]
|
222
|
+
when :binary
|
223
|
+
self[2]
|
224
|
+
when :command_call, :field, :call
|
225
|
+
self[3]
|
226
|
+
when :method_add_arg, :method_add_block
|
227
|
+
self[1].message
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Get arguments node.
|
232
|
+
#
|
233
|
+
# s(:command,
|
234
|
+
# s(:@ident, "resources", s(1, 0)),
|
235
|
+
# s(:args_add_block,
|
236
|
+
# s(:args_add, s(:args_new),
|
237
|
+
# s(:symbol_literal, s(:symbol, s(:@ident, "posts", s(1, 11))))
|
238
|
+
# ), false
|
239
|
+
# )
|
240
|
+
# )
|
241
|
+
# => s(:args_add_block,
|
242
|
+
# s(:args_add, s(:args_new),
|
243
|
+
# s(:symbol_literal, s(:symbol, s(:@ident, "posts", s(1, 11))))
|
244
|
+
# ), false
|
245
|
+
# )
|
246
|
+
#
|
247
|
+
# @return [Sexp] arguments node
|
248
|
+
def arguments
|
249
|
+
case sexp_type
|
250
|
+
when :command
|
251
|
+
self[2]
|
252
|
+
when :command_call
|
253
|
+
self[4]
|
254
|
+
when :method_add_arg
|
255
|
+
self[2].arguments
|
256
|
+
when :method_add_block
|
257
|
+
self[1].arguments
|
258
|
+
when :arg_paren
|
259
|
+
self[1]
|
260
|
+
when :array
|
261
|
+
self
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Get only argument for binary.
|
266
|
+
#
|
267
|
+
# s(:binary,
|
268
|
+
# s(:var_ref, s(:@ident, "user", s(1, 0))),
|
269
|
+
# :==,
|
270
|
+
# s(:var_ref, s(:@ident, "current_user", s(1, 8)))
|
271
|
+
# )
|
272
|
+
# => s(:var_ref, s(:@ident, "current_user", s(1, 8)))
|
273
|
+
#
|
274
|
+
# @return [Sexp] argument node
|
275
|
+
def argument
|
276
|
+
if :binary == sexp_type
|
277
|
+
self[3]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Get all arguments.
|
282
|
+
#
|
283
|
+
# s(:args_add_block,
|
284
|
+
# s(:args_add,
|
285
|
+
# s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "hello", s(1, 6))))),
|
286
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "world", s(1, 15))))
|
287
|
+
# ), false
|
288
|
+
# )
|
289
|
+
# => [
|
290
|
+
# s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "hello", s(1, 6))))),
|
291
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "world", s(1, 15))))
|
292
|
+
# ]
|
293
|
+
#
|
294
|
+
# @return [Array] all arguments
|
295
|
+
def all
|
296
|
+
nodes = []
|
297
|
+
case sexp_type
|
298
|
+
when :args_add_block, :array
|
299
|
+
if :args_new == self[1].sexp_type
|
300
|
+
nodes << self[2]
|
301
|
+
else
|
302
|
+
node = self[1]
|
303
|
+
while true
|
304
|
+
if [:args_add, :args_add_star].include? node.sexp_type
|
305
|
+
nodes.unshift node[2]
|
306
|
+
node = node[1]
|
307
|
+
elsif :args_new == node.sexp_type
|
308
|
+
break
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
when :args_add
|
313
|
+
nodes.unshift self[2]
|
314
|
+
end
|
315
|
+
nodes
|
316
|
+
end
|
317
|
+
|
318
|
+
# Get the conditional statement of if node.
|
319
|
+
#
|
320
|
+
# s(:if,
|
321
|
+
# s(:var_ref, s(:@kw, "true", s(1, 3))),
|
322
|
+
# s(:stmts_add, s(:stmts_new), s(:void_stmt)),
|
323
|
+
# nil
|
324
|
+
# )
|
325
|
+
# => s(:var_ref, s(:@kw, "true", s(1, 3))),
|
326
|
+
#
|
327
|
+
# @return [Sexp] conditional statement of if node
|
328
|
+
def conditional_statement
|
329
|
+
if [:if, :unless, :elsif, :ifop].include? sexp_type
|
330
|
+
self[1]
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# Get all condition nodes.
|
335
|
+
#
|
336
|
+
# s(:binary,
|
337
|
+
# s(:binary,
|
338
|
+
# s(:var_ref, s(:@ident, "user", s(1, 0))),
|
339
|
+
# :==,
|
340
|
+
# s(:var_ref, s(:@ident, "current_user", s(1, 8)))
|
341
|
+
# ),
|
342
|
+
# :"&&",
|
343
|
+
# s(:call,
|
344
|
+
# s(:var_ref, s(:@ident, "user", s(1, 24))),
|
345
|
+
# :".",
|
346
|
+
# s(:@ident, "valid?", s(1, 29))
|
347
|
+
# )
|
348
|
+
# )
|
349
|
+
# => [
|
350
|
+
# s(:binary,
|
351
|
+
# s(:var_ref, s(:@ident, "user", s(1, 0))),
|
352
|
+
# :==,
|
353
|
+
# s(:var_ref, s(:@ident, "current_user", s(1, 8)))
|
354
|
+
# ),
|
355
|
+
# s(:call,
|
356
|
+
# s(:var_ref, s(:@ident, "user", s(1, 24))),
|
357
|
+
# :".",
|
358
|
+
# s(:@ident, "valid?", s(1, 29))
|
359
|
+
# )
|
360
|
+
# ]
|
361
|
+
#
|
362
|
+
# @return [Array] all condition nodes
|
363
|
+
def all_conditions
|
364
|
+
nodes = []
|
365
|
+
if :binary == sexp_type && %w(&& || and or).include?(self[2].to_s)
|
366
|
+
if :binary == self[1].sexp_type && %w(&& || and or).include?(self[1][2].to_s)
|
367
|
+
nodes += self[1].all_conditions
|
368
|
+
else
|
369
|
+
nodes << self[1]
|
370
|
+
end
|
371
|
+
if :binary == self[3].sexp_type && %w(&& || and or).include?(self[3][2].to_s)
|
372
|
+
nodes += self[3].all_conditions
|
373
|
+
else
|
374
|
+
nodes << self[3]
|
375
|
+
end
|
376
|
+
else
|
377
|
+
self
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# Get the method name of def node.
|
382
|
+
#
|
383
|
+
# s(:def,
|
384
|
+
# s(:@ident, "show", s(1, 4)),
|
385
|
+
# s(:params, nil, nil, nil, nil, nil),
|
386
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
387
|
+
# )
|
388
|
+
# => s(:@ident, "show", s(1, 4)),
|
389
|
+
#
|
390
|
+
# @return [Sexp] method name node
|
391
|
+
def method_name
|
392
|
+
case sexp_type
|
393
|
+
when :def
|
394
|
+
self[1]
|
395
|
+
when :defs
|
396
|
+
self[3]
|
397
|
+
else
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# Get body node.
|
402
|
+
#
|
403
|
+
# s(:class,
|
404
|
+
# s(:const_ref, s(:@const, "User", s(1, 6))),
|
405
|
+
# nil,
|
406
|
+
# s(:bodystmt,
|
407
|
+
# s(:stmts_add, s(:stmts_new),
|
408
|
+
# s(:def,
|
409
|
+
# s(:@ident, "login", s(1, 16)),
|
410
|
+
# s(:params, nil, nil, nil, nil, nil),
|
411
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
412
|
+
# )
|
413
|
+
# ), nil, nil, nil
|
414
|
+
# )
|
415
|
+
# )
|
416
|
+
# => s(:bodystmt,
|
417
|
+
# s(:stmts_add, s(:stmts_new),
|
418
|
+
# s(:def,
|
419
|
+
# s(:@ident, "login", s(1, 16)),
|
420
|
+
# s(:params, nil, nil, nil, nil, nil),
|
421
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
422
|
+
# )
|
423
|
+
# ), nil, nil, nil
|
424
|
+
# )
|
425
|
+
#
|
426
|
+
# @return [Sexp] body node
|
427
|
+
def body
|
428
|
+
case sexp_type
|
429
|
+
when :else
|
430
|
+
self[1]
|
431
|
+
when :module, :if, :elsif, :unless
|
432
|
+
self[2]
|
433
|
+
when :class, :def
|
434
|
+
self[3]
|
435
|
+
when :defs
|
436
|
+
self[5]
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
# Get block node.
|
441
|
+
#
|
442
|
+
# s(:method_add_block,
|
443
|
+
# s(:command,
|
444
|
+
# s(:@ident, "resources", s(1, 0)),
|
445
|
+
# s(:args_add_block, s(:args_add, s(:args_new), s(:symbol_literal, s(:symbol, s(:@ident, "posts", s(1, 11))))), false)
|
446
|
+
# ),
|
447
|
+
# s(:do_block, nil,
|
448
|
+
# s(:stmts_add, s(:stmts_add, s(:stmts_new), s(:void_stmt)),
|
449
|
+
# s(:command,
|
450
|
+
# s(:@ident, "resources", s(1, 21)),
|
451
|
+
# s(:args_add_block, s(:args_add, s(:args_new), s(:symbol_literal, s(:symbol, s(:@ident, "comments", s(1, 32))))), false))
|
452
|
+
# )
|
453
|
+
# )
|
454
|
+
# )
|
455
|
+
# => s(:do_block, nil,
|
456
|
+
# s(:stmts_add, s(:stmts_add, s(:stmts_new), s(:void_stmt)),
|
457
|
+
# s(:command,
|
458
|
+
# s(:@ident, "resources", s(1, 21)),
|
459
|
+
# s(:args_add_block, s(:args_add, s(:args_new), s(:symbol_literal, s(:symbol, s(:@ident, "comments", s(1, 32))))), false))
|
460
|
+
# )
|
461
|
+
# )
|
462
|
+
#
|
463
|
+
# @return [Sexp] body node
|
464
|
+
def block
|
465
|
+
case sexp_type
|
466
|
+
when :method_add_block
|
467
|
+
self[2]
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
# Get all statements nodes.
|
472
|
+
#
|
473
|
+
# s(:bodystmt,
|
474
|
+
# s(:stmts_add,
|
475
|
+
# s(:stmts_add, s(:stmts_new),
|
476
|
+
# s(:def,
|
477
|
+
# s(:@ident, "login?", s(1, 16)),
|
478
|
+
# s(:params, nil, nil, nil, nil, nil),
|
479
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
480
|
+
# )
|
481
|
+
# ),
|
482
|
+
# s(:def,
|
483
|
+
# s(:@ident, "admin?", s(1, 33)),
|
484
|
+
# s(:params, nil, nil, nil, nil, nil),
|
485
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
486
|
+
# )
|
487
|
+
# ), nil, nil, nil
|
488
|
+
# )
|
489
|
+
# => [
|
490
|
+
# s(:def,
|
491
|
+
# s(:@ident, "login?", s(1, 16)),
|
492
|
+
# s(:params, nil, nil, nil, nil, nil),
|
493
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
494
|
+
# ),
|
495
|
+
# s(:def,
|
496
|
+
# s(:@ident, "admin?", s(1, 33)),
|
497
|
+
# s(:params, nil, nil, nil, nil, nil),
|
498
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
499
|
+
# )
|
500
|
+
# ]
|
501
|
+
#
|
502
|
+
# @return [Array] all statements
|
503
|
+
def statements
|
504
|
+
stmts = []
|
505
|
+
node = case sexp_type
|
506
|
+
when :do_block, :brace_block
|
507
|
+
self[2]
|
508
|
+
when :bodystmt
|
509
|
+
self[1]
|
510
|
+
else
|
511
|
+
end
|
512
|
+
if node
|
513
|
+
while true
|
514
|
+
if :stmts_add == node.sexp_type && s(:void_stmt) != node[2]
|
515
|
+
stmts.unshift node[2]
|
516
|
+
node = node[1]
|
517
|
+
else
|
518
|
+
break
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
stmts
|
523
|
+
end
|
524
|
+
|
525
|
+
# Get hash value node.
|
526
|
+
#
|
527
|
+
# s(:hash,
|
528
|
+
# s(:assoclist_from_args,
|
529
|
+
# s(
|
530
|
+
# s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
|
531
|
+
# s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
|
532
|
+
# )
|
533
|
+
# )
|
534
|
+
# )
|
535
|
+
# => s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))
|
536
|
+
#
|
537
|
+
# @return [Sexp] hash value node
|
538
|
+
def hash_value(key)
|
539
|
+
pair_nodes = case sexp_type
|
540
|
+
when :bare_assoc_hash
|
541
|
+
self[1]
|
542
|
+
when :hash
|
543
|
+
self[1][1]
|
544
|
+
else
|
545
|
+
end
|
546
|
+
if pair_nodes
|
547
|
+
pair_nodes.size.times do |i|
|
548
|
+
if key == pair_nodes[i][1].to_s
|
549
|
+
return pair_nodes[i][2]
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
CodeAnalyzer::Nil.new
|
554
|
+
end
|
555
|
+
|
556
|
+
# Get hash size.
|
557
|
+
#
|
558
|
+
# s(:hash,
|
559
|
+
# s(:assoclist_from_args,
|
560
|
+
# s(
|
561
|
+
# s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
|
562
|
+
# s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
|
563
|
+
# )
|
564
|
+
# )
|
565
|
+
# )
|
566
|
+
# => 2
|
567
|
+
#
|
568
|
+
# @return [Integer] hash size
|
569
|
+
def hash_size
|
570
|
+
case sexp_type
|
571
|
+
when :hash
|
572
|
+
self[1].hash_size
|
573
|
+
when :assoclist_from_args
|
574
|
+
self[1].size
|
575
|
+
when :bare_assoc_hash
|
576
|
+
self[1].size
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
# Get the hash keys.
|
581
|
+
#
|
582
|
+
# s(:hash,
|
583
|
+
# s(:assoclist_from_args,
|
584
|
+
# s(
|
585
|
+
# s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
|
586
|
+
# s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
|
587
|
+
# )
|
588
|
+
# )
|
589
|
+
# )
|
590
|
+
# => ["first_name", "last_name"]
|
591
|
+
#
|
592
|
+
# @return [Array] hash keys
|
593
|
+
def hash_keys
|
594
|
+
pair_nodes = case sexp_type
|
595
|
+
when :bare_assoc_hash
|
596
|
+
self[1]
|
597
|
+
when :hash
|
598
|
+
self[1][1]
|
599
|
+
else
|
600
|
+
end
|
601
|
+
if pair_nodes
|
602
|
+
keys = []
|
603
|
+
pair_nodes.size.times do |i|
|
604
|
+
keys << pair_nodes[i][1].to_s
|
605
|
+
end
|
606
|
+
keys
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
# Get the hash values.
|
611
|
+
#
|
612
|
+
# s(:hash,
|
613
|
+
# s(:assoclist_from_args,
|
614
|
+
# s(
|
615
|
+
# s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
|
616
|
+
# s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
|
617
|
+
# )
|
618
|
+
# )
|
619
|
+
# )
|
620
|
+
# => [
|
621
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14)))),
|
622
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36))))
|
623
|
+
# ]
|
624
|
+
#
|
625
|
+
# @return [Array] hash values
|
626
|
+
def hash_values
|
627
|
+
pair_nodes = case sexp_type
|
628
|
+
when :bare_assoc_hash
|
629
|
+
self[1]
|
630
|
+
when :hash
|
631
|
+
self[1][1]
|
632
|
+
else
|
633
|
+
end
|
634
|
+
if pair_nodes
|
635
|
+
values = []
|
636
|
+
pair_nodes.size.times do |i|
|
637
|
+
values << pair_nodes[i][2]
|
638
|
+
end
|
639
|
+
values
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
# Get the array size.
|
644
|
+
#
|
645
|
+
# s(:array,
|
646
|
+
# s(:args_add,
|
647
|
+
# s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2))))),
|
648
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
|
649
|
+
# )
|
650
|
+
# )
|
651
|
+
# => 2
|
652
|
+
#
|
653
|
+
# @return [Integer] array size
|
654
|
+
def array_size
|
655
|
+
if :array == sexp_type
|
656
|
+
first_node = self[1]
|
657
|
+
array_size = 0
|
658
|
+
if first_node
|
659
|
+
while true
|
660
|
+
array_size += 1
|
661
|
+
first_node = s(:args_new) == first_node[1] ? first_node[2] : first_node[1]
|
662
|
+
if :args_add != first_node.sexp_type
|
663
|
+
if :array == first_node.sexp_type
|
664
|
+
array_size += first_node.array_size
|
665
|
+
end
|
666
|
+
break
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|
670
|
+
array_size
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
# Get the array values.
|
675
|
+
#
|
676
|
+
# s(:array,
|
677
|
+
# s(:args_add,
|
678
|
+
# s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2))))),
|
679
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
|
680
|
+
# )
|
681
|
+
# )
|
682
|
+
# => [
|
683
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2)))),
|
684
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
|
685
|
+
# ]
|
686
|
+
#
|
687
|
+
# @return [Array] array values
|
688
|
+
def array_values
|
689
|
+
if :array == sexp_type
|
690
|
+
if nil == self[1]
|
691
|
+
[]
|
692
|
+
elsif :qwords_add == self[1].sexp_type
|
693
|
+
self[1].array_values
|
694
|
+
else
|
695
|
+
arguments.all
|
696
|
+
end
|
697
|
+
elsif :qwords_add
|
698
|
+
values = []
|
699
|
+
node = self
|
700
|
+
while true
|
701
|
+
if :qwords_add == node.sexp_type
|
702
|
+
values.unshift node[2]
|
703
|
+
node = node[1]
|
704
|
+
elsif :qwords_new == node.sexp_type
|
705
|
+
break
|
706
|
+
end
|
707
|
+
end
|
708
|
+
values
|
709
|
+
end
|
710
|
+
end
|
711
|
+
|
712
|
+
# old method for alias node.
|
713
|
+
#
|
714
|
+
# s(:alias,
|
715
|
+
# s(:symbol_literal, s(:@ident, "new", s(1, 6))),
|
716
|
+
# s(:symbol_literal, s(:@ident, "old", s(1, 10)))
|
717
|
+
# )
|
718
|
+
# => s(:symbol_literal, s(:@ident, "old", s(1, 10))),
|
719
|
+
def old_method
|
720
|
+
self[2]
|
721
|
+
end
|
722
|
+
|
723
|
+
# new method for alias node.
|
724
|
+
#
|
725
|
+
# s(:alias,
|
726
|
+
# s(:symbol_literal, s(:@ident, "new", s(1, 6))),
|
727
|
+
# s(:symbol_literal, s(:@ident, "old", s(1, 10)))
|
728
|
+
# )
|
729
|
+
# => s(:symbol_literal, s(:@ident, "new", s(1, 6))),
|
730
|
+
def new_method
|
731
|
+
self[1]
|
732
|
+
end
|
733
|
+
|
734
|
+
# To object.
|
735
|
+
#
|
736
|
+
# s(:array,
|
737
|
+
# s(:args_add,
|
738
|
+
# s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2))))),
|
739
|
+
# s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
|
740
|
+
# )
|
741
|
+
# )
|
742
|
+
# => ["first_name", "last_name"]
|
743
|
+
#
|
744
|
+
# @return [Object]
|
745
|
+
def to_object
|
746
|
+
case sexp_type
|
747
|
+
when :array
|
748
|
+
array_values.map(&:to_s)
|
749
|
+
else
|
750
|
+
to_s
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
# to_s.
|
755
|
+
#
|
756
|
+
# @return [String] to_s
|
757
|
+
def to_s
|
758
|
+
case sexp_type
|
759
|
+
when :string_literal, :xstring_literal, :string_content, :const_ref, :symbol_literal, :symbol,
|
760
|
+
:args_add_block, :var_ref, :vcall, :var_field,
|
761
|
+
:@ident, :@tstring_content, :@const, :@ivar, :@kw, :@gvar, :@cvar
|
762
|
+
self[1].to_s
|
763
|
+
when :string_add
|
764
|
+
if s(:string_content) == self[1]
|
765
|
+
self[2].to_s
|
766
|
+
else
|
767
|
+
self[1].to_s
|
768
|
+
end
|
769
|
+
when :args_add
|
770
|
+
if s(:args_new) == self[1]
|
771
|
+
self[2].to_s
|
772
|
+
else
|
773
|
+
self[1].to_s
|
774
|
+
end
|
775
|
+
when :qwords_add
|
776
|
+
if s(:qwords_new) == self[1]
|
777
|
+
self[2].to_s
|
778
|
+
else
|
779
|
+
self[1].to_s
|
780
|
+
end
|
781
|
+
when :const_path_ref
|
782
|
+
"#{self[1]}::#{self[2]}"
|
783
|
+
when :@label
|
784
|
+
self[1].to_s[0..-2]
|
785
|
+
when :aref
|
786
|
+
"#{self[1]}[#{self[2]}]"
|
787
|
+
when :call, :field
|
788
|
+
"#{self.receiver}.#{self.message}"
|
789
|
+
else
|
790
|
+
""
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
# check if the self node is a const.
|
795
|
+
def const?
|
796
|
+
:@const == self.sexp_type || ([:var_ref, :vcall].include?(self.sexp_type) && :@const == self[1].sexp_type)
|
797
|
+
end
|
798
|
+
|
799
|
+
# true
|
800
|
+
def present?
|
801
|
+
true
|
802
|
+
end
|
803
|
+
|
804
|
+
# false
|
805
|
+
def blank?
|
806
|
+
false
|
807
|
+
end
|
808
|
+
|
809
|
+
# remove the line and column info from sexp.
|
810
|
+
def remove_line_and_column
|
811
|
+
node = self.clone
|
812
|
+
last_node = node.last
|
813
|
+
if Sexp === last_node && last_node.size == 2 && last_node.first.is_a?(Integer) && last_node.last.is_a?(Integer)
|
814
|
+
node.delete_at(-1)
|
815
|
+
end
|
816
|
+
node.sexp_body.each_with_index do |child, index|
|
817
|
+
if Sexp === child
|
818
|
+
node[index+1] = child.remove_line_and_column
|
819
|
+
end
|
820
|
+
end
|
821
|
+
node
|
822
|
+
end
|
823
|
+
|
824
|
+
# if the return value of these methods is nil, then return CodeAnalyzer::Nil.new instead
|
825
|
+
[:sexp_type, :receiver, :message, :arguments, :argument, :class_name, :base_class, :method_name,
|
826
|
+
:body, :block, :conditional_statement, :left_value, :right_value].each do |method|
|
827
|
+
class_eval <<-EOS
|
828
|
+
alias_method :origin_#{method}, :#{method}
|
829
|
+
|
830
|
+
def #{method}
|
831
|
+
ret = origin_#{method}
|
832
|
+
ret.nil? ? CodeAnalyzer::Nil.new : ret
|
833
|
+
end
|
834
|
+
EOS
|
835
|
+
end
|
836
|
+
end
|