@suss/adapter-ruby 0.21.1 → 0.22.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.
- package/README.md +78 -2
- package/dist/index.cjs +2224 -519
- package/dist/index.d.cts +280 -8
- package/dist/index.d.ts +280 -8
- package/dist/index.js +1902 -174
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -30,19 +30,37 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ADAPTER_VERSION: () => ADAPTER_VERSION,
|
|
34
|
+
RubyWhySession: () => RubyWhySession,
|
|
33
35
|
ancestryOf: () => ancestryOf,
|
|
36
|
+
bodyStatements: () => bodyStatements,
|
|
37
|
+
booleanLiteralValue: () => booleanLiteralValue,
|
|
38
|
+
collectFileConstants: () => collectFileConstants,
|
|
34
39
|
createFileCache: () => createFileCache,
|
|
35
40
|
discoverUnits: () => discoverUnits,
|
|
41
|
+
emitConstantBindings: () => emitConstantBindings,
|
|
36
42
|
emitEntryFact: () => emitEntryFact,
|
|
43
|
+
emitValueFacts: () => emitValueFacts,
|
|
37
44
|
extractRubyProject: () => extractRubyProject,
|
|
45
|
+
field: () => field,
|
|
38
46
|
findRubyFiles: () => findRubyFiles,
|
|
39
47
|
graphqlTypeNameFromQualified: () => graphqlTypeNameFromQualified,
|
|
48
|
+
hashKeySymbolName: () => hashKeySymbolName,
|
|
40
49
|
inheritedStatements: () => inheritedStatements,
|
|
41
50
|
methodInAncestry: () => methodInAncestry,
|
|
51
|
+
nodeId: () => nodeId,
|
|
42
52
|
parseRuby: () => parseRuby,
|
|
53
|
+
parseRubySync: () => parseRubySync,
|
|
54
|
+
preloadRubyGrammar: () => preloadRubyGrammar,
|
|
43
55
|
qualifyConstantRef: () => qualifyConstantRef,
|
|
44
56
|
reachDefinition: () => reachDefinition,
|
|
57
|
+
readCallArgs: () => readCallArgs,
|
|
58
|
+
readKey: () => readKey,
|
|
45
59
|
resolveConstantFile: () => resolveConstantFile,
|
|
60
|
+
rubyStubEvidence: () => rubyStubEvidence,
|
|
61
|
+
runStatements: () => runStatements,
|
|
62
|
+
stringLiteralValue: () => stringLiteralValue,
|
|
63
|
+
symbolValue: () => symbolValue,
|
|
46
64
|
typeShapeFromNode: () => typeShapeFromNode,
|
|
47
65
|
underscoreConstantPath: () => underscoreConstantPath,
|
|
48
66
|
unitKey: () => unitKey,
|
|
@@ -80,6 +98,7 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
80
98
|
"module",
|
|
81
99
|
"singleton_class"
|
|
82
100
|
]);
|
|
101
|
+
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
83
102
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
84
103
|
function runStatements(body, blockConfigures = () => false) {
|
|
85
104
|
const found = [];
|
|
@@ -97,6 +116,22 @@ function runStatements(body, blockConfigures = () => false) {
|
|
|
97
116
|
});
|
|
98
117
|
return found;
|
|
99
118
|
}
|
|
119
|
+
function stringLiteralValue(node) {
|
|
120
|
+
if (node.type !== "string") {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
let content = "";
|
|
124
|
+
for (const child of bodyStatements(node)) {
|
|
125
|
+
if (child.type === "string_content") {
|
|
126
|
+
content += child.text;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (child.type === "interpolation") {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return content;
|
|
134
|
+
}
|
|
100
135
|
function symbolValue(node) {
|
|
101
136
|
return node.type === "simple_symbol" ? node.text.slice(1) : null;
|
|
102
137
|
}
|
|
@@ -113,6 +148,71 @@ function instanceMethodsByName(body) {
|
|
|
113
148
|
}
|
|
114
149
|
return methods;
|
|
115
150
|
}
|
|
151
|
+
var VISIBILITY_KEYWORDS = /* @__PURE__ */ new Set([
|
|
152
|
+
"private",
|
|
153
|
+
"protected",
|
|
154
|
+
"public"
|
|
155
|
+
]);
|
|
156
|
+
function visibilityKeyword(text) {
|
|
157
|
+
return VISIBILITY_KEYWORDS.has(text) ? text : null;
|
|
158
|
+
}
|
|
159
|
+
function setVisibility(visibility, name, keyword) {
|
|
160
|
+
if (keyword === "public") {
|
|
161
|
+
visibility.delete(name);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
visibility.set(name, keyword);
|
|
165
|
+
}
|
|
166
|
+
function markCalledOutMethods(call, keyword, visibility) {
|
|
167
|
+
const argumentList = field(call, "arguments");
|
|
168
|
+
if (argumentList === null) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
for (const arg of bodyStatements(argumentList)) {
|
|
172
|
+
const name = arg.type === "method" ? field(arg, "name")?.text : symbolValue(arg);
|
|
173
|
+
if (name !== void 0 && name !== null) {
|
|
174
|
+
setVisibility(visibility, name, keyword);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function instanceMethodVisibility(body) {
|
|
179
|
+
const visibility = /* @__PURE__ */ new Map();
|
|
180
|
+
let mode = "public";
|
|
181
|
+
for (const stmt of bodyStatements(body)) {
|
|
182
|
+
if (stmt.type === "method") {
|
|
183
|
+
const name = field(stmt, "name")?.text;
|
|
184
|
+
if (name !== void 0) {
|
|
185
|
+
setVisibility(visibility, name, mode);
|
|
186
|
+
}
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (stmt.type === "identifier") {
|
|
190
|
+
mode = visibilityKeyword(stmt.text) ?? mode;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (stmt.type !== "call" || field(stmt, "receiver") !== null) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const keyword = visibilityKeyword(field(stmt, "method")?.text ?? "");
|
|
197
|
+
if (keyword !== null) {
|
|
198
|
+
markCalledOutMethods(stmt, keyword, visibility);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return visibility;
|
|
202
|
+
}
|
|
203
|
+
function singletonMethodsByName(body) {
|
|
204
|
+
const methods = /* @__PURE__ */ new Map();
|
|
205
|
+
for (const stmt of runStatements(body)) {
|
|
206
|
+
if (stmt.type !== "singleton_method") {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const name = field(stmt, "name")?.text;
|
|
210
|
+
if (name !== void 0) {
|
|
211
|
+
methods.set(name, stmt);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return methods;
|
|
215
|
+
}
|
|
116
216
|
function bareCallArgumentGroups(body, name) {
|
|
117
217
|
const groups = [];
|
|
118
218
|
for (const stmt of runStatements(body)) {
|
|
@@ -300,7 +400,10 @@ async function reachDefinition(qualifiedName, lookup) {
|
|
|
300
400
|
qualifiedName,
|
|
301
401
|
lookup.pathConvention
|
|
302
402
|
);
|
|
303
|
-
|
|
403
|
+
if (filePath === null) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
const fileRoot = await lookup.parsedFile(filePath);
|
|
304
407
|
if (fileRoot === null) {
|
|
305
408
|
return null;
|
|
306
409
|
}
|
|
@@ -311,7 +414,7 @@ async function reachDefinition(qualifiedName, lookup) {
|
|
|
311
414
|
return null;
|
|
312
415
|
}
|
|
313
416
|
const knownClasses = new Set(all.map((info) => info.qualifiedName));
|
|
314
|
-
return matches.map((info) => ({ info, knownClasses }));
|
|
417
|
+
return matches.map((info) => ({ info, knownClasses, file: filePath }));
|
|
315
418
|
}
|
|
316
419
|
function ancestryOf(name, blocks, lookup) {
|
|
317
420
|
return chainOf(
|
|
@@ -403,17 +506,19 @@ function methodInAncestry(ancestry, name) {
|
|
|
403
506
|
if (entry.type === "unfollowed") {
|
|
404
507
|
return {
|
|
405
508
|
type: "unsettled",
|
|
406
|
-
reason: `inherited from ${entry.name}, which this run did not read
|
|
509
|
+
reason: `inherited from ${entry.name}, which this run did not read`,
|
|
510
|
+
cause: "unreadAncestor"
|
|
407
511
|
};
|
|
408
512
|
}
|
|
409
513
|
const found = definitionIn(entry.blocks, name);
|
|
410
|
-
if (found.method !== null) {
|
|
411
|
-
return { type: "found", method: found.method };
|
|
514
|
+
if (found.method !== null && found.block !== null) {
|
|
515
|
+
return { type: "found", method: found.method, block: found.block };
|
|
412
516
|
}
|
|
413
517
|
if (found.dynamic) {
|
|
414
518
|
return {
|
|
415
519
|
type: "unsettled",
|
|
416
|
-
reason: "defined with define_method, which this reader does not follow"
|
|
520
|
+
reason: "defined with define_method, which this reader does not follow",
|
|
521
|
+
cause: "dynamicDefine"
|
|
417
522
|
};
|
|
418
523
|
}
|
|
419
524
|
}
|
|
@@ -421,16 +526,21 @@ function methodInAncestry(ancestry, name) {
|
|
|
421
526
|
}
|
|
422
527
|
function definitionIn(blocks, name) {
|
|
423
528
|
let method = null;
|
|
529
|
+
let block = null;
|
|
424
530
|
let dynamic = false;
|
|
425
|
-
for (const
|
|
426
|
-
const body =
|
|
531
|
+
for (const candidate of blocks) {
|
|
532
|
+
const body = candidate.info.bodyNode;
|
|
427
533
|
if (body === null) {
|
|
428
534
|
continue;
|
|
429
535
|
}
|
|
430
|
-
|
|
536
|
+
const found = instanceMethodsByName(body).get(name);
|
|
537
|
+
if (found !== void 0) {
|
|
538
|
+
method = found;
|
|
539
|
+
block = candidate;
|
|
540
|
+
}
|
|
431
541
|
dynamic ||= bareCallArgumentGroups(body, DEFINE_METHOD_CALL).length > 0;
|
|
432
542
|
}
|
|
433
|
-
return { method, dynamic };
|
|
543
|
+
return { method, block, dynamic };
|
|
434
544
|
}
|
|
435
545
|
function inheritedStatements(ancestry, blockConfigures) {
|
|
436
546
|
const statements = [];
|
|
@@ -452,11 +562,112 @@ function inheritedStatements(ancestry, blockConfigures) {
|
|
|
452
562
|
}
|
|
453
563
|
|
|
454
564
|
// src/discovery.ts
|
|
565
|
+
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
566
|
+
var import_extractor4 = require("@suss/extractor");
|
|
567
|
+
|
|
568
|
+
// src/envReads.ts
|
|
455
569
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
456
|
-
var
|
|
570
|
+
var import_extractor2 = require("@suss/extractor");
|
|
571
|
+
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
572
|
+
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
573
|
+
function envReadEffects(root) {
|
|
574
|
+
const effects = [];
|
|
575
|
+
(0, import_extractor2.walkDescendants)(root, null, {
|
|
576
|
+
at: (node) => {
|
|
577
|
+
const read = envReadAt(node);
|
|
578
|
+
if (read !== null) {
|
|
579
|
+
effects.push(configReadEffect(read));
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? import_extractor2.SKIP_CHILDREN : null
|
|
583
|
+
});
|
|
584
|
+
return effects;
|
|
585
|
+
}
|
|
586
|
+
function configReadEffect(read) {
|
|
587
|
+
return {
|
|
588
|
+
type: "interaction",
|
|
589
|
+
binding: (0, import_behavioral_ir.runtimeConfigBinding)({
|
|
590
|
+
recognition: RUBY_ENV_RECOGNITION,
|
|
591
|
+
deploymentTarget: "lambda",
|
|
592
|
+
instanceName: "<unknown>"
|
|
593
|
+
}),
|
|
594
|
+
callee: `ENV["${read.name}"]`,
|
|
595
|
+
interaction: {
|
|
596
|
+
class: "config-read",
|
|
597
|
+
name: read.name,
|
|
598
|
+
defaulted: read.defaulted
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function envReadAt(node) {
|
|
603
|
+
if (node.type === "element_reference") {
|
|
604
|
+
return elementRead(node);
|
|
605
|
+
}
|
|
606
|
+
if (node.type === "call") {
|
|
607
|
+
return fetchRead(node);
|
|
608
|
+
}
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
function elementRead(node) {
|
|
612
|
+
const object2 = field(node, "object");
|
|
613
|
+
if (object2 === null || !isEnv(object2) || isAssignedTo(node)) {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
const index = node.namedChildren.find(
|
|
617
|
+
(child) => child !== null && child.id !== object2.id
|
|
618
|
+
);
|
|
619
|
+
const name = index === void 0 ? null : stringLiteralValue(index);
|
|
620
|
+
if (name === null) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
return { name, defaulted: isDefaultedAt(node) };
|
|
624
|
+
}
|
|
625
|
+
function fetchRead(node) {
|
|
626
|
+
const receiver = field(node, "receiver");
|
|
627
|
+
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
const { positional } = readCallArgs(field(node, "arguments"));
|
|
631
|
+
const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
632
|
+
if (name === null) {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
636
|
+
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
637
|
+
}
|
|
638
|
+
function isEnv(node) {
|
|
639
|
+
if (node.type === "constant") {
|
|
640
|
+
return node.text === "ENV";
|
|
641
|
+
}
|
|
642
|
+
return node.type === "scope_resolution" && field(node, "scope") === null && field(node, "name")?.text === "ENV";
|
|
643
|
+
}
|
|
644
|
+
function isAssignedTo(node) {
|
|
645
|
+
const parent = node.parent;
|
|
646
|
+
return parent !== null && (parent.type === "assignment" || parent.type === "operator_assignment") && field(parent, "left")?.id === node.id;
|
|
647
|
+
}
|
|
648
|
+
function isDefaultedAt(node) {
|
|
649
|
+
let child = node;
|
|
650
|
+
let parent = node.parent;
|
|
651
|
+
while (parent !== null) {
|
|
652
|
+
if (parent.type === "parenthesized_statements") {
|
|
653
|
+
child = parent;
|
|
654
|
+
parent = parent.parent;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (parent.type !== "binary" || field(parent, "operator")?.text !== "||") {
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
if (field(parent, "left")?.id === child.id) {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
child = parent;
|
|
664
|
+
parent = parent.parent;
|
|
665
|
+
}
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
457
668
|
|
|
458
669
|
// src/paths/effects.ts
|
|
459
|
-
var
|
|
670
|
+
var import_extractor3 = require("@suss/extractor");
|
|
460
671
|
|
|
461
672
|
// src/paths/lowering.ts
|
|
462
673
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -707,8 +918,18 @@ function withoutChainLinks(calls) {
|
|
|
707
918
|
}
|
|
708
919
|
return calls.filter((call) => !isLink.has(call.id));
|
|
709
920
|
}
|
|
921
|
+
function calleeMethodName(call) {
|
|
922
|
+
const method = field(call, "method");
|
|
923
|
+
if (method !== null) {
|
|
924
|
+
return method.text;
|
|
925
|
+
}
|
|
926
|
+
return field(call, "receiver") !== null ? "call" : children(call)[0]?.text;
|
|
927
|
+
}
|
|
710
928
|
function isPropertyRead(node) {
|
|
711
|
-
|
|
929
|
+
if (field(node, "receiver") === null || field(node, "arguments") !== null) {
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
return calleeMethodName(node) !== "call";
|
|
712
933
|
}
|
|
713
934
|
function isRaise2(node) {
|
|
714
935
|
const method = field(node, "method") ?? children(node)[0] ?? null;
|
|
@@ -728,11 +949,11 @@ function bodyCalls(node, found = []) {
|
|
|
728
949
|
}
|
|
729
950
|
function calleeText(call) {
|
|
730
951
|
const receiver = field(call, "receiver");
|
|
731
|
-
const method =
|
|
952
|
+
const method = calleeMethodName(call);
|
|
732
953
|
if (method === void 0) {
|
|
733
954
|
return call.text;
|
|
734
955
|
}
|
|
735
|
-
return receiver === null ? method
|
|
956
|
+
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
736
957
|
}
|
|
737
958
|
function argOf(node) {
|
|
738
959
|
const literal2 = LITERAL_ARGS[node.type]?.(node);
|
|
@@ -780,7 +1001,7 @@ function invocationEffects(definitionNode) {
|
|
|
780
1001
|
statements.push(statement);
|
|
781
1002
|
}
|
|
782
1003
|
const lowered = lowerRubyBody(body, statements);
|
|
783
|
-
const enumerated = (0,
|
|
1004
|
+
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
784
1005
|
{
|
|
785
1006
|
statements: lowered.statements,
|
|
786
1007
|
terminalsByStmt: lowered.terminalsByStmt
|
|
@@ -789,7 +1010,7 @@ function invocationEffects(definitionNode) {
|
|
|
789
1010
|
);
|
|
790
1011
|
return calls.map((call) => {
|
|
791
1012
|
const statement = statementOf.get(call.id);
|
|
792
|
-
const conditions = (0,
|
|
1013
|
+
const conditions = (0, import_extractor3.sharedGatingConditions)(
|
|
793
1014
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
794
1015
|
);
|
|
795
1016
|
return {
|
|
@@ -803,7 +1024,7 @@ function invocationEffects(definitionNode) {
|
|
|
803
1024
|
}
|
|
804
1025
|
|
|
805
1026
|
// src/storage.ts
|
|
806
|
-
var
|
|
1027
|
+
var import_datalog4 = require("@suss/datalog");
|
|
807
1028
|
|
|
808
1029
|
// ../../../node_modules/zod/v4/classic/external.js
|
|
809
1030
|
var external_exports = {};
|
|
@@ -1485,8 +1706,8 @@ function assertNever(_x) {
|
|
|
1485
1706
|
function assert(_) {
|
|
1486
1707
|
}
|
|
1487
1708
|
function getEnumValues(entries) {
|
|
1488
|
-
const numericValues = Object.values(entries).filter((
|
|
1489
|
-
const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_,
|
|
1709
|
+
const numericValues = Object.values(entries).filter((v4) => typeof v4 === "number");
|
|
1710
|
+
const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v4]) => v4);
|
|
1490
1711
|
return values;
|
|
1491
1712
|
}
|
|
1492
1713
|
function joinValues(array2, separator = "|") {
|
|
@@ -1540,9 +1761,9 @@ function defineLazy(object2, key, getter) {
|
|
|
1540
1761
|
}
|
|
1541
1762
|
return value;
|
|
1542
1763
|
},
|
|
1543
|
-
set(
|
|
1764
|
+
set(v4) {
|
|
1544
1765
|
Object.defineProperty(object2, key, {
|
|
1545
|
-
value:
|
|
1766
|
+
value: v4
|
|
1546
1767
|
// configurable: true,
|
|
1547
1768
|
});
|
|
1548
1769
|
},
|
|
@@ -1571,10 +1792,10 @@ function mergeDefs(...defs) {
|
|
|
1571
1792
|
function cloneDef(schema) {
|
|
1572
1793
|
return mergeDefs(schema._zod.def);
|
|
1573
1794
|
}
|
|
1574
|
-
function getElementAtPath(obj,
|
|
1575
|
-
if (!
|
|
1795
|
+
function getElementAtPath(obj, path7) {
|
|
1796
|
+
if (!path7)
|
|
1576
1797
|
return obj;
|
|
1577
|
-
return
|
|
1798
|
+
return path7.reduce((acc, key) => acc?.[key], obj);
|
|
1578
1799
|
}
|
|
1579
1800
|
function promiseAllObject(promisesObj) {
|
|
1580
1801
|
const keys = Object.keys(promisesObj);
|
|
@@ -1983,11 +2204,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1983
2204
|
}
|
|
1984
2205
|
return false;
|
|
1985
2206
|
}
|
|
1986
|
-
function prefixIssues(
|
|
2207
|
+
function prefixIssues(path7, issues) {
|
|
1987
2208
|
return issues.map((iss) => {
|
|
1988
2209
|
var _a3;
|
|
1989
2210
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1990
|
-
iss.path.unshift(
|
|
2211
|
+
iss.path.unshift(path7);
|
|
1991
2212
|
return iss;
|
|
1992
2213
|
});
|
|
1993
2214
|
}
|
|
@@ -2134,16 +2355,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2134
2355
|
}
|
|
2135
2356
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2136
2357
|
const fieldErrors = { _errors: [] };
|
|
2137
|
-
const processError = (error52,
|
|
2358
|
+
const processError = (error52, path7 = []) => {
|
|
2138
2359
|
for (const issue2 of error52.issues) {
|
|
2139
2360
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2140
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2361
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
2141
2362
|
} else if (issue2.code === "invalid_key") {
|
|
2142
|
-
processError({ issues: issue2.issues }, [...
|
|
2363
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
2143
2364
|
} else if (issue2.code === "invalid_element") {
|
|
2144
|
-
processError({ issues: issue2.issues }, [...
|
|
2365
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
2145
2366
|
} else {
|
|
2146
|
-
const fullpath = [...
|
|
2367
|
+
const fullpath = [...path7, ...issue2.path];
|
|
2147
2368
|
if (fullpath.length === 0) {
|
|
2148
2369
|
fieldErrors._errors.push(mapper(issue2));
|
|
2149
2370
|
} else {
|
|
@@ -2170,17 +2391,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2170
2391
|
}
|
|
2171
2392
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2172
2393
|
const result = { errors: [] };
|
|
2173
|
-
const processError = (error52,
|
|
2394
|
+
const processError = (error52, path7 = []) => {
|
|
2174
2395
|
var _a3, _b;
|
|
2175
2396
|
for (const issue2 of error52.issues) {
|
|
2176
2397
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2177
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2398
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
2178
2399
|
} else if (issue2.code === "invalid_key") {
|
|
2179
|
-
processError({ issues: issue2.issues }, [...
|
|
2400
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
2180
2401
|
} else if (issue2.code === "invalid_element") {
|
|
2181
|
-
processError({ issues: issue2.issues }, [...
|
|
2402
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
2182
2403
|
} else {
|
|
2183
|
-
const fullpath = [...
|
|
2404
|
+
const fullpath = [...path7, ...issue2.path];
|
|
2184
2405
|
if (fullpath.length === 0) {
|
|
2185
2406
|
result.errors.push(mapper(issue2));
|
|
2186
2407
|
continue;
|
|
@@ -2212,8 +2433,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2212
2433
|
}
|
|
2213
2434
|
function toDotPath(_path) {
|
|
2214
2435
|
const segs = [];
|
|
2215
|
-
const
|
|
2216
|
-
for (const seg of
|
|
2436
|
+
const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2437
|
+
for (const seg of path7) {
|
|
2217
2438
|
if (typeof seg === "number")
|
|
2218
2439
|
segs.push(`[${seg}]`);
|
|
2219
2440
|
else if (typeof seg === "symbol")
|
|
@@ -3229,10 +3450,10 @@ var $ZodUUID = /* @__PURE__ */ $constructor("$ZodUUID", (inst, def) => {
|
|
|
3229
3450
|
v7: 7,
|
|
3230
3451
|
v8: 8
|
|
3231
3452
|
};
|
|
3232
|
-
const
|
|
3233
|
-
if (
|
|
3453
|
+
const v4 = versionMap[def.version];
|
|
3454
|
+
if (v4 === void 0)
|
|
3234
3455
|
throw new Error(`Invalid UUID version: "${def.version}"`);
|
|
3235
|
-
def.pattern ?? (def.pattern = uuid(
|
|
3456
|
+
def.pattern ?? (def.pattern = uuid(v4));
|
|
3236
3457
|
} else
|
|
3237
3458
|
def.pattern ?? (def.pattern = uuid());
|
|
3238
3459
|
$ZodStringFormat.init(inst, def);
|
|
@@ -3837,8 +4058,8 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
3837
4058
|
const field2 = shape[key]._zod;
|
|
3838
4059
|
if (field2.values) {
|
|
3839
4060
|
propValues[key] ?? (propValues[key] = /* @__PURE__ */ new Set());
|
|
3840
|
-
for (const
|
|
3841
|
-
propValues[key].add(
|
|
4061
|
+
for (const v4 of field2.values)
|
|
4062
|
+
propValues[key].add(v4);
|
|
3842
4063
|
}
|
|
3843
4064
|
}
|
|
3844
4065
|
return propValues;
|
|
@@ -4133,10 +4354,10 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
|
|
|
4133
4354
|
const pv = option._zod.propValues;
|
|
4134
4355
|
if (!pv || Object.keys(pv).length === 0)
|
|
4135
4356
|
throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(option)}"`);
|
|
4136
|
-
for (const [k,
|
|
4357
|
+
for (const [k, v4] of Object.entries(pv)) {
|
|
4137
4358
|
if (!propValues[k])
|
|
4138
4359
|
propValues[k] = /* @__PURE__ */ new Set();
|
|
4139
|
-
for (const val of
|
|
4360
|
+
for (const val of v4) {
|
|
4140
4361
|
propValues[k].add(val);
|
|
4141
4362
|
}
|
|
4142
4363
|
}
|
|
@@ -4150,11 +4371,11 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
|
|
|
4150
4371
|
const values = o._zod.propValues?.[def.discriminator];
|
|
4151
4372
|
if (!values || values.size === 0)
|
|
4152
4373
|
throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
|
|
4153
|
-
for (const
|
|
4154
|
-
if (map2.has(
|
|
4155
|
-
throw new Error(`Duplicate discriminator value "${String(
|
|
4374
|
+
for (const v4 of values) {
|
|
4375
|
+
if (map2.has(v4)) {
|
|
4376
|
+
throw new Error(`Duplicate discriminator value "${String(v4)}"`);
|
|
4156
4377
|
}
|
|
4157
|
-
map2.set(
|
|
4378
|
+
map2.set(v4, o);
|
|
4158
4379
|
}
|
|
4159
4380
|
}
|
|
4160
4381
|
return map2;
|
|
@@ -4793,8 +5014,8 @@ var $ZodPrefault = /* @__PURE__ */ $constructor("$ZodPrefault", (inst, def) => {
|
|
|
4793
5014
|
var $ZodNonOptional = /* @__PURE__ */ $constructor("$ZodNonOptional", (inst, def) => {
|
|
4794
5015
|
$ZodType.init(inst, def);
|
|
4795
5016
|
defineLazy(inst._zod, "values", () => {
|
|
4796
|
-
const
|
|
4797
|
-
return
|
|
5017
|
+
const v4 = def.innerType._zod.values;
|
|
5018
|
+
return v4 ? new Set([...v4].filter((x) => x !== void 0)) : void 0;
|
|
4798
5019
|
});
|
|
4799
5020
|
inst._zod.parse = (payload, ctx) => {
|
|
4800
5021
|
const result = def.innerType._zod.run(payload, ctx);
|
|
@@ -7191,7 +7412,7 @@ var error17 = () => {
|
|
|
7191
7412
|
if (issue2.values.length === 1) {
|
|
7192
7413
|
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05E2\u05E8\u05DA \u05D7\u05D9\u05D9\u05D1 \u05DC\u05D4\u05D9\u05D5\u05EA ${stringifyPrimitive(issue2.values[0])}`;
|
|
7193
7414
|
}
|
|
7194
|
-
const stringified = issue2.values.map((
|
|
7415
|
+
const stringified = issue2.values.map((v4) => stringifyPrimitive(v4));
|
|
7195
7416
|
if (issue2.values.length === 2) {
|
|
7196
7417
|
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05D4\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05D4\u05DF ${stringified[0]} \u05D0\u05D5 ${stringified[1]}`;
|
|
7197
7418
|
}
|
|
@@ -11946,7 +12167,7 @@ function _set(Class2, valueType, params) {
|
|
|
11946
12167
|
}
|
|
11947
12168
|
// @__NO_SIDE_EFFECTS__
|
|
11948
12169
|
function _enum(Class2, values, params) {
|
|
11949
|
-
const entries = Array.isArray(values) ? Object.fromEntries(values.map((
|
|
12170
|
+
const entries = Array.isArray(values) ? Object.fromEntries(values.map((v4) => [v4, v4])) : values;
|
|
11950
12171
|
return new Class2({
|
|
11951
12172
|
type: "enum",
|
|
11952
12173
|
entries,
|
|
@@ -12151,8 +12372,8 @@ function _stringbool(Classes, _params) {
|
|
|
12151
12372
|
let truthyArray = params.truthy ?? ["true", "1", "yes", "on", "y", "enabled"];
|
|
12152
12373
|
let falsyArray = params.falsy ?? ["false", "0", "no", "off", "n", "disabled"];
|
|
12153
12374
|
if (params.case !== "sensitive") {
|
|
12154
|
-
truthyArray = truthyArray.map((
|
|
12155
|
-
falsyArray = falsyArray.map((
|
|
12375
|
+
truthyArray = truthyArray.map((v4) => typeof v4 === "string" ? v4.toLowerCase() : v4);
|
|
12376
|
+
falsyArray = falsyArray.map((v4) => typeof v4 === "string" ? v4.toLowerCase() : v4);
|
|
12156
12377
|
}
|
|
12157
12378
|
const truthySet = new Set(truthyArray);
|
|
12158
12379
|
const falsySet = new Set(falsyArray);
|
|
@@ -12694,9 +12915,9 @@ var dateProcessor = (_schema, ctx, _json, _params) => {
|
|
|
12694
12915
|
var enumProcessor = (schema, _ctx, json2, _params) => {
|
|
12695
12916
|
const def = schema._zod.def;
|
|
12696
12917
|
const values = getEnumValues(def.entries);
|
|
12697
|
-
if (values.every((
|
|
12918
|
+
if (values.every((v4) => typeof v4 === "number"))
|
|
12698
12919
|
json2.type = "number";
|
|
12699
|
-
if (values.every((
|
|
12920
|
+
if (values.every((v4) => typeof v4 === "string"))
|
|
12700
12921
|
json2.type = "string";
|
|
12701
12922
|
json2.enum = values;
|
|
12702
12923
|
};
|
|
@@ -12729,13 +12950,13 @@ var literalProcessor = (schema, ctx, json2, _params) => {
|
|
|
12729
12950
|
json2.const = val;
|
|
12730
12951
|
}
|
|
12731
12952
|
} else {
|
|
12732
|
-
if (vals.every((
|
|
12953
|
+
if (vals.every((v4) => typeof v4 === "number"))
|
|
12733
12954
|
json2.type = "number";
|
|
12734
|
-
if (vals.every((
|
|
12955
|
+
if (vals.every((v4) => typeof v4 === "string"))
|
|
12735
12956
|
json2.type = "string";
|
|
12736
|
-
if (vals.every((
|
|
12957
|
+
if (vals.every((v4) => typeof v4 === "boolean"))
|
|
12737
12958
|
json2.type = "boolean";
|
|
12738
|
-
if (vals.every((
|
|
12959
|
+
if (vals.every((v4) => v4 === null))
|
|
12739
12960
|
json2.type = "null";
|
|
12740
12961
|
json2.enum = vals;
|
|
12741
12962
|
}
|
|
@@ -12833,11 +13054,11 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12833
13054
|
}
|
|
12834
13055
|
const allKeys = new Set(Object.keys(shape));
|
|
12835
13056
|
const requiredKeys = new Set([...allKeys].filter((key) => {
|
|
12836
|
-
const
|
|
13057
|
+
const v4 = def.shape[key]._zod;
|
|
12837
13058
|
if (ctx.io === "input") {
|
|
12838
|
-
return
|
|
13059
|
+
return v4.optin === void 0;
|
|
12839
13060
|
} else {
|
|
12840
|
-
return
|
|
13061
|
+
return v4.optout === void 0;
|
|
12841
13062
|
}
|
|
12842
13063
|
}));
|
|
12843
13064
|
if (requiredKeys.size > 0) {
|
|
@@ -12957,7 +13178,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12957
13178
|
}
|
|
12958
13179
|
const keyValues = keyType._zod.values;
|
|
12959
13180
|
if (keyValues) {
|
|
12960
|
-
const validKeyValues = [...keyValues].filter((
|
|
13181
|
+
const validKeyValues = [...keyValues].filter((v4) => typeof v4 === "string" || typeof v4 === "number");
|
|
12961
13182
|
if (validKeyValues.length > 0) {
|
|
12962
13183
|
json2.required = validKeyValues;
|
|
12963
13184
|
}
|
|
@@ -13522,12 +13743,12 @@ function _installLazyMethods(inst, group, methods) {
|
|
|
13522
13743
|
});
|
|
13523
13744
|
return bound;
|
|
13524
13745
|
},
|
|
13525
|
-
set(
|
|
13746
|
+
set(v4) {
|
|
13526
13747
|
Object.defineProperty(this, key, {
|
|
13527
13748
|
configurable: true,
|
|
13528
13749
|
writable: true,
|
|
13529
13750
|
enumerable: true,
|
|
13530
|
-
value:
|
|
13751
|
+
value: v4
|
|
13531
13752
|
});
|
|
13532
13753
|
}
|
|
13533
13754
|
});
|
|
@@ -14415,7 +14636,7 @@ var ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
|
|
|
14415
14636
|
};
|
|
14416
14637
|
});
|
|
14417
14638
|
function _enum2(values, params) {
|
|
14418
|
-
const entries = Array.isArray(values) ? Object.fromEntries(values.map((
|
|
14639
|
+
const entries = Array.isArray(values) ? Object.fromEntries(values.map((v4) => [v4, v4])) : values;
|
|
14419
14640
|
return new ZodEnum({
|
|
14420
14641
|
type: "enum",
|
|
14421
14642
|
entries,
|
|
@@ -14905,13 +15126,13 @@ function resolveRef(ref, ctx) {
|
|
|
14905
15126
|
if (!ref.startsWith("#")) {
|
|
14906
15127
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
14907
15128
|
}
|
|
14908
|
-
const
|
|
14909
|
-
if (
|
|
15129
|
+
const path7 = ref.slice(1).split("/").filter(Boolean);
|
|
15130
|
+
if (path7.length === 0) {
|
|
14910
15131
|
return ctx.rootSchema;
|
|
14911
15132
|
}
|
|
14912
15133
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
14913
|
-
if (
|
|
14914
|
-
const key =
|
|
15134
|
+
if (path7[0] === defsKey) {
|
|
15135
|
+
const key = path7[1];
|
|
14915
15136
|
if (!key || !ctx.defs[key]) {
|
|
14916
15137
|
throw new Error(`Reference not found: ${ref}`);
|
|
14917
15138
|
}
|
|
@@ -14969,10 +15190,10 @@ function convertBaseSchema(schema, ctx) {
|
|
|
14969
15190
|
if (enumValues.length === 1) {
|
|
14970
15191
|
return z.literal(enumValues[0]);
|
|
14971
15192
|
}
|
|
14972
|
-
if (enumValues.every((
|
|
15193
|
+
if (enumValues.every((v4) => typeof v4 === "string")) {
|
|
14973
15194
|
return z.enum(enumValues);
|
|
14974
15195
|
}
|
|
14975
|
-
const literalSchemas = enumValues.map((
|
|
15196
|
+
const literalSchemas = enumValues.map((v4) => z.literal(v4));
|
|
14976
15197
|
if (literalSchemas.length < 2) {
|
|
14977
15198
|
return literalSchemas[0];
|
|
14978
15199
|
}
|
|
@@ -15671,8 +15892,8 @@ function baseUrlReference(semantics) {
|
|
|
15671
15892
|
const label = OPENING_HOLE.exec(semantics.path ?? "")?.[1];
|
|
15672
15893
|
return label === void 0 ? null : referenceFromName(patternHole(label));
|
|
15673
15894
|
}
|
|
15674
|
-
function normalizePath(
|
|
15675
|
-
let normalized =
|
|
15895
|
+
function normalizePath(path7) {
|
|
15896
|
+
let normalized = path7.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
15676
15897
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
15677
15898
|
normalized = normalized.slice(0, -1);
|
|
15678
15899
|
}
|
|
@@ -15682,8 +15903,8 @@ function normalizePath(path4) {
|
|
|
15682
15903
|
);
|
|
15683
15904
|
return normalized;
|
|
15684
15905
|
}
|
|
15685
|
-
function pathShape(
|
|
15686
|
-
return normalizePath(
|
|
15906
|
+
function pathShape(path7) {
|
|
15907
|
+
return normalizePath(path7).replace(/\{[^}]*\}/g, "{}");
|
|
15687
15908
|
}
|
|
15688
15909
|
function methodsAgree(a, b) {
|
|
15689
15910
|
if (a === null || b === null) {
|
|
@@ -15761,8 +15982,8 @@ var restSemantics = defineBoundarySemantics({
|
|
|
15761
15982
|
return null;
|
|
15762
15983
|
}
|
|
15763
15984
|
const method = semantics.method === null ? "ANY" : semantics.method.toUpperCase();
|
|
15764
|
-
const
|
|
15765
|
-
return `${method} ${
|
|
15985
|
+
const path7 = semantics.path === null ? "?" : normalizePath(semantics.path);
|
|
15986
|
+
return `${method} ${path7}`;
|
|
15766
15987
|
},
|
|
15767
15988
|
/**
|
|
15768
15989
|
* A route with both halves stated decides for itself: the method
|
|
@@ -15771,14 +15992,14 @@ var restSemantics = defineBoundarySemantics({
|
|
|
15771
15992
|
* half might still be the one that handles the request, and nothing
|
|
15772
15993
|
* here can settle that, so it abstains instead of refusing.
|
|
15773
15994
|
*/
|
|
15774
|
-
servesRequest(semantics, method,
|
|
15995
|
+
servesRequest(semantics, method, path7) {
|
|
15775
15996
|
if (semantics.method === null || semantics.path === null) {
|
|
15776
15997
|
return "unknown";
|
|
15777
15998
|
}
|
|
15778
15999
|
if (!methodsAgree(semantics.method, method.toUpperCase())) {
|
|
15779
16000
|
return "nomatch";
|
|
15780
16001
|
}
|
|
15781
|
-
return routePathAdmits(semantics.path,
|
|
16002
|
+
return routePathAdmits(semantics.path, path7) ? "match" : "nomatch";
|
|
15782
16003
|
},
|
|
15783
16004
|
/**
|
|
15784
16005
|
* A route the registration pattern admits is inside it. A route
|
|
@@ -16198,323 +16419,619 @@ function storageBinding(opts) {
|
|
|
16198
16419
|
// ../../resolution/dist/index.js
|
|
16199
16420
|
var import_datalog = require("@suss/datalog");
|
|
16200
16421
|
var import_datalog2 = require("@suss/datalog");
|
|
16201
|
-
var
|
|
16422
|
+
var import_datalog3 = require("@suss/datalog");
|
|
16423
|
+
function exportTrail(proof, describe3) {
|
|
16424
|
+
if (proof.kind !== "derived") {
|
|
16425
|
+
return [];
|
|
16426
|
+
}
|
|
16427
|
+
const name = (0, import_datalog.ruleLabel)(proof.rule);
|
|
16428
|
+
if (name === "re-export") {
|
|
16429
|
+
const [m, n, m2, n2] = proof.premises[0].tuple;
|
|
16430
|
+
return [
|
|
16431
|
+
`${describe3(m)} takes ${String(n)} from ${describe3(m2)}, where it is called ${String(n2)}`,
|
|
16432
|
+
...exportTrail(proof.premises[1], describe3)
|
|
16433
|
+
];
|
|
16434
|
+
}
|
|
16435
|
+
if (name === "re-export all") {
|
|
16436
|
+
const [m, m2] = proof.premises[0].tuple;
|
|
16437
|
+
return [
|
|
16438
|
+
`${describe3(m)} forwards everything ${describe3(m2)} exports`,
|
|
16439
|
+
...exportTrail(proof.premises[1], describe3)
|
|
16440
|
+
];
|
|
16441
|
+
}
|
|
16442
|
+
return [];
|
|
16443
|
+
}
|
|
16444
|
+
function argumentReason(proof, describe3) {
|
|
16445
|
+
const [, p, a] = proof.tuple;
|
|
16446
|
+
if (proof.kind !== "derived") {
|
|
16447
|
+
return `${describe3(p)} is a parameter, and a call passes it ${describe3(a)}`;
|
|
16448
|
+
}
|
|
16449
|
+
const f = proof.premises[0].tuple[0];
|
|
16450
|
+
return `${describe3(p)} is a parameter of ${describe3(f)}, and a call passes it ${describe3(a)}`;
|
|
16451
|
+
}
|
|
16452
|
+
var DEFAULT_PHRASES = {
|
|
16453
|
+
alias: ({ tuple: tuple2, describe: describe3 }) => ({
|
|
16454
|
+
reason: `${describe3(tuple2[0])} is declared as ${describe3(tuple2[1])}`
|
|
16455
|
+
}),
|
|
16456
|
+
"last write": ({ tuple: tuple2, describe: describe3 }) => ({
|
|
16457
|
+
reason: `${describe3(tuple2[0])} is written more than once, and the last write leaves it as ${describe3(tuple2[1])}`
|
|
16458
|
+
}),
|
|
16459
|
+
fallback: ({ tuple: tuple2, describe: describe3 }) => ({
|
|
16460
|
+
reason: `${describe3(tuple2[0])} is a fallback expression, and ${describe3(tuple2[1])} is the branch that resolves`
|
|
16461
|
+
}),
|
|
16462
|
+
import: ({ tuple: tuple2, premises, describe: describe3 }) => {
|
|
16463
|
+
const [, module2, name] = premises[0].tuple;
|
|
16464
|
+
return {
|
|
16465
|
+
reason: `${describe3(tuple2[0])} is imported from ${describe3(module2)} under the name ${String(name)}`,
|
|
16466
|
+
notes: exportTrail(premises[1], describe3)
|
|
16467
|
+
};
|
|
16468
|
+
},
|
|
16469
|
+
argument: ({ premises, describe: describe3 }) => ({
|
|
16470
|
+
reason: argumentReason(premises[0], describe3)
|
|
16471
|
+
}),
|
|
16472
|
+
"property read": ({ tuple: tuple2, premises, describe: describe3 }) => {
|
|
16473
|
+
const [, object2, name] = premises[0].tuple;
|
|
16474
|
+
const contains = premises[2];
|
|
16475
|
+
const inherited = contains.kind === "derived" && contains.premises.length > 1;
|
|
16476
|
+
return {
|
|
16477
|
+
reason: `${describe3(tuple2[0])} reads ${String(name)} off ${describe3(object2)}, which contains ${describe3(tuple2[1])}`,
|
|
16478
|
+
notes: inherited ? [`${String(name)} comes from a base class`] : []
|
|
16479
|
+
};
|
|
16480
|
+
},
|
|
16481
|
+
"class instance": ({ tuple: tuple2, premises, describe: describe3, inline }) => ({
|
|
16482
|
+
reason: `${describe3(tuple2[0])} makes an instance of ${describe3(tuple2[1])}`,
|
|
16483
|
+
notes: inline(premises[1])
|
|
16484
|
+
}),
|
|
16485
|
+
"factory unwrap": ({ tuple: tuple2, premises, describe: describe3, inline }) => {
|
|
16486
|
+
const factory = premises[1].tuple[1];
|
|
16487
|
+
return {
|
|
16488
|
+
reason: `${describe3(tuple2[0])} calls ${describe3(factory)}, a factory that passes its argument through, so it comes down to ${describe3(tuple2[1])}`,
|
|
16489
|
+
notes: inline(premises[1])
|
|
16490
|
+
};
|
|
16491
|
+
},
|
|
16492
|
+
"declared wrapper": ({ tuple: tuple2, premises, describe: describe3 }) => {
|
|
16493
|
+
const callee = String(premises[0].tuple[1]);
|
|
16494
|
+
const module2 = String(premises[2].tuple[1]);
|
|
16495
|
+
const argument = String(premises[1].tuple[1]);
|
|
16496
|
+
return {
|
|
16497
|
+
reason: `${describe3(tuple2[0])} calls ${callee} from ${module2}, which passes argument ${argument} through, so it comes down to ${describe3(tuple2[1])}`,
|
|
16498
|
+
assumptions: [
|
|
16499
|
+
`a pack declares that ${callee} from ${module2} passes argument ${argument} through to its result`
|
|
16500
|
+
]
|
|
16501
|
+
};
|
|
16502
|
+
},
|
|
16503
|
+
"call result": ({ tuple: tuple2, premises, describe: describe3, inline }) => {
|
|
16504
|
+
const invokes = premises[0];
|
|
16505
|
+
const invoked = invokes.tuple[1];
|
|
16506
|
+
const calleeWalks = invokes.kind === "derived" ? invokes.premises.filter(
|
|
16507
|
+
(premise) => premise.kind === "derived" && (premise.relation === "comesTo" || premise.relation === "givesBack")
|
|
16508
|
+
) : [];
|
|
16509
|
+
return {
|
|
16510
|
+
reason: `${describe3(tuple2[0])} runs ${describe3(invoked)}, which returns ${describe3(tuple2[1])}`,
|
|
16511
|
+
notes: calleeWalks.flatMap((premise) => inline(premise))
|
|
16512
|
+
};
|
|
16513
|
+
}
|
|
16514
|
+
};
|
|
16515
|
+
var CHAIN_RELATIONS = /* @__PURE__ */ new Set([
|
|
16516
|
+
"resolves",
|
|
16517
|
+
"comesTo",
|
|
16518
|
+
"givesBack",
|
|
16519
|
+
"isWrittenAs",
|
|
16520
|
+
"comesFrom",
|
|
16521
|
+
"reaches",
|
|
16522
|
+
"stepsTo"
|
|
16523
|
+
]);
|
|
16524
|
+
function inlineChain(proof, state) {
|
|
16525
|
+
const explained = explainResolutionProof(proof, {
|
|
16526
|
+
describe: state.describe,
|
|
16527
|
+
phrases: state.phrases
|
|
16528
|
+
});
|
|
16529
|
+
if (explained === null || explained.steps.length === 0) {
|
|
16530
|
+
return [];
|
|
16531
|
+
}
|
|
16532
|
+
if (explained.truncated) {
|
|
16533
|
+
state.truncated = true;
|
|
16534
|
+
}
|
|
16535
|
+
return explained.steps.map((step) => step.reason);
|
|
16536
|
+
}
|
|
16537
|
+
function stepFrom(proof, state) {
|
|
16538
|
+
const name = (0, import_datalog.ruleLabel)(proof.rule);
|
|
16539
|
+
const phrase = state.phrases[name];
|
|
16540
|
+
const from = proof.tuple[0];
|
|
16541
|
+
const to = proof.tuple[1];
|
|
16542
|
+
if (phrase === void 0) {
|
|
16543
|
+
return {
|
|
16544
|
+
from,
|
|
16545
|
+
to,
|
|
16546
|
+
rule: name,
|
|
16547
|
+
reason: `${state.describe(from)} steps to ${state.describe(to)} (${name})`,
|
|
16548
|
+
notes: [],
|
|
16549
|
+
assumptions: []
|
|
16550
|
+
};
|
|
16551
|
+
}
|
|
16552
|
+
const said = phrase({
|
|
16553
|
+
tuple: proof.tuple,
|
|
16554
|
+
premises: proof.premises,
|
|
16555
|
+
describe: state.describe,
|
|
16556
|
+
inline: (nested) => inlineChain(nested, state)
|
|
16557
|
+
});
|
|
16558
|
+
return {
|
|
16559
|
+
from,
|
|
16560
|
+
to,
|
|
16561
|
+
rule: name,
|
|
16562
|
+
reason: said.reason,
|
|
16563
|
+
notes: said.notes ?? [],
|
|
16564
|
+
assumptions: said.assumptions ?? []
|
|
16565
|
+
};
|
|
16566
|
+
}
|
|
16567
|
+
function importStep(proof, state) {
|
|
16568
|
+
const [x, module2, name] = proof.tuple;
|
|
16569
|
+
return {
|
|
16570
|
+
from: x,
|
|
16571
|
+
to: module2,
|
|
16572
|
+
rule: "import",
|
|
16573
|
+
reason: `${state.describe(x)} is imported from ${state.describe(module2)} under the name ${String(name)}`,
|
|
16574
|
+
notes: [],
|
|
16575
|
+
assumptions: []
|
|
16576
|
+
};
|
|
16577
|
+
}
|
|
16578
|
+
var flattenInto = (proof, state) => {
|
|
16579
|
+
if (proof.kind === "truncated") {
|
|
16580
|
+
state.truncated = true;
|
|
16581
|
+
return;
|
|
16582
|
+
}
|
|
16583
|
+
if (proof.kind !== "derived") {
|
|
16584
|
+
return;
|
|
16585
|
+
}
|
|
16586
|
+
const relation = proof.relation;
|
|
16587
|
+
if (relation === "stepsTo") {
|
|
16588
|
+
state.steps.push(stepFrom(proof, state));
|
|
16589
|
+
return;
|
|
16590
|
+
}
|
|
16591
|
+
if (relation === "reaches") {
|
|
16592
|
+
for (const p of proof.premises) {
|
|
16593
|
+
flattenInto(p, state);
|
|
16594
|
+
}
|
|
16595
|
+
return;
|
|
16596
|
+
}
|
|
16597
|
+
if (relation === "comesFrom") {
|
|
16598
|
+
if (proof.premises.length === 2) {
|
|
16599
|
+
flattenInto(proof.premises[0], state);
|
|
16600
|
+
state.steps.push(importStep(proof.premises[1], state));
|
|
16601
|
+
} else {
|
|
16602
|
+
state.steps.push(importStep(proof.premises[0], state));
|
|
16603
|
+
}
|
|
16604
|
+
return;
|
|
16605
|
+
}
|
|
16606
|
+
for (const p of proof.premises) {
|
|
16607
|
+
if (p.kind === "derived" && (p.relation === "reaches" || p.relation === "comesTo")) {
|
|
16608
|
+
flattenInto(p, state);
|
|
16609
|
+
}
|
|
16610
|
+
if (p.kind === "truncated") {
|
|
16611
|
+
state.truncated = true;
|
|
16612
|
+
}
|
|
16613
|
+
}
|
|
16614
|
+
};
|
|
16615
|
+
function explainResolutionProof(proof, options) {
|
|
16616
|
+
if (proof.kind === "absence") {
|
|
16617
|
+
return null;
|
|
16618
|
+
}
|
|
16619
|
+
if (proof.kind === "derived" && !CHAIN_RELATIONS.has(proof.relation)) {
|
|
16620
|
+
return null;
|
|
16621
|
+
}
|
|
16622
|
+
const state = {
|
|
16623
|
+
steps: [],
|
|
16624
|
+
truncated: proof.kind === "truncated",
|
|
16625
|
+
describe: options.describe,
|
|
16626
|
+
phrases: { ...DEFAULT_PHRASES, ...options.phrases }
|
|
16627
|
+
};
|
|
16628
|
+
flattenInto(proof, state);
|
|
16629
|
+
const atoms = proof.kind === "fact" || proof.kind === "truncated" ? [proof.tuple[0]] : [proof.tuple[0], ...state.steps.map((step) => step.to)];
|
|
16630
|
+
return {
|
|
16631
|
+
atoms,
|
|
16632
|
+
steps: state.steps,
|
|
16633
|
+
assumptions: state.steps.flatMap((step) => step.assumptions),
|
|
16634
|
+
truncated: state.truncated
|
|
16635
|
+
};
|
|
16636
|
+
}
|
|
16637
|
+
function renderExplanation(explanation, describe3) {
|
|
16638
|
+
const lines = [];
|
|
16639
|
+
if (explanation.steps.length > 0) {
|
|
16640
|
+
lines.push(explanation.atoms.map((atom) => describe3(atom)).join(" -> "));
|
|
16641
|
+
}
|
|
16642
|
+
for (const step of explanation.steps) {
|
|
16643
|
+
lines.push(` ${step.reason}`);
|
|
16644
|
+
for (const note of step.notes) {
|
|
16645
|
+
lines.push(` ${note}`);
|
|
16646
|
+
}
|
|
16647
|
+
}
|
|
16648
|
+
for (const assumption of explanation.assumptions) {
|
|
16649
|
+
lines.push(` assuming ${assumption}`);
|
|
16650
|
+
}
|
|
16651
|
+
if (explanation.truncated) {
|
|
16652
|
+
lines.push(" the chain goes on past the proof depth cap");
|
|
16653
|
+
}
|
|
16654
|
+
return lines;
|
|
16655
|
+
}
|
|
16656
|
+
function explainResolvedKey(options) {
|
|
16657
|
+
const { db, rules, key, locate, displayPath } = options;
|
|
16658
|
+
const proofDb = new import_datalog2.Database();
|
|
16659
|
+
let baseFacts = 0;
|
|
16660
|
+
for (const relation of db.relationNames()) {
|
|
16661
|
+
for (const tuple2 of db.facts(relation)) {
|
|
16662
|
+
proofDb.add(relation, tuple2);
|
|
16663
|
+
baseFacts++;
|
|
16664
|
+
}
|
|
16665
|
+
}
|
|
16666
|
+
const started = performance.now();
|
|
16667
|
+
(0, import_datalog2.evaluate)(proofDb, rules, import_datalog2.witnesses);
|
|
16668
|
+
const evaluateMs = performance.now() - started;
|
|
16669
|
+
const derivedFacts = proofDb.relationNames().reduce((count, relation) => count + proofDb.size(relation), 0) - baseFacts;
|
|
16670
|
+
const targets = new Set(
|
|
16671
|
+
proofDb.lookup("resolves", 0, key).map((tuple2) => String(tuple2[1]))
|
|
16672
|
+
);
|
|
16673
|
+
if (targets.size !== 1) {
|
|
16674
|
+
return null;
|
|
16675
|
+
}
|
|
16676
|
+
const target = [...targets][0];
|
|
16677
|
+
const proof = (0, import_datalog2.proofOf)(
|
|
16678
|
+
proofDb,
|
|
16679
|
+
"resolves",
|
|
16680
|
+
[key, target],
|
|
16681
|
+
options.maxDepth === void 0 ? {} : { maxDepth: options.maxDepth }
|
|
16682
|
+
);
|
|
16683
|
+
const describe3 = (atom) => {
|
|
16684
|
+
const location = locate(String(atom));
|
|
16685
|
+
return location === null ? displayPath(String(atom)) : `${location.name} (${location.file}:${location.line})`;
|
|
16686
|
+
};
|
|
16687
|
+
const explanation = explainResolutionProof(proof, {
|
|
16688
|
+
describe: describe3,
|
|
16689
|
+
...options.phrases === void 0 ? {} : { phrases: options.phrases }
|
|
16690
|
+
});
|
|
16691
|
+
if (explanation === null) {
|
|
16692
|
+
return null;
|
|
16693
|
+
}
|
|
16694
|
+
const targetLocation = locate(target);
|
|
16695
|
+
if (targetLocation === null) {
|
|
16696
|
+
return null;
|
|
16697
|
+
}
|
|
16698
|
+
return {
|
|
16699
|
+
explanation,
|
|
16700
|
+
chain: explanation.atoms.map(describe3),
|
|
16701
|
+
lines: renderExplanation(explanation, describe3),
|
|
16702
|
+
target: targetLocation,
|
|
16703
|
+
stats: { baseFacts, derivedFacts, evaluateMs }
|
|
16704
|
+
};
|
|
16705
|
+
}
|
|
16706
|
+
var VALUE_STEP = (0, import_datalog3.constant)("value");
|
|
16202
16707
|
var NAMESPACE_IMPORT_NAME = "*";
|
|
16203
|
-
var NAMESPACE_IMPORT = (0,
|
|
16204
|
-
var RESULT_STEP = (0,
|
|
16708
|
+
var NAMESPACE_IMPORT = (0, import_datalog3.constant)(NAMESPACE_IMPORT_NAME);
|
|
16709
|
+
var RESULT_STEP = (0, import_datalog3.constant)("result");
|
|
16205
16710
|
var RESOLUTION_RULES = [
|
|
16206
16711
|
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
16207
16712
|
// A language with a hop of its own, like JavaScript's `.bind`, states
|
|
16208
16713
|
// it as a step too, or every question but `comesTo` misses it.
|
|
16209
|
-
(0,
|
|
16714
|
+
(0, import_datalog3.rule)(
|
|
16210
16715
|
"stepsTo",
|
|
16211
|
-
[(0,
|
|
16212
|
-
[(0,
|
|
16716
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP],
|
|
16717
|
+
[(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))],
|
|
16213
16718
|
"alias"
|
|
16214
16719
|
),
|
|
16215
16720
|
// A name written more than once has the value the last write left
|
|
16216
16721
|
// there. The adapter works out which write that is and stays quiet
|
|
16217
16722
|
// when control flow decides, so such a name steps nowhere at all.
|
|
16218
|
-
(0,
|
|
16723
|
+
(0, import_datalog3.rule)(
|
|
16219
16724
|
"stepsTo",
|
|
16220
|
-
[(0,
|
|
16221
|
-
[(0,
|
|
16725
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP],
|
|
16726
|
+
[(0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))],
|
|
16222
16727
|
"last write"
|
|
16223
16728
|
),
|
|
16224
16729
|
// A fallback says the value is one of its branches, so each branch is
|
|
16225
16730
|
// a step. A branch that resolves to nothing makes no claim, and two
|
|
16226
16731
|
// branches resolving to different things fail the single-answer policy.
|
|
16227
|
-
(0,
|
|
16732
|
+
(0, import_datalog3.rule)(
|
|
16228
16733
|
"stepsTo",
|
|
16229
|
-
[(0,
|
|
16230
|
-
[(0,
|
|
16734
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("b"), VALUE_STEP],
|
|
16735
|
+
[(0, import_datalog3.lit)("fallbackBranch", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("b"))],
|
|
16231
16736
|
"fallback"
|
|
16232
16737
|
),
|
|
16233
16738
|
// An import steps to what the module exports under that name.
|
|
16234
|
-
(0,
|
|
16739
|
+
(0, import_datalog3.rule)(
|
|
16235
16740
|
"stepsTo",
|
|
16236
|
-
[(0,
|
|
16741
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("value"), VALUE_STEP],
|
|
16237
16742
|
[
|
|
16238
|
-
(0,
|
|
16239
|
-
(0,
|
|
16743
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
|
|
16744
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))
|
|
16240
16745
|
],
|
|
16241
16746
|
"import"
|
|
16242
16747
|
),
|
|
16243
16748
|
// A parameter steps to what a call passes it. A function called from
|
|
16244
16749
|
// several places leaves its parameter with more than one value, and a
|
|
16245
16750
|
// caller that needs those apart asks `paramAt`.
|
|
16246
|
-
(0,
|
|
16751
|
+
(0, import_datalog3.rule)(
|
|
16247
16752
|
"stepsTo",
|
|
16248
|
-
[(0,
|
|
16249
|
-
[(0,
|
|
16753
|
+
[(0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a"), VALUE_STEP],
|
|
16754
|
+
[(0, import_datalog3.lit)("passesArgument", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a"))],
|
|
16250
16755
|
"argument"
|
|
16251
16756
|
),
|
|
16252
16757
|
// Reading a property steps to what the object contains under that
|
|
16253
16758
|
// name, whichever way the object arrived: `routes.list` off a name,
|
|
16254
16759
|
// or `make(body).handle` off a call.
|
|
16255
|
-
(0,
|
|
16760
|
+
(0, import_datalog3.rule)(
|
|
16256
16761
|
"stepsTo",
|
|
16257
|
-
[(0,
|
|
16762
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("held"), VALUE_STEP],
|
|
16258
16763
|
[
|
|
16259
|
-
(0,
|
|
16260
|
-
(0,
|
|
16261
|
-
(0,
|
|
16764
|
+
(0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n")),
|
|
16765
|
+
(0, import_datalog3.lit)("objectOf", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("obj")),
|
|
16766
|
+
(0, import_datalog3.lit)("contains", (0, import_datalog3.variable)("obj"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held"))
|
|
16262
16767
|
],
|
|
16263
16768
|
"property read"
|
|
16264
16769
|
),
|
|
16265
16770
|
// Calling a class makes one of it, so the call steps to the class and
|
|
16266
16771
|
// a method read off the result is the one the class declares. The
|
|
16267
16772
|
// caveat below is about a factory function, and a class is not one.
|
|
16268
|
-
(0,
|
|
16773
|
+
(0, import_datalog3.rule)(
|
|
16269
16774
|
"stepsTo",
|
|
16270
|
-
[(0,
|
|
16775
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("cls"), VALUE_STEP],
|
|
16271
16776
|
[
|
|
16272
|
-
(0,
|
|
16273
|
-
(0,
|
|
16274
|
-
(0,
|
|
16777
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
|
|
16778
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("cls")),
|
|
16779
|
+
(0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("cls"))
|
|
16275
16780
|
],
|
|
16276
16781
|
"class instance"
|
|
16277
16782
|
),
|
|
16278
16783
|
// Wrapper transparency, derived: calling a factory that returns a
|
|
16279
16784
|
// function which calls its parameter k steps to argument k.
|
|
16280
|
-
(0,
|
|
16785
|
+
(0, import_datalog3.rule)(
|
|
16281
16786
|
"stepsTo",
|
|
16282
|
-
[(0,
|
|
16787
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("a"), VALUE_STEP],
|
|
16283
16788
|
[
|
|
16284
|
-
(0,
|
|
16285
|
-
(0,
|
|
16286
|
-
(0,
|
|
16287
|
-
(0,
|
|
16789
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
|
|
16790
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f")),
|
|
16791
|
+
(0, import_datalog3.lit)("unwraps", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")),
|
|
16792
|
+
(0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a"))
|
|
16288
16793
|
],
|
|
16289
16794
|
"factory unwrap"
|
|
16290
16795
|
),
|
|
16291
16796
|
// Wrapper transparency, declared: a pack says this callee wraps
|
|
16292
16797
|
// argument k. The callee has to come from the library the pack said,
|
|
16293
16798
|
// so a local object spelled the same way is not mistaken for it.
|
|
16294
|
-
(0,
|
|
16799
|
+
(0, import_datalog3.rule)(
|
|
16295
16800
|
"stepsTo",
|
|
16296
|
-
[(0,
|
|
16801
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("a"), VALUE_STEP],
|
|
16297
16802
|
[
|
|
16298
|
-
(0,
|
|
16299
|
-
(0,
|
|
16300
|
-
(0,
|
|
16301
|
-
(0,
|
|
16302
|
-
(0,
|
|
16803
|
+
(0, import_datalog3.lit)("calleeName", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("n")),
|
|
16804
|
+
(0, import_datalog3.lit)("unwrapsByName", (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("k")),
|
|
16805
|
+
(0, import_datalog3.lit)("wrapperModule", (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m")),
|
|
16806
|
+
(0, import_datalog3.lit)("calleeOrigin", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("m")),
|
|
16807
|
+
(0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a"))
|
|
16303
16808
|
],
|
|
16304
16809
|
"declared wrapper"
|
|
16305
16810
|
),
|
|
16306
16811
|
// The one step that runs a function forwards: a call steps to what
|
|
16307
16812
|
// the function it invokes returns.
|
|
16308
|
-
(0,
|
|
16813
|
+
(0, import_datalog3.rule)(
|
|
16309
16814
|
"stepsTo",
|
|
16310
|
-
[(0,
|
|
16311
|
-
[(0,
|
|
16815
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("ret"), RESULT_STEP],
|
|
16816
|
+
[(0, import_datalog3.lit)("invokes", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")), (0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("ret"))],
|
|
16312
16817
|
"call result"
|
|
16313
16818
|
),
|
|
16314
16819
|
// Where a walk gets to, and whether it ran a call on the way. The walk
|
|
16315
16820
|
// so far comes first, so demand stays on the value asked about; the
|
|
16316
16821
|
// README says what the other order cost.
|
|
16317
|
-
(0,
|
|
16822
|
+
(0, import_datalog3.rule)(
|
|
16318
16823
|
"reaches",
|
|
16319
|
-
[(0,
|
|
16320
|
-
[(0,
|
|
16824
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind")],
|
|
16825
|
+
[(0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind"))]
|
|
16321
16826
|
),
|
|
16322
|
-
(0,
|
|
16827
|
+
(0, import_datalog3.rule)(
|
|
16323
16828
|
"reaches",
|
|
16324
|
-
[(0,
|
|
16829
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind")],
|
|
16325
16830
|
[
|
|
16326
|
-
(0,
|
|
16327
|
-
(0,
|
|
16831
|
+
(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP),
|
|
16832
|
+
(0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind"))
|
|
16328
16833
|
]
|
|
16329
16834
|
),
|
|
16330
16835
|
// Two rules rather than one leaving the next step's kind free. A free
|
|
16331
16836
|
// kind is a second question about the same relation, and a
|
|
16332
16837
|
// demand-driven run then derives both to answer either.
|
|
16333
|
-
(0,
|
|
16838
|
+
(0, import_datalog3.rule)(
|
|
16334
16839
|
"reaches",
|
|
16335
|
-
[(0,
|
|
16840
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP],
|
|
16336
16841
|
[
|
|
16337
|
-
(0,
|
|
16338
|
-
(0,
|
|
16842
|
+
(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), RESULT_STEP),
|
|
16843
|
+
(0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"), VALUE_STEP)
|
|
16339
16844
|
]
|
|
16340
16845
|
),
|
|
16341
|
-
(0,
|
|
16846
|
+
(0, import_datalog3.rule)(
|
|
16342
16847
|
"reaches",
|
|
16343
|
-
[(0,
|
|
16848
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP],
|
|
16344
16849
|
[
|
|
16345
|
-
(0,
|
|
16346
|
-
(0,
|
|
16850
|
+
(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), RESULT_STEP),
|
|
16851
|
+
(0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"), RESULT_STEP)
|
|
16347
16852
|
]
|
|
16348
16853
|
),
|
|
16349
16854
|
// What a value comes down to: the walk that never ran a call, stopped
|
|
16350
16855
|
// at a function or an object. Something already written out comes to
|
|
16351
16856
|
// itself, and every other walk ends at one of those.
|
|
16352
|
-
(0,
|
|
16353
|
-
(0,
|
|
16354
|
-
(0,
|
|
16857
|
+
(0, import_datalog3.rule)("comesTo", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("func", (0, import_datalog3.variable)("x"))]),
|
|
16858
|
+
(0, import_datalog3.rule)("comesTo", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("x"))]),
|
|
16859
|
+
(0, import_datalog3.rule)(
|
|
16355
16860
|
"comesTo",
|
|
16356
|
-
[(0,
|
|
16357
|
-
[(0,
|
|
16861
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16862
|
+
[(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("z"))]
|
|
16358
16863
|
),
|
|
16359
|
-
(0,
|
|
16864
|
+
(0, import_datalog3.rule)(
|
|
16360
16865
|
"comesTo",
|
|
16361
|
-
[(0,
|
|
16362
|
-
[(0,
|
|
16866
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16867
|
+
[(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("z"))]
|
|
16363
16868
|
),
|
|
16364
16869
|
// The same stopping condition, for the walk that ran a call. A call
|
|
16365
16870
|
// gets no `comesTo` answer on purpose: coming back with what a
|
|
16366
16871
|
// factory returned would fight the unwrapping answer.
|
|
16367
|
-
(0,
|
|
16872
|
+
(0, import_datalog3.rule)(
|
|
16368
16873
|
"givesBack",
|
|
16369
|
-
[(0,
|
|
16370
|
-
[(0,
|
|
16874
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16875
|
+
[(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP), (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("z"))]
|
|
16371
16876
|
),
|
|
16372
|
-
(0,
|
|
16877
|
+
(0, import_datalog3.rule)(
|
|
16373
16878
|
"givesBack",
|
|
16374
|
-
[(0,
|
|
16375
|
-
[(0,
|
|
16879
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16880
|
+
[(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("z"))]
|
|
16376
16881
|
),
|
|
16377
16882
|
// Following a value to the expression it is written as, whatever kind
|
|
16378
16883
|
// that is. A GraphQL document is neither a function nor an object, so
|
|
16379
16884
|
// `comesTo` never reaches one.
|
|
16380
|
-
(0,
|
|
16381
|
-
(0,
|
|
16382
|
-
(0,
|
|
16885
|
+
(0, import_datalog3.rule)("isWrittenAs", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("writtenValue", (0, import_datalog3.variable)("x"))]),
|
|
16886
|
+
(0, import_datalog3.rule)("isWrittenAs", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("x"))]),
|
|
16887
|
+
(0, import_datalog3.rule)(
|
|
16888
|
+
"isWrittenAs",
|
|
16889
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16890
|
+
[(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("writtenValue", (0, import_datalog3.variable)("z"))]
|
|
16891
|
+
),
|
|
16892
|
+
(0, import_datalog3.rule)(
|
|
16383
16893
|
"isWrittenAs",
|
|
16384
|
-
[(0,
|
|
16385
|
-
[(0,
|
|
16894
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16895
|
+
[(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("z"))]
|
|
16386
16896
|
),
|
|
16387
|
-
|
|
16897
|
+
// A call is written as whatever its callee's return value is written
|
|
16898
|
+
// as. Unlike `comesTo`, this has nothing to protect: which expression
|
|
16899
|
+
// a value was written as does not compete with unwrapping it.
|
|
16900
|
+
(0, import_datalog3.rule)(
|
|
16388
16901
|
"isWrittenAs",
|
|
16389
|
-
[(0,
|
|
16390
|
-
[
|
|
16902
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16903
|
+
[
|
|
16904
|
+
(0, import_datalog3.lit)("invokes", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("f")),
|
|
16905
|
+
(0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("ret")),
|
|
16906
|
+
(0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("ret"), (0, import_datalog3.variable)("z"))
|
|
16907
|
+
]
|
|
16391
16908
|
),
|
|
16392
16909
|
// What one call site put in a parameter, told apart from what the
|
|
16393
16910
|
// other callers passed.
|
|
16394
|
-
(0,
|
|
16911
|
+
(0, import_datalog3.rule)(
|
|
16395
16912
|
"paramAt",
|
|
16396
|
-
[(0,
|
|
16913
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("z")],
|
|
16397
16914
|
[
|
|
16398
|
-
(0,
|
|
16399
|
-
(0,
|
|
16915
|
+
(0, import_datalog3.lit)("passesArgument", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a")),
|
|
16916
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("a"), (0, import_datalog3.variable)("z"))
|
|
16400
16917
|
]
|
|
16401
16918
|
),
|
|
16402
16919
|
// An argument arriving at the parameter it is passed to, by position
|
|
16403
16920
|
// or by the name the caller wrote, keeping the call it went through
|
|
16404
16921
|
// so `paramAt` can tell two call sites apart.
|
|
16405
|
-
(0,
|
|
16922
|
+
(0, import_datalog3.rule)(
|
|
16406
16923
|
"passesArgument",
|
|
16407
|
-
[(0,
|
|
16924
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a")],
|
|
16408
16925
|
[
|
|
16409
|
-
(0,
|
|
16410
|
-
(0,
|
|
16411
|
-
(0,
|
|
16926
|
+
(0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("p")),
|
|
16927
|
+
(0, import_datalog3.lit)("callsFunction", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")),
|
|
16928
|
+
(0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a"))
|
|
16412
16929
|
]
|
|
16413
16930
|
),
|
|
16414
|
-
(0,
|
|
16931
|
+
(0, import_datalog3.rule)(
|
|
16415
16932
|
"passesArgument",
|
|
16416
|
-
[(0,
|
|
16933
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a")],
|
|
16417
16934
|
[
|
|
16418
|
-
(0,
|
|
16419
|
-
(0,
|
|
16420
|
-
(0,
|
|
16935
|
+
(0, import_datalog3.lit)("paramNamed", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("p")),
|
|
16936
|
+
(0, import_datalog3.lit)("callsFunction", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")),
|
|
16937
|
+
(0, import_datalog3.lit)("callKeywordArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("a"))
|
|
16421
16938
|
]
|
|
16422
16939
|
),
|
|
16423
16940
|
// Which module's export a re-exported name forwards to, however
|
|
16424
16941
|
// many barrels deep the forwarding runs.
|
|
16425
|
-
(0,
|
|
16942
|
+
(0, import_datalog3.rule)(
|
|
16426
16943
|
"moduleForwards",
|
|
16427
|
-
[(0,
|
|
16428
|
-
[(0,
|
|
16944
|
+
[(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")],
|
|
16945
|
+
[(0, import_datalog3.lit)("reExports", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"))]
|
|
16429
16946
|
),
|
|
16430
|
-
(0,
|
|
16947
|
+
(0, import_datalog3.rule)(
|
|
16431
16948
|
"moduleForwards",
|
|
16432
|
-
[(0,
|
|
16949
|
+
[(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m3"), (0, import_datalog3.variable)("n3")],
|
|
16433
16950
|
[
|
|
16434
|
-
(0,
|
|
16435
|
-
(0,
|
|
16951
|
+
(0, import_datalog3.lit)("reExports", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")),
|
|
16952
|
+
(0, import_datalog3.lit)("moduleForwards", (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"), (0, import_datalog3.variable)("m3"), (0, import_datalog3.variable)("n3"))
|
|
16436
16953
|
]
|
|
16437
16954
|
),
|
|
16438
16955
|
// What a module exports: directly, or through re-export chains.
|
|
16439
|
-
(0,
|
|
16956
|
+
(0, import_datalog3.rule)(
|
|
16440
16957
|
"moduleExport",
|
|
16441
|
-
[(0,
|
|
16442
|
-
[(0,
|
|
16958
|
+
[(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
|
|
16959
|
+
[(0, import_datalog3.lit)("exportsAs", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))],
|
|
16443
16960
|
"export"
|
|
16444
16961
|
),
|
|
16445
|
-
(0,
|
|
16962
|
+
(0, import_datalog3.rule)(
|
|
16446
16963
|
"moduleExport",
|
|
16447
|
-
[(0,
|
|
16964
|
+
[(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
|
|
16448
16965
|
[
|
|
16449
|
-
(0,
|
|
16450
|
-
(0,
|
|
16966
|
+
(0, import_datalog3.lit)("reExports", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")),
|
|
16967
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"), (0, import_datalog3.variable)("value"))
|
|
16451
16968
|
],
|
|
16452
16969
|
"re-export"
|
|
16453
16970
|
),
|
|
16454
|
-
(0,
|
|
16971
|
+
(0, import_datalog3.rule)(
|
|
16455
16972
|
"moduleExport",
|
|
16456
|
-
[(0,
|
|
16973
|
+
[(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
|
|
16457
16974
|
[
|
|
16458
|
-
(0,
|
|
16459
|
-
(0,
|
|
16975
|
+
(0, import_datalog3.lit)("reExportsAll", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("m2")),
|
|
16976
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))
|
|
16460
16977
|
],
|
|
16461
16978
|
"re-export all"
|
|
16462
16979
|
),
|
|
16463
16980
|
// The function a call runs, written from the call's side because a
|
|
16464
16981
|
// caller asking what a call gives back has the call in hand.
|
|
16465
|
-
(0,
|
|
16982
|
+
(0, import_datalog3.rule)(
|
|
16466
16983
|
"invokes",
|
|
16467
|
-
[(0,
|
|
16468
|
-
[(0,
|
|
16984
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
|
|
16985
|
+
[(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")), (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))]
|
|
16469
16986
|
),
|
|
16470
16987
|
// The callee is itself a call: `daoBuilder()()`.
|
|
16471
|
-
(0,
|
|
16988
|
+
(0, import_datalog3.rule)(
|
|
16472
16989
|
"invokes",
|
|
16473
|
-
[(0,
|
|
16474
|
-
[(0,
|
|
16990
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
|
|
16991
|
+
[(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")), (0, import_datalog3.lit)("givesBack", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))]
|
|
16475
16992
|
),
|
|
16476
16993
|
// The object an expression refers to: a name through `comesTo`, a
|
|
16477
16994
|
// factory call through what it gives back. Naming the step is what
|
|
16478
16995
|
// makes `routes.list` and `make(body).handle` one rule.
|
|
16479
|
-
(0,
|
|
16996
|
+
(0, import_datalog3.rule)(
|
|
16480
16997
|
"objectOf",
|
|
16481
|
-
[(0,
|
|
16482
|
-
[(0,
|
|
16998
|
+
[(0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("obj")],
|
|
16999
|
+
[(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("obj")), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("obj"))]
|
|
16483
17000
|
),
|
|
16484
|
-
(0,
|
|
17001
|
+
(0, import_datalog3.rule)(
|
|
16485
17002
|
"objectOf",
|
|
16486
|
-
[(0,
|
|
16487
|
-
[(0,
|
|
17003
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("obj")],
|
|
17004
|
+
[(0, import_datalog3.lit)("givesBack", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("obj")), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("obj"))]
|
|
16488
17005
|
),
|
|
16489
17006
|
// Which calls a function, found by the name the call is written as
|
|
16490
17007
|
// rather than by resolving every callee in the project. A caller knows
|
|
16491
17008
|
// the function and wants its call sites, so both of these start from
|
|
16492
17009
|
// the function. Starting from `call` instead asks what every call in
|
|
16493
17010
|
// the project imports, which was 72% of everything derived.
|
|
16494
|
-
(0,
|
|
17011
|
+
(0, import_datalog3.rule)(
|
|
16495
17012
|
"callsFunction",
|
|
16496
|
-
[(0,
|
|
16497
|
-
[(0,
|
|
17013
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
|
|
17014
|
+
[(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))]
|
|
16498
17015
|
),
|
|
16499
|
-
(0,
|
|
17016
|
+
(0, import_datalog3.rule)(
|
|
16500
17017
|
"callsFunction",
|
|
16501
|
-
[(0,
|
|
17018
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
|
|
16502
17019
|
[
|
|
16503
|
-
(0,
|
|
16504
|
-
(0,
|
|
16505
|
-
(0,
|
|
17020
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("f")),
|
|
17021
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
|
|
17022
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))
|
|
16506
17023
|
]
|
|
16507
17024
|
),
|
|
16508
17025
|
// The same, for a language whose adapter writes the import down as a
|
|
16509
17026
|
// declaration and the call's callee as the name referring to it.
|
|
16510
|
-
(0,
|
|
17027
|
+
(0, import_datalog3.rule)(
|
|
16511
17028
|
"callsFunction",
|
|
16512
|
-
[(0,
|
|
17029
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
|
|
16513
17030
|
[
|
|
16514
|
-
(0,
|
|
16515
|
-
(0,
|
|
16516
|
-
(0,
|
|
16517
|
-
(0,
|
|
17031
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("f")),
|
|
17032
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("d"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
|
|
17033
|
+
(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("d")),
|
|
17034
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))
|
|
16518
17035
|
]
|
|
16519
17036
|
),
|
|
16520
17037
|
// What an object contains, its base class included, so a method the base
|
|
@@ -16522,47 +17039,47 @@ var RESOLUTION_RULES = [
|
|
|
16522
17039
|
// declare gives two, and the caller decides. This is its own relation
|
|
16523
17040
|
// rather than more `holdsProperty`, which stays something an adapter
|
|
16524
17041
|
// states and the rules only read.
|
|
16525
|
-
(0,
|
|
17042
|
+
(0, import_datalog3.rule)(
|
|
16526
17043
|
"contains",
|
|
16527
|
-
[(0,
|
|
16528
|
-
[(0,
|
|
17044
|
+
[(0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held")],
|
|
17045
|
+
[(0, import_datalog3.lit)("holdsProperty", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held"))]
|
|
16529
17046
|
),
|
|
16530
|
-
(0,
|
|
17047
|
+
(0, import_datalog3.rule)(
|
|
16531
17048
|
"contains",
|
|
16532
|
-
[(0,
|
|
17049
|
+
[(0, import_datalog3.variable)("cls"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held")],
|
|
16533
17050
|
[
|
|
16534
|
-
(0,
|
|
16535
|
-
(0,
|
|
16536
|
-
(0,
|
|
17051
|
+
(0, import_datalog3.lit)("extends", (0, import_datalog3.variable)("cls"), (0, import_datalog3.variable)("base")),
|
|
17052
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("base"), (0, import_datalog3.variable)("baseCls")),
|
|
17053
|
+
(0, import_datalog3.lit)("contains", (0, import_datalog3.variable)("baseCls"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held"))
|
|
16537
17054
|
]
|
|
16538
17055
|
),
|
|
16539
17056
|
// Which function a factory returns, and which of its parameters that
|
|
16540
17057
|
// function calls: together they are what makes it a wrapper.
|
|
16541
|
-
(0,
|
|
17058
|
+
(0, import_datalog3.rule)(
|
|
16542
17059
|
"returnsFunc",
|
|
16543
|
-
[(0,
|
|
17060
|
+
[(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("g")],
|
|
16544
17061
|
[
|
|
16545
|
-
(0,
|
|
16546
|
-
(0,
|
|
16547
|
-
(0,
|
|
17062
|
+
(0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("value")),
|
|
17063
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("value"), (0, import_datalog3.variable)("g")),
|
|
17064
|
+
(0, import_datalog3.lit)("func", (0, import_datalog3.variable)("g"))
|
|
16548
17065
|
]
|
|
16549
17066
|
),
|
|
16550
17067
|
// A call made by a nested closure counts as made by the function
|
|
16551
17068
|
// that declares it; the closure runs as part of that function.
|
|
16552
|
-
(0,
|
|
16553
|
-
(0,
|
|
17069
|
+
(0, import_datalog3.rule)("bodyCallsDeep", [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")], [(0, import_datalog3.lit)("bodyCalls", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c"))]),
|
|
17070
|
+
(0, import_datalog3.rule)(
|
|
16554
17071
|
"bodyCallsDeep",
|
|
16555
|
-
[(0,
|
|
16556
|
-
[(0,
|
|
17072
|
+
[(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")],
|
|
17073
|
+
[(0, import_datalog3.lit)("containsFn", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("g")), (0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("c"))]
|
|
16557
17074
|
),
|
|
16558
|
-
(0,
|
|
17075
|
+
(0, import_datalog3.rule)(
|
|
16559
17076
|
"unwraps",
|
|
16560
|
-
[(0,
|
|
17077
|
+
[(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")],
|
|
16561
17078
|
[
|
|
16562
|
-
(0,
|
|
16563
|
-
(0,
|
|
16564
|
-
(0,
|
|
16565
|
-
(0,
|
|
17079
|
+
(0, import_datalog3.lit)("returnsFunc", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("g")),
|
|
17080
|
+
(0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("c")),
|
|
17081
|
+
(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("p")),
|
|
17082
|
+
(0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("p"))
|
|
16566
17083
|
]
|
|
16567
17084
|
),
|
|
16568
17085
|
// Argument flow: which parameter a value traces back to. Directly
|
|
@@ -16570,289 +17087,289 @@ var RESOLUTION_RULES = [
|
|
|
16570
17087
|
// another unwrapping factory. This is what lets
|
|
16571
17088
|
// `createProtected(h) { return service.withAuth(h); }` unwrap:
|
|
16572
17089
|
// the returned call passes h through withAuth, which unwraps.
|
|
16573
|
-
(0,
|
|
17090
|
+
(0, import_datalog3.rule)(
|
|
16574
17091
|
"flowsToParam",
|
|
16575
|
-
[(0,
|
|
17092
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("p")],
|
|
16576
17093
|
[
|
|
16577
|
-
(0,
|
|
16578
|
-
(0,
|
|
17094
|
+
(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("p")),
|
|
17095
|
+
(0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("anyF"), (0, import_datalog3.variable)("anyK"), (0, import_datalog3.variable)("p"))
|
|
16579
17096
|
]
|
|
16580
17097
|
),
|
|
16581
|
-
(0,
|
|
17098
|
+
(0, import_datalog3.rule)(
|
|
16582
17099
|
"flowsToParam",
|
|
16583
|
-
[(0,
|
|
17100
|
+
[(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p")],
|
|
16584
17101
|
[
|
|
16585
|
-
(0,
|
|
16586
|
-
(0,
|
|
16587
|
-
(0,
|
|
16588
|
-
(0,
|
|
16589
|
-
(0,
|
|
17102
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
|
|
17103
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f")),
|
|
17104
|
+
(0, import_datalog3.lit)("unwraps", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")),
|
|
17105
|
+
(0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a")),
|
|
17106
|
+
(0, import_datalog3.lit)("flowsToParam", (0, import_datalog3.variable)("a"), (0, import_datalog3.variable)("p"))
|
|
16590
17107
|
]
|
|
16591
17108
|
),
|
|
16592
|
-
(0,
|
|
17109
|
+
(0, import_datalog3.rule)(
|
|
16593
17110
|
"unwraps",
|
|
16594
|
-
[(0,
|
|
17111
|
+
[(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")],
|
|
16595
17112
|
[
|
|
16596
|
-
(0,
|
|
16597
|
-
(0,
|
|
16598
|
-
(0,
|
|
17113
|
+
(0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("value")),
|
|
17114
|
+
(0, import_datalog3.lit)("flowsToParam", (0, import_datalog3.variable)("value"), (0, import_datalog3.variable)("p")),
|
|
17115
|
+
(0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("p"))
|
|
16599
17116
|
]
|
|
16600
17117
|
),
|
|
16601
17118
|
// Where a name comes from, when what it refers to lives outside the
|
|
16602
17119
|
// source being read. A walk ends at something written out in source,
|
|
16603
17120
|
// so it never reaches a library's own function.
|
|
16604
|
-
(0,
|
|
17121
|
+
(0, import_datalog3.rule)(
|
|
16605
17122
|
"comesFrom",
|
|
16606
|
-
[(0,
|
|
16607
|
-
[(0,
|
|
17123
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
17124
|
+
[(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))]
|
|
16608
17125
|
),
|
|
16609
|
-
(0,
|
|
17126
|
+
(0, import_datalog3.rule)(
|
|
16610
17127
|
"comesFrom",
|
|
16611
|
-
[(0,
|
|
17128
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16612
17129
|
[
|
|
16613
|
-
(0,
|
|
16614
|
-
(0,
|
|
17130
|
+
(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP),
|
|
17131
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16615
17132
|
]
|
|
16616
17133
|
),
|
|
16617
17134
|
// Calling f ends up calling the name n that module m exports, one
|
|
16618
17135
|
// hop through f's own body or deeper through a wrapper of a wrapper.
|
|
16619
17136
|
// Several results is normal: a composed decorator applies them all.
|
|
16620
|
-
(0,
|
|
17137
|
+
(0, import_datalog3.rule)(
|
|
16621
17138
|
"callsInto",
|
|
16622
|
-
[(0,
|
|
17139
|
+
[(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16623
17140
|
[
|
|
16624
|
-
(0,
|
|
16625
|
-
(0,
|
|
17141
|
+
(0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")),
|
|
17142
|
+
(0, import_datalog3.lit)("comesFrom", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16626
17143
|
]
|
|
16627
17144
|
),
|
|
16628
|
-
(0,
|
|
17145
|
+
(0, import_datalog3.rule)(
|
|
16629
17146
|
"callsInto",
|
|
16630
|
-
[(0,
|
|
17147
|
+
[(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16631
17148
|
[
|
|
16632
|
-
(0,
|
|
16633
|
-
(0,
|
|
16634
|
-
(0,
|
|
17149
|
+
(0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")),
|
|
17150
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("g")),
|
|
17151
|
+
(0, import_datalog3.lit)("callsInto", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16635
17152
|
]
|
|
16636
17153
|
),
|
|
16637
17154
|
// The question callers ask: what a value comes to, narrowed to the
|
|
16638
17155
|
// functions. Objects appear in the middle of chains and never in an
|
|
16639
17156
|
// answer.
|
|
16640
|
-
(0,
|
|
17157
|
+
(0, import_datalog3.rule)(
|
|
16641
17158
|
"resolves",
|
|
16642
|
-
[(0,
|
|
16643
|
-
[(0,
|
|
17159
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17160
|
+
[(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")), (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("z"))]
|
|
16644
17161
|
)
|
|
16645
17162
|
];
|
|
16646
17163
|
var RESOLUTION_QUESTIONS = [
|
|
16647
|
-
(0,
|
|
17164
|
+
(0, import_datalog3.rule)(
|
|
16648
17165
|
"wantedResolves",
|
|
16649
|
-
[(0,
|
|
16650
|
-
[(0,
|
|
17166
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17167
|
+
[(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("resolves", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
|
|
16651
17168
|
),
|
|
16652
|
-
(0,
|
|
17169
|
+
(0, import_datalog3.rule)(
|
|
16653
17170
|
"wantedComesTo",
|
|
16654
|
-
[(0,
|
|
16655
|
-
[(0,
|
|
17171
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17172
|
+
[(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
|
|
16656
17173
|
),
|
|
16657
|
-
(0,
|
|
17174
|
+
(0, import_datalog3.rule)(
|
|
16658
17175
|
"wantedComesTo",
|
|
16659
|
-
[(0,
|
|
16660
|
-
[(0,
|
|
17176
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17177
|
+
[(0, import_datalog3.lit)("wantedOrigin", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
|
|
16661
17178
|
),
|
|
16662
|
-
(0,
|
|
17179
|
+
(0, import_datalog3.rule)(
|
|
16663
17180
|
"wantedIsWrittenAs",
|
|
16664
|
-
[(0,
|
|
16665
|
-
[(0,
|
|
17181
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17182
|
+
[(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
|
|
16666
17183
|
),
|
|
16667
17184
|
// A call is given no `comesTo`, so this is the only way to ask what
|
|
16668
17185
|
// object one arrives at, and without it a demand-driven run derives
|
|
16669
17186
|
// `objectOf` nowhere.
|
|
16670
|
-
(0,
|
|
17187
|
+
(0, import_datalog3.rule)(
|
|
16671
17188
|
"wantedObjectOf",
|
|
16672
|
-
[(0,
|
|
16673
|
-
[(0,
|
|
17189
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17190
|
+
[(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("objectOf", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
|
|
16674
17191
|
),
|
|
16675
17192
|
// Keyed by the parameter, since that is what a caller has in hand
|
|
16676
17193
|
// when it wants the call sites told apart.
|
|
16677
|
-
(0,
|
|
17194
|
+
(0, import_datalog3.rule)(
|
|
16678
17195
|
"wantedParamAt",
|
|
16679
|
-
[(0,
|
|
16680
|
-
[(0,
|
|
17196
|
+
[(0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("z")],
|
|
17197
|
+
[(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("p")), (0, import_datalog3.lit)("paramAt", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("z"))]
|
|
16681
17198
|
),
|
|
16682
|
-
(0,
|
|
17199
|
+
(0, import_datalog3.rule)(
|
|
16683
17200
|
"wantedComesFrom",
|
|
16684
|
-
[(0,
|
|
16685
|
-
[(0,
|
|
17201
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
17202
|
+
[(0, import_datalog3.lit)("wantedOrigin", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("comesFrom", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))]
|
|
16686
17203
|
),
|
|
16687
17204
|
// Call-origin questions, for attribution, in their own demand
|
|
16688
17205
|
// class. Attribution stops at the import declaration, so nothing
|
|
16689
17206
|
// here demands reaches or callsInto. The chains are closures with
|
|
16690
17207
|
// no depth bound: an alias run of any length resolves.
|
|
16691
|
-
(0,
|
|
17208
|
+
(0, import_datalog3.rule)("callOriginChain", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("wantedCallOrigin", (0, import_datalog3.variable)("x"))]),
|
|
16692
17209
|
// The chain with at least one hop taken, which is what tells a
|
|
16693
17210
|
// destructured binding apart from asking about a node directly. The
|
|
16694
17211
|
// whole chain is the seed plus those hops.
|
|
16695
|
-
(0,
|
|
17212
|
+
(0, import_datalog3.rule)(
|
|
16696
17213
|
"callOriginChain",
|
|
16697
|
-
[(0,
|
|
16698
|
-
[(0,
|
|
17214
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17215
|
+
[(0, import_datalog3.lit)("callOriginChainStepped", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
|
|
16699
17216
|
),
|
|
16700
|
-
(0,
|
|
17217
|
+
(0, import_datalog3.rule)(
|
|
16701
17218
|
"callOriginChainStepped",
|
|
16702
|
-
[(0,
|
|
16703
|
-
[(0,
|
|
17219
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17220
|
+
[(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
|
|
16704
17221
|
),
|
|
16705
|
-
(0,
|
|
17222
|
+
(0, import_datalog3.rule)(
|
|
16706
17223
|
"callOriginChainStepped",
|
|
16707
|
-
[(0,
|
|
17224
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16708
17225
|
[
|
|
16709
|
-
(0,
|
|
16710
|
-
(0,
|
|
17226
|
+
(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17227
|
+
(0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
|
|
16711
17228
|
]
|
|
16712
17229
|
),
|
|
16713
|
-
(0,
|
|
17230
|
+
(0, import_datalog3.rule)(
|
|
16714
17231
|
"callOriginChainStepped",
|
|
16715
|
-
[(0,
|
|
17232
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16716
17233
|
[
|
|
16717
|
-
(0,
|
|
16718
|
-
(0,
|
|
17234
|
+
(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17235
|
+
(0, import_datalog3.lit)("fallbackBranch", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
|
|
16719
17236
|
]
|
|
16720
17237
|
),
|
|
16721
|
-
(0,
|
|
17238
|
+
(0, import_datalog3.rule)(
|
|
16722
17239
|
"wantedCallOriginPair",
|
|
16723
|
-
[(0,
|
|
17240
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16724
17241
|
[
|
|
16725
|
-
(0,
|
|
16726
|
-
(0,
|
|
17242
|
+
(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17243
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16727
17244
|
]
|
|
16728
17245
|
),
|
|
16729
17246
|
// An import of a project barrel is an import of what the barrel
|
|
16730
17247
|
// forwards, so the pair surfaces the module behind the chain.
|
|
16731
|
-
(0,
|
|
17248
|
+
(0, import_datalog3.rule)(
|
|
16732
17249
|
"wantedCallOriginPair",
|
|
16733
|
-
[(0,
|
|
17250
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")],
|
|
16734
17251
|
[
|
|
16735
|
-
(0,
|
|
16736
|
-
(0,
|
|
16737
|
-
(0,
|
|
17252
|
+
(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17253
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
|
|
17254
|
+
(0, import_datalog3.lit)("moduleForwards", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"))
|
|
16738
17255
|
]
|
|
16739
17256
|
),
|
|
16740
17257
|
// A namespace member: the module's export of the member's own name.
|
|
16741
17258
|
// The imports literal leads, since it is the smallest relation.
|
|
16742
|
-
(0,
|
|
17259
|
+
(0, import_datalog3.rule)(
|
|
16743
17260
|
"wantedCallOriginPair",
|
|
16744
|
-
[(0,
|
|
17261
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("p")],
|
|
16745
17262
|
[
|
|
16746
|
-
(0,
|
|
16747
|
-
(0,
|
|
16748
|
-
(0,
|
|
17263
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("m"), NAMESPACE_IMPORT),
|
|
17264
|
+
(0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("p")),
|
|
17265
|
+
(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))
|
|
16749
17266
|
]
|
|
16750
17267
|
),
|
|
16751
|
-
(0,
|
|
17268
|
+
(0, import_datalog3.rule)(
|
|
16752
17269
|
"wantedCallOriginPair",
|
|
16753
|
-
[(0,
|
|
17270
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("p")],
|
|
16754
17271
|
[
|
|
16755
|
-
(0,
|
|
16756
|
-
(0,
|
|
16757
|
-
(0,
|
|
16758
|
-
(0,
|
|
17272
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("d"), (0, import_datalog3.variable)("m"), NAMESPACE_IMPORT),
|
|
17273
|
+
(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("d")),
|
|
17274
|
+
(0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("p")),
|
|
17275
|
+
(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))
|
|
16759
17276
|
]
|
|
16760
17277
|
),
|
|
16761
17278
|
// What a call made: through the call to its callee, whose own alias
|
|
16762
17279
|
// chain leads to the import.
|
|
16763
|
-
(0,
|
|
17280
|
+
(0, import_datalog3.rule)(
|
|
16764
17281
|
"callMadeChain",
|
|
16765
|
-
[(0,
|
|
16766
|
-
[(0,
|
|
17282
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("f")],
|
|
17283
|
+
[(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("c")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))]
|
|
16767
17284
|
),
|
|
16768
|
-
(0,
|
|
17285
|
+
(0, import_datalog3.rule)(
|
|
16769
17286
|
"callMadeChain",
|
|
16770
|
-
[(0,
|
|
16771
|
-
[(0,
|
|
17287
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17288
|
+
[(0, import_datalog3.lit)("callMadeChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
|
|
16772
17289
|
),
|
|
16773
|
-
(0,
|
|
17290
|
+
(0, import_datalog3.rule)(
|
|
16774
17291
|
"callMadeChain",
|
|
16775
|
-
[(0,
|
|
16776
|
-
[(0,
|
|
17292
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17293
|
+
[(0, import_datalog3.lit)("callMadeChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
|
|
16777
17294
|
),
|
|
16778
|
-
(0,
|
|
17295
|
+
(0, import_datalog3.rule)(
|
|
16779
17296
|
"wantedCallOriginPair",
|
|
16780
|
-
[(0,
|
|
17297
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16781
17298
|
[
|
|
16782
|
-
(0,
|
|
16783
|
-
(0,
|
|
17299
|
+
(0, import_datalog3.lit)("callMadeChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17300
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16784
17301
|
]
|
|
16785
17302
|
),
|
|
16786
17303
|
// A member destructured off what a call made keeps the member name
|
|
16787
17304
|
// as one more path segment.
|
|
16788
|
-
(0,
|
|
17305
|
+
(0, import_datalog3.rule)(
|
|
16789
17306
|
"callMemberChain",
|
|
16790
|
-
[(0,
|
|
17307
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("p")],
|
|
16791
17308
|
[
|
|
16792
|
-
(0,
|
|
16793
|
-
(0,
|
|
16794
|
-
(0,
|
|
17309
|
+
(0, import_datalog3.lit)("callOriginChainStepped", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("e")),
|
|
17310
|
+
(0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("e"), (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("p")),
|
|
17311
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))
|
|
16795
17312
|
]
|
|
16796
17313
|
),
|
|
16797
|
-
(0,
|
|
17314
|
+
(0, import_datalog3.rule)(
|
|
16798
17315
|
"callMemberChain",
|
|
16799
|
-
[(0,
|
|
17316
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("p")],
|
|
16800
17317
|
[
|
|
16801
|
-
(0,
|
|
16802
|
-
(0,
|
|
17318
|
+
(0, import_datalog3.lit)("callMemberChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("p")),
|
|
17319
|
+
(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
|
|
16803
17320
|
]
|
|
16804
17321
|
),
|
|
16805
|
-
(0,
|
|
17322
|
+
(0, import_datalog3.rule)(
|
|
16806
17323
|
"callMemberChain",
|
|
16807
|
-
[(0,
|
|
17324
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("p")],
|
|
16808
17325
|
[
|
|
16809
|
-
(0,
|
|
16810
|
-
(0,
|
|
17326
|
+
(0, import_datalog3.lit)("callMemberChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("p")),
|
|
17327
|
+
(0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
|
|
16811
17328
|
]
|
|
16812
17329
|
),
|
|
16813
|
-
(0,
|
|
17330
|
+
(0, import_datalog3.rule)(
|
|
16814
17331
|
"wantedCallOriginMember",
|
|
16815
|
-
[(0,
|
|
17332
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("p")],
|
|
16816
17333
|
[
|
|
16817
|
-
(0,
|
|
16818
|
-
(0,
|
|
17334
|
+
(0, import_datalog3.lit)("callMemberChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("p")),
|
|
17335
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16819
17336
|
]
|
|
16820
17337
|
),
|
|
16821
|
-
(0,
|
|
17338
|
+
(0, import_datalog3.rule)(
|
|
16822
17339
|
"wantedCallsInto",
|
|
16823
|
-
[(0,
|
|
17340
|
+
[(0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16824
17341
|
[
|
|
16825
|
-
(0,
|
|
16826
|
-
(0,
|
|
16827
|
-
(0,
|
|
17342
|
+
(0, import_datalog3.lit)("wantedOrigin", (0, import_datalog3.variable)("x")),
|
|
17343
|
+
(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("g")),
|
|
17344
|
+
(0, import_datalog3.lit)("callsInto", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16828
17345
|
]
|
|
16829
17346
|
),
|
|
16830
17347
|
// Subject identity, its own demand class; the README's subject
|
|
16831
17348
|
// section says why the origin pair rides on the written-value walk.
|
|
16832
|
-
(0,
|
|
17349
|
+
(0, import_datalog3.rule)(
|
|
16833
17350
|
"wantedSubjectWritten",
|
|
16834
|
-
[(0,
|
|
16835
|
-
[(0,
|
|
17351
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")],
|
|
17352
|
+
[(0, import_datalog3.lit)("wantedSubject", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r"))]
|
|
16836
17353
|
),
|
|
16837
|
-
(0,
|
|
17354
|
+
(0, import_datalog3.rule)(
|
|
16838
17355
|
"wantedSubjectConstruction",
|
|
16839
|
-
[(0,
|
|
17356
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
|
|
16840
17357
|
[
|
|
16841
|
-
(0,
|
|
16842
|
-
(0,
|
|
16843
|
-
(0,
|
|
16844
|
-
(0,
|
|
17358
|
+
(0, import_datalog3.lit)("wantedSubject", (0, import_datalog3.variable)("x")),
|
|
17359
|
+
(0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")),
|
|
17360
|
+
(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
|
|
17361
|
+
(0, import_datalog3.lit)("comesFrom", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
|
|
16845
17362
|
]
|
|
16846
17363
|
),
|
|
16847
17364
|
// The export table of one module, keyed by the module asked about.
|
|
16848
17365
|
// The moduleExport rules flatten re-export chains of any length, and
|
|
16849
17366
|
// the demand cone stays on export facts alone.
|
|
16850
|
-
(0,
|
|
17367
|
+
(0, import_datalog3.rule)(
|
|
16851
17368
|
"wantedModuleExport",
|
|
16852
|
-
[(0,
|
|
17369
|
+
[(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
|
|
16853
17370
|
[
|
|
16854
|
-
(0,
|
|
16855
|
-
(0,
|
|
17371
|
+
(0, import_datalog3.lit)("wantedExportsOf", (0, import_datalog3.variable)("m")),
|
|
17372
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))
|
|
16856
17373
|
]
|
|
16857
17374
|
),
|
|
16858
17375
|
// The calls behind a receiver, for a pack that wants the anchor a
|
|
@@ -16861,63 +17378,63 @@ var RESOLUTION_QUESTIONS = [
|
|
|
16861
17378
|
// A class's ancestry, one hop per extends through the binding that
|
|
16862
17379
|
// says which class the written base is; the names on the way out
|
|
16863
17380
|
// are what a storage pack matches its library's bases against.
|
|
16864
|
-
(0,
|
|
16865
|
-
(0,
|
|
17381
|
+
(0, import_datalog3.rule)("ancestryChain", [(0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("c")], [(0, import_datalog3.lit)("wantedAncestry", (0, import_datalog3.variable)("c"))]),
|
|
17382
|
+
(0, import_datalog3.rule)(
|
|
16866
17383
|
"ancestryChain",
|
|
16867
|
-
[(0,
|
|
17384
|
+
[(0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("b2")],
|
|
16868
17385
|
[
|
|
16869
|
-
(0,
|
|
16870
|
-
(0,
|
|
16871
|
-
(0,
|
|
17386
|
+
(0, import_datalog3.lit)("ancestryChain", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("b")),
|
|
17387
|
+
(0, import_datalog3.lit)("extends", (0, import_datalog3.variable)("b"), (0, import_datalog3.variable)("x")),
|
|
17388
|
+
(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("b2"))
|
|
16872
17389
|
]
|
|
16873
17390
|
),
|
|
16874
|
-
(0,
|
|
17391
|
+
(0, import_datalog3.rule)(
|
|
16875
17392
|
"wantedBaseName",
|
|
16876
|
-
[(0,
|
|
16877
|
-
[(0,
|
|
17393
|
+
[(0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("n")],
|
|
17394
|
+
[(0, import_datalog3.lit)("ancestryChain", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("b")), (0, import_datalog3.lit)("extendsNamed", (0, import_datalog3.variable)("b"), (0, import_datalog3.variable)("n"))]
|
|
16878
17395
|
),
|
|
16879
|
-
(0,
|
|
16880
|
-
(0,
|
|
17396
|
+
(0, import_datalog3.rule)("anchorChain", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("wantedAnchor", (0, import_datalog3.variable)("x"))]),
|
|
17397
|
+
(0, import_datalog3.rule)(
|
|
16881
17398
|
"anchorChain",
|
|
16882
|
-
[(0,
|
|
16883
|
-
[(0,
|
|
17399
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17400
|
+
[(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
|
|
16884
17401
|
),
|
|
16885
|
-
(0,
|
|
17402
|
+
(0, import_datalog3.rule)(
|
|
16886
17403
|
"anchorChain",
|
|
16887
|
-
[(0,
|
|
16888
|
-
[(0,
|
|
17404
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17405
|
+
[(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
|
|
16889
17406
|
),
|
|
16890
|
-
(0,
|
|
17407
|
+
(0, import_datalog3.rule)(
|
|
16891
17408
|
"anchorChain",
|
|
16892
|
-
[(0,
|
|
16893
|
-
[(0,
|
|
17409
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
17410
|
+
[(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("fallbackBranch", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
|
|
16894
17411
|
),
|
|
16895
|
-
(0,
|
|
17412
|
+
(0, import_datalog3.rule)(
|
|
16896
17413
|
"anchorChain",
|
|
16897
|
-
[(0,
|
|
17414
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
|
|
16898
17415
|
[
|
|
16899
|
-
(0,
|
|
16900
|
-
(0,
|
|
16901
|
-
(0,
|
|
17416
|
+
(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17417
|
+
(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
|
|
17418
|
+
(0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("z"))
|
|
16902
17419
|
]
|
|
16903
17420
|
),
|
|
16904
|
-
(0,
|
|
17421
|
+
(0, import_datalog3.rule)(
|
|
16905
17422
|
"anchorChain",
|
|
16906
|
-
[(0,
|
|
16907
|
-
[(0,
|
|
17423
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("c")],
|
|
17424
|
+
[(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))]
|
|
16908
17425
|
),
|
|
16909
|
-
(0,
|
|
17426
|
+
(0, import_datalog3.rule)(
|
|
16910
17427
|
"anchorChain",
|
|
16911
|
-
[(0,
|
|
17428
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("o")],
|
|
16912
17429
|
[
|
|
16913
|
-
(0,
|
|
16914
|
-
(0,
|
|
17430
|
+
(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
|
|
17431
|
+
(0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n"))
|
|
16915
17432
|
]
|
|
16916
17433
|
),
|
|
16917
|
-
(0,
|
|
17434
|
+
(0, import_datalog3.rule)(
|
|
16918
17435
|
"wantedAnchorCall",
|
|
16919
|
-
[(0,
|
|
16920
|
-
[(0,
|
|
17436
|
+
[(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")],
|
|
17437
|
+
[(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))]
|
|
16921
17438
|
)
|
|
16922
17439
|
];
|
|
16923
17440
|
var ANSWER_RELATIONS = [
|
|
@@ -16944,17 +17461,17 @@ function rootConstant(node) {
|
|
|
16944
17461
|
}
|
|
16945
17462
|
return rootConstant(receiver);
|
|
16946
17463
|
}
|
|
16947
|
-
var ANCESTRY_PROGRAM = (0,
|
|
17464
|
+
var ANCESTRY_PROGRAM = (0, import_datalog4.deriveOnDemand)(
|
|
16948
17465
|
[...RESOLUTION_RULES, ...RESOLUTION_QUESTIONS],
|
|
16949
17466
|
ANSWER_RELATIONS
|
|
16950
17467
|
);
|
|
16951
17468
|
function reachesBase(facts, classKey, bases) {
|
|
16952
17469
|
facts.add("wantedAncestry", [classKey]);
|
|
16953
|
-
(0,
|
|
17470
|
+
(0, import_datalog4.evaluate)(facts, ANCESTRY_PROGRAM.rules);
|
|
16954
17471
|
return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
|
|
16955
17472
|
}
|
|
16956
|
-
function classBehind(facts,
|
|
16957
|
-
const wanted = `#${
|
|
17473
|
+
function classBehind(facts, constant3) {
|
|
17474
|
+
const wanted = `#${constant3.text}`;
|
|
16958
17475
|
const bound = new Set(
|
|
16959
17476
|
facts.facts("binds").filter((row) => String(row[0]).endsWith(wanted)).map((row) => String(row[1]))
|
|
16960
17477
|
);
|
|
@@ -16997,11 +17514,11 @@ function storageEffects(calls, options) {
|
|
|
16997
17514
|
if (partOfOne.has(call.id)) {
|
|
16998
17515
|
continue;
|
|
16999
17516
|
}
|
|
17000
|
-
const
|
|
17001
|
-
if (
|
|
17517
|
+
const constant3 = rootConstant(call);
|
|
17518
|
+
if (constant3 === null) {
|
|
17002
17519
|
continue;
|
|
17003
17520
|
}
|
|
17004
|
-
const classKey = classBehind(options.facts,
|
|
17521
|
+
const classKey = classBehind(options.facts, constant3);
|
|
17005
17522
|
if (classKey === void 0) {
|
|
17006
17523
|
continue;
|
|
17007
17524
|
}
|
|
@@ -17019,7 +17536,7 @@ function storageEffects(calls, options) {
|
|
|
17019
17536
|
recognition: "ruby-storage",
|
|
17020
17537
|
storageSystem: pattern.storageSystem,
|
|
17021
17538
|
scope: pattern.baseClasses[0] ?? "",
|
|
17022
|
-
container:
|
|
17539
|
+
container: constant3.text
|
|
17023
17540
|
}),
|
|
17024
17541
|
callee: call.text,
|
|
17025
17542
|
interaction: {
|
|
@@ -17135,7 +17652,8 @@ async function discoverUnits(root, options) {
|
|
|
17135
17652
|
const knownClasses = new Set(classes.map((info) => info.qualifiedName));
|
|
17136
17653
|
const fileBlocks = classes.map((info) => ({
|
|
17137
17654
|
info,
|
|
17138
|
-
knownClasses
|
|
17655
|
+
knownClasses,
|
|
17656
|
+
file: options.absoluteFile ?? options.filePath
|
|
17139
17657
|
}));
|
|
17140
17658
|
const units = [];
|
|
17141
17659
|
for (const info of classes) {
|
|
@@ -17159,16 +17677,17 @@ async function discoverUnits(root, options) {
|
|
|
17159
17677
|
}
|
|
17160
17678
|
return units;
|
|
17161
17679
|
}
|
|
17162
|
-
function reachesConfiguredBase(ancestry, self,
|
|
17680
|
+
function reachesConfiguredBase(ancestry, self, baseClassNames) {
|
|
17163
17681
|
return ancestry.some(
|
|
17164
|
-
(entry) => entry.name !== self &&
|
|
17682
|
+
(entry) => entry.name !== self && baseClassNames.includes(entry.name)
|
|
17165
17683
|
);
|
|
17166
17684
|
}
|
|
17167
17685
|
function unitsFor(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
17168
17686
|
const table = {
|
|
17169
|
-
graphqlObjectFields: (p) => graphqlObjectFieldUnits(p, pack, info, ownBlocks, fileBlocks, options)
|
|
17687
|
+
graphqlObjectFields: (p) => graphqlObjectFieldUnits(p, pack, info, ownBlocks, fileBlocks, options),
|
|
17688
|
+
controllerActions: (p) => controllerActionUnits(p, pack, info, ownBlocks, fileBlocks, options)
|
|
17170
17689
|
};
|
|
17171
|
-
return (0,
|
|
17690
|
+
return (0, import_behavioral_ir2.dispatchByType)(table, pattern);
|
|
17172
17691
|
}
|
|
17173
17692
|
async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
17174
17693
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
@@ -17181,7 +17700,7 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
17181
17700
|
options.storage
|
|
17182
17701
|
);
|
|
17183
17702
|
const ancestry = await ancestryOf(info.qualifiedName, ownBlocks, ctx.lookup);
|
|
17184
|
-
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern)) {
|
|
17703
|
+
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
17185
17704
|
return [];
|
|
17186
17705
|
}
|
|
17187
17706
|
const typeName = graphqlTypeNameFromQualified(
|
|
@@ -17200,9 +17719,145 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
17200
17719
|
declsByName.set(decl.fieldName, decl);
|
|
17201
17720
|
}
|
|
17202
17721
|
}
|
|
17203
|
-
return [...declsByName.values()].map(
|
|
17204
|
-
|
|
17205
|
-
|
|
17722
|
+
return [...declsByName.values()].map((decl) => {
|
|
17723
|
+
const raw = buildFieldUnit(pack, typeName, decl, options.filePath);
|
|
17724
|
+
if (decl.body.reachSeed !== void 0) {
|
|
17725
|
+
options.onReachSeed?.(raw, decl.body.reachSeed);
|
|
17726
|
+
}
|
|
17727
|
+
return raw;
|
|
17728
|
+
});
|
|
17729
|
+
}
|
|
17730
|
+
async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
17731
|
+
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
17732
|
+
return [];
|
|
17733
|
+
}
|
|
17734
|
+
const lookup = {
|
|
17735
|
+
root: pattern.root,
|
|
17736
|
+
pathConvention: pattern.pathConvention,
|
|
17737
|
+
ancestryRootClassNames: pattern.ancestryRootClassNames,
|
|
17738
|
+
parsedFile: (absPath) => options.cache.get(absPath),
|
|
17739
|
+
localDefinition: (name) => sameFileBlocks(name, fileBlocks)
|
|
17740
|
+
};
|
|
17741
|
+
const ancestry = await ancestryOf(info.qualifiedName, ownBlocks, lookup);
|
|
17742
|
+
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
17743
|
+
return [];
|
|
17744
|
+
}
|
|
17745
|
+
const units = [];
|
|
17746
|
+
for (const block of ownBlocks) {
|
|
17747
|
+
if (block.info.bodyNode === null) {
|
|
17748
|
+
continue;
|
|
17749
|
+
}
|
|
17750
|
+
const visibility = instanceMethodVisibility(block.info.bodyNode);
|
|
17751
|
+
for (const [actionName, method] of instanceMethodsByName(
|
|
17752
|
+
block.info.bodyNode
|
|
17753
|
+
)) {
|
|
17754
|
+
if ((visibility.get(actionName) ?? "public") !== "public") {
|
|
17755
|
+
continue;
|
|
17756
|
+
}
|
|
17757
|
+
const raw = buildControllerActionUnit(
|
|
17758
|
+
pack,
|
|
17759
|
+
pattern,
|
|
17760
|
+
info.qualifiedName,
|
|
17761
|
+
actionName,
|
|
17762
|
+
method,
|
|
17763
|
+
options.filePath,
|
|
17764
|
+
options.storage
|
|
17765
|
+
);
|
|
17766
|
+
units.push(raw);
|
|
17767
|
+
options.onReachSeed?.(raw, {
|
|
17768
|
+
file: block.file,
|
|
17769
|
+
node: method,
|
|
17770
|
+
enclosingQualifiedName: info.qualifiedName
|
|
17771
|
+
});
|
|
17772
|
+
}
|
|
17773
|
+
}
|
|
17774
|
+
return units;
|
|
17775
|
+
}
|
|
17776
|
+
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, method, filePath, storage) {
|
|
17777
|
+
const range = rangeOf(method);
|
|
17778
|
+
const route = pattern.routeFor(controllerQualifiedName, actionName);
|
|
17779
|
+
const body = bodyOfMethod(method, storage);
|
|
17780
|
+
return {
|
|
17781
|
+
identity: {
|
|
17782
|
+
name: actionName,
|
|
17783
|
+
nameKind: "binding",
|
|
17784
|
+
kind: "handler",
|
|
17785
|
+
file: filePath,
|
|
17786
|
+
range,
|
|
17787
|
+
span: spanOf(method),
|
|
17788
|
+
exportName: actionName,
|
|
17789
|
+
exportPath: [controllerQualifiedName, actionName]
|
|
17790
|
+
},
|
|
17791
|
+
boundaryBinding: route === null ? null : (0, import_behavioral_ir2.restBinding)({
|
|
17792
|
+
transport: pack.protocol,
|
|
17793
|
+
method: route.method,
|
|
17794
|
+
path: route.path,
|
|
17795
|
+
recognition: pack.name
|
|
17796
|
+
}),
|
|
17797
|
+
parameters: [],
|
|
17798
|
+
branches: [
|
|
17799
|
+
{
|
|
17800
|
+
conditions: [],
|
|
17801
|
+
terminal: {
|
|
17802
|
+
kind: "response",
|
|
17803
|
+
statusCode: { type: "literal", value: pattern.defaultStatusCode },
|
|
17804
|
+
body: null,
|
|
17805
|
+
exceptionType: null,
|
|
17806
|
+
message: null,
|
|
17807
|
+
component: null,
|
|
17808
|
+
renderTree: null,
|
|
17809
|
+
delegateTarget: null,
|
|
17810
|
+
emitEvent: null,
|
|
17811
|
+
location: range
|
|
17812
|
+
},
|
|
17813
|
+
effects: body.effects ?? [],
|
|
17814
|
+
...body.extraEffects === void 0 ? {} : { extraEffects: body.extraEffects },
|
|
17815
|
+
location: range,
|
|
17816
|
+
isDefault: true
|
|
17817
|
+
}
|
|
17818
|
+
],
|
|
17819
|
+
bodyContent: body.bodyContent ?? "absent",
|
|
17820
|
+
dependencyCalls: [],
|
|
17821
|
+
declaredContract: null
|
|
17822
|
+
};
|
|
17823
|
+
}
|
|
17824
|
+
function routingGapUnit(pattern, gaps) {
|
|
17825
|
+
const range = { start: 1, end: 1 };
|
|
17826
|
+
return {
|
|
17827
|
+
identity: {
|
|
17828
|
+
name: "routes",
|
|
17829
|
+
kind: "module-init",
|
|
17830
|
+
file: pattern.routesFile,
|
|
17831
|
+
range,
|
|
17832
|
+
exportName: null,
|
|
17833
|
+
exportPath: null
|
|
17834
|
+
},
|
|
17835
|
+
boundaryBinding: null,
|
|
17836
|
+
parameters: [],
|
|
17837
|
+
branches: [
|
|
17838
|
+
{
|
|
17839
|
+
conditions: [],
|
|
17840
|
+
terminal: {
|
|
17841
|
+
kind: "void",
|
|
17842
|
+
statusCode: null,
|
|
17843
|
+
body: null,
|
|
17844
|
+
exceptionType: null,
|
|
17845
|
+
message: null,
|
|
17846
|
+
component: null,
|
|
17847
|
+
renderTree: null,
|
|
17848
|
+
delegateTarget: null,
|
|
17849
|
+
emitEvent: null,
|
|
17850
|
+
location: range
|
|
17851
|
+
},
|
|
17852
|
+
effects: [],
|
|
17853
|
+
location: range,
|
|
17854
|
+
isDefault: true
|
|
17855
|
+
}
|
|
17856
|
+
],
|
|
17857
|
+
readings: gaps.map((gap) => (0, import_extractor4.unreadableReading)(gap, range)),
|
|
17858
|
+
dependencyCalls: [],
|
|
17859
|
+
declaredContract: null
|
|
17860
|
+
};
|
|
17206
17861
|
}
|
|
17207
17862
|
function blockConfiguresCall(pattern) {
|
|
17208
17863
|
const names = /* @__PURE__ */ new Set([
|
|
@@ -17243,12 +17898,15 @@ function branchesFor(body, range) {
|
|
|
17243
17898
|
}
|
|
17244
17899
|
function bodyOfMethod(method, storage) {
|
|
17245
17900
|
const effects = invocationEffects(method);
|
|
17246
|
-
const
|
|
17901
|
+
const extra = [
|
|
17902
|
+
...envReadEffects(method),
|
|
17903
|
+
...storage === void 0 ? [] : storageEffects(callsUnder(method), storage)
|
|
17904
|
+
];
|
|
17247
17905
|
return {
|
|
17248
17906
|
bodyContent: methodHasStatements(method) ? "statements" : "empty",
|
|
17249
17907
|
readings: [],
|
|
17250
17908
|
...effects.length > 0 ? { effects } : {},
|
|
17251
|
-
...
|
|
17909
|
+
...extra.length > 0 ? { extraEffects: extra } : {}
|
|
17252
17910
|
};
|
|
17253
17911
|
}
|
|
17254
17912
|
function callsUnder(node, found = []) {
|
|
@@ -17268,18 +17926,25 @@ var NO_METHOD_BEHIND_IT = {
|
|
|
17268
17926
|
readings: []
|
|
17269
17927
|
};
|
|
17270
17928
|
function methodNotSettled(reason, range) {
|
|
17271
|
-
return { readings: [(0,
|
|
17929
|
+
return { readings: [(0, import_extractor4.unreadableReading)(reason, range)] };
|
|
17272
17930
|
}
|
|
17273
17931
|
function bodyFromLookup(found, range, subject, nothingThere, storage) {
|
|
17274
17932
|
const table = {
|
|
17275
|
-
found: (lookup) =>
|
|
17933
|
+
found: (lookup) => ({
|
|
17934
|
+
...bodyOfMethod(lookup.method, storage),
|
|
17935
|
+
reachSeed: {
|
|
17936
|
+
file: lookup.block.file,
|
|
17937
|
+
node: lookup.method,
|
|
17938
|
+
enclosingQualifiedName: lookup.block.info.qualifiedName
|
|
17939
|
+
}
|
|
17940
|
+
}),
|
|
17276
17941
|
unsettled: (lookup) => methodNotSettled(
|
|
17277
17942
|
`${subject} could be answered by a method ${lookup.reason}, so whether one exists was not settled here`,
|
|
17278
17943
|
range
|
|
17279
17944
|
),
|
|
17280
17945
|
none: () => nothingThere
|
|
17281
17946
|
};
|
|
17282
|
-
return (0,
|
|
17947
|
+
return (0, import_behavioral_ir2.dispatchByType)(table, found);
|
|
17283
17948
|
}
|
|
17284
17949
|
async function readFieldCall(stmt, scope, ctx, ancestry) {
|
|
17285
17950
|
if (stmt.type !== "call" || field(stmt, "receiver") !== null) {
|
|
@@ -17322,7 +17987,7 @@ function computedNameDeclaration(stmt, nameArg) {
|
|
|
17322
17987
|
contract: null,
|
|
17323
17988
|
body: {
|
|
17324
17989
|
readings: [
|
|
17325
|
-
(0,
|
|
17990
|
+
(0, import_extractor4.unreadableReading)(
|
|
17326
17991
|
`This field is named by ${nameArg.text}, which is worked out when the class body runs, so the name the schema exposes it under was not read here`,
|
|
17327
17992
|
range
|
|
17328
17993
|
)
|
|
@@ -17548,7 +18213,7 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
17548
18213
|
exportName: null,
|
|
17549
18214
|
exportPath: null
|
|
17550
18215
|
},
|
|
17551
|
-
boundaryBinding: decl.namedOnTheWire ? (0,
|
|
18216
|
+
boundaryBinding: decl.namedOnTheWire ? (0, import_behavioral_ir2.graphqlResolverBinding)({
|
|
17552
18217
|
transport: pack.protocol,
|
|
17553
18218
|
recognition: pack.name,
|
|
17554
18219
|
typeName,
|
|
@@ -17564,65 +18229,39 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
17564
18229
|
};
|
|
17565
18230
|
}
|
|
17566
18231
|
|
|
17567
|
-
// src/facts.ts
|
|
17568
|
-
function
|
|
17569
|
-
return `${filePath}:${
|
|
18232
|
+
// src/facts/values.ts
|
|
18233
|
+
function nodeId(filePath, node) {
|
|
18234
|
+
return `${filePath}:${node.startIndex}-${node.endIndex}`;
|
|
17570
18235
|
}
|
|
17571
|
-
function
|
|
17572
|
-
|
|
18236
|
+
function nameId(filePath, name) {
|
|
18237
|
+
return `${filePath}#${name}`;
|
|
17573
18238
|
}
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
17577
|
-
var import_node_path2 = __toESM(require("path"), 1);
|
|
17578
|
-
var import_node_url = require("url");
|
|
17579
|
-
var import_web_tree_sitter = require("web-tree-sitter");
|
|
17580
|
-
var GRAMMAR_PATH = import_node_path2.default.join(
|
|
17581
|
-
import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl)),
|
|
17582
|
-
"..",
|
|
17583
|
-
"grammar",
|
|
17584
|
-
"tree-sitter-ruby.wasm"
|
|
17585
|
-
);
|
|
17586
|
-
var languagePromise = null;
|
|
17587
|
-
function loadLanguage() {
|
|
17588
|
-
languagePromise ??= (async () => {
|
|
17589
|
-
if (!import_node_fs2.default.existsSync(GRAMMAR_PATH)) {
|
|
17590
|
-
throw new Error(
|
|
17591
|
-
`Ruby grammar not found at ${GRAMMAR_PATH}. See grammar/README.md.`
|
|
17592
|
-
);
|
|
17593
|
-
}
|
|
17594
|
-
await import_web_tree_sitter.Parser.init();
|
|
17595
|
-
return import_web_tree_sitter.Language.load(GRAMMAR_PATH);
|
|
17596
|
-
})();
|
|
17597
|
-
return languagePromise;
|
|
18239
|
+
function paramNameOf(param) {
|
|
18240
|
+
return param.type === "identifier" ? param : field(param, "name") ?? null;
|
|
17598
18241
|
}
|
|
17599
|
-
|
|
17600
|
-
const
|
|
17601
|
-
const
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
throw new Error("tree-sitter produced no tree for this source");
|
|
18242
|
+
function declaredParamNames(method) {
|
|
18243
|
+
const params = field(method, "parameters");
|
|
18244
|
+
const declared = /* @__PURE__ */ new Set();
|
|
18245
|
+
for (const param of params === null ? [] : children3(params)) {
|
|
18246
|
+
const name = paramNameOf(param);
|
|
18247
|
+
if (name !== null) {
|
|
18248
|
+
declared.add(name.text);
|
|
17607
18249
|
}
|
|
17608
|
-
return tree;
|
|
17609
|
-
} finally {
|
|
17610
|
-
parser.delete();
|
|
17611
18250
|
}
|
|
18251
|
+
return declared;
|
|
17612
18252
|
}
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17622
|
-
|
|
17623
|
-
}
|
|
17624
|
-
|
|
17625
|
-
return `${filePath}#${name}`;
|
|
18253
|
+
function readKey(filePath, node, enclosing) {
|
|
18254
|
+
if (node.type !== "identifier" && node.type !== "constant") {
|
|
18255
|
+
return nodeId(filePath, node);
|
|
18256
|
+
}
|
|
18257
|
+
const parent = node.parent;
|
|
18258
|
+
if (parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null) {
|
|
18259
|
+
return isPropertyRead2(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
18260
|
+
}
|
|
18261
|
+
if (enclosing !== null && declaredParamNames(enclosing).has(node.text)) {
|
|
18262
|
+
return `${nodeId(filePath, enclosing)}#${node.text}`;
|
|
18263
|
+
}
|
|
18264
|
+
return nameId(filePath, node.text);
|
|
17626
18265
|
}
|
|
17627
18266
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
17628
18267
|
"string",
|
|
@@ -17784,6 +18423,9 @@ function emitExpressionFacts(emitter, node) {
|
|
|
17784
18423
|
if (WRITTEN_VALUE_TYPES.has(child.type)) {
|
|
17785
18424
|
add(emitter, "writtenValue", nodeId(emitter.filePath, child));
|
|
17786
18425
|
}
|
|
18426
|
+
if (child.type === "nil") {
|
|
18427
|
+
add(emitter, "placeholderValue", nodeId(emitter.filePath, child));
|
|
18428
|
+
}
|
|
17787
18429
|
});
|
|
17788
18430
|
}
|
|
17789
18431
|
function implicitReturn(body) {
|
|
@@ -17800,7 +18442,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
17800
18442
|
const declared = /* @__PURE__ */ new Set();
|
|
17801
18443
|
let position = 0;
|
|
17802
18444
|
for (const param of params === null ? [] : children3(params)) {
|
|
17803
|
-
const paramName =
|
|
18445
|
+
const paramName = paramNameOf(param);
|
|
17804
18446
|
if (paramName !== null) {
|
|
17805
18447
|
declared.add(paramName.text);
|
|
17806
18448
|
const paramKey = `${funcKey}#${paramName.text}`;
|
|
@@ -17895,6 +18537,10 @@ function emitClassFacts(emitter, cls) {
|
|
|
17895
18537
|
}
|
|
17896
18538
|
continue;
|
|
17897
18539
|
}
|
|
18540
|
+
if (NESTING_TYPES.has(statement.type)) {
|
|
18541
|
+
emitClassFacts(emitter, statement);
|
|
18542
|
+
continue;
|
|
18543
|
+
}
|
|
17898
18544
|
if (!METHOD_TYPES.has(statement.type)) {
|
|
17899
18545
|
continue;
|
|
17900
18546
|
}
|
|
@@ -17918,7 +18564,7 @@ function emitValueFacts(db, filePath, root) {
|
|
|
17918
18564
|
};
|
|
17919
18565
|
const walk = (node) => {
|
|
17920
18566
|
for (const child of children3(node)) {
|
|
17921
|
-
if (child.type
|
|
18567
|
+
if (NESTING_TYPES.has(child.type)) {
|
|
17922
18568
|
declaresName(child, emitClassFacts(emitter, child));
|
|
17923
18569
|
continue;
|
|
17924
18570
|
}
|
|
@@ -17936,7 +18582,6 @@ function emitValueFacts(db, filePath, root) {
|
|
|
17936
18582
|
}
|
|
17937
18583
|
|
|
17938
18584
|
// src/facts/constants.ts
|
|
17939
|
-
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
17940
18585
|
function children4(node) {
|
|
17941
18586
|
return node.namedChildren.filter((child) => child !== null);
|
|
17942
18587
|
}
|
|
@@ -18014,16 +18659,18 @@ function collectFileConstants(filePath, root) {
|
|
|
18014
18659
|
}
|
|
18015
18660
|
};
|
|
18016
18661
|
walk(root, null, []);
|
|
18017
|
-
return { definitions, references };
|
|
18662
|
+
return { filePath, definitions, references };
|
|
18018
18663
|
}
|
|
18019
18664
|
function emitConstantBindings(db, perFile) {
|
|
18020
18665
|
const byName = /* @__PURE__ */ new Map();
|
|
18666
|
+
const fileOfDefinition = /* @__PURE__ */ new Map();
|
|
18021
18667
|
const files = [...perFile];
|
|
18022
18668
|
for (const file2 of files) {
|
|
18023
18669
|
for (const definition of file2.definitions) {
|
|
18024
18670
|
const found = byName.get(definition.qualifiedName) ?? [];
|
|
18025
18671
|
found.push(definition.key);
|
|
18026
18672
|
byName.set(definition.qualifiedName, found);
|
|
18673
|
+
fileOfDefinition.set(definition.key, file2.filePath);
|
|
18027
18674
|
}
|
|
18028
18675
|
}
|
|
18029
18676
|
const settles = (candidate) => {
|
|
@@ -18040,6 +18687,10 @@ function emitConstantBindings(db, perFile) {
|
|
|
18040
18687
|
const key = settles(candidate);
|
|
18041
18688
|
if (key !== null) {
|
|
18042
18689
|
db.add("binds", [reference.key, key]);
|
|
18690
|
+
const definedIn = fileOfDefinition.get(key);
|
|
18691
|
+
if (definedIn !== void 0 && definedIn !== file2.filePath) {
|
|
18692
|
+
db.add("rbConstantFrom", [file2.filePath, definedIn]);
|
|
18693
|
+
}
|
|
18043
18694
|
break;
|
|
18044
18695
|
}
|
|
18045
18696
|
}
|
|
@@ -18047,10 +18698,652 @@ function emitConstantBindings(db, perFile) {
|
|
|
18047
18698
|
}
|
|
18048
18699
|
}
|
|
18049
18700
|
|
|
18701
|
+
// src/facts/why.ts
|
|
18702
|
+
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
18703
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
18704
|
+
var import_datalog8 = require("@suss/datalog");
|
|
18705
|
+
|
|
18706
|
+
// src/parser.ts
|
|
18707
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
18708
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
18709
|
+
var import_node_url = require("url");
|
|
18710
|
+
var import_web_tree_sitter = require("web-tree-sitter");
|
|
18711
|
+
var GRAMMAR_PATH = import_node_path2.default.join(
|
|
18712
|
+
import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl)),
|
|
18713
|
+
"..",
|
|
18714
|
+
"grammar",
|
|
18715
|
+
"tree-sitter-ruby.wasm"
|
|
18716
|
+
);
|
|
18717
|
+
var languagePromise = null;
|
|
18718
|
+
var loadedLanguage = null;
|
|
18719
|
+
function loadLanguage() {
|
|
18720
|
+
languagePromise ??= (async () => {
|
|
18721
|
+
if (!import_node_fs2.default.existsSync(GRAMMAR_PATH)) {
|
|
18722
|
+
throw new Error(
|
|
18723
|
+
`Ruby grammar not found at ${GRAMMAR_PATH}. See grammar/README.md.`
|
|
18724
|
+
);
|
|
18725
|
+
}
|
|
18726
|
+
await import_web_tree_sitter.Parser.init();
|
|
18727
|
+
const language = await import_web_tree_sitter.Language.load(GRAMMAR_PATH);
|
|
18728
|
+
loadedLanguage = language;
|
|
18729
|
+
return language;
|
|
18730
|
+
})();
|
|
18731
|
+
return languagePromise;
|
|
18732
|
+
}
|
|
18733
|
+
async function parseRuby(source) {
|
|
18734
|
+
const language = await loadLanguage();
|
|
18735
|
+
return parseWith(language, source);
|
|
18736
|
+
}
|
|
18737
|
+
async function preloadRubyGrammar() {
|
|
18738
|
+
await loadLanguage();
|
|
18739
|
+
}
|
|
18740
|
+
function parseRubySync(source) {
|
|
18741
|
+
if (loadedLanguage === null) {
|
|
18742
|
+
throw new Error(
|
|
18743
|
+
"the Ruby grammar has not been loaded yet; call preloadRubyGrammar first"
|
|
18744
|
+
);
|
|
18745
|
+
}
|
|
18746
|
+
return parseWith(loadedLanguage, source);
|
|
18747
|
+
}
|
|
18748
|
+
function parseWith(language, source) {
|
|
18749
|
+
const parser = new import_web_tree_sitter.Parser();
|
|
18750
|
+
parser.setLanguage(language);
|
|
18751
|
+
try {
|
|
18752
|
+
const tree = parser.parse(source);
|
|
18753
|
+
if (tree === null) {
|
|
18754
|
+
throw new Error("tree-sitter produced no tree for this source");
|
|
18755
|
+
}
|
|
18756
|
+
return tree;
|
|
18757
|
+
} finally {
|
|
18758
|
+
parser.delete();
|
|
18759
|
+
}
|
|
18760
|
+
}
|
|
18761
|
+
|
|
18762
|
+
// src/project.ts
|
|
18763
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
18764
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
18765
|
+
var import_behavioral_ir4 = require("@suss/behavioral-ir");
|
|
18766
|
+
var import_datalog6 = require("@suss/datalog");
|
|
18767
|
+
var import_extractor9 = require("@suss/extractor");
|
|
18768
|
+
|
|
18769
|
+
// src/diagnostics.ts
|
|
18770
|
+
var import_extractor5 = require("@suss/extractor");
|
|
18771
|
+
function buildRubyExtractionReport(args) {
|
|
18772
|
+
return (0, import_extractor5.buildUngatedExtractionReport)({
|
|
18773
|
+
packs: args.packs.map((pack) => ({
|
|
18774
|
+
name: pack.name,
|
|
18775
|
+
version: pack.version ?? null,
|
|
18776
|
+
discovers: pack.discovery.length > 0
|
|
18777
|
+
})),
|
|
18778
|
+
tallies: args.tallies,
|
|
18779
|
+
filesWalked: args.filesWalked,
|
|
18780
|
+
summaries: args.summaries
|
|
18781
|
+
});
|
|
18782
|
+
}
|
|
18783
|
+
|
|
18784
|
+
// src/facts.ts
|
|
18785
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
18786
|
+
var import_extractor6 = require("@suss/extractor");
|
|
18787
|
+
function unitKey(filePath, range, name) {
|
|
18788
|
+
return `${filePath}:${range.start}-${range.end}#${name}`;
|
|
18789
|
+
}
|
|
18790
|
+
function emitEntryFact(db, filePath, range, name) {
|
|
18791
|
+
db.add("entry", [unitKey(filePath, range, name)]);
|
|
18792
|
+
}
|
|
18793
|
+
function emitRequireFacts(db, filePath, root, known) {
|
|
18794
|
+
(0, import_extractor6.walkDescendants)(root, null, {
|
|
18795
|
+
at: (node) => {
|
|
18796
|
+
const target = requiredRelativePath(node);
|
|
18797
|
+
if (target === null) {
|
|
18798
|
+
return;
|
|
18799
|
+
}
|
|
18800
|
+
const resolved = import_node_path3.default.resolve(
|
|
18801
|
+
import_node_path3.default.dirname(filePath),
|
|
18802
|
+
target.endsWith(".rb") ? target : `${target}.rb`
|
|
18803
|
+
);
|
|
18804
|
+
if (known.has(resolved)) {
|
|
18805
|
+
db.add("rbRequires", [filePath, resolved]);
|
|
18806
|
+
}
|
|
18807
|
+
},
|
|
18808
|
+
into: (node) => node.type === "argument_list" ? import_extractor6.SKIP_CHILDREN : null
|
|
18809
|
+
});
|
|
18810
|
+
}
|
|
18811
|
+
function requiredRelativePath(node) {
|
|
18812
|
+
if (node.type !== "call" || field(node, "receiver") !== null || field(node, "method")?.text !== "require_relative") {
|
|
18813
|
+
return null;
|
|
18814
|
+
}
|
|
18815
|
+
const { positional } = readCallArgs(field(node, "arguments"));
|
|
18816
|
+
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
18817
|
+
}
|
|
18818
|
+
|
|
18819
|
+
// src/reach/closure.ts
|
|
18820
|
+
var import_behavioral_ir3 = require("@suss/behavioral-ir");
|
|
18821
|
+
var import_datalog5 = require("@suss/datalog");
|
|
18822
|
+
var import_extractor7 = require("@suss/extractor");
|
|
18823
|
+
|
|
18824
|
+
// src/reach/resolveCallee.ts
|
|
18825
|
+
var DYNAMIC_SEND_NAMES = /* @__PURE__ */ new Set(["send", "public_send", "__send__"]);
|
|
18826
|
+
var stop = (reason) => ({
|
|
18827
|
+
kind: "stopped",
|
|
18828
|
+
reason
|
|
18829
|
+
});
|
|
18830
|
+
function followed(target) {
|
|
18831
|
+
return { kind: "followed", target };
|
|
18832
|
+
}
|
|
18833
|
+
async function resolveCallee(call, site, ctx) {
|
|
18834
|
+
const methodName = calleeMethodName(call);
|
|
18835
|
+
if (methodName === void 0) {
|
|
18836
|
+
return stop("noDeclaration");
|
|
18837
|
+
}
|
|
18838
|
+
if (DYNAMIC_SEND_NAMES.has(methodName)) {
|
|
18839
|
+
return stop("unsettledValue");
|
|
18840
|
+
}
|
|
18841
|
+
const receiver = field(call, "receiver");
|
|
18842
|
+
if (receiver === null || receiver.type === "self") {
|
|
18843
|
+
return resolveImplicitSelf(methodName, site, ctx);
|
|
18844
|
+
}
|
|
18845
|
+
if (receiver.type === "identifier" && methodName === "call" && site.ownParameters.includes(receiver.text)) {
|
|
18846
|
+
return stop("callerSupplied");
|
|
18847
|
+
}
|
|
18848
|
+
return resolveOnReceiver(receiver, methodName, site, ctx);
|
|
18849
|
+
}
|
|
18850
|
+
async function resolveMethodReference(name, site, ctx) {
|
|
18851
|
+
const resolved = await resolveImplicitSelf(name, site, ctx);
|
|
18852
|
+
return resolved.kind === "followed" ? resolved.target : null;
|
|
18853
|
+
}
|
|
18854
|
+
function leavesRoomForATopLevelMethod(reason) {
|
|
18855
|
+
return reason === "noDeclaration" || reason === "outsideRun";
|
|
18856
|
+
}
|
|
18857
|
+
async function resolveImplicitSelf(methodName, site, ctx) {
|
|
18858
|
+
if (site.enclosingQualifiedName !== null) {
|
|
18859
|
+
const onClass = await methodOnAncestryOf(
|
|
18860
|
+
site.enclosingQualifiedName,
|
|
18861
|
+
methodName,
|
|
18862
|
+
ctx
|
|
18863
|
+
);
|
|
18864
|
+
if (onClass.kind === "followed" || !leavesRoomForATopLevelMethod(onClass.reason)) {
|
|
18865
|
+
return onClass;
|
|
18866
|
+
}
|
|
18867
|
+
}
|
|
18868
|
+
return resolveTopLevelName(methodName, ctx);
|
|
18869
|
+
}
|
|
18870
|
+
function resolveTopLevelName(methodName, ctx) {
|
|
18871
|
+
const candidates = ctx.topLevelMethods.get(methodName);
|
|
18872
|
+
if (candidates === void 0 || candidates.length === 0) {
|
|
18873
|
+
return stop("noDeclaration");
|
|
18874
|
+
}
|
|
18875
|
+
if (candidates.length > 1) {
|
|
18876
|
+
return stop("multipleSources");
|
|
18877
|
+
}
|
|
18878
|
+
return followed(candidates[0]);
|
|
18879
|
+
}
|
|
18880
|
+
async function resolveOnReceiver(receiver, methodName, site, ctx) {
|
|
18881
|
+
if (receiver.type === "call") {
|
|
18882
|
+
const innerName = calleeMethodName(receiver);
|
|
18883
|
+
const innerReceiver = field(receiver, "receiver");
|
|
18884
|
+
if (innerName === "new" && innerReceiver !== null) {
|
|
18885
|
+
const classRef = classRefOf(innerReceiver, site, ctx);
|
|
18886
|
+
return classRef === null ? stop("unsettledValue") : methodOnAncestryOf(classRef, methodName, ctx);
|
|
18887
|
+
}
|
|
18888
|
+
const root = rootConstantOf(receiver);
|
|
18889
|
+
if (root === null) {
|
|
18890
|
+
return stop("unsettledValue");
|
|
18891
|
+
}
|
|
18892
|
+
return classRefOf(root, site, ctx) === null ? stop("outsideRun") : stop("unsettledValue");
|
|
18893
|
+
}
|
|
18894
|
+
if (receiver.type === "constant" || receiver.type === "scope_resolution") {
|
|
18895
|
+
const classRef = classRefOf(receiver, site, ctx);
|
|
18896
|
+
return classRef === null ? stop("outsideRun") : singletonMethodOn(classRef, methodName, ctx);
|
|
18897
|
+
}
|
|
18898
|
+
return stop("unsettledValue");
|
|
18899
|
+
}
|
|
18900
|
+
function rootConstantOf(node) {
|
|
18901
|
+
if (node.type === "constant" || node.type === "scope_resolution") {
|
|
18902
|
+
return node;
|
|
18903
|
+
}
|
|
18904
|
+
if (node.type !== "call") {
|
|
18905
|
+
return null;
|
|
18906
|
+
}
|
|
18907
|
+
const receiver = field(node, "receiver");
|
|
18908
|
+
return receiver === null ? null : rootConstantOf(receiver);
|
|
18909
|
+
}
|
|
18910
|
+
function classRefOf(node, site, ctx) {
|
|
18911
|
+
if (node.type === "constant") {
|
|
18912
|
+
const shadow = shadowingClassFor(node, site.nesting, ctx.knownClasses);
|
|
18913
|
+
if (shadow !== null) {
|
|
18914
|
+
return shadow;
|
|
18915
|
+
}
|
|
18916
|
+
const qualified = qualifyConstantRef(node, site.nesting);
|
|
18917
|
+
return qualified !== null && ctx.knownClasses.has(qualified) ? qualified : null;
|
|
18918
|
+
}
|
|
18919
|
+
if (node.type === "scope_resolution") {
|
|
18920
|
+
const qualified = qualifyConstantRef(node, site.nesting);
|
|
18921
|
+
return qualified !== null && ctx.knownClasses.has(qualified) ? qualified : null;
|
|
18922
|
+
}
|
|
18923
|
+
return null;
|
|
18924
|
+
}
|
|
18925
|
+
async function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
18926
|
+
const ownBlocks = ctx.lookup.localDefinition?.(qualifiedName) ?? null;
|
|
18927
|
+
if (ownBlocks === null) {
|
|
18928
|
+
return stop("outsideRun");
|
|
18929
|
+
}
|
|
18930
|
+
const ancestry = await ancestryOf(qualifiedName, ownBlocks, ctx.lookup);
|
|
18931
|
+
const found = methodInAncestry(ancestry, methodName);
|
|
18932
|
+
if (found.type === "found") {
|
|
18933
|
+
return followed(reachedMethod(found.method, found.block, methodName));
|
|
18934
|
+
}
|
|
18935
|
+
if (found.type === "unsettled") {
|
|
18936
|
+
return stop(
|
|
18937
|
+
found.cause === "dynamicDefine" ? "unsettledValue" : "outsideRun"
|
|
18938
|
+
);
|
|
18939
|
+
}
|
|
18940
|
+
return stop("noDeclaration");
|
|
18941
|
+
}
|
|
18942
|
+
function reachedMethod(node, block, name) {
|
|
18943
|
+
return {
|
|
18944
|
+
file: block.file,
|
|
18945
|
+
node,
|
|
18946
|
+
name,
|
|
18947
|
+
exportPath: [block.info.qualifiedName, name],
|
|
18948
|
+
enclosingQualifiedName: block.info.qualifiedName
|
|
18949
|
+
};
|
|
18950
|
+
}
|
|
18951
|
+
function singletonMethodOn(qualifiedName, methodName, ctx) {
|
|
18952
|
+
const ownBlocks = ctx.lookup.localDefinition?.(qualifiedName) ?? null;
|
|
18953
|
+
if (ownBlocks === null) {
|
|
18954
|
+
return stop("outsideRun");
|
|
18955
|
+
}
|
|
18956
|
+
for (const block of ownBlocks) {
|
|
18957
|
+
if (block.info.bodyNode === null) {
|
|
18958
|
+
continue;
|
|
18959
|
+
}
|
|
18960
|
+
const found = singletonMethodsByName(block.info.bodyNode).get(methodName);
|
|
18961
|
+
if (found !== void 0) {
|
|
18962
|
+
return followed(reachedMethod(found, block, methodName));
|
|
18963
|
+
}
|
|
18964
|
+
}
|
|
18965
|
+
return stop("noDeclaration");
|
|
18966
|
+
}
|
|
18967
|
+
|
|
18968
|
+
// src/reach/closure.ts
|
|
18969
|
+
var REACHABLE_RULES = [
|
|
18970
|
+
(0, import_datalog5.rule)("reachable", [(0, import_datalog5.variable)("f")], [(0, import_datalog5.lit)("entry", (0, import_datalog5.variable)("f"))]),
|
|
18971
|
+
(0, import_datalog5.rule)(
|
|
18972
|
+
"reachable",
|
|
18973
|
+
[(0, import_datalog5.variable)("g")],
|
|
18974
|
+
[(0, import_datalog5.lit)("reachable", (0, import_datalog5.variable)("f")), (0, import_datalog5.lit)("calls", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("g"))]
|
|
18975
|
+
)
|
|
18976
|
+
];
|
|
18977
|
+
async function reachedFunctions(seeds, options) {
|
|
18978
|
+
const ctx = buildReachContext(options.files);
|
|
18979
|
+
const db = new import_datalog5.Database();
|
|
18980
|
+
const functionByKey = /* @__PURE__ */ new Map();
|
|
18981
|
+
const seedKeys = /* @__PURE__ */ new Set();
|
|
18982
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
18983
|
+
const targetsByKey = /* @__PURE__ */ new Map();
|
|
18984
|
+
const stopsByKey = /* @__PURE__ */ new Map();
|
|
18985
|
+
const argTargetsByKey = /* @__PURE__ */ new Map();
|
|
18986
|
+
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
18987
|
+
const passedPositions = /* @__PURE__ */ new Set();
|
|
18988
|
+
for (const seed of seeds) {
|
|
18989
|
+
seedKeys.add(seed.key);
|
|
18990
|
+
functionByKey.set(seed.key, {
|
|
18991
|
+
file: seed.file,
|
|
18992
|
+
node: seed.node,
|
|
18993
|
+
name: field(seed.node, "name")?.text ?? "<anon>",
|
|
18994
|
+
exportPath: [],
|
|
18995
|
+
enclosingQualifiedName: seed.enclosingQualifiedName
|
|
18996
|
+
});
|
|
18997
|
+
db.add("entry", [seed.key]);
|
|
18998
|
+
}
|
|
18999
|
+
for (; ; ) {
|
|
19000
|
+
(0, import_datalog5.evaluate)(db, REACHABLE_RULES);
|
|
19001
|
+
const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
|
|
19002
|
+
if (frontier.length === 0) {
|
|
19003
|
+
break;
|
|
19004
|
+
}
|
|
19005
|
+
for (const key of frontier) {
|
|
19006
|
+
scanned.add(key);
|
|
19007
|
+
const source = functionByKey.get(key);
|
|
19008
|
+
if (source === void 0) {
|
|
19009
|
+
continue;
|
|
19010
|
+
}
|
|
19011
|
+
const scan = await scanBody(source, ctx, options.displayPathOf);
|
|
19012
|
+
if (scan.stops.length > 0) {
|
|
19013
|
+
stopsByKey.set(key, scan.stops);
|
|
19014
|
+
}
|
|
19015
|
+
targetsByKey.set(key, scan.targets);
|
|
19016
|
+
argTargetsByKey.set(key, scan.argTargets);
|
|
19017
|
+
if (scan.parameterCalls.length > 0) {
|
|
19018
|
+
parameterCallsByKey.set(key, scan.parameterCalls);
|
|
19019
|
+
}
|
|
19020
|
+
for (const position of scan.passedPositions) {
|
|
19021
|
+
passedPositions.add(position);
|
|
19022
|
+
}
|
|
19023
|
+
for (const target of scan.followed) {
|
|
19024
|
+
const calleeKey = keyOf(target);
|
|
19025
|
+
if (!functionByKey.has(calleeKey)) {
|
|
19026
|
+
functionByKey.set(calleeKey, target);
|
|
19027
|
+
}
|
|
19028
|
+
db.add("calls", [key, calleeKey]);
|
|
19029
|
+
}
|
|
19030
|
+
}
|
|
19031
|
+
}
|
|
19032
|
+
const summaries = [];
|
|
19033
|
+
const summariesByKey = /* @__PURE__ */ new Map();
|
|
19034
|
+
for (const [keyAtom] of db.facts("reachable")) {
|
|
19035
|
+
const key = String(keyAtom);
|
|
19036
|
+
const target = functionByKey.get(key);
|
|
19037
|
+
if (seedKeys.has(key) || target === void 0) {
|
|
19038
|
+
continue;
|
|
19039
|
+
}
|
|
19040
|
+
const summary = (0, import_extractor7.assembleSummary)(libraryUnit(target, options), {
|
|
19041
|
+
gapHandling: "permissive"
|
|
19042
|
+
});
|
|
19043
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
19044
|
+
summary.gaps.push(...(stopsByKey.get(key) ?? []).map(import_behavioral_ir3.unfollowedCallGap));
|
|
19045
|
+
(0, import_behavioral_ir3.placeCalls)(summary, targetsByKey.get(key));
|
|
19046
|
+
(0, import_behavioral_ir3.placeArgTargets)(summary, argTargetsByKey.get(key));
|
|
19047
|
+
(0, import_behavioral_ir3.placeCalleeParameters)(summary, parameterCallsByKey.get(key));
|
|
19048
|
+
summariesByKey.set(key, [summary]);
|
|
19049
|
+
summaries.push(summary);
|
|
19050
|
+
}
|
|
19051
|
+
(0, import_behavioral_ir3.recordParameterGaps)(parameterCallsByKey, summariesByKey, passedPositions);
|
|
19052
|
+
return {
|
|
19053
|
+
summaries,
|
|
19054
|
+
targetsByKey,
|
|
19055
|
+
stopsByKey,
|
|
19056
|
+
argTargetsByKey,
|
|
19057
|
+
parameterCallsByKey,
|
|
19058
|
+
passedPositions
|
|
19059
|
+
};
|
|
19060
|
+
}
|
|
19061
|
+
function keyOf(target) {
|
|
19062
|
+
return nodeId(target.file, target.node);
|
|
19063
|
+
}
|
|
19064
|
+
function buildReachContext(files) {
|
|
19065
|
+
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
19066
|
+
const classes = [];
|
|
19067
|
+
for (const { file: file2, root } of files) {
|
|
19068
|
+
walkDefinitions(root, (info) => classes.push({ file: file2, info }));
|
|
19069
|
+
}
|
|
19070
|
+
const knownClasses = new Set(classes.map(({ info }) => info.qualifiedName));
|
|
19071
|
+
for (const { file: file2, info } of classes) {
|
|
19072
|
+
const list = blocksByQualifiedName.get(info.qualifiedName) ?? [];
|
|
19073
|
+
list.push({ info, knownClasses, file: file2 });
|
|
19074
|
+
blocksByQualifiedName.set(info.qualifiedName, list);
|
|
19075
|
+
}
|
|
19076
|
+
const topLevelMethods = /* @__PURE__ */ new Map();
|
|
19077
|
+
for (const { file: file2, root } of files) {
|
|
19078
|
+
for (const method of topLevelMethodNodes(root)) {
|
|
19079
|
+
const name = field(method, "name")?.text;
|
|
19080
|
+
if (name === void 0) {
|
|
19081
|
+
continue;
|
|
19082
|
+
}
|
|
19083
|
+
const list = topLevelMethods.get(name) ?? [];
|
|
19084
|
+
list.push({
|
|
19085
|
+
file: file2,
|
|
19086
|
+
node: method,
|
|
19087
|
+
name,
|
|
19088
|
+
exportPath: [name],
|
|
19089
|
+
enclosingQualifiedName: null
|
|
19090
|
+
});
|
|
19091
|
+
topLevelMethods.set(name, list);
|
|
19092
|
+
}
|
|
19093
|
+
}
|
|
19094
|
+
const lookup = {
|
|
19095
|
+
root: "",
|
|
19096
|
+
pathConvention: "railsUnderscore",
|
|
19097
|
+
ancestryRootClassNames: [],
|
|
19098
|
+
// Every class the run defines is already in `blocksByQualifiedName`
|
|
19099
|
+
// above, so a name that misses there is outside the run and this
|
|
19100
|
+
// never has a file on disk to read.
|
|
19101
|
+
parsedFile: async () => null,
|
|
19102
|
+
localDefinition: (name) => blocksByQualifiedName.get(name) ?? null
|
|
19103
|
+
};
|
|
19104
|
+
return { lookup, knownClasses, topLevelMethods };
|
|
19105
|
+
}
|
|
19106
|
+
function topLevelMethodNodes(root, found = []) {
|
|
19107
|
+
for (const child of bodyStatements(root)) {
|
|
19108
|
+
if (child.type === "method") {
|
|
19109
|
+
found.push(child);
|
|
19110
|
+
continue;
|
|
19111
|
+
}
|
|
19112
|
+
if (OWN_BODY_TYPES.has(child.type)) {
|
|
19113
|
+
continue;
|
|
19114
|
+
}
|
|
19115
|
+
topLevelMethodNodes(child, found);
|
|
19116
|
+
}
|
|
19117
|
+
return found;
|
|
19118
|
+
}
|
|
19119
|
+
function nestingOf(qualifiedName) {
|
|
19120
|
+
const parts = qualifiedName.split("::");
|
|
19121
|
+
const out = [];
|
|
19122
|
+
for (let depth = parts.length; depth >= 1; depth--) {
|
|
19123
|
+
out.push(parts.slice(0, depth).join("::"));
|
|
19124
|
+
}
|
|
19125
|
+
return out;
|
|
19126
|
+
}
|
|
19127
|
+
var EMPTY_SCAN = {
|
|
19128
|
+
followed: [],
|
|
19129
|
+
stops: [],
|
|
19130
|
+
targets: /* @__PURE__ */ new Map(),
|
|
19131
|
+
argTargets: /* @__PURE__ */ new Map(),
|
|
19132
|
+
parameterCalls: [],
|
|
19133
|
+
passedPositions: /* @__PURE__ */ new Set()
|
|
19134
|
+
};
|
|
19135
|
+
function methodReferenceSymbol(node) {
|
|
19136
|
+
const target = node.type === "block_argument" ? bodyStatements(node)[0] : node;
|
|
19137
|
+
if (target === void 0 || target.type !== "call" || field(target, "receiver") !== null || field(target, "method")?.text !== "method") {
|
|
19138
|
+
return null;
|
|
19139
|
+
}
|
|
19140
|
+
const args = field(target, "arguments");
|
|
19141
|
+
const argChildren = args === null ? [] : bodyStatements(args);
|
|
19142
|
+
return argChildren.length === 1 && argChildren[0]?.type === "simple_symbol" ? argChildren[0] : null;
|
|
19143
|
+
}
|
|
19144
|
+
async function scanBody(source, ctx, displayPathOf) {
|
|
19145
|
+
const body = field(source.node, "body");
|
|
19146
|
+
if (body === null) {
|
|
19147
|
+
return EMPTY_SCAN;
|
|
19148
|
+
}
|
|
19149
|
+
const calls = withoutChainLinks(bodyCalls(body));
|
|
19150
|
+
const ownParameters = positionalParameters(source.node).map((p) => p.name);
|
|
19151
|
+
const site = {
|
|
19152
|
+
enclosingQualifiedName: source.enclosingQualifiedName,
|
|
19153
|
+
nesting: source.enclosingQualifiedName === null ? [] : nestingOf(source.enclosingQualifiedName),
|
|
19154
|
+
ownParameters
|
|
19155
|
+
};
|
|
19156
|
+
const followed2 = [];
|
|
19157
|
+
const stops = [];
|
|
19158
|
+
const placements = new import_behavioral_ir3.TargetPlacements();
|
|
19159
|
+
const parameterCalls = [];
|
|
19160
|
+
const passedPositions = /* @__PURE__ */ new Set();
|
|
19161
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19162
|
+
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
19163
|
+
const recordPassedArgs = async (call, callee, calleeKey) => {
|
|
19164
|
+
const args = field(call, "arguments");
|
|
19165
|
+
if (args === null) {
|
|
19166
|
+
return;
|
|
19167
|
+
}
|
|
19168
|
+
const argNodes = bodyStatements(args);
|
|
19169
|
+
for (let position = 0; position < argNodes.length; position += 1) {
|
|
19170
|
+
const symbol2 = methodReferenceSymbol(argNodes[position]);
|
|
19171
|
+
if (symbol2 === null) {
|
|
19172
|
+
continue;
|
|
19173
|
+
}
|
|
19174
|
+
const resolved = await resolveMethodReference(
|
|
19175
|
+
symbol2.text.slice(1),
|
|
19176
|
+
site,
|
|
19177
|
+
ctx
|
|
19178
|
+
);
|
|
19179
|
+
if (resolved === null) {
|
|
19180
|
+
continue;
|
|
19181
|
+
}
|
|
19182
|
+
const key = keyOf(resolved);
|
|
19183
|
+
if (!seen.has(key)) {
|
|
19184
|
+
seen.add(key);
|
|
19185
|
+
followed2.push(resolved);
|
|
19186
|
+
}
|
|
19187
|
+
placements.placeArg(callee, position, {
|
|
19188
|
+
file: displayPathOf(resolved.file),
|
|
19189
|
+
span: spanOf(resolved.node)
|
|
19190
|
+
});
|
|
19191
|
+
if (calleeKey !== null) {
|
|
19192
|
+
passedPositions.add(`${calleeKey}#${position}`);
|
|
19193
|
+
}
|
|
19194
|
+
}
|
|
19195
|
+
};
|
|
19196
|
+
for (const call of calls) {
|
|
19197
|
+
const callee = calleeText(call);
|
|
19198
|
+
const outcome = await resolveCallee(call, site, ctx);
|
|
19199
|
+
const placed = outcome.kind === "followed" ? {
|
|
19200
|
+
file: displayPathOf(outcome.target.file),
|
|
19201
|
+
span: spanOf(outcome.target.node)
|
|
19202
|
+
} : outcome.reason === "noDeclaration" ? null : { file: displayPathOf(source.file), span: spanOf(call) };
|
|
19203
|
+
placements.place(callee, placed);
|
|
19204
|
+
await recordPassedArgs(
|
|
19205
|
+
call,
|
|
19206
|
+
callee,
|
|
19207
|
+
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
19208
|
+
);
|
|
19209
|
+
if (outcome.kind === "stopped") {
|
|
19210
|
+
const stopKey = `${outcome.reason}:${callee}`;
|
|
19211
|
+
if (!seen.has(stopKey) && (0, import_behavioral_ir3.worthRecording)(outcome.reason)) {
|
|
19212
|
+
seen.add(stopKey);
|
|
19213
|
+
stops.push({ callee, reason: outcome.reason });
|
|
19214
|
+
}
|
|
19215
|
+
if (outcome.reason === "callerSupplied" && !parameterCallsSeen.has(callee)) {
|
|
19216
|
+
const receiverName = field(call, "receiver")?.text;
|
|
19217
|
+
const parameterIndex = receiverName === void 0 ? -1 : ownParameters.indexOf(receiverName);
|
|
19218
|
+
if (parameterIndex !== -1) {
|
|
19219
|
+
parameterCallsSeen.add(callee);
|
|
19220
|
+
parameterCalls.push({ callee, parameterIndex });
|
|
19221
|
+
}
|
|
19222
|
+
}
|
|
19223
|
+
continue;
|
|
19224
|
+
}
|
|
19225
|
+
const key = keyOf(outcome.target);
|
|
19226
|
+
if (!seen.has(key)) {
|
|
19227
|
+
seen.add(key);
|
|
19228
|
+
followed2.push(outcome.target);
|
|
19229
|
+
}
|
|
19230
|
+
}
|
|
19231
|
+
return {
|
|
19232
|
+
followed: followed2,
|
|
19233
|
+
stops,
|
|
19234
|
+
targets: placements.targets,
|
|
19235
|
+
argTargets: placements.argTargets,
|
|
19236
|
+
parameterCalls,
|
|
19237
|
+
passedPositions
|
|
19238
|
+
};
|
|
19239
|
+
}
|
|
19240
|
+
function libraryUnit(target, options) {
|
|
19241
|
+
const { file: file2, node, name, exportPath } = target;
|
|
19242
|
+
const body = bodyOfMethod(node, options.storage);
|
|
19243
|
+
const range = rangeOf(node);
|
|
19244
|
+
return {
|
|
19245
|
+
identity: {
|
|
19246
|
+
name,
|
|
19247
|
+
nameKind: "binding",
|
|
19248
|
+
kind: "library",
|
|
19249
|
+
file: options.displayPathOf(file2),
|
|
19250
|
+
range,
|
|
19251
|
+
span: spanOf(node),
|
|
19252
|
+
exportName: exportPath[0] ?? name,
|
|
19253
|
+
exportPath
|
|
19254
|
+
},
|
|
19255
|
+
boundaryBinding: (0, import_behavioral_ir3.functionCallBinding)({
|
|
19256
|
+
transport: "in-process",
|
|
19257
|
+
recognition: "reachable"
|
|
19258
|
+
}),
|
|
19259
|
+
parameters: positionalParameters(node),
|
|
19260
|
+
branches: [
|
|
19261
|
+
{
|
|
19262
|
+
conditions: [],
|
|
19263
|
+
terminal: {
|
|
19264
|
+
kind: "return",
|
|
19265
|
+
statusCode: null,
|
|
19266
|
+
body: null,
|
|
19267
|
+
exceptionType: null,
|
|
19268
|
+
message: null,
|
|
19269
|
+
component: null,
|
|
19270
|
+
renderTree: null,
|
|
19271
|
+
delegateTarget: null,
|
|
19272
|
+
emitEvent: null,
|
|
19273
|
+
location: range
|
|
19274
|
+
},
|
|
19275
|
+
effects: body.effects ?? [],
|
|
19276
|
+
...body.extraEffects === void 0 ? {} : { extraEffects: body.extraEffects },
|
|
19277
|
+
location: range,
|
|
19278
|
+
isDefault: true
|
|
19279
|
+
}
|
|
19280
|
+
],
|
|
19281
|
+
bodyContent: body.bodyContent ?? "absent",
|
|
19282
|
+
dependencyCalls: [],
|
|
19283
|
+
declaredContract: null
|
|
19284
|
+
};
|
|
19285
|
+
}
|
|
19286
|
+
function positionalParameters(node) {
|
|
19287
|
+
const parameters = field(node, "parameters");
|
|
19288
|
+
if (parameters === null) {
|
|
19289
|
+
return [];
|
|
19290
|
+
}
|
|
19291
|
+
const out = [];
|
|
19292
|
+
let position = 0;
|
|
19293
|
+
for (const param of bodyStatements(parameters)) {
|
|
19294
|
+
const name = parameterName(param);
|
|
19295
|
+
if (name === null) {
|
|
19296
|
+
continue;
|
|
19297
|
+
}
|
|
19298
|
+
out.push({ name, position, role: name, typeText: null });
|
|
19299
|
+
position += 1;
|
|
19300
|
+
}
|
|
19301
|
+
return out;
|
|
19302
|
+
}
|
|
19303
|
+
function parameterName(param) {
|
|
19304
|
+
if (param.type === "identifier") {
|
|
19305
|
+
return param.text;
|
|
19306
|
+
}
|
|
19307
|
+
const named = field(param, "name");
|
|
19308
|
+
return named !== null && named.type === "identifier" ? named.text : null;
|
|
19309
|
+
}
|
|
19310
|
+
|
|
19311
|
+
// src/version.ts
|
|
19312
|
+
var import_extractor8 = require("@suss/extractor");
|
|
19313
|
+
var ADAPTER_VERSION = "0.1.0";
|
|
19314
|
+
var adapterStamp = (0, import_extractor8.createAdapterStamp)({
|
|
19315
|
+
moduleUrl: importMetaUrl,
|
|
19316
|
+
version: ADAPTER_VERSION
|
|
19317
|
+
});
|
|
19318
|
+
|
|
18050
19319
|
// src/project.ts
|
|
18051
19320
|
async function extractRubyProject(options) {
|
|
18052
|
-
const
|
|
19321
|
+
const timer = options.onTiming !== void 0 ? (0, import_extractor9.createTimer)() : (0, import_extractor9.noopTimer)();
|
|
19322
|
+
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
19323
|
+
options.cacheDir === null ? null : options.cacheDir ?? (options.projectRoot !== void 0 ? import_node_path4.default.join(options.projectRoot, ".suss", "cache") : null)
|
|
19324
|
+
);
|
|
19325
|
+
const extractionCache = (0, import_extractor9.createCacheLayer)(cacheDir);
|
|
19326
|
+
const packsDigest = adapterStamp.packsDigest(
|
|
19327
|
+
options.packs.map(
|
|
19328
|
+
(pack) => pack.version !== void 0 ? { name: pack.name, version: pack.version } : { name: pack.name }
|
|
19329
|
+
)
|
|
19330
|
+
);
|
|
19331
|
+
const cacheInput = {
|
|
19332
|
+
files: cacheDir === null ? [] : options.files,
|
|
19333
|
+
adapterPacksDigest: cacheDir === null ? packsDigest : (0, import_extractor9.runDigest)(packsDigest, options.packs, options.files)
|
|
19334
|
+
};
|
|
19335
|
+
const lookup = await timer.timeAsync(
|
|
19336
|
+
"cache.lookup",
|
|
19337
|
+
() => extractionCache.lookup(cacheInput)
|
|
19338
|
+
);
|
|
19339
|
+
options.onCacheDiagnostic?.(lookup.diagnostic);
|
|
19340
|
+
if (lookup.kind === "hit") {
|
|
19341
|
+
options.onTiming?.(timer.report());
|
|
19342
|
+
return { summaries: lookup.summaries, facts: new import_datalog6.Database() };
|
|
19343
|
+
}
|
|
19344
|
+
const db = new import_datalog6.Database();
|
|
18053
19345
|
const summaries = [];
|
|
19346
|
+
const tallies = (0, import_extractor5.createPackTallies)(options.packs);
|
|
18054
19347
|
const constants = [];
|
|
18055
19348
|
const cache = createFileCache(
|
|
18056
19349
|
(source) => parseRuby(source).then((tree) => tree.rootNode),
|
|
@@ -18058,33 +19351,178 @@ async function extractRubyProject(options) {
|
|
|
18058
19351
|
);
|
|
18059
19352
|
const parsed = [];
|
|
18060
19353
|
for (const file2 of options.files) {
|
|
18061
|
-
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
19354
|
+
await timer.timeAsync("parse", async () => {
|
|
19355
|
+
const root = await cache.get(file2);
|
|
19356
|
+
if (root === null) {
|
|
19357
|
+
return;
|
|
19358
|
+
}
|
|
19359
|
+
parsed.push({ file: file2, root });
|
|
19360
|
+
emitValueFacts(db, file2, root);
|
|
19361
|
+
constants.push(collectFileConstants(file2, root));
|
|
19362
|
+
});
|
|
18068
19363
|
}
|
|
18069
|
-
|
|
19364
|
+
timer.time("discover", () => {
|
|
19365
|
+
emitConstantBindings(db, constants);
|
|
19366
|
+
const known = new Set(parsed.map(({ file: file2 }) => file2));
|
|
19367
|
+
for (const { file: file2, root } of parsed) {
|
|
19368
|
+
emitRequireFacts(db, file2, root, known);
|
|
19369
|
+
}
|
|
19370
|
+
});
|
|
18070
19371
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
19372
|
+
const displayPathOf = (file2) => options.workspaceRoot !== void 0 ? import_node_path4.default.relative(options.workspaceRoot, file2) : file2;
|
|
19373
|
+
const seeds = [];
|
|
19374
|
+
const summariesBySeed = /* @__PURE__ */ new Map();
|
|
18071
19375
|
for (const { file: file2, root } of parsed) {
|
|
18072
|
-
const displayPath =
|
|
18073
|
-
const
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
19376
|
+
const displayPath = displayPathOf(file2);
|
|
19377
|
+
const seedByRaw = /* @__PURE__ */ new Map();
|
|
19378
|
+
const rawUnits = await timer.timeAsync(
|
|
19379
|
+
"discover",
|
|
19380
|
+
() => discoverUnits(root, {
|
|
19381
|
+
packs: options.packs,
|
|
19382
|
+
filePath: displayPath,
|
|
19383
|
+
absoluteFile: file2,
|
|
19384
|
+
cache,
|
|
19385
|
+
...storagePatterns.length > 0 ? { storage: { facts: db, patterns: storagePatterns } } : {},
|
|
19386
|
+
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
19387
|
+
})
|
|
19388
|
+
);
|
|
18079
19389
|
for (const raw of rawUnits) {
|
|
18080
|
-
const summary =
|
|
19390
|
+
const summary = timer.time(
|
|
19391
|
+
"summarize",
|
|
19392
|
+
() => (0, import_extractor9.assembleSummary)(raw, { gapHandling: "permissive" })
|
|
19393
|
+
);
|
|
18081
19394
|
summary.confidence = { source: "inferred_static", level: "low" };
|
|
18082
19395
|
summaries.push(summary);
|
|
18083
19396
|
emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
|
|
19397
|
+
(0, import_extractor5.tallyUnit)(tallies, raw.boundaryBinding?.recognition);
|
|
19398
|
+
const seed = seedByRaw.get(raw);
|
|
19399
|
+
if (seed === void 0) {
|
|
19400
|
+
continue;
|
|
19401
|
+
}
|
|
19402
|
+
const key = nodeId(seed.file, seed.node);
|
|
19403
|
+
const sharing = summariesBySeed.get(key);
|
|
19404
|
+
if (sharing === void 0) {
|
|
19405
|
+
seeds.push({
|
|
19406
|
+
key,
|
|
19407
|
+
file: seed.file,
|
|
19408
|
+
node: seed.node,
|
|
19409
|
+
enclosingQualifiedName: seed.enclosingQualifiedName
|
|
19410
|
+
});
|
|
19411
|
+
summariesBySeed.set(key, [summary]);
|
|
19412
|
+
} else {
|
|
19413
|
+
sharing.push(summary);
|
|
19414
|
+
}
|
|
19415
|
+
}
|
|
19416
|
+
const loadTimeReads = timer.time("discover", () => envReadEffects(root));
|
|
19417
|
+
if (loadTimeReads.length > 0) {
|
|
19418
|
+
const summary = timer.time(
|
|
19419
|
+
"summarize",
|
|
19420
|
+
() => (0, import_extractor9.assembleSummary)(
|
|
19421
|
+
(0, import_extractor9.moduleInitStructure)({
|
|
19422
|
+
name: import_node_path4.default.basename(displayPath),
|
|
19423
|
+
file: displayPath,
|
|
19424
|
+
range: rangeOf(root),
|
|
19425
|
+
effects: loadTimeReads
|
|
19426
|
+
}),
|
|
19427
|
+
{ gapHandling: "permissive" }
|
|
19428
|
+
)
|
|
19429
|
+
);
|
|
19430
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
19431
|
+
summaries.push(summary);
|
|
18084
19432
|
}
|
|
18085
19433
|
}
|
|
19434
|
+
for (const pack of options.packs) {
|
|
19435
|
+
for (const pattern of pack.discovery) {
|
|
19436
|
+
if (pattern.type !== "controllerActions") {
|
|
19437
|
+
continue;
|
|
19438
|
+
}
|
|
19439
|
+
const gaps = pattern.routingGaps?.() ?? [];
|
|
19440
|
+
if (gaps.length === 0) {
|
|
19441
|
+
continue;
|
|
19442
|
+
}
|
|
19443
|
+
const summary = timer.time(
|
|
19444
|
+
"summarize",
|
|
19445
|
+
() => (0, import_extractor9.assembleSummary)(routingGapUnit(pattern, gaps), {
|
|
19446
|
+
gapHandling: "permissive"
|
|
19447
|
+
})
|
|
19448
|
+
);
|
|
19449
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
19450
|
+
summaries.push(summary);
|
|
19451
|
+
}
|
|
19452
|
+
}
|
|
19453
|
+
const reached = await timer.timeAsync(
|
|
19454
|
+
"summarize",
|
|
19455
|
+
() => reachedFunctions(seeds, {
|
|
19456
|
+
files: parsed,
|
|
19457
|
+
displayPathOf,
|
|
19458
|
+
...storagePatterns.length > 0 ? { storage: { facts: db, patterns: storagePatterns } } : {}
|
|
19459
|
+
})
|
|
19460
|
+
);
|
|
19461
|
+
for (const [key, owners] of summariesBySeed) {
|
|
19462
|
+
for (const summary of owners) {
|
|
19463
|
+
summary.gaps.push(
|
|
19464
|
+
...(reached.stopsByKey.get(key) ?? []).map(import_behavioral_ir4.unfollowedCallGap)
|
|
19465
|
+
);
|
|
19466
|
+
(0, import_behavioral_ir4.placeCalls)(summary, reached.targetsByKey.get(key));
|
|
19467
|
+
(0, import_behavioral_ir4.placeArgTargets)(summary, reached.argTargetsByKey.get(key));
|
|
19468
|
+
(0, import_behavioral_ir4.placeCalleeParameters)(summary, reached.parameterCallsByKey.get(key));
|
|
19469
|
+
}
|
|
19470
|
+
}
|
|
19471
|
+
(0, import_behavioral_ir4.recordParameterGaps)(
|
|
19472
|
+
reached.parameterCallsByKey,
|
|
19473
|
+
summariesBySeed,
|
|
19474
|
+
reached.passedPositions
|
|
19475
|
+
);
|
|
19476
|
+
summaries.push(...reached.summaries);
|
|
19477
|
+
const dependencies = fileDependenciesOf(db, displayPathOf);
|
|
19478
|
+
(0, import_extractor9.stampModuleImports)(summaries, (file2) => dependencies.get(file2) ?? []);
|
|
19479
|
+
const idRoot = options.projectRoot ?? options.workspaceRoot;
|
|
19480
|
+
for (const summary of summaries) {
|
|
19481
|
+
const absoluteFile = options.workspaceRoot === void 0 ? summary.location.file : import_node_path4.default.resolve(options.workspaceRoot, summary.location.file);
|
|
19482
|
+
summary.identity.id = (0, import_behavioral_ir4.summaryIdFromParts)({
|
|
19483
|
+
workspace: void 0,
|
|
19484
|
+
file: idRoot === void 0 ? absoluteFile : import_node_path4.default.relative(idRoot, absoluteFile),
|
|
19485
|
+
name: summary.identity.name,
|
|
19486
|
+
exportPath: summary.identity.exportPath
|
|
19487
|
+
});
|
|
19488
|
+
}
|
|
19489
|
+
(0, import_behavioral_ir4.disambiguateSummaryIds)(summaries);
|
|
19490
|
+
(0, import_behavioral_ir4.linkCallsToSummaries)(summaries);
|
|
19491
|
+
await timer.timeAsync("cache.write", async () => {
|
|
19492
|
+
if (cacheDir === null || summaries.length === 0) {
|
|
19493
|
+
return;
|
|
19494
|
+
}
|
|
19495
|
+
try {
|
|
19496
|
+
await extractionCache.write(cacheInput, summaries);
|
|
19497
|
+
} catch {
|
|
19498
|
+
}
|
|
19499
|
+
});
|
|
19500
|
+
options.onExtractionReport?.(
|
|
19501
|
+
buildRubyExtractionReport({
|
|
19502
|
+
packs: options.packs,
|
|
19503
|
+
tallies,
|
|
19504
|
+
filesWalked: options.files.length,
|
|
19505
|
+
summaries
|
|
19506
|
+
})
|
|
19507
|
+
);
|
|
19508
|
+
options.onTiming?.(timer.report());
|
|
18086
19509
|
return { summaries, facts: db };
|
|
18087
19510
|
}
|
|
19511
|
+
function fileDependenciesOf(db, displayPathOf) {
|
|
19512
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
19513
|
+
for (const relation of ["rbRequires", "rbConstantFrom"]) {
|
|
19514
|
+
for (const [from, to] of db.facts(relation)) {
|
|
19515
|
+
if (typeof from !== "string" || typeof to !== "string") {
|
|
19516
|
+
continue;
|
|
19517
|
+
}
|
|
19518
|
+
const key = displayPathOf(from);
|
|
19519
|
+
const seen = byFile.get(key) ?? [];
|
|
19520
|
+
seen.push(displayPathOf(to));
|
|
19521
|
+
byFile.set(key, seen);
|
|
19522
|
+
}
|
|
19523
|
+
}
|
|
19524
|
+
return byFile;
|
|
19525
|
+
}
|
|
18088
19526
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["vendor", "node_modules", "tmp", ".git"]);
|
|
18089
19527
|
function findRubyFiles(root) {
|
|
18090
19528
|
const found = [];
|
|
@@ -18092,33 +19530,300 @@ function findRubyFiles(root) {
|
|
|
18092
19530
|
for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
18093
19531
|
if (entry.isDirectory()) {
|
|
18094
19532
|
if (!SKIPPED_DIRECTORIES.has(entry.name)) {
|
|
18095
|
-
walk(
|
|
19533
|
+
walk(import_node_path4.default.join(dir, entry.name));
|
|
18096
19534
|
}
|
|
18097
19535
|
continue;
|
|
18098
19536
|
}
|
|
18099
19537
|
if (entry.isFile() && entry.name.endsWith(".rb")) {
|
|
18100
|
-
found.push(
|
|
19538
|
+
found.push(import_node_path4.default.join(dir, entry.name));
|
|
18101
19539
|
}
|
|
18102
19540
|
}
|
|
18103
19541
|
};
|
|
18104
19542
|
walk(root);
|
|
18105
19543
|
return found.sort();
|
|
18106
19544
|
}
|
|
19545
|
+
|
|
19546
|
+
// src/facts/resolve.ts
|
|
19547
|
+
var import_datalog7 = require("@suss/datalog");
|
|
19548
|
+
var RUBY_RULES = [
|
|
19549
|
+
// `Loader.new` makes one of the class, which the shared rules already say
|
|
19550
|
+
// about calling a class. Ruby writes it as a method read off the constant
|
|
19551
|
+
// instead, so the read is what steps to the class here.
|
|
19552
|
+
(0, import_datalog7.rule)(
|
|
19553
|
+
"stepsTo",
|
|
19554
|
+
[(0, import_datalog7.variable)("x"), (0, import_datalog7.variable)("cls"), VALUE_STEP],
|
|
19555
|
+
[
|
|
19556
|
+
(0, import_datalog7.lit)("readsProperty", (0, import_datalog7.variable)("x"), (0, import_datalog7.variable)("o"), (0, import_datalog7.constant)("new")),
|
|
19557
|
+
(0, import_datalog7.lit)("comesTo", (0, import_datalog7.variable)("o"), (0, import_datalog7.variable)("cls")),
|
|
19558
|
+
(0, import_datalog7.lit)("objectValue", (0, import_datalog7.variable)("cls"))
|
|
19559
|
+
]
|
|
19560
|
+
)
|
|
19561
|
+
];
|
|
19562
|
+
var RESOLUTION_PROGRAM = (0, import_datalog7.deriveOnDemand)(
|
|
19563
|
+
[...RESOLUTION_RULES, ...RUBY_RULES, ...RESOLUTION_QUESTIONS],
|
|
19564
|
+
ANSWER_RELATIONS
|
|
19565
|
+
);
|
|
19566
|
+
|
|
19567
|
+
// src/facts/why.ts
|
|
19568
|
+
var WITNESS_RULES = [...RESOLUTION_RULES, ...RUBY_RULES];
|
|
19569
|
+
var METHOD_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
19570
|
+
function namedChildrenOf(node) {
|
|
19571
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
19572
|
+
}
|
|
19573
|
+
function enclosingMethod(node) {
|
|
19574
|
+
let current = node.parent;
|
|
19575
|
+
while (current !== null) {
|
|
19576
|
+
if (METHOD_TYPES2.has(current.type)) {
|
|
19577
|
+
return current;
|
|
19578
|
+
}
|
|
19579
|
+
current = current.parent;
|
|
19580
|
+
}
|
|
19581
|
+
return null;
|
|
19582
|
+
}
|
|
19583
|
+
function indexFile(file2, root, locations) {
|
|
19584
|
+
const walk = (node) => {
|
|
19585
|
+
locations.set(nodeId(file2, node), { file: file2, node });
|
|
19586
|
+
if (node.type === "constant") {
|
|
19587
|
+
locations.set(`${file2}#${node.text}`, { file: file2, node });
|
|
19588
|
+
}
|
|
19589
|
+
if (node.type === "assignment") {
|
|
19590
|
+
const left = field(node, "left");
|
|
19591
|
+
if (left !== null && left.type === "identifier") {
|
|
19592
|
+
locations.set(`${file2}#${left.text}`, { file: file2, node: left });
|
|
19593
|
+
}
|
|
19594
|
+
}
|
|
19595
|
+
for (const child of namedChildrenOf(node)) {
|
|
19596
|
+
walk(child);
|
|
19597
|
+
}
|
|
19598
|
+
};
|
|
19599
|
+
walk(root);
|
|
19600
|
+
}
|
|
19601
|
+
function displayNameOf(node) {
|
|
19602
|
+
if (node.type === "identifier" || node.type === "constant") {
|
|
19603
|
+
return node.text;
|
|
19604
|
+
}
|
|
19605
|
+
if (node.type === "method" || node.type === "singleton_method") {
|
|
19606
|
+
return field(node, "name")?.text ?? node.text;
|
|
19607
|
+
}
|
|
19608
|
+
if (node.type === "class" || node.type === "module") {
|
|
19609
|
+
return field(node, "name")?.text ?? node.text;
|
|
19610
|
+
}
|
|
19611
|
+
const text = node.text.split("\n")[0].trim();
|
|
19612
|
+
return text.length > 40 ? `${text.slice(0, 40)}...` : text;
|
|
19613
|
+
}
|
|
19614
|
+
function width(node) {
|
|
19615
|
+
return node.endIndex - node.startIndex;
|
|
19616
|
+
}
|
|
19617
|
+
var RubyWhySession = class {
|
|
19618
|
+
root;
|
|
19619
|
+
db = new import_datalog8.Database();
|
|
19620
|
+
locations = /* @__PURE__ */ new Map();
|
|
19621
|
+
trees = /* @__PURE__ */ new Map();
|
|
19622
|
+
constructor(options) {
|
|
19623
|
+
this.root = import_node_path5.default.resolve(options.dir);
|
|
19624
|
+
const constants = [];
|
|
19625
|
+
for (const file2 of findRubyFiles(this.root)) {
|
|
19626
|
+
const source = import_node_fs4.default.readFileSync(file2, "utf8");
|
|
19627
|
+
const root = parseRubySync(source).rootNode;
|
|
19628
|
+
this.trees.set(file2, root);
|
|
19629
|
+
emitValueFacts(this.db, file2, root);
|
|
19630
|
+
constants.push(collectFileConstants(file2, root));
|
|
19631
|
+
indexFile(file2, root, this.locations);
|
|
19632
|
+
}
|
|
19633
|
+
emitConstantBindings(this.db, constants);
|
|
19634
|
+
}
|
|
19635
|
+
/**
|
|
19636
|
+
* The smallest expression on `line` of `file` whose text is exactly
|
|
19637
|
+
* `text`, or null.
|
|
19638
|
+
*/
|
|
19639
|
+
findExpression(file2, line, text) {
|
|
19640
|
+
const root = this.rootOf(file2);
|
|
19641
|
+
if (root === null) {
|
|
19642
|
+
return null;
|
|
19643
|
+
}
|
|
19644
|
+
let found = null;
|
|
19645
|
+
const visit = (node) => {
|
|
19646
|
+
if (node.startPosition.row + 1 === line && node.text === text) {
|
|
19647
|
+
if (found === null || width(node) <= width(found)) {
|
|
19648
|
+
found = node;
|
|
19649
|
+
}
|
|
19650
|
+
}
|
|
19651
|
+
for (const child of namedChildrenOf(node)) {
|
|
19652
|
+
visit(child);
|
|
19653
|
+
}
|
|
19654
|
+
};
|
|
19655
|
+
visit(root);
|
|
19656
|
+
return found === null ? null : { file: this.pathOf(file2), node: found };
|
|
19657
|
+
}
|
|
19658
|
+
/**
|
|
19659
|
+
* The callee of the call written as `calleeText` between `startLine`
|
|
19660
|
+
* and `endLine` of `file`, or null.
|
|
19661
|
+
*/
|
|
19662
|
+
findCallee(file2, startLine, endLine, calleeText2) {
|
|
19663
|
+
const root = this.rootOf(file2);
|
|
19664
|
+
if (root === null) {
|
|
19665
|
+
return null;
|
|
19666
|
+
}
|
|
19667
|
+
let found = null;
|
|
19668
|
+
const visit = (node) => {
|
|
19669
|
+
if (node.type === "call") {
|
|
19670
|
+
const line = node.startPosition.row + 1;
|
|
19671
|
+
const callee = field(node, "method");
|
|
19672
|
+
if (line >= startLine && line <= endLine && callee !== null && callee.text === calleeText2) {
|
|
19673
|
+
found = callee;
|
|
19674
|
+
}
|
|
19675
|
+
}
|
|
19676
|
+
for (const child of namedChildrenOf(node)) {
|
|
19677
|
+
visit(child);
|
|
19678
|
+
}
|
|
19679
|
+
};
|
|
19680
|
+
visit(root);
|
|
19681
|
+
return found === null ? null : { file: this.pathOf(file2), node: found };
|
|
19682
|
+
}
|
|
19683
|
+
/**
|
|
19684
|
+
* Why `value` resolves to what it does: the witness proof, flattened
|
|
19685
|
+
* to the chain and rendered. Null when the value does not resolve,
|
|
19686
|
+
* which the caller says in its own words.
|
|
19687
|
+
*/
|
|
19688
|
+
explain(value, options = {}) {
|
|
19689
|
+
return explainResolvedKey({
|
|
19690
|
+
db: this.db,
|
|
19691
|
+
rules: WITNESS_RULES,
|
|
19692
|
+
key: readKey(value.file, value.node, enclosingMethod(value.node)),
|
|
19693
|
+
locate: (key) => this.locate(key),
|
|
19694
|
+
displayPath: (key) => this.displayPath(key),
|
|
19695
|
+
...options
|
|
19696
|
+
});
|
|
19697
|
+
}
|
|
19698
|
+
locate(key) {
|
|
19699
|
+
const located = this.locations.get(key);
|
|
19700
|
+
if (located === void 0) {
|
|
19701
|
+
return null;
|
|
19702
|
+
}
|
|
19703
|
+
return {
|
|
19704
|
+
name: displayNameOf(located.node),
|
|
19705
|
+
file: this.displayPath(located.file),
|
|
19706
|
+
line: located.node.startPosition.row + 1
|
|
19707
|
+
};
|
|
19708
|
+
}
|
|
19709
|
+
rootOf(file2) {
|
|
19710
|
+
return this.trees.get(this.pathOf(file2)) ?? null;
|
|
19711
|
+
}
|
|
19712
|
+
pathOf(file2) {
|
|
19713
|
+
return import_node_path5.default.isAbsolute(file2) ? file2 : import_node_path5.default.resolve(this.root, file2);
|
|
19714
|
+
}
|
|
19715
|
+
/** A file path said relative to the root, or as given when it is outside the root. */
|
|
19716
|
+
displayPath(key) {
|
|
19717
|
+
if (key.startsWith(this.root)) {
|
|
19718
|
+
return import_node_path5.default.relative(this.root, key);
|
|
19719
|
+
}
|
|
19720
|
+
return key;
|
|
19721
|
+
}
|
|
19722
|
+
};
|
|
19723
|
+
|
|
19724
|
+
// src/stubEvidence.ts
|
|
19725
|
+
var import_node_fs5 = __toESM(require("fs"), 1);
|
|
19726
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
19727
|
+
var import_extractor10 = require("@suss/extractor");
|
|
19728
|
+
function bareLetters(name) {
|
|
19729
|
+
return name.replaceAll(/[-_]/g, "").toLowerCase();
|
|
19730
|
+
}
|
|
19731
|
+
function isSpelledFrom(qualifiedName, packageName) {
|
|
19732
|
+
const wanted = bareLetters(packageName);
|
|
19733
|
+
const constants = qualifiedName.split("::");
|
|
19734
|
+
return constants.some(
|
|
19735
|
+
(_, end) => bareLetters(constants.slice(0, end + 1).join("")) === wanted
|
|
19736
|
+
);
|
|
19737
|
+
}
|
|
19738
|
+
function isPackageRequire(target, packageName) {
|
|
19739
|
+
const bundlerName = packageName.replaceAll("-", "_");
|
|
19740
|
+
return target === bundlerName || target.startsWith(`${bundlerName}/`) || target === packageName || target.startsWith(`${packageName}/`);
|
|
19741
|
+
}
|
|
19742
|
+
function requireTarget(node) {
|
|
19743
|
+
if (node.type !== "call" || field(node, "receiver") !== null || field(node, "method")?.text !== "require" && field(node, "method")?.text !== "require_relative") {
|
|
19744
|
+
return null;
|
|
19745
|
+
}
|
|
19746
|
+
const { positional } = readCallArgs(field(node, "arguments"));
|
|
19747
|
+
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
19748
|
+
}
|
|
19749
|
+
function requireSitesIn(root, file2) {
|
|
19750
|
+
const found = [];
|
|
19751
|
+
(0, import_extractor10.walkDescendants)(root, null, {
|
|
19752
|
+
at: (node) => {
|
|
19753
|
+
const target = requireTarget(node);
|
|
19754
|
+
if (target !== null) {
|
|
19755
|
+
found.push({ target, file: file2, line: rangeOf(node).start });
|
|
19756
|
+
}
|
|
19757
|
+
},
|
|
19758
|
+
into: (node) => node.type === "argument_list" ? import_extractor10.SKIP_CHILDREN : null
|
|
19759
|
+
});
|
|
19760
|
+
return found;
|
|
19761
|
+
}
|
|
19762
|
+
function extendsSitesIn(root, file2, packageName) {
|
|
19763
|
+
const found = [];
|
|
19764
|
+
walkClasses(root, (info) => {
|
|
19765
|
+
if (info.superclassQualifiedName !== null && isSpelledFrom(info.superclassQualifiedName, packageName)) {
|
|
19766
|
+
found.push({
|
|
19767
|
+
className: info.qualifiedName,
|
|
19768
|
+
superclassName: info.superclassQualifiedName,
|
|
19769
|
+
file: file2,
|
|
19770
|
+
line: rangeOf(info.node).start
|
|
19771
|
+
});
|
|
19772
|
+
}
|
|
19773
|
+
});
|
|
19774
|
+
return found;
|
|
19775
|
+
}
|
|
19776
|
+
async function rubyStubEvidence(options) {
|
|
19777
|
+
const requires = [];
|
|
19778
|
+
const extendsSites = [];
|
|
19779
|
+
for (const file2 of findRubyFiles(options.directory)) {
|
|
19780
|
+
const source = import_node_fs5.default.readFileSync(file2, "utf8");
|
|
19781
|
+
const tree = await parseRuby(source);
|
|
19782
|
+
const displayPath = import_node_path6.default.relative(options.directory, file2);
|
|
19783
|
+
for (const site of requireSitesIn(tree.rootNode, displayPath)) {
|
|
19784
|
+
if (isPackageRequire(site.target, options.packageName)) {
|
|
19785
|
+
requires.push(site);
|
|
19786
|
+
}
|
|
19787
|
+
}
|
|
19788
|
+
extendsSites.push(
|
|
19789
|
+
...extendsSitesIn(tree.rootNode, displayPath, options.packageName)
|
|
19790
|
+
);
|
|
19791
|
+
}
|
|
19792
|
+
return { requires, extendsSites };
|
|
19793
|
+
}
|
|
18107
19794
|
// Annotate the CommonJS export names for ESM import in node:
|
|
18108
19795
|
0 && (module.exports = {
|
|
19796
|
+
ADAPTER_VERSION,
|
|
19797
|
+
RubyWhySession,
|
|
18109
19798
|
ancestryOf,
|
|
19799
|
+
bodyStatements,
|
|
19800
|
+
booleanLiteralValue,
|
|
19801
|
+
collectFileConstants,
|
|
18110
19802
|
createFileCache,
|
|
18111
19803
|
discoverUnits,
|
|
19804
|
+
emitConstantBindings,
|
|
18112
19805
|
emitEntryFact,
|
|
19806
|
+
emitValueFacts,
|
|
18113
19807
|
extractRubyProject,
|
|
19808
|
+
field,
|
|
18114
19809
|
findRubyFiles,
|
|
18115
19810
|
graphqlTypeNameFromQualified,
|
|
19811
|
+
hashKeySymbolName,
|
|
18116
19812
|
inheritedStatements,
|
|
18117
19813
|
methodInAncestry,
|
|
19814
|
+
nodeId,
|
|
18118
19815
|
parseRuby,
|
|
19816
|
+
parseRubySync,
|
|
19817
|
+
preloadRubyGrammar,
|
|
18119
19818
|
qualifyConstantRef,
|
|
18120
19819
|
reachDefinition,
|
|
19820
|
+
readCallArgs,
|
|
19821
|
+
readKey,
|
|
18121
19822
|
resolveConstantFile,
|
|
19823
|
+
rubyStubEvidence,
|
|
19824
|
+
runStatements,
|
|
19825
|
+
stringLiteralValue,
|
|
19826
|
+
symbolValue,
|
|
18122
19827
|
typeShapeFromNode,
|
|
18123
19828
|
underscoreConstantPath,
|
|
18124
19829
|
unitKey,
|