@suss/adapter-ruby 0.23.0 → 0.24.1
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 +25 -264
- package/dist/index.cjs +764 -366
- package/dist/index.d.cts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +746 -348
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -607,113 +607,15 @@ function inheritedStatements(ancestry, blockConfigures) {
|
|
|
607
607
|
}
|
|
608
608
|
|
|
609
609
|
// src/discovery.ts
|
|
610
|
-
var
|
|
610
|
+
var import_behavioral_ir3 = require("@suss/behavioral-ir");
|
|
611
611
|
var import_extractor5 = require("@suss/extractor");
|
|
612
612
|
|
|
613
|
-
// src/
|
|
613
|
+
// src/clientCalls.ts
|
|
614
614
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
615
|
-
var
|
|
616
|
-
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
617
|
-
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
618
|
-
function envReadEffects(root) {
|
|
619
|
-
const effects = [];
|
|
620
|
-
(0, import_extractor2.walkDescendants)(root, null, {
|
|
621
|
-
at: (node) => {
|
|
622
|
-
const read = envReadAt(node);
|
|
623
|
-
if (read !== null) {
|
|
624
|
-
effects.push(configReadEffect(read));
|
|
625
|
-
}
|
|
626
|
-
},
|
|
627
|
-
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? import_extractor2.SKIP_CHILDREN : null
|
|
628
|
-
});
|
|
629
|
-
return effects;
|
|
630
|
-
}
|
|
631
|
-
function configReadEffect(read) {
|
|
632
|
-
return {
|
|
633
|
-
type: "interaction",
|
|
634
|
-
binding: (0, import_behavioral_ir.runtimeConfigBinding)({
|
|
635
|
-
recognition: RUBY_ENV_RECOGNITION,
|
|
636
|
-
deploymentTarget: "lambda",
|
|
637
|
-
instanceName: "<unknown>"
|
|
638
|
-
}),
|
|
639
|
-
callee: `ENV["${read.name}"]`,
|
|
640
|
-
interaction: {
|
|
641
|
-
class: "config-read",
|
|
642
|
-
name: read.name,
|
|
643
|
-
defaulted: read.defaulted
|
|
644
|
-
}
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
function envReadAt(node) {
|
|
648
|
-
if (node.type === "element_reference") {
|
|
649
|
-
return elementRead(node);
|
|
650
|
-
}
|
|
651
|
-
if (node.type === "call") {
|
|
652
|
-
return fetchRead(node);
|
|
653
|
-
}
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
function elementRead(node) {
|
|
657
|
-
const object = field(node, "object");
|
|
658
|
-
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
659
|
-
return null;
|
|
660
|
-
}
|
|
661
|
-
const index = node.namedChildren.find(
|
|
662
|
-
(child) => child !== null && child.id !== object.id
|
|
663
|
-
);
|
|
664
|
-
const name = index === void 0 ? null : stringLiteralValue(index);
|
|
665
|
-
if (name === null) {
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
669
|
-
}
|
|
670
|
-
function fetchRead(node) {
|
|
671
|
-
const receiver = field(node, "receiver");
|
|
672
|
-
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
673
|
-
return null;
|
|
674
|
-
}
|
|
675
|
-
const { positional } = readCallArgs(field(node, "arguments"));
|
|
676
|
-
const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
677
|
-
if (name === null) {
|
|
678
|
-
return null;
|
|
679
|
-
}
|
|
680
|
-
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
681
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
682
|
-
}
|
|
683
|
-
function isEnv(node) {
|
|
684
|
-
if (node.type === "constant") {
|
|
685
|
-
return node.text === "ENV";
|
|
686
|
-
}
|
|
687
|
-
return node.type === "scope_resolution" && field(node, "scope") === null && field(node, "name")?.text === "ENV";
|
|
688
|
-
}
|
|
689
|
-
function isAssignedTo(node) {
|
|
690
|
-
const parent = node.parent;
|
|
691
|
-
return parent !== null && (parent.type === "assignment" || parent.type === "operator_assignment") && field(parent, "left")?.id === node.id;
|
|
692
|
-
}
|
|
693
|
-
function isDefaultedAt(node) {
|
|
694
|
-
let child = node;
|
|
695
|
-
let parent = node.parent;
|
|
696
|
-
while (parent !== null) {
|
|
697
|
-
if (parent.type === "parenthesized_statements") {
|
|
698
|
-
child = parent;
|
|
699
|
-
parent = parent.parent;
|
|
700
|
-
continue;
|
|
701
|
-
}
|
|
702
|
-
if (parent.type !== "binary" || field(parent, "operator")?.text !== "||") {
|
|
703
|
-
return false;
|
|
704
|
-
}
|
|
705
|
-
if (field(parent, "left")?.id === child.id) {
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
child = parent;
|
|
709
|
-
parent = parent.parent;
|
|
710
|
-
}
|
|
711
|
-
return false;
|
|
712
|
-
}
|
|
615
|
+
var import_values5 = require("@suss/values");
|
|
713
616
|
|
|
714
|
-
// src/
|
|
715
|
-
var
|
|
716
|
-
var import_values4 = require("@suss/values");
|
|
617
|
+
// src/paths/effects.ts
|
|
618
|
+
var import_extractor2 = require("@suss/extractor");
|
|
717
619
|
|
|
718
620
|
// src/paths/bareCalls.ts
|
|
719
621
|
var PARAMETER_LIST_TYPES = [
|
|
@@ -1076,17 +978,297 @@ var Lowerer = class {
|
|
|
1076
978
|
}
|
|
1077
979
|
return this.attach({ kind: "opaque", exitKind: exitKindOf(node) }, [node]);
|
|
1078
980
|
}
|
|
1079
|
-
};
|
|
1080
|
-
function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
1081
|
-
const lowerer = new Lowerer(
|
|
1082
|
-
new Map(terminals.map((terminal) => [terminal.id, terminal])),
|
|
1083
|
-
new NodeSet(responseCalls)
|
|
1084
|
-
);
|
|
1085
|
-
return {
|
|
1086
|
-
statements: lowerer.lowerBlock(body),
|
|
1087
|
-
terminalsByStmt: lowerer.terminalsByStmt,
|
|
1088
|
-
terminalHome: lowerer.terminalHome
|
|
1089
|
-
};
|
|
981
|
+
};
|
|
982
|
+
function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
983
|
+
const lowerer = new Lowerer(
|
|
984
|
+
new Map(terminals.map((terminal) => [terminal.id, terminal])),
|
|
985
|
+
new NodeSet(responseCalls)
|
|
986
|
+
);
|
|
987
|
+
return {
|
|
988
|
+
statements: lowerer.lowerBlock(body),
|
|
989
|
+
terminalsByStmt: lowerer.terminalsByStmt,
|
|
990
|
+
terminalHome: lowerer.terminalHome
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/paths/effects.ts
|
|
995
|
+
var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
996
|
+
var BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
997
|
+
"body_statement",
|
|
998
|
+
"then",
|
|
999
|
+
"else",
|
|
1000
|
+
"do_block",
|
|
1001
|
+
"block"
|
|
1002
|
+
]);
|
|
1003
|
+
var LITERAL_ARGS = {
|
|
1004
|
+
string: (node) => ({
|
|
1005
|
+
kind: "string",
|
|
1006
|
+
value: node.text.replace(/^["']|["']$/g, "")
|
|
1007
|
+
}),
|
|
1008
|
+
integer: (node) => {
|
|
1009
|
+
const value = Number.parseInt(node.text, 10);
|
|
1010
|
+
return Number.isNaN(value) ? null : { kind: "number", value };
|
|
1011
|
+
},
|
|
1012
|
+
float: (node) => {
|
|
1013
|
+
const value = Number.parseFloat(node.text);
|
|
1014
|
+
return Number.isNaN(value) ? null : { kind: "number", value };
|
|
1015
|
+
},
|
|
1016
|
+
true: () => ({ kind: "boolean", value: true }),
|
|
1017
|
+
false: () => ({ kind: "boolean", value: false }),
|
|
1018
|
+
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
1019
|
+
};
|
|
1020
|
+
function children(node) {
|
|
1021
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
1022
|
+
}
|
|
1023
|
+
function withoutChainLinks(calls) {
|
|
1024
|
+
const isLink = /* @__PURE__ */ new Set();
|
|
1025
|
+
for (const call of calls) {
|
|
1026
|
+
const receiver = field(call, "receiver");
|
|
1027
|
+
if (receiver !== null && receiver.type === "call") {
|
|
1028
|
+
isLink.add(receiver.id);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return calls.filter((call) => !isLink.has(call.id));
|
|
1032
|
+
}
|
|
1033
|
+
function calleeMethodName(call) {
|
|
1034
|
+
if (call.type === "identifier") {
|
|
1035
|
+
return call.text;
|
|
1036
|
+
}
|
|
1037
|
+
const method = field(call, "method");
|
|
1038
|
+
if (method !== null) {
|
|
1039
|
+
return method.text;
|
|
1040
|
+
}
|
|
1041
|
+
return field(call, "receiver") !== null ? "call" : children(call)[0]?.text;
|
|
1042
|
+
}
|
|
1043
|
+
function isPropertyRead(node) {
|
|
1044
|
+
if (field(node, "receiver") === null || field(node, "arguments") !== null) {
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
return calleeMethodName(node) !== "call";
|
|
1048
|
+
}
|
|
1049
|
+
function isRaise2(node) {
|
|
1050
|
+
const method = field(node, "method") ?? children(node)[0] ?? null;
|
|
1051
|
+
return field(node, "receiver") === null && method !== null && RAISE_NAMES2.has(method.text);
|
|
1052
|
+
}
|
|
1053
|
+
var NO_INHERITED_METHODS = /* @__PURE__ */ new Set();
|
|
1054
|
+
function isInherited(node, inherited) {
|
|
1055
|
+
const method = field(node, "method");
|
|
1056
|
+
return field(node, "receiver") === null && method !== null && inherited.has(method.text);
|
|
1057
|
+
}
|
|
1058
|
+
function isCall(node, locals, inherited) {
|
|
1059
|
+
if (node.type === "call") {
|
|
1060
|
+
return !isPropertyRead(node) && !isRaise2(node) && !isInherited(node, inherited);
|
|
1061
|
+
}
|
|
1062
|
+
return isBareMethodCall(node, locals) && !RAISE_NAMES2.has(node.text) && !inherited.has(node.text);
|
|
1063
|
+
}
|
|
1064
|
+
function collectCalls(node, locals, inherited, found) {
|
|
1065
|
+
for (const child of children(node)) {
|
|
1066
|
+
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
if (isCall(child, locals, inherited)) {
|
|
1070
|
+
found.push(child);
|
|
1071
|
+
}
|
|
1072
|
+
collectCalls(child, locals, inherited, found);
|
|
1073
|
+
}
|
|
1074
|
+
return found;
|
|
1075
|
+
}
|
|
1076
|
+
function bodyCalls(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
1077
|
+
const body = field(definitionNode, "body");
|
|
1078
|
+
if (body === null) {
|
|
1079
|
+
return [];
|
|
1080
|
+
}
|
|
1081
|
+
return collectCalls(body, localNamesIn(definitionNode), inherited, []);
|
|
1082
|
+
}
|
|
1083
|
+
function calleeText(call) {
|
|
1084
|
+
const receiver = field(call, "receiver");
|
|
1085
|
+
const method = calleeMethodName(call);
|
|
1086
|
+
if (method === void 0) {
|
|
1087
|
+
return call.text;
|
|
1088
|
+
}
|
|
1089
|
+
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
1090
|
+
}
|
|
1091
|
+
function argOf(node) {
|
|
1092
|
+
const literal = LITERAL_ARGS[node.type]?.(node);
|
|
1093
|
+
if (literal !== null && literal !== void 0) {
|
|
1094
|
+
return literal;
|
|
1095
|
+
}
|
|
1096
|
+
if (node.type === "call" && !isPropertyRead(node)) {
|
|
1097
|
+
return { kind: "call", callee: calleeText(node), args: argsOf(node) };
|
|
1098
|
+
}
|
|
1099
|
+
return { kind: "identifier", name: node.text };
|
|
1100
|
+
}
|
|
1101
|
+
function argsOf(call) {
|
|
1102
|
+
const args = field(call, "arguments");
|
|
1103
|
+
if (args === null) {
|
|
1104
|
+
return [];
|
|
1105
|
+
}
|
|
1106
|
+
return children(args).map(
|
|
1107
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
function enclosingStatement(call, body) {
|
|
1111
|
+
let current = call;
|
|
1112
|
+
while (current.parent !== null) {
|
|
1113
|
+
if (BLOCK_TYPES.has(current.parent.type) || current.parent.id === body.id) {
|
|
1114
|
+
return current;
|
|
1115
|
+
}
|
|
1116
|
+
current = current.parent;
|
|
1117
|
+
}
|
|
1118
|
+
return current;
|
|
1119
|
+
}
|
|
1120
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS) {
|
|
1121
|
+
const body = field(definitionNode, "body");
|
|
1122
|
+
if (body === null) {
|
|
1123
|
+
return [];
|
|
1124
|
+
}
|
|
1125
|
+
const calls = withoutChainLinks(bodyCalls(definitionNode, inherited));
|
|
1126
|
+
if (calls.length === 0) {
|
|
1127
|
+
return [];
|
|
1128
|
+
}
|
|
1129
|
+
const statementOf2 = /* @__PURE__ */ new Map();
|
|
1130
|
+
const statements = [];
|
|
1131
|
+
for (const call of calls) {
|
|
1132
|
+
const statement = enclosingStatement(call, body);
|
|
1133
|
+
statementOf2.set(call.id, statement);
|
|
1134
|
+
statements.push(statement);
|
|
1135
|
+
}
|
|
1136
|
+
const lowered = lowerRubyBody(body, statements);
|
|
1137
|
+
const enumerated = (0, import_extractor2.enumerateOrDegrade)(
|
|
1138
|
+
{
|
|
1139
|
+
statements: lowered.statements,
|
|
1140
|
+
terminalsByStmt: lowered.terminalsByStmt
|
|
1141
|
+
},
|
|
1142
|
+
statements
|
|
1143
|
+
);
|
|
1144
|
+
return calls.map((call) => {
|
|
1145
|
+
const statement = statementOf2.get(call.id);
|
|
1146
|
+
const conditions = (0, import_extractor2.sharedGatingConditions)(
|
|
1147
|
+
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
1148
|
+
);
|
|
1149
|
+
return {
|
|
1150
|
+
type: "invocation",
|
|
1151
|
+
callee: calleeText(call),
|
|
1152
|
+
args: argsOf(call),
|
|
1153
|
+
async: false,
|
|
1154
|
+
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
1155
|
+
};
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// src/responseStatus.ts
|
|
1160
|
+
var import_extractor3 = require("@suss/extractor");
|
|
1161
|
+
var import_values4 = require("@suss/values");
|
|
1162
|
+
|
|
1163
|
+
// src/paths/predicates.ts
|
|
1164
|
+
var OPERATORS = {
|
|
1165
|
+
"==": "eq",
|
|
1166
|
+
"!=": "neq",
|
|
1167
|
+
">": "gt",
|
|
1168
|
+
">=": "gte",
|
|
1169
|
+
"<": "lt",
|
|
1170
|
+
"<=": "lte"
|
|
1171
|
+
};
|
|
1172
|
+
var CONVERSIONS = /* @__PURE__ */ new Set(["to_i", "to_s", "to_sym"]);
|
|
1173
|
+
var opaqueOf = (node) => ({
|
|
1174
|
+
type: "opaque",
|
|
1175
|
+
sourceText: node.text,
|
|
1176
|
+
reason: "complexExpression"
|
|
1177
|
+
});
|
|
1178
|
+
function predicateOf(node) {
|
|
1179
|
+
if (node.type === "parenthesized_statements") {
|
|
1180
|
+
const inner = node.namedChildren[0];
|
|
1181
|
+
return inner == null ? opaqueOf(node) : predicateOf(inner);
|
|
1182
|
+
}
|
|
1183
|
+
if (node.type === "unary" && field(node, "operator")?.text === "!") {
|
|
1184
|
+
const operand2 = field(node, "operand") ?? node.namedChildren[0];
|
|
1185
|
+
return operand2 == null ? opaqueOf(node) : { type: "negation", operand: predicateOf(operand2) };
|
|
1186
|
+
}
|
|
1187
|
+
if (node.type === "binary") {
|
|
1188
|
+
return binaryOf(node);
|
|
1189
|
+
}
|
|
1190
|
+
const asked = calledMethod(node);
|
|
1191
|
+
if (asked === "nil?") {
|
|
1192
|
+
const receiver = field(node, "receiver");
|
|
1193
|
+
return receiver === null ? opaqueOf(node) : { type: "nullCheck", subject: valueRefOf(receiver), negated: false };
|
|
1194
|
+
}
|
|
1195
|
+
if (node.type === "identifier" || memberChain(node) !== null) {
|
|
1196
|
+
return {
|
|
1197
|
+
type: "truthinessCheck",
|
|
1198
|
+
subject: valueRefOf(node),
|
|
1199
|
+
negated: false
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
return opaqueOf(node);
|
|
1203
|
+
}
|
|
1204
|
+
function binaryOf(node) {
|
|
1205
|
+
const operator = field(node, "operator")?.text ?? "";
|
|
1206
|
+
const left = field(node, "left");
|
|
1207
|
+
const right = field(node, "right");
|
|
1208
|
+
if (left === null || right === null) {
|
|
1209
|
+
return opaqueOf(node);
|
|
1210
|
+
}
|
|
1211
|
+
const op = OPERATORS[operator];
|
|
1212
|
+
if (op !== void 0) {
|
|
1213
|
+
return {
|
|
1214
|
+
type: "comparison",
|
|
1215
|
+
left: valueRefOf(left),
|
|
1216
|
+
op,
|
|
1217
|
+
right: valueRefOf(right)
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
if (operator === "&&" || operator === "||" || operator === "and" || operator === "or") {
|
|
1221
|
+
return {
|
|
1222
|
+
type: "compound",
|
|
1223
|
+
op: operator === "&&" || operator === "and" ? "and" : "or",
|
|
1224
|
+
operands: [predicateOf(left), predicateOf(right)]
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
return opaqueOf(node);
|
|
1228
|
+
}
|
|
1229
|
+
function valueRefOf(node) {
|
|
1230
|
+
const literal = literalOf(node);
|
|
1231
|
+
if (literal !== null) {
|
|
1232
|
+
return literal;
|
|
1233
|
+
}
|
|
1234
|
+
const chain = memberChain(node);
|
|
1235
|
+
if (chain !== null) {
|
|
1236
|
+
const [name, ...accessChain] = chain;
|
|
1237
|
+
if (name !== void 0 && accessChain.length > 0) {
|
|
1238
|
+
return { type: "dependency", name, accessChain };
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return { type: "unresolved", sourceText: node.text };
|
|
1242
|
+
}
|
|
1243
|
+
function literalOf(node) {
|
|
1244
|
+
if (node.type === "integer") {
|
|
1245
|
+
const value = Number.parseInt(node.text, 10);
|
|
1246
|
+
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
1247
|
+
}
|
|
1248
|
+
const string = stringLiteralValue(node);
|
|
1249
|
+
if (string !== null) {
|
|
1250
|
+
return { type: "literal", value: string };
|
|
1251
|
+
}
|
|
1252
|
+
const boolean = booleanLiteralValue(node);
|
|
1253
|
+
return boolean === null ? null : { type: "literal", value: boolean };
|
|
1254
|
+
}
|
|
1255
|
+
function memberChain(node, tail = []) {
|
|
1256
|
+
if (node.type === "identifier") {
|
|
1257
|
+
return [node.text, ...tail];
|
|
1258
|
+
}
|
|
1259
|
+
const called = calledMethod(node);
|
|
1260
|
+
const receiver = field(node, "receiver");
|
|
1261
|
+
if (called === null || receiver === null || field(node, "arguments") !== null) {
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
const rest = CONVERSIONS.has(called) && tail.length === 0 ? tail : [called, ...tail];
|
|
1265
|
+
return memberChain(receiver, rest);
|
|
1266
|
+
}
|
|
1267
|
+
function calledMethod(node) {
|
|
1268
|
+
if (node.type !== "call" || field(node, "block") !== null) {
|
|
1269
|
+
return null;
|
|
1270
|
+
}
|
|
1271
|
+
return field(node, "method")?.text ?? null;
|
|
1090
1272
|
}
|
|
1091
1273
|
|
|
1092
1274
|
// src/values/evaluator.ts
|
|
@@ -1144,7 +1326,7 @@ function paramNameOf(param) {
|
|
|
1144
1326
|
function declaredParamNames(method) {
|
|
1145
1327
|
const params = field(method, "parameters");
|
|
1146
1328
|
const declared = /* @__PURE__ */ new Set();
|
|
1147
|
-
for (const param of params === null ? [] :
|
|
1329
|
+
for (const param of params === null ? [] : children2(params)) {
|
|
1148
1330
|
const name = paramNameOf(param);
|
|
1149
1331
|
if (name !== null) {
|
|
1150
1332
|
declared.add(name.text);
|
|
@@ -1158,7 +1340,7 @@ function readKey(filePath, node, enclosing) {
|
|
|
1158
1340
|
}
|
|
1159
1341
|
const parent = node.parent;
|
|
1160
1342
|
if (parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null) {
|
|
1161
|
-
return
|
|
1343
|
+
return isPropertyRead2(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
1162
1344
|
}
|
|
1163
1345
|
if (enclosing !== null && declaredParamNames(enclosing).has(node.text)) {
|
|
1164
1346
|
return `${nodeId(filePath, enclosing)}#${node.text}`;
|
|
@@ -1186,7 +1368,7 @@ var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1186
1368
|
"parenthesized_statements"
|
|
1187
1369
|
]);
|
|
1188
1370
|
var ARRAY_TYPES = /* @__PURE__ */ new Set(["array", "string_array", "symbol_array"]);
|
|
1189
|
-
function
|
|
1371
|
+
function children2(node) {
|
|
1190
1372
|
return node.namedChildren.filter((child) => child !== null);
|
|
1191
1373
|
}
|
|
1192
1374
|
function add(emitter, relation, ...tuple) {
|
|
@@ -1214,8 +1396,8 @@ function pairKeyText(key) {
|
|
|
1214
1396
|
}
|
|
1215
1397
|
return null;
|
|
1216
1398
|
}
|
|
1217
|
-
function
|
|
1218
|
-
return node.type === "call" && field(node, "receiver") !== null && field(node, "arguments") === null &&
|
|
1399
|
+
function isPropertyRead2(node) {
|
|
1400
|
+
return node.type === "call" && field(node, "receiver") !== null && field(node, "arguments") === null && children2(node).every(
|
|
1219
1401
|
(child) => child.type !== "do_block" && child.type !== "block"
|
|
1220
1402
|
);
|
|
1221
1403
|
}
|
|
@@ -1254,7 +1436,7 @@ function emitCall(emitter, call) {
|
|
|
1254
1436
|
}
|
|
1255
1437
|
const args = field(call, "arguments");
|
|
1256
1438
|
let position = 0;
|
|
1257
|
-
for (const argument of args === null ? [] :
|
|
1439
|
+
for (const argument of args === null ? [] : children2(args)) {
|
|
1258
1440
|
if (argument.type === "pair") {
|
|
1259
1441
|
const key = field(argument, "key");
|
|
1260
1442
|
const value = field(argument, "value");
|
|
@@ -1284,7 +1466,7 @@ function emitArray(emitter, array) {
|
|
|
1284
1466
|
const objectKey = nodeId(emitter.filePath, array);
|
|
1285
1467
|
add(emitter, "objectValue", objectKey);
|
|
1286
1468
|
let position = 0;
|
|
1287
|
-
for (const element of
|
|
1469
|
+
for (const element of children2(array)) {
|
|
1288
1470
|
add(
|
|
1289
1471
|
emitter,
|
|
1290
1472
|
"holdsProperty",
|
|
@@ -1298,7 +1480,7 @@ function emitArray(emitter, array) {
|
|
|
1298
1480
|
function emitHash(emitter, hash) {
|
|
1299
1481
|
const objectKey = nodeId(emitter.filePath, hash);
|
|
1300
1482
|
add(emitter, "objectValue", objectKey);
|
|
1301
|
-
for (const pair of
|
|
1483
|
+
for (const pair of children2(hash)) {
|
|
1302
1484
|
if (pair.type !== "pair") {
|
|
1303
1485
|
continue;
|
|
1304
1486
|
}
|
|
@@ -1312,7 +1494,7 @@ function emitHash(emitter, hash) {
|
|
|
1312
1494
|
}
|
|
1313
1495
|
}
|
|
1314
1496
|
function walkExpressions(node, visit) {
|
|
1315
|
-
for (const child of
|
|
1497
|
+
for (const child of children2(node)) {
|
|
1316
1498
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1317
1499
|
continue;
|
|
1318
1500
|
}
|
|
@@ -1322,7 +1504,7 @@ function walkExpressions(node, visit) {
|
|
|
1322
1504
|
}
|
|
1323
1505
|
function emitExpressionFacts(emitter, node) {
|
|
1324
1506
|
walkExpressions(node, (child) => {
|
|
1325
|
-
if (
|
|
1507
|
+
if (isPropertyRead2(child)) {
|
|
1326
1508
|
emitPropertyRead(emitter, child);
|
|
1327
1509
|
} else if (child.type === "call") {
|
|
1328
1510
|
emitCall(emitter, child);
|
|
@@ -1342,7 +1524,7 @@ function emitExpressionFacts(emitter, node) {
|
|
|
1342
1524
|
});
|
|
1343
1525
|
}
|
|
1344
1526
|
function implicitReturn(body) {
|
|
1345
|
-
const statements =
|
|
1527
|
+
const statements = children2(body).filter(
|
|
1346
1528
|
(child) => child.type !== "rescue" && child.type !== "ensure"
|
|
1347
1529
|
);
|
|
1348
1530
|
const last = statements[statements.length - 1];
|
|
@@ -1354,7 +1536,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1354
1536
|
const params = field(method, "parameters");
|
|
1355
1537
|
const declared = /* @__PURE__ */ new Set();
|
|
1356
1538
|
let position = 0;
|
|
1357
|
-
for (const param of params === null ? [] :
|
|
1539
|
+
for (const param of params === null ? [] : children2(params)) {
|
|
1358
1540
|
const paramName = paramNameOf(param);
|
|
1359
1541
|
if (paramName !== null) {
|
|
1360
1542
|
declared.add(paramName.text);
|
|
@@ -1373,7 +1555,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1373
1555
|
return funcKey;
|
|
1374
1556
|
}
|
|
1375
1557
|
const recordNested = (node) => {
|
|
1376
|
-
for (const child of
|
|
1558
|
+
for (const child of children2(node)) {
|
|
1377
1559
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1378
1560
|
add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
|
|
1379
1561
|
continue;
|
|
@@ -1384,8 +1566,8 @@ function emitMethodFacts(emitter, method) {
|
|
|
1384
1566
|
recordNested(body);
|
|
1385
1567
|
walkExpressions(body, (child) => {
|
|
1386
1568
|
if (child.type === "return") {
|
|
1387
|
-
const first =
|
|
1388
|
-
const returned = first?.type === "argument_list" ?
|
|
1569
|
+
const first = children2(child)[0];
|
|
1570
|
+
const returned = first?.type === "argument_list" ? children2(first)[0] : first;
|
|
1389
1571
|
if (returned !== void 0) {
|
|
1390
1572
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
1391
1573
|
}
|
|
@@ -1429,13 +1611,13 @@ function emitClassFacts(emitter, cls) {
|
|
|
1429
1611
|
const classKey = nodeId(emitter.filePath, cls);
|
|
1430
1612
|
add(emitter, "objectValue", classKey);
|
|
1431
1613
|
const superclass = field(cls, "superclass");
|
|
1432
|
-
const base = superclass === null ? null :
|
|
1614
|
+
const base = superclass === null ? null : children2(superclass)[0] ?? null;
|
|
1433
1615
|
if (base !== null) {
|
|
1434
1616
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
1435
1617
|
add(emitter, "extendsNamed", classKey, base.text);
|
|
1436
1618
|
}
|
|
1437
1619
|
const body = field(cls, "body");
|
|
1438
|
-
for (const statement of body === null ? [] :
|
|
1620
|
+
for (const statement of body === null ? [] : children2(body)) {
|
|
1439
1621
|
if (statement.type === "assignment") {
|
|
1440
1622
|
const left = field(statement, "left");
|
|
1441
1623
|
const right = field(statement, "right");
|
|
@@ -1477,7 +1659,7 @@ function emitValueFacts(db, filePath, root) {
|
|
|
1477
1659
|
add(emitter, "exportsAs", filePath, name.text, key);
|
|
1478
1660
|
};
|
|
1479
1661
|
const walk = (node) => {
|
|
1480
|
-
for (const child of
|
|
1662
|
+
for (const child of children2(node)) {
|
|
1481
1663
|
if (NESTING_TYPES.has(child.type)) {
|
|
1482
1664
|
declaresName(child, emitClassFacts(emitter, child));
|
|
1483
1665
|
continue;
|
|
@@ -1508,7 +1690,7 @@ var OWN_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1508
1690
|
"module",
|
|
1509
1691
|
"singleton_class"
|
|
1510
1692
|
]);
|
|
1511
|
-
var
|
|
1693
|
+
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1512
1694
|
var METHOD_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1513
1695
|
var NAME_TYPES = /* @__PURE__ */ new Set([
|
|
1514
1696
|
"identifier",
|
|
@@ -2014,7 +2196,7 @@ function isTailExpression(node) {
|
|
|
2014
2196
|
if (owner.type === "block" && owner.parent?.type === "lambda") {
|
|
2015
2197
|
return true;
|
|
2016
2198
|
}
|
|
2017
|
-
if (METHOD_TYPES2.has(owner.type) ||
|
|
2199
|
+
if (METHOD_TYPES2.has(owner.type) || BLOCK_TYPES2.has(owner.type)) {
|
|
2018
2200
|
return true;
|
|
2019
2201
|
}
|
|
2020
2202
|
if (owner.type === "when") {
|
|
@@ -2033,7 +2215,7 @@ function isRoot(node) {
|
|
|
2033
2215
|
if (OWN_ROOT_TYPES.has(node.type)) {
|
|
2034
2216
|
return true;
|
|
2035
2217
|
}
|
|
2036
|
-
return
|
|
2218
|
+
return BLOCK_TYPES2.has(node.type) && node.parent?.type === "call";
|
|
2037
2219
|
}
|
|
2038
2220
|
function isPathStatement(node, parent) {
|
|
2039
2221
|
if (node.type === "elsif") {
|
|
@@ -2087,7 +2269,7 @@ function functionOf(node) {
|
|
|
2087
2269
|
body: bodyOf(block)
|
|
2088
2270
|
};
|
|
2089
2271
|
}
|
|
2090
|
-
if (
|
|
2272
|
+
if (BLOCK_TYPES2.has(node.type)) {
|
|
2091
2273
|
return {
|
|
2092
2274
|
parameters: parametersOf(field(node, "parameters")),
|
|
2093
2275
|
body: bodyOf(node)
|
|
@@ -2405,6 +2587,18 @@ var calleeRows = [
|
|
|
2405
2587
|
kind: "callee",
|
|
2406
2588
|
origin: { module: "Kernel", name: "String" },
|
|
2407
2589
|
apply: ({ args }) => ({ result: (0, import_values.concat)([(0, import_values.operand)(args[0])]) })
|
|
2590
|
+
},
|
|
2591
|
+
// A URI is the string it was parsed from, as far as a reader of paths
|
|
2592
|
+
// is concerned, and both spellings are the standard library's own.
|
|
2593
|
+
{
|
|
2594
|
+
kind: "callee",
|
|
2595
|
+
origin: { module: "Kernel", name: "URI" },
|
|
2596
|
+
apply: ({ args }) => ({ result: (0, import_values.concat)([(0, import_values.operand)(args[0])]) })
|
|
2597
|
+
},
|
|
2598
|
+
{
|
|
2599
|
+
kind: "callee",
|
|
2600
|
+
origin: { module: "URI", name: "parse" },
|
|
2601
|
+
apply: ({ args }) => ({ result: (0, import_values.concat)([(0, import_values.operand)(args[0])]) })
|
|
2408
2602
|
}
|
|
2409
2603
|
];
|
|
2410
2604
|
var rubyRows = [
|
|
@@ -2592,7 +2786,7 @@ function effectsReaching(effects, conditions) {
|
|
|
2592
2786
|
function conditionOf(condition) {
|
|
2593
2787
|
return {
|
|
2594
2788
|
sourceText: condition.sourceText,
|
|
2595
|
-
structured: null,
|
|
2789
|
+
structured: condition.expression === null ? null : predicateOf(condition.expression),
|
|
2596
2790
|
polarity: condition.polarity,
|
|
2597
2791
|
source: condition.source
|
|
2598
2792
|
};
|
|
@@ -2646,55 +2840,414 @@ function handsOnBranch(outcome, effects, extraEffects) {
|
|
|
2646
2840
|
isDefault: outcome.conditions.length === 0
|
|
2647
2841
|
};
|
|
2648
2842
|
}
|
|
2843
|
+
function returnPathBranches(method, effects) {
|
|
2844
|
+
const body = field(method, "body");
|
|
2845
|
+
if (body === null) {
|
|
2846
|
+
return null;
|
|
2847
|
+
}
|
|
2848
|
+
const returns = collectReturns(body, []);
|
|
2849
|
+
const lowered = lowerRubyBody(body, returns, []);
|
|
2850
|
+
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
2851
|
+
{
|
|
2852
|
+
statements: lowered.statements,
|
|
2853
|
+
terminalsByStmt: lowered.terminalsByStmt
|
|
2854
|
+
},
|
|
2855
|
+
returns
|
|
2856
|
+
);
|
|
2857
|
+
const branches = [];
|
|
2858
|
+
const push = (paths, at) => {
|
|
2859
|
+
for (const path7 of paths) {
|
|
2860
|
+
const conditions = path7.map(conditionOf);
|
|
2861
|
+
branches.push({
|
|
2862
|
+
conditions,
|
|
2863
|
+
terminal: {
|
|
2864
|
+
kind: "return",
|
|
2865
|
+
statusCode: null,
|
|
2866
|
+
body: null,
|
|
2867
|
+
exceptionType: null,
|
|
2868
|
+
message: null,
|
|
2869
|
+
component: null,
|
|
2870
|
+
renderTree: null,
|
|
2871
|
+
delegateTarget: null,
|
|
2872
|
+
emitEvent: null,
|
|
2873
|
+
location: at
|
|
2874
|
+
},
|
|
2875
|
+
effects: effectsReaching(effects, conditions),
|
|
2876
|
+
location: at,
|
|
2877
|
+
isDefault: conditions.length === 0
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
for (const statement of returns) {
|
|
2882
|
+
push(enumerated.byTerminal.get(statement) ?? [], rangeOf(statement));
|
|
2883
|
+
}
|
|
2884
|
+
push(enumerated.fallthrough, rangeOf(method));
|
|
2885
|
+
return branches.length === 0 ? null : branches;
|
|
2886
|
+
}
|
|
2649
2887
|
function responseBranches(method, pattern, effects, extraEffects, options = {}) {
|
|
2650
2888
|
const declarations = pattern.responseStatusCalls ?? [];
|
|
2651
2889
|
const body = field(method, "body");
|
|
2652
2890
|
if (declarations.length === 0 || body === null) {
|
|
2653
2891
|
return null;
|
|
2654
2892
|
}
|
|
2655
|
-
const byName = declarationsByName(declarations);
|
|
2656
|
-
const locals = localNamesIn(method);
|
|
2657
|
-
const responses = collectResponseCalls(body, byName, locals, []);
|
|
2658
|
-
const returns = collectReturns(body, []);
|
|
2659
|
-
const lowered = lowerRubyBody(body, returns, responses);
|
|
2660
|
-
const bareReturns = returns.filter(
|
|
2661
|
-
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, byName, locals, []).length === 0
|
|
2662
|
-
);
|
|
2663
|
-
const terminals = [...responses, ...bareReturns];
|
|
2664
|
-
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
2665
|
-
{
|
|
2666
|
-
statements: lowered.statements,
|
|
2667
|
-
terminalsByStmt: lowered.terminalsByStmt
|
|
2668
|
-
},
|
|
2669
|
-
terminals
|
|
2893
|
+
const byName = declarationsByName(declarations);
|
|
2894
|
+
const locals = localNamesIn(method);
|
|
2895
|
+
const responses = collectResponseCalls(body, byName, locals, []);
|
|
2896
|
+
const returns = collectReturns(body, []);
|
|
2897
|
+
const lowered = lowerRubyBody(body, returns, responses);
|
|
2898
|
+
const bareReturns = returns.filter(
|
|
2899
|
+
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, byName, locals, []).length === 0
|
|
2900
|
+
);
|
|
2901
|
+
const terminals = [...responses, ...bareReturns];
|
|
2902
|
+
const enumerated = (0, import_extractor3.enumerateOrDegrade)(
|
|
2903
|
+
{
|
|
2904
|
+
statements: lowered.statements,
|
|
2905
|
+
terminalsByStmt: lowered.terminalsByStmt
|
|
2906
|
+
},
|
|
2907
|
+
terminals
|
|
2908
|
+
);
|
|
2909
|
+
const statusNames = pattern.statusCodeNames ?? {};
|
|
2910
|
+
const outcomes = [];
|
|
2911
|
+
for (const terminal of terminals) {
|
|
2912
|
+
const declaration = declarationOf(terminal, byName, locals);
|
|
2913
|
+
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
2914
|
+
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
2915
|
+
outcomes.push({
|
|
2916
|
+
conditions: path7.map(conditionOf),
|
|
2917
|
+
reading,
|
|
2918
|
+
location: rangeOf(terminal)
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
for (const path7 of enumerated.fallthrough) {
|
|
2923
|
+
outcomes.push({
|
|
2924
|
+
conditions: path7.map(conditionOf),
|
|
2925
|
+
reading: import_extractor3.absentReading,
|
|
2926
|
+
location: rangeOf(method),
|
|
2927
|
+
fellThrough: true
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
if (outcomes.length === 0) {
|
|
2931
|
+
return null;
|
|
2932
|
+
}
|
|
2933
|
+
return outcomes.map(
|
|
2934
|
+
(outcome) => branchOf(outcome, pattern, effects, extraEffects, options)
|
|
2935
|
+
);
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
// src/clientCalls.ts
|
|
2939
|
+
function clientCallUnits(root, pack, pattern, options) {
|
|
2940
|
+
const units = [];
|
|
2941
|
+
for (const method of methodDefinitions(root)) {
|
|
2942
|
+
const name = field(method, "name")?.text;
|
|
2943
|
+
if (name === void 0) {
|
|
2944
|
+
continue;
|
|
2945
|
+
}
|
|
2946
|
+
for (const call of callsUnder(method)) {
|
|
2947
|
+
const request = requestCall(call, method, pattern, options);
|
|
2948
|
+
if (request === null) {
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return units;
|
|
2955
|
+
}
|
|
2956
|
+
function methodDefinitions(node, found = []) {
|
|
2957
|
+
for (const child of node.namedChildren) {
|
|
2958
|
+
if (child === null) {
|
|
2959
|
+
continue;
|
|
2960
|
+
}
|
|
2961
|
+
if (child.type === "method" || child.type === "singleton_method") {
|
|
2962
|
+
found.push(child);
|
|
2963
|
+
}
|
|
2964
|
+
methodDefinitions(child, found);
|
|
2965
|
+
}
|
|
2966
|
+
return found;
|
|
2967
|
+
}
|
|
2968
|
+
function callsUnder(node, found = []) {
|
|
2969
|
+
for (const child of node.namedChildren) {
|
|
2970
|
+
if (child === null) {
|
|
2971
|
+
continue;
|
|
2972
|
+
}
|
|
2973
|
+
if (child.type === "call") {
|
|
2974
|
+
found.push(child);
|
|
2975
|
+
}
|
|
2976
|
+
callsUnder(child, found);
|
|
2977
|
+
}
|
|
2978
|
+
return found;
|
|
2979
|
+
}
|
|
2980
|
+
function requestCall(call, method, pattern, options) {
|
|
2981
|
+
const receiver = field(call, "receiver");
|
|
2982
|
+
const called = field(call, "method")?.text;
|
|
2983
|
+
if (receiver === null || called === void 0) {
|
|
2984
|
+
return null;
|
|
2985
|
+
}
|
|
2986
|
+
const prefix = receiverPrefix(receiver, method, pattern, options);
|
|
2987
|
+
if (prefix === null) {
|
|
2988
|
+
return null;
|
|
2989
|
+
}
|
|
2990
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
2991
|
+
const verb = pattern.verbMethodNames[called];
|
|
2992
|
+
if (verb !== void 0) {
|
|
2993
|
+
const path8 = urlIn(args, pattern, options);
|
|
2994
|
+
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
2995
|
+
}
|
|
2996
|
+
const sent = pattern.requestObject;
|
|
2997
|
+
if (sent === void 0 || sent.attribute !== called) {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
const built = requestBuilt(args.positional[0], method, sent);
|
|
3001
|
+
if (built === null) {
|
|
3002
|
+
return null;
|
|
3003
|
+
}
|
|
3004
|
+
const path7 = pathAt(built.url, options);
|
|
3005
|
+
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3006
|
+
}
|
|
3007
|
+
function requestBuilt(argument, method, sent) {
|
|
3008
|
+
const written = argument === void 0 ? null : assignedCall(argument, method) ?? argument;
|
|
3009
|
+
if (written === null || written.type !== "call") {
|
|
3010
|
+
return null;
|
|
3011
|
+
}
|
|
3012
|
+
const requestClass = field(written, "receiver")?.text;
|
|
3013
|
+
const verb = requestClass === void 0 ? void 0 : sent.constructors[requestClass];
|
|
3014
|
+
if (verb === void 0) {
|
|
3015
|
+
return null;
|
|
3016
|
+
}
|
|
3017
|
+
const args = readCallArgs(field(written, "arguments"));
|
|
3018
|
+
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3019
|
+
}
|
|
3020
|
+
function assignedCall(node, method) {
|
|
3021
|
+
if (node.type !== "identifier") {
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
const value = assignedValue(node.text, method);
|
|
3025
|
+
return value !== null && value.type === "call" ? value : null;
|
|
3026
|
+
}
|
|
3027
|
+
function receiverPrefix(receiver, method, pattern, options) {
|
|
3028
|
+
if (namesConstant(receiver, pattern.constantName)) {
|
|
3029
|
+
return "";
|
|
3030
|
+
}
|
|
3031
|
+
if (receiver.type !== "identifier") {
|
|
3032
|
+
return null;
|
|
3033
|
+
}
|
|
3034
|
+
const built = builtBy(receiver.text, method, pattern);
|
|
3035
|
+
if (built === null) {
|
|
3036
|
+
return null;
|
|
3037
|
+
}
|
|
3038
|
+
const keyword = pattern.builderUrlKeyword;
|
|
3039
|
+
if (keyword === void 0) {
|
|
3040
|
+
return "";
|
|
3041
|
+
}
|
|
3042
|
+
const base = readCallArgs(field(built, "arguments")).keyword[keyword];
|
|
3043
|
+
const path7 = base === void 0 ? null : (0, import_values5.pathOf)(evaluatedValue(base, options.facts));
|
|
3044
|
+
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3045
|
+
}
|
|
3046
|
+
function trimmed(path7) {
|
|
3047
|
+
return path7 === "/" ? "" : path7.replace(/\/+$/, "");
|
|
3048
|
+
}
|
|
3049
|
+
function namesConstant(receiver, constantName) {
|
|
3050
|
+
return (receiver.type === "constant" || receiver.type === "scope_resolution") && receiver.text === constantName;
|
|
3051
|
+
}
|
|
3052
|
+
function builtBy(name, method, pattern) {
|
|
3053
|
+
const builders = pattern.receiverBuilders ?? [];
|
|
3054
|
+
const value = builders.length === 0 ? null : assignedValue(name, method);
|
|
3055
|
+
if (value === null || value.type !== "call" || !builders.includes(field(value, "method")?.text ?? "")) {
|
|
3056
|
+
return null;
|
|
3057
|
+
}
|
|
3058
|
+
const receiver = field(value, "receiver");
|
|
3059
|
+
return receiver !== null && namesConstant(receiver, pattern.constantName) ? value : null;
|
|
3060
|
+
}
|
|
3061
|
+
function assignedValue(name, method) {
|
|
3062
|
+
const body = field(method, "body");
|
|
3063
|
+
if (body === null) {
|
|
3064
|
+
return null;
|
|
3065
|
+
}
|
|
3066
|
+
for (const statement of runStatements(body)) {
|
|
3067
|
+
if (statement.type !== "assignment") {
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
3070
|
+
if (field(statement, "left")?.text === name) {
|
|
3071
|
+
return field(statement, "right");
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
function urlIn(args, pattern, options) {
|
|
3077
|
+
const keyword = pattern.url.keyword;
|
|
3078
|
+
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3079
|
+
return pathAt(written, options);
|
|
3080
|
+
}
|
|
3081
|
+
function pathAt(written, options) {
|
|
3082
|
+
if (written === void 0) {
|
|
3083
|
+
return null;
|
|
3084
|
+
}
|
|
3085
|
+
const path7 = (0, import_values5.pathOf)(evaluatedValue(written, options.facts));
|
|
3086
|
+
return path7 === void 0 || (0, import_behavioral_ir.namesNothing)(path7) ? null : path7;
|
|
3087
|
+
}
|
|
3088
|
+
function clientUnit(method, name, request, pattern, pack, options) {
|
|
3089
|
+
const range = rangeOf(method);
|
|
3090
|
+
return {
|
|
3091
|
+
identity: {
|
|
3092
|
+
name,
|
|
3093
|
+
nameKind: "binding",
|
|
3094
|
+
kind: "client",
|
|
3095
|
+
file: options.filePath,
|
|
3096
|
+
range,
|
|
3097
|
+
span: spanOf(method),
|
|
3098
|
+
exportName: name,
|
|
3099
|
+
exportPath: [name]
|
|
3100
|
+
},
|
|
3101
|
+
boundaryBinding: (0, import_behavioral_ir.restBinding)({
|
|
3102
|
+
transport: pack.protocol,
|
|
3103
|
+
method: request.method,
|
|
3104
|
+
path: request.path,
|
|
3105
|
+
recognition: pack.name
|
|
3106
|
+
}),
|
|
3107
|
+
parameters: [],
|
|
3108
|
+
branches: callerBranches(method, range),
|
|
3109
|
+
...responseAccessors(pattern),
|
|
3110
|
+
bodyContent: "statements",
|
|
3111
|
+
dependencyCalls: [],
|
|
3112
|
+
declaredContract: null
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
function callerBranches(method, range) {
|
|
3116
|
+
const effects = invocationEffects(method);
|
|
3117
|
+
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3118
|
+
}
|
|
3119
|
+
function responseAccessors(pattern) {
|
|
3120
|
+
const response = pattern.response;
|
|
3121
|
+
if (response === void 0) {
|
|
3122
|
+
return {};
|
|
3123
|
+
}
|
|
3124
|
+
return {
|
|
3125
|
+
...response.body === void 0 ? {} : { bodyAccessors: response.body },
|
|
3126
|
+
...response.statusCode === void 0 ? {} : { statusAccessors: response.statusCode },
|
|
3127
|
+
...response.success === void 0 ? {} : { successAccessors: response.success },
|
|
3128
|
+
...response.failureDelivery === void 0 ? {} : { failureDelivery: response.failureDelivery }
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
function returnBranch(range, effects) {
|
|
3132
|
+
return {
|
|
3133
|
+
conditions: [],
|
|
3134
|
+
terminal: {
|
|
3135
|
+
kind: "return",
|
|
3136
|
+
statusCode: null,
|
|
3137
|
+
body: null,
|
|
3138
|
+
exceptionType: null,
|
|
3139
|
+
message: null,
|
|
3140
|
+
component: null,
|
|
3141
|
+
renderTree: null,
|
|
3142
|
+
delegateTarget: null,
|
|
3143
|
+
emitEvent: null,
|
|
3144
|
+
location: range
|
|
3145
|
+
},
|
|
3146
|
+
effects,
|
|
3147
|
+
location: range,
|
|
3148
|
+
isDefault: true
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
// src/envReads.ts
|
|
3153
|
+
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
3154
|
+
var import_extractor4 = require("@suss/extractor");
|
|
3155
|
+
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
3156
|
+
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
3157
|
+
function envReadEffects(root) {
|
|
3158
|
+
const effects = [];
|
|
3159
|
+
(0, import_extractor4.walkDescendants)(root, null, {
|
|
3160
|
+
at: (node) => {
|
|
3161
|
+
const read = envReadAt(node);
|
|
3162
|
+
if (read !== null) {
|
|
3163
|
+
effects.push(configReadEffect(read));
|
|
3164
|
+
}
|
|
3165
|
+
},
|
|
3166
|
+
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? import_extractor4.SKIP_CHILDREN : null
|
|
3167
|
+
});
|
|
3168
|
+
return effects;
|
|
3169
|
+
}
|
|
3170
|
+
function configReadEffect(read) {
|
|
3171
|
+
return {
|
|
3172
|
+
type: "interaction",
|
|
3173
|
+
binding: (0, import_behavioral_ir2.runtimeConfigBinding)({
|
|
3174
|
+
recognition: RUBY_ENV_RECOGNITION,
|
|
3175
|
+
deploymentTarget: "lambda",
|
|
3176
|
+
instanceName: "<unknown>"
|
|
3177
|
+
}),
|
|
3178
|
+
callee: `ENV["${read.name}"]`,
|
|
3179
|
+
interaction: {
|
|
3180
|
+
class: "config-read",
|
|
3181
|
+
name: read.name,
|
|
3182
|
+
defaulted: read.defaulted
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
function envReadAt(node) {
|
|
3187
|
+
if (node.type === "element_reference") {
|
|
3188
|
+
return elementRead(node);
|
|
3189
|
+
}
|
|
3190
|
+
if (node.type === "call") {
|
|
3191
|
+
return fetchRead(node);
|
|
3192
|
+
}
|
|
3193
|
+
return null;
|
|
3194
|
+
}
|
|
3195
|
+
function elementRead(node) {
|
|
3196
|
+
const object = field(node, "object");
|
|
3197
|
+
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
3198
|
+
return null;
|
|
3199
|
+
}
|
|
3200
|
+
const index = node.namedChildren.find(
|
|
3201
|
+
(child) => child !== null && child.id !== object.id
|
|
2670
3202
|
);
|
|
2671
|
-
const
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
const declaration = declarationOf(terminal, byName, locals);
|
|
2675
|
-
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
2676
|
-
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
2677
|
-
outcomes.push({
|
|
2678
|
-
conditions: path7.map(conditionOf),
|
|
2679
|
-
reading,
|
|
2680
|
-
location: rangeOf(terminal)
|
|
2681
|
-
});
|
|
2682
|
-
}
|
|
3203
|
+
const name = index === void 0 ? null : stringLiteralValue(index);
|
|
3204
|
+
if (name === null) {
|
|
3205
|
+
return null;
|
|
2683
3206
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
});
|
|
3207
|
+
return { name, defaulted: isDefaultedAt(node) };
|
|
3208
|
+
}
|
|
3209
|
+
function fetchRead(node) {
|
|
3210
|
+
const receiver = field(node, "receiver");
|
|
3211
|
+
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
3212
|
+
return null;
|
|
2691
3213
|
}
|
|
2692
|
-
|
|
3214
|
+
const { positional } = readCallArgs(field(node, "arguments"));
|
|
3215
|
+
const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
3216
|
+
if (name === null) {
|
|
2693
3217
|
return null;
|
|
2694
3218
|
}
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
3219
|
+
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
3220
|
+
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
3221
|
+
}
|
|
3222
|
+
function isEnv(node) {
|
|
3223
|
+
if (node.type === "constant") {
|
|
3224
|
+
return node.text === "ENV";
|
|
3225
|
+
}
|
|
3226
|
+
return node.type === "scope_resolution" && field(node, "scope") === null && field(node, "name")?.text === "ENV";
|
|
3227
|
+
}
|
|
3228
|
+
function isAssignedTo(node) {
|
|
3229
|
+
const parent = node.parent;
|
|
3230
|
+
return parent !== null && (parent.type === "assignment" || parent.type === "operator_assignment") && field(parent, "left")?.id === node.id;
|
|
3231
|
+
}
|
|
3232
|
+
function isDefaultedAt(node) {
|
|
3233
|
+
let child = node;
|
|
3234
|
+
let parent = node.parent;
|
|
3235
|
+
while (parent !== null) {
|
|
3236
|
+
if (parent.type === "parenthesized_statements") {
|
|
3237
|
+
child = parent;
|
|
3238
|
+
parent = parent.parent;
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
if (parent.type !== "binary" || field(parent, "operator")?.text !== "||") {
|
|
3242
|
+
return false;
|
|
3243
|
+
}
|
|
3244
|
+
if (field(parent, "left")?.id === child.id) {
|
|
3245
|
+
return true;
|
|
3246
|
+
}
|
|
3247
|
+
child = parent;
|
|
3248
|
+
parent = parent.parent;
|
|
3249
|
+
}
|
|
3250
|
+
return false;
|
|
2698
3251
|
}
|
|
2699
3252
|
|
|
2700
3253
|
// src/filters.ts
|
|
@@ -2882,172 +3435,6 @@ function namesUnder(node) {
|
|
|
2882
3435
|
return names;
|
|
2883
3436
|
}
|
|
2884
3437
|
|
|
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
|
-
|
|
3051
3438
|
// src/storage.ts
|
|
3052
3439
|
var import_datalog2 = require("@suss/datalog");
|
|
3053
3440
|
var import_ir_core = require("@suss/ir-core");
|
|
@@ -3266,6 +3653,16 @@ async function discoverUnits(root, options) {
|
|
|
3266
3653
|
file: options.absoluteFile ?? options.filePath
|
|
3267
3654
|
}));
|
|
3268
3655
|
const units = [];
|
|
3656
|
+
for (const pack of options.packs) {
|
|
3657
|
+
for (const pattern of pack.clients ?? []) {
|
|
3658
|
+
units.push(
|
|
3659
|
+
...clientCallUnits(root, pack, pattern, {
|
|
3660
|
+
filePath: options.filePath,
|
|
3661
|
+
...options.facts === void 0 ? {} : { facts: options.facts }
|
|
3662
|
+
})
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3269
3666
|
for (const info of classes) {
|
|
3270
3667
|
const ownBlocks = fileBlocks.filter(
|
|
3271
3668
|
(block) => block.info.qualifiedName === info.qualifiedName
|
|
@@ -3297,7 +3694,7 @@ function unitsFor(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
|
3297
3694
|
graphqlObjectFields: (p) => graphqlObjectFieldUnits(p, pack, info, ownBlocks, fileBlocks, options),
|
|
3298
3695
|
controllerActions: (p) => controllerActionUnits(p, pack, info, ownBlocks, fileBlocks, options)
|
|
3299
3696
|
};
|
|
3300
|
-
return (0,
|
|
3697
|
+
return (0, import_behavioral_ir3.dispatchByType)(table, pattern);
|
|
3301
3698
|
}
|
|
3302
3699
|
async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
3303
3700
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
@@ -3422,7 +3819,7 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
3422
3819
|
exportName: actionName,
|
|
3423
3820
|
exportPath: [controllerQualifiedName, actionName]
|
|
3424
3821
|
},
|
|
3425
|
-
boundaryBinding: route === null ? null : (0,
|
|
3822
|
+
boundaryBinding: route === null ? null : (0, import_behavioral_ir3.restBinding)({
|
|
3426
3823
|
transport: pack.protocol,
|
|
3427
3824
|
method: route.method,
|
|
3428
3825
|
path: route.path,
|
|
@@ -3540,7 +3937,7 @@ function bodyOfMethod(method, bodyRead = {}) {
|
|
|
3540
3937
|
const storage = bodyRead.storage;
|
|
3541
3938
|
const extra = [
|
|
3542
3939
|
...envReadEffects(method),
|
|
3543
|
-
...storage === void 0 ? [] : storageEffects(
|
|
3940
|
+
...storage === void 0 ? [] : storageEffects(callsUnder2(method), storage)
|
|
3544
3941
|
];
|
|
3545
3942
|
return {
|
|
3546
3943
|
bodyContent: methodHasStatements(method) ? "statements" : "empty",
|
|
@@ -3549,7 +3946,7 @@ function bodyOfMethod(method, bodyRead = {}) {
|
|
|
3549
3946
|
...extra.length > 0 ? { extraEffects: extra } : {}
|
|
3550
3947
|
};
|
|
3551
3948
|
}
|
|
3552
|
-
function
|
|
3949
|
+
function callsUnder2(node, found = []) {
|
|
3553
3950
|
for (const child of node.namedChildren) {
|
|
3554
3951
|
if (child === null) {
|
|
3555
3952
|
continue;
|
|
@@ -3557,7 +3954,7 @@ function callsUnder(node, found = []) {
|
|
|
3557
3954
|
if (child.type === "call") {
|
|
3558
3955
|
found.push(child);
|
|
3559
3956
|
}
|
|
3560
|
-
|
|
3957
|
+
callsUnder2(child, found);
|
|
3561
3958
|
}
|
|
3562
3959
|
return found;
|
|
3563
3960
|
}
|
|
@@ -3584,7 +3981,7 @@ function bodyFromLookup(found, range, subject, nothingThere, bodyRead) {
|
|
|
3584
3981
|
),
|
|
3585
3982
|
none: () => nothingThere
|
|
3586
3983
|
};
|
|
3587
|
-
return (0,
|
|
3984
|
+
return (0, import_behavioral_ir3.dispatchByType)(table, found);
|
|
3588
3985
|
}
|
|
3589
3986
|
async function readFieldCall(stmt, scope, ctx, ancestry) {
|
|
3590
3987
|
if (stmt.type !== "call" || field(stmt, "receiver") !== null) {
|
|
@@ -3853,7 +4250,7 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
3853
4250
|
exportName: null,
|
|
3854
4251
|
exportPath: null
|
|
3855
4252
|
},
|
|
3856
|
-
boundaryBinding: decl.namedOnTheWire ? (0,
|
|
4253
|
+
boundaryBinding: decl.namedOnTheWire ? (0, import_behavioral_ir3.graphqlResolverBinding)({
|
|
3857
4254
|
transport: pack.protocol,
|
|
3858
4255
|
recognition: pack.name,
|
|
3859
4256
|
typeName,
|
|
@@ -4051,7 +4448,7 @@ function parseWith(language, source) {
|
|
|
4051
4448
|
// src/project.ts
|
|
4052
4449
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
4053
4450
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
4054
|
-
var
|
|
4451
|
+
var import_behavioral_ir5 = require("@suss/behavioral-ir");
|
|
4055
4452
|
var import_datalog4 = require("@suss/datalog");
|
|
4056
4453
|
var import_extractor10 = require("@suss/extractor");
|
|
4057
4454
|
|
|
@@ -4106,7 +4503,7 @@ function requiredRelativePath(node) {
|
|
|
4106
4503
|
}
|
|
4107
4504
|
|
|
4108
4505
|
// src/reach/closure.ts
|
|
4109
|
-
var
|
|
4506
|
+
var import_behavioral_ir4 = require("@suss/behavioral-ir");
|
|
4110
4507
|
var import_datalog3 = require("@suss/datalog");
|
|
4111
4508
|
var import_extractor8 = require("@suss/extractor");
|
|
4112
4509
|
|
|
@@ -4330,14 +4727,14 @@ async function reachedFunctions(seeds, options) {
|
|
|
4330
4727
|
gapHandling: "permissive"
|
|
4331
4728
|
});
|
|
4332
4729
|
summary.confidence = { source: "inferred_static", level: "low" };
|
|
4333
|
-
summary.gaps.push(...(stopsByKey.get(key) ?? []).map(
|
|
4334
|
-
(0,
|
|
4335
|
-
(0,
|
|
4336
|
-
(0,
|
|
4730
|
+
summary.gaps.push(...(stopsByKey.get(key) ?? []).map(import_behavioral_ir4.unfollowedCallGap));
|
|
4731
|
+
(0, import_behavioral_ir4.placeCalls)(summary, targetsByKey.get(key));
|
|
4732
|
+
(0, import_behavioral_ir4.placeArgTargets)(summary, argTargetsByKey.get(key));
|
|
4733
|
+
(0, import_behavioral_ir4.placeCalleeParameters)(summary, parameterCallsByKey.get(key));
|
|
4337
4734
|
summariesByKey.set(key, [summary]);
|
|
4338
4735
|
summaries.push(summary);
|
|
4339
4736
|
}
|
|
4340
|
-
(0,
|
|
4737
|
+
(0, import_behavioral_ir4.recordParameterGaps)(parameterCallsByKey, summariesByKey, passedPositions);
|
|
4341
4738
|
return {
|
|
4342
4739
|
summaries,
|
|
4343
4740
|
targetsByKey,
|
|
@@ -4447,7 +4844,7 @@ async function scanBody(source, ctx, options) {
|
|
|
4447
4844
|
};
|
|
4448
4845
|
const followed2 = [];
|
|
4449
4846
|
const stops = [];
|
|
4450
|
-
const placements = new
|
|
4847
|
+
const placements = new import_behavioral_ir4.TargetPlacements();
|
|
4451
4848
|
const parameterCalls = [];
|
|
4452
4849
|
const passedPositions = /* @__PURE__ */ new Set();
|
|
4453
4850
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4500,7 +4897,7 @@ async function scanBody(source, ctx, options) {
|
|
|
4500
4897
|
);
|
|
4501
4898
|
if (outcome.kind === "stopped") {
|
|
4502
4899
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
4503
|
-
if (!seen.has(stopKey) && (0,
|
|
4900
|
+
if (!seen.has(stopKey) && (0, import_behavioral_ir4.worthRecording)(outcome.reason)) {
|
|
4504
4901
|
seen.add(stopKey);
|
|
4505
4902
|
stops.push({ callee, reason: outcome.reason });
|
|
4506
4903
|
}
|
|
@@ -4544,7 +4941,7 @@ function libraryUnit(target, options) {
|
|
|
4544
4941
|
exportName: exportPath[0] ?? name,
|
|
4545
4942
|
exportPath
|
|
4546
4943
|
},
|
|
4547
|
-
boundaryBinding: (0,
|
|
4944
|
+
boundaryBinding: (0, import_behavioral_ir4.functionCallBinding)({
|
|
4548
4945
|
transport: "in-process",
|
|
4549
4946
|
recognition: "reachable"
|
|
4550
4947
|
}),
|
|
@@ -4706,6 +5103,7 @@ async function extractRubyProject(options) {
|
|
|
4706
5103
|
...storagePatterns.length > 0 ? { storage: { facts: db, patterns: storagePatterns } } : {},
|
|
4707
5104
|
inheritedMethods,
|
|
4708
5105
|
displayPathOf,
|
|
5106
|
+
facts: db,
|
|
4709
5107
|
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
4710
5108
|
})
|
|
4711
5109
|
);
|
|
@@ -4788,14 +5186,14 @@ async function extractRubyProject(options) {
|
|
|
4788
5186
|
for (const [key, owners] of summariesBySeed) {
|
|
4789
5187
|
for (const summary of owners) {
|
|
4790
5188
|
summary.gaps.push(
|
|
4791
|
-
...(reached.stopsByKey.get(key) ?? []).map(
|
|
5189
|
+
...(reached.stopsByKey.get(key) ?? []).map(import_behavioral_ir5.unfollowedCallGap)
|
|
4792
5190
|
);
|
|
4793
|
-
(0,
|
|
4794
|
-
(0,
|
|
4795
|
-
(0,
|
|
5191
|
+
(0, import_behavioral_ir5.placeCalls)(summary, reached.targetsByKey.get(key));
|
|
5192
|
+
(0, import_behavioral_ir5.placeArgTargets)(summary, reached.argTargetsByKey.get(key));
|
|
5193
|
+
(0, import_behavioral_ir5.placeCalleeParameters)(summary, reached.parameterCallsByKey.get(key));
|
|
4796
5194
|
}
|
|
4797
5195
|
}
|
|
4798
|
-
(0,
|
|
5196
|
+
(0, import_behavioral_ir5.recordParameterGaps)(
|
|
4799
5197
|
reached.parameterCallsByKey,
|
|
4800
5198
|
summariesBySeed,
|
|
4801
5199
|
reached.passedPositions
|
|
@@ -4806,15 +5204,15 @@ async function extractRubyProject(options) {
|
|
|
4806
5204
|
const idRoot = options.projectRoot ?? options.workspaceRoot;
|
|
4807
5205
|
for (const summary of summaries) {
|
|
4808
5206
|
const absoluteFile = options.workspaceRoot === void 0 ? summary.location.file : import_node_path4.default.resolve(options.workspaceRoot, summary.location.file);
|
|
4809
|
-
summary.identity.id = (0,
|
|
5207
|
+
summary.identity.id = (0, import_behavioral_ir5.summaryIdFromParts)({
|
|
4810
5208
|
workspace: void 0,
|
|
4811
5209
|
file: idRoot === void 0 ? absoluteFile : import_node_path4.default.relative(idRoot, absoluteFile),
|
|
4812
5210
|
name: summary.identity.name,
|
|
4813
5211
|
exportPath: summary.identity.exportPath
|
|
4814
5212
|
});
|
|
4815
5213
|
}
|
|
4816
|
-
(0,
|
|
4817
|
-
(0,
|
|
5214
|
+
(0, import_behavioral_ir5.disambiguateSummaryIds)(summaries);
|
|
5215
|
+
(0, import_behavioral_ir5.linkCallsToSummaries)(summaries);
|
|
4818
5216
|
const composed = timer.time(
|
|
4819
5217
|
"summarize",
|
|
4820
5218
|
() => (0, import_extractor10.composeWrappers)(summaries, {
|