deep-cover 0.1.14 → 0.1.15
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 +4 -4
- data/.rubocop.yml +227 -0
- data/Gemfile +5 -2
- data/Rakefile +9 -6
- data/bin/console +3 -3
- data/bin/cov +8 -8
- data/bin/gemcov +2 -2
- data/bin/selfcov +5 -5
- data/bin/test_gems +11 -10
- data/bin/testall +6 -6
- data/deep_cover.gemspec +26 -21
- data/exe/deep-cover +1 -0
- data/lib/deep-cover.rb +2 -0
- data/lib/deep_cover.rb +3 -0
- data/lib/deep_cover/analyser.rb +2 -0
- data/lib/deep_cover/analyser/base.rb +2 -0
- data/lib/deep_cover/analyser/branch.rb +4 -2
- data/lib/deep_cover/analyser/covered_code_source.rb +3 -1
- data/lib/deep_cover/analyser/function.rb +3 -1
- data/lib/deep_cover/analyser/ignore_uncovered.rb +6 -4
- data/lib/deep_cover/analyser/node.rb +3 -0
- data/lib/deep_cover/analyser/optionally_covered.rb +12 -7
- data/lib/deep_cover/analyser/per_char.rb +7 -6
- data/lib/deep_cover/analyser/per_line.rb +9 -8
- data/lib/deep_cover/analyser/statement.rb +2 -0
- data/lib/deep_cover/analyser/subset.rb +4 -1
- data/lib/deep_cover/auto_run.rb +3 -0
- data/lib/deep_cover/autoload_tracker.rb +6 -3
- data/lib/deep_cover/backports.rb +2 -0
- data/lib/deep_cover/base.rb +11 -2
- data/lib/deep_cover/builtin_takeover.rb +2 -0
- data/lib/deep_cover/cli/debugger.rb +55 -30
- data/lib/deep_cover/cli/deep_cover.rb +17 -11
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +16 -14
- data/lib/deep_cover/config.rb +29 -16
- data/lib/deep_cover/core_ext/autoload_overrides.rb +2 -0
- data/lib/deep_cover/core_ext/coverage_replacement.rb +2 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +5 -6
- data/lib/deep_cover/core_ext/require_overrides.rb +6 -7
- data/lib/deep_cover/coverage.rb +21 -18
- data/lib/deep_cover/covered_code.rb +22 -12
- data/lib/deep_cover/custom_requirer.rb +82 -35
- data/lib/deep_cover/memoize.rb +48 -0
- data/lib/deep_cover/module_override.rb +2 -0
- data/lib/deep_cover/node.rb +14 -1
- data/lib/deep_cover/node/arguments.rb +2 -0
- data/lib/deep_cover/node/assignments.rb +32 -30
- data/lib/deep_cover/node/base.rb +30 -29
- data/lib/deep_cover/node/begin.rb +3 -1
- data/lib/deep_cover/node/block.rb +5 -2
- data/lib/deep_cover/node/branch.rb +2 -1
- data/lib/deep_cover/node/case.rb +15 -13
- data/lib/deep_cover/node/collections.rb +2 -0
- data/lib/deep_cover/node/const.rb +2 -0
- data/lib/deep_cover/node/def.rb +10 -8
- data/lib/deep_cover/node/empty_body.rb +2 -0
- data/lib/deep_cover/node/exceptions.rb +3 -1
- data/lib/deep_cover/node/if.rb +3 -1
- data/lib/deep_cover/node/keywords.rb +4 -2
- data/lib/deep_cover/node/literals.rb +2 -0
- data/lib/deep_cover/node/loops.rb +5 -3
- data/lib/deep_cover/node/mixin/can_augment_children.rb +8 -7
- data/lib/deep_cover/node/mixin/check_completion.rb +3 -1
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +4 -2
- data/lib/deep_cover/node/mixin/executed_after_children.rb +2 -0
- data/lib/deep_cover/node/mixin/execution_location.rb +4 -2
- data/lib/deep_cover/node/mixin/flow_accounting.rb +2 -0
- data/lib/deep_cover/node/mixin/has_child.rb +22 -18
- data/lib/deep_cover/node/mixin/has_child_handler.rb +10 -8
- data/lib/deep_cover/node/mixin/has_tracker.rb +4 -2
- data/lib/deep_cover/node/mixin/is_statement.rb +3 -1
- data/lib/deep_cover/node/mixin/rewriting.rb +5 -3
- data/lib/deep_cover/node/mixin/wrapper.rb +2 -0
- data/lib/deep_cover/node/module.rb +11 -9
- data/lib/deep_cover/node/root.rb +2 -0
- data/lib/deep_cover/node/send.rb +4 -2
- data/lib/deep_cover/node/short_circuit.rb +4 -2
- data/lib/deep_cover/node/splat.rb +2 -0
- data/lib/deep_cover/node/variables.rb +2 -0
- data/lib/deep_cover/parser_ext/range.rb +3 -1
- data/lib/deep_cover/problem_with_diagnostic.rb +11 -9
- data/lib/deep_cover/reporter.rb +2 -0
- data/lib/deep_cover/reporter/istanbul.rb +28 -24
- data/lib/deep_cover/tools.rb +2 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +6 -4
- data/lib/deep_cover/tools/camelize.rb +3 -1
- data/lib/deep_cover/tools/dasherize.rb +3 -1
- data/lib/deep_cover/tools/dump_covered_code.rb +7 -6
- data/lib/deep_cover/tools/execute_sample.rb +13 -13
- data/lib/deep_cover/tools/format.rb +3 -1
- data/lib/deep_cover/tools/format_char_cover.rb +4 -2
- data/lib/deep_cover/tools/format_generated_code.rb +3 -1
- data/lib/deep_cover/tools/number_lines.rb +2 -0
- data/lib/deep_cover/tools/our_coverage.rb +5 -3
- data/lib/deep_cover/tools/profiling.rb +66 -0
- data/lib/deep_cover/tools/require_relative_dir.rb +3 -1
- data/lib/deep_cover/tools/silence_warnings.rb +4 -1
- data/lib/deep_cover/tools/slice.rb +3 -1
- data/lib/deep_cover/tools/truncate_backtrace.rb +2 -0
- data/lib/deep_cover/version.rb +3 -1
- metadata +47 -30
@@ -1,11 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# TODO: must handle circular requires
|
2
4
|
|
3
5
|
module DeepCover
|
4
6
|
class CustomRequirer
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
class LoadPathsSubset
|
8
|
+
def initialize(load_paths: raise, lookup_paths: raise)
|
9
|
+
@original_load_paths = load_paths
|
10
|
+
@cached_load_paths_subset = []
|
11
|
+
@cached_load_paths_hash = nil
|
12
|
+
@lookup_paths = lookup_paths.map { |p| File.expand_path(p) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_paths
|
16
|
+
if @cached_load_paths_hash != (h = @original_load_paths.hash)
|
17
|
+
@cached_load_paths_subset = compute_subset
|
18
|
+
@cached_load_paths_hash = h
|
19
|
+
end
|
20
|
+
@cached_load_paths_subset
|
21
|
+
end
|
22
|
+
|
23
|
+
# E.g. '/a/b/' => true if a lookup path is '/a/b/c/', because '/a/b/' + 'c/ok' is within lookup.
|
24
|
+
def potentially_within_lookup?(full_dir_path)
|
25
|
+
@lookup_paths.any? { |p| p.start_with? full_dir_path }
|
26
|
+
end
|
27
|
+
|
28
|
+
# E.g. '/a/b' => true when a lookup path is '/a/'
|
29
|
+
def within_lookup?(full_path)
|
30
|
+
@lookup_paths.any? { |p| full_path.start_with? p }
|
31
|
+
end
|
32
|
+
|
33
|
+
def exist?(full_path)
|
34
|
+
within_lookup?(full_path) && File.exist?(full_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def compute_subset
|
40
|
+
@original_load_paths.map { |p| File.expand_path(p) }
|
41
|
+
.select { |p| within_lookup?(p) || potentially_within_lookup?(p) }
|
42
|
+
.freeze
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :load_paths, :loaded_features, :filter
|
47
|
+
def initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES, lookup_paths: nil, &filter)
|
48
|
+
@load_paths = load_paths
|
49
|
+
lookup_paths ||= Dir.getwd
|
50
|
+
lookup_paths = Array(lookup_paths)
|
51
|
+
@load_paths_subset = LoadPathsSubset.new(load_paths: load_paths, lookup_paths: lookup_paths) unless lookup_paths.include? '/'
|
8
52
|
@loaded_features = loaded_features
|
53
|
+
@filter = filter
|
9
54
|
end
|
10
55
|
|
11
56
|
# Returns a path to an existing file or nil if none can be found.
|
@@ -14,46 +59,46 @@ module DeepCover
|
|
14
59
|
# An absolute path is returned directly if it exists, otherwise nil
|
15
60
|
# is returned without searching anywhere else.
|
16
61
|
def resolve_path(path)
|
17
|
-
path = File.absolute_path(path) if path.start_with?('./'
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
62
|
+
path = File.absolute_path(path) if path.start_with?('./', '../')
|
63
|
+
|
64
|
+
abs_path = File.absolute_path(path)
|
65
|
+
if path == abs_path
|
66
|
+
path if (@load_paths_subset || File).exist?(path)
|
67
|
+
else
|
68
|
+
(@load_paths_subset || self).load_paths.each do |load_path|
|
69
|
+
possible_path = File.absolute_path(path, load_path)
|
70
|
+
return possible_path if (@load_paths_subset || File).exist?(possible_path)
|
71
|
+
end
|
72
|
+
nil
|
27
73
|
end
|
28
|
-
|
29
|
-
nil
|
30
74
|
end
|
31
75
|
|
32
76
|
# Homemade #require to be able to instrument the code before it gets executed.
|
33
77
|
# Returns true when everything went right. (Same as regular ruby)
|
34
78
|
# Returns false when the found file was already required. (Same as regular ruby)
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
79
|
+
# Throws :use_fallback in case caller should delegate to the default #require.
|
80
|
+
# Reasons given could be:
|
81
|
+
# - :not_found if the file couldn't be found.
|
82
|
+
# - :cover_failed if DeepCover couldn't apply instrumentation the file found.
|
83
|
+
# - :not_supported for files that are not supported (such as ike .so files)
|
84
|
+
# - :skipped if the filter block returned `true`
|
41
85
|
# Exceptions raised by the required code bubble up as normal.
|
42
86
|
# It is *NOT* recommended to simply delegate to the default #require, since it
|
43
87
|
# might not be safe to run part of the code again.
|
44
88
|
def require(path)
|
45
89
|
ext = File.extname(path)
|
46
|
-
|
47
|
-
path
|
90
|
+
throw :use_fallback, :not_supported if ext == '.so'
|
91
|
+
path += '.rb' if ext != '.rb'
|
48
92
|
return false if @loaded_features.include?(path)
|
49
93
|
|
50
94
|
found_path = resolve_path(path)
|
51
95
|
|
52
|
-
|
96
|
+
throw :use_fallback, :not_found unless found_path
|
53
97
|
return false if @loaded_features.include?(found_path)
|
54
98
|
|
55
|
-
|
56
|
-
|
99
|
+
throw :use_fallback, :skipped if filter && filter.call(found_path)
|
100
|
+
|
101
|
+
cover_and_execute(found_path)
|
57
102
|
|
58
103
|
@loaded_features << found_path
|
59
104
|
true
|
@@ -61,7 +106,7 @@ module DeepCover
|
|
61
106
|
|
62
107
|
# Homemade #load to be able to instrument the code before it gets executed.
|
63
108
|
# Note, this doesn't support the `wrap` parameter that ruby's #load has.
|
64
|
-
# Same return/
|
109
|
+
# Same return/throw as CustomRequirer#require, except:
|
65
110
|
# Cannot return false since #load doesn't care about a file already being executed.
|
66
111
|
def load(path)
|
67
112
|
found_path = resolve_path(path)
|
@@ -69,18 +114,18 @@ module DeepCover
|
|
69
114
|
if found_path.nil?
|
70
115
|
# #load has a final fallback of always trying relative to current work directory of process
|
71
116
|
possible_path = File.absolute_path(path)
|
72
|
-
found_path = possible_path if File.exist?(possible_path)
|
117
|
+
found_path = possible_path if (@load_paths_subset || File).exist?(possible_path)
|
73
118
|
end
|
74
119
|
|
75
|
-
|
120
|
+
throw :use_fallback, :not_found unless found_path
|
76
121
|
|
77
|
-
|
78
|
-
return covered_code if covered_code.is_a?(Symbol)
|
122
|
+
cover_and_execute(found_path)
|
79
123
|
|
80
124
|
true
|
81
125
|
end
|
82
126
|
|
83
127
|
protected
|
128
|
+
|
84
129
|
def cover_and_execute(path)
|
85
130
|
begin
|
86
131
|
covered_code = DeepCover.coverage.covered_code(path)
|
@@ -90,15 +135,17 @@ module DeepCover
|
|
90
135
|
else
|
91
136
|
warn "The file #{path} can't be instrumented"
|
92
137
|
end
|
93
|
-
|
138
|
+
throw :use_fallback, :cover_failed
|
94
139
|
end
|
95
140
|
DeepCover.autoload_tracker.wrap_require(path) do
|
96
141
|
begin
|
97
142
|
covered_code.execute_code
|
98
143
|
rescue ::SyntaxError => e
|
99
|
-
warn "DeepCover is getting confused with the file #{path} and it won't be instrumented
|
100
|
-
|
101
|
-
|
144
|
+
warn ["DeepCover is getting confused with the file #{path} and it won't be instrumented.",
|
145
|
+
'Please report this error and provide the source code around the following:',
|
146
|
+
e,
|
147
|
+
].join("\n")
|
148
|
+
throw :use_fallback, :cover_failed
|
102
149
|
end
|
103
150
|
end
|
104
151
|
covered_code
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
# Memoize is a quick way to prepend a module that defines
|
5
|
+
# the memoized methods as `@_cache ||= super.freeze`
|
6
|
+
# It also refines `freeze` to precache memoized methods
|
7
|
+
#
|
8
|
+
module Memoize
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
def freeze
|
14
|
+
self.class.memoized.each do |method|
|
15
|
+
send method
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def memoized
|
22
|
+
@memoized ||= [].freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def memoizer_module
|
26
|
+
@memoizer_module ||= begin
|
27
|
+
mod = const_set(:Memoizer, Module.new)
|
28
|
+
prepend mod
|
29
|
+
mod
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def memoize(*methods)
|
34
|
+
@memoized ||= []
|
35
|
+
@memoized |= methods
|
36
|
+
@memoized.freeze
|
37
|
+
|
38
|
+
methods.each do |method|
|
39
|
+
memoizer_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
40
|
+
def #{method} # def foo
|
41
|
+
@_#{method} ||= super.freeze # @_foo ||= super.freeze
|
42
|
+
end # end
|
43
|
+
RUBY
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/deep_cover/node.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
|
-
class
|
4
|
+
class Node
|
3
5
|
# Reopened in base
|
6
|
+
CLASSES = []
|
7
|
+
def self.inherited(parent)
|
8
|
+
CLASSES << parent
|
9
|
+
super
|
10
|
+
end
|
4
11
|
end
|
5
12
|
require_relative_dir 'node/mixin'
|
6
13
|
require_relative 'node/base'
|
7
14
|
require_relative_dir 'node'
|
15
|
+
|
16
|
+
require_relative 'memoize'
|
17
|
+
Node.include Memoize
|
18
|
+
Node::CLASSES.freeze.each do |klass|
|
19
|
+
klass.memoize :flow_entry_count, :flow_completion_count, :execution_count, :loc_hash
|
20
|
+
end
|
8
21
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'const'
|
4
|
+
require_relative 'literals'
|
3
5
|
|
4
6
|
module DeepCover
|
5
7
|
class Node
|
@@ -88,17 +90,17 @@ module DeepCover
|
|
88
90
|
if method_name == :[]=
|
89
91
|
selector = base[:selector]
|
90
92
|
{
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
93
|
+
expression: base[:expression],
|
94
|
+
selector_begin: selector.resize(1),
|
95
|
+
# The = is implicit, so only backtrack the end by one
|
96
|
+
selector_end: Parser::Source::Range.new(selector.source_buffer, selector.end_pos - 1, selector.end_pos),
|
95
97
|
}
|
96
98
|
else
|
97
99
|
{
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
dot: base[:dot],
|
101
|
+
expression: base[:expression],
|
102
|
+
selector_begin: base[:selector],
|
103
|
+
selector_end: nil, # ,
|
102
104
|
}
|
103
105
|
end
|
104
106
|
end
|
@@ -134,11 +136,11 @@ module DeepCover
|
|
134
136
|
end
|
135
137
|
|
136
138
|
BASE_MAP = {
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
139
|
+
cvasgn: VariableAssignment, gvasgn: VariableAssignment,
|
140
|
+
ivasgn: VariableAssignment, lvasgn: VariableAssignment,
|
141
|
+
casgn: ConstantAssignment,
|
142
|
+
send: Setter,
|
143
|
+
}.freeze
|
142
144
|
class Splat < Node
|
143
145
|
include BackwardsStrategy
|
144
146
|
has_child rest_arg: [nil], remap: BASE_MAP
|
@@ -148,10 +150,10 @@ module DeepCover
|
|
148
150
|
class LeftSide < Node
|
149
151
|
include BackwardsStrategy
|
150
152
|
has_extra_children receivers: {
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
153
|
+
splat: Splat,
|
154
|
+
mlhs: LeftSide,
|
155
|
+
**BASE_MAP,
|
156
|
+
}
|
155
157
|
executed_loc_keys # none
|
156
158
|
|
157
159
|
def flow_completion_count
|
@@ -195,7 +197,7 @@ module DeepCover
|
|
195
197
|
|
196
198
|
def loc_hash
|
197
199
|
base = super
|
198
|
-
hash = {
|
200
|
+
hash = {expression: base[:expression], begin: base[:begin], end: base[:end], dot: base[:dot]}
|
199
201
|
selector = base[:selector]
|
200
202
|
|
201
203
|
if [:[], :[]=].include?(method_name)
|
@@ -214,11 +216,11 @@ module DeepCover
|
|
214
216
|
check_completion
|
215
217
|
has_tracker :reader
|
216
218
|
has_child receiver: {
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
219
|
+
lvasgn: VariableOperatorAssign, ivasgn: VariableOperatorAssign,
|
220
|
+
cvasgn: VariableOperatorAssign, gvasgn: VariableOperatorAssign,
|
221
|
+
casgn: Casgn, # TODO
|
222
|
+
send: SendOperatorAssign,
|
223
|
+
}
|
222
224
|
has_child operator: Symbol
|
223
225
|
has_child value: Node, rewrite: '(%{reader_tracker};%{node})', flow_entry_count: :reader_tracker_hits
|
224
226
|
executed_loc_keys :operator
|
@@ -233,11 +235,11 @@ module DeepCover
|
|
233
235
|
check_completion
|
234
236
|
has_tracker :long_branch
|
235
237
|
has_child receiver: {
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
238
|
+
lvasgn: VariableOperatorAssign, ivasgn: VariableOperatorAssign,
|
239
|
+
cvasgn: VariableOperatorAssign, gvasgn: VariableOperatorAssign,
|
240
|
+
casgn: ConstantOperatorAssign,
|
241
|
+
send: SendOperatorAssign,
|
242
|
+
}
|
241
243
|
has_child value: Node, rewrite: '(%{long_branch_tracker};%{node})', flow_entry_count: :long_branch_tracker_hits
|
242
244
|
executed_loc_keys :operator
|
243
245
|
|
data/lib/deep_cover/node/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
# Base class to handle covered nodes.
|
3
5
|
class Node
|
@@ -22,6 +24,7 @@ module DeepCover
|
|
22
24
|
@children = []
|
23
25
|
begin
|
24
26
|
@children = augment_children(base_children)
|
27
|
+
initialize_siblings
|
25
28
|
super()
|
26
29
|
rescue StandardError => e
|
27
30
|
diagnose(e)
|
@@ -36,11 +39,11 @@ module DeepCover
|
|
36
39
|
when ::Class
|
37
40
|
each_node.grep(lookup)
|
38
41
|
when ::Symbol
|
39
|
-
each_node.find_all{|n| n.type == lookup}
|
42
|
+
each_node.find_all { |n| n.type == lookup }
|
40
43
|
when ::String
|
41
|
-
each_node.find_all{|n| n.source == lookup}
|
44
|
+
each_node.find_all { |n| n.source == lookup }
|
42
45
|
when ::Regexp
|
43
|
-
each_node.find_all{|n| n.source =~ lookup}
|
46
|
+
each_node.find_all { |n| n.source =~ lookup }
|
44
47
|
else
|
45
48
|
binding.pry
|
46
49
|
raise ::TypeError, "Expected class or symbol, got #{lookup.class}: #{lookup.inspect}"
|
@@ -70,28 +73,25 @@ module DeepCover
|
|
70
73
|
end
|
71
74
|
|
72
75
|
def children_nodes
|
73
|
-
children.select{|c| c.is_a? Node }
|
76
|
+
children.select { |c| c.is_a? Node }
|
74
77
|
end
|
75
78
|
alias_method :children_nodes_in_flow_order, :children_nodes
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def previous_sibling
|
85
|
-
parent.children_nodes_in_flow_order.each_cons(2) do |previous_child, child|
|
86
|
-
return previous_child if child.equal? self
|
80
|
+
attr_accessor :next_sibling
|
81
|
+
attr_accessor :previous_sibling
|
82
|
+
protected :next_sibling=, :previous_sibling=
|
83
|
+
def initialize_siblings
|
84
|
+
children_nodes_in_flow_order.each_cons(2) do |child, next_child|
|
85
|
+
child.next_sibling = next_child
|
86
|
+
next_child.previous_sibling = child
|
87
87
|
end
|
88
|
-
nil
|
89
88
|
end
|
89
|
+
private :initialize_siblings
|
90
90
|
|
91
91
|
# Adapted from https://github.com/whitequark/ast/blob/master/lib/ast/node.rb
|
92
|
-
def to_s(indent=0)
|
92
|
+
def to_s(indent = 0)
|
93
93
|
[
|
94
|
-
|
94
|
+
' ' * indent,
|
95
95
|
'(',
|
96
96
|
fancy_type,
|
97
97
|
*children.map do |child, idx|
|
@@ -101,7 +101,7 @@ module DeepCover
|
|
101
101
|
" #{child.inspect}"
|
102
102
|
end
|
103
103
|
end,
|
104
|
-
')'
|
104
|
+
')',
|
105
105
|
].join
|
106
106
|
end
|
107
107
|
|
@@ -128,25 +128,26 @@ module DeepCover
|
|
128
128
|
end
|
129
129
|
|
130
130
|
def fancy_type
|
131
|
-
class_name = self.class.to_s.gsub(/^DeepCover::/,'').gsub(/^Node::/, '')
|
131
|
+
class_name = self.class.to_s.gsub(/^DeepCover::/, '').gsub(/^Node::/, '')
|
132
132
|
t = type.to_s
|
133
133
|
t.casecmp(class_name) == 0 ? t : "#{t}[#{class_name}]"
|
134
134
|
end
|
135
135
|
|
136
136
|
private
|
137
|
+
|
137
138
|
def diagnose(exception)
|
138
139
|
msg = if self.class == Node
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
140
|
+
"Unknown node type encountered: #{base_node.type}"
|
141
|
+
else
|
142
|
+
"Node class #{self.class} incorrectly defined"
|
143
|
+
end
|
143
144
|
warn [msg,
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
145
|
+
'Attempting to continue, but this node will not be handled properly',
|
146
|
+
('Its subnodes will be ignored' if children.empty?),
|
147
|
+
'Source:',
|
148
|
+
expression,
|
149
|
+
'Original exception:',
|
150
|
+
exception.inspect,
|
150
151
|
].join("\n")
|
151
152
|
end
|
152
153
|
end
|