@suss/adapter-ruby 0.31.2 → 0.33.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 +5 -4
- package/dist/index.cjs +1050 -187
- package/dist/index.d.cts +127 -5
- package/dist/index.d.ts +127 -5
- package/dist/index.js +1039 -178
- package/package.json +8 -7
package/dist/index.js
CHANGED
|
@@ -62,7 +62,25 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
62
62
|
"module",
|
|
63
63
|
"singleton_class"
|
|
64
64
|
]);
|
|
65
|
+
var PROGRAM_TYPE = "program";
|
|
66
|
+
var MODULE_SCOPE_STOPS = /* @__PURE__ */ new Set([
|
|
67
|
+
...OWN_BODY_TYPES,
|
|
68
|
+
"block",
|
|
69
|
+
"do_block"
|
|
70
|
+
]);
|
|
65
71
|
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
72
|
+
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
73
|
+
var LAMBDA_TYPE = "lambda";
|
|
74
|
+
function enclosingDefinition(node) {
|
|
75
|
+
let current = node.parent;
|
|
76
|
+
while (current !== null) {
|
|
77
|
+
if (METHOD_TYPES.has(current.type) || current.type === LAMBDA_TYPE) {
|
|
78
|
+
return current;
|
|
79
|
+
}
|
|
80
|
+
current = current.parent;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
66
84
|
var INCLUDE_CALL = "include";
|
|
67
85
|
var PREPEND_CALL = "prepend";
|
|
68
86
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
@@ -330,7 +348,7 @@ import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
|
|
|
330
348
|
import { hole as hole2, literalOf as literalOf3, piecesOf } from "@suss/values";
|
|
331
349
|
|
|
332
350
|
// src/values/evaluator.ts
|
|
333
|
-
import { nodeOfKey } from "@suss/resolution";
|
|
351
|
+
import { nodeOfKey, writtenValuesByKey } from "@suss/resolution";
|
|
334
352
|
import { Evaluator, force, literalOf, text } from "@suss/values";
|
|
335
353
|
|
|
336
354
|
// src/facts/resolve.ts
|
|
@@ -390,8 +408,14 @@ var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
|
390
408
|
function resolveValues(db, keys) {
|
|
391
409
|
askResolution(db, keys, "wanted", RUBY_PROGRAM);
|
|
392
410
|
}
|
|
411
|
+
function resolveEnvObjects(db, objects) {
|
|
412
|
+
askResolution(db, objects, "wantedEnvObject", RUBY_PROGRAM);
|
|
413
|
+
}
|
|
393
414
|
function resolvedFunctions(db, key) {
|
|
394
|
-
return
|
|
415
|
+
return [
|
|
416
|
+
...db.lookup("wantedResolves", 0, key),
|
|
417
|
+
...db.lookup("wantedGivesBack", 0, key)
|
|
418
|
+
].map((row) => String(row[1]));
|
|
395
419
|
}
|
|
396
420
|
function writtenValueOf(db, key) {
|
|
397
421
|
resolveValues(db, [key]);
|
|
@@ -491,12 +515,15 @@ var BINDS_A_NAME = {
|
|
|
491
515
|
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
492
516
|
)
|
|
493
517
|
};
|
|
494
|
-
|
|
518
|
+
var DESCENDS_EVERYWHERE = /* @__PURE__ */ new Set();
|
|
519
|
+
function localNamesIn(definitionNode, stops = DESCENDS_EVERYWHERE) {
|
|
495
520
|
const names = /* @__PURE__ */ new Set();
|
|
496
521
|
const visit = (node) => {
|
|
497
522
|
BINDS_A_NAME[node.type]?.(node, names);
|
|
498
523
|
for (const child of bodyStatements(node)) {
|
|
499
|
-
|
|
524
|
+
if (!stops.has(child.type)) {
|
|
525
|
+
visit(child);
|
|
526
|
+
}
|
|
500
527
|
}
|
|
501
528
|
};
|
|
502
529
|
visit(definitionNode);
|
|
@@ -509,11 +536,12 @@ function spellsAName(node) {
|
|
|
509
536
|
}
|
|
510
537
|
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
511
538
|
}
|
|
539
|
+
var KEYWORD_LITERALS = /* @__PURE__ */ new Set(["__FILE__", "__LINE__", "__ENCODING__"]);
|
|
512
540
|
function isBareMethodCall(node, locals) {
|
|
513
541
|
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
514
542
|
return false;
|
|
515
543
|
}
|
|
516
|
-
return !spellsAName(node);
|
|
544
|
+
return !KEYWORD_LITERALS.has(node.text) && !spellsAName(node);
|
|
517
545
|
}
|
|
518
546
|
|
|
519
547
|
// src/facts/locals.ts
|
|
@@ -664,25 +692,46 @@ function namesOfFile(tree) {
|
|
|
664
692
|
cache.file ??= scopeNames(/* @__PURE__ */ new Set(), tree.rootNode);
|
|
665
693
|
return cache.file;
|
|
666
694
|
}
|
|
695
|
+
function scopesAround(enclosing) {
|
|
696
|
+
const chain = [];
|
|
697
|
+
let current = enclosing;
|
|
698
|
+
while (current !== null && current.type === LAMBDA_TYPE) {
|
|
699
|
+
chain.push(current);
|
|
700
|
+
current = enclosingDefinition(current);
|
|
701
|
+
}
|
|
702
|
+
chain.push(current);
|
|
703
|
+
return chain;
|
|
704
|
+
}
|
|
705
|
+
var namesOfScope = (scope, node) => scope === null ? namesOfFile(node.tree) : namesOfMethod(scope);
|
|
667
706
|
function ownerOfName(node, name, enclosing) {
|
|
668
707
|
if (node.type === "constant") {
|
|
669
708
|
return null;
|
|
670
709
|
}
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
710
|
+
for (const scope of scopesAround(enclosing)) {
|
|
711
|
+
const names = namesOfScope(scope, node);
|
|
712
|
+
if (names.blockParams.has(name)) {
|
|
713
|
+
const block = blockDeclaring(node, name);
|
|
714
|
+
if (block !== null) {
|
|
715
|
+
return block;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (scope !== null && names.locals.has(name)) {
|
|
719
|
+
return scope;
|
|
676
720
|
}
|
|
677
721
|
}
|
|
678
|
-
return
|
|
722
|
+
return null;
|
|
679
723
|
}
|
|
680
724
|
function isLocalName(node, name, enclosing) {
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
725
|
+
for (const scope of scopesAround(enclosing)) {
|
|
726
|
+
const names = namesOfScope(scope, node);
|
|
727
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
if (names.locals.has(name)) {
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
684
733
|
}
|
|
685
|
-
return
|
|
734
|
+
return false;
|
|
686
735
|
}
|
|
687
736
|
function blockDeclaring(node, name) {
|
|
688
737
|
let current = node.parent;
|
|
@@ -873,19 +922,33 @@ function calleeKeyOf(filePath, node, enclosing) {
|
|
|
873
922
|
}
|
|
874
923
|
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
875
924
|
}
|
|
925
|
+
function invokedKeyOf(filePath, node, enclosing) {
|
|
926
|
+
const invoked = invokedValueOf(node);
|
|
927
|
+
return invoked === null ? null : readKey(filePath, invoked, enclosing);
|
|
928
|
+
}
|
|
876
929
|
function emitCall(emitter, call) {
|
|
877
|
-
|
|
930
|
+
const callKey = nodeId(emitter.filePath, call);
|
|
931
|
+
add(emitter, "writtenValue", callKey);
|
|
932
|
+
const invoked = invokedValueOf(call);
|
|
933
|
+
if (invoked !== null) {
|
|
934
|
+
add(emitter, "call", callKey, valueKey(emitter, invoked));
|
|
935
|
+
}
|
|
936
|
+
if (!emitMessageSent(emitter, call, callKey) && invoked === null) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
emitCallArguments(emitter, call, callKey);
|
|
940
|
+
}
|
|
941
|
+
function emitMessageSent(emitter, call, callKey) {
|
|
878
942
|
const method = call.type === "identifier" ? call : field(call, "method");
|
|
879
943
|
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
880
944
|
if (method === null || calleeKey === null) {
|
|
881
|
-
return;
|
|
945
|
+
return false;
|
|
882
946
|
}
|
|
883
|
-
const receiver = field(call, "receiver");
|
|
884
|
-
const callKey = nodeId(emitter.filePath, call);
|
|
885
947
|
add(emitter, "call", callKey, calleeKey);
|
|
886
948
|
if (!emitter.insideMethod) {
|
|
887
949
|
add(emitter, "callOutsideMethod", callKey);
|
|
888
950
|
}
|
|
951
|
+
const receiver = field(call, "receiver");
|
|
889
952
|
if (receiver !== null) {
|
|
890
953
|
add(
|
|
891
954
|
emitter,
|
|
@@ -897,6 +960,21 @@ function emitCall(emitter, call) {
|
|
|
897
960
|
} else if (emitter.selfKey !== null) {
|
|
898
961
|
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
899
962
|
}
|
|
963
|
+
return true;
|
|
964
|
+
}
|
|
965
|
+
var INVOKE_METHOD = "call";
|
|
966
|
+
function invokedValueOf(call) {
|
|
967
|
+
if (call.type !== "call") {
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
const receiver = field(call, "receiver");
|
|
971
|
+
if (receiver === null) {
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
const method = field(call, "method");
|
|
975
|
+
return method === null || method.text === INVOKE_METHOD ? receiver : null;
|
|
976
|
+
}
|
|
977
|
+
function emitCallArguments(emitter, call, callKey) {
|
|
900
978
|
const args = field(call, "arguments");
|
|
901
979
|
let position = 0;
|
|
902
980
|
for (const argument of args === null ? [] : children(args)) {
|
|
@@ -1016,12 +1094,58 @@ function walkExpressions(node, emitter, visit, turns = []) {
|
|
|
1016
1094
|
);
|
|
1017
1095
|
}
|
|
1018
1096
|
}
|
|
1097
|
+
var WRITTEN_KEY_TYPES = /* @__PURE__ */ new Set([
|
|
1098
|
+
"string",
|
|
1099
|
+
"integer",
|
|
1100
|
+
"simple_symbol",
|
|
1101
|
+
"bare_string",
|
|
1102
|
+
"bare_symbol"
|
|
1103
|
+
]);
|
|
1104
|
+
function emitKeyedRead(emitter, site, container, key) {
|
|
1105
|
+
if (WRITTEN_KEY_TYPES.has(key.type)) {
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
add(
|
|
1109
|
+
emitter,
|
|
1110
|
+
"readsKeyed",
|
|
1111
|
+
nodeId(emitter.filePath, site),
|
|
1112
|
+
valueKey(emitter, container),
|
|
1113
|
+
valueKey(emitter, key)
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
function emitKeyedElement(emitter, node) {
|
|
1117
|
+
const object = field(node, "object");
|
|
1118
|
+
if (object === null || isWriteTarget(node)) {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
const index = node.namedChildren.find(
|
|
1122
|
+
(child) => child !== null && child.id !== object.id
|
|
1123
|
+
);
|
|
1124
|
+
if (index !== void 0) {
|
|
1125
|
+
emitKeyedRead(emitter, node, object, index);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
function emitKeyedFetch(emitter, call) {
|
|
1129
|
+
const receiver = field(call, "receiver");
|
|
1130
|
+
if (receiver === null || field(call, "method")?.text !== "fetch") {
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
const { positional } = readCallArgs(field(call, "arguments"));
|
|
1134
|
+
const key = positional[0];
|
|
1135
|
+
if (key !== void 0) {
|
|
1136
|
+
emitKeyedRead(emitter, call, receiver, key);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1019
1139
|
function emitExpressionFacts(emitter, node) {
|
|
1020
1140
|
emitDynamicDefinition(emitter, node, []);
|
|
1021
1141
|
walkExpressions(node, emitter, (child, turns) => {
|
|
1022
1142
|
emitDynamicDefinition(emitter, child, turns);
|
|
1023
1143
|
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
1024
1144
|
emitCall(emitter, child);
|
|
1145
|
+
emitKeyedFetch(emitter, child);
|
|
1146
|
+
}
|
|
1147
|
+
if (child.type === "element_reference") {
|
|
1148
|
+
emitKeyedElement(emitter, child);
|
|
1025
1149
|
}
|
|
1026
1150
|
if (ARRAY_TYPES.has(child.type)) {
|
|
1027
1151
|
emitArray(emitter, child);
|
|
@@ -1145,6 +1269,25 @@ function assignedValueOf(node) {
|
|
|
1145
1269
|
}
|
|
1146
1270
|
return field(node, "right");
|
|
1147
1271
|
}
|
|
1272
|
+
function emitLambdasIn(emitter, body) {
|
|
1273
|
+
for (const child of children(body)) {
|
|
1274
|
+
if (child.type === LAMBDA_TYPE) {
|
|
1275
|
+
emitMethodFacts(emitter, child);
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
emitLambdasIn(emitter, child);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function definitionBody(definition) {
|
|
1285
|
+
const body = field(definition, "body");
|
|
1286
|
+
if (body === null || definition.type !== LAMBDA_TYPE) {
|
|
1287
|
+
return body;
|
|
1288
|
+
}
|
|
1289
|
+
return field(body, "body");
|
|
1290
|
+
}
|
|
1148
1291
|
function emitMethodFacts(emitter, method) {
|
|
1149
1292
|
const funcKey = nodeId(emitter.filePath, method);
|
|
1150
1293
|
add(emitter, "func", funcKey);
|
|
@@ -1159,7 +1302,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1159
1302
|
position += 1;
|
|
1160
1303
|
}
|
|
1161
1304
|
const inside = { ...emitter, enclosing: method };
|
|
1162
|
-
const body =
|
|
1305
|
+
const body = definitionBody(method);
|
|
1163
1306
|
if (body === null) {
|
|
1164
1307
|
return funcKey;
|
|
1165
1308
|
}
|
|
@@ -1191,6 +1334,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1191
1334
|
}
|
|
1192
1335
|
emitExpressionFacts(inside, body);
|
|
1193
1336
|
emitScopeWrites(inside, method, body);
|
|
1337
|
+
emitLambdasIn(inside, body);
|
|
1194
1338
|
return funcKey;
|
|
1195
1339
|
}
|
|
1196
1340
|
function isConstruction(value) {
|
|
@@ -1270,7 +1414,6 @@ function emitCandidates(emitter, key, group) {
|
|
|
1270
1414
|
add(emitter, "writesAllStated", key);
|
|
1271
1415
|
}
|
|
1272
1416
|
}
|
|
1273
|
-
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1274
1417
|
var INITIALIZE_METHOD = "initialize";
|
|
1275
1418
|
function receiverKeyOf(filePath, method, classKey) {
|
|
1276
1419
|
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
@@ -1318,7 +1461,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1318
1461
|
left.text,
|
|
1319
1462
|
valueKey(emitter, right)
|
|
1320
1463
|
);
|
|
1321
|
-
emitExpressionFacts(
|
|
1464
|
+
emitExpressionFacts(within, statement);
|
|
1322
1465
|
}
|
|
1323
1466
|
continue;
|
|
1324
1467
|
}
|
|
@@ -1351,12 +1494,15 @@ function emitClassFacts(emitter, cls) {
|
|
|
1351
1494
|
add(emitter, "initializes", classKey, funcKey);
|
|
1352
1495
|
}
|
|
1353
1496
|
}
|
|
1354
|
-
const
|
|
1355
|
-
if (
|
|
1356
|
-
emitInstanceStores(within, funcKey,
|
|
1497
|
+
const methodBody2 = field(statement, "body");
|
|
1498
|
+
if (methodBody2 !== null) {
|
|
1499
|
+
emitInstanceStores(within, funcKey, methodBody2, stored);
|
|
1357
1500
|
}
|
|
1358
1501
|
}
|
|
1359
1502
|
emitInstanceWrites(within, classKey, collected);
|
|
1503
|
+
if (body !== null) {
|
|
1504
|
+
emitLambdasIn(within, body);
|
|
1505
|
+
}
|
|
1360
1506
|
return classKey;
|
|
1361
1507
|
}
|
|
1362
1508
|
function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
@@ -1392,6 +1538,7 @@ function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
|
1392
1538
|
walk(root);
|
|
1393
1539
|
emitExpressionFacts(emitter, root);
|
|
1394
1540
|
emitScopeWrites(emitter, null, root);
|
|
1541
|
+
emitLambdasIn(emitter, root);
|
|
1395
1542
|
}
|
|
1396
1543
|
|
|
1397
1544
|
// src/values/lowering.ts
|
|
@@ -2399,7 +2546,6 @@ var evaluators = /* @__PURE__ */ new WeakMap();
|
|
|
2399
2546
|
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2400
2547
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2401
2548
|
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2402
|
-
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2403
2549
|
function bindEvaluator(db, nodes) {
|
|
2404
2550
|
for (const entry of nodes.files) {
|
|
2405
2551
|
filesByTree.set(entry.root.tree, entry.file);
|
|
@@ -2412,6 +2558,24 @@ function writtenNodeOf(node, db, site) {
|
|
|
2412
2558
|
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2413
2559
|
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2414
2560
|
}
|
|
2561
|
+
function askWrittenValues(nodes, db) {
|
|
2562
|
+
if (db === void 0 || !contexts.has(db)) {
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2566
|
+
for (const node of nodes) {
|
|
2567
|
+
const file = filesByTree.get(node.tree);
|
|
2568
|
+
if (file !== void 0) {
|
|
2569
|
+
keys.add(readKey(file, node, enclosingDefinition(node)));
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
if (keys.size === 0) {
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2575
|
+
const asked = [...keys];
|
|
2576
|
+
resolveValues(db, asked);
|
|
2577
|
+
writtenValuesByKey(db, asked, (more) => resolveValues(db, more));
|
|
2578
|
+
}
|
|
2415
2579
|
function evaluatedValue(node, db, bindings, site) {
|
|
2416
2580
|
const evaluator = evaluatorFor(node, db);
|
|
2417
2581
|
if (bindings === void 0) {
|
|
@@ -2453,7 +2617,7 @@ function stringValueOf(node, db, bindings) {
|
|
|
2453
2617
|
function methodDefinitionsIn(file, root) {
|
|
2454
2618
|
const found = /* @__PURE__ */ new Map();
|
|
2455
2619
|
const visit = (node) => {
|
|
2456
|
-
if (
|
|
2620
|
+
if (METHOD_TYPES.has(node.type)) {
|
|
2457
2621
|
found.set(nodeId(file, node), node);
|
|
2458
2622
|
}
|
|
2459
2623
|
for (const child of node.namedChildren) {
|
|
@@ -2478,16 +2642,6 @@ function evaluatorFor(node, db) {
|
|
|
2478
2642
|
}
|
|
2479
2643
|
return local;
|
|
2480
2644
|
}
|
|
2481
|
-
function enclosingMethod(node) {
|
|
2482
|
-
let current = node.parent;
|
|
2483
|
-
while (current !== null) {
|
|
2484
|
-
if (METHOD_TYPES3.has(current.type)) {
|
|
2485
|
-
return current;
|
|
2486
|
-
}
|
|
2487
|
-
current = current.parent;
|
|
2488
|
-
}
|
|
2489
|
-
return null;
|
|
2490
|
-
}
|
|
2491
2645
|
function contextOver(db, nodes) {
|
|
2492
2646
|
const filesByRoot = /* @__PURE__ */ new Map();
|
|
2493
2647
|
const rootsByFile = /* @__PURE__ */ new Map();
|
|
@@ -2502,7 +2656,7 @@ function contextOver(db, nodes) {
|
|
|
2502
2656
|
if (file === null) {
|
|
2503
2657
|
return null;
|
|
2504
2658
|
}
|
|
2505
|
-
const key = readKey(file, node,
|
|
2659
|
+
const key = readKey(file, node, enclosingDefinition(node));
|
|
2506
2660
|
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2507
2661
|
return answer === null ? null : nodeOfKey(rootsByFile, answer);
|
|
2508
2662
|
},
|
|
@@ -2512,7 +2666,7 @@ function contextOver(db, nodes) {
|
|
|
2512
2666
|
if (file === null || method === null) {
|
|
2513
2667
|
return null;
|
|
2514
2668
|
}
|
|
2515
|
-
const key = field(call, "receiver") === null ? readKey(file, method,
|
|
2669
|
+
const key = field(call, "receiver") === null ? readKey(file, method, enclosingDefinition(call)) : nodeId(file, method);
|
|
2516
2670
|
resolveValues(db, [key]);
|
|
2517
2671
|
const resolved = resolvedFunctions(db, key);
|
|
2518
2672
|
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
@@ -2664,8 +2818,8 @@ function qualifyConstantRef(node, nesting) {
|
|
|
2664
2818
|
return compoundName(node);
|
|
2665
2819
|
}
|
|
2666
2820
|
if (node.type === "constant") {
|
|
2667
|
-
const
|
|
2668
|
-
return
|
|
2821
|
+
const innermost2 = nesting[0] ?? null;
|
|
2822
|
+
return innermost2 === null ? node.text : `${innermost2}::${node.text}`;
|
|
2669
2823
|
}
|
|
2670
2824
|
return null;
|
|
2671
2825
|
}
|
|
@@ -2722,9 +2876,9 @@ function ownIdentity(nameNode, outerNesting) {
|
|
|
2722
2876
|
return { qualifiedName, bodyNesting: [qualifiedName, ...outerNesting] };
|
|
2723
2877
|
}
|
|
2724
2878
|
function definedName(nameNode, outerNesting) {
|
|
2725
|
-
const
|
|
2726
|
-
if (nameNode.type === "scope_resolution" &&
|
|
2727
|
-
return `${
|
|
2879
|
+
const innermost2 = outerNesting[0] ?? null;
|
|
2880
|
+
if (nameNode.type === "scope_resolution" && innermost2 !== null && !nameNode.text.startsWith("::")) {
|
|
2881
|
+
return `${innermost2}::${nameNode.text}`;
|
|
2728
2882
|
}
|
|
2729
2883
|
return qualifyConstantRef(nameNode, outerNesting);
|
|
2730
2884
|
}
|
|
@@ -3352,24 +3506,38 @@ function isCall(node, locals, inherited) {
|
|
|
3352
3506
|
}
|
|
3353
3507
|
return isBareMethodCall(node, locals) && !RAISE_NAMES2.has(node.text) && !inherited.has(node.text);
|
|
3354
3508
|
}
|
|
3355
|
-
function collectCalls(node,
|
|
3509
|
+
function collectCalls(node, read, inherited, found) {
|
|
3356
3510
|
for (const child of children2(node)) {
|
|
3357
|
-
if (
|
|
3511
|
+
if (read.stops.has(child.type)) {
|
|
3358
3512
|
continue;
|
|
3359
3513
|
}
|
|
3360
|
-
if (isCall(child, locals, inherited)) {
|
|
3514
|
+
if (isCall(child, read.locals, inherited)) {
|
|
3361
3515
|
found.push(child);
|
|
3362
3516
|
}
|
|
3363
|
-
collectCalls(child,
|
|
3517
|
+
collectCalls(child, read, inherited, found);
|
|
3364
3518
|
}
|
|
3365
3519
|
return found;
|
|
3366
3520
|
}
|
|
3367
|
-
function
|
|
3521
|
+
function methodBody(definitionNode) {
|
|
3368
3522
|
const body = field(definitionNode, "body");
|
|
3369
3523
|
if (body === null) {
|
|
3370
|
-
return
|
|
3524
|
+
return null;
|
|
3371
3525
|
}
|
|
3372
|
-
return
|
|
3526
|
+
return {
|
|
3527
|
+
body,
|
|
3528
|
+
locals: localNamesIn(definitionNode),
|
|
3529
|
+
stops: OWN_BODY_TYPES
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
function moduleScopeBody(root) {
|
|
3533
|
+
return {
|
|
3534
|
+
body: root,
|
|
3535
|
+
locals: localNamesIn(root, MODULE_SCOPE_STOPS),
|
|
3536
|
+
stops: MODULE_SCOPE_STOPS
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
function bodyCalls(read, inherited = NO_INHERITED_METHODS) {
|
|
3540
|
+
return collectCalls(read.body, read, inherited, []);
|
|
3373
3541
|
}
|
|
3374
3542
|
function calleeText(call) {
|
|
3375
3543
|
const receiver = field(call, "receiver");
|
|
@@ -3413,14 +3581,23 @@ function enclosingStatement(call, body) {
|
|
|
3413
3581
|
return current;
|
|
3414
3582
|
}
|
|
3415
3583
|
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3416
|
-
const
|
|
3417
|
-
if (
|
|
3584
|
+
const read = methodBody(definitionNode);
|
|
3585
|
+
if (read === null) {
|
|
3418
3586
|
return [];
|
|
3419
3587
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3588
|
+
return effectsOfBody(read, inherited, keepsArglessCall, facts);
|
|
3589
|
+
}
|
|
3590
|
+
function moduleScopeInvocationEffects(root, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3591
|
+
return effectsOfBody(
|
|
3592
|
+
moduleScopeBody(root),
|
|
3593
|
+
inherited,
|
|
3594
|
+
keepsArglessCall,
|
|
3595
|
+
facts
|
|
3423
3596
|
);
|
|
3597
|
+
}
|
|
3598
|
+
function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
3599
|
+
const body = read.body;
|
|
3600
|
+
const calls = callsReported(bodyCalls(read, inherited), keepsArglessCall);
|
|
3424
3601
|
if (calls.length === 0) {
|
|
3425
3602
|
return [];
|
|
3426
3603
|
}
|
|
@@ -3832,6 +4009,57 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3832
4009
|
}
|
|
3833
4010
|
return units;
|
|
3834
4011
|
}
|
|
4012
|
+
function askClientCallReads(root, patterns, options) {
|
|
4013
|
+
const facts = options.facts;
|
|
4014
|
+
if (facts === void 0 || patterns.length === 0) {
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
const calls = [];
|
|
4018
|
+
for (const method of methodDefinitions(root)) {
|
|
4019
|
+
calls.push(...callsUnder(method));
|
|
4020
|
+
}
|
|
4021
|
+
if (patterns.some((pattern) => (pattern.receiverBuilders ?? []).length > 0)) {
|
|
4022
|
+
askWrittenValues(receiversOf(calls), facts);
|
|
4023
|
+
}
|
|
4024
|
+
askWrittenValues(requestArguments(calls, patterns, options), facts);
|
|
4025
|
+
}
|
|
4026
|
+
function receiversOf(calls) {
|
|
4027
|
+
const found = [];
|
|
4028
|
+
for (const call of calls) {
|
|
4029
|
+
const receiver = field(call, "receiver");
|
|
4030
|
+
if (receiver !== null) {
|
|
4031
|
+
found.push(receiver);
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
return found;
|
|
4035
|
+
}
|
|
4036
|
+
function requestArguments(calls, patterns, options) {
|
|
4037
|
+
const found = [];
|
|
4038
|
+
for (const call of calls) {
|
|
4039
|
+
const receiver = field(call, "receiver");
|
|
4040
|
+
const called = field(call, "method")?.text;
|
|
4041
|
+
if (receiver === null || called === void 0) {
|
|
4042
|
+
continue;
|
|
4043
|
+
}
|
|
4044
|
+
for (const pattern of patterns) {
|
|
4045
|
+
if (!isLibraryReceiver(receiver, pattern, options)) {
|
|
4046
|
+
continue;
|
|
4047
|
+
}
|
|
4048
|
+
const argument = requestArgument(call, called, pattern);
|
|
4049
|
+
if (argument !== void 0) {
|
|
4050
|
+
found.push(argument);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
return found;
|
|
4055
|
+
}
|
|
4056
|
+
function requestArgument(call, called, pattern) {
|
|
4057
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
4058
|
+
if (pattern.verbMethodNames[called] !== void 0) {
|
|
4059
|
+
return urlNodeIn(args, pattern);
|
|
4060
|
+
}
|
|
4061
|
+
return pattern.requestObject?.attribute === called ? args.positional[0] : void 0;
|
|
4062
|
+
}
|
|
3835
4063
|
function methodDefinitions(node, found = []) {
|
|
3836
4064
|
for (const child of node.namedChildren) {
|
|
3837
4065
|
if (child === null) {
|
|
@@ -3956,9 +4184,11 @@ function builderCallBehind(receiver, pattern, facts) {
|
|
|
3956
4184
|
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3957
4185
|
}
|
|
3958
4186
|
function urlIn(args, pattern, options, site) {
|
|
4187
|
+
return pathAt(urlNodeIn(args, pattern), options, site);
|
|
4188
|
+
}
|
|
4189
|
+
function urlNodeIn(args, pattern) {
|
|
3959
4190
|
const keyword = pattern.url.keyword;
|
|
3960
|
-
|
|
3961
|
-
return pathAt(written, options, site);
|
|
4191
|
+
return (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3962
4192
|
}
|
|
3963
4193
|
function pathAt(written, options, site) {
|
|
3964
4194
|
if (written === void 0) {
|
|
@@ -4035,19 +4265,161 @@ function returnBranch(range, effects) {
|
|
|
4035
4265
|
import { runtimeConfigBinding } from "@suss/behavioral-ir";
|
|
4036
4266
|
import { SKIP_CHILDREN as SKIP_CHILDREN2, walkDescendants as walkDescendants2 } from "@suss/extractor";
|
|
4037
4267
|
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
4268
|
+
var ENV_DEFAULTED = "rbEnvDefaulted";
|
|
4038
4269
|
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
4039
|
-
function envReadEffects(root) {
|
|
4040
|
-
const
|
|
4270
|
+
function envReadEffects(root, facts) {
|
|
4271
|
+
const helpers = helperFactsOf(facts);
|
|
4272
|
+
const slots = [];
|
|
4041
4273
|
walkDescendants2(root, null, {
|
|
4042
4274
|
at: (node) => {
|
|
4043
4275
|
const read = envReadAt(node);
|
|
4044
4276
|
if (read !== null) {
|
|
4045
|
-
|
|
4277
|
+
slots.push({ read });
|
|
4278
|
+
return;
|
|
4279
|
+
}
|
|
4280
|
+
if (helpers !== null && node.type === "call") {
|
|
4281
|
+
slots.push({ call: node });
|
|
4046
4282
|
}
|
|
4047
4283
|
},
|
|
4048
4284
|
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? SKIP_CHILDREN2 : null
|
|
4049
4285
|
});
|
|
4050
|
-
|
|
4286
|
+
const calls = slots.flatMap((slot) => "call" in slot ? [slot.call] : []);
|
|
4287
|
+
const throughHelpers = helpers === null ? new NodeMap() : helperReads(calls, helpers);
|
|
4288
|
+
return slots.flatMap((slot) => {
|
|
4289
|
+
if ("read" in slot) {
|
|
4290
|
+
return [configReadEffect(slot.read)];
|
|
4291
|
+
}
|
|
4292
|
+
return (throughHelpers.get(slot.call) ?? []).map(configReadEffect);
|
|
4293
|
+
});
|
|
4294
|
+
}
|
|
4295
|
+
function helperFactsOf(facts) {
|
|
4296
|
+
if (facts === void 0) {
|
|
4297
|
+
return null;
|
|
4298
|
+
}
|
|
4299
|
+
const named2 = namedParameters(facts.db);
|
|
4300
|
+
if (named2.size === 0) {
|
|
4301
|
+
return null;
|
|
4302
|
+
}
|
|
4303
|
+
return { ...facts, named: named2 };
|
|
4304
|
+
}
|
|
4305
|
+
var namedByDb = /* @__PURE__ */ new WeakMap();
|
|
4306
|
+
function namedParameters(db) {
|
|
4307
|
+
const memo = namedByDb.get(db);
|
|
4308
|
+
if (memo !== void 0) {
|
|
4309
|
+
return memo;
|
|
4310
|
+
}
|
|
4311
|
+
const named2 = /* @__PURE__ */ new Map();
|
|
4312
|
+
namedByDb.set(db, named2);
|
|
4313
|
+
const objects = db.facts("environmentObject").map((row) => String(row[0]));
|
|
4314
|
+
if (objects.length === 0) {
|
|
4315
|
+
return named2;
|
|
4316
|
+
}
|
|
4317
|
+
resolveEnvObjects(db, objects);
|
|
4318
|
+
for (const row of db.facts("wantedParamNamesEnv")) {
|
|
4319
|
+
const found = named2.get(String(row[0]));
|
|
4320
|
+
if (found === void 0) {
|
|
4321
|
+
named2.set(String(row[0]), [String(row[1])]);
|
|
4322
|
+
continue;
|
|
4323
|
+
}
|
|
4324
|
+
found.push(String(row[1]));
|
|
4325
|
+
}
|
|
4326
|
+
return named2;
|
|
4327
|
+
}
|
|
4328
|
+
function emitEnvFacts(db, file, root) {
|
|
4329
|
+
walkDescendants2(root, null, {
|
|
4330
|
+
at: (node) => {
|
|
4331
|
+
if (isEnv(node) && handsOnward(node)) {
|
|
4332
|
+
db.add("environmentObject", [
|
|
4333
|
+
readKey(file, node, enclosingDefinition(node))
|
|
4334
|
+
]);
|
|
4335
|
+
}
|
|
4336
|
+
const site = envSiteAt(node);
|
|
4337
|
+
if (site !== null && site.defaulted && stringLiteralValue(site.name) === null) {
|
|
4338
|
+
db.add(ENV_DEFAULTED, [nodeId(file, site.read)]);
|
|
4339
|
+
}
|
|
4340
|
+
},
|
|
4341
|
+
into: () => null
|
|
4342
|
+
});
|
|
4343
|
+
}
|
|
4344
|
+
function handsOnward(node) {
|
|
4345
|
+
const parent = node.parent;
|
|
4346
|
+
if (parent === null) {
|
|
4347
|
+
return true;
|
|
4348
|
+
}
|
|
4349
|
+
if (parent.type === "element_reference" || parent.type === "call") {
|
|
4350
|
+
const site = envSiteAt(parent);
|
|
4351
|
+
return site === null || stringLiteralValue(site.name) === null;
|
|
4352
|
+
}
|
|
4353
|
+
return true;
|
|
4354
|
+
}
|
|
4355
|
+
function helperReads(calls, facts) {
|
|
4356
|
+
const { db, file, named: named2 } = facts;
|
|
4357
|
+
const callees = new NodeMap();
|
|
4358
|
+
for (const call of calls) {
|
|
4359
|
+
const enclosing = enclosingDefinition(call);
|
|
4360
|
+
const keys = [
|
|
4361
|
+
calleeKeyOf(file, call, enclosing),
|
|
4362
|
+
invokedKeyOf(file, call, enclosing)
|
|
4363
|
+
].filter((key) => key !== null);
|
|
4364
|
+
if (keys.length > 0) {
|
|
4365
|
+
callees.set(call, keys);
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
const reads = new NodeMap();
|
|
4369
|
+
if (callees.size === 0) {
|
|
4370
|
+
return reads;
|
|
4371
|
+
}
|
|
4372
|
+
resolveValues(
|
|
4373
|
+
db,
|
|
4374
|
+
[...callees].flatMap(([, keys]) => keys)
|
|
4375
|
+
);
|
|
4376
|
+
for (const [call, callee] of callees) {
|
|
4377
|
+
const args = namingArguments(db, named2, call, callee);
|
|
4378
|
+
if (args.length === 0) {
|
|
4379
|
+
continue;
|
|
4380
|
+
}
|
|
4381
|
+
const found = readsAtCall(db, call, args);
|
|
4382
|
+
if (found.length > 0) {
|
|
4383
|
+
reads.set(call, found);
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
return reads;
|
|
4387
|
+
}
|
|
4388
|
+
function namingArguments(db, named2, call, callees) {
|
|
4389
|
+
const { positional, keyword } = readCallArgs(field(call, "arguments"));
|
|
4390
|
+
const found = [];
|
|
4391
|
+
for (const func of callees.flatMap((key) => resolvedFunctions(db, key))) {
|
|
4392
|
+
for (const row of db.lookup("paramOf", 0, func)) {
|
|
4393
|
+
const sites = named2.get(String(row[2]));
|
|
4394
|
+
const argument = positional[Number(row[1])];
|
|
4395
|
+
if (sites !== void 0 && argument !== void 0) {
|
|
4396
|
+
found.push({ sites, argument });
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
for (const row of db.lookup("paramNamed", 0, func)) {
|
|
4400
|
+
const sites = named2.get(String(row[2]));
|
|
4401
|
+
const argument = keyword[String(row[1])];
|
|
4402
|
+
if (sites !== void 0 && argument !== void 0) {
|
|
4403
|
+
found.push({ sites, argument });
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
return found;
|
|
4408
|
+
}
|
|
4409
|
+
function readsAtCall(db, call, args) {
|
|
4410
|
+
const defaultedAtCall = isDefaultedAt(call);
|
|
4411
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4412
|
+
for (const { sites, argument } of args) {
|
|
4413
|
+
const name = stringLiteralValue(argument) ?? stringValueOf(argument, db);
|
|
4414
|
+
if (name === null) {
|
|
4415
|
+
continue;
|
|
4416
|
+
}
|
|
4417
|
+
for (const site of sites) {
|
|
4418
|
+
const defaulted = defaultedAtCall || db.lookup(ENV_DEFAULTED, 0, site).length > 0;
|
|
4419
|
+
byName.set(name, (byName.get(name) ?? true) && defaulted);
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
return [...byName].map(([name, defaulted]) => ({ name, defaulted }));
|
|
4051
4423
|
}
|
|
4052
4424
|
function configReadEffect(read) {
|
|
4053
4425
|
return {
|
|
@@ -4066,15 +4438,23 @@ function configReadEffect(read) {
|
|
|
4066
4438
|
};
|
|
4067
4439
|
}
|
|
4068
4440
|
function envReadAt(node) {
|
|
4441
|
+
const site = envSiteAt(node);
|
|
4442
|
+
if (site === null) {
|
|
4443
|
+
return null;
|
|
4444
|
+
}
|
|
4445
|
+
const name = stringLiteralValue(site.name);
|
|
4446
|
+
return name === null ? null : { name, defaulted: site.defaulted };
|
|
4447
|
+
}
|
|
4448
|
+
function envSiteAt(node) {
|
|
4069
4449
|
if (node.type === "element_reference") {
|
|
4070
|
-
return
|
|
4450
|
+
return elementSite(node);
|
|
4071
4451
|
}
|
|
4072
4452
|
if (node.type === "call") {
|
|
4073
|
-
return
|
|
4453
|
+
return fetchSite(node);
|
|
4074
4454
|
}
|
|
4075
4455
|
return null;
|
|
4076
4456
|
}
|
|
4077
|
-
function
|
|
4457
|
+
function elementSite(node) {
|
|
4078
4458
|
const object = field(node, "object");
|
|
4079
4459
|
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
4080
4460
|
return null;
|
|
@@ -4082,24 +4462,23 @@ function elementRead(node) {
|
|
|
4082
4462
|
const index = node.namedChildren.find(
|
|
4083
4463
|
(child) => child !== null && child.id !== object.id
|
|
4084
4464
|
);
|
|
4085
|
-
|
|
4086
|
-
if (name === null) {
|
|
4465
|
+
if (index === void 0) {
|
|
4087
4466
|
return null;
|
|
4088
4467
|
}
|
|
4089
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
4468
|
+
return { read: node, name: index, defaulted: isDefaultedAt(node) };
|
|
4090
4469
|
}
|
|
4091
|
-
function
|
|
4470
|
+
function fetchSite(node) {
|
|
4092
4471
|
const receiver = field(node, "receiver");
|
|
4093
4472
|
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
4094
4473
|
return null;
|
|
4095
4474
|
}
|
|
4096
4475
|
const { positional } = readCallArgs(field(node, "arguments"));
|
|
4097
|
-
const name = positional[0]
|
|
4098
|
-
if (name ===
|
|
4476
|
+
const name = positional[0];
|
|
4477
|
+
if (name === void 0) {
|
|
4099
4478
|
return null;
|
|
4100
4479
|
}
|
|
4101
4480
|
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
4102
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
4481
|
+
return { read: node, name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
4103
4482
|
}
|
|
4104
4483
|
function isEnv(node) {
|
|
4105
4484
|
if (node.type === "constant") {
|
|
@@ -4273,6 +4652,9 @@ function methodNamesOf(args, filter, facts) {
|
|
|
4273
4652
|
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
4274
4653
|
return value === null ? [] : [value];
|
|
4275
4654
|
}
|
|
4655
|
+
return symbolArgumentNames(args, facts);
|
|
4656
|
+
}
|
|
4657
|
+
function symbolArgumentNames(args, facts) {
|
|
4276
4658
|
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
4277
4659
|
}
|
|
4278
4660
|
function skipsOf(statement, filterName, facts) {
|
|
@@ -4311,19 +4693,315 @@ function actionsUnder(node, facts) {
|
|
|
4311
4693
|
}
|
|
4312
4694
|
|
|
4313
4695
|
// src/storage.ts
|
|
4314
|
-
import { storageBinding } from "@suss/ir-core";
|
|
4696
|
+
import { storageBinding as storageBinding2 } from "@suss/ir-core";
|
|
4697
|
+
import { askResolution as askResolution3 } from "@suss/resolution";
|
|
4698
|
+
|
|
4699
|
+
// src/baseClass.ts
|
|
4315
4700
|
import { askResolution as askResolution2 } from "@suss/resolution";
|
|
4316
|
-
function
|
|
4317
|
-
|
|
4701
|
+
function classBehind(facts, file, constant2) {
|
|
4702
|
+
const key = constant2.type === "constant" ? `${file}#${constant2.text}` : nodeId(file, constant2);
|
|
4703
|
+
const bound = new Set(
|
|
4704
|
+
facts.lookup("binds", 0, key).map((row) => String(row[1]))
|
|
4705
|
+
);
|
|
4706
|
+
return bound.size === 1 ? [...bound][0] : void 0;
|
|
4318
4707
|
}
|
|
4708
|
+
function reachesBase(facts, classKey, bases) {
|
|
4709
|
+
askResolution2(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
|
|
4710
|
+
return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
|
|
4711
|
+
}
|
|
4712
|
+
function enclosingClassKey(node, file) {
|
|
4713
|
+
let current = node.parent;
|
|
4714
|
+
while (current !== null) {
|
|
4715
|
+
if (NESTING_TYPES.has(current.type)) {
|
|
4716
|
+
return nodeId(file, current);
|
|
4717
|
+
}
|
|
4718
|
+
current = current.parent;
|
|
4719
|
+
}
|
|
4720
|
+
return null;
|
|
4721
|
+
}
|
|
4722
|
+
|
|
4723
|
+
// src/loaders.ts
|
|
4319
4724
|
function receiverOf(node) {
|
|
4320
4725
|
return node.type === "call" ? field(node, "receiver") : null;
|
|
4321
4726
|
}
|
|
4322
4727
|
function methodOf(node) {
|
|
4323
4728
|
return field(node, "method")?.text ?? "";
|
|
4324
4729
|
}
|
|
4730
|
+
function isLoader(node, loader) {
|
|
4731
|
+
if (node === null) {
|
|
4732
|
+
return false;
|
|
4733
|
+
}
|
|
4734
|
+
if (node.type === "identifier") {
|
|
4735
|
+
return node.text === loader.loader;
|
|
4736
|
+
}
|
|
4737
|
+
return node.type === "call" && receiverOf(node) === null && methodOf(node) === loader.loader;
|
|
4738
|
+
}
|
|
4739
|
+
function loaderPick(call, loader) {
|
|
4740
|
+
const method = methodOf(call);
|
|
4741
|
+
const receiver = receiverOf(call);
|
|
4742
|
+
if (receiver === null) {
|
|
4743
|
+
return loader.shortcuts.includes(method) ? call : null;
|
|
4744
|
+
}
|
|
4745
|
+
if (!loader.reads.includes(method) || receiver.type !== "call") {
|
|
4746
|
+
return null;
|
|
4747
|
+
}
|
|
4748
|
+
const picks = methodOf(receiver) === loader.pick && isLoader(receiverOf(receiver), loader);
|
|
4749
|
+
return picks ? receiver : null;
|
|
4750
|
+
}
|
|
4751
|
+
function pickedSource(call, loader) {
|
|
4752
|
+
const source = loader.source;
|
|
4753
|
+
const pick = source === void 0 ? null : loaderPick(call, loader);
|
|
4754
|
+
if (source === void 0 || pick === null || methodOf(pick) !== loader.pick) {
|
|
4755
|
+
return null;
|
|
4756
|
+
}
|
|
4757
|
+
const args = field(pick, "arguments");
|
|
4758
|
+
const given = args === null ? void 0 : args.namedChildren[source.at];
|
|
4759
|
+
return given?.type === "constant" || given?.type === "scope_resolution" ? given : null;
|
|
4760
|
+
}
|
|
4761
|
+
|
|
4762
|
+
// src/rawSql.ts
|
|
4763
|
+
import { storageBinding } from "@suss/ir-core";
|
|
4764
|
+
import { readSqlAccess, splitQualifiedTable, sqlFromParts } from "@suss/sql";
|
|
4765
|
+
import { piecesOf as piecesOf2 } from "@suss/values";
|
|
4766
|
+
var MOST_HOPS = 8;
|
|
4767
|
+
var NOWHERE = { scope: null, container: null };
|
|
4768
|
+
function rawSqlEffects(call, options) {
|
|
4769
|
+
for (const pattern of options.patterns) {
|
|
4770
|
+
const effects = patternEffects(call, pattern, options);
|
|
4771
|
+
if (effects.length > 0) {
|
|
4772
|
+
return effects;
|
|
4773
|
+
}
|
|
4774
|
+
}
|
|
4775
|
+
return [];
|
|
4776
|
+
}
|
|
4777
|
+
function patternEffects(call, pattern, options) {
|
|
4778
|
+
const receiver = field(call, "receiver");
|
|
4779
|
+
if (receiver === null) {
|
|
4780
|
+
return [];
|
|
4781
|
+
}
|
|
4782
|
+
const address = addressBehind(receiver, pattern, options, 0);
|
|
4783
|
+
if (address === null) {
|
|
4784
|
+
return [];
|
|
4785
|
+
}
|
|
4786
|
+
const method = field(call, "method")?.text ?? "";
|
|
4787
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
4788
|
+
const place = pattern.statements?.[method];
|
|
4789
|
+
if (place !== void 0) {
|
|
4790
|
+
const statement = statementAt(
|
|
4791
|
+
args,
|
|
4792
|
+
place,
|
|
4793
|
+
options.facts,
|
|
4794
|
+
pattern.bindPlaceholder
|
|
4795
|
+
);
|
|
4796
|
+
return statementEffects(call, statement, address, pattern);
|
|
4797
|
+
}
|
|
4798
|
+
return rowCallEffects(call, args, address, pattern, options);
|
|
4799
|
+
}
|
|
4800
|
+
function statementEffects(call, statement, address, store) {
|
|
4801
|
+
if (statement === null) {
|
|
4802
|
+
return [];
|
|
4803
|
+
}
|
|
4804
|
+
return readSqlAccess(statement, { dialect: store.dialect }).map(
|
|
4805
|
+
(access) => effectOf(
|
|
4806
|
+
call,
|
|
4807
|
+
store,
|
|
4808
|
+
{
|
|
4809
|
+
scope: innermost(access.qualifier) ?? address.scope,
|
|
4810
|
+
container: access.table
|
|
4811
|
+
},
|
|
4812
|
+
{
|
|
4813
|
+
kind: access.kind,
|
|
4814
|
+
fields: access.fields,
|
|
4815
|
+
selector: access.selector
|
|
4816
|
+
}
|
|
4817
|
+
)
|
|
4818
|
+
);
|
|
4819
|
+
}
|
|
4820
|
+
function innermost(qualifier) {
|
|
4821
|
+
return qualifier[qualifier.length - 1] ?? null;
|
|
4822
|
+
}
|
|
4823
|
+
function rowCallEffects(call, args, address, pattern, options) {
|
|
4824
|
+
const method = field(call, "method")?.text ?? "";
|
|
4825
|
+
const rowCall = pattern.rowCalls?.[method];
|
|
4826
|
+
if (rowCall === void 0) {
|
|
4827
|
+
return [];
|
|
4828
|
+
}
|
|
4829
|
+
const given = rowCall.container === void 0 ? null : namedTable(stringAt(args, rowCall.container, options.facts));
|
|
4830
|
+
const reached = {
|
|
4831
|
+
scope: given?.scope ?? address.scope,
|
|
4832
|
+
container: given?.container ?? address.container
|
|
4833
|
+
};
|
|
4834
|
+
if (reached.container === null) {
|
|
4835
|
+
return [];
|
|
4836
|
+
}
|
|
4837
|
+
return [
|
|
4838
|
+
effectOf(call, pattern, reached, {
|
|
4839
|
+
kind: rowCall.kind,
|
|
4840
|
+
fields: [],
|
|
4841
|
+
selector: []
|
|
4842
|
+
})
|
|
4843
|
+
];
|
|
4844
|
+
}
|
|
4845
|
+
function namedTable(name) {
|
|
4846
|
+
const split = name === null ? null : splitQualifiedTable(name);
|
|
4847
|
+
return split === null ? null : { scope: innermost(split.qualifier), container: split.table };
|
|
4848
|
+
}
|
|
4849
|
+
function effectOf(call, store, address, access) {
|
|
4850
|
+
const operation = field(call, "method")?.text ?? "";
|
|
4851
|
+
return {
|
|
4852
|
+
type: "interaction",
|
|
4853
|
+
binding: storageBinding({
|
|
4854
|
+
recognition: "ruby-raw-sql",
|
|
4855
|
+
storageSystem: store.storageSystem,
|
|
4856
|
+
scope: address.scope ?? store.scope ?? "default",
|
|
4857
|
+
container: address.container
|
|
4858
|
+
}),
|
|
4859
|
+
callee: call.text,
|
|
4860
|
+
interaction: {
|
|
4861
|
+
class: "storage-access",
|
|
4862
|
+
kind: access.kind,
|
|
4863
|
+
fields: access.fields,
|
|
4864
|
+
operation,
|
|
4865
|
+
...access.selector.length > 0 ? { selector: access.selector } : {}
|
|
4866
|
+
}
|
|
4867
|
+
};
|
|
4868
|
+
}
|
|
4869
|
+
function addressBehind(receiver, pattern, options, hops) {
|
|
4870
|
+
if (hops > MOST_HOPS) {
|
|
4871
|
+
return null;
|
|
4872
|
+
}
|
|
4873
|
+
if (receiver.type !== "call") {
|
|
4874
|
+
const written = writtenNodeOf(receiver, options.facts);
|
|
4875
|
+
if (written !== null && written.id !== receiver.id) {
|
|
4876
|
+
return addressBehind(written, pattern, options, hops + 1);
|
|
4877
|
+
}
|
|
4878
|
+
return receiver.type === "identifier" && buildsOnOwnClass(receiver, receiver.text, pattern, options) ? NOWHERE : null;
|
|
4879
|
+
}
|
|
4880
|
+
const method = field(receiver, "method")?.text ?? "";
|
|
4881
|
+
const inner = field(receiver, "receiver");
|
|
4882
|
+
if (inner === null) {
|
|
4883
|
+
return buildsOnOwnClass(receiver, method, pattern, options) ? NOWHERE : null;
|
|
4884
|
+
}
|
|
4885
|
+
if (namesConstant2(inner, pattern.constantName) || namesSubclass(inner, pattern, options)) {
|
|
4886
|
+
return pattern.clientBuilders.includes(method) ? NOWHERE : null;
|
|
4887
|
+
}
|
|
4888
|
+
const addressing = pattern.addressing?.[method];
|
|
4889
|
+
if (addressing === void 0 || inner === null) {
|
|
4890
|
+
return null;
|
|
4891
|
+
}
|
|
4892
|
+
const behind = addressBehind(inner, pattern, options, hops + 1);
|
|
4893
|
+
if (behind === null) {
|
|
4894
|
+
return null;
|
|
4895
|
+
}
|
|
4896
|
+
const named2 = stringAt(
|
|
4897
|
+
readCallArgs(field(receiver, "arguments")),
|
|
4898
|
+
addressing,
|
|
4899
|
+
options.facts
|
|
4900
|
+
);
|
|
4901
|
+
if (addressing.says === "scope") {
|
|
4902
|
+
return { scope: named2 ?? behind.scope, container: behind.container };
|
|
4903
|
+
}
|
|
4904
|
+
const table = namedTable(named2);
|
|
4905
|
+
return {
|
|
4906
|
+
scope: table?.scope ?? behind.scope,
|
|
4907
|
+
container: table?.container ?? behind.container
|
|
4908
|
+
};
|
|
4909
|
+
}
|
|
4910
|
+
function namesConstant2(receiver, constantName2) {
|
|
4911
|
+
if (receiver.type === "constant") {
|
|
4912
|
+
return receiver.text === constantName2;
|
|
4913
|
+
}
|
|
4914
|
+
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
4915
|
+
}
|
|
4916
|
+
function namesSubclass(receiver, pattern, options) {
|
|
4917
|
+
const bases = pattern.baseClasses ?? [];
|
|
4918
|
+
const facts = options.facts;
|
|
4919
|
+
const file = options.file;
|
|
4920
|
+
if (bases.length === 0 || facts === void 0 || file === void 0 || receiver.type !== "constant" && receiver.type !== "scope_resolution") {
|
|
4921
|
+
return false;
|
|
4922
|
+
}
|
|
4923
|
+
const classKey = classBehind(facts, file, receiver);
|
|
4924
|
+
return classKey !== void 0 && reachesBase(facts, classKey, bases);
|
|
4925
|
+
}
|
|
4926
|
+
function buildsOnOwnClass(node, method, pattern, options) {
|
|
4927
|
+
const bases = pattern.baseClasses ?? [];
|
|
4928
|
+
const facts = options.facts;
|
|
4929
|
+
const file = options.file;
|
|
4930
|
+
if (bases.length === 0 || facts === void 0 || file === void 0 || !pattern.clientBuilders.includes(method)) {
|
|
4931
|
+
return false;
|
|
4932
|
+
}
|
|
4933
|
+
const classKey = enclosingClassKey(node, file);
|
|
4934
|
+
return classKey !== null && reachesBase(facts, classKey, bases);
|
|
4935
|
+
}
|
|
4936
|
+
function argumentAt(args, place) {
|
|
4937
|
+
const keyword = place.keyword;
|
|
4938
|
+
return (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[place.at];
|
|
4939
|
+
}
|
|
4940
|
+
function stringAt(args, place, facts) {
|
|
4941
|
+
const written = argumentAt(args, place);
|
|
4942
|
+
if (written === void 0) {
|
|
4943
|
+
return null;
|
|
4944
|
+
}
|
|
4945
|
+
const pieces = piecesOf2(evaluatedValue(written, facts));
|
|
4946
|
+
const only = pieces.length === 1 ? pieces[0] : void 0;
|
|
4947
|
+
if (only === void 0 || only.kind !== "text" || only.options.length !== 1) {
|
|
4948
|
+
return null;
|
|
4949
|
+
}
|
|
4950
|
+
return only.options[0] ?? null;
|
|
4951
|
+
}
|
|
4952
|
+
function statementAt(args, place, facts, bindPlaceholder) {
|
|
4953
|
+
const written = argumentAt(args, place);
|
|
4954
|
+
if (written === void 0) {
|
|
4955
|
+
return null;
|
|
4956
|
+
}
|
|
4957
|
+
const parts = literalParts(statementValue(evaluatedValue(written, facts)));
|
|
4958
|
+
if (parts === null) {
|
|
4959
|
+
return null;
|
|
4960
|
+
}
|
|
4961
|
+
const statement = sqlFromParts(bindsSplit(parts, bindPlaceholder));
|
|
4962
|
+
return statement.trim() === "" ? null : statement;
|
|
4963
|
+
}
|
|
4964
|
+
function bindsSplit(parts, bindPlaceholder) {
|
|
4965
|
+
if (bindPlaceholder === void 0 || bindPlaceholder === "") {
|
|
4966
|
+
return [...parts];
|
|
4967
|
+
}
|
|
4968
|
+
return parts.flatMap((part) => part.split(bindPlaceholder));
|
|
4969
|
+
}
|
|
4970
|
+
function statementValue(value) {
|
|
4971
|
+
if (value.kind !== "sequence") {
|
|
4972
|
+
return value;
|
|
4973
|
+
}
|
|
4974
|
+
const first = value.items[0];
|
|
4975
|
+
return first === void 0 ? value : first.value;
|
|
4976
|
+
}
|
|
4977
|
+
function literalParts(value) {
|
|
4978
|
+
if (value.kind !== "string") {
|
|
4979
|
+
return null;
|
|
4980
|
+
}
|
|
4981
|
+
const parts = [""];
|
|
4982
|
+
for (const piece of value.pieces) {
|
|
4983
|
+
const only = piece.kind === "text" ? piece.options : [];
|
|
4984
|
+
if (only.length === 1) {
|
|
4985
|
+
parts[parts.length - 1] += only[0] ?? "";
|
|
4986
|
+
continue;
|
|
4987
|
+
}
|
|
4988
|
+
parts.push("");
|
|
4989
|
+
}
|
|
4990
|
+
return parts;
|
|
4991
|
+
}
|
|
4992
|
+
|
|
4993
|
+
// src/storage.ts
|
|
4994
|
+
function children3(node) {
|
|
4995
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
4996
|
+
}
|
|
4997
|
+
function receiverOf2(node) {
|
|
4998
|
+
return node.type === "call" ? field(node, "receiver") : null;
|
|
4999
|
+
}
|
|
5000
|
+
function methodOf2(node) {
|
|
5001
|
+
return field(node, "method")?.text ?? "";
|
|
5002
|
+
}
|
|
4325
5003
|
function rootConstant(node) {
|
|
4326
|
-
const receiver =
|
|
5004
|
+
const receiver = receiverOf2(node);
|
|
4327
5005
|
if (receiver === null) {
|
|
4328
5006
|
return null;
|
|
4329
5007
|
}
|
|
@@ -4338,17 +5016,6 @@ function isConstant(node) {
|
|
|
4338
5016
|
function constantName(constant2) {
|
|
4339
5017
|
return constant2.type === "constant" ? constant2.text : compoundName(constant2);
|
|
4340
5018
|
}
|
|
4341
|
-
function reachesBase(facts, classKey, bases) {
|
|
4342
|
-
askResolution2(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
|
|
4343
|
-
return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
|
|
4344
|
-
}
|
|
4345
|
-
function classBehind(facts, file, constant2) {
|
|
4346
|
-
const key = constant2.type === "constant" ? `${file}#${constant2.text}` : nodeId(file, constant2);
|
|
4347
|
-
const bound = new Set(
|
|
4348
|
-
facts.lookup("binds", 0, key).map((row) => String(row[1]))
|
|
4349
|
-
);
|
|
4350
|
-
return bound.size === 1 ? [...bound][0] : void 0;
|
|
4351
|
-
}
|
|
4352
5019
|
function modelPattern(constant2, file, options) {
|
|
4353
5020
|
const classKey = classBehind(options.facts, file, constant2);
|
|
4354
5021
|
if (classKey === void 0) {
|
|
@@ -4363,7 +5030,7 @@ function chainLinks(call) {
|
|
|
4363
5030
|
let current = call;
|
|
4364
5031
|
while (current !== null && current.type === "call") {
|
|
4365
5032
|
links.push(current);
|
|
4366
|
-
current =
|
|
5033
|
+
current = receiverOf2(current);
|
|
4367
5034
|
}
|
|
4368
5035
|
return links;
|
|
4369
5036
|
}
|
|
@@ -4376,9 +5043,17 @@ function kindOfCall(pattern, method) {
|
|
|
4376
5043
|
}
|
|
4377
5044
|
return void 0;
|
|
4378
5045
|
}
|
|
5046
|
+
function libraryCallOf(pattern, method) {
|
|
5047
|
+
const place = pattern.statements?.[method];
|
|
5048
|
+
if (place !== void 0) {
|
|
5049
|
+
return { how: "statement", place };
|
|
5050
|
+
}
|
|
5051
|
+
const kind = kindOfCall(pattern, method);
|
|
5052
|
+
return kind === void 0 ? void 0 : { how: "rows", kind };
|
|
5053
|
+
}
|
|
4379
5054
|
function someLibraryDefines(options, method) {
|
|
4380
5055
|
return options.patterns.some(
|
|
4381
|
-
(pattern) =>
|
|
5056
|
+
(pattern) => libraryCallOf(pattern, method) !== void 0
|
|
4382
5057
|
);
|
|
4383
5058
|
}
|
|
4384
5059
|
function argumentsOf2(call) {
|
|
@@ -4413,7 +5088,7 @@ function hasPositionalArgument(call, facts) {
|
|
|
4413
5088
|
);
|
|
4414
5089
|
}
|
|
4415
5090
|
function columnsAskedFor(call, pattern, facts) {
|
|
4416
|
-
if (!(pattern.columnArguments ?? []).includes(
|
|
5091
|
+
if (!(pattern.columnArguments ?? []).includes(methodOf2(call))) {
|
|
4417
5092
|
return [];
|
|
4418
5093
|
}
|
|
4419
5094
|
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
@@ -4423,14 +5098,14 @@ function primaryKeySelector(call, pattern, facts) {
|
|
|
4423
5098
|
if (byPrimaryKey === void 0) {
|
|
4424
5099
|
return [];
|
|
4425
5100
|
}
|
|
4426
|
-
const picks = byPrimaryKey.methods.includes(
|
|
5101
|
+
const picks = byPrimaryKey.methods.includes(methodOf2(call)) && hasPositionalArgument(call, facts);
|
|
4427
5102
|
return picks ? [byPrimaryKey.column] : [];
|
|
4428
5103
|
}
|
|
4429
5104
|
function selectorOf(call, pattern, facts) {
|
|
4430
5105
|
const picked = [];
|
|
4431
5106
|
for (const link of chainLinks(call)) {
|
|
4432
5107
|
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4433
|
-
if (pattern.reads.includes(
|
|
5108
|
+
if (pattern.reads.includes(methodOf2(link))) {
|
|
4434
5109
|
picked.push(...keywordKeys(link, facts));
|
|
4435
5110
|
}
|
|
4436
5111
|
}
|
|
@@ -4447,10 +5122,10 @@ function fieldsOf(call, pattern, kind, facts) {
|
|
|
4447
5122
|
];
|
|
4448
5123
|
}
|
|
4449
5124
|
function storageEffect(call, container, pattern, kind, selector, fields) {
|
|
4450
|
-
const operation =
|
|
5125
|
+
const operation = methodOf2(call);
|
|
4451
5126
|
return {
|
|
4452
5127
|
type: "interaction",
|
|
4453
|
-
binding:
|
|
5128
|
+
binding: storageBinding2({
|
|
4454
5129
|
recognition: "ruby-storage",
|
|
4455
5130
|
storageSystem: pattern.storageSystem,
|
|
4456
5131
|
scope: "default",
|
|
@@ -4467,7 +5142,7 @@ function storageEffect(call, container, pattern, kind, selector, fields) {
|
|
|
4467
5142
|
};
|
|
4468
5143
|
}
|
|
4469
5144
|
function classSettledOn(facts, key) {
|
|
4470
|
-
|
|
5145
|
+
askResolution3(facts, [key], "wanted", RUBY_PROGRAM);
|
|
4471
5146
|
const objects = new Set(
|
|
4472
5147
|
facts.lookup("wantedObjectOf", 0, key).map((row) => String(row[1]))
|
|
4473
5148
|
);
|
|
@@ -4499,7 +5174,7 @@ function receiverClass(call, file, options, enclosing) {
|
|
|
4499
5174
|
if (constant2 !== null) {
|
|
4500
5175
|
return constantReceiver(constant2, file, options.facts);
|
|
4501
5176
|
}
|
|
4502
|
-
const receiver =
|
|
5177
|
+
const receiver = receiverOf2(call);
|
|
4503
5178
|
if (receiver === null) {
|
|
4504
5179
|
return void 0;
|
|
4505
5180
|
}
|
|
@@ -4507,60 +5182,91 @@ function receiverClass(call, file, options, enclosing) {
|
|
|
4507
5182
|
}
|
|
4508
5183
|
function libraryCallIn(call, options) {
|
|
4509
5184
|
return chainLinks(call).find(
|
|
4510
|
-
(link) => someLibraryDefines(options,
|
|
5185
|
+
(link) => someLibraryDefines(options, methodOf2(link))
|
|
4511
5186
|
) ?? null;
|
|
4512
5187
|
}
|
|
5188
|
+
function modelStatementEffects(call, place, pattern, options) {
|
|
5189
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
5190
|
+
return statementEffects(
|
|
5191
|
+
call,
|
|
5192
|
+
statementAt(args, place, options.facts, pattern.bindPlaceholder),
|
|
5193
|
+
NOWHERE,
|
|
5194
|
+
{ storageSystem: pattern.storageSystem, dialect: pattern.storageSystem }
|
|
5195
|
+
);
|
|
5196
|
+
}
|
|
5197
|
+
function callbacksRunBy(facts, classKey, pattern, writeMethod) {
|
|
5198
|
+
const events = pattern.callbacks?.eventOf[writeMethod] ?? [];
|
|
5199
|
+
if (events.length === 0) {
|
|
5200
|
+
return [];
|
|
5201
|
+
}
|
|
5202
|
+
askResolution3(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
|
|
5203
|
+
const found = /* @__PURE__ */ new Map();
|
|
5204
|
+
for (const row of facts.lookup("wantedCallbackMethod", 0, classKey)) {
|
|
5205
|
+
if (events.includes(String(row[1]))) {
|
|
5206
|
+
const key = String(row[3]);
|
|
5207
|
+
found.set(key, { name: String(row[2]), key });
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
return [...found.values()];
|
|
5211
|
+
}
|
|
5212
|
+
function callbackEffect(callback) {
|
|
5213
|
+
return {
|
|
5214
|
+
type: "invocation",
|
|
5215
|
+
callee: callback.name,
|
|
5216
|
+
args: [],
|
|
5217
|
+
async: false
|
|
5218
|
+
};
|
|
5219
|
+
}
|
|
4513
5220
|
function modelCallEffects(call, file, options, enclosing) {
|
|
4514
5221
|
const worked = libraryCallIn(call, options);
|
|
4515
5222
|
if (worked === null) {
|
|
4516
5223
|
return [];
|
|
4517
5224
|
}
|
|
4518
|
-
const method =
|
|
5225
|
+
const method = methodOf2(worked);
|
|
4519
5226
|
const target = receiverClass(worked, file, options, enclosing);
|
|
4520
5227
|
if (target === void 0) {
|
|
4521
5228
|
return [];
|
|
4522
5229
|
}
|
|
4523
5230
|
for (const pattern of options.patterns) {
|
|
4524
|
-
const
|
|
4525
|
-
if (
|
|
5231
|
+
const library = libraryCallOf(pattern, method);
|
|
5232
|
+
if (library === void 0 || !reachesBase(options.facts, target.classKey, pattern.baseClasses)) {
|
|
4526
5233
|
continue;
|
|
4527
5234
|
}
|
|
4528
5235
|
if (projectDeclares(options.facts, target.classKey, method)) {
|
|
4529
5236
|
return [];
|
|
4530
5237
|
}
|
|
5238
|
+
if (library.how === "statement") {
|
|
5239
|
+
return modelStatementEffects(worked, library.place, pattern, options);
|
|
5240
|
+
}
|
|
5241
|
+
const ran = library.kind === "write" ? callbacksRunBy(options.facts, target.classKey, pattern, method) : [];
|
|
4531
5242
|
return [
|
|
4532
5243
|
storageEffect(
|
|
4533
5244
|
worked,
|
|
4534
5245
|
target.container,
|
|
4535
5246
|
pattern,
|
|
4536
|
-
kind,
|
|
5247
|
+
library.kind,
|
|
4537
5248
|
selectorOf(worked, pattern, options.facts),
|
|
4538
|
-
fieldsOf(worked, pattern, kind, options.facts)
|
|
4539
|
-
)
|
|
5249
|
+
fieldsOf(worked, pattern, library.kind, options.facts)
|
|
5250
|
+
),
|
|
5251
|
+
...ran.map(callbackEffect)
|
|
4540
5252
|
];
|
|
4541
5253
|
}
|
|
4542
5254
|
return [];
|
|
4543
5255
|
}
|
|
4544
|
-
function
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
return node.text === loader.loader;
|
|
4550
|
-
}
|
|
4551
|
-
return node.type === "call" && receiverOf(node) === null && methodOf(node) === loader.loader;
|
|
4552
|
-
}
|
|
4553
|
-
function loaderPick(call, loader) {
|
|
4554
|
-
const method = methodOf(call);
|
|
4555
|
-
const receiver = receiverOf(call);
|
|
4556
|
-
if (receiver === null) {
|
|
4557
|
-
return loader.shortcuts.includes(method) ? call : null;
|
|
5256
|
+
function callbacksReached(call, file, options, enclosing = null) {
|
|
5257
|
+
const worked = libraryCallIn(call, options);
|
|
5258
|
+
const target = worked === null ? void 0 : receiverClass(worked, file, options, enclosing);
|
|
5259
|
+
if (worked === null || target === void 0) {
|
|
5260
|
+
return [];
|
|
4558
5261
|
}
|
|
4559
|
-
|
|
4560
|
-
|
|
5262
|
+
const method = methodOf2(worked);
|
|
5263
|
+
for (const pattern of options.patterns) {
|
|
5264
|
+
if (libraryCallOf(pattern, method)?.how !== "rows" || kindOfCall(pattern, method) !== "write" || !reachesBase(options.facts, target.classKey, pattern.baseClasses) || projectDeclares(options.facts, target.classKey, method)) {
|
|
5265
|
+
continue;
|
|
5266
|
+
}
|
|
5267
|
+
return callbacksRunBy(options.facts, target.classKey, pattern, method);
|
|
4561
5268
|
}
|
|
4562
|
-
|
|
4563
|
-
return picks ? receiver : null;
|
|
5269
|
+
return [];
|
|
4564
5270
|
}
|
|
4565
5271
|
function loaderCallEffects(call, file, options) {
|
|
4566
5272
|
const effects = [];
|
|
@@ -4586,10 +5292,24 @@ function loaderCallEffects(call, file, options) {
|
|
|
4586
5292
|
}
|
|
4587
5293
|
function effectsOfCall(call, file, options, enclosing) {
|
|
4588
5294
|
const onModel = modelCallEffects(call, file, options, enclosing);
|
|
4589
|
-
|
|
5295
|
+
if (onModel.length > 0) {
|
|
5296
|
+
return onModel;
|
|
5297
|
+
}
|
|
5298
|
+
const throughLoader = loaderCallEffects(call, file, options);
|
|
5299
|
+
if (throughLoader.length > 0) {
|
|
5300
|
+
return throughLoader;
|
|
5301
|
+
}
|
|
5302
|
+
return rawSqlEffects(call, {
|
|
5303
|
+
facts: options.facts,
|
|
5304
|
+
patterns: options.rawSql ?? [],
|
|
5305
|
+
file
|
|
5306
|
+
});
|
|
4590
5307
|
}
|
|
4591
5308
|
function storageClaims(call, file, options, enclosing = null) {
|
|
4592
|
-
return options
|
|
5309
|
+
return saysAnything(options) && effectsOfCall(call, file, options, enclosing).length > 0;
|
|
5310
|
+
}
|
|
5311
|
+
function saysAnything(options) {
|
|
5312
|
+
return options.patterns.length > 0 || (options.rawSql ?? []).length > 0;
|
|
4593
5313
|
}
|
|
4594
5314
|
function receiverKeysToAsk(chains, file, options, enclosing) {
|
|
4595
5315
|
const keys = [];
|
|
@@ -4598,7 +5318,7 @@ function receiverKeysToAsk(chains, file, options, enclosing) {
|
|
|
4598
5318
|
if (worked === null || rootConstant(worked) !== null) {
|
|
4599
5319
|
continue;
|
|
4600
5320
|
}
|
|
4601
|
-
const receiver =
|
|
5321
|
+
const receiver = receiverOf2(worked);
|
|
4602
5322
|
if (receiver !== null) {
|
|
4603
5323
|
keys.push(readKey(file, receiver, enclosing));
|
|
4604
5324
|
}
|
|
@@ -4606,18 +5326,18 @@ function receiverKeysToAsk(chains, file, options, enclosing) {
|
|
|
4606
5326
|
return keys;
|
|
4607
5327
|
}
|
|
4608
5328
|
function storageEffects(calls, file, options, enclosing = null) {
|
|
4609
|
-
if (options
|
|
5329
|
+
if (!saysAnything(options)) {
|
|
4610
5330
|
return [];
|
|
4611
5331
|
}
|
|
4612
5332
|
const partOfOne = /* @__PURE__ */ new Set();
|
|
4613
5333
|
for (const call of calls) {
|
|
4614
|
-
const receiver =
|
|
5334
|
+
const receiver = receiverOf2(call);
|
|
4615
5335
|
if (receiver !== null) {
|
|
4616
5336
|
partOfOne.add(receiver.id);
|
|
4617
5337
|
}
|
|
4618
5338
|
}
|
|
4619
5339
|
const chains = calls.filter((call) => !partOfOne.has(call.id));
|
|
4620
|
-
|
|
5340
|
+
askResolution3(
|
|
4621
5341
|
options.facts,
|
|
4622
5342
|
receiverKeysToAsk(chains, file, options, enclosing),
|
|
4623
5343
|
"wanted",
|
|
@@ -4742,14 +5462,18 @@ async function discoverUnits(root, options) {
|
|
|
4742
5462
|
file: options.absoluteFile ?? options.filePath
|
|
4743
5463
|
}));
|
|
4744
5464
|
const units = [];
|
|
5465
|
+
const clientOptions = {
|
|
5466
|
+
filePath: options.filePath,
|
|
5467
|
+
...options.facts === void 0 ? {} : { facts: options.facts }
|
|
5468
|
+
};
|
|
5469
|
+
askClientCallReads(
|
|
5470
|
+
root,
|
|
5471
|
+
options.packs.flatMap((pack) => pack.clients ?? []),
|
|
5472
|
+
clientOptions
|
|
5473
|
+
);
|
|
4745
5474
|
for (const pack of options.packs) {
|
|
4746
5475
|
for (const pattern of pack.clients ?? []) {
|
|
4747
|
-
units.push(
|
|
4748
|
-
...clientCallUnits(root, pack, pattern, {
|
|
4749
|
-
filePath: options.filePath,
|
|
4750
|
-
...options.facts === void 0 ? {} : { facts: options.facts }
|
|
4751
|
-
})
|
|
4752
|
-
);
|
|
5476
|
+
units.push(...clientCallUnits(root, pack, pattern, clientOptions));
|
|
4753
5477
|
}
|
|
4754
5478
|
}
|
|
4755
5479
|
for (const info of classes) {
|
|
@@ -5082,8 +5806,12 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
5082
5806
|
bodyRead.facts
|
|
5083
5807
|
);
|
|
5084
5808
|
const storage = bodyRead.storage;
|
|
5809
|
+
const facts = bodyRead.facts;
|
|
5085
5810
|
const extra = [
|
|
5086
|
-
...envReadEffects(
|
|
5811
|
+
...envReadEffects(
|
|
5812
|
+
method,
|
|
5813
|
+
facts === void 0 ? void 0 : { db: facts, file }
|
|
5814
|
+
),
|
|
5087
5815
|
...storage === void 0 ? [] : storageEffects(callsUnder2(method), file, storage, method)
|
|
5088
5816
|
];
|
|
5089
5817
|
return {
|
|
@@ -5859,6 +6587,47 @@ function buildRubyExtractionReport(args) {
|
|
|
5859
6587
|
});
|
|
5860
6588
|
}
|
|
5861
6589
|
|
|
6590
|
+
// src/facts/callbacks.ts
|
|
6591
|
+
function callbacksIn(patterns) {
|
|
6592
|
+
return patterns.flatMap(
|
|
6593
|
+
(pattern) => pattern.callbacks === void 0 ? [] : [pattern.callbacks]
|
|
6594
|
+
);
|
|
6595
|
+
}
|
|
6596
|
+
function emitClassCallbacks(db, file, classNode, declared) {
|
|
6597
|
+
const body = field(classNode, "body");
|
|
6598
|
+
if (body === null || declared.length === 0) {
|
|
6599
|
+
return;
|
|
6600
|
+
}
|
|
6601
|
+
const classKey = nodeId(file, classNode);
|
|
6602
|
+
for (const statement of runStatements(body)) {
|
|
6603
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
6604
|
+
continue;
|
|
6605
|
+
}
|
|
6606
|
+
const called = field(statement, "method")?.text ?? "";
|
|
6607
|
+
const args = readCallArgs(field(statement, "arguments"));
|
|
6608
|
+
for (const callbacks of declared) {
|
|
6609
|
+
const events = eventsOf(args, called, callbacks, db);
|
|
6610
|
+
for (const event of events) {
|
|
6611
|
+
for (const method of symbolArgumentNames(args, db)) {
|
|
6612
|
+
db.add("classCallback", [classKey, event, method]);
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
}
|
|
6618
|
+
function eventsOf(args, called, callbacks, db) {
|
|
6619
|
+
const covered = callbacks.registeredBy[called];
|
|
6620
|
+
if (covered === void 0) {
|
|
6621
|
+
return [];
|
|
6622
|
+
}
|
|
6623
|
+
const written = args.keyword[callbacks.eventKeyword];
|
|
6624
|
+
const narrowed = written === void 0 ? null : stringValueOf(written, db);
|
|
6625
|
+
if (narrowed === null) {
|
|
6626
|
+
return covered;
|
|
6627
|
+
}
|
|
6628
|
+
return covered.filter((event) => event === narrowed);
|
|
6629
|
+
}
|
|
6630
|
+
|
|
5862
6631
|
// src/facts.ts
|
|
5863
6632
|
import path3 from "path";
|
|
5864
6633
|
import { SKIP_CHILDREN as SKIP_CHILDREN3, walkDescendants as walkDescendants3 } from "@suss/extractor";
|
|
@@ -5975,6 +6744,10 @@ function calleeSpellings(calls, ctx) {
|
|
|
5975
6744
|
};
|
|
5976
6745
|
}
|
|
5977
6746
|
function resolveCallee(call, site, ctx, read) {
|
|
6747
|
+
const throughLoader = loaderSourceCallee(call, site, ctx);
|
|
6748
|
+
if (throughLoader !== null) {
|
|
6749
|
+
return throughLoader;
|
|
6750
|
+
}
|
|
5978
6751
|
const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site);
|
|
5979
6752
|
if (spelling.kind === "stopped") {
|
|
5980
6753
|
return spelling;
|
|
@@ -5984,6 +6757,21 @@ function resolveCallee(call, site, ctx, read) {
|
|
|
5984
6757
|
}
|
|
5985
6758
|
return asCallee(spelling, outcomeFor(spelling.key, ctx, read), site, ctx);
|
|
5986
6759
|
}
|
|
6760
|
+
function loaderSourceCallee(call, site, ctx) {
|
|
6761
|
+
for (const loader of ctx.loaders) {
|
|
6762
|
+
const constant2 = pickedSource(call, loader);
|
|
6763
|
+
const source = loader.source;
|
|
6764
|
+
if (constant2 === null || source === void 0) {
|
|
6765
|
+
continue;
|
|
6766
|
+
}
|
|
6767
|
+
const classKey = classBehind(ctx.facts, site.file, constant2);
|
|
6768
|
+
const qualifiedName = classKey === void 0 ? void 0 : ctx.classNames.get(classKey);
|
|
6769
|
+
if (qualifiedName !== void 0) {
|
|
6770
|
+
return methodOnAncestryOf(qualifiedName, source.method, ctx);
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
return null;
|
|
6774
|
+
}
|
|
5987
6775
|
function outcomeFor(key, ctx, read) {
|
|
5988
6776
|
return read?.outcomes.get(key) ?? calleeOutcomeOf(ctx.facts, key, RUBY_PROGRAM);
|
|
5989
6777
|
}
|
|
@@ -6165,6 +6953,7 @@ async function reachedFunctions(seeds, options) {
|
|
|
6165
6953
|
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
6166
6954
|
const propertyReadsByKey = /* @__PURE__ */ new Map();
|
|
6167
6955
|
const passedPositions = /* @__PURE__ */ new Set();
|
|
6956
|
+
const followedKeys = /* @__PURE__ */ new Set();
|
|
6168
6957
|
for (const seed of seeds) {
|
|
6169
6958
|
seedKeys.add(seed.key);
|
|
6170
6959
|
functionByKey.set(seed.key, {
|
|
@@ -6212,6 +7001,9 @@ async function reachedFunctions(seeds, options) {
|
|
|
6212
7001
|
for (const position of scan.passedPositions) {
|
|
6213
7002
|
passedPositions.add(position);
|
|
6214
7003
|
}
|
|
7004
|
+
if (scan.followed.length > 0) {
|
|
7005
|
+
followedKeys.add(key);
|
|
7006
|
+
}
|
|
6215
7007
|
for (const target of scan.followed) {
|
|
6216
7008
|
const calleeKey = keyOf(target);
|
|
6217
7009
|
if (!functionByKey.has(calleeKey)) {
|
|
@@ -6248,7 +7040,8 @@ async function reachedFunctions(seeds, options) {
|
|
|
6248
7040
|
argTargetsByKey,
|
|
6249
7041
|
parameterCallsByKey,
|
|
6250
7042
|
propertyReadsByKey,
|
|
6251
|
-
passedPositions
|
|
7043
|
+
passedPositions,
|
|
7044
|
+
followedKeys
|
|
6252
7045
|
};
|
|
6253
7046
|
}
|
|
6254
7047
|
function keyOf(target) {
|
|
@@ -6287,13 +7080,9 @@ function methodReferenceSymbol(node) {
|
|
|
6287
7080
|
return argChildren.length === 1 && argChildren[0]?.type === "simple_symbol" ? argChildren[0] : null;
|
|
6288
7081
|
}
|
|
6289
7082
|
function bodyOf2(source, options) {
|
|
6290
|
-
const site =
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
owner: keyOf(source),
|
|
6294
|
-
enclosingQualifiedName: source.enclosingQualifiedName
|
|
6295
|
-
};
|
|
6296
|
-
const written = field(source.node, "body") === null ? [] : bodyCalls(source.node, options.inheritedMethods);
|
|
7083
|
+
const site = siteOf2(source);
|
|
7084
|
+
const read = readableBodyOf(source.node);
|
|
7085
|
+
const written = read === null ? [] : bodyCalls(read, options.inheritedMethods);
|
|
6297
7086
|
const calls = callsReported(
|
|
6298
7087
|
written,
|
|
6299
7088
|
(call) => mightReadAsACall(call, site, options.context)
|
|
@@ -6305,6 +7094,17 @@ function bodyOf2(source, options) {
|
|
|
6305
7094
|
written: calls.map((call) => ({ call, site }))
|
|
6306
7095
|
};
|
|
6307
7096
|
}
|
|
7097
|
+
function readableBodyOf(node) {
|
|
7098
|
+
return node.type === PROGRAM_TYPE ? moduleScopeBody(node) : methodBody(node);
|
|
7099
|
+
}
|
|
7100
|
+
function siteOf2(source) {
|
|
7101
|
+
return {
|
|
7102
|
+
file: source.file,
|
|
7103
|
+
method: source.node.type === PROGRAM_TYPE ? null : source.node,
|
|
7104
|
+
owner: keyOf(source),
|
|
7105
|
+
enclosingQualifiedName: source.enclosingQualifiedName
|
|
7106
|
+
};
|
|
7107
|
+
}
|
|
6308
7108
|
function callsMade(asked, site, ctx, spellings) {
|
|
6309
7109
|
return asked.filter(
|
|
6310
7110
|
(call) => !isArglessReceiverCall(call) || readsAsACall(call, site, ctx, spellings)
|
|
@@ -6312,7 +7112,7 @@ function callsMade(asked, site, ctx, spellings) {
|
|
|
6312
7112
|
}
|
|
6313
7113
|
function scanBody(source, ctx, options, read) {
|
|
6314
7114
|
const displayPathOf = options.displayPathOf;
|
|
6315
|
-
if (
|
|
7115
|
+
if (readableBodyOf(source.node) === null) {
|
|
6316
7116
|
return EMPTY_SCAN;
|
|
6317
7117
|
}
|
|
6318
7118
|
const site = read.site;
|
|
@@ -6355,9 +7155,34 @@ function scanBody(source, ctx, options, read) {
|
|
|
6355
7155
|
}
|
|
6356
7156
|
}
|
|
6357
7157
|
};
|
|
7158
|
+
const followCallback = (name, key) => {
|
|
7159
|
+
const target = ctx.definitions.get(key);
|
|
7160
|
+
if (target === void 0) {
|
|
7161
|
+
return;
|
|
7162
|
+
}
|
|
7163
|
+
const targetKey = keyOf(target);
|
|
7164
|
+
if (!seen.has(targetKey)) {
|
|
7165
|
+
seen.add(targetKey);
|
|
7166
|
+
followed2.push(target);
|
|
7167
|
+
}
|
|
7168
|
+
placements.place(name, {
|
|
7169
|
+
file: displayPathOf(target.file),
|
|
7170
|
+
span: spanOf(target.node)
|
|
7171
|
+
});
|
|
7172
|
+
};
|
|
6358
7173
|
for (const call of calls) {
|
|
6359
7174
|
const callee = calleeText(call);
|
|
6360
7175
|
const outcome = resolveCallee(call, site, ctx, read.spellings);
|
|
7176
|
+
if (options.storage !== void 0) {
|
|
7177
|
+
for (const callback of callbacksReached(
|
|
7178
|
+
call,
|
|
7179
|
+
source.file,
|
|
7180
|
+
options.storage,
|
|
7181
|
+
site.method
|
|
7182
|
+
)) {
|
|
7183
|
+
followCallback(callback.name, callback.key);
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
6361
7186
|
const placed = outcome.kind === "followed" ? {
|
|
6362
7187
|
file: displayPathOf(outcome.target.file),
|
|
6363
7188
|
span: spanOf(outcome.target.node)
|
|
@@ -6489,8 +7314,8 @@ function parameterName(param) {
|
|
|
6489
7314
|
}
|
|
6490
7315
|
|
|
6491
7316
|
// src/reach/context.ts
|
|
6492
|
-
var
|
|
6493
|
-
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
|
|
7317
|
+
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
7318
|
+
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map(), loaders = []) {
|
|
6494
7319
|
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
6495
7320
|
const classes = [];
|
|
6496
7321
|
const classNames = /* @__PURE__ */ new Map();
|
|
@@ -6500,7 +7325,7 @@ async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dyna
|
|
|
6500
7325
|
classes.push({ file, info });
|
|
6501
7326
|
classNames.set(nodeId(file, info.node), info.qualifiedName);
|
|
6502
7327
|
for (const statement of namedChildren(info.bodyNode)) {
|
|
6503
|
-
if (
|
|
7328
|
+
if (METHOD_TYPES3.has(statement.type)) {
|
|
6504
7329
|
classOfMethod.set(nodeId(file, statement), info.qualifiedName);
|
|
6505
7330
|
}
|
|
6506
7331
|
}
|
|
@@ -6566,7 +7391,8 @@ async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dyna
|
|
|
6566
7391
|
classNames,
|
|
6567
7392
|
definitions,
|
|
6568
7393
|
bodyBlocks,
|
|
6569
|
-
dynamicNames
|
|
7394
|
+
dynamicNames,
|
|
7395
|
+
loaders
|
|
6570
7396
|
};
|
|
6571
7397
|
}
|
|
6572
7398
|
function namedChildren(node) {
|
|
@@ -6634,6 +7460,7 @@ function factsForFile(options) {
|
|
|
6634
7460
|
const db = new Database2();
|
|
6635
7461
|
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6636
7462
|
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
7463
|
+
emitEnvFacts(db, options.file, options.root);
|
|
6637
7464
|
emitConstantBindings(db, [
|
|
6638
7465
|
collectFileConstants(
|
|
6639
7466
|
options.file,
|
|
@@ -6650,6 +7477,19 @@ function factsForFile(options) {
|
|
|
6650
7477
|
addPackWords(db, packWordsOf(options.packs));
|
|
6651
7478
|
return db;
|
|
6652
7479
|
}
|
|
7480
|
+
function moduleInitUnit(options) {
|
|
7481
|
+
const raw = moduleInitStructure({
|
|
7482
|
+
name: options.name,
|
|
7483
|
+
file: options.file,
|
|
7484
|
+
range: options.range,
|
|
7485
|
+
effects: options.effects
|
|
7486
|
+
});
|
|
7487
|
+
const branch = raw.branches[0];
|
|
7488
|
+
if (branch !== void 0) {
|
|
7489
|
+
branch.effects = options.calls;
|
|
7490
|
+
}
|
|
7491
|
+
return raw;
|
|
7492
|
+
}
|
|
6653
7493
|
function alreadyDiscovered(seen, raw) {
|
|
6654
7494
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
6655
7495
|
const boundary = raw.boundaryBinding === null ? "" : boundaryKey(raw.boundaryBinding) ?? "";
|
|
@@ -6705,6 +7545,7 @@ async function extractRubyProject(options) {
|
|
|
6705
7545
|
}
|
|
6706
7546
|
parsed.push({ file, root });
|
|
6707
7547
|
emitValueFacts(db, file, root, bodyBlocks);
|
|
7548
|
+
emitEnvFacts(db, file, root);
|
|
6708
7549
|
for (const [key, method] of methodDefinitionsIn(file, root)) {
|
|
6709
7550
|
definitions.set(key, method);
|
|
6710
7551
|
}
|
|
@@ -6721,6 +7562,15 @@ async function extractRubyProject(options) {
|
|
|
6721
7562
|
}
|
|
6722
7563
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6723
7564
|
addPackWords(db, packWordsOf(options.packs));
|
|
7565
|
+
const callbacks = callbacksIn(
|
|
7566
|
+
options.packs.flatMap((pack) => pack.storage ?? [])
|
|
7567
|
+
);
|
|
7568
|
+
for (const { file, root } of parsed) {
|
|
7569
|
+
walkDefinitions(
|
|
7570
|
+
root,
|
|
7571
|
+
(info) => emitClassCallbacks(db, file, info.node, callbacks)
|
|
7572
|
+
);
|
|
7573
|
+
}
|
|
6724
7574
|
return readDynamicNames(
|
|
6725
7575
|
db,
|
|
6726
7576
|
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
@@ -6728,11 +7578,17 @@ async function extractRubyProject(options) {
|
|
|
6728
7578
|
});
|
|
6729
7579
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
6730
7580
|
const loaderPatterns = options.packs.flatMap((pack) => pack.loaders ?? []);
|
|
6731
|
-
const
|
|
7581
|
+
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
7582
|
+
const storage = storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
|
|
7583
|
+
facts: db,
|
|
7584
|
+
patterns: storagePatterns,
|
|
7585
|
+
loaders: loaderPatterns,
|
|
7586
|
+
rawSql: rawSqlPatterns
|
|
7587
|
+
} : void 0;
|
|
6732
7588
|
const inheritedMethods = inheritedMethodsIn(options.packs);
|
|
6733
7589
|
const reachContext = await timer.timeAsync(
|
|
6734
7590
|
"discover",
|
|
6735
|
-
() => buildReachContext(parsed, db, bodyBlocks, dynamicNames)
|
|
7591
|
+
() => buildReachContext(parsed, db, bodyBlocks, dynamicNames, loaderPatterns)
|
|
6736
7592
|
);
|
|
6737
7593
|
const displayPathOf = (file) => options.workspaceRoot !== void 0 ? path4.relative(options.workspaceRoot, file) : file;
|
|
6738
7594
|
const seeds = [];
|
|
@@ -6782,23 +7638,34 @@ async function extractRubyProject(options) {
|
|
|
6782
7638
|
});
|
|
6783
7639
|
}
|
|
6784
7640
|
}
|
|
6785
|
-
const
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
}
|
|
7641
|
+
const moduleKey = nodeId(file, root);
|
|
7642
|
+
seedKeys.add(moduleKey);
|
|
7643
|
+
seeds.push({
|
|
7644
|
+
key: moduleKey,
|
|
7645
|
+
file,
|
|
7646
|
+
node: root,
|
|
7647
|
+
enclosingQualifiedName: null
|
|
7648
|
+
});
|
|
7649
|
+
const loadTime = timer.time("discover", () => ({
|
|
7650
|
+
reads: envReadEffects(root, { db, file }),
|
|
7651
|
+
calls: moduleScopeInvocationEffects(
|
|
7652
|
+
root,
|
|
7653
|
+
inheritedMethods,
|
|
7654
|
+
EVERY_ARGLESS_CALL,
|
|
7655
|
+
db
|
|
7656
|
+
)
|
|
7657
|
+
}));
|
|
7658
|
+
found.push({
|
|
7659
|
+
raw: moduleInitUnit({
|
|
7660
|
+
name: path4.basename(displayPath),
|
|
7661
|
+
file: displayPath,
|
|
7662
|
+
range: rangeOf(root),
|
|
7663
|
+
effects: loadTime.reads,
|
|
7664
|
+
calls: loadTime.calls
|
|
7665
|
+
}),
|
|
7666
|
+
seedKey: moduleKey,
|
|
7667
|
+
onlyIfItReaches: loadTime.reads.length === 0
|
|
7668
|
+
});
|
|
6802
7669
|
}
|
|
6803
7670
|
for (const pack of options.packs) {
|
|
6804
7671
|
for (const pattern of pack.discovery) {
|
|
@@ -6836,6 +7703,9 @@ async function extractRubyProject(options) {
|
|
|
6836
7703
|
continue;
|
|
6837
7704
|
}
|
|
6838
7705
|
const { raw, seedKey } = entry;
|
|
7706
|
+
if (entry.onlyIfItReaches === true && (seedKey === null || !reached.followedKeys.has(seedKey))) {
|
|
7707
|
+
continue;
|
|
7708
|
+
}
|
|
6839
7709
|
if (seedKey !== null) {
|
|
6840
7710
|
dropPropertyReads(raw, reached.propertyReadsByKey.get(seedKey));
|
|
6841
7711
|
}
|
|
@@ -6945,20 +7815,9 @@ function findRubyFiles(root) {
|
|
|
6945
7815
|
|
|
6946
7816
|
// src/facts/why.ts
|
|
6947
7817
|
var WITNESS_RULES = [...RESOLUTION_RULES, ...RUBY_RULES];
|
|
6948
|
-
var METHOD_TYPES5 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6949
7818
|
function namedChildrenOf(node) {
|
|
6950
7819
|
return node.namedChildren.filter((child) => child !== null);
|
|
6951
7820
|
}
|
|
6952
|
-
function enclosingMethod2(node) {
|
|
6953
|
-
let current = node.parent;
|
|
6954
|
-
while (current !== null) {
|
|
6955
|
-
if (METHOD_TYPES5.has(current.type)) {
|
|
6956
|
-
return current;
|
|
6957
|
-
}
|
|
6958
|
-
current = current.parent;
|
|
6959
|
-
}
|
|
6960
|
-
return null;
|
|
6961
|
-
}
|
|
6962
7821
|
function indexFile(file, root, locations) {
|
|
6963
7822
|
const walk = (node) => {
|
|
6964
7823
|
locations.set(nodeId(file, node), { file, node });
|
|
@@ -6994,7 +7853,7 @@ function width(node) {
|
|
|
6994
7853
|
return node.endIndex - node.startIndex;
|
|
6995
7854
|
}
|
|
6996
7855
|
function askedKey(value) {
|
|
6997
|
-
const enclosing =
|
|
7856
|
+
const enclosing = enclosingDefinition(value.node);
|
|
6998
7857
|
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6999
7858
|
}
|
|
7000
7859
|
var RubyWhySession = class {
|
|
@@ -7209,6 +8068,8 @@ export {
|
|
|
7209
8068
|
resolveConstantFile,
|
|
7210
8069
|
rubyStubEvidence,
|
|
7211
8070
|
runStatements,
|
|
8071
|
+
storageClaims,
|
|
8072
|
+
storageEffects,
|
|
7212
8073
|
stringLiteralValue,
|
|
7213
8074
|
stringValueOf,
|
|
7214
8075
|
symbolValue,
|