necropsy 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/MEASUREMENTS.md +27 -0
- data/README.md +182 -17
- data/bench/README.md +92 -0
- data/bench/audit.rb +113 -0
- data/bench/audits/0.2.1/audit.json +501 -0
- data/bench/audits/0.2.1/audit.md +55 -0
- data/bench/audits/0.2.1/baseline_performance.yml +16 -0
- data/bench/audits/0.2.1/config.yml +42 -0
- data/bench/audits/0.2.1/review.yml +56 -0
- data/bench/audits/0.2.1/review_queue.yml +3651 -0
- data/bench/corpora/v1/README.md +30 -0
- data/bench/corpora/v1/labels.yml +37 -0
- data/bench/corpora/v1/manifest.yml +63 -0
- data/bench/corpora/v1/rubocop.necropsy.yml +7 -0
- data/bench/corpora/v1/self.necropsy.yml +8 -0
- data/bench/corpora/v1/tools/debride.yml +42 -0
- data/bench/corpora/v1/tools/spoom.yml +12 -0
- data/bench/corpora/v1/tools/type_aware.yml +11 -0
- data/bench/golden/v1/candidate_union.json +21918 -0
- data/bench/golden/v1/metadata.json +12 -0
- data/bench/golden/v1/reports/dynamic_evidence.json +146 -0
- data/bench/golden/v1/reports/plain_ruby.json +135 -0
- data/bench/golden/v1/reports/rails.json +176 -0
- data/bench/golden/v1/reports/rubocop_1_75_0.json +26848 -0
- data/bench/golden/v1/reports/self.json +3449 -0
- data/bench/review_queue.rb +35 -0
- data/bench/run.rb +31 -0
- data/bench/schema/candidate-union-v1.schema.json +70 -0
- data/docs/impv_implementation_matrix.md +179 -0
- data/docs/migrations/0.2.1.md +57 -0
- data/docs/migrations/0.3.0.md +207 -0
- data/docs/migrations/0.4.0.md +13 -0
- data/docs/necropsy_performance_adr.md +43 -0
- data/docs/necropsy_scope_decisions.md +58 -0
- data/docs/necropsy_type_facts_adr.md +22 -0
- data/gemfiles/prism_min.gemfile +9 -0
- data/gemfiles/prism_min.gemfile.lock +50 -0
- data/lib/necropsy/analyzer.rb +121 -2
- data/lib/necropsy/analyzers/dynamic/coverage_collector.rb +69 -17
- data/lib/necropsy/analyzers/dynamic/coverage_importer.rb +99 -9
- data/lib/necropsy/analyzers/dynamic/coverband_importer.rb +69 -299
- data/lib/necropsy/analyzers/dynamic/coverband_payload_set.rb +149 -0
- data/lib/necropsy/analyzers/dynamic/observation_policy.rb +96 -0
- data/lib/necropsy/analyzers/dynamic/redis_input_limits.rb +121 -0
- data/lib/necropsy/analyzers/dynamic/redis_nonblocking_io.rb +94 -0
- data/lib/necropsy/analyzers/dynamic/redis_payload_loader.rb +165 -0
- data/lib/necropsy/analyzers/dynamic/redis_transport.rb +217 -0
- data/lib/necropsy/analyzers/dynamic/runtime_reference.rb +96 -0
- data/lib/necropsy/analyzers/dynamic/trace_point_collector.rb +133 -23
- data/lib/necropsy/analyzers/dynamic/trace_point_importer.rb +3 -1
- data/lib/necropsy/analyzers/legacy_result_adapter.rb +226 -0
- data/lib/necropsy/analyzers/static/cha.rb +33 -73
- data/lib/necropsy/analyzers/static/name_resolution.rb +133 -12
- data/lib/necropsy/analyzers/static/rta.rb +237 -29
- data/lib/necropsy/ast_scanner/call_recording.rb +149 -30
- data/lib/necropsy/ast_scanner/call_site_creation.rb +54 -0
- data/lib/necropsy/ast_scanner/definition_creation.rb +43 -0
- data/lib/necropsy/ast_scanner/dsl_macros.rb +431 -41
- data/lib/necropsy/ast_scanner/method_definitions.rb +232 -50
- data/lib/necropsy/ast_scanner/references.rb +43 -9
- data/lib/necropsy/ast_scanner/ruby_semantics.rb +101 -15
- data/lib/necropsy/ast_scanner/traversal.rb +234 -71
- data/lib/necropsy/ast_scanner/value_definitions.rb +23 -13
- data/lib/necropsy/ast_scanner.rb +67 -6
- data/lib/necropsy/bench/candidate_union.rb +555 -0
- data/lib/necropsy/bench/claim_gate.rb +112 -0
- data/lib/necropsy/bench/evaluator.rb +329 -16
- data/lib/necropsy/bench/finding_facts.rb +152 -0
- data/lib/necropsy/bench/precision_gate.rb +144 -0
- data/lib/necropsy/bench/release_audit/adversarial_runner.rb +56 -0
- data/lib/necropsy/bench/release_audit/artifact_writer.rb +112 -0
- data/lib/necropsy/bench/release_audit/config_validator.rb +112 -0
- data/lib/necropsy/bench/release_audit/git_snapshot.rb +36 -0
- data/lib/necropsy/bench/release_audit/performance_gate.rb +165 -0
- data/lib/necropsy/bench/release_audit/run_provenance.rb +133 -0
- data/lib/necropsy/bench/release_audit.rb +360 -0
- data/lib/necropsy/bench/report_normalizer.rb +140 -0
- data/lib/necropsy/bench/review_queue.rb +154 -0
- data/lib/necropsy/bench/safety_mutation_harness.rb +59 -0
- data/lib/necropsy/bench/seed_runner.rb +408 -0
- data/lib/necropsy/bounded_canonicalizer.rb +218 -0
- data/lib/necropsy/cache/scan_cache.rb +85 -17
- data/lib/necropsy/call_site_identity.rb +54 -0
- data/lib/necropsy/cli.rb +220 -33
- data/lib/necropsy/clock.rb +40 -0
- data/lib/necropsy/confidence/scorer.rb +103 -58
- data/lib/necropsy/configuration.rb +224 -21
- data/lib/necropsy/convention_rules.rb +138 -0
- data/lib/necropsy/definition_identity/canonical_digest.rb +278 -0
- data/lib/necropsy/definition_identity.rb +37 -0
- data/lib/necropsy/diagnostics.rb +176 -36
- data/lib/necropsy/embedded_ruby.rb +55 -0
- data/lib/necropsy/entry_points/plain.rb +111 -10
- data/lib/necropsy/entry_points/rails.rb +322 -41
- data/lib/necropsy/entry_points/test.rb +6 -1
- data/lib/necropsy/flow_interpreter.rb +460 -0
- data/lib/necropsy/graph/blocker_matching.rb +338 -0
- data/lib/necropsy/graph/call_graph.rb +1099 -109
- data/lib/necropsy/graph/definition_index.rb +149 -0
- data/lib/necropsy/graph/dynamic_evidence_tracking.rb +206 -0
- data/lib/necropsy/graph/evidence_store.rb +213 -0
- data/lib/necropsy/graph/resolution_store.rb +497 -0
- data/lib/necropsy/graph_self_check.rb +79 -0
- data/lib/necropsy/guardrail/baseline.rb +350 -13
- data/lib/necropsy/guardrail/quarantine.rb +94 -9
- data/lib/necropsy/load_graph.rb +206 -0
- data/lib/necropsy/models.rb +878 -13
- data/lib/necropsy/performance_profiler.rb +108 -0
- data/lib/necropsy/project.rb +327 -25
- data/lib/necropsy/reachability/engine.rb +54 -13
- data/lib/necropsy/reference_barrier.rb +458 -0
- data/lib/necropsy/report.rb +113 -4
- data/lib/necropsy/reporter.rb +431 -15
- data/lib/necropsy/runner.rb +233 -18
- data/lib/necropsy/runtime_feedback.rb +136 -0
- data/lib/necropsy/semantics_matrix.rb +153 -0
- data/lib/necropsy/type_facts.rb +53 -0
- data/lib/necropsy/version.rb +1 -1
- data/lib/necropsy/why_not_explanation.rb +436 -0
- data/lib/necropsy/why_not_renderer.rb +197 -0
- data/lib/necropsy/world_policy.rb +90 -0
- data/lib/necropsy.rb +35 -2
- data/schema/necropsy-report-v2.schema.json +366 -0
- metadata +85 -1
|
@@ -4,34 +4,107 @@ module Necropsy
|
|
|
4
4
|
class AstScanner
|
|
5
5
|
private
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def build_call_sites(node, context)
|
|
8
|
+
messages = finite_dynamic_messages(node, context)
|
|
9
|
+
return [build_call_site(node, context)] if messages.empty?
|
|
10
|
+
|
|
11
|
+
messages.map { |message| build_call_site(node, context, message_override: message) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def build_call_site(node, context, message_override: nil)
|
|
8
15
|
message = node.name&.to_s
|
|
9
16
|
dynamic = false
|
|
10
17
|
receiver = classify_receiver(node.receiver, context)
|
|
11
18
|
metadata = { 'original_message' => message, 'receiver_candidates' => receiver[:candidates] }
|
|
19
|
+
metadata['arguments'] = call_arguments(node, offset: DYNAMIC_SENDS.include?(node.name) ? 1 : 0)
|
|
20
|
+
metadata['block_kind'] = call_block_kind(node)
|
|
21
|
+
receiver_fact = context.flow_result&.fact_for(node.receiver)
|
|
22
|
+
compact_receiver_fact = compact_receiver_fact(receiver_fact)
|
|
23
|
+
metadata['receiver_value_fact'] = compact_receiver_fact if compact_receiver_fact
|
|
24
|
+
argument_facts = arguments(node).map do |argument|
|
|
25
|
+
compact_receiver_fact(context.flow_result&.fact_for(argument))
|
|
26
|
+
end
|
|
27
|
+
metadata['argument_value_facts'] = argument_facts if argument_facts.any?
|
|
28
|
+
load_reference = load_reference_metadata(node)
|
|
29
|
+
metadata['load_reference'] = load_reference if load_reference
|
|
12
30
|
|
|
13
31
|
if DYNAMIC_SENDS.include?(node.name)
|
|
14
|
-
literal =
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
literal = literal_argument(node, index: 0)
|
|
33
|
+
message = message_override || literal
|
|
34
|
+
dynamic = message.nil?
|
|
17
35
|
metadata['dynamic_dispatch'] = true
|
|
36
|
+
metadata['finite_dynamic_dispatch'] = true if message_override
|
|
18
37
|
end
|
|
19
38
|
|
|
20
39
|
return nil unless message
|
|
21
40
|
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
scanned_call_site(
|
|
42
|
+
source_node: node,
|
|
43
|
+
context: context,
|
|
44
|
+
role: :call,
|
|
24
45
|
message: message,
|
|
25
46
|
receiver_kind: receiver.fetch(:kind),
|
|
26
47
|
receiver_name: receiver[:name],
|
|
27
|
-
file: context.relative_file,
|
|
28
|
-
line: node.location.start_line,
|
|
29
|
-
test: context.test,
|
|
30
48
|
dynamic: dynamic,
|
|
31
49
|
metadata: metadata
|
|
32
50
|
)
|
|
33
51
|
end
|
|
34
52
|
|
|
53
|
+
def finite_dynamic_messages(node, context)
|
|
54
|
+
return [] unless DYNAMIC_SENDS.include?(node.name)
|
|
55
|
+
return [] if literal_argument(node, index: 0)
|
|
56
|
+
|
|
57
|
+
argument = Array(node.arguments&.arguments).first
|
|
58
|
+
fact = context.flow_result&.fact_for(argument)
|
|
59
|
+
return [] unless fact&.exact && %i[symbol_set string_set].include?(fact.kind)
|
|
60
|
+
|
|
61
|
+
Array(fact.values).map(&:to_s).uniq.sort.first(FlowInterpreter::MAX_ATOMS)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def load_reference_metadata(node)
|
|
65
|
+
argument_index = case node.name
|
|
66
|
+
when :require, :require_relative then 0
|
|
67
|
+
when :autoload then 1
|
|
68
|
+
else return
|
|
69
|
+
end
|
|
70
|
+
return unless load_primitive_receiver?(node)
|
|
71
|
+
|
|
72
|
+
path = literal_argument(node, index: argument_index)
|
|
73
|
+
{ 'kind' => node.name.to_s, 'literal' => !path.nil?, 'path' => path }.compact
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def load_primitive_receiver?(node)
|
|
77
|
+
return true unless node.receiver
|
|
78
|
+
return true if node.receiver.is_a?(Prism::SelfNode)
|
|
79
|
+
|
|
80
|
+
receiver = constant_name(node.receiver)
|
|
81
|
+
node.name == :autoload ? !receiver.nil? : receiver == 'Kernel'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def compact_receiver_fact(fact)
|
|
85
|
+
return unless fact&.exact
|
|
86
|
+
return unless %i[instance_types callable_set container].include?(fact.kind)
|
|
87
|
+
|
|
88
|
+
fact.to_h.merge('summary' => fact.kind == :container ? fact.summary : nil)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def call_block_kind(node)
|
|
92
|
+
case node.block
|
|
93
|
+
when nil
|
|
94
|
+
'none'
|
|
95
|
+
when Prism::BlockNode
|
|
96
|
+
'literal'
|
|
97
|
+
when Prism::BlockArgumentNode
|
|
98
|
+
case node.block.expression
|
|
99
|
+
when Prism::NilNode then 'nil'
|
|
100
|
+
when Prism::SymbolNode then 'symbol_to_proc'
|
|
101
|
+
else 'dynamic'
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
'dynamic'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
35
108
|
def record_instantiation(node, context)
|
|
36
109
|
return record_factory_instantiation(node, context) unless %i[new []].include?(node.name)
|
|
37
110
|
|
|
@@ -57,17 +130,16 @@ module Necropsy
|
|
|
57
130
|
def record_initialize_call(node, context, receiver)
|
|
58
131
|
return unless node.name == :new
|
|
59
132
|
|
|
60
|
-
|
|
61
|
-
|
|
133
|
+
add_scanned_call_site(
|
|
134
|
+
source_node: node,
|
|
135
|
+
context: context,
|
|
136
|
+
role: :initialize,
|
|
62
137
|
message: 'initialize',
|
|
63
138
|
receiver_kind: :instance,
|
|
64
139
|
receiver_name: receiver[:name],
|
|
65
|
-
file: context.relative_file,
|
|
66
|
-
line: node.location.start_line,
|
|
67
|
-
test: context.test,
|
|
68
140
|
dynamic: false,
|
|
69
141
|
metadata: { 'original_message' => 'new', 'receiver_candidates' => receiver[:candidates],
|
|
70
|
-
'implicit_from' => 'new' }
|
|
142
|
+
'implicit_from' => 'new', 'arguments' => call_arguments(node) }
|
|
71
143
|
)
|
|
72
144
|
end
|
|
73
145
|
|
|
@@ -91,15 +163,32 @@ module Necropsy
|
|
|
91
163
|
end
|
|
92
164
|
|
|
93
165
|
def unresolved_dynamic_dispatch?(node)
|
|
94
|
-
DYNAMIC_SENDS.include?(node.name) &&
|
|
166
|
+
DYNAMIC_SENDS.include?(node.name) && literal_argument(node, index: 0).nil?
|
|
95
167
|
end
|
|
96
168
|
|
|
97
|
-
def record_parse_errors(root_id, result)
|
|
169
|
+
def record_parse_errors(root_id, relative, result)
|
|
98
170
|
result.errors.each do |error|
|
|
99
171
|
uncertainties[root_id] << "Parse warning at line #{error.location.start_line}: #{error.message}"
|
|
172
|
+
source_errors << SourceError.new(
|
|
173
|
+
file: relative,
|
|
174
|
+
line: error.location.start_line,
|
|
175
|
+
message: error.message,
|
|
176
|
+
type: error.type
|
|
177
|
+
)
|
|
100
178
|
end
|
|
101
179
|
end
|
|
102
180
|
|
|
181
|
+
def record_source_failure(root_id, relative, error)
|
|
182
|
+
file_statuses[relative] = :failed
|
|
183
|
+
uncertainties[root_id] << "Could not parse #{relative}: #{error.message}"
|
|
184
|
+
source_errors << SourceError.new(
|
|
185
|
+
file: relative,
|
|
186
|
+
line: 1,
|
|
187
|
+
message: error.message,
|
|
188
|
+
type: error.class.name.to_sym
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
|
|
103
192
|
def definition_owner(node, context)
|
|
104
193
|
return context.owner unless node.receiver
|
|
105
194
|
return context.owner if node.receiver.is_a?(Prism::SelfNode)
|
|
@@ -125,30 +214,60 @@ module Necropsy
|
|
|
125
214
|
context.singleton_scope ? [:singleton_method, '.'] : [:instance_method, '#']
|
|
126
215
|
end
|
|
127
216
|
|
|
128
|
-
def update_method_visibility(context, name, visibility, singleton: false)
|
|
217
|
+
def update_method_visibility(context, name, visibility, source_node, singleton: false)
|
|
129
218
|
separator = singleton ? '.' : method_kind_and_separator(context).last
|
|
130
219
|
id = "#{context.owner}#{separator}#{name}"
|
|
131
|
-
|
|
220
|
+
candidates = nodes.each_index.select { |index| nodes[index].symbol_id == id }
|
|
221
|
+
local = candidates.select do |index|
|
|
222
|
+
definition = nodes[index]
|
|
223
|
+
definition.file == context.relative_file && definition.line <= source_node.location.start_line
|
|
224
|
+
end
|
|
225
|
+
index = local.max_by { |candidate| [nodes[candidate].line, nodes[candidate].ordinal] }
|
|
132
226
|
nodes[index] = nodes[index].with(visibility: visibility) if index
|
|
227
|
+
return if index && candidates == [index]
|
|
228
|
+
|
|
229
|
+
semantic_blockers << Blocker.new(
|
|
230
|
+
kind: :visibility_activation,
|
|
231
|
+
scope_kind: :message,
|
|
232
|
+
scope_value: name,
|
|
233
|
+
source: 'ast_scanner',
|
|
234
|
+
reason: "visibility activation for #{id} depends on repository load order",
|
|
235
|
+
suggested_action: :review_load_order,
|
|
236
|
+
metadata: {
|
|
237
|
+
'caller_domain' => context.test ? 'test' : 'runtime',
|
|
238
|
+
'caller_id' => context.current_caller_id,
|
|
239
|
+
'owner_scope' => [context.owner],
|
|
240
|
+
'file' => context.relative_file,
|
|
241
|
+
'line' => source_node.location.start_line,
|
|
242
|
+
'message' => name,
|
|
243
|
+
'reason_code' => 'visibility_activation'
|
|
244
|
+
}
|
|
245
|
+
)
|
|
133
246
|
end
|
|
134
247
|
|
|
135
|
-
def
|
|
248
|
+
def defer_module_function(context, name, source_node)
|
|
136
249
|
instance_id = "#{context.owner}##{name}"
|
|
137
|
-
|
|
138
|
-
|
|
250
|
+
privatize_current_module_function_source(instance_id, context.relative_file)
|
|
251
|
+
copy = add_module_function_definition(context, name, source_node)
|
|
252
|
+
deferred_module_functions[copy.graph_id] = instance_id
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def privatize_current_module_function_source(instance_id, relative_file)
|
|
256
|
+
index = nodes.rindex do |definition|
|
|
257
|
+
definition.kind == :instance_method && definition.symbol_id == instance_id && definition.file == relative_file
|
|
258
|
+
end
|
|
259
|
+
nodes[index] = nodes[index].with(visibility: :private) if index
|
|
260
|
+
end
|
|
139
261
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
id: "#{context.owner}.#{name}",
|
|
262
|
+
def add_module_function_definition(context, name, source_node)
|
|
263
|
+
add_definition(
|
|
264
|
+
symbol_id: "#{context.owner}.#{name}",
|
|
144
265
|
kind: :singleton_method,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
end_line: location.end_line,
|
|
266
|
+
source_node: source_node,
|
|
267
|
+
context: context,
|
|
148
268
|
defined_via: :module_function,
|
|
149
269
|
owner: context.owner,
|
|
150
270
|
name: name,
|
|
151
|
-
test: context.test,
|
|
152
271
|
visibility: :public
|
|
153
272
|
)
|
|
154
273
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Necropsy
|
|
4
|
+
class AstScanner
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def scanned_call_site(source_node:, context:, role:, message:, receiver_kind:, receiver_name: nil,
|
|
8
|
+
caller_id: context.current_caller_id, dynamic: false, metadata: {})
|
|
9
|
+
structural_digest = DefinitionIdentity.body_digest(source_node)
|
|
10
|
+
ordinal_key = [caller_id, context.relative_file, role.to_sym, message.to_s, structural_digest]
|
|
11
|
+
ordinal = call_site_ordinals[ordinal_key] += 1
|
|
12
|
+
CallSite.new(
|
|
13
|
+
call_site_id: CallSiteIdentity.source_id(
|
|
14
|
+
caller_definition_id: caller_id,
|
|
15
|
+
relative_path: context.relative_file,
|
|
16
|
+
role: role,
|
|
17
|
+
message: message,
|
|
18
|
+
structural_digest: structural_digest,
|
|
19
|
+
ordinal: ordinal
|
|
20
|
+
),
|
|
21
|
+
caller_id: caller_id,
|
|
22
|
+
message: message.to_s,
|
|
23
|
+
receiver_kind: receiver_kind,
|
|
24
|
+
receiver_name: receiver_name,
|
|
25
|
+
file: context.relative_file,
|
|
26
|
+
line: source_node.location.start_line,
|
|
27
|
+
test: context.test,
|
|
28
|
+
dynamic: dynamic,
|
|
29
|
+
metadata: metadata
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_scanned_call_site(**attributes)
|
|
34
|
+
scanned_call_site(**attributes).tap { |site| call_sites << site }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def derived_call_site(site, derivation:, caller_id: site.caller_id, message: site.message, metadata: {})
|
|
38
|
+
site.with(
|
|
39
|
+
call_site_id: CallSiteIdentity.derived_id(
|
|
40
|
+
parent_call_site_id: site.call_site_id,
|
|
41
|
+
derivation: derivation,
|
|
42
|
+
caller_definition_id: caller_id,
|
|
43
|
+
message: message
|
|
44
|
+
),
|
|
45
|
+
caller_id: caller_id,
|
|
46
|
+
message: message.to_s,
|
|
47
|
+
metadata: site.metadata.merge(
|
|
48
|
+
'derived_from_call_site_id' => site.call_site_id,
|
|
49
|
+
'derived_via' => derivation.to_s
|
|
50
|
+
).merge(metadata)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Necropsy
|
|
4
|
+
class AstScanner
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def add_definition(symbol_id:, kind:, source_node:, context:, defined_via:, owner:, name:,
|
|
8
|
+
visibility: context.visibility)
|
|
9
|
+
body_digest = DefinitionIdentity.body_digest(source_node)
|
|
10
|
+
ordinal_key = [kind, symbol_id, context.relative_file, body_digest]
|
|
11
|
+
ordinal = definition_ordinals[ordinal_key] += 1
|
|
12
|
+
definition_id = if defined_via == :file
|
|
13
|
+
DefinitionIdentity.file_root_id(relative_path: context.relative_file)
|
|
14
|
+
else
|
|
15
|
+
DefinitionIdentity.definition_id(
|
|
16
|
+
kind: kind,
|
|
17
|
+
symbol_id: symbol_id,
|
|
18
|
+
relative_path: context.relative_file,
|
|
19
|
+
body_digest: body_digest,
|
|
20
|
+
ordinal: ordinal
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
definition = Node.new(
|
|
24
|
+
id: symbol_id,
|
|
25
|
+
symbol_id: symbol_id,
|
|
26
|
+
definition_id: definition_id,
|
|
27
|
+
body_digest: body_digest,
|
|
28
|
+
ordinal: ordinal,
|
|
29
|
+
kind: kind,
|
|
30
|
+
file: context.relative_file,
|
|
31
|
+
line: source_node.location.start_line,
|
|
32
|
+
end_line: source_node.location.end_line,
|
|
33
|
+
defined_via: defined_via,
|
|
34
|
+
owner: owner,
|
|
35
|
+
name: name.to_s,
|
|
36
|
+
test: context.test,
|
|
37
|
+
visibility: visibility
|
|
38
|
+
)
|
|
39
|
+
nodes << definition
|
|
40
|
+
definition
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|