@suss/adapter-ruby 0.22.4 → 0.23.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 +16 -0
- package/dist/index.cjs +476 -213
- package/dist/index.d.cts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +478 -214
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -288,9 +288,21 @@ var PATH_CONVENTIONS = {
|
|
|
288
288
|
railsUnderscore: underscoreConstantPath
|
|
289
289
|
};
|
|
290
290
|
function resolveConstantFile(root, qualifiedName, convention) {
|
|
291
|
-
const relative = PATH_CONVENTIONS[convention](qualifiedName)
|
|
292
|
-
const
|
|
293
|
-
|
|
291
|
+
const relative = `${PATH_CONVENTIONS[convention](qualifiedName)}.rb`;
|
|
292
|
+
for (const candidate of [root, ...autoloadDirectories(root)]) {
|
|
293
|
+
const file = import_node_path.default.join(candidate, relative);
|
|
294
|
+
if (import_node_fs.default.existsSync(file)) {
|
|
295
|
+
return file;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
function autoloadDirectories(root) {
|
|
301
|
+
try {
|
|
302
|
+
return import_node_fs.default.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => import_node_path.default.join(root, entry.name)).sort();
|
|
303
|
+
} catch {
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
294
306
|
}
|
|
295
307
|
|
|
296
308
|
// src/scope.ts
|
|
@@ -699,8 +711,9 @@ function isDefaultedAt(node) {
|
|
|
699
711
|
return false;
|
|
700
712
|
}
|
|
701
713
|
|
|
702
|
-
// src/
|
|
714
|
+
// src/responseStatus.ts
|
|
703
715
|
var import_extractor3 = require("@suss/extractor");
|
|
716
|
+
var import_values4 = require("@suss/values");
|
|
704
717
|
|
|
705
718
|
// src/paths/bareCalls.ts
|
|
706
719
|
var PARAMETER_LIST_TYPES = [
|
|
@@ -1076,175 +1089,6 @@ function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
|
1076
1089
|
};
|
|
1077
1090
|
}
|
|
1078
1091
|
|
|
1079
|
-
// src/paths/effects.ts
|
|
1080
|
-
var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
1081
|
-
var BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
1082
|
-
"body_statement",
|
|
1083
|
-
"then",
|
|
1084
|
-
"else",
|
|
1085
|
-
"do_block",
|
|
1086
|
-
"block"
|
|
1087
|
-
]);
|
|
1088
|
-
var LITERAL_ARGS = {
|
|
1089
|
-
string: (node) => ({
|
|
1090
|
-
kind: "string",
|
|
1091
|
-
value: node.text.replace(/^["']|["']$/g, "")
|
|
1092
|
-
}),
|
|
1093
|
-
integer: (node) => {
|
|
1094
|
-
const value = Number.parseInt(node.text, 10);
|
|
1095
|
-
return Number.isNaN(value) ? null : { kind: "number", value };
|
|
1096
|
-
},
|
|
1097
|
-
float: (node) => {
|
|
1098
|
-
const value = Number.parseFloat(node.text);
|
|
1099
|
-
return Number.isNaN(value) ? null : { kind: "number", value };
|
|
1100
|
-
},
|
|
1101
|
-
true: () => ({ kind: "boolean", value: true }),
|
|
1102
|
-
false: () => ({ kind: "boolean", value: false }),
|
|
1103
|
-
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
1104
|
-
};
|
|
1105
|
-
function children(node) {
|
|
1106
|
-
return node.namedChildren.filter((child) => child !== null);
|
|
1107
|
-
}
|
|
1108
|
-
function withoutChainLinks(calls) {
|
|
1109
|
-
const isLink = /* @__PURE__ */ new Set();
|
|
1110
|
-
for (const call of calls) {
|
|
1111
|
-
const receiver = field(call, "receiver");
|
|
1112
|
-
if (receiver !== null && receiver.type === "call") {
|
|
1113
|
-
isLink.add(receiver.id);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return calls.filter((call) => !isLink.has(call.id));
|
|
1117
|
-
}
|
|
1118
|
-
function calleeMethodName(call) {
|
|
1119
|
-
if (call.type === "identifier") {
|
|
1120
|
-
return call.text;
|
|
1121
|
-
}
|
|
1122
|
-
const method = field(call, "method");
|
|
1123
|
-
if (method !== null) {
|
|
1124
|
-
return method.text;
|
|
1125
|
-
}
|
|
1126
|
-
return field(call, "receiver") !== null ? "call" : children(call)[0]?.text;
|
|
1127
|
-
}
|
|
1128
|
-
function isPropertyRead(node) {
|
|
1129
|
-
if (field(node, "receiver") === null || field(node, "arguments") !== null) {
|
|
1130
|
-
return false;
|
|
1131
|
-
}
|
|
1132
|
-
return calleeMethodName(node) !== "call";
|
|
1133
|
-
}
|
|
1134
|
-
function isRaise2(node) {
|
|
1135
|
-
const method = field(node, "method") ?? children(node)[0] ?? null;
|
|
1136
|
-
return field(node, "receiver") === null && method !== null && RAISE_NAMES2.has(method.text);
|
|
1137
|
-
}
|
|
1138
|
-
var NO_INHERITED_METHODS = /* @__PURE__ */ new Set();
|
|
1139
|
-
function isInherited(node, inherited) {
|
|
1140
|
-
const method = field(node, "method");
|
|
1141
|
-
return field(node, "receiver") === null && method !== null && inherited.has(method.text);
|
|
1142
|
-
}
|
|
1143
|
-
function isCall(node, locals, inherited) {
|
|
1144
|
-
if (node.type === "call") {
|
|
1145
|
-
return !isPropertyRead(node) && !isRaise2(node) && !isInherited(node, inherited);
|
|
1146
|
-
}
|
|
1147
|
-
return isBareMethodCall(node, locals) && !RAISE_NAMES2.has(node.text) && !inherited.has(node.text);
|
|
1148
|
-
}
|
|
1149
|
-
function collectCalls(node, locals, inherited, found) {
|
|
1150
|
-
for (const child of children(node)) {
|
|
1151
|
-
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1152
|
-
continue;
|
|
1153
|
-
}
|
|
1154
|
-
if (isCall(child, locals, inherited)) {
|
|
1155
|
-
found.push(child);
|
|
1156
|
-
}
|
|
1157
|
-
collectCalls(child, locals, inherited, found);
|
|
1158
|
-
}
|
|
1159
|
-
return found;
|
|
1160
|
-
}
|
|
1161
|
-
function bodyCalls(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
1162
|
-
const body = field(definitionNode, "body");
|
|
1163
|
-
if (body === null) {
|
|
1164
|
-
return [];
|
|
1165
|
-
}
|
|
1166
|
-
return collectCalls(body, localNamesIn(definitionNode), inherited, []);
|
|
1167
|
-
}
|
|
1168
|
-
function calleeText(call) {
|
|
1169
|
-
const receiver = field(call, "receiver");
|
|
1170
|
-
const method = calleeMethodName(call);
|
|
1171
|
-
if (method === void 0) {
|
|
1172
|
-
return call.text;
|
|
1173
|
-
}
|
|
1174
|
-
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
1175
|
-
}
|
|
1176
|
-
function argOf(node) {
|
|
1177
|
-
const literal = LITERAL_ARGS[node.type]?.(node);
|
|
1178
|
-
if (literal !== null && literal !== void 0) {
|
|
1179
|
-
return literal;
|
|
1180
|
-
}
|
|
1181
|
-
if (node.type === "call" && !isPropertyRead(node)) {
|
|
1182
|
-
return { kind: "call", callee: calleeText(node), args: argsOf(node) };
|
|
1183
|
-
}
|
|
1184
|
-
return { kind: "identifier", name: node.text };
|
|
1185
|
-
}
|
|
1186
|
-
function argsOf(call) {
|
|
1187
|
-
const args = field(call, "arguments");
|
|
1188
|
-
if (args === null) {
|
|
1189
|
-
return [];
|
|
1190
|
-
}
|
|
1191
|
-
return children(args).map(
|
|
1192
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
1193
|
-
);
|
|
1194
|
-
}
|
|
1195
|
-
function enclosingStatement(call, body) {
|
|
1196
|
-
let current = call;
|
|
1197
|
-
while (current.parent !== null) {
|
|
1198
|
-
if (BLOCK_TYPES.has(current.parent.type) || current.parent.id === body.id) {
|
|
1199
|
-
return current;
|
|
1200
|
-
}
|
|
1201
|
-
current = current.parent;
|
|
1202
|
-
}
|
|
1203
|
-
return current;
|
|
1204
|
-
}
|
|
1205
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
1206
|
-
const body = field(definitionNode, "body");
|
|
1207
|
-
if (body === null) {
|
|
1208
|
-
return [];
|
|
1209
|
-
}
|
|
1210
|
-
const calls = withoutChainLinks(bodyCalls(definitionNode, inherited));
|
|
1211
|
-
if (calls.length === 0) {
|
|
1212
|
-
return [];
|
|
1213
|
-
}
|
|
1214
|
-
const statementOf2 = /* @__PURE__ */ new Map();
|
|
1215
|
-
const statements = [];
|
|
1216
|
-
for (const call of calls) {
|
|
1217
|
-
const statement = enclosingStatement(call, body);
|
|
1218
|
-
statementOf2.set(call.id, statement);
|
|
1219
|
-
statements.push(statement);
|
|
1220
|
-
}
|
|
1221
|
-
const lowered = lowerRubyBody(body, statements);
|
|
1222
|
-
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
1223
|
-
{
|
|
1224
|
-
statements: lowered.statements,
|
|
1225
|
-
terminalsByStmt: lowered.terminalsByStmt
|
|
1226
|
-
},
|
|
1227
|
-
statements
|
|
1228
|
-
);
|
|
1229
|
-
return calls.map((call) => {
|
|
1230
|
-
const statement = statementOf2.get(call.id);
|
|
1231
|
-
const conditions = (0, import_extractor3.sharedGatingConditions)(
|
|
1232
|
-
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
1233
|
-
);
|
|
1234
|
-
return {
|
|
1235
|
-
type: "invocation",
|
|
1236
|
-
callee: calleeText(call),
|
|
1237
|
-
args: argsOf(call),
|
|
1238
|
-
async: false,
|
|
1239
|
-
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
1240
|
-
};
|
|
1241
|
-
});
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
// src/responseStatus.ts
|
|
1245
|
-
var import_extractor4 = require("@suss/extractor");
|
|
1246
|
-
var import_values4 = require("@suss/values");
|
|
1247
|
-
|
|
1248
1092
|
// src/values/evaluator.ts
|
|
1249
1093
|
var import_resolution2 = require("@suss/resolution");
|
|
1250
1094
|
var import_values2 = require("@suss/values");
|
|
@@ -1300,7 +1144,7 @@ function paramNameOf(param) {
|
|
|
1300
1144
|
function declaredParamNames(method) {
|
|
1301
1145
|
const params = field(method, "parameters");
|
|
1302
1146
|
const declared = /* @__PURE__ */ new Set();
|
|
1303
|
-
for (const param of params === null ? [] :
|
|
1147
|
+
for (const param of params === null ? [] : children(params)) {
|
|
1304
1148
|
const name = paramNameOf(param);
|
|
1305
1149
|
if (name !== null) {
|
|
1306
1150
|
declared.add(name.text);
|
|
@@ -1314,7 +1158,7 @@ function readKey(filePath, node, enclosing) {
|
|
|
1314
1158
|
}
|
|
1315
1159
|
const parent = node.parent;
|
|
1316
1160
|
if (parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null) {
|
|
1317
|
-
return
|
|
1161
|
+
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
1318
1162
|
}
|
|
1319
1163
|
if (enclosing !== null && declaredParamNames(enclosing).has(node.text)) {
|
|
1320
1164
|
return `${nodeId(filePath, enclosing)}#${node.text}`;
|
|
@@ -1342,7 +1186,7 @@ var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1342
1186
|
"parenthesized_statements"
|
|
1343
1187
|
]);
|
|
1344
1188
|
var ARRAY_TYPES = /* @__PURE__ */ new Set(["array", "string_array", "symbol_array"]);
|
|
1345
|
-
function
|
|
1189
|
+
function children(node) {
|
|
1346
1190
|
return node.namedChildren.filter((child) => child !== null);
|
|
1347
1191
|
}
|
|
1348
1192
|
function add(emitter, relation, ...tuple) {
|
|
@@ -1370,8 +1214,8 @@ function pairKeyText(key) {
|
|
|
1370
1214
|
}
|
|
1371
1215
|
return null;
|
|
1372
1216
|
}
|
|
1373
|
-
function
|
|
1374
|
-
return node.type === "call" && field(node, "receiver") !== null && field(node, "arguments") === null &&
|
|
1217
|
+
function isPropertyRead(node) {
|
|
1218
|
+
return node.type === "call" && field(node, "receiver") !== null && field(node, "arguments") === null && children(node).every(
|
|
1375
1219
|
(child) => child.type !== "do_block" && child.type !== "block"
|
|
1376
1220
|
);
|
|
1377
1221
|
}
|
|
@@ -1410,7 +1254,7 @@ function emitCall(emitter, call) {
|
|
|
1410
1254
|
}
|
|
1411
1255
|
const args = field(call, "arguments");
|
|
1412
1256
|
let position = 0;
|
|
1413
|
-
for (const argument of args === null ? [] :
|
|
1257
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
1414
1258
|
if (argument.type === "pair") {
|
|
1415
1259
|
const key = field(argument, "key");
|
|
1416
1260
|
const value = field(argument, "value");
|
|
@@ -1440,7 +1284,7 @@ function emitArray(emitter, array) {
|
|
|
1440
1284
|
const objectKey = nodeId(emitter.filePath, array);
|
|
1441
1285
|
add(emitter, "objectValue", objectKey);
|
|
1442
1286
|
let position = 0;
|
|
1443
|
-
for (const element of
|
|
1287
|
+
for (const element of children(array)) {
|
|
1444
1288
|
add(
|
|
1445
1289
|
emitter,
|
|
1446
1290
|
"holdsProperty",
|
|
@@ -1454,7 +1298,7 @@ function emitArray(emitter, array) {
|
|
|
1454
1298
|
function emitHash(emitter, hash) {
|
|
1455
1299
|
const objectKey = nodeId(emitter.filePath, hash);
|
|
1456
1300
|
add(emitter, "objectValue", objectKey);
|
|
1457
|
-
for (const pair of
|
|
1301
|
+
for (const pair of children(hash)) {
|
|
1458
1302
|
if (pair.type !== "pair") {
|
|
1459
1303
|
continue;
|
|
1460
1304
|
}
|
|
@@ -1468,7 +1312,7 @@ function emitHash(emitter, hash) {
|
|
|
1468
1312
|
}
|
|
1469
1313
|
}
|
|
1470
1314
|
function walkExpressions(node, visit) {
|
|
1471
|
-
for (const child of
|
|
1315
|
+
for (const child of children(node)) {
|
|
1472
1316
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1473
1317
|
continue;
|
|
1474
1318
|
}
|
|
@@ -1478,7 +1322,7 @@ function walkExpressions(node, visit) {
|
|
|
1478
1322
|
}
|
|
1479
1323
|
function emitExpressionFacts(emitter, node) {
|
|
1480
1324
|
walkExpressions(node, (child) => {
|
|
1481
|
-
if (
|
|
1325
|
+
if (isPropertyRead(child)) {
|
|
1482
1326
|
emitPropertyRead(emitter, child);
|
|
1483
1327
|
} else if (child.type === "call") {
|
|
1484
1328
|
emitCall(emitter, child);
|
|
@@ -1498,7 +1342,7 @@ function emitExpressionFacts(emitter, node) {
|
|
|
1498
1342
|
});
|
|
1499
1343
|
}
|
|
1500
1344
|
function implicitReturn(body) {
|
|
1501
|
-
const statements =
|
|
1345
|
+
const statements = children(body).filter(
|
|
1502
1346
|
(child) => child.type !== "rescue" && child.type !== "ensure"
|
|
1503
1347
|
);
|
|
1504
1348
|
const last = statements[statements.length - 1];
|
|
@@ -1510,7 +1354,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1510
1354
|
const params = field(method, "parameters");
|
|
1511
1355
|
const declared = /* @__PURE__ */ new Set();
|
|
1512
1356
|
let position = 0;
|
|
1513
|
-
for (const param of params === null ? [] :
|
|
1357
|
+
for (const param of params === null ? [] : children(params)) {
|
|
1514
1358
|
const paramName = paramNameOf(param);
|
|
1515
1359
|
if (paramName !== null) {
|
|
1516
1360
|
declared.add(paramName.text);
|
|
@@ -1529,7 +1373,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1529
1373
|
return funcKey;
|
|
1530
1374
|
}
|
|
1531
1375
|
const recordNested = (node) => {
|
|
1532
|
-
for (const child of
|
|
1376
|
+
for (const child of children(node)) {
|
|
1533
1377
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1534
1378
|
add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
|
|
1535
1379
|
continue;
|
|
@@ -1540,8 +1384,8 @@ function emitMethodFacts(emitter, method) {
|
|
|
1540
1384
|
recordNested(body);
|
|
1541
1385
|
walkExpressions(body, (child) => {
|
|
1542
1386
|
if (child.type === "return") {
|
|
1543
|
-
const first =
|
|
1544
|
-
const returned = first?.type === "argument_list" ?
|
|
1387
|
+
const first = children(child)[0];
|
|
1388
|
+
const returned = first?.type === "argument_list" ? children(first)[0] : first;
|
|
1545
1389
|
if (returned !== void 0) {
|
|
1546
1390
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
1547
1391
|
}
|
|
@@ -1585,13 +1429,13 @@ function emitClassFacts(emitter, cls) {
|
|
|
1585
1429
|
const classKey = nodeId(emitter.filePath, cls);
|
|
1586
1430
|
add(emitter, "objectValue", classKey);
|
|
1587
1431
|
const superclass = field(cls, "superclass");
|
|
1588
|
-
const base = superclass === null ? null :
|
|
1432
|
+
const base = superclass === null ? null : children(superclass)[0] ?? null;
|
|
1589
1433
|
if (base !== null) {
|
|
1590
1434
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
1591
1435
|
add(emitter, "extendsNamed", classKey, base.text);
|
|
1592
1436
|
}
|
|
1593
1437
|
const body = field(cls, "body");
|
|
1594
|
-
for (const statement of body === null ? [] :
|
|
1438
|
+
for (const statement of body === null ? [] : children(body)) {
|
|
1595
1439
|
if (statement.type === "assignment") {
|
|
1596
1440
|
const left = field(statement, "left");
|
|
1597
1441
|
const right = field(statement, "right");
|
|
@@ -1633,7 +1477,7 @@ function emitValueFacts(db, filePath, root) {
|
|
|
1633
1477
|
add(emitter, "exportsAs", filePath, name.text, key);
|
|
1634
1478
|
};
|
|
1635
1479
|
const walk = (node) => {
|
|
1636
|
-
for (const child of
|
|
1480
|
+
for (const child of children(node)) {
|
|
1637
1481
|
if (NESTING_TYPES.has(child.type)) {
|
|
1638
1482
|
declaresName(child, emitClassFacts(emitter, child));
|
|
1639
1483
|
continue;
|
|
@@ -1664,7 +1508,7 @@ var OWN_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1664
1508
|
"module",
|
|
1665
1509
|
"singleton_class"
|
|
1666
1510
|
]);
|
|
1667
|
-
var
|
|
1511
|
+
var BLOCK_TYPES = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1668
1512
|
var METHOD_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1669
1513
|
var NAME_TYPES = /* @__PURE__ */ new Set([
|
|
1670
1514
|
"identifier",
|
|
@@ -2170,7 +2014,7 @@ function isTailExpression(node) {
|
|
|
2170
2014
|
if (owner.type === "block" && owner.parent?.type === "lambda") {
|
|
2171
2015
|
return true;
|
|
2172
2016
|
}
|
|
2173
|
-
if (METHOD_TYPES2.has(owner.type) ||
|
|
2017
|
+
if (METHOD_TYPES2.has(owner.type) || BLOCK_TYPES.has(owner.type)) {
|
|
2174
2018
|
return true;
|
|
2175
2019
|
}
|
|
2176
2020
|
if (owner.type === "when") {
|
|
@@ -2189,7 +2033,7 @@ function isRoot(node) {
|
|
|
2189
2033
|
if (OWN_ROOT_TYPES.has(node.type)) {
|
|
2190
2034
|
return true;
|
|
2191
2035
|
}
|
|
2192
|
-
return
|
|
2036
|
+
return BLOCK_TYPES.has(node.type) && node.parent?.type === "call";
|
|
2193
2037
|
}
|
|
2194
2038
|
function isPathStatement(node, parent) {
|
|
2195
2039
|
if (node.type === "elsif") {
|
|
@@ -2243,7 +2087,7 @@ function functionOf(node) {
|
|
|
2243
2087
|
body: bodyOf(block)
|
|
2244
2088
|
};
|
|
2245
2089
|
}
|
|
2246
|
-
if (
|
|
2090
|
+
if (BLOCK_TYPES.has(node.type)) {
|
|
2247
2091
|
return {
|
|
2248
2092
|
parameters: parametersOf(field(node, "parameters")),
|
|
2249
2093
|
body: bodyOf(node)
|
|
@@ -2726,20 +2570,20 @@ function collectReturns(node, found) {
|
|
|
2726
2570
|
function readingOfCall(call, declaration, names) {
|
|
2727
2571
|
const argument = statusArgumentOf(call, declaration);
|
|
2728
2572
|
if (argument === null) {
|
|
2729
|
-
return declaration.defaultStatusCode === void 0 ?
|
|
2573
|
+
return declaration.defaultStatusCode === void 0 ? import_extractor3.absentReading : (0, import_extractor3.writtenReading)(declaration.defaultStatusCode, rangeOf(call));
|
|
2730
2574
|
}
|
|
2731
2575
|
const status = statusNumberOf(argument, names);
|
|
2732
2576
|
if (status === null) {
|
|
2733
|
-
return (0,
|
|
2577
|
+
return (0, import_extractor3.unreadableReading)(
|
|
2734
2578
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
2735
2579
|
rangeOf(argument)
|
|
2736
2580
|
);
|
|
2737
2581
|
}
|
|
2738
|
-
return (0,
|
|
2582
|
+
return (0, import_extractor3.writtenReading)(status, rangeOf(argument));
|
|
2739
2583
|
}
|
|
2740
2584
|
function effectsReaching(effects, conditions) {
|
|
2741
2585
|
return effects.filter(
|
|
2742
|
-
(effect) => (0,
|
|
2586
|
+
(effect) => (0, import_extractor3.guardsHoldOn)(
|
|
2743
2587
|
effect.type === "invocation" ? effect.preconditions : void 0,
|
|
2744
2588
|
conditions
|
|
2745
2589
|
)
|
|
@@ -2753,7 +2597,10 @@ function conditionOf(condition) {
|
|
|
2753
2597
|
source: condition.source
|
|
2754
2598
|
};
|
|
2755
2599
|
}
|
|
2756
|
-
function branchOf(outcome, pattern, effects, extraEffects) {
|
|
2600
|
+
function branchOf(outcome, pattern, effects, extraEffects, options) {
|
|
2601
|
+
if (outcome.fellThrough === true && options.fallthrough === "handOn") {
|
|
2602
|
+
return handsOnBranch(outcome, effects, extraEffects);
|
|
2603
|
+
}
|
|
2757
2604
|
return {
|
|
2758
2605
|
conditions: outcome.conditions,
|
|
2759
2606
|
terminal: {
|
|
@@ -2778,7 +2625,28 @@ function branchOf(outcome, pattern, effects, extraEffects) {
|
|
|
2778
2625
|
isDefault: outcome.conditions.length === 0
|
|
2779
2626
|
};
|
|
2780
2627
|
}
|
|
2781
|
-
function
|
|
2628
|
+
function handsOnBranch(outcome, effects, extraEffects) {
|
|
2629
|
+
return {
|
|
2630
|
+
conditions: outcome.conditions,
|
|
2631
|
+
terminal: {
|
|
2632
|
+
kind: "delegate",
|
|
2633
|
+
statusCode: null,
|
|
2634
|
+
body: null,
|
|
2635
|
+
exceptionType: null,
|
|
2636
|
+
message: null,
|
|
2637
|
+
component: null,
|
|
2638
|
+
renderTree: null,
|
|
2639
|
+
delegateTarget: null,
|
|
2640
|
+
emitEvent: null,
|
|
2641
|
+
location: outcome.location
|
|
2642
|
+
},
|
|
2643
|
+
effects: effectsReaching(effects, outcome.conditions),
|
|
2644
|
+
...extraEffects === void 0 ? {} : { extraEffects },
|
|
2645
|
+
location: outcome.location,
|
|
2646
|
+
isDefault: outcome.conditions.length === 0
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
function responseBranches(method, pattern, effects, extraEffects, options = {}) {
|
|
2782
2650
|
const declarations = pattern.responseStatusCalls ?? [];
|
|
2783
2651
|
const body = field(method, "body");
|
|
2784
2652
|
if (declarations.length === 0 || body === null) {
|
|
@@ -2793,7 +2661,7 @@ function responseBranches(method, pattern, effects, extraEffects) {
|
|
|
2793
2661
|
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, byName, locals, []).length === 0
|
|
2794
2662
|
);
|
|
2795
2663
|
const terminals = [...responses, ...bareReturns];
|
|
2796
|
-
const enumerated = (0,
|
|
2664
|
+
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
2797
2665
|
{
|
|
2798
2666
|
statements: lowered.statements,
|
|
2799
2667
|
terminalsByStmt: lowered.terminalsByStmt
|
|
@@ -2804,7 +2672,7 @@ function responseBranches(method, pattern, effects, extraEffects) {
|
|
|
2804
2672
|
const outcomes = [];
|
|
2805
2673
|
for (const terminal of terminals) {
|
|
2806
2674
|
const declaration = declarationOf(terminal, byName, locals);
|
|
2807
|
-
const reading = declaration === void 0 ?
|
|
2675
|
+
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
2808
2676
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
2809
2677
|
outcomes.push({
|
|
2810
2678
|
conditions: path7.map(conditionOf),
|
|
@@ -2816,18 +2684,370 @@ function responseBranches(method, pattern, effects, extraEffects) {
|
|
|
2816
2684
|
for (const path7 of enumerated.fallthrough) {
|
|
2817
2685
|
outcomes.push({
|
|
2818
2686
|
conditions: path7.map(conditionOf),
|
|
2819
|
-
reading:
|
|
2820
|
-
location: rangeOf(method)
|
|
2687
|
+
reading: import_extractor3.absentReading,
|
|
2688
|
+
location: rangeOf(method),
|
|
2689
|
+
fellThrough: true
|
|
2821
2690
|
});
|
|
2822
2691
|
}
|
|
2823
2692
|
if (outcomes.length === 0) {
|
|
2824
2693
|
return null;
|
|
2825
2694
|
}
|
|
2826
2695
|
return outcomes.map(
|
|
2827
|
-
(outcome) => branchOf(outcome, pattern, effects, extraEffects)
|
|
2696
|
+
(outcome) => branchOf(outcome, pattern, effects, extraEffects, options)
|
|
2828
2697
|
);
|
|
2829
2698
|
}
|
|
2830
2699
|
|
|
2700
|
+
// src/filters.ts
|
|
2701
|
+
function controllerFilters(pattern, ancestry) {
|
|
2702
|
+
const forms = pattern.filters ?? [];
|
|
2703
|
+
if (forms.length === 0) {
|
|
2704
|
+
return [];
|
|
2705
|
+
}
|
|
2706
|
+
const byName = new Map(forms.map((form) => [form.name, form]));
|
|
2707
|
+
const skipNames = /* @__PURE__ */ new Map();
|
|
2708
|
+
for (const form of forms) {
|
|
2709
|
+
if (form.skippedBy !== void 0) {
|
|
2710
|
+
skipNames.set(form.skippedBy, form.name);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
const declared = [];
|
|
2714
|
+
const skips = [];
|
|
2715
|
+
for (const { statement } of inheritedStatements(ancestry)) {
|
|
2716
|
+
const called = calledName(statement);
|
|
2717
|
+
if (called === null) {
|
|
2718
|
+
continue;
|
|
2719
|
+
}
|
|
2720
|
+
const form = byName.get(called);
|
|
2721
|
+
if (form !== void 0) {
|
|
2722
|
+
declared.push(...declarationsOf(statement, form));
|
|
2723
|
+
continue;
|
|
2724
|
+
}
|
|
2725
|
+
const skipped = skipNames.get(called);
|
|
2726
|
+
if (skipped !== void 0) {
|
|
2727
|
+
skips.push(...skipsOf(statement, skipped));
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
const resolved = [];
|
|
2731
|
+
for (const declaration of applySkips(declared, skips)) {
|
|
2732
|
+
const found = methodInAncestry(ancestry, declaration.methodName);
|
|
2733
|
+
if (found.type !== "found") {
|
|
2734
|
+
continue;
|
|
2735
|
+
}
|
|
2736
|
+
resolved.push({
|
|
2737
|
+
filter: declaration.filter,
|
|
2738
|
+
methodName: declaration.methodName,
|
|
2739
|
+
method: found.method,
|
|
2740
|
+
file: found.block.file,
|
|
2741
|
+
enclosingQualifiedName: found.block.info.qualifiedName,
|
|
2742
|
+
only: declaration.only,
|
|
2743
|
+
except: declaration.except
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
return [
|
|
2747
|
+
...resolved.filter((one) => one.filter.onThrow !== true),
|
|
2748
|
+
...resolved.filter((one) => one.filter.onThrow === true)
|
|
2749
|
+
];
|
|
2750
|
+
}
|
|
2751
|
+
function filterCoversAction(filter, actionName) {
|
|
2752
|
+
if (filter.except.has(actionName)) {
|
|
2753
|
+
return false;
|
|
2754
|
+
}
|
|
2755
|
+
return filter.only === null || filter.only.has(actionName);
|
|
2756
|
+
}
|
|
2757
|
+
function filterReference(filter, displayPath) {
|
|
2758
|
+
return {
|
|
2759
|
+
file: displayPath,
|
|
2760
|
+
name: filter.methodName,
|
|
2761
|
+
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
function filterUnit(filter, pattern, displayPath, body) {
|
|
2765
|
+
const range = rangeOf(filter.method);
|
|
2766
|
+
const branches = responseBranches(
|
|
2767
|
+
filter.method,
|
|
2768
|
+
pattern,
|
|
2769
|
+
body.effects ?? [],
|
|
2770
|
+
body.extraEffects,
|
|
2771
|
+
{ fallthrough: "handOn" }
|
|
2772
|
+
);
|
|
2773
|
+
return {
|
|
2774
|
+
identity: {
|
|
2775
|
+
name: filter.methodName,
|
|
2776
|
+
nameKind: "binding",
|
|
2777
|
+
kind: "middleware",
|
|
2778
|
+
file: displayPath,
|
|
2779
|
+
range,
|
|
2780
|
+
span: spanOf(filter.method),
|
|
2781
|
+
exportName: filter.methodName,
|
|
2782
|
+
exportPath: [filter.enclosingQualifiedName, filter.methodName]
|
|
2783
|
+
},
|
|
2784
|
+
boundaryBinding: null,
|
|
2785
|
+
parameters: [],
|
|
2786
|
+
branches: branches ?? [handsOn(range, body)],
|
|
2787
|
+
bodyContent: body.bodyContent ?? "absent",
|
|
2788
|
+
dependencyCalls: [],
|
|
2789
|
+
declaredContract: null
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
function handsOn(range, body) {
|
|
2793
|
+
return {
|
|
2794
|
+
conditions: [],
|
|
2795
|
+
terminal: {
|
|
2796
|
+
kind: "delegate",
|
|
2797
|
+
statusCode: null,
|
|
2798
|
+
body: null,
|
|
2799
|
+
exceptionType: null,
|
|
2800
|
+
message: null,
|
|
2801
|
+
component: null,
|
|
2802
|
+
renderTree: null,
|
|
2803
|
+
delegateTarget: null,
|
|
2804
|
+
emitEvent: null,
|
|
2805
|
+
location: range
|
|
2806
|
+
},
|
|
2807
|
+
effects: body.effects ?? [],
|
|
2808
|
+
...body.extraEffects === void 0 ? {} : { extraEffects: body.extraEffects },
|
|
2809
|
+
location: range,
|
|
2810
|
+
isDefault: true
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
function calledName(statement) {
|
|
2814
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
2815
|
+
return null;
|
|
2816
|
+
}
|
|
2817
|
+
return field(statement, "method")?.text ?? null;
|
|
2818
|
+
}
|
|
2819
|
+
function declarationsOf(statement, filter) {
|
|
2820
|
+
const args = readCallArgs(field(statement, "arguments"));
|
|
2821
|
+
const keywords = filter.actionKeywords;
|
|
2822
|
+
const only = keywords === void 0 ? null : namesUnder(args.keyword[keywords.include]);
|
|
2823
|
+
const except = keywords === void 0 ? null : namesUnder(args.keyword[keywords.exclude]);
|
|
2824
|
+
return methodNamesOf(args, filter).map((methodName) => ({
|
|
2825
|
+
filter,
|
|
2826
|
+
methodName,
|
|
2827
|
+
only,
|
|
2828
|
+
except: except ?? /* @__PURE__ */ new Set()
|
|
2829
|
+
}));
|
|
2830
|
+
}
|
|
2831
|
+
function methodNamesOf(args, filter) {
|
|
2832
|
+
if (filter.methodFrom === "withKeyword") {
|
|
2833
|
+
const named2 = args.keyword.with;
|
|
2834
|
+
const value = named2 === void 0 ? null : symbolValue(named2);
|
|
2835
|
+
return value === null ? [] : [value];
|
|
2836
|
+
}
|
|
2837
|
+
return args.positional.map((arg) => symbolValue(arg)).filter((name) => name !== null);
|
|
2838
|
+
}
|
|
2839
|
+
function skipsOf(statement, filterName) {
|
|
2840
|
+
const args = readCallArgs(field(statement, "arguments"));
|
|
2841
|
+
const actions = namesUnder(args.keyword.only);
|
|
2842
|
+
return args.positional.map((arg) => symbolValue(arg)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
2843
|
+
}
|
|
2844
|
+
function applySkips(declared, skips) {
|
|
2845
|
+
const left = [];
|
|
2846
|
+
for (const declaration of declared) {
|
|
2847
|
+
const reaching = skips.filter(
|
|
2848
|
+
(skip) => skip.filterName === declaration.filter.name && skip.methodName === declaration.methodName
|
|
2849
|
+
);
|
|
2850
|
+
if (reaching.some((skip) => skip.actions === null)) {
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
const except = new Set(declaration.except);
|
|
2854
|
+
for (const skip of reaching) {
|
|
2855
|
+
for (const action of skip.actions ?? []) {
|
|
2856
|
+
except.add(action);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
left.push({ ...declaration, except });
|
|
2860
|
+
}
|
|
2861
|
+
return left;
|
|
2862
|
+
}
|
|
2863
|
+
function namesUnder(node) {
|
|
2864
|
+
if (node === void 0) {
|
|
2865
|
+
return null;
|
|
2866
|
+
}
|
|
2867
|
+
const single = symbolValue(node);
|
|
2868
|
+
if (single !== null) {
|
|
2869
|
+
return /* @__PURE__ */ new Set([single]);
|
|
2870
|
+
}
|
|
2871
|
+
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
2872
|
+
return null;
|
|
2873
|
+
}
|
|
2874
|
+
const names = /* @__PURE__ */ new Set();
|
|
2875
|
+
for (const child of bodyStatements(node)) {
|
|
2876
|
+
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
2877
|
+
const name = symbolValue(child) ?? bare;
|
|
2878
|
+
if (name !== null) {
|
|
2879
|
+
names.add(name);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
return names;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// src/paths/effects.ts
|
|
2886
|
+
var import_extractor4 = require("@suss/extractor");
|
|
2887
|
+
var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
2888
|
+
var BLOCK_TYPES2 = /* @__PURE__ */ new Set([
|
|
2889
|
+
"body_statement",
|
|
2890
|
+
"then",
|
|
2891
|
+
"else",
|
|
2892
|
+
"do_block",
|
|
2893
|
+
"block"
|
|
2894
|
+
]);
|
|
2895
|
+
var LITERAL_ARGS = {
|
|
2896
|
+
string: (node) => ({
|
|
2897
|
+
kind: "string",
|
|
2898
|
+
value: node.text.replace(/^["']|["']$/g, "")
|
|
2899
|
+
}),
|
|
2900
|
+
integer: (node) => {
|
|
2901
|
+
const value = Number.parseInt(node.text, 10);
|
|
2902
|
+
return Number.isNaN(value) ? null : { kind: "number", value };
|
|
2903
|
+
},
|
|
2904
|
+
float: (node) => {
|
|
2905
|
+
const value = Number.parseFloat(node.text);
|
|
2906
|
+
return Number.isNaN(value) ? null : { kind: "number", value };
|
|
2907
|
+
},
|
|
2908
|
+
true: () => ({ kind: "boolean", value: true }),
|
|
2909
|
+
false: () => ({ kind: "boolean", value: false }),
|
|
2910
|
+
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
2911
|
+
};
|
|
2912
|
+
function children2(node) {
|
|
2913
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
2914
|
+
}
|
|
2915
|
+
function withoutChainLinks(calls) {
|
|
2916
|
+
const isLink = /* @__PURE__ */ new Set();
|
|
2917
|
+
for (const call of calls) {
|
|
2918
|
+
const receiver = field(call, "receiver");
|
|
2919
|
+
if (receiver !== null && receiver.type === "call") {
|
|
2920
|
+
isLink.add(receiver.id);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
return calls.filter((call) => !isLink.has(call.id));
|
|
2924
|
+
}
|
|
2925
|
+
function calleeMethodName(call) {
|
|
2926
|
+
if (call.type === "identifier") {
|
|
2927
|
+
return call.text;
|
|
2928
|
+
}
|
|
2929
|
+
const method = field(call, "method");
|
|
2930
|
+
if (method !== null) {
|
|
2931
|
+
return method.text;
|
|
2932
|
+
}
|
|
2933
|
+
return field(call, "receiver") !== null ? "call" : children2(call)[0]?.text;
|
|
2934
|
+
}
|
|
2935
|
+
function isPropertyRead2(node) {
|
|
2936
|
+
if (field(node, "receiver") === null || field(node, "arguments") !== null) {
|
|
2937
|
+
return false;
|
|
2938
|
+
}
|
|
2939
|
+
return calleeMethodName(node) !== "call";
|
|
2940
|
+
}
|
|
2941
|
+
function isRaise2(node) {
|
|
2942
|
+
const method = field(node, "method") ?? children2(node)[0] ?? null;
|
|
2943
|
+
return field(node, "receiver") === null && method !== null && RAISE_NAMES2.has(method.text);
|
|
2944
|
+
}
|
|
2945
|
+
var NO_INHERITED_METHODS = /* @__PURE__ */ new Set();
|
|
2946
|
+
function isInherited(node, inherited) {
|
|
2947
|
+
const method = field(node, "method");
|
|
2948
|
+
return field(node, "receiver") === null && method !== null && inherited.has(method.text);
|
|
2949
|
+
}
|
|
2950
|
+
function isCall(node, locals, inherited) {
|
|
2951
|
+
if (node.type === "call") {
|
|
2952
|
+
return !isPropertyRead2(node) && !isRaise2(node) && !isInherited(node, inherited);
|
|
2953
|
+
}
|
|
2954
|
+
return isBareMethodCall(node, locals) && !RAISE_NAMES2.has(node.text) && !inherited.has(node.text);
|
|
2955
|
+
}
|
|
2956
|
+
function collectCalls(node, locals, inherited, found) {
|
|
2957
|
+
for (const child of children2(node)) {
|
|
2958
|
+
if (OWN_BODY_TYPES.has(child.type)) {
|
|
2959
|
+
continue;
|
|
2960
|
+
}
|
|
2961
|
+
if (isCall(child, locals, inherited)) {
|
|
2962
|
+
found.push(child);
|
|
2963
|
+
}
|
|
2964
|
+
collectCalls(child, locals, inherited, found);
|
|
2965
|
+
}
|
|
2966
|
+
return found;
|
|
2967
|
+
}
|
|
2968
|
+
function bodyCalls(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
2969
|
+
const body = field(definitionNode, "body");
|
|
2970
|
+
if (body === null) {
|
|
2971
|
+
return [];
|
|
2972
|
+
}
|
|
2973
|
+
return collectCalls(body, localNamesIn(definitionNode), inherited, []);
|
|
2974
|
+
}
|
|
2975
|
+
function calleeText(call) {
|
|
2976
|
+
const receiver = field(call, "receiver");
|
|
2977
|
+
const method = calleeMethodName(call);
|
|
2978
|
+
if (method === void 0) {
|
|
2979
|
+
return call.text;
|
|
2980
|
+
}
|
|
2981
|
+
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
2982
|
+
}
|
|
2983
|
+
function argOf(node) {
|
|
2984
|
+
const literal = LITERAL_ARGS[node.type]?.(node);
|
|
2985
|
+
if (literal !== null && literal !== void 0) {
|
|
2986
|
+
return literal;
|
|
2987
|
+
}
|
|
2988
|
+
if (node.type === "call" && !isPropertyRead2(node)) {
|
|
2989
|
+
return { kind: "call", callee: calleeText(node), args: argsOf(node) };
|
|
2990
|
+
}
|
|
2991
|
+
return { kind: "identifier", name: node.text };
|
|
2992
|
+
}
|
|
2993
|
+
function argsOf(call) {
|
|
2994
|
+
const args = field(call, "arguments");
|
|
2995
|
+
if (args === null) {
|
|
2996
|
+
return [];
|
|
2997
|
+
}
|
|
2998
|
+
return children2(args).map(
|
|
2999
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3000
|
+
);
|
|
3001
|
+
}
|
|
3002
|
+
function enclosingStatement(call, body) {
|
|
3003
|
+
let current = call;
|
|
3004
|
+
while (current.parent !== null) {
|
|
3005
|
+
if (BLOCK_TYPES2.has(current.parent.type) || current.parent.id === body.id) {
|
|
3006
|
+
return current;
|
|
3007
|
+
}
|
|
3008
|
+
current = current.parent;
|
|
3009
|
+
}
|
|
3010
|
+
return current;
|
|
3011
|
+
}
|
|
3012
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
3013
|
+
const body = field(definitionNode, "body");
|
|
3014
|
+
if (body === null) {
|
|
3015
|
+
return [];
|
|
3016
|
+
}
|
|
3017
|
+
const calls = withoutChainLinks(bodyCalls(definitionNode, inherited));
|
|
3018
|
+
if (calls.length === 0) {
|
|
3019
|
+
return [];
|
|
3020
|
+
}
|
|
3021
|
+
const statementOf2 = /* @__PURE__ */ new Map();
|
|
3022
|
+
const statements = [];
|
|
3023
|
+
for (const call of calls) {
|
|
3024
|
+
const statement = enclosingStatement(call, body);
|
|
3025
|
+
statementOf2.set(call.id, statement);
|
|
3026
|
+
statements.push(statement);
|
|
3027
|
+
}
|
|
3028
|
+
const lowered = lowerRubyBody(body, statements);
|
|
3029
|
+
const enumerated = (0, import_extractor4.enumerateOrDegrade)(
|
|
3030
|
+
{
|
|
3031
|
+
statements: lowered.statements,
|
|
3032
|
+
terminalsByStmt: lowered.terminalsByStmt
|
|
3033
|
+
},
|
|
3034
|
+
statements
|
|
3035
|
+
);
|
|
3036
|
+
return calls.map((call) => {
|
|
3037
|
+
const statement = statementOf2.get(call.id);
|
|
3038
|
+
const conditions = (0, import_extractor4.sharedGatingConditions)(
|
|
3039
|
+
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
3040
|
+
);
|
|
3041
|
+
return {
|
|
3042
|
+
type: "invocation",
|
|
3043
|
+
callee: calleeText(call),
|
|
3044
|
+
args: argsOf(call),
|
|
3045
|
+
async: false,
|
|
3046
|
+
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3047
|
+
};
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
3050
|
+
|
|
2831
3051
|
// src/storage.ts
|
|
2832
3052
|
var import_datalog2 = require("@suss/datalog");
|
|
2833
3053
|
var import_ir_core = require("@suss/ir-core");
|
|
@@ -3127,7 +3347,23 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3127
3347
|
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
3128
3348
|
return [];
|
|
3129
3349
|
}
|
|
3350
|
+
const filters = controllerFilters(pattern, ancestry);
|
|
3130
3351
|
const units = [];
|
|
3352
|
+
for (const filter of filters) {
|
|
3353
|
+
const displayPath = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
3354
|
+
const raw = filterUnit(
|
|
3355
|
+
filter,
|
|
3356
|
+
pattern,
|
|
3357
|
+
displayPath,
|
|
3358
|
+
bodyOfMethod(filter.method, options)
|
|
3359
|
+
);
|
|
3360
|
+
units.push(raw);
|
|
3361
|
+
options.onReachSeed?.(raw, {
|
|
3362
|
+
file: filter.file,
|
|
3363
|
+
node: filter.method,
|
|
3364
|
+
enclosingQualifiedName: filter.enclosingQualifiedName
|
|
3365
|
+
});
|
|
3366
|
+
}
|
|
3131
3367
|
for (const block of ownBlocks) {
|
|
3132
3368
|
if (block.info.bodyNode === null) {
|
|
3133
3369
|
continue;
|
|
@@ -3139,6 +3375,12 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3139
3375
|
if ((visibility.get(actionName) ?? "public") !== "public") {
|
|
3140
3376
|
continue;
|
|
3141
3377
|
}
|
|
3378
|
+
const around = filters.filter((filter) => filterCoversAction(filter, actionName)).map(
|
|
3379
|
+
(filter) => filterReference(
|
|
3380
|
+
filter,
|
|
3381
|
+
options.displayPathOf?.(filter.file) ?? filter.file
|
|
3382
|
+
)
|
|
3383
|
+
);
|
|
3142
3384
|
const raw = buildControllerActionUnit(
|
|
3143
3385
|
pack,
|
|
3144
3386
|
pattern,
|
|
@@ -3146,7 +3388,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3146
3388
|
actionName,
|
|
3147
3389
|
method,
|
|
3148
3390
|
options.filePath,
|
|
3149
|
-
options
|
|
3391
|
+
options,
|
|
3392
|
+
around
|
|
3150
3393
|
);
|
|
3151
3394
|
units.push(raw);
|
|
3152
3395
|
options.onReachSeed?.(raw, {
|
|
@@ -3158,7 +3401,7 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
3158
3401
|
}
|
|
3159
3402
|
return units;
|
|
3160
3403
|
}
|
|
3161
|
-
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, method, filePath, bodyRead) {
|
|
3404
|
+
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, method, filePath, bodyRead, wrappers = []) {
|
|
3162
3405
|
const range = rangeOf(method);
|
|
3163
3406
|
const route = pattern.routeFor(controllerQualifiedName, actionName);
|
|
3164
3407
|
const body = bodyOfMethod(method, bodyRead);
|
|
@@ -3213,7 +3456,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
3213
3456
|
],
|
|
3214
3457
|
bodyContent: body.bodyContent ?? "absent",
|
|
3215
3458
|
dependencyCalls: [],
|
|
3216
|
-
declaredContract: null
|
|
3459
|
+
declaredContract: null,
|
|
3460
|
+
...wrappers.length > 0 ? { wrappers: [...wrappers] } : {}
|
|
3217
3461
|
};
|
|
3218
3462
|
}
|
|
3219
3463
|
function routingGapUnit(pattern, gaps) {
|
|
@@ -4379,6 +4623,14 @@ function inheritedMethodsIn(packs) {
|
|
|
4379
4623
|
}
|
|
4380
4624
|
return found;
|
|
4381
4625
|
}
|
|
4626
|
+
function alreadyDiscovered(seen, raw) {
|
|
4627
|
+
const key = `${raw.identity.file}::${raw.identity.name}::${raw.identity.range.start}`;
|
|
4628
|
+
if (seen.has(key)) {
|
|
4629
|
+
return true;
|
|
4630
|
+
}
|
|
4631
|
+
seen.add(key);
|
|
4632
|
+
return false;
|
|
4633
|
+
}
|
|
4382
4634
|
async function extractRubyProject(options) {
|
|
4383
4635
|
const timer = options.onTiming !== void 0 ? (0, import_extractor10.createTimer)() : (0, import_extractor10.noopTimer)();
|
|
4384
4636
|
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
@@ -4440,6 +4692,7 @@ async function extractRubyProject(options) {
|
|
|
4440
4692
|
const displayPathOf = (file) => options.workspaceRoot !== void 0 ? import_node_path4.default.relative(options.workspaceRoot, file) : file;
|
|
4441
4693
|
const seeds = [];
|
|
4442
4694
|
const summariesBySeed = /* @__PURE__ */ new Map();
|
|
4695
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
4443
4696
|
for (const { file, root } of parsed) {
|
|
4444
4697
|
const displayPath = displayPathOf(file);
|
|
4445
4698
|
const seedByRaw = /* @__PURE__ */ new Map();
|
|
@@ -4452,10 +4705,14 @@ async function extractRubyProject(options) {
|
|
|
4452
4705
|
cache,
|
|
4453
4706
|
...storagePatterns.length > 0 ? { storage: { facts: db, patterns: storagePatterns } } : {},
|
|
4454
4707
|
inheritedMethods,
|
|
4708
|
+
displayPathOf,
|
|
4455
4709
|
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
4456
4710
|
})
|
|
4457
4711
|
);
|
|
4458
4712
|
for (const raw of rawUnits) {
|
|
4713
|
+
if (alreadyDiscovered(discovered, raw)) {
|
|
4714
|
+
continue;
|
|
4715
|
+
}
|
|
4459
4716
|
const summary = timer.time(
|
|
4460
4717
|
"summarize",
|
|
4461
4718
|
() => (0, import_extractor10.assembleSummary)(raw, { gapHandling: "permissive" })
|
|
@@ -4558,12 +4815,18 @@ async function extractRubyProject(options) {
|
|
|
4558
4815
|
}
|
|
4559
4816
|
(0, import_behavioral_ir4.disambiguateSummaryIds)(summaries);
|
|
4560
4817
|
(0, import_behavioral_ir4.linkCallsToSummaries)(summaries);
|
|
4818
|
+
const composed = timer.time(
|
|
4819
|
+
"summarize",
|
|
4820
|
+
() => (0, import_extractor10.composeWrappers)(summaries, {
|
|
4821
|
+
gapHandling: options.gapHandling ?? "permissive"
|
|
4822
|
+
})
|
|
4823
|
+
);
|
|
4561
4824
|
await timer.timeAsync("cache.write", async () => {
|
|
4562
|
-
if (cacheDir === null ||
|
|
4825
|
+
if (cacheDir === null || composed.length === 0) {
|
|
4563
4826
|
return;
|
|
4564
4827
|
}
|
|
4565
4828
|
try {
|
|
4566
|
-
await extractionCache.write(cacheInput,
|
|
4829
|
+
await extractionCache.write(cacheInput, composed);
|
|
4567
4830
|
} catch {
|
|
4568
4831
|
}
|
|
4569
4832
|
});
|
|
@@ -4572,11 +4835,11 @@ async function extractRubyProject(options) {
|
|
|
4572
4835
|
packs: options.packs,
|
|
4573
4836
|
tallies,
|
|
4574
4837
|
filesWalked: options.files.length,
|
|
4575
|
-
summaries
|
|
4838
|
+
summaries: composed
|
|
4576
4839
|
})
|
|
4577
4840
|
);
|
|
4578
4841
|
options.onTiming?.(timer.report());
|
|
4579
|
-
return { summaries, facts: db };
|
|
4842
|
+
return { summaries: composed, facts: db };
|
|
4580
4843
|
}
|
|
4581
4844
|
function fileDependenciesOf(db, displayPathOf) {
|
|
4582
4845
|
const byFile = /* @__PURE__ */ new Map();
|