deep-cover 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/cov +43 -0
- data/bin/gemcov +8 -0
- data/bin/selfcov +21 -0
- data/bin/setup +8 -0
- data/bin/testall +88 -0
- data/deep_cover.gemspec +44 -0
- data/exe/deep-cover +6 -0
- data/future_read_me.md +108 -0
- data/lib/deep-cover.rb +1 -0
- data/lib/deep_cover.rb +11 -0
- data/lib/deep_cover/analyser.rb +24 -0
- data/lib/deep_cover/analyser/base.rb +51 -0
- data/lib/deep_cover/analyser/branch.rb +20 -0
- data/lib/deep_cover/analyser/covered_code_source.rb +31 -0
- data/lib/deep_cover/analyser/function.rb +12 -0
- data/lib/deep_cover/analyser/ignore_uncovered.rb +19 -0
- data/lib/deep_cover/analyser/node.rb +11 -0
- data/lib/deep_cover/analyser/per_char.rb +20 -0
- data/lib/deep_cover/analyser/per_line.rb +23 -0
- data/lib/deep_cover/analyser/statement.rb +31 -0
- data/lib/deep_cover/analyser/subset.rb +24 -0
- data/lib/deep_cover/auto_run.rb +49 -0
- data/lib/deep_cover/autoload_tracker.rb +75 -0
- data/lib/deep_cover/backports.rb +9 -0
- data/lib/deep_cover/base.rb +55 -0
- data/lib/deep_cover/builtin_takeover.rb +2 -0
- data/lib/deep_cover/cli/debugger.rb +93 -0
- data/lib/deep_cover/cli/deep_cover.rb +49 -0
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +105 -0
- data/lib/deep_cover/config.rb +52 -0
- data/lib/deep_cover/core_ext/autoload_overrides.rb +40 -0
- data/lib/deep_cover/core_ext/coverage_replacement.rb +26 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +24 -0
- data/lib/deep_cover/core_ext/require_overrides.rb +36 -0
- data/lib/deep_cover/coverage.rb +198 -0
- data/lib/deep_cover/covered_code.rb +138 -0
- data/lib/deep_cover/custom_requirer.rb +93 -0
- data/lib/deep_cover/node.rb +8 -0
- data/lib/deep_cover/node/arguments.rb +50 -0
- data/lib/deep_cover/node/assignments.rb +250 -0
- data/lib/deep_cover/node/base.rb +99 -0
- data/lib/deep_cover/node/begin.rb +25 -0
- data/lib/deep_cover/node/block.rb +53 -0
- data/lib/deep_cover/node/boolean.rb +22 -0
- data/lib/deep_cover/node/branch.rb +28 -0
- data/lib/deep_cover/node/case.rb +94 -0
- data/lib/deep_cover/node/collections.rb +21 -0
- data/lib/deep_cover/node/const.rb +10 -0
- data/lib/deep_cover/node/def.rb +38 -0
- data/lib/deep_cover/node/empty_body.rb +21 -0
- data/lib/deep_cover/node/exceptions.rb +74 -0
- data/lib/deep_cover/node/if.rb +36 -0
- data/lib/deep_cover/node/keywords.rb +84 -0
- data/lib/deep_cover/node/literals.rb +77 -0
- data/lib/deep_cover/node/loops.rb +72 -0
- data/lib/deep_cover/node/mixin/can_augment_children.rb +65 -0
- data/lib/deep_cover/node/mixin/check_completion.rb +16 -0
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +25 -0
- data/lib/deep_cover/node/mixin/executed_after_children.rb +13 -0
- data/lib/deep_cover/node/mixin/execution_location.rb +56 -0
- data/lib/deep_cover/node/mixin/flow_accounting.rb +63 -0
- data/lib/deep_cover/node/mixin/has_child.rb +138 -0
- data/lib/deep_cover/node/mixin/has_child_handler.rb +73 -0
- data/lib/deep_cover/node/mixin/has_tracker.rb +44 -0
- data/lib/deep_cover/node/mixin/is_statement.rb +18 -0
- data/lib/deep_cover/node/mixin/rewriting.rb +32 -0
- data/lib/deep_cover/node/mixin/wrapper.rb +13 -0
- data/lib/deep_cover/node/module.rb +64 -0
- data/lib/deep_cover/node/root.rb +18 -0
- data/lib/deep_cover/node/send.rb +83 -0
- data/lib/deep_cover/node/splat.rb +13 -0
- data/lib/deep_cover/node/variables.rb +14 -0
- data/lib/deep_cover/parser_ext/range.rb +40 -0
- data/lib/deep_cover/reporter.rb +6 -0
- data/lib/deep_cover/reporter/istanbul.rb +151 -0
- data/lib/deep_cover/tools.rb +18 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +50 -0
- data/lib/deep_cover/tools/camelize.rb +8 -0
- data/lib/deep_cover/tools/dump_covered_code.rb +32 -0
- data/lib/deep_cover/tools/execute_sample.rb +23 -0
- data/lib/deep_cover/tools/format.rb +16 -0
- data/lib/deep_cover/tools/format_char_cover.rb +18 -0
- data/lib/deep_cover/tools/format_generated_code.rb +25 -0
- data/lib/deep_cover/tools/number_lines.rb +18 -0
- data/lib/deep_cover/tools/our_coverage.rb +9 -0
- data/lib/deep_cover/tools/require_relative_dir.rb +10 -0
- data/lib/deep_cover/tools/silence_warnings.rb +15 -0
- data/lib/deep_cover/version.rb +3 -0
- metadata +326 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# TODO: must handle circular requires
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class CustomRequirer
|
5
|
+
attr_reader :load_path, :loaded_features
|
6
|
+
def initialize(load_path=$LOAD_PATH, loaded_features=$LOADED_FEATURES)
|
7
|
+
@load_path = load_path
|
8
|
+
@loaded_features = loaded_features
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns a path to an existing file or nil if none can be found.
|
12
|
+
# The search follows how ruby search for files using the $LOAD_PATH
|
13
|
+
#
|
14
|
+
# An absolute path is returned directly if it exists, otherwise nil
|
15
|
+
# is returned without searching anywhere else.
|
16
|
+
def resolve_path(path)
|
17
|
+
path = File.absolute_path(path) if path.start_with?('./') || path.start_with?('../')
|
18
|
+
|
19
|
+
if Pathname.new(path).absolute?
|
20
|
+
return path if File.exists?(path)
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
|
24
|
+
@load_path.each do |load_path|
|
25
|
+
possible_path = File.absolute_path(path, load_path)
|
26
|
+
return possible_path if File.exists?(possible_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Homemade #require to be able to instrument the code before it gets executed.
|
33
|
+
# Returns true when everything went right. (Same as regular ruby)
|
34
|
+
# Returns false when the found file was already required. (Same as regular ruby)
|
35
|
+
# Returns :not_found if the file couldn't be found.
|
36
|
+
# Caller should delegate to the default #require.
|
37
|
+
# Returns :cover_failed if DeepCover couldn't apply instrumentation the file found.
|
38
|
+
# Caller should delegate to the default #require.
|
39
|
+
# Returns :not_supported for files that are not supported (such as ike .so files)
|
40
|
+
# Caller should delegate to the default #require.
|
41
|
+
# Exceptions raised by the required code bubble up as normal.
|
42
|
+
# It is *NOT* recommended to simply delegate to the default #require, since it
|
43
|
+
# might not be safe to run part of the code again.
|
44
|
+
def require(path)
|
45
|
+
ext = File.extname(path)
|
46
|
+
return :not_supported if ext == '.so'
|
47
|
+
path = path + '.rb' if ext != '.rb'
|
48
|
+
return false if @loaded_features.include?(path)
|
49
|
+
|
50
|
+
found_path = resolve_path(path)
|
51
|
+
|
52
|
+
return :not_found unless found_path
|
53
|
+
return false if @loaded_features.include?(found_path)
|
54
|
+
|
55
|
+
covered_code = cover_and_execute(found_path)
|
56
|
+
return covered_code if covered_code.is_a?(Symbol)
|
57
|
+
|
58
|
+
@loaded_features << found_path
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
# Homemade #load to be able to instrument the code before it gets executed.
|
63
|
+
# Note, this doesn't support the `wrap` parameter that ruby's #load has.
|
64
|
+
# Same return/raise as CustomRequirer#require, except:
|
65
|
+
# Cannot return false since #load doesn't care about a file already being executed.
|
66
|
+
def load(path)
|
67
|
+
found_path = resolve_path(path)
|
68
|
+
|
69
|
+
if found_path.nil?
|
70
|
+
# #load has a final fallback of always trying relative to current work directory of process
|
71
|
+
possible_path = File.absolute_path(path)
|
72
|
+
found_path = possible_path if File.exists?(possible_path)
|
73
|
+
end
|
74
|
+
|
75
|
+
return :not_found unless found_path
|
76
|
+
|
77
|
+
covered_code = cover_and_execute(found_path)
|
78
|
+
return covered_code if covered_code.is_a?(Symbol)
|
79
|
+
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
def cover_and_execute(path)
|
85
|
+
covered_code = DeepCover.coverage.covered_code(path)
|
86
|
+
return :cover_failed unless covered_code
|
87
|
+
DeepCover.autoload_tracker.wrap_require(path) do
|
88
|
+
covered_code.execute_code
|
89
|
+
end
|
90
|
+
covered_code
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'assignments'
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class Node
|
5
|
+
class Arg < Node
|
6
|
+
has_child name: Symbol
|
7
|
+
def executable?
|
8
|
+
false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
Kwarg = Arg
|
12
|
+
|
13
|
+
class Restarg < Node
|
14
|
+
has_child name: [Symbol, nil]
|
15
|
+
def executable?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
Kwrestarg = Restarg
|
20
|
+
|
21
|
+
class Optarg < Node
|
22
|
+
has_tracker :default
|
23
|
+
has_child name: Symbol
|
24
|
+
has_child default: Node, flow_entry_count: :default_tracker_hits, rewrite: '(%{default_tracker};%{node})'
|
25
|
+
executed_loc_keys :name, :operator
|
26
|
+
|
27
|
+
def executable?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
Kwoptarg = Optarg
|
32
|
+
|
33
|
+
# foo(&block)
|
34
|
+
class Blockarg < Node
|
35
|
+
has_child name: Symbol
|
36
|
+
|
37
|
+
def executable?
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Args < Node
|
43
|
+
has_extra_children arguments: [Arg, Optarg, Restarg, Kwarg, Kwoptarg, Kwrestarg, Blockarg, Mlhs]
|
44
|
+
|
45
|
+
def executable?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require_relative "const"
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class Node
|
5
|
+
class VariableAssignment < Node
|
6
|
+
has_child var_name: Symbol
|
7
|
+
has_child value: [Node, nil]
|
8
|
+
executed_loc_keys :name, :operator
|
9
|
+
|
10
|
+
def execution_count
|
11
|
+
return super unless value
|
12
|
+
value.flow_completion_count
|
13
|
+
end
|
14
|
+
end
|
15
|
+
Cvasgn = Gvasgn = Ivasgn = Lvasgn = VariableAssignment
|
16
|
+
|
17
|
+
class Casgn < Node
|
18
|
+
has_child cbase: [Cbase, Const, nil]
|
19
|
+
has_child var_name: Symbol
|
20
|
+
has_child value: [Node, nil]
|
21
|
+
|
22
|
+
def execution_count
|
23
|
+
return super unless value
|
24
|
+
value.flow_completion_count
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Mlhs < Node
|
29
|
+
has_extra_children being_set: Node
|
30
|
+
# TODO
|
31
|
+
end
|
32
|
+
|
33
|
+
module BackwardsStrategy
|
34
|
+
# Instead of assuming our parent tracks our entry and we are responsible
|
35
|
+
# for tracking our completion, we go the other way and assume our parent
|
36
|
+
# tracks our completion and we are responsible for our entry.
|
37
|
+
def flow_completion_count
|
38
|
+
if (s = next_sibling)
|
39
|
+
s.flow_entry_count
|
40
|
+
else
|
41
|
+
parent.flow_completion_count
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def flow_entry_count
|
46
|
+
if (first_child = children_nodes.first)
|
47
|
+
first_child.flow_entry_count
|
48
|
+
else
|
49
|
+
flow_completion_count
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# a, b = ...
|
55
|
+
class Masgn < Node
|
56
|
+
class BackwardsNode < Node
|
57
|
+
include BackwardsStrategy
|
58
|
+
end
|
59
|
+
|
60
|
+
class SelfReceiver < BackwardsNode
|
61
|
+
executed_loc_keys :expression
|
62
|
+
end
|
63
|
+
|
64
|
+
class ConstantCbase < BackwardsNode
|
65
|
+
end
|
66
|
+
|
67
|
+
class DynamicReceiverWrap < Node
|
68
|
+
include Wrapper
|
69
|
+
has_tracker :entry
|
70
|
+
has_child actual_receiver: Node
|
71
|
+
def rewrite
|
72
|
+
# The local=local is to avoid Ruby warning about "Possible use of value in void context"
|
73
|
+
'(%{local} = (%{node});%{entry_tracker}; %{local}=%{local})'
|
74
|
+
end
|
75
|
+
alias_method :flow_entry_count, :entry_tracker_hits
|
76
|
+
end
|
77
|
+
|
78
|
+
class Setter < Node
|
79
|
+
include BackwardsStrategy
|
80
|
+
has_child receiver: {self: SelfReceiver, Parser::AST::Node => DynamicReceiverWrap}
|
81
|
+
has_child method_name: Symbol
|
82
|
+
has_child arg: [Node, nil] # When method is :[]=
|
83
|
+
executed_loc_keys :dot, :selector_begin, :selector_end
|
84
|
+
|
85
|
+
def loc_hash
|
86
|
+
base = super
|
87
|
+
if method_name == :[]=
|
88
|
+
selector = base[:selector]
|
89
|
+
{
|
90
|
+
expression: base[:expression],
|
91
|
+
selector_begin: selector.resize(1),
|
92
|
+
# The = is implicit, so only backtrack the end by one
|
93
|
+
selector_end: Parser::Source::Range.new(selector.source_buffer, selector.end_pos - 1, selector.end_pos),
|
94
|
+
}
|
95
|
+
else
|
96
|
+
{
|
97
|
+
dot: base[:dot],
|
98
|
+
expression: base[:expression],
|
99
|
+
selector_begin: base[:selector],
|
100
|
+
selector_end: nil#,
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class ConstantScopeWrapper < Node
|
107
|
+
include Wrapper
|
108
|
+
has_tracker :entry
|
109
|
+
has_child actual_node: Node
|
110
|
+
|
111
|
+
def rewrite
|
112
|
+
'(%{entry_tracker};%{node})'
|
113
|
+
end
|
114
|
+
|
115
|
+
def flow_entry_count
|
116
|
+
entry_tracker_hits
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class ConstantAssignment < Node
|
121
|
+
include BackwardsStrategy
|
122
|
+
has_child scope: [nil], remap: {cbase: ConstantCbase, Parser::AST::Node => ConstantScopeWrapper}
|
123
|
+
has_child constant_name: Symbol
|
124
|
+
|
125
|
+
def execution_count
|
126
|
+
scope ? scope.flow_completion_count : super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class VariableAssignment < Node
|
131
|
+
include BackwardsStrategy
|
132
|
+
has_child var_name: Symbol
|
133
|
+
end
|
134
|
+
|
135
|
+
BASE_MAP = {
|
136
|
+
cvasgn: VariableAssignment, gvasgn: VariableAssignment,
|
137
|
+
ivasgn: VariableAssignment, lvasgn: VariableAssignment,
|
138
|
+
casgn: ConstantAssignment,
|
139
|
+
send: Setter,
|
140
|
+
}
|
141
|
+
class Splat < Node
|
142
|
+
include BackwardsStrategy
|
143
|
+
has_child rest_arg: BASE_MAP
|
144
|
+
executed_loc_keys :operator
|
145
|
+
end
|
146
|
+
|
147
|
+
class LeftSide < Node
|
148
|
+
include BackwardsStrategy
|
149
|
+
has_extra_children receivers: {
|
150
|
+
splat: Splat,
|
151
|
+
mlhs: LeftSide,
|
152
|
+
**BASE_MAP,
|
153
|
+
}
|
154
|
+
executed_loc_keys # none
|
155
|
+
|
156
|
+
def flow_completion_count
|
157
|
+
parent.flow_completion_count
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
check_completion
|
162
|
+
|
163
|
+
has_child left: {mlhs: LeftSide}
|
164
|
+
has_child value: Node
|
165
|
+
|
166
|
+
executed_loc_keys :operator
|
167
|
+
|
168
|
+
def execution_count
|
169
|
+
value.flow_completion_count
|
170
|
+
end
|
171
|
+
|
172
|
+
def children_nodes_in_flow_order
|
173
|
+
[value, left]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class VariableOperatorAssign < Node
|
178
|
+
has_child var_name: Symbol
|
179
|
+
end
|
180
|
+
|
181
|
+
class ConstantOperatorAssign < Node
|
182
|
+
has_child scope: [Node, nil]
|
183
|
+
has_child const_name: Symbol
|
184
|
+
def execution_count
|
185
|
+
flow_completion_count
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class SendOperatorAssign < Node
|
190
|
+
has_child receiver: [Node, nil]
|
191
|
+
has_child method_name: Symbol
|
192
|
+
has_extra_children arguments: Node
|
193
|
+
executed_loc_keys :dot, :selector_begin, :selector_end, :operator
|
194
|
+
|
195
|
+
def loc_hash
|
196
|
+
base = super
|
197
|
+
hash = { expression: base[:expression], begin: base[:begin], end: base[:end], dot: base[:dot]}
|
198
|
+
selector = base[:selector]
|
199
|
+
|
200
|
+
if [:[], :[]=].include?(method_name)
|
201
|
+
hash[:selector_begin] = selector.resize(1)
|
202
|
+
hash[:selector_end] = Parser::Source::Range.new(selector.source_buffer, selector.end_pos - 1, selector.end_pos)
|
203
|
+
else
|
204
|
+
hash[:selector_begin] = base[:selector]
|
205
|
+
end
|
206
|
+
|
207
|
+
hash
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# foo += bar
|
212
|
+
class OpAsgn < Node
|
213
|
+
check_completion
|
214
|
+
has_tracker :reader
|
215
|
+
has_child receiver: {
|
216
|
+
lvasgn: VariableOperatorAssign, ivasgn: VariableOperatorAssign,
|
217
|
+
cvasgn: VariableOperatorAssign, gvasgn: VariableOperatorAssign,
|
218
|
+
casgn: Casgn, # TODO
|
219
|
+
send: SendOperatorAssign,
|
220
|
+
}
|
221
|
+
has_child operator: Symbol
|
222
|
+
has_child value: Node, rewrite: '(%{reader_tracker};%{node})', flow_entry_count: :reader_tracker_hits
|
223
|
+
executed_loc_keys :operator
|
224
|
+
|
225
|
+
def execution_count
|
226
|
+
flow_completion_count
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# foo ||= bar, foo &&= base
|
231
|
+
class BooleanAssignment < Node
|
232
|
+
check_completion
|
233
|
+
has_tracker :long_branch
|
234
|
+
has_child receiver: {
|
235
|
+
lvasgn: VariableOperatorAssign, ivasgn: VariableOperatorAssign,
|
236
|
+
cvasgn: VariableOperatorAssign, gvasgn: VariableOperatorAssign,
|
237
|
+
casgn: ConstantOperatorAssign,
|
238
|
+
send: SendOperatorAssign,
|
239
|
+
}
|
240
|
+
has_child value: Node, rewrite: '(%{long_branch_tracker};%{node})', flow_entry_count: :long_branch_tracker_hits
|
241
|
+
executed_loc_keys :operator
|
242
|
+
|
243
|
+
def execution_count
|
244
|
+
flow_completion_count
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
OrAsgn = AndAsgn = BooleanAssignment
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module DeepCover
|
2
|
+
# Base class to handle covered nodes.
|
3
|
+
class Node
|
4
|
+
include Mixin
|
5
|
+
include HasTracker
|
6
|
+
include HasChild
|
7
|
+
include HasChildHandler
|
8
|
+
include CanAugmentChildren
|
9
|
+
include Rewriting
|
10
|
+
extend CheckCompletion
|
11
|
+
include FlowAccounting
|
12
|
+
include IsStatement
|
13
|
+
include ExecutionLocation
|
14
|
+
include ChildCanBeEmpty
|
15
|
+
|
16
|
+
attr_reader :index, :parent, :children, :base_node
|
17
|
+
|
18
|
+
def initialize(base_node, parent: raise, index: 0, base_children: base_node.children)
|
19
|
+
@base_node = base_node
|
20
|
+
@parent = parent
|
21
|
+
@index = index
|
22
|
+
@children = augment_children(base_children)
|
23
|
+
super()
|
24
|
+
end
|
25
|
+
|
26
|
+
### High level API for coverage purposes
|
27
|
+
|
28
|
+
def [](v)
|
29
|
+
children[v]
|
30
|
+
end
|
31
|
+
|
32
|
+
### Public API
|
33
|
+
|
34
|
+
def children_nodes
|
35
|
+
children.select{|c| c.is_a? Node }
|
36
|
+
end
|
37
|
+
alias_method :children_nodes_in_flow_order, :children_nodes
|
38
|
+
|
39
|
+
def next_sibling
|
40
|
+
parent.children_nodes_in_flow_order.each_cons(2) do |child, next_child|
|
41
|
+
return next_child if child.equal? self
|
42
|
+
end
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def previous_sibling
|
47
|
+
parent.children_nodes_in_flow_order.each_cons(2) do |previous_child, child|
|
48
|
+
return previous_child if child.equal? self
|
49
|
+
end
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# Adapted from https://github.com/whitequark/ast/blob/master/lib/ast/node.rb
|
54
|
+
def to_s(indent=0)
|
55
|
+
[
|
56
|
+
" " * indent,
|
57
|
+
'(',
|
58
|
+
fancy_type,
|
59
|
+
*children.map do |child, idx|
|
60
|
+
if child.is_a?(Node)
|
61
|
+
"\n#{child.to_s(indent + 1)}"
|
62
|
+
else
|
63
|
+
" #{child.inspect}"
|
64
|
+
end
|
65
|
+
end,
|
66
|
+
')'
|
67
|
+
].join
|
68
|
+
end
|
69
|
+
|
70
|
+
alias_method :inspect, :to_s
|
71
|
+
### Internal API
|
72
|
+
|
73
|
+
def covered_code
|
74
|
+
parent.covered_code
|
75
|
+
end
|
76
|
+
|
77
|
+
def type
|
78
|
+
return base_node.type if base_node
|
79
|
+
self.class.name.split('::').last.to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
def each_node(order = :postorder, &block)
|
83
|
+
return to_enum :each_node, order unless block_given?
|
84
|
+
yield self unless order == :postorder
|
85
|
+
children_nodes.each do |child|
|
86
|
+
child.each_node(order, &block)
|
87
|
+
end
|
88
|
+
yield self if order == :postorder
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
def fancy_type
|
93
|
+
class_name = self.class.to_s.gsub(/^DeepCover::/,'').gsub(/^Node::/, '')
|
94
|
+
t = type.to_s
|
95
|
+
t.casecmp(class_name) == 0 ? t : "#{t}[#{class_name}]"
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|