@suss/adapter-python 0.21.1 → 0.22.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/dist/index.cjs CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ADAPTER_VERSION: () => ADAPTER_VERSION,
34
+ PythonWhySession: () => PythonWhySession,
33
35
  annotationToShape: () => annotationToShape,
34
36
  bindModule: () => bindModule,
35
37
  buildRouterIndex: () => buildRouterIndex,
@@ -44,6 +46,10 @@ __export(index_exports, {
44
46
  nodeId: () => nodeId,
45
47
  objectReturnedBy: () => objectReturnedBy,
46
48
  parsePython: () => parsePython,
49
+ parsePythonSync: () => parsePythonSync,
50
+ preloadPythonGrammar: () => preloadPythonGrammar,
51
+ pythonImportEvidence: () => pythonImportEvidence,
52
+ readKey: () => readKey,
47
53
  resolveAbsoluteModule: () => resolveAbsoluteModule,
48
54
  resolveCalls: () => resolveCalls,
49
55
  resolveModule: () => resolveModule,
@@ -808,18 +814,398 @@ function passesParametersThrough(def, inner) {
808
814
  }
809
815
 
810
816
  // src/discovery.ts
817
+ var import_behavioral_ir2 = require("@suss/behavioral-ir");
818
+ var import_extractor4 = require("@suss/extractor");
819
+
820
+ // src/envReads.ts
811
821
  var import_behavioral_ir = require("@suss/behavioral-ir");
812
- var import_extractor3 = require("@suss/extractor");
822
+ var import_extractor2 = require("@suss/extractor");
823
+ var PYTHON_ENV_RECOGNITION = "python-env";
824
+ var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
825
+ function envReadEffects(root, module2) {
826
+ const effects = [];
827
+ const startScope = module2.scopeFor.get(root.id) ?? module2.moduleScope;
828
+ (0, import_extractor2.walkDescendants)(root, startScope, {
829
+ at: (node, scope) => {
830
+ const read = envReadAt(node, scope);
831
+ if (read !== null) {
832
+ effects.push(configReadEffect(read));
833
+ }
834
+ },
835
+ into: (node, scope) => {
836
+ if (DEFERRED_BODY_TYPES.has(node.type)) {
837
+ return import_extractor2.SKIP_CHILDREN;
838
+ }
839
+ return module2.scopeFor.get(node.id) ?? scope;
840
+ }
841
+ });
842
+ return effects;
843
+ }
844
+ function configReadEffect(read) {
845
+ return {
846
+ type: "interaction",
847
+ binding: (0, import_behavioral_ir.runtimeConfigBinding)({
848
+ recognition: PYTHON_ENV_RECOGNITION,
849
+ deploymentTarget: "lambda",
850
+ instanceName: "<unknown>"
851
+ }),
852
+ callee: `os.environ["${read.name}"]`,
853
+ interaction: {
854
+ class: "config-read",
855
+ name: read.name,
856
+ defaulted: read.defaulted
857
+ }
858
+ };
859
+ }
860
+ function envReadAt(node, scope) {
861
+ if (node.type === "subscript") {
862
+ return subscriptRead(node, scope);
863
+ }
864
+ if (node.type === "call") {
865
+ return callRead(node, scope);
866
+ }
867
+ return null;
868
+ }
869
+ function subscriptRead(node, scope) {
870
+ const value = field(node, "value");
871
+ const index = field(node, "subscript");
872
+ if (value === null || index === null || !isEnviron(value, scope)) {
873
+ return null;
874
+ }
875
+ const name = stringLiteralValue(index);
876
+ if (name === null || isAssignedTo(node)) {
877
+ return null;
878
+ }
879
+ return { name, defaulted: isDefaultedAt(node) };
880
+ }
881
+ function isAssignedTo(node) {
882
+ const parent = node.parent;
883
+ if (parent === null) {
884
+ return false;
885
+ }
886
+ if (parent.type === "delete_statement") {
887
+ return true;
888
+ }
889
+ return (parent.type === "assignment" || parent.type === "augmented_assignment") && field(parent, "left")?.id === node.id;
890
+ }
891
+ function callRead(node, scope) {
892
+ const callee = field(node, "function");
893
+ const args = field(node, "arguments");
894
+ if (callee === null || args === null || !isEnvGetter(callee, scope)) {
895
+ return null;
896
+ }
897
+ const positional = args.namedChildren.filter(
898
+ (arg) => arg !== null && arg.type !== "keyword_argument"
899
+ );
900
+ const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
901
+ if (name === null) {
902
+ return null;
903
+ }
904
+ const keywords = args.namedChildren.filter(
905
+ (arg) => arg !== null && arg.type === "keyword_argument"
906
+ );
907
+ const hasDefault = positional.length > 1 || keywords.some((arg) => field(arg, "name")?.text === "default");
908
+ return { name, defaulted: hasDefault || isDefaultedAt(node) };
909
+ }
910
+ function isEnvGetter(callee, scope) {
911
+ if (callee.type === "attribute") {
912
+ const object2 = field(callee, "object");
913
+ const attribute = field(callee, "attribute")?.text;
914
+ if (object2 === null) {
915
+ return false;
916
+ }
917
+ if (attribute === "get") {
918
+ return isEnviron(object2, scope);
919
+ }
920
+ return attribute === "getenv" && isOsModule(object2, scope);
921
+ }
922
+ return isImportedFromOs(callee, scope, "getenv");
923
+ }
924
+ function isEnviron(node, scope) {
925
+ if (node.type === "attribute") {
926
+ const object2 = field(node, "object");
927
+ return object2 !== null && field(node, "attribute")?.text === "environ" && isOsModule(object2, scope);
928
+ }
929
+ return isImportedFromOs(node, scope, "environ");
930
+ }
931
+ function isOsModule(node, scope) {
932
+ if (node.type !== "identifier") {
933
+ return false;
934
+ }
935
+ const binding = resolveName(scope, node.text);
936
+ return binding?.kind === "import" && binding.module === "os" && binding.relativeLevel === 0;
937
+ }
938
+ function isImportedFromOs(node, scope, name) {
939
+ if (node.type !== "identifier") {
940
+ return false;
941
+ }
942
+ const binding = resolveName(scope, node.text);
943
+ return binding?.kind === "importFrom" && binding.module === "os" && binding.relativeLevel === 0 && binding.importedName === name;
944
+ }
945
+ function isDefaultedAt(node) {
946
+ let child = node;
947
+ let parent = node.parent;
948
+ while (parent !== null) {
949
+ if (parent.type === "parenthesized_expression") {
950
+ child = parent;
951
+ parent = parent.parent;
952
+ continue;
953
+ }
954
+ if (parent.type !== "boolean_operator" || field(parent, "operator")?.text !== "or") {
955
+ return false;
956
+ }
957
+ if (field(parent, "left")?.id === child.id) {
958
+ return true;
959
+ }
960
+ child = parent;
961
+ parent = parent.parent;
962
+ }
963
+ return false;
964
+ }
813
965
 
814
966
  // src/facts/resolve.ts
815
- var import_datalog3 = require("@suss/datalog");
967
+ var import_datalog4 = require("@suss/datalog");
816
968
 
817
969
  // ../../resolution/dist/index.js
818
970
  var import_datalog = require("@suss/datalog");
819
971
  var import_datalog2 = require("@suss/datalog");
820
- function joinMountedPath(prefix, path4) {
972
+ var import_datalog3 = require("@suss/datalog");
973
+ function exportTrail(proof, describe3) {
974
+ if (proof.kind !== "derived") {
975
+ return [];
976
+ }
977
+ const name = (0, import_datalog.ruleLabel)(proof.rule);
978
+ if (name === "re-export") {
979
+ const [m, n, m2, n2] = proof.premises[0].tuple;
980
+ return [
981
+ `${describe3(m)} takes ${String(n)} from ${describe3(m2)}, where it is called ${String(n2)}`,
982
+ ...exportTrail(proof.premises[1], describe3)
983
+ ];
984
+ }
985
+ if (name === "re-export all") {
986
+ const [m, m2] = proof.premises[0].tuple;
987
+ return [
988
+ `${describe3(m)} forwards everything ${describe3(m2)} exports`,
989
+ ...exportTrail(proof.premises[1], describe3)
990
+ ];
991
+ }
992
+ return [];
993
+ }
994
+ function argumentReason(proof, describe3) {
995
+ const [, p, a] = proof.tuple;
996
+ if (proof.kind !== "derived") {
997
+ return `${describe3(p)} is a parameter, and a call passes it ${describe3(a)}`;
998
+ }
999
+ const f = proof.premises[0].tuple[0];
1000
+ return `${describe3(p)} is a parameter of ${describe3(f)}, and a call passes it ${describe3(a)}`;
1001
+ }
1002
+ var DEFAULT_PHRASES = {
1003
+ alias: ({ tuple: tuple2, describe: describe3 }) => ({
1004
+ reason: `${describe3(tuple2[0])} is declared as ${describe3(tuple2[1])}`
1005
+ }),
1006
+ "last write": ({ tuple: tuple2, describe: describe3 }) => ({
1007
+ reason: `${describe3(tuple2[0])} is written more than once, and the last write leaves it as ${describe3(tuple2[1])}`
1008
+ }),
1009
+ fallback: ({ tuple: tuple2, describe: describe3 }) => ({
1010
+ reason: `${describe3(tuple2[0])} is a fallback expression, and ${describe3(tuple2[1])} is the branch that resolves`
1011
+ }),
1012
+ import: ({ tuple: tuple2, premises, describe: describe3 }) => {
1013
+ const [, module2, name] = premises[0].tuple;
1014
+ return {
1015
+ reason: `${describe3(tuple2[0])} is imported from ${describe3(module2)} under the name ${String(name)}`,
1016
+ notes: exportTrail(premises[1], describe3)
1017
+ };
1018
+ },
1019
+ argument: ({ premises, describe: describe3 }) => ({
1020
+ reason: argumentReason(premises[0], describe3)
1021
+ }),
1022
+ "property read": ({ tuple: tuple2, premises, describe: describe3 }) => {
1023
+ const [, object2, name] = premises[0].tuple;
1024
+ const contains = premises[2];
1025
+ const inherited = contains.kind === "derived" && contains.premises.length > 1;
1026
+ return {
1027
+ reason: `${describe3(tuple2[0])} reads ${String(name)} off ${describe3(object2)}, which contains ${describe3(tuple2[1])}`,
1028
+ notes: inherited ? [`${String(name)} comes from a base class`] : []
1029
+ };
1030
+ },
1031
+ "class instance": ({ tuple: tuple2, premises, describe: describe3, inline }) => ({
1032
+ reason: `${describe3(tuple2[0])} makes an instance of ${describe3(tuple2[1])}`,
1033
+ notes: inline(premises[1])
1034
+ }),
1035
+ "factory unwrap": ({ tuple: tuple2, premises, describe: describe3, inline }) => {
1036
+ const factory = premises[1].tuple[1];
1037
+ return {
1038
+ reason: `${describe3(tuple2[0])} calls ${describe3(factory)}, a factory that passes its argument through, so it comes down to ${describe3(tuple2[1])}`,
1039
+ notes: inline(premises[1])
1040
+ };
1041
+ },
1042
+ "declared wrapper": ({ tuple: tuple2, premises, describe: describe3 }) => {
1043
+ const callee = String(premises[0].tuple[1]);
1044
+ const module2 = String(premises[2].tuple[1]);
1045
+ const argument = String(premises[1].tuple[1]);
1046
+ return {
1047
+ reason: `${describe3(tuple2[0])} calls ${callee} from ${module2}, which passes argument ${argument} through, so it comes down to ${describe3(tuple2[1])}`,
1048
+ assumptions: [
1049
+ `a pack declares that ${callee} from ${module2} passes argument ${argument} through to its result`
1050
+ ]
1051
+ };
1052
+ },
1053
+ "call result": ({ tuple: tuple2, premises, describe: describe3, inline }) => {
1054
+ const invokes = premises[0];
1055
+ const invoked = invokes.tuple[1];
1056
+ const calleeWalks = invokes.kind === "derived" ? invokes.premises.filter(
1057
+ (premise) => premise.kind === "derived" && (premise.relation === "comesTo" || premise.relation === "givesBack")
1058
+ ) : [];
1059
+ return {
1060
+ reason: `${describe3(tuple2[0])} runs ${describe3(invoked)}, which returns ${describe3(tuple2[1])}`,
1061
+ notes: calleeWalks.flatMap((premise) => inline(premise))
1062
+ };
1063
+ }
1064
+ };
1065
+ var CHAIN_RELATIONS = /* @__PURE__ */ new Set([
1066
+ "resolves",
1067
+ "comesTo",
1068
+ "givesBack",
1069
+ "isWrittenAs",
1070
+ "comesFrom",
1071
+ "reaches",
1072
+ "stepsTo"
1073
+ ]);
1074
+ function inlineChain(proof, state) {
1075
+ const explained = explainResolutionProof(proof, {
1076
+ describe: state.describe,
1077
+ phrases: state.phrases
1078
+ });
1079
+ if (explained === null || explained.steps.length === 0) {
1080
+ return [];
1081
+ }
1082
+ if (explained.truncated) {
1083
+ state.truncated = true;
1084
+ }
1085
+ return explained.steps.map((step) => step.reason);
1086
+ }
1087
+ function stepFrom(proof, state) {
1088
+ const name = (0, import_datalog.ruleLabel)(proof.rule);
1089
+ const phrase = state.phrases[name];
1090
+ const from = proof.tuple[0];
1091
+ const to = proof.tuple[1];
1092
+ if (phrase === void 0) {
1093
+ return {
1094
+ from,
1095
+ to,
1096
+ rule: name,
1097
+ reason: `${state.describe(from)} steps to ${state.describe(to)} (${name})`,
1098
+ notes: [],
1099
+ assumptions: []
1100
+ };
1101
+ }
1102
+ const said = phrase({
1103
+ tuple: proof.tuple,
1104
+ premises: proof.premises,
1105
+ describe: state.describe,
1106
+ inline: (nested) => inlineChain(nested, state)
1107
+ });
1108
+ return {
1109
+ from,
1110
+ to,
1111
+ rule: name,
1112
+ reason: said.reason,
1113
+ notes: said.notes ?? [],
1114
+ assumptions: said.assumptions ?? []
1115
+ };
1116
+ }
1117
+ function importStep(proof, state) {
1118
+ const [x, module2, name] = proof.tuple;
1119
+ return {
1120
+ from: x,
1121
+ to: module2,
1122
+ rule: "import",
1123
+ reason: `${state.describe(x)} is imported from ${state.describe(module2)} under the name ${String(name)}`,
1124
+ notes: [],
1125
+ assumptions: []
1126
+ };
1127
+ }
1128
+ var flattenInto = (proof, state) => {
1129
+ if (proof.kind === "truncated") {
1130
+ state.truncated = true;
1131
+ return;
1132
+ }
1133
+ if (proof.kind !== "derived") {
1134
+ return;
1135
+ }
1136
+ const relation = proof.relation;
1137
+ if (relation === "stepsTo") {
1138
+ state.steps.push(stepFrom(proof, state));
1139
+ return;
1140
+ }
1141
+ if (relation === "reaches") {
1142
+ for (const p of proof.premises) {
1143
+ flattenInto(p, state);
1144
+ }
1145
+ return;
1146
+ }
1147
+ if (relation === "comesFrom") {
1148
+ if (proof.premises.length === 2) {
1149
+ flattenInto(proof.premises[0], state);
1150
+ state.steps.push(importStep(proof.premises[1], state));
1151
+ } else {
1152
+ state.steps.push(importStep(proof.premises[0], state));
1153
+ }
1154
+ return;
1155
+ }
1156
+ for (const p of proof.premises) {
1157
+ if (p.kind === "derived" && (p.relation === "reaches" || p.relation === "comesTo")) {
1158
+ flattenInto(p, state);
1159
+ }
1160
+ if (p.kind === "truncated") {
1161
+ state.truncated = true;
1162
+ }
1163
+ }
1164
+ };
1165
+ function explainResolutionProof(proof, options) {
1166
+ if (proof.kind === "absence") {
1167
+ return null;
1168
+ }
1169
+ if (proof.kind === "derived" && !CHAIN_RELATIONS.has(proof.relation)) {
1170
+ return null;
1171
+ }
1172
+ const state = {
1173
+ steps: [],
1174
+ truncated: proof.kind === "truncated",
1175
+ describe: options.describe,
1176
+ phrases: { ...DEFAULT_PHRASES, ...options.phrases }
1177
+ };
1178
+ flattenInto(proof, state);
1179
+ const atoms = proof.kind === "fact" || proof.kind === "truncated" ? [proof.tuple[0]] : [proof.tuple[0], ...state.steps.map((step) => step.to)];
1180
+ return {
1181
+ atoms,
1182
+ steps: state.steps,
1183
+ assumptions: state.steps.flatMap((step) => step.assumptions),
1184
+ truncated: state.truncated
1185
+ };
1186
+ }
1187
+ function renderExplanation(explanation, describe3) {
1188
+ const lines = [];
1189
+ if (explanation.steps.length > 0) {
1190
+ lines.push(explanation.atoms.map((atom) => describe3(atom)).join(" -> "));
1191
+ }
1192
+ for (const step of explanation.steps) {
1193
+ lines.push(` ${step.reason}`);
1194
+ for (const note of step.notes) {
1195
+ lines.push(` ${note}`);
1196
+ }
1197
+ }
1198
+ for (const assumption of explanation.assumptions) {
1199
+ lines.push(` assuming ${assumption}`);
1200
+ }
1201
+ if (explanation.truncated) {
1202
+ lines.push(" the chain goes on past the proof depth cap");
1203
+ }
1204
+ return lines;
1205
+ }
1206
+ function joinMountedPath(prefix, path6) {
821
1207
  const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
822
- return trimmed === "" ? path4 : trimmed + path4;
1208
+ return trimmed === "" ? path6 : trimmed + path6;
823
1209
  }
824
1210
  function mountPathsOf(edges, childId) {
825
1211
  return pathsOf(edges, /* @__PURE__ */ new Map(), childId, /* @__PURE__ */ new Set());
@@ -856,323 +1242,417 @@ function pathsOf(edges, memo, childId, visiting) {
856
1242
  memo.set(childId, result);
857
1243
  return result;
858
1244
  }
859
- var VALUE_STEP = (0, import_datalog2.constant)("value");
1245
+ function explainResolvedKey(options) {
1246
+ const { db, rules, key, locate, displayPath } = options;
1247
+ const proofDb = new import_datalog2.Database();
1248
+ let baseFacts = 0;
1249
+ for (const relation of db.relationNames()) {
1250
+ for (const tuple2 of db.facts(relation)) {
1251
+ proofDb.add(relation, tuple2);
1252
+ baseFacts++;
1253
+ }
1254
+ }
1255
+ const started = performance.now();
1256
+ (0, import_datalog2.evaluate)(proofDb, rules, import_datalog2.witnesses);
1257
+ const evaluateMs = performance.now() - started;
1258
+ const derivedFacts = proofDb.relationNames().reduce((count, relation) => count + proofDb.size(relation), 0) - baseFacts;
1259
+ const targets = new Set(
1260
+ proofDb.lookup("resolves", 0, key).map((tuple2) => String(tuple2[1]))
1261
+ );
1262
+ if (targets.size !== 1) {
1263
+ return null;
1264
+ }
1265
+ const target = [...targets][0];
1266
+ const proof = (0, import_datalog2.proofOf)(
1267
+ proofDb,
1268
+ "resolves",
1269
+ [key, target],
1270
+ options.maxDepth === void 0 ? {} : { maxDepth: options.maxDepth }
1271
+ );
1272
+ const describe3 = (atom) => {
1273
+ const location = locate(String(atom));
1274
+ return location === null ? displayPath(String(atom)) : `${location.name} (${location.file}:${location.line})`;
1275
+ };
1276
+ const explanation = explainResolutionProof(proof, {
1277
+ describe: describe3,
1278
+ ...options.phrases === void 0 ? {} : { phrases: options.phrases }
1279
+ });
1280
+ if (explanation === null) {
1281
+ return null;
1282
+ }
1283
+ const targetLocation = locate(target);
1284
+ if (targetLocation === null) {
1285
+ return null;
1286
+ }
1287
+ return {
1288
+ explanation,
1289
+ chain: explanation.atoms.map(describe3),
1290
+ lines: renderExplanation(explanation, describe3),
1291
+ target: targetLocation,
1292
+ stats: { baseFacts, derivedFacts, evaluateMs }
1293
+ };
1294
+ }
1295
+ var NO_PLACEHOLDERS = /* @__PURE__ */ new Set();
1296
+ function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1297
+ const candidates = /* @__PURE__ */ new Map();
1298
+ for (const row of rows) {
1299
+ const key = String(row[0]);
1300
+ const answer = String(row[1]);
1301
+ if (answer === key) {
1302
+ continue;
1303
+ }
1304
+ const set2 = candidates.get(key) ?? /* @__PURE__ */ new Set();
1305
+ set2.add(answer);
1306
+ candidates.set(key, set2);
1307
+ }
1308
+ const settled = /* @__PURE__ */ new Map();
1309
+ for (const [key, set2] of candidates) {
1310
+ const answers = withoutPlaceholders(set2, placeholders);
1311
+ if (answers.length === 1) {
1312
+ settled.set(key, answers[0]);
1313
+ }
1314
+ }
1315
+ return settled;
1316
+ }
1317
+ function placeholderValues(db) {
1318
+ return new Set(db.facts("placeholderValue").map((row) => String(row[0])));
1319
+ }
1320
+ function withoutPlaceholders(answers, placeholders) {
1321
+ const kept = [...answers].filter((answer) => !placeholders.has(answer));
1322
+ if (kept.length === 0) {
1323
+ return [...answers];
1324
+ }
1325
+ return kept;
1326
+ }
1327
+ var VALUE_STEP = (0, import_datalog3.constant)("value");
860
1328
  var NAMESPACE_IMPORT_NAME = "*";
861
- var NAMESPACE_IMPORT = (0, import_datalog2.constant)(NAMESPACE_IMPORT_NAME);
862
- var RESULT_STEP = (0, import_datalog2.constant)("result");
1329
+ var NAMESPACE_IMPORT = (0, import_datalog3.constant)(NAMESPACE_IMPORT_NAME);
1330
+ var RESULT_STEP = (0, import_datalog3.constant)("result");
863
1331
  var RESOLUTION_RULES = [
864
1332
  // Aliasing: const x = y, or an identifier referencing a declaration.
865
1333
  // A language with a hop of its own, like JavaScript's `.bind`, states
866
1334
  // it as a step too, or every question but `comesTo` misses it.
867
- (0, import_datalog2.rule)(
1335
+ (0, import_datalog3.rule)(
868
1336
  "stepsTo",
869
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP],
870
- [(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))],
1337
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP],
1338
+ [(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))],
871
1339
  "alias"
872
1340
  ),
873
1341
  // A name written more than once has the value the last write left
874
1342
  // there. The adapter works out which write that is and stays quiet
875
1343
  // when control flow decides, so such a name steps nowhere at all.
876
- (0, import_datalog2.rule)(
1344
+ (0, import_datalog3.rule)(
877
1345
  "stepsTo",
878
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP],
879
- [(0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))],
1346
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP],
1347
+ [(0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))],
880
1348
  "last write"
881
1349
  ),
882
1350
  // A fallback says the value is one of its branches, so each branch is
883
1351
  // a step. A branch that resolves to nothing makes no claim, and two
884
1352
  // branches resolving to different things fail the single-answer policy.
885
- (0, import_datalog2.rule)(
1353
+ (0, import_datalog3.rule)(
886
1354
  "stepsTo",
887
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b"), VALUE_STEP],
888
- [(0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b"))],
1355
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("b"), VALUE_STEP],
1356
+ [(0, import_datalog3.lit)("fallbackBranch", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("b"))],
889
1357
  "fallback"
890
1358
  ),
891
1359
  // An import steps to what the module exports under that name.
892
- (0, import_datalog2.rule)(
1360
+ (0, import_datalog3.rule)(
893
1361
  "stepsTo",
894
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("value"), VALUE_STEP],
1362
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("value"), VALUE_STEP],
895
1363
  [
896
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
897
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
1364
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
1365
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))
898
1366
  ],
899
1367
  "import"
900
1368
  ),
901
1369
  // A parameter steps to what a call passes it. A function called from
902
1370
  // several places leaves its parameter with more than one value, and a
903
1371
  // caller that needs those apart asks `paramAt`.
904
- (0, import_datalog2.rule)(
1372
+ (0, import_datalog3.rule)(
905
1373
  "stepsTo",
906
- [(0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a"), VALUE_STEP],
907
- [(0, import_datalog2.lit)("passesArgument", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a"))],
1374
+ [(0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a"), VALUE_STEP],
1375
+ [(0, import_datalog3.lit)("passesArgument", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a"))],
908
1376
  "argument"
909
1377
  ),
910
1378
  // Reading a property steps to what the object contains under that
911
1379
  // name, whichever way the object arrived: `routes.list` off a name,
912
1380
  // or `make(body).handle` off a call.
913
- (0, import_datalog2.rule)(
1381
+ (0, import_datalog3.rule)(
914
1382
  "stepsTo",
915
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("held"), VALUE_STEP],
1383
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("held"), VALUE_STEP],
916
1384
  [
917
- (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n")),
918
- (0, import_datalog2.lit)("objectOf", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("obj")),
919
- (0, import_datalog2.lit)("contains", (0, import_datalog2.variable)("obj"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held"))
1385
+ (0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n")),
1386
+ (0, import_datalog3.lit)("objectOf", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("obj")),
1387
+ (0, import_datalog3.lit)("contains", (0, import_datalog3.variable)("obj"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held"))
920
1388
  ],
921
1389
  "property read"
922
1390
  ),
923
1391
  // Calling a class makes one of it, so the call steps to the class and
924
1392
  // a method read off the result is the one the class declares. The
925
1393
  // caveat below is about a factory function, and a class is not one.
926
- (0, import_datalog2.rule)(
1394
+ (0, import_datalog3.rule)(
927
1395
  "stepsTo",
928
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("cls"), VALUE_STEP],
1396
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("cls"), VALUE_STEP],
929
1397
  [
930
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
931
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("cls")),
932
- (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("cls"))
1398
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
1399
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("cls")),
1400
+ (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("cls"))
933
1401
  ],
934
1402
  "class instance"
935
1403
  ),
936
1404
  // Wrapper transparency, derived: calling a factory that returns a
937
1405
  // function which calls its parameter k steps to argument k.
938
- (0, import_datalog2.rule)(
1406
+ (0, import_datalog3.rule)(
939
1407
  "stepsTo",
940
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("a"), VALUE_STEP],
1408
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("a"), VALUE_STEP],
941
1409
  [
942
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
943
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f")),
944
- (0, import_datalog2.lit)("unwraps", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")),
945
- (0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a"))
1410
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
1411
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f")),
1412
+ (0, import_datalog3.lit)("unwraps", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")),
1413
+ (0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a"))
946
1414
  ],
947
1415
  "factory unwrap"
948
1416
  ),
949
1417
  // Wrapper transparency, declared: a pack says this callee wraps
950
1418
  // argument k. The callee has to come from the library the pack said,
951
1419
  // so a local object spelled the same way is not mistaken for it.
952
- (0, import_datalog2.rule)(
1420
+ (0, import_datalog3.rule)(
953
1421
  "stepsTo",
954
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("a"), VALUE_STEP],
1422
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("a"), VALUE_STEP],
955
1423
  [
956
- (0, import_datalog2.lit)("calleeName", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("n")),
957
- (0, import_datalog2.lit)("unwrapsByName", (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("k")),
958
- (0, import_datalog2.lit)("wrapperModule", (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m")),
959
- (0, import_datalog2.lit)("calleeOrigin", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("m")),
960
- (0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a"))
1424
+ (0, import_datalog3.lit)("calleeName", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("n")),
1425
+ (0, import_datalog3.lit)("unwrapsByName", (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("k")),
1426
+ (0, import_datalog3.lit)("wrapperModule", (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m")),
1427
+ (0, import_datalog3.lit)("calleeOrigin", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("m")),
1428
+ (0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a"))
961
1429
  ],
962
1430
  "declared wrapper"
963
1431
  ),
964
1432
  // The one step that runs a function forwards: a call steps to what
965
1433
  // the function it invokes returns.
966
- (0, import_datalog2.rule)(
1434
+ (0, import_datalog3.rule)(
967
1435
  "stepsTo",
968
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("ret"), RESULT_STEP],
969
- [(0, import_datalog2.lit)("invokes", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")), (0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("ret"))],
1436
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("ret"), RESULT_STEP],
1437
+ [(0, import_datalog3.lit)("invokes", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")), (0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("ret"))],
970
1438
  "call result"
971
1439
  ),
972
1440
  // Where a walk gets to, and whether it ran a call on the way. The walk
973
1441
  // so far comes first, so demand stays on the value asked about; the
974
1442
  // README says what the other order cost.
975
- (0, import_datalog2.rule)(
1443
+ (0, import_datalog3.rule)(
976
1444
  "reaches",
977
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind")],
978
- [(0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind"))]
1445
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind")],
1446
+ [(0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind"))]
979
1447
  ),
980
- (0, import_datalog2.rule)(
1448
+ (0, import_datalog3.rule)(
981
1449
  "reaches",
982
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind")],
1450
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind")],
983
1451
  [
984
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP),
985
- (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind"))
1452
+ (0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP),
1453
+ (0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("kind"))
986
1454
  ]
987
1455
  ),
988
1456
  // Two rules rather than one leaving the next step's kind free. A free
989
1457
  // kind is a second question about the same relation, and a
990
1458
  // demand-driven run then derives both to answer either.
991
- (0, import_datalog2.rule)(
1459
+ (0, import_datalog3.rule)(
992
1460
  "reaches",
993
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP],
1461
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP],
994
1462
  [
995
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
996
- (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), VALUE_STEP)
1463
+ (0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), RESULT_STEP),
1464
+ (0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"), VALUE_STEP)
997
1465
  ]
998
1466
  ),
999
- (0, import_datalog2.rule)(
1467
+ (0, import_datalog3.rule)(
1000
1468
  "reaches",
1001
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP],
1469
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP],
1002
1470
  [
1003
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
1004
- (0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), RESULT_STEP)
1471
+ (0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), RESULT_STEP),
1472
+ (0, import_datalog3.lit)("stepsTo", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"), RESULT_STEP)
1005
1473
  ]
1006
1474
  ),
1007
1475
  // What a value comes down to: the walk that never ran a call, stopped
1008
1476
  // at a function or an object. Something already written out comes to
1009
1477
  // itself, and every other walk ends at one of those.
1010
- (0, import_datalog2.rule)("comesTo", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("func", (0, import_datalog2.variable)("x"))]),
1011
- (0, import_datalog2.rule)("comesTo", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("x"))]),
1012
- (0, import_datalog2.rule)(
1478
+ (0, import_datalog3.rule)("comesTo", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("func", (0, import_datalog3.variable)("x"))]),
1479
+ (0, import_datalog3.rule)("comesTo", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("x"))]),
1480
+ (0, import_datalog3.rule)(
1013
1481
  "comesTo",
1014
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1015
- [(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("z"))]
1482
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1483
+ [(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("z"))]
1016
1484
  ),
1017
- (0, import_datalog2.rule)(
1485
+ (0, import_datalog3.rule)(
1018
1486
  "comesTo",
1019
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1020
- [(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("z"))]
1487
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1488
+ [(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("z"))]
1021
1489
  ),
1022
1490
  // The same stopping condition, for the walk that ran a call. A call
1023
1491
  // gets no `comesTo` answer on purpose: coming back with what a
1024
1492
  // factory returned would fight the unwrapping answer.
1025
- (0, import_datalog2.rule)(
1493
+ (0, import_datalog3.rule)(
1026
1494
  "givesBack",
1027
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1028
- [(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP), (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("z"))]
1495
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1496
+ [(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP), (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("z"))]
1029
1497
  ),
1030
- (0, import_datalog2.rule)(
1498
+ (0, import_datalog3.rule)(
1031
1499
  "givesBack",
1032
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1033
- [(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("z"))]
1500
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1501
+ [(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), RESULT_STEP), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("z"))]
1034
1502
  ),
1035
1503
  // Following a value to the expression it is written as, whatever kind
1036
1504
  // that is. A GraphQL document is neither a function nor an object, so
1037
1505
  // `comesTo` never reaches one.
1038
- (0, import_datalog2.rule)("isWrittenAs", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("writtenValue", (0, import_datalog2.variable)("x"))]),
1039
- (0, import_datalog2.rule)("isWrittenAs", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("x"))]),
1040
- (0, import_datalog2.rule)(
1506
+ (0, import_datalog3.rule)("isWrittenAs", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("writtenValue", (0, import_datalog3.variable)("x"))]),
1507
+ (0, import_datalog3.rule)("isWrittenAs", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("x"))]),
1508
+ (0, import_datalog3.rule)(
1509
+ "isWrittenAs",
1510
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1511
+ [(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("writtenValue", (0, import_datalog3.variable)("z"))]
1512
+ ),
1513
+ (0, import_datalog3.rule)(
1041
1514
  "isWrittenAs",
1042
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1043
- [(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("writtenValue", (0, import_datalog2.variable)("z"))]
1515
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1516
+ [(0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), VALUE_STEP), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("z"))]
1044
1517
  ),
1045
- (0, import_datalog2.rule)(
1518
+ // A call is written as whatever its callee's return value is written
1519
+ // as. Unlike `comesTo`, this has nothing to protect: which expression
1520
+ // a value was written as does not compete with unwrapping it.
1521
+ (0, import_datalog3.rule)(
1046
1522
  "isWrittenAs",
1047
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1048
- [(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("z"))]
1523
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1524
+ [
1525
+ (0, import_datalog3.lit)("invokes", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("f")),
1526
+ (0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("ret")),
1527
+ (0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("ret"), (0, import_datalog3.variable)("z"))
1528
+ ]
1049
1529
  ),
1050
1530
  // What one call site put in a parameter, told apart from what the
1051
1531
  // other callers passed.
1052
- (0, import_datalog2.rule)(
1532
+ (0, import_datalog3.rule)(
1053
1533
  "paramAt",
1054
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("z")],
1534
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("z")],
1055
1535
  [
1056
- (0, import_datalog2.lit)("passesArgument", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a")),
1057
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("a"), (0, import_datalog2.variable)("z"))
1536
+ (0, import_datalog3.lit)("passesArgument", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a")),
1537
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("a"), (0, import_datalog3.variable)("z"))
1058
1538
  ]
1059
1539
  ),
1060
1540
  // An argument arriving at the parameter it is passed to, by position
1061
1541
  // or by the name the caller wrote, keeping the call it went through
1062
1542
  // so `paramAt` can tell two call sites apart.
1063
- (0, import_datalog2.rule)(
1543
+ (0, import_datalog3.rule)(
1064
1544
  "passesArgument",
1065
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a")],
1545
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a")],
1066
1546
  [
1067
- (0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("p")),
1068
- (0, import_datalog2.lit)("callsFunction", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")),
1069
- (0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a"))
1547
+ (0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("p")),
1548
+ (0, import_datalog3.lit)("callsFunction", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")),
1549
+ (0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a"))
1070
1550
  ]
1071
1551
  ),
1072
- (0, import_datalog2.rule)(
1552
+ (0, import_datalog3.rule)(
1073
1553
  "passesArgument",
1074
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a")],
1554
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("a")],
1075
1555
  [
1076
- (0, import_datalog2.lit)("paramNamed", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("p")),
1077
- (0, import_datalog2.lit)("callsFunction", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")),
1078
- (0, import_datalog2.lit)("callKeywordArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("a"))
1556
+ (0, import_datalog3.lit)("paramNamed", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("p")),
1557
+ (0, import_datalog3.lit)("callsFunction", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")),
1558
+ (0, import_datalog3.lit)("callKeywordArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("a"))
1079
1559
  ]
1080
1560
  ),
1081
1561
  // Which module's export a re-exported name forwards to, however
1082
1562
  // many barrels deep the forwarding runs.
1083
- (0, import_datalog2.rule)(
1563
+ (0, import_datalog3.rule)(
1084
1564
  "moduleForwards",
1085
- [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")],
1086
- [(0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"))]
1565
+ [(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")],
1566
+ [(0, import_datalog3.lit)("reExports", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"))]
1087
1567
  ),
1088
- (0, import_datalog2.rule)(
1568
+ (0, import_datalog3.rule)(
1089
1569
  "moduleForwards",
1090
- [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m3"), (0, import_datalog2.variable)("n3")],
1570
+ [(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m3"), (0, import_datalog3.variable)("n3")],
1091
1571
  [
1092
- (0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")),
1093
- (0, import_datalog2.lit)("moduleForwards", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"), (0, import_datalog2.variable)("m3"), (0, import_datalog2.variable)("n3"))
1572
+ (0, import_datalog3.lit)("reExports", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")),
1573
+ (0, import_datalog3.lit)("moduleForwards", (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"), (0, import_datalog3.variable)("m3"), (0, import_datalog3.variable)("n3"))
1094
1574
  ]
1095
1575
  ),
1096
1576
  // What a module exports: directly, or through re-export chains.
1097
- (0, import_datalog2.rule)(
1577
+ (0, import_datalog3.rule)(
1098
1578
  "moduleExport",
1099
- [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
1100
- [(0, import_datalog2.lit)("exportsAs", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))],
1579
+ [(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
1580
+ [(0, import_datalog3.lit)("exportsAs", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))],
1101
1581
  "export"
1102
1582
  ),
1103
- (0, import_datalog2.rule)(
1583
+ (0, import_datalog3.rule)(
1104
1584
  "moduleExport",
1105
- [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
1585
+ [(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
1106
1586
  [
1107
- (0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")),
1108
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"), (0, import_datalog2.variable)("value"))
1587
+ (0, import_datalog3.lit)("reExports", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")),
1588
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"), (0, import_datalog3.variable)("value"))
1109
1589
  ],
1110
1590
  "re-export"
1111
1591
  ),
1112
- (0, import_datalog2.rule)(
1592
+ (0, import_datalog3.rule)(
1113
1593
  "moduleExport",
1114
- [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
1594
+ [(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
1115
1595
  [
1116
- (0, import_datalog2.lit)("reExportsAll", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("m2")),
1117
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
1596
+ (0, import_datalog3.lit)("reExportsAll", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("m2")),
1597
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))
1118
1598
  ],
1119
1599
  "re-export all"
1120
1600
  ),
1121
1601
  // The function a call runs, written from the call's side because a
1122
1602
  // caller asking what a call gives back has the call in hand.
1123
- (0, import_datalog2.rule)(
1603
+ (0, import_datalog3.rule)(
1124
1604
  "invokes",
1125
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
1126
- [(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
1605
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
1606
+ [(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")), (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))]
1127
1607
  ),
1128
1608
  // The callee is itself a call: `daoBuilder()()`.
1129
- (0, import_datalog2.rule)(
1609
+ (0, import_datalog3.rule)(
1130
1610
  "invokes",
1131
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
1132
- [(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("givesBack", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
1611
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
1612
+ [(0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")), (0, import_datalog3.lit)("givesBack", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))]
1133
1613
  ),
1134
1614
  // The object an expression refers to: a name through `comesTo`, a
1135
1615
  // factory call through what it gives back. Naming the step is what
1136
1616
  // makes `routes.list` and `make(body).handle` one rule.
1137
- (0, import_datalog2.rule)(
1617
+ (0, import_datalog3.rule)(
1138
1618
  "objectOf",
1139
- [(0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("obj")],
1140
- [(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("obj")), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("obj"))]
1619
+ [(0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("obj")],
1620
+ [(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("obj")), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("obj"))]
1141
1621
  ),
1142
- (0, import_datalog2.rule)(
1622
+ (0, import_datalog3.rule)(
1143
1623
  "objectOf",
1144
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("obj")],
1145
- [(0, import_datalog2.lit)("givesBack", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("obj")), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("obj"))]
1624
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("obj")],
1625
+ [(0, import_datalog3.lit)("givesBack", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("obj")), (0, import_datalog3.lit)("objectValue", (0, import_datalog3.variable)("obj"))]
1146
1626
  ),
1147
1627
  // Which calls a function, found by the name the call is written as
1148
1628
  // rather than by resolving every callee in the project. A caller knows
1149
1629
  // the function and wants its call sites, so both of these start from
1150
1630
  // the function. Starting from `call` instead asks what every call in
1151
1631
  // the project imports, which was 72% of everything derived.
1152
- (0, import_datalog2.rule)(
1632
+ (0, import_datalog3.rule)(
1153
1633
  "callsFunction",
1154
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
1155
- [(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
1634
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
1635
+ [(0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))]
1156
1636
  ),
1157
- (0, import_datalog2.rule)(
1637
+ (0, import_datalog3.rule)(
1158
1638
  "callsFunction",
1159
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
1639
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
1160
1640
  [
1161
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("f")),
1162
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
1163
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))
1641
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("f")),
1642
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
1643
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))
1164
1644
  ]
1165
1645
  ),
1166
1646
  // The same, for a language whose adapter writes the import down as a
1167
1647
  // declaration and the call's callee as the name referring to it.
1168
- (0, import_datalog2.rule)(
1648
+ (0, import_datalog3.rule)(
1169
1649
  "callsFunction",
1170
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
1650
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("f")],
1171
1651
  [
1172
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("f")),
1173
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("d"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
1174
- (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("d")),
1175
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))
1652
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("f")),
1653
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("d"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
1654
+ (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("d")),
1655
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))
1176
1656
  ]
1177
1657
  ),
1178
1658
  // What an object contains, its base class included, so a method the base
@@ -1180,47 +1660,47 @@ var RESOLUTION_RULES = [
1180
1660
  // declare gives two, and the caller decides. This is its own relation
1181
1661
  // rather than more `holdsProperty`, which stays something an adapter
1182
1662
  // states and the rules only read.
1183
- (0, import_datalog2.rule)(
1663
+ (0, import_datalog3.rule)(
1184
1664
  "contains",
1185
- [(0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held")],
1186
- [(0, import_datalog2.lit)("holdsProperty", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held"))]
1665
+ [(0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held")],
1666
+ [(0, import_datalog3.lit)("holdsProperty", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held"))]
1187
1667
  ),
1188
- (0, import_datalog2.rule)(
1668
+ (0, import_datalog3.rule)(
1189
1669
  "contains",
1190
- [(0, import_datalog2.variable)("cls"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held")],
1670
+ [(0, import_datalog3.variable)("cls"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held")],
1191
1671
  [
1192
- (0, import_datalog2.lit)("extends", (0, import_datalog2.variable)("cls"), (0, import_datalog2.variable)("base")),
1193
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("base"), (0, import_datalog2.variable)("baseCls")),
1194
- (0, import_datalog2.lit)("contains", (0, import_datalog2.variable)("baseCls"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held"))
1672
+ (0, import_datalog3.lit)("extends", (0, import_datalog3.variable)("cls"), (0, import_datalog3.variable)("base")),
1673
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("base"), (0, import_datalog3.variable)("baseCls")),
1674
+ (0, import_datalog3.lit)("contains", (0, import_datalog3.variable)("baseCls"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("held"))
1195
1675
  ]
1196
1676
  ),
1197
1677
  // Which function a factory returns, and which of its parameters that
1198
1678
  // function calls: together they are what makes it a wrapper.
1199
- (0, import_datalog2.rule)(
1679
+ (0, import_datalog3.rule)(
1200
1680
  "returnsFunc",
1201
- [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g")],
1681
+ [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("g")],
1202
1682
  [
1203
- (0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("value")),
1204
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("value"), (0, import_datalog2.variable)("g")),
1205
- (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("g"))
1683
+ (0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("value")),
1684
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("value"), (0, import_datalog3.variable)("g")),
1685
+ (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("g"))
1206
1686
  ]
1207
1687
  ),
1208
1688
  // A call made by a nested closure counts as made by the function
1209
1689
  // that declares it; the closure runs as part of that function.
1210
- (0, import_datalog2.rule)("bodyCallsDeep", [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")], [(0, import_datalog2.lit)("bodyCalls", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c"))]),
1211
- (0, import_datalog2.rule)(
1690
+ (0, import_datalog3.rule)("bodyCallsDeep", [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")], [(0, import_datalog3.lit)("bodyCalls", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c"))]),
1691
+ (0, import_datalog3.rule)(
1212
1692
  "bodyCallsDeep",
1213
- [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")],
1214
- [(0, import_datalog2.lit)("containsFn", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g")), (0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("c"))]
1693
+ [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")],
1694
+ [(0, import_datalog3.lit)("containsFn", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("g")), (0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("c"))]
1215
1695
  ),
1216
- (0, import_datalog2.rule)(
1696
+ (0, import_datalog3.rule)(
1217
1697
  "unwraps",
1218
- [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")],
1698
+ [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")],
1219
1699
  [
1220
- (0, import_datalog2.lit)("returnsFunc", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g")),
1221
- (0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("c")),
1222
- (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("p")),
1223
- (0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("p"))
1700
+ (0, import_datalog3.lit)("returnsFunc", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("g")),
1701
+ (0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("c")),
1702
+ (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("p")),
1703
+ (0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("p"))
1224
1704
  ]
1225
1705
  ),
1226
1706
  // Argument flow: which parameter a value traces back to. Directly
@@ -1228,289 +1708,289 @@ var RESOLUTION_RULES = [
1228
1708
  // another unwrapping factory. This is what lets
1229
1709
  // `createProtected(h) { return service.withAuth(h); }` unwrap:
1230
1710
  // the returned call passes h through withAuth, which unwraps.
1231
- (0, import_datalog2.rule)(
1711
+ (0, import_datalog3.rule)(
1232
1712
  "flowsToParam",
1233
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("p")],
1713
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("p")],
1234
1714
  [
1235
- (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("p")),
1236
- (0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("anyF"), (0, import_datalog2.variable)("anyK"), (0, import_datalog2.variable)("p"))
1715
+ (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("p")),
1716
+ (0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("anyF"), (0, import_datalog3.variable)("anyK"), (0, import_datalog3.variable)("p"))
1237
1717
  ]
1238
1718
  ),
1239
- (0, import_datalog2.rule)(
1719
+ (0, import_datalog3.rule)(
1240
1720
  "flowsToParam",
1241
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p")],
1721
+ [(0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p")],
1242
1722
  [
1243
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
1244
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f")),
1245
- (0, import_datalog2.lit)("unwraps", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")),
1246
- (0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a")),
1247
- (0, import_datalog2.lit)("flowsToParam", (0, import_datalog2.variable)("a"), (0, import_datalog2.variable)("p"))
1723
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
1724
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f")),
1725
+ (0, import_datalog3.lit)("unwraps", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")),
1726
+ (0, import_datalog3.lit)("callArg", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("a")),
1727
+ (0, import_datalog3.lit)("flowsToParam", (0, import_datalog3.variable)("a"), (0, import_datalog3.variable)("p"))
1248
1728
  ]
1249
1729
  ),
1250
- (0, import_datalog2.rule)(
1730
+ (0, import_datalog3.rule)(
1251
1731
  "unwraps",
1252
- [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")],
1732
+ [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k")],
1253
1733
  [
1254
- (0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("value")),
1255
- (0, import_datalog2.lit)("flowsToParam", (0, import_datalog2.variable)("value"), (0, import_datalog2.variable)("p")),
1256
- (0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("p"))
1734
+ (0, import_datalog3.lit)("returnsValue", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("value")),
1735
+ (0, import_datalog3.lit)("flowsToParam", (0, import_datalog3.variable)("value"), (0, import_datalog3.variable)("p")),
1736
+ (0, import_datalog3.lit)("paramOf", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("k"), (0, import_datalog3.variable)("p"))
1257
1737
  ]
1258
1738
  ),
1259
1739
  // Where a name comes from, when what it refers to lives outside the
1260
1740
  // source being read. A walk ends at something written out in source,
1261
1741
  // so it never reaches a library's own function.
1262
- (0, import_datalog2.rule)(
1742
+ (0, import_datalog3.rule)(
1263
1743
  "comesFrom",
1264
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1265
- [(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))]
1744
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1745
+ [(0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))]
1266
1746
  ),
1267
- (0, import_datalog2.rule)(
1747
+ (0, import_datalog3.rule)(
1268
1748
  "comesFrom",
1269
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1749
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1270
1750
  [
1271
- (0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP),
1272
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1751
+ (0, import_datalog3.lit)("reaches", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), VALUE_STEP),
1752
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1273
1753
  ]
1274
1754
  ),
1275
1755
  // Calling f ends up calling the name n that module m exports, one
1276
1756
  // hop through f's own body or deeper through a wrapper of a wrapper.
1277
1757
  // Several results is normal: a composed decorator applies them all.
1278
- (0, import_datalog2.rule)(
1758
+ (0, import_datalog3.rule)(
1279
1759
  "callsInto",
1280
- [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1760
+ [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1281
1761
  [
1282
- (0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")),
1283
- (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1762
+ (0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")),
1763
+ (0, import_datalog3.lit)("comesFrom", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1284
1764
  ]
1285
1765
  ),
1286
- (0, import_datalog2.rule)(
1766
+ (0, import_datalog3.rule)(
1287
1767
  "callsInto",
1288
- [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1768
+ [(0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1289
1769
  [
1290
- (0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")),
1291
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("g")),
1292
- (0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1770
+ (0, import_datalog3.lit)("bodyCallsDeep", (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("c")),
1771
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("g")),
1772
+ (0, import_datalog3.lit)("callsInto", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1293
1773
  ]
1294
1774
  ),
1295
1775
  // The question callers ask: what a value comes to, narrowed to the
1296
1776
  // functions. Objects appear in the middle of chains and never in an
1297
1777
  // answer.
1298
- (0, import_datalog2.rule)(
1778
+ (0, import_datalog3.rule)(
1299
1779
  "resolves",
1300
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1301
- [(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")), (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("z"))]
1780
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1781
+ [(0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")), (0, import_datalog3.lit)("func", (0, import_datalog3.variable)("z"))]
1302
1782
  )
1303
1783
  ];
1304
1784
  var RESOLUTION_QUESTIONS = [
1305
- (0, import_datalog2.rule)(
1785
+ (0, import_datalog3.rule)(
1306
1786
  "wantedResolves",
1307
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1308
- [(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("resolves", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
1787
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1788
+ [(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("resolves", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
1309
1789
  ),
1310
- (0, import_datalog2.rule)(
1790
+ (0, import_datalog3.rule)(
1311
1791
  "wantedComesTo",
1312
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1313
- [(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
1792
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1793
+ [(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
1314
1794
  ),
1315
- (0, import_datalog2.rule)(
1795
+ (0, import_datalog3.rule)(
1316
1796
  "wantedComesTo",
1317
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1318
- [(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
1797
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1798
+ [(0, import_datalog3.lit)("wantedOrigin", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
1319
1799
  ),
1320
- (0, import_datalog2.rule)(
1800
+ (0, import_datalog3.rule)(
1321
1801
  "wantedIsWrittenAs",
1322
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1323
- [(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
1802
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1803
+ [(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
1324
1804
  ),
1325
1805
  // A call is given no `comesTo`, so this is the only way to ask what
1326
1806
  // object one arrives at, and without it a demand-driven run derives
1327
1807
  // `objectOf` nowhere.
1328
- (0, import_datalog2.rule)(
1808
+ (0, import_datalog3.rule)(
1329
1809
  "wantedObjectOf",
1330
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1331
- [(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("objectOf", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
1810
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1811
+ [(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("objectOf", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
1332
1812
  ),
1333
1813
  // Keyed by the parameter, since that is what a caller has in hand
1334
1814
  // when it wants the call sites told apart.
1335
- (0, import_datalog2.rule)(
1815
+ (0, import_datalog3.rule)(
1336
1816
  "wantedParamAt",
1337
- [(0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("z")],
1338
- [(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("p")), (0, import_datalog2.lit)("paramAt", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("z"))]
1817
+ [(0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("z")],
1818
+ [(0, import_datalog3.lit)("wanted", (0, import_datalog3.variable)("p")), (0, import_datalog3.lit)("paramAt", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("p"), (0, import_datalog3.variable)("z"))]
1339
1819
  ),
1340
- (0, import_datalog2.rule)(
1820
+ (0, import_datalog3.rule)(
1341
1821
  "wantedComesFrom",
1342
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1343
- [(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))]
1822
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1823
+ [(0, import_datalog3.lit)("wantedOrigin", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("comesFrom", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))]
1344
1824
  ),
1345
1825
  // Call-origin questions, for attribution, in their own demand
1346
1826
  // class. Attribution stops at the import declaration, so nothing
1347
1827
  // here demands reaches or callsInto. The chains are closures with
1348
1828
  // no depth bound: an alias run of any length resolves.
1349
- (0, import_datalog2.rule)("callOriginChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedCallOrigin", (0, import_datalog2.variable)("x"))]),
1829
+ (0, import_datalog3.rule)("callOriginChain", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("wantedCallOrigin", (0, import_datalog3.variable)("x"))]),
1350
1830
  // The chain with at least one hop taken, which is what tells a
1351
1831
  // destructured binding apart from asking about a node directly. The
1352
1832
  // whole chain is the seed plus those hops.
1353
- (0, import_datalog2.rule)(
1833
+ (0, import_datalog3.rule)(
1354
1834
  "callOriginChain",
1355
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1356
- [(0, import_datalog2.lit)("callOriginChainStepped", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
1835
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1836
+ [(0, import_datalog3.lit)("callOriginChainStepped", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"))]
1357
1837
  ),
1358
- (0, import_datalog2.rule)(
1838
+ (0, import_datalog3.rule)(
1359
1839
  "callOriginChainStepped",
1360
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1361
- [(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1840
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1841
+ [(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
1362
1842
  ),
1363
- (0, import_datalog2.rule)(
1843
+ (0, import_datalog3.rule)(
1364
1844
  "callOriginChainStepped",
1365
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1845
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1366
1846
  [
1367
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1368
- (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1847
+ (0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
1848
+ (0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
1369
1849
  ]
1370
1850
  ),
1371
- (0, import_datalog2.rule)(
1851
+ (0, import_datalog3.rule)(
1372
1852
  "callOriginChainStepped",
1373
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1853
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1374
1854
  [
1375
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1376
- (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1855
+ (0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
1856
+ (0, import_datalog3.lit)("fallbackBranch", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
1377
1857
  ]
1378
1858
  ),
1379
- (0, import_datalog2.rule)(
1859
+ (0, import_datalog3.rule)(
1380
1860
  "wantedCallOriginPair",
1381
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1861
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1382
1862
  [
1383
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1384
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1863
+ (0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
1864
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1385
1865
  ]
1386
1866
  ),
1387
1867
  // An import of a project barrel is an import of what the barrel
1388
1868
  // forwards, so the pair surfaces the module behind the chain.
1389
- (0, import_datalog2.rule)(
1869
+ (0, import_datalog3.rule)(
1390
1870
  "wantedCallOriginPair",
1391
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")],
1871
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2")],
1392
1872
  [
1393
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1394
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
1395
- (0, import_datalog2.lit)("moduleForwards", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"))
1873
+ (0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
1874
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
1875
+ (0, import_datalog3.lit)("moduleForwards", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("m2"), (0, import_datalog3.variable)("n2"))
1396
1876
  ]
1397
1877
  ),
1398
1878
  // A namespace member: the module's export of the member's own name.
1399
1879
  // The imports literal leads, since it is the smallest relation.
1400
- (0, import_datalog2.rule)(
1880
+ (0, import_datalog3.rule)(
1401
1881
  "wantedCallOriginPair",
1402
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("p")],
1882
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("p")],
1403
1883
  [
1404
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("m"), NAMESPACE_IMPORT),
1405
- (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("p")),
1406
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))
1884
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("m"), NAMESPACE_IMPORT),
1885
+ (0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("p")),
1886
+ (0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))
1407
1887
  ]
1408
1888
  ),
1409
- (0, import_datalog2.rule)(
1889
+ (0, import_datalog3.rule)(
1410
1890
  "wantedCallOriginPair",
1411
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("p")],
1891
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("p")],
1412
1892
  [
1413
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("d"), (0, import_datalog2.variable)("m"), NAMESPACE_IMPORT),
1414
- (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("d")),
1415
- (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("p")),
1416
- (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))
1893
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("d"), (0, import_datalog3.variable)("m"), NAMESPACE_IMPORT),
1894
+ (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("d")),
1895
+ (0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("p")),
1896
+ (0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"))
1417
1897
  ]
1418
1898
  ),
1419
1899
  // What a call made: through the call to its callee, whose own alias
1420
1900
  // chain leads to the import.
1421
- (0, import_datalog2.rule)(
1901
+ (0, import_datalog3.rule)(
1422
1902
  "callMadeChain",
1423
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("f")],
1424
- [(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
1903
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("f")],
1904
+ [(0, import_datalog3.lit)("callOriginChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("c")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))]
1425
1905
  ),
1426
- (0, import_datalog2.rule)(
1906
+ (0, import_datalog3.rule)(
1427
1907
  "callMadeChain",
1428
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1429
- [(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1908
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1909
+ [(0, import_datalog3.lit)("callMadeChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
1430
1910
  ),
1431
- (0, import_datalog2.rule)(
1911
+ (0, import_datalog3.rule)(
1432
1912
  "callMadeChain",
1433
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1434
- [(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1913
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1914
+ [(0, import_datalog3.lit)("callMadeChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
1435
1915
  ),
1436
- (0, import_datalog2.rule)(
1916
+ (0, import_datalog3.rule)(
1437
1917
  "wantedCallOriginPair",
1438
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1918
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1439
1919
  [
1440
- (0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1441
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1920
+ (0, import_datalog3.lit)("callMadeChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
1921
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1442
1922
  ]
1443
1923
  ),
1444
1924
  // A member destructured off what a call made keeps the member name
1445
1925
  // as one more path segment.
1446
- (0, import_datalog2.rule)(
1926
+ (0, import_datalog3.rule)(
1447
1927
  "callMemberChain",
1448
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("p")],
1928
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("f"), (0, import_datalog3.variable)("p")],
1449
1929
  [
1450
- (0, import_datalog2.lit)("callOriginChainStepped", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("e")),
1451
- (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("e"), (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("p")),
1452
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))
1930
+ (0, import_datalog3.lit)("callOriginChainStepped", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("e")),
1931
+ (0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("e"), (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("p")),
1932
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("f"))
1453
1933
  ]
1454
1934
  ),
1455
- (0, import_datalog2.rule)(
1935
+ (0, import_datalog3.rule)(
1456
1936
  "callMemberChain",
1457
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("p")],
1937
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("p")],
1458
1938
  [
1459
- (0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
1460
- (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1939
+ (0, import_datalog3.lit)("callMemberChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("p")),
1940
+ (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
1461
1941
  ]
1462
1942
  ),
1463
- (0, import_datalog2.rule)(
1943
+ (0, import_datalog3.rule)(
1464
1944
  "callMemberChain",
1465
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("p")],
1945
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z"), (0, import_datalog3.variable)("p")],
1466
1946
  [
1467
- (0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
1468
- (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1947
+ (0, import_datalog3.lit)("callMemberChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("p")),
1948
+ (0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))
1469
1949
  ]
1470
1950
  ),
1471
- (0, import_datalog2.rule)(
1951
+ (0, import_datalog3.rule)(
1472
1952
  "wantedCallOriginMember",
1473
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("p")],
1953
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("p")],
1474
1954
  [
1475
- (0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
1476
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1955
+ (0, import_datalog3.lit)("callMemberChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("p")),
1956
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1477
1957
  ]
1478
1958
  ),
1479
- (0, import_datalog2.rule)(
1959
+ (0, import_datalog3.rule)(
1480
1960
  "wantedCallsInto",
1481
- [(0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1961
+ [(0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1482
1962
  [
1483
- (0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")),
1484
- (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("g")),
1485
- (0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1963
+ (0, import_datalog3.lit)("wantedOrigin", (0, import_datalog3.variable)("x")),
1964
+ (0, import_datalog3.lit)("comesTo", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("g")),
1965
+ (0, import_datalog3.lit)("callsInto", (0, import_datalog3.variable)("g"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1486
1966
  ]
1487
1967
  ),
1488
1968
  // Subject identity, its own demand class; the README's subject
1489
1969
  // section says why the origin pair rides on the written-value walk.
1490
- (0, import_datalog2.rule)(
1970
+ (0, import_datalog3.rule)(
1491
1971
  "wantedSubjectWritten",
1492
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")],
1493
- [(0, import_datalog2.lit)("wantedSubject", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r"))]
1972
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")],
1973
+ [(0, import_datalog3.lit)("wantedSubject", (0, import_datalog3.variable)("x")), (0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r"))]
1494
1974
  ),
1495
- (0, import_datalog2.rule)(
1975
+ (0, import_datalog3.rule)(
1496
1976
  "wantedSubjectConstruction",
1497
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1977
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")],
1498
1978
  [
1499
- (0, import_datalog2.lit)("wantedSubject", (0, import_datalog2.variable)("x")),
1500
- (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")),
1501
- (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
1502
- (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1979
+ (0, import_datalog3.lit)("wantedSubject", (0, import_datalog3.variable)("x")),
1980
+ (0, import_datalog3.lit)("isWrittenAs", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")),
1981
+ (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c")),
1982
+ (0, import_datalog3.lit)("comesFrom", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"))
1503
1983
  ]
1504
1984
  ),
1505
1985
  // The export table of one module, keyed by the module asked about.
1506
1986
  // The moduleExport rules flatten re-export chains of any length, and
1507
1987
  // the demand cone stays on export facts alone.
1508
- (0, import_datalog2.rule)(
1988
+ (0, import_datalog3.rule)(
1509
1989
  "wantedModuleExport",
1510
- [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
1990
+ [(0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value")],
1511
1991
  [
1512
- (0, import_datalog2.lit)("wantedExportsOf", (0, import_datalog2.variable)("m")),
1513
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
1992
+ (0, import_datalog3.lit)("wantedExportsOf", (0, import_datalog3.variable)("m")),
1993
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("value"))
1514
1994
  ]
1515
1995
  ),
1516
1996
  // The calls behind a receiver, for a pack that wants the anchor a
@@ -1519,63 +1999,63 @@ var RESOLUTION_QUESTIONS = [
1519
1999
  // A class's ancestry, one hop per extends through the binding that
1520
2000
  // says which class the written base is; the names on the way out
1521
2001
  // are what a storage pack matches its library's bases against.
1522
- (0, import_datalog2.rule)("ancestryChain", [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("c")], [(0, import_datalog2.lit)("wantedAncestry", (0, import_datalog2.variable)("c"))]),
1523
- (0, import_datalog2.rule)(
2002
+ (0, import_datalog3.rule)("ancestryChain", [(0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("c")], [(0, import_datalog3.lit)("wantedAncestry", (0, import_datalog3.variable)("c"))]),
2003
+ (0, import_datalog3.rule)(
1524
2004
  "ancestryChain",
1525
- [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b2")],
2005
+ [(0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("b2")],
1526
2006
  [
1527
- (0, import_datalog2.lit)("ancestryChain", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b")),
1528
- (0, import_datalog2.lit)("extends", (0, import_datalog2.variable)("b"), (0, import_datalog2.variable)("x")),
1529
- (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b2"))
2007
+ (0, import_datalog3.lit)("ancestryChain", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("b")),
2008
+ (0, import_datalog3.lit)("extends", (0, import_datalog3.variable)("b"), (0, import_datalog3.variable)("x")),
2009
+ (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("b2"))
1530
2010
  ]
1531
2011
  ),
1532
- (0, import_datalog2.rule)(
2012
+ (0, import_datalog3.rule)(
1533
2013
  "wantedBaseName",
1534
- [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("n")],
1535
- [(0, import_datalog2.lit)("ancestryChain", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b")), (0, import_datalog2.lit)("extendsNamed", (0, import_datalog2.variable)("b"), (0, import_datalog2.variable)("n"))]
2014
+ [(0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("n")],
2015
+ [(0, import_datalog3.lit)("ancestryChain", (0, import_datalog3.variable)("c"), (0, import_datalog3.variable)("b")), (0, import_datalog3.lit)("extendsNamed", (0, import_datalog3.variable)("b"), (0, import_datalog3.variable)("n"))]
1536
2016
  ),
1537
- (0, import_datalog2.rule)("anchorChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedAnchor", (0, import_datalog2.variable)("x"))]),
1538
- (0, import_datalog2.rule)(
2017
+ (0, import_datalog3.rule)("anchorChain", [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("x")], [(0, import_datalog3.lit)("wantedAnchor", (0, import_datalog3.variable)("x"))]),
2018
+ (0, import_datalog3.rule)(
1539
2019
  "anchorChain",
1540
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1541
- [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
2020
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
2021
+ [(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("binds", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
1542
2022
  ),
1543
- (0, import_datalog2.rule)(
2023
+ (0, import_datalog3.rule)(
1544
2024
  "anchorChain",
1545
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1546
- [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
2025
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
2026
+ [(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("endsHolding", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
1547
2027
  ),
1548
- (0, import_datalog2.rule)(
2028
+ (0, import_datalog3.rule)(
1549
2029
  "anchorChain",
1550
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1551
- [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
2030
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
2031
+ [(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")), (0, import_datalog3.lit)("fallbackBranch", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("z"))]
1552
2032
  ),
1553
- (0, import_datalog2.rule)(
2033
+ (0, import_datalog3.rule)(
1554
2034
  "anchorChain",
1555
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
2035
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("z")],
1556
2036
  [
1557
- (0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1558
- (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
1559
- (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("z"))
2037
+ (0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
2038
+ (0, import_datalog3.lit)("imports", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n")),
2039
+ (0, import_datalog3.lit)("moduleExport", (0, import_datalog3.variable)("m"), (0, import_datalog3.variable)("n"), (0, import_datalog3.variable)("z"))
1560
2040
  ]
1561
2041
  ),
1562
- (0, import_datalog2.rule)(
2042
+ (0, import_datalog3.rule)(
1563
2043
  "anchorChain",
1564
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("c")],
1565
- [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
2044
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("c")],
2045
+ [(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))]
1566
2046
  ),
1567
- (0, import_datalog2.rule)(
2047
+ (0, import_datalog3.rule)(
1568
2048
  "anchorChain",
1569
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("o")],
2049
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("o")],
1570
2050
  [
1571
- (0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1572
- (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"))
2051
+ (0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("y")),
2052
+ (0, import_datalog3.lit)("readsProperty", (0, import_datalog3.variable)("y"), (0, import_datalog3.variable)("o"), (0, import_datalog3.variable)("n"))
1573
2053
  ]
1574
2054
  ),
1575
- (0, import_datalog2.rule)(
2055
+ (0, import_datalog3.rule)(
1576
2056
  "wantedAnchorCall",
1577
- [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")],
1578
- [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
2057
+ [(0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")],
2058
+ [(0, import_datalog3.lit)("anchorChain", (0, import_datalog3.variable)("x"), (0, import_datalog3.variable)("r")), (0, import_datalog3.lit)("call", (0, import_datalog3.variable)("r"), (0, import_datalog3.variable)("c"))]
1579
2059
  )
1580
2060
  ];
1581
2061
  var ANSWER_RELATIONS = [
@@ -1583,7 +2063,7 @@ var ANSWER_RELATIONS = [
1583
2063
  ];
1584
2064
 
1585
2065
  // src/facts/resolve.ts
1586
- var RESOLUTION_PROGRAM = (0, import_datalog3.deriveOnDemand)(
2066
+ var RESOLUTION_PROGRAM = (0, import_datalog4.deriveOnDemand)(
1587
2067
  [...RESOLUTION_RULES, ...RESOLUTION_QUESTIONS],
1588
2068
  ANSWER_RELATIONS
1589
2069
  );
@@ -1594,23 +2074,33 @@ function resolveCalls(db, callKeys) {
1594
2074
  for (const callKey of callKeys) {
1595
2075
  db.add("wanted", [callKey]);
1596
2076
  }
1597
- (0, import_datalog3.evaluate)(db, RESOLUTION_PROGRAM.rules);
2077
+ (0, import_datalog4.evaluate)(db, RESOLUTION_PROGRAM.rules);
1598
2078
  }
1599
2079
  function subjectConstructions(db, valueKeys) {
1600
2080
  const found = /* @__PURE__ */ new Map();
1601
2081
  if (valueKeys.length === 0) {
1602
2082
  return found;
1603
2083
  }
1604
- for (const valueKey2 of valueKeys) {
1605
- db.add("wantedSubject", [valueKey2]);
1606
- }
1607
- (0, import_datalog3.evaluate)(db, RESOLUTION_PROGRAM.rules);
1608
- const writtenAs = /* @__PURE__ */ new Map();
1609
- for (const row of db.facts("wantedSubjectWritten")) {
1610
- const written = writtenAs.get(String(row[0])) ?? /* @__PURE__ */ new Set();
1611
- written.add(String(row[1]));
1612
- writtenAs.set(String(row[0]), written);
2084
+ const askSubjects = (keys) => {
2085
+ for (const key of keys) {
2086
+ db.add("wantedSubject", [key]);
2087
+ }
2088
+ (0, import_datalog4.evaluate)(db, RESOLUTION_PROGRAM.rules);
2089
+ };
2090
+ askSubjects(valueKeys);
2091
+ const placeholders = placeholderValues(db);
2092
+ const written = singleAnswers(db.facts("wantedSubjectWritten"), placeholders);
2093
+ const calls = new Set(db.facts("call").map((row) => String(row[0])));
2094
+ const throughCalls = new Set(
2095
+ [...written.values()].filter((candidate) => calls.has(candidate))
2096
+ );
2097
+ if (throughCalls.size > 0) {
2098
+ askSubjects(throughCalls);
1613
2099
  }
2100
+ const writtenThroughCall = singleAnswers(
2101
+ db.facts("wantedSubjectWritten"),
2102
+ placeholders
2103
+ );
1614
2104
  const origins = /* @__PURE__ */ new Map();
1615
2105
  for (const row of db.facts("wantedSubjectConstruction")) {
1616
2106
  const key = `${String(row[0])}|${String(row[1])}`;
@@ -1619,12 +2109,14 @@ function subjectConstructions(db, valueKeys) {
1619
2109
  origins.set(key, listed);
1620
2110
  }
1621
2111
  for (const valueKey2 of valueKeys) {
1622
- const written = writtenAs.get(valueKey2);
1623
- if (written === void 0 || written.size !== 1) {
2112
+ const direct = written.get(valueKey2);
2113
+ if (direct === void 0) {
1624
2114
  continue;
1625
2115
  }
1626
- const constructionKey = [...written][0];
1627
- const from = origins.get(`${valueKey2}|${constructionKey}`);
2116
+ const deeper = calls.has(direct) ? writtenThroughCall.get(direct) : void 0;
2117
+ const constructionKey = deeper ?? direct;
2118
+ const originOf = deeper === void 0 ? valueKey2 : direct;
2119
+ const from = origins.get(`${originOf}|${constructionKey}`);
1628
2120
  if (from !== void 0) {
1629
2121
  found.set(valueKey2, { constructionKey, origins: from });
1630
2122
  }
@@ -1820,6 +2312,9 @@ function emitExpressionFact(emitter, child) {
1820
2312
  if (WRITTEN_VALUE_TYPES.has(child.type)) {
1821
2313
  add(emitter, "writtenValue", nodeId(emitter.filePath, child));
1822
2314
  }
2315
+ if (child.type === "none") {
2316
+ add(emitter, "placeholderValue", nodeId(emitter.filePath, child));
2317
+ }
1823
2318
  }
1824
2319
  function emitExpressionFacts(emitter, node) {
1825
2320
  walkExpressions(emitter, node, (child) => {
@@ -2014,7 +2509,7 @@ function emitValueFacts(db, filePath, root) {
2014
2509
  }
2015
2510
 
2016
2511
  // src/paths/effects.ts
2017
- var import_extractor2 = require("@suss/extractor");
2512
+ var import_extractor3 = require("@suss/extractor");
2018
2513
 
2019
2514
  // src/paths/lowering.ts
2020
2515
  var EXIT_KINDS = {
@@ -2278,7 +2773,7 @@ function predicateOf(node) {
2278
2773
  }
2279
2774
 
2280
2775
  // src/paths/effects.ts
2281
- var NESTED_FUNCTION_TYPES2 = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
2776
+ var NESTED_DEFINITION_TYPES = /* @__PURE__ */ new Set(["function_definition"]);
2282
2777
  var LITERAL_ARGS = {
2283
2778
  string: (node) => ({ kind: "string", value: node.text.slice(1, -1) }),
2284
2779
  integer: (node) => {
@@ -2294,7 +2789,7 @@ var LITERAL_ARGS = {
2294
2789
  };
2295
2790
  function bodyCalls(node, found = []) {
2296
2791
  for (const child of node.namedChildren) {
2297
- if (child === null || NESTED_FUNCTION_TYPES2.has(child.type)) {
2792
+ if (child === null || NESTED_DEFINITION_TYPES.has(child.type)) {
2298
2793
  continue;
2299
2794
  }
2300
2795
  if (child.type === "call") {
@@ -2336,26 +2831,12 @@ function argsOf(call) {
2336
2831
  function calleeText(call) {
2337
2832
  return field(call, "function")?.text ?? call.text;
2338
2833
  }
2339
- function withoutChainLinks(calls) {
2340
- const isLink = /* @__PURE__ */ new Set();
2341
- for (const call of calls) {
2342
- const callee = field(call, "function");
2343
- if (callee === null || callee.type !== "attribute") {
2344
- continue;
2345
- }
2346
- const object2 = field(callee, "object");
2347
- if (object2 !== null && object2.type === "call") {
2348
- isLink.add(object2.id);
2349
- }
2350
- }
2351
- return calls.filter((call) => !isLink.has(call.id));
2352
- }
2353
2834
  function invocationEffects(definitionNode) {
2354
2835
  const body = field(definitionNode, "body");
2355
2836
  if (body === null) {
2356
2837
  return [];
2357
2838
  }
2358
- const calls = withoutChainLinks(bodyCalls(body));
2839
+ const calls = bodyCalls(body).sort((a, b) => a.endIndex - b.endIndex);
2359
2840
  if (calls.length === 0) {
2360
2841
  return [];
2361
2842
  }
@@ -2370,7 +2851,7 @@ function invocationEffects(definitionNode) {
2370
2851
  statements.push(statement);
2371
2852
  }
2372
2853
  const lowered = lowerPythonBody(body, statements);
2373
- const enumerated = (0, import_extractor2.enumerateOrDegrade)(
2854
+ const enumerated = (0, import_extractor3.enumerateOrDegrade)(
2374
2855
  {
2375
2856
  statements: lowered.statements,
2376
2857
  terminalsByStmt: lowered.terminalsByStmt
@@ -2379,7 +2860,7 @@ function invocationEffects(definitionNode) {
2379
2860
  );
2380
2861
  return calls.map((call) => {
2381
2862
  const statement = statementOf.get(call.id);
2382
- const conditions = (0, import_extractor2.sharedGatingConditions)(
2863
+ const conditions = (0, import_extractor3.sharedGatingConditions)(
2383
2864
  statement === void 0 ? void 0 : enumerated.byTerminal.get(statement),
2384
2865
  predicateOf
2385
2866
  );
@@ -3233,10 +3714,10 @@ function mergeDefs(...defs) {
3233
3714
  function cloneDef(schema) {
3234
3715
  return mergeDefs(schema._zod.def);
3235
3716
  }
3236
- function getElementAtPath(obj, path4) {
3237
- if (!path4)
3717
+ function getElementAtPath(obj, path6) {
3718
+ if (!path6)
3238
3719
  return obj;
3239
- return path4.reduce((acc, key) => acc?.[key], obj);
3720
+ return path6.reduce((acc, key) => acc?.[key], obj);
3240
3721
  }
3241
3722
  function promiseAllObject(promisesObj) {
3242
3723
  const keys = Object.keys(promisesObj);
@@ -3645,11 +4126,11 @@ function explicitlyAborted(x, startIndex = 0) {
3645
4126
  }
3646
4127
  return false;
3647
4128
  }
3648
- function prefixIssues(path4, issues) {
4129
+ function prefixIssues(path6, issues) {
3649
4130
  return issues.map((iss) => {
3650
4131
  var _a3;
3651
4132
  (_a3 = iss).path ?? (_a3.path = []);
3652
- iss.path.unshift(path4);
4133
+ iss.path.unshift(path6);
3653
4134
  return iss;
3654
4135
  });
3655
4136
  }
@@ -3796,16 +4277,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
3796
4277
  }
3797
4278
  function formatError(error51, mapper = (issue2) => issue2.message) {
3798
4279
  const fieldErrors = { _errors: [] };
3799
- const processError = (error52, path4 = []) => {
4280
+ const processError = (error52, path6 = []) => {
3800
4281
  for (const issue2 of error52.issues) {
3801
4282
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3802
- issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
4283
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
3803
4284
  } else if (issue2.code === "invalid_key") {
3804
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
4285
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
3805
4286
  } else if (issue2.code === "invalid_element") {
3806
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
4287
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
3807
4288
  } else {
3808
- const fullpath = [...path4, ...issue2.path];
4289
+ const fullpath = [...path6, ...issue2.path];
3809
4290
  if (fullpath.length === 0) {
3810
4291
  fieldErrors._errors.push(mapper(issue2));
3811
4292
  } else {
@@ -3832,17 +4313,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
3832
4313
  }
3833
4314
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
3834
4315
  const result = { errors: [] };
3835
- const processError = (error52, path4 = []) => {
4316
+ const processError = (error52, path6 = []) => {
3836
4317
  var _a3, _b;
3837
4318
  for (const issue2 of error52.issues) {
3838
4319
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3839
- issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
4320
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
3840
4321
  } else if (issue2.code === "invalid_key") {
3841
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
4322
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
3842
4323
  } else if (issue2.code === "invalid_element") {
3843
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
4324
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
3844
4325
  } else {
3845
- const fullpath = [...path4, ...issue2.path];
4326
+ const fullpath = [...path6, ...issue2.path];
3846
4327
  if (fullpath.length === 0) {
3847
4328
  result.errors.push(mapper(issue2));
3848
4329
  continue;
@@ -3874,8 +4355,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
3874
4355
  }
3875
4356
  function toDotPath(_path) {
3876
4357
  const segs = [];
3877
- const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3878
- for (const seg of path4) {
4358
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4359
+ for (const seg of path6) {
3879
4360
  if (typeof seg === "number")
3880
4361
  segs.push(`[${seg}]`);
3881
4362
  else if (typeof seg === "symbol")
@@ -16567,13 +17048,13 @@ function resolveRef(ref, ctx) {
16567
17048
  if (!ref.startsWith("#")) {
16568
17049
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
16569
17050
  }
16570
- const path4 = ref.slice(1).split("/").filter(Boolean);
16571
- if (path4.length === 0) {
17051
+ const path6 = ref.slice(1).split("/").filter(Boolean);
17052
+ if (path6.length === 0) {
16572
17053
  return ctx.rootSchema;
16573
17054
  }
16574
17055
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
16575
- if (path4[0] === defsKey) {
16576
- const key = path4[1];
17056
+ if (path6[0] === defsKey) {
17057
+ const key = path6[1];
16577
17058
  if (!key || !ctx.defs[key]) {
16578
17059
  throw new Error(`Reference not found: ${ref}`);
16579
17060
  }
@@ -17333,8 +17814,8 @@ function baseUrlReference(semantics) {
17333
17814
  const label = OPENING_HOLE.exec(semantics.path ?? "")?.[1];
17334
17815
  return label === void 0 ? null : referenceFromName(patternHole(label));
17335
17816
  }
17336
- function normalizePath(path4) {
17337
- let normalized = path4.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
17817
+ function normalizePath(path6) {
17818
+ let normalized = path6.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
17338
17819
  if (normalized.length > 1 && normalized.endsWith("/")) {
17339
17820
  normalized = normalized.slice(0, -1);
17340
17821
  }
@@ -17344,8 +17825,8 @@ function normalizePath(path4) {
17344
17825
  );
17345
17826
  return normalized;
17346
17827
  }
17347
- function pathShape(path4) {
17348
- return normalizePath(path4).replace(/\{[^}]*\}/g, "{}");
17828
+ function pathShape(path6) {
17829
+ return normalizePath(path6).replace(/\{[^}]*\}/g, "{}");
17349
17830
  }
17350
17831
  function methodsAgree(a, b) {
17351
17832
  if (a === null || b === null) {
@@ -17423,8 +17904,8 @@ var restSemantics = defineBoundarySemantics({
17423
17904
  return null;
17424
17905
  }
17425
17906
  const method = semantics.method === null ? "ANY" : semantics.method.toUpperCase();
17426
- const path4 = semantics.path === null ? "?" : normalizePath(semantics.path);
17427
- return `${method} ${path4}`;
17907
+ const path6 = semantics.path === null ? "?" : normalizePath(semantics.path);
17908
+ return `${method} ${path6}`;
17428
17909
  },
17429
17910
  /**
17430
17911
  * A route with both halves stated decides for itself: the method
@@ -17433,14 +17914,14 @@ var restSemantics = defineBoundarySemantics({
17433
17914
  * half might still be the one that handles the request, and nothing
17434
17915
  * here can settle that, so it abstains instead of refusing.
17435
17916
  */
17436
- servesRequest(semantics, method, path4) {
17917
+ servesRequest(semantics, method, path6) {
17437
17918
  if (semantics.method === null || semantics.path === null) {
17438
17919
  return "unknown";
17439
17920
  }
17440
17921
  if (!methodsAgree(semantics.method, method.toUpperCase())) {
17441
17922
  return "nomatch";
17442
17923
  }
17443
- return routePathAdmits(semantics.path, path4) ? "match" : "nomatch";
17924
+ return routePathAdmits(semantics.path, path6) ? "match" : "nomatch";
17444
17925
  },
17445
17926
  /**
17446
17927
  * A route the registration pattern admits is inside it. A route
@@ -18077,71 +18558,10 @@ function returnTypeName(node) {
18077
18558
  }
18078
18559
  return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
18079
18560
  }
18080
- function callsIn(node, found = []) {
18081
- for (const child of node.namedChildren) {
18082
- if (child === null) {
18083
- continue;
18084
- }
18085
- if (child.type === "call") {
18086
- found.push(child);
18087
- }
18088
- callsIn(child, found);
18089
- }
18090
- return found;
18091
- }
18092
- function storageEffects(calls, options) {
18093
- const seen = /* @__PURE__ */ new Set();
18094
- const asked = new Set(
18095
- options.facts.facts("wanted").map((row) => String(row[0]))
18096
- );
18097
- const gather = (inCalls, filePath) => {
18098
- const found = [...directStorageEffects(inCalls, { ...options, filePath })];
18099
- resolveCalls(
18100
- options.facts,
18101
- inCalls.filter((call) => options.leadsToStorage.has(methodName(call))).map((call) => field(call, "function")).filter((callee) => callee !== null).map((callee) => calleeKeyOf(filePath, callee)).filter((key) => {
18102
- const fresh = !asked.has(key);
18103
- asked.add(key);
18104
- return fresh;
18105
- })
18106
- );
18107
- for (const call of inCalls) {
18108
- const callee = field(call, "function");
18109
- if (callee === null || !options.leadsToStorage.has(methodName(call))) {
18110
- continue;
18111
- }
18112
- const key = resolvedOnce(options, calleeKeyOf(filePath, callee));
18113
- const body = key === void 0 ? void 0 : options.definitionAt(key);
18114
- if (key === void 0 || body === void 0 || seen.has(key)) {
18115
- continue;
18116
- }
18117
- seen.add(key);
18118
- const name = field(body, "name")?.text;
18119
- const origin = {
18120
- file: fileOf(key),
18121
- line: body.startPosition.row + 1,
18122
- ...name === void 0 ? {} : { function: name }
18123
- };
18124
- found.push(
18125
- ...gather(callsIn(body), fileOf(key)).map(
18126
- (effect) => effect.type === "interaction" && effect.origin === void 0 ? { ...effect, origin } : effect
18127
- )
18128
- );
18129
- }
18130
- return found;
18131
- };
18132
- return gather(calls, options.filePath);
18133
- }
18134
- function calleeKeyOf(filePath, callee) {
18135
- return callee.type === "identifier" ? `${filePath}#${callee.text}` : nodeId(filePath, callee);
18136
- }
18137
- function resolvedOnce(options, calleeKey) {
18138
- const resolved = options.facts.facts("wantedResolves").filter((row) => String(row[0]) === calleeKey).map((row) => String(row[1]));
18139
- return resolved.length === 1 ? resolved[0] : void 0;
18140
- }
18141
18561
  function storageCallees(calls, filePath, couldMatch) {
18142
18562
  return chainsIn(calls).filter((chain) => couldMatch.has(methodName(chain.root))).map((chain) => field(chain.root, "function")).filter((callee) => callee !== null).map((callee) => nodeId(filePath, callee));
18143
18563
  }
18144
- function directStorageEffects(calls, options) {
18564
+ function storageEffects(calls, options) {
18145
18565
  if (options.patterns.length === 0) {
18146
18566
  return [];
18147
18567
  }
@@ -18199,7 +18619,7 @@ function directStorageEffects(calls, options) {
18199
18619
  // src/discovery.ts
18200
18620
  function decoratedStatements(root, module2) {
18201
18621
  const found = [];
18202
- (0, import_extractor3.walkDescendants)(
18622
+ (0, import_extractor4.walkDescendants)(
18203
18623
  root,
18204
18624
  { scope: module2.moduleScope, enclosingFunction: null },
18205
18625
  {
@@ -18238,21 +18658,21 @@ function builtSubjects(decorated, options) {
18238
18658
  }
18239
18659
  const factsPath = options.absoluteFile ?? options.filePath;
18240
18660
  const asked = /* @__PURE__ */ new Set();
18241
- for (const { enclosingFunction, decorators } of decorated) {
18661
+ for (const { enclosingFunction: enclosingFunction2, decorators } of decorated) {
18242
18662
  for (const { node, direct } of decorators) {
18243
18663
  if (direct.module !== null && acceptedByAnyPattern(direct.module, options)) {
18244
18664
  continue;
18245
18665
  }
18246
18666
  const written = decoratorReceiver(node);
18247
18667
  if (written !== null) {
18248
- asked.add(readKey(factsPath, written.object, enclosingFunction));
18668
+ asked.add(readKey(factsPath, written.object, enclosingFunction2));
18249
18669
  }
18250
18670
  }
18251
18671
  }
18252
18672
  return subjectConstructions(facts, [...asked]);
18253
18673
  }
18254
18674
  function decoratedUnits(decoratedStatement, module2, options, subjects) {
18255
- const { stmt, scope, enclosingFunction, decorators } = decoratedStatement;
18675
+ const { stmt, scope, enclosingFunction: enclosingFunction2, decorators } = decoratedStatement;
18256
18676
  const units = [];
18257
18677
  const { definition } = stripDecorators(stmt);
18258
18678
  for (const { node: decoratorNode, direct } of decorators) {
@@ -18268,7 +18688,7 @@ function decoratedUnits(decoratedStatement, module2, options, subjects) {
18268
18688
  ) ?? null
18269
18689
  ) ?? builtSubjectClassification(
18270
18690
  decoratorNode,
18271
- enclosingFunction,
18691
+ enclosingFunction2,
18272
18692
  subjects,
18273
18693
  options
18274
18694
  )
@@ -18304,14 +18724,14 @@ function acceptedByAnyPattern(module2, options) {
18304
18724
  (pack) => pack.discovery.some((pattern) => pattern.importModule.includes(module2))
18305
18725
  );
18306
18726
  }
18307
- function builtSubjectClassification(decoratorNode, enclosingFunction, subjects, options) {
18727
+ function builtSubjectClassification(decoratorNode, enclosingFunction2, subjects, options) {
18308
18728
  const written = decoratorReceiver(decoratorNode);
18309
18729
  if (written === null) {
18310
18730
  return null;
18311
18731
  }
18312
18732
  const factsPath = options.absoluteFile ?? options.filePath;
18313
18733
  const built = subjects.get(
18314
- readKey(factsPath, written.object, enclosingFunction)
18734
+ readKey(factsPath, written.object, enclosingFunction2)
18315
18735
  );
18316
18736
  const origin = built?.origins.find(
18317
18737
  (candidate) => acceptedByAnyPattern(candidate.module, options)
@@ -18370,23 +18790,23 @@ function unitsFor(pattern, pack, decoratorModule, classification, definition, mo
18370
18790
  );
18371
18791
  }
18372
18792
  };
18373
- return (0, import_behavioral_ir.dispatchByType)(table, pattern);
18793
+ return (0, import_behavioral_ir2.dispatchByType)(table, pattern);
18374
18794
  }
18375
18795
  function readPathArgument(classification) {
18376
18796
  const first = classification.args[0];
18377
18797
  if (first?.kind === "string") {
18378
- return (0, import_extractor3.writtenReading)(first.value, classification.range);
18798
+ return (0, import_extractor4.writtenReading)(first.value, classification.range);
18379
18799
  }
18380
- return (0, import_extractor3.unreadableReading)(
18800
+ return (0, import_extractor4.unreadableReading)(
18381
18801
  "The path in this route's decorator is not a string literal, so the binding names no path and nothing pairs with it",
18382
18802
  classification.range
18383
18803
  );
18384
18804
  }
18385
18805
  var NO_PATH_PARAMS = /* @__PURE__ */ new Set();
18386
18806
  function templateReader(template) {
18387
- return (path4) => {
18807
+ return (path6) => {
18388
18808
  const names = [];
18389
- const canonical = path4.replace(template, (_match, name) => {
18809
+ const canonical = path6.replace(template, (_match, name) => {
18390
18810
  names.push(name);
18391
18811
  return `{${name}}`;
18392
18812
  });
@@ -18399,19 +18819,19 @@ var PATH_TEMPLATE_READERS = {
18399
18819
  /<(?:[A-Za-z_]\w*(?:\(.*?\))?:)?([A-Za-z_]\w*)>/g
18400
18820
  )
18401
18821
  };
18402
- function readPathTemplate(pattern, path4, range) {
18822
+ function readPathTemplate(pattern, path6, range) {
18403
18823
  const syntax = pattern.pathParamSyntax;
18404
18824
  if (syntax === void 0) {
18405
- return (0, import_extractor3.writtenReading)({ path: path4, paramNames: NO_PATH_PARAMS }, range);
18825
+ return (0, import_extractor4.writtenReading)({ path: path6, paramNames: NO_PATH_PARAMS }, range);
18406
18826
  }
18407
18827
  const reader = PATH_TEMPLATE_READERS[syntax];
18408
18828
  if (reader === void 0) {
18409
- return (0, import_extractor3.unreadableReading)(
18829
+ return (0, import_extractor4.unreadableReading)(
18410
18830
  `The route's pack declares path-parameter syntax "${syntax}", which this adapter has no reader for, so the binding names no path and nothing pairs with it`,
18411
18831
  range
18412
18832
  );
18413
18833
  }
18414
- return (0, import_extractor3.writtenReading)(reader(path4), range);
18834
+ return (0, import_extractor4.writtenReading)(reader(path6), range);
18415
18835
  }
18416
18836
  function classRouteUnits(pattern, pack, classification, classNode, module2, options) {
18417
18837
  const pathArgument = readPathArgument(classification);
@@ -18460,7 +18880,7 @@ function classRouteUnits(pattern, pack, classification, classNode, module2, opti
18460
18880
  module: module2,
18461
18881
  filePath: options.filePath,
18462
18882
  skipReceiverParam: true,
18463
- responseShape: import_extractor3.absentReading,
18883
+ responseShape: import_extractor4.absentReading,
18464
18884
  statusCode: defaultedStatus(
18465
18885
  readReturnedStatus(pattern, maybeMethod),
18466
18886
  pattern
@@ -18475,13 +18895,13 @@ function classRouteUnits(pattern, pack, classification, classNode, module2, opti
18475
18895
  return units;
18476
18896
  }
18477
18897
  function readRoutePath(pattern, pathArgument, classification, module2, options) {
18478
- const composed = (0, import_extractor3.andThenReading)(
18898
+ const composed = (0, import_extractor4.andThenReading)(
18479
18899
  pathArgument,
18480
18900
  (literal2, range) => composeRoutePath(pattern, classification, module2, options, literal2, range)
18481
18901
  );
18482
- return (0, import_extractor3.andThenReading)(
18902
+ return (0, import_extractor4.andThenReading)(
18483
18903
  composed,
18484
- (path4, range) => readPathTemplate(pattern, path4, range)
18904
+ (path6, range) => readPathTemplate(pattern, path6, range)
18485
18905
  );
18486
18906
  }
18487
18907
  function readRoutePaths(pattern, pathArgument, classification, module2, options) {
@@ -18505,12 +18925,12 @@ function readRoutePaths(pattern, pathArgument, classification, module2, options)
18505
18925
  ];
18506
18926
  }
18507
18927
  return resolution.values.map((prefix) => ({
18508
- routePath: (0, import_extractor3.andThenReading)(
18509
- (0, import_extractor3.andThenReading)(
18928
+ routePath: (0, import_extractor4.andThenReading)(
18929
+ (0, import_extractor4.andThenReading)(
18510
18930
  pathArgument,
18511
- (literal2, range) => (0, import_extractor3.writtenReading)(servedSpelling(pattern, prefix + literal2), range)
18931
+ (literal2, range) => (0, import_extractor4.writtenReading)(servedSpelling(pattern, prefix + literal2), range)
18512
18932
  ),
18513
- (path4, range) => readPathTemplate(pattern, path4, range)
18933
+ (path6, range) => readPathTemplate(pattern, path6, range)
18514
18934
  ),
18515
18935
  mount: { siblings: resolution.values.length, prefix }
18516
18936
  }));
@@ -18544,19 +18964,19 @@ function composeRoutePath(pattern, classification, module2, options, literal2, r
18544
18964
  range
18545
18965
  );
18546
18966
  if (prefix.kind === "absent") {
18547
- return (0, import_extractor3.writtenReading)(servedSpelling(pattern, literal2), range);
18967
+ return (0, import_extractor4.writtenReading)(servedSpelling(pattern, literal2), range);
18548
18968
  }
18549
- return (0, import_extractor3.mapReading)(
18969
+ return (0, import_extractor4.mapReading)(
18550
18970
  prefix,
18551
18971
  (value) => servedSpelling(pattern, value + literal2)
18552
18972
  );
18553
18973
  }
18554
18974
  var REPEATED_SLASH_READERS = {
18555
- kept: (path4) => path4,
18556
- merged: (path4) => path4.replace(/\/{2,}/g, "/")
18975
+ kept: (path6) => path6,
18976
+ merged: (path6) => path6.replace(/\/{2,}/g, "/")
18557
18977
  };
18558
- function servedSpelling(pattern, path4) {
18559
- return REPEATED_SLASH_READERS[pattern.pathRepeatedSlashes ?? "kept"](path4);
18978
+ function servedSpelling(pattern, path6) {
18979
+ return REPEATED_SLASH_READERS[pattern.pathRepeatedSlashes ?? "kept"](path6);
18560
18980
  }
18561
18981
  function readRouterPrefix(pattern, classification, module2, options, range) {
18562
18982
  const resolution = routerResolutionOf(
@@ -18566,31 +18986,31 @@ function readRouterPrefix(pattern, classification, module2, options, range) {
18566
18986
  options
18567
18987
  );
18568
18988
  if (resolution === null) {
18569
- return import_extractor3.absentReading;
18989
+ return import_extractor4.absentReading;
18570
18990
  }
18571
18991
  if (resolution.kind === "abstain") {
18572
- return (0, import_extractor3.unreadableReading)(
18992
+ return (0, import_extractor4.unreadableReading)(
18573
18993
  `The router this route is declared on ${resolution.reason}, so the binding names no path and nothing pairs with it`,
18574
18994
  range
18575
18995
  );
18576
18996
  }
18577
18997
  if (resolution.kind === "composed") {
18578
- return (0, import_extractor3.writtenReading)(resolution.value, range);
18998
+ return (0, import_extractor4.writtenReading)(resolution.value, range);
18579
18999
  }
18580
- return import_extractor3.absentReading;
19000
+ return import_extractor4.absentReading;
18581
19001
  }
18582
19002
  function readStatusCode(pattern, classification) {
18583
19003
  if (pattern.statusCodeKeyword === void 0) {
18584
- return import_extractor3.absentReading;
19004
+ return import_extractor4.absentReading;
18585
19005
  }
18586
19006
  const arg = classification.keywordArgs[pattern.statusCodeKeyword];
18587
19007
  if (arg === void 0) {
18588
- return import_extractor3.absentReading;
19008
+ return import_extractor4.absentReading;
18589
19009
  }
18590
19010
  if (arg.kind === "number") {
18591
- return (0, import_extractor3.writtenReading)(arg.value, classification.range);
19011
+ return (0, import_extractor4.writtenReading)(arg.value, classification.range);
18592
19012
  }
18593
- return (0, import_extractor3.unreadableReading)(
19013
+ return (0, import_extractor4.unreadableReading)(
18594
19014
  "The status this route's decorator states is not a literal number, so the response claims no status",
18595
19015
  classification.range
18596
19016
  );
@@ -18635,7 +19055,7 @@ function statusOfReturn(statement) {
18635
19055
  }
18636
19056
  function effectsReaching(effects, conditions) {
18637
19057
  return effects.filter(
18638
- (effect) => (0, import_extractor3.guardsHoldOn)(effect.preconditions, conditions)
19058
+ (effect) => (0, import_extractor4.guardsHoldOn)(effect.preconditions, conditions)
18639
19059
  );
18640
19060
  }
18641
19061
  function branchesFromReturns(readsReturnedStatus, definitionNode, effects, responseShape, declaredStatus) {
@@ -18648,7 +19068,7 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
18648
19068
  return null;
18649
19069
  }
18650
19070
  const lowered = lowerPythonBody(body, returns);
18651
- const enumerated = (0, import_extractor3.enumerateOrDegrade)(
19071
+ const enumerated = (0, import_extractor4.enumerateOrDegrade)(
18652
19072
  {
18653
19073
  statements: lowered.statements,
18654
19074
  terminalsByStmt: lowered.terminalsByStmt
@@ -18664,12 +19084,12 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
18664
19084
  const read = statusOfReturn(statement);
18665
19085
  const range = rangeOf(statement);
18666
19086
  const writtenBody = returnedBodyShape(statement);
18667
- const statusReading = read.kind === "status" ? (0, import_extractor3.writtenReading)(read.value, range) : read.kind === "none" ? import_extractor3.absentReading : (0, import_extractor3.unreadableReading)(
19087
+ const statusReading = read.kind === "status" ? (0, import_extractor4.writtenReading)(read.value, range) : read.kind === "none" ? import_extractor4.absentReading : (0, import_extractor4.unreadableReading)(
18668
19088
  "This return gives a status this reading cannot resolve to a number, so this outcome claims none",
18669
19089
  range
18670
19090
  );
18671
- for (const path4 of paths) {
18672
- const conditions = path4.map((condition) => ({
19091
+ for (const path6 of paths) {
19092
+ const conditions = path6.map((condition) => ({
18673
19093
  sourceText: condition.sourceText,
18674
19094
  structured: condition.expression === null ? null : predicateOf(condition.expression),
18675
19095
  polarity: condition.polarity,
@@ -18694,11 +19114,11 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
18694
19114
  ...declaredStatus.libraryDefault !== void 0 ? { libraryDefault: declaredStatus.libraryDefault } : {}
18695
19115
  },
18696
19116
  bodyShapeReading: {
18697
- reading: writtenBody === null ? responseShape.reading : (0, import_extractor3.writtenReading)(writtenBody, range)
19117
+ reading: writtenBody === null ? responseShape.reading : (0, import_extractor4.writtenReading)(writtenBody, range)
18698
19118
  },
18699
19119
  effects: effectsReaching(effects, conditions),
18700
19120
  location: range,
18701
- isDefault: path4.length === 0
19121
+ isDefault: path6.length === 0
18702
19122
  });
18703
19123
  }
18704
19124
  }
@@ -18706,7 +19126,7 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
18706
19126
  }
18707
19127
  function readReturnedStatus(pattern, definitionNode) {
18708
19128
  if (pattern.statusFromReturnedTuple !== true) {
18709
- return import_extractor3.absentReading;
19129
+ return import_extractor4.absentReading;
18710
19130
  }
18711
19131
  const range = rangeOf(definitionNode);
18712
19132
  const statuses = /* @__PURE__ */ new Set();
@@ -18723,23 +19143,23 @@ function readReturnedStatus(pattern, definitionNode) {
18723
19143
  }
18724
19144
  }
18725
19145
  if (unreadable > 0) {
18726
- return (0, import_extractor3.unreadableReading)(
19146
+ return (0, import_extractor4.unreadableReading)(
18727
19147
  "This route returns a status this reading cannot resolve to a number, so no status is claimed for it",
18728
19148
  range
18729
19149
  );
18730
19150
  }
18731
19151
  if (statuses.size === 0) {
18732
- return import_extractor3.absentReading;
19152
+ return import_extractor4.absentReading;
18733
19153
  }
18734
19154
  const candidates = [...statuses];
18735
19155
  if (statuses.size > 1 || plainReturns > 0) {
18736
- return (0, import_extractor3.ambiguousReading)(
19156
+ return (0, import_extractor4.ambiguousReading)(
18737
19157
  candidates,
18738
19158
  "This route returns more than one status depending on which branch runs, and nothing here reads which, so no status is claimed for it",
18739
19159
  range
18740
19160
  );
18741
19161
  }
18742
- return (0, import_extractor3.writtenReading)(candidates[0], range);
19162
+ return (0, import_extractor4.writtenReading)(candidates[0], range);
18743
19163
  }
18744
19164
  function declaredOrReturnedStatus(declared, returned) {
18745
19165
  return declared.kind === "absent" ? returned : declared;
@@ -18752,19 +19172,19 @@ function defaultedStatus(reading, pattern) {
18752
19172
  }
18753
19173
  function readResponseModel(pattern, classification, module2, ctx) {
18754
19174
  if (pattern.responseModelKeyword === void 0) {
18755
- return import_extractor3.absentReading;
19175
+ return import_extractor4.absentReading;
18756
19176
  }
18757
19177
  const arg = classification.keywordArgs[pattern.responseModelKeyword];
18758
19178
  if (arg === void 0) {
18759
- return import_extractor3.absentReading;
19179
+ return import_extractor4.absentReading;
18760
19180
  }
18761
19181
  if (arg.kind === "identifier") {
18762
- return (0, import_extractor3.writtenReading)(
19182
+ return (0, import_extractor4.writtenReading)(
18763
19183
  shapeFromName(arg.name, module2.moduleScope, ctx),
18764
19184
  classification.range
18765
19185
  );
18766
19186
  }
18767
- return (0, import_extractor3.unreadableReading)(
19187
+ return (0, import_extractor4.unreadableReading)(
18768
19188
  "The response model this route's decorator states is not a name, so the response claims no body shape",
18769
19189
  classification.range
18770
19190
  );
@@ -18772,9 +19192,9 @@ function readResponseModel(pattern, classification, module2, ctx) {
18772
19192
  function readReturnAnnotation(definitionNode, scope, ctx) {
18773
19193
  const returnTypeNode = field(definitionNode, "return_type");
18774
19194
  if (returnTypeNode === null) {
18775
- return import_extractor3.absentReading;
19195
+ return import_extractor4.absentReading;
18776
19196
  }
18777
- return (0, import_extractor3.writtenReading)(
19197
+ return (0, import_extractor4.writtenReading)(
18778
19198
  annotationToShape(returnTypeNode, scope, ctx),
18779
19199
  rangeOf(returnTypeNode)
18780
19200
  );
@@ -18827,7 +19247,7 @@ function readResponseShape(declared, readAnnotation) {
18827
19247
  if (declared.kind === "written") {
18828
19248
  return { reading: declared, passedOver: [] };
18829
19249
  }
18830
- return (0, import_extractor3.firstWrittenReading)([declared, readAnnotation()]);
19250
+ return (0, import_extractor4.firstWrittenReading)([declared, readAnnotation()]);
18831
19251
  }
18832
19252
  function routeUnitOrAbstention(unitOptions, options) {
18833
19253
  try {
@@ -18855,7 +19275,7 @@ function unbuiltRouteUnit(unitOptions, error51) {
18855
19275
  exportName: unitOptions.exportPath[0] ?? unitOptions.name,
18856
19276
  exportPath: unitOptions.exportPath
18857
19277
  },
18858
- boundaryBinding: (0, import_behavioral_ir.restBinding)({
19278
+ boundaryBinding: (0, import_behavioral_ir2.restBinding)({
18859
19279
  transport: unitOptions.pack.protocol,
18860
19280
  method: nullIfEmpty(unitOptions.method),
18861
19281
  path: null,
@@ -18867,7 +19287,7 @@ function unbuiltRouteUnit(unitOptions, error51) {
18867
19287
  dependencyCalls: [],
18868
19288
  declaredContract: null,
18869
19289
  readings: [
18870
- (0, import_extractor3.unreadableReading)(
19290
+ (0, import_extractor4.unreadableReading)(
18871
19291
  `This route could not be read into a unit (${message}), so the binding names no path and nothing pairs with it`,
18872
19292
  range
18873
19293
  )
@@ -18893,7 +19313,7 @@ function buildRouteUnit(options) {
18893
19313
  definitionsCtx,
18894
19314
  storage: storageLookup
18895
19315
  } = options;
18896
- const template = (0, import_extractor3.valueToReadFurtherFrom)(routePath);
19316
+ const template = (0, import_extractor4.valueToReadFurtherFrom)(routePath);
18897
19317
  const ctx = definitionsCtx ?? createAnnotationContext(module2.scopeFor);
18898
19318
  const parameters = readParameters(
18899
19319
  definitionNode,
@@ -18909,17 +19329,7 @@ function buildRouteUnit(options) {
18909
19329
  () => readReturnAnnotation(definitionNode, enclosingScope, ctx)
18910
19330
  );
18911
19331
  const effects = invocationEffects(definitionNode);
18912
- const storage = storageLookup === void 0 ? [] : [
18913
- ...storageEffects(bodyCalls(definitionNode), {
18914
- ...storageLookup,
18915
- filePath: storageLookup.factsPath
18916
- }),
18917
- ...rawSqlEffects(bodyCalls(definitionNode), {
18918
- facts: storageLookup.facts,
18919
- filePath: storageLookup.factsPath,
18920
- patterns: storageLookup.rawSql ?? []
18921
- })
18922
- ];
19332
+ const extra = recognizedBodyEffects(definitionNode, module2, storageLookup);
18923
19333
  const perReturn = branchesFromReturns(
18924
19334
  readsReturnedStatus,
18925
19335
  definitionNode,
@@ -18928,9 +19338,9 @@ function buildRouteUnit(options) {
18928
19338
  statusCode
18929
19339
  );
18930
19340
  const branches = (perReturn ?? []).map(
18931
- (branch) => storage.length === 0 ? branch : { ...branch, extraEffects: storage }
19341
+ (branch) => extra.length === 0 ? branch : { ...branch, extraEffects: extra }
18932
19342
  );
18933
- if (perReturn === null && (responseShape.reading.kind !== "absent" || statusCode.reading.kind !== "absent" || storage.length > 0 || effects.length > 0)) {
19343
+ if (perReturn === null && (responseShape.reading.kind !== "absent" || statusCode.reading.kind !== "absent" || extra.length > 0 || effects.length > 0)) {
18934
19344
  branches.push({
18935
19345
  conditions: [],
18936
19346
  terminal: {
@@ -18950,7 +19360,7 @@ function buildRouteUnit(options) {
18950
19360
  statusCodeReading: statusCode,
18951
19361
  bodyShapeReading: { reading: responseShape.reading },
18952
19362
  effects,
18953
- ...storage.length === 0 ? {} : { extraEffects: storage },
19363
+ ...extra.length === 0 ? {} : { extraEffects: extra },
18954
19364
  location: rangeOf(definitionNode),
18955
19365
  isDefault: true
18956
19366
  });
@@ -18967,7 +19377,7 @@ function buildRouteUnit(options) {
18967
19377
  exportName: exportPath[0] ?? name,
18968
19378
  exportPath
18969
19379
  },
18970
- boundaryBinding: (0, import_behavioral_ir.restBinding)({
19380
+ boundaryBinding: (0, import_behavioral_ir2.restBinding)({
18971
19381
  transport: pack.protocol,
18972
19382
  method,
18973
19383
  path: template?.path ?? null,
@@ -18992,12 +19402,26 @@ function unreadRoleReadings(parameters, range) {
18992
19402
  return [];
18993
19403
  }
18994
19404
  return [
18995
- (0, import_extractor3.unreadableReading)(
19405
+ (0, import_extractor4.unreadableReading)(
18996
19406
  "Nothing read this route's path, so its parameters name no role and a path parameter here does not read as one",
18997
19407
  range
18998
19408
  )
18999
19409
  ];
19000
19410
  }
19411
+ function recognizedBodyEffects(definitionNode, module2, storageLookup) {
19412
+ const storage = storageLookup === void 0 ? [] : [
19413
+ ...storageEffects(bodyCalls(definitionNode), {
19414
+ ...storageLookup,
19415
+ filePath: storageLookup.factsPath
19416
+ }),
19417
+ ...rawSqlEffects(bodyCalls(definitionNode), {
19418
+ facts: storageLookup.facts,
19419
+ filePath: storageLookup.factsPath,
19420
+ patterns: storageLookup.rawSql ?? []
19421
+ })
19422
+ ];
19423
+ return [...envReadEffects(definitionNode, module2), ...storage];
19424
+ }
19001
19425
  function bodyContentOf(bodyNode) {
19002
19426
  for (const stmt of bodyStatements(bodyNode)) {
19003
19427
  if (stmt.type === "pass_statement") {
@@ -19133,6 +19557,11 @@ function parameterNameAndType(param) {
19133
19557
  return null;
19134
19558
  }
19135
19559
 
19560
+ // src/facts/why.ts
19561
+ var import_node_fs4 = __toESM(require("fs"), 1);
19562
+ var import_node_path4 = __toESM(require("path"), 1);
19563
+ var import_datalog7 = require("@suss/datalog");
19564
+
19136
19565
  // src/moduleResolver.ts
19137
19566
  var import_node_fs = __toESM(require("fs"), 1);
19138
19567
  var import_node_path = __toESM(require("path"), 1);
@@ -19225,43 +19654,38 @@ function importedModuleText(module2, relativeLevel) {
19225
19654
  return relativeLevel > 0 ? `${".".repeat(relativeLevel)}${module2}` : module2;
19226
19655
  }
19227
19656
  function emitModuleImportFacts(db, filePath, module2, resolverOptions) {
19228
- const everyBinding = [...module2.scopeFor.values()].flatMap((scope) => [
19229
- ...scope.bindings
19230
- ]);
19231
- for (const [localName, binding] of everyBinding) {
19232
- if (binding.kind !== "import" && binding.kind !== "importFrom") {
19233
- continue;
19234
- }
19235
- const moduleText = importedModuleText(
19236
- binding.module,
19237
- binding.relativeLevel
19238
- );
19239
- const resolution = resolveModule(
19240
- filePath,
19241
- { module: binding.module, relativeLevel: binding.relativeLevel },
19242
- resolverOptions
19243
- );
19244
- db.add("pyImport", [
19245
- filePath,
19246
- moduleText,
19247
- resolution.status === "resolved" ? "resolved" : resolution.reason
19248
- ]);
19249
- const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
19250
- db.add("pyImportedName", [
19251
- `${filePath}#${localName}`,
19252
- moduleText,
19253
- importedName
19254
- ]);
19255
- if (resolution.status === "resolved") {
19256
- db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
19257
- db.add("imports", [
19258
- `${filePath}#${localName}`,
19259
- resolution.file,
19260
- importedName
19657
+ for (const scope of module2.scopeFor.values()) {
19658
+ for (const [localName, binding] of scope.bindings) {
19659
+ if (binding.kind !== "import" && binding.kind !== "importFrom") {
19660
+ continue;
19661
+ }
19662
+ const nameKey = `${filePath}#${localName}`;
19663
+ const moduleText = importedModuleText(
19664
+ binding.module,
19665
+ binding.relativeLevel
19666
+ );
19667
+ const resolution = resolveModule(
19668
+ filePath,
19669
+ { module: binding.module, relativeLevel: binding.relativeLevel },
19670
+ resolverOptions
19671
+ );
19672
+ db.add("pyImport", [
19673
+ filePath,
19674
+ moduleText,
19675
+ resolution.status === "resolved" ? "resolved" : resolution.reason
19261
19676
  ]);
19262
- continue;
19677
+ const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
19678
+ db.add("pyImportedName", [nameKey, moduleText, importedName]);
19679
+ if (scope.kind === "module") {
19680
+ db.add("exportsAs", [filePath, localName, nameKey]);
19681
+ }
19682
+ if (resolution.status === "resolved") {
19683
+ db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
19684
+ db.add("imports", [nameKey, resolution.file, importedName]);
19685
+ continue;
19686
+ }
19687
+ db.add("imports", [nameKey, moduleText, importedName]);
19263
19688
  }
19264
- db.add("imports", [`${filePath}#${localName}`, moduleText, importedName]);
19265
19689
  }
19266
19690
  for (const openModule of module2.openImports) {
19267
19691
  db.add("pyOpenImport", [filePath, openModule]);
@@ -19280,6 +19704,7 @@ var GRAMMAR_PATH = import_node_path2.default.join(
19280
19704
  "tree-sitter-python.wasm"
19281
19705
  );
19282
19706
  var languagePromise = null;
19707
+ var loadedLanguage = null;
19283
19708
  function loadLanguage() {
19284
19709
  languagePromise ??= (async () => {
19285
19710
  if (!import_node_fs2.default.existsSync(GRAMMAR_PATH)) {
@@ -19288,12 +19713,28 @@ function loadLanguage() {
19288
19713
  );
19289
19714
  }
19290
19715
  await import_web_tree_sitter.Parser.init();
19291
- return import_web_tree_sitter.Language.load(GRAMMAR_PATH);
19716
+ const language = await import_web_tree_sitter.Language.load(GRAMMAR_PATH);
19717
+ loadedLanguage = language;
19718
+ return language;
19292
19719
  })();
19293
19720
  return languagePromise;
19294
19721
  }
19295
19722
  async function parsePython(source) {
19296
19723
  const language = await loadLanguage();
19724
+ return parseWith(language, source);
19725
+ }
19726
+ async function preloadPythonGrammar() {
19727
+ await loadLanguage();
19728
+ }
19729
+ function parsePythonSync(source) {
19730
+ if (loadedLanguage === null) {
19731
+ throw new Error(
19732
+ "the Python grammar has not been loaded yet; call preloadPythonGrammar first"
19733
+ );
19734
+ }
19735
+ return parseWith(loadedLanguage, source);
19736
+ }
19737
+ function parseWith(language, source) {
19297
19738
  const parser = new import_web_tree_sitter.Parser();
19298
19739
  parser.setLanguage(language);
19299
19740
  try {
@@ -19310,8 +19751,671 @@ async function parsePython(source) {
19310
19751
  // src/project.ts
19311
19752
  var import_node_fs3 = __toESM(require("fs"), 1);
19312
19753
  var import_node_path3 = __toESM(require("path"), 1);
19313
- var import_datalog4 = require("@suss/datalog");
19314
- var import_extractor4 = require("@suss/extractor");
19754
+ var import_behavioral_ir4 = require("@suss/behavioral-ir");
19755
+ var import_datalog6 = require("@suss/datalog");
19756
+ var import_extractor8 = require("@suss/extractor");
19757
+
19758
+ // src/diagnostics.ts
19759
+ var import_extractor5 = require("@suss/extractor");
19760
+ function buildPythonExtractionReport(args) {
19761
+ return (0, import_extractor5.buildUngatedExtractionReport)({
19762
+ packs: args.packs.map((pack) => ({
19763
+ name: pack.name,
19764
+ version: pack.version ?? null,
19765
+ discovers: pack.discovery.length > 0
19766
+ })),
19767
+ tallies: args.tallies,
19768
+ filesWalked: args.filesWalked,
19769
+ summaries: args.summaries
19770
+ });
19771
+ }
19772
+
19773
+ // src/reach/closure.ts
19774
+ var import_behavioral_ir3 = require("@suss/behavioral-ir");
19775
+ var import_datalog5 = require("@suss/datalog");
19776
+ var import_extractor6 = require("@suss/extractor");
19777
+
19778
+ // src/reach/resolveCallee.ts
19779
+ var stop = (reason) => ({ kind: "stop", reason });
19780
+ var MODULE_STOP = {
19781
+ ambiguous: "multipleSources",
19782
+ outsideRoots: "outsideRun"
19783
+ };
19784
+ function resolveCallee2(call, site, ctx) {
19785
+ const callee = field(call, "function");
19786
+ if (callee === null) {
19787
+ return { kind: "stopped", reason: "noDeclaration" };
19788
+ }
19789
+ return asCallee(resolveExpression(callee, site, ctx), ctx);
19790
+ }
19791
+ function resolveNamedFunctionArgument(name, site, ctx) {
19792
+ if (site.rebound.has(name)) {
19793
+ return null;
19794
+ }
19795
+ const binding = resolveName(site.scope, name);
19796
+ if (binding !== null && binding.kind === "assignment") {
19797
+ return null;
19798
+ }
19799
+ const resolved = binding === null ? throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set()) : resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
19800
+ return resolved.kind === "function" ? resolved.target : null;
19801
+ }
19802
+ function asCallee(resolved, ctx) {
19803
+ if (resolved.kind === "function") {
19804
+ return { kind: "followed", target: resolved.target };
19805
+ }
19806
+ if (resolved.kind === "class") {
19807
+ return asCallee(
19808
+ memberOfClass(resolved.file, resolved.node, "__init__", ctx),
19809
+ ctx
19810
+ );
19811
+ }
19812
+ if (resolved.kind === "stop") {
19813
+ return { kind: "stopped", reason: resolved.reason };
19814
+ }
19815
+ if (resolved.kind === "package") {
19816
+ return { kind: "stopped", reason: "outsideRun" };
19817
+ }
19818
+ return { kind: "stopped", reason: "noDeclaration" };
19819
+ }
19820
+ var EXPRESSION_RESOLVERS = {
19821
+ identifier: (node, site, ctx) => resolveIdentifier(node.text, site, ctx),
19822
+ attribute: (node, site, ctx) => {
19823
+ const object2 = field(node, "object");
19824
+ const attribute = field(node, "attribute");
19825
+ if (object2 === null || attribute === null) {
19826
+ return stop("noDeclaration");
19827
+ }
19828
+ return memberOf(resolveExpression(object2, site, ctx), attribute.text, ctx);
19829
+ },
19830
+ // `Service().run()` runs a method on whatever the constructor built.
19831
+ call: (node, site, ctx) => {
19832
+ const callee = field(node, "function");
19833
+ if (callee === null) {
19834
+ return stop("noDeclaration");
19835
+ }
19836
+ const built = resolveExpression(callee, site, ctx);
19837
+ return built.kind === "class" ? built : stop("noDeclaration");
19838
+ },
19839
+ parenthesized_expression: (node, site, ctx) => {
19840
+ const inner = node.namedChildren[0];
19841
+ return inner === null || inner === void 0 ? stop("noDeclaration") : resolveExpression(inner, site, ctx);
19842
+ }
19843
+ };
19844
+ function resolveExpression(node, site, ctx) {
19845
+ const resolver = EXPRESSION_RESOLVERS[node.type];
19846
+ return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
19847
+ }
19848
+ function resolveIdentifier(name, site, ctx) {
19849
+ if (site.rebound.has(name)) {
19850
+ return stop("unsettledValue");
19851
+ }
19852
+ if (isReceiver(name, site.scope)) {
19853
+ return receiverClass(site);
19854
+ }
19855
+ const binding = resolveName(site.scope, name);
19856
+ if (binding === null) {
19857
+ return throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set());
19858
+ }
19859
+ return resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
19860
+ }
19861
+ function isReceiver(name, scope) {
19862
+ return (name === "self" || name === "cls") && scope.kind === "function" && scope.parent?.kind === "class";
19863
+ }
19864
+ function receiverClass(site) {
19865
+ const classScope = site.scope.parent;
19866
+ if (classScope === null || classScope === void 0) {
19867
+ return stop("noDeclaration");
19868
+ }
19869
+ return { kind: "class", file: site.file, node: classScope.node };
19870
+ }
19871
+ var BINDING_RESOLVERS = {
19872
+ functionDef: (binding, file2) => ({
19873
+ kind: "function",
19874
+ target: reachedFunction(file2, binding.node)
19875
+ }),
19876
+ classDef: (binding, file2) => ({ kind: "class", file: file2, node: binding.node }),
19877
+ parameter: () => stop("callerSupplied"),
19878
+ // `import a.b.c` binds `a`, and `import a.b.c as m` binds `m` to `a.b.c`.
19879
+ import: (binding, file2, _scope, ctx) => moduleNamed(
19880
+ file2,
19881
+ binding.localName === binding.module.split(".")[0] ? binding.localName : binding.module,
19882
+ 0,
19883
+ ctx
19884
+ ),
19885
+ importFrom: (binding, file2, _scope, ctx, visited) => memberOf(
19886
+ moduleNamed(file2, binding.module, binding.relativeLevel, ctx),
19887
+ binding.importedName,
19888
+ ctx,
19889
+ visited
19890
+ ),
19891
+ // `run = load` is an alias worth one hop; anything else is a value.
19892
+ assignment: (binding, file2, scope, ctx, visited) => aliasedValue(binding.value, file2, scope, ctx, visited),
19893
+ global: () => stop("unsettledValue"),
19894
+ nonlocal: () => stop("unsettledValue")
19895
+ };
19896
+ function resolveBinding(binding, file2, scope, ctx, visited) {
19897
+ const resolver = BINDING_RESOLVERS[binding.kind];
19898
+ return resolver(binding, file2, scope, ctx, visited);
19899
+ }
19900
+ function aliasedValue(value, file2, scope, ctx, visited) {
19901
+ if (value === null) {
19902
+ return stop("unsettledValue");
19903
+ }
19904
+ if (value.type === "identifier") {
19905
+ const binding = resolveName(scope, value.text);
19906
+ if (binding === null || binding.kind === "assignment") {
19907
+ return stop("unsettledValue");
19908
+ }
19909
+ return resolveBinding(binding, file2, scope, ctx, visited);
19910
+ }
19911
+ if (value.type === "call") {
19912
+ const callee = field(value, "function");
19913
+ const built = callee === null ? stop("unsettledValue") : aliasedValue(callee, file2, scope, ctx, visited);
19914
+ return built.kind === "class" ? built : stop("unsettledValue");
19915
+ }
19916
+ if (value.type === "attribute") {
19917
+ const object2 = field(value, "object");
19918
+ const attribute = field(value, "attribute");
19919
+ if (object2 === null || attribute === null) {
19920
+ return stop("unsettledValue");
19921
+ }
19922
+ return memberOf(
19923
+ aliasedValue(object2, file2, scope, ctx, visited),
19924
+ attribute.text,
19925
+ ctx
19926
+ );
19927
+ }
19928
+ return stop("unsettledValue");
19929
+ }
19930
+ function reachedFunction(file2, node, owner) {
19931
+ const name = field(node, "name")?.text ?? "<anon>";
19932
+ const ownerName = owner === void 0 ? null : field(owner, "name")?.text;
19933
+ return {
19934
+ file: file2,
19935
+ node,
19936
+ name,
19937
+ exportPath: ownerName === null || ownerName === void 0 ? [name] : [ownerName, name]
19938
+ };
19939
+ }
19940
+ function moduleNamed(from, module2, relativeLevel, ctx) {
19941
+ const resolution = resolveModule(
19942
+ from.file,
19943
+ { module: module2, relativeLevel },
19944
+ { roots: ctx.roots }
19945
+ );
19946
+ if (resolution.status !== "resolved") {
19947
+ return resolution.reason === "external" ? { kind: "package", dotted: module2, relativeLevel, from } : stop(MODULE_STOP[resolution.reason]);
19948
+ }
19949
+ const file2 = ctx.filesByPath.get(resolution.file);
19950
+ return file2 === void 0 ? stop("outsideRun") : { kind: "module", file: file2 };
19951
+ }
19952
+ function memberOf(base, name, ctx, visited = /* @__PURE__ */ new Set()) {
19953
+ if (base.kind === "module") {
19954
+ return memberOfModule(base.file, name, ctx, visited);
19955
+ }
19956
+ if (base.kind === "package") {
19957
+ return moduleNamed(
19958
+ base.from,
19959
+ base.dotted === "" ? name : `${base.dotted}.${name}`,
19960
+ base.relativeLevel,
19961
+ ctx
19962
+ );
19963
+ }
19964
+ if (base.kind === "class") {
19965
+ return memberOfClass(base.file, base.node, name, ctx);
19966
+ }
19967
+ if (base.kind === "stop") {
19968
+ return base;
19969
+ }
19970
+ return stop("noDeclaration");
19971
+ }
19972
+ function memberOfClass(file2, classNode, name, ctx) {
19973
+ const classScope = file2.module.scopeFor.get(classNode.id);
19974
+ const binding = classScope?.bindings.get(name);
19975
+ if (binding === void 0) {
19976
+ return stop("noDeclaration");
19977
+ }
19978
+ if (binding.kind === "functionDef") {
19979
+ return {
19980
+ kind: "function",
19981
+ target: reachedFunction(file2, binding.node, classNode)
19982
+ };
19983
+ }
19984
+ return resolveBinding(binding, file2, classScope, ctx, /* @__PURE__ */ new Set());
19985
+ }
19986
+ function memberOfModule(file2, name, ctx, visited) {
19987
+ const key = `${file2.file}#${name}`;
19988
+ if (visited.has(key)) {
19989
+ return stop("unsettledValue");
19990
+ }
19991
+ visited.add(key);
19992
+ const binding = file2.module.moduleScope.bindings.get(name);
19993
+ if (binding !== void 0) {
19994
+ return resolveBinding(binding, file2, file2.module.moduleScope, ctx, visited);
19995
+ }
19996
+ if (file2.file.endsWith("__init__.py")) {
19997
+ const submodule = moduleNamed(file2, name, 1, ctx);
19998
+ if (submodule.kind === "module") {
19999
+ return submodule;
20000
+ }
20001
+ }
20002
+ return throughOpenImports(file2, name, ctx, visited);
20003
+ }
20004
+ function throughOpenImports(file2, name, ctx, visited) {
20005
+ const found = [];
20006
+ for (const spec of file2.module.openImports) {
20007
+ const dots = spec.length - spec.replace(/^\.+/, "").length;
20008
+ const opened = moduleNamed(file2, spec.slice(dots), dots, ctx);
20009
+ if (opened.kind !== "module") {
20010
+ continue;
20011
+ }
20012
+ const member = memberOfModule(opened.file, name, ctx, visited);
20013
+ if (member.kind !== "stop") {
20014
+ found.push(member);
20015
+ }
20016
+ }
20017
+ if (found.length > 1) {
20018
+ return stop("multipleSources");
20019
+ }
20020
+ return found[0] ?? stop("noDeclaration");
20021
+ }
20022
+
20023
+ // src/reach/closure.ts
20024
+ var REACHABLE_RULES = [
20025
+ (0, import_datalog5.rule)("reachable", [(0, import_datalog5.variable)("f")], [(0, import_datalog5.lit)("entry", (0, import_datalog5.variable)("f"))]),
20026
+ (0, import_datalog5.rule)(
20027
+ "reachable",
20028
+ [(0, import_datalog5.variable)("g")],
20029
+ [(0, import_datalog5.lit)("reachable", (0, import_datalog5.variable)("f")), (0, import_datalog5.lit)("calls", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("g"))]
20030
+ )
20031
+ ];
20032
+ function reachedFunctions(seeds, options) {
20033
+ const ctx = {
20034
+ filesByPath: new Map(options.files.map((file2) => [file2.file, file2])),
20035
+ roots: options.roots
20036
+ };
20037
+ const db = new import_datalog5.Database();
20038
+ const functionByKey = /* @__PURE__ */ new Map();
20039
+ const seedKeys = /* @__PURE__ */ new Set();
20040
+ const scanned = /* @__PURE__ */ new Set();
20041
+ const targetsByKey = /* @__PURE__ */ new Map();
20042
+ const stopsByKey = /* @__PURE__ */ new Map();
20043
+ const argTargetsByKey = /* @__PURE__ */ new Map();
20044
+ const parameterCallsByKey = /* @__PURE__ */ new Map();
20045
+ const passedPositions = /* @__PURE__ */ new Set();
20046
+ for (const seed of seeds) {
20047
+ seedKeys.add(seed.key);
20048
+ functionByKey.set(seed.key, {
20049
+ file: seed.file,
20050
+ node: seed.node,
20051
+ name: field(seed.node, "name")?.text ?? "<anon>",
20052
+ exportPath: []
20053
+ });
20054
+ db.add("entry", [seed.key]);
20055
+ }
20056
+ for (; ; ) {
20057
+ (0, import_datalog5.evaluate)(db, REACHABLE_RULES);
20058
+ const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
20059
+ if (frontier.length === 0) {
20060
+ break;
20061
+ }
20062
+ for (const key of frontier) {
20063
+ scanned.add(key);
20064
+ const source = functionByKey.get(key);
20065
+ if (source === void 0) {
20066
+ continue;
20067
+ }
20068
+ const scan = scanBody(source, ctx);
20069
+ if (scan.stops.length > 0) {
20070
+ stopsByKey.set(key, scan.stops);
20071
+ }
20072
+ targetsByKey.set(key, scan.targets);
20073
+ argTargetsByKey.set(key, scan.argTargets);
20074
+ if (scan.parameterCalls.length > 0) {
20075
+ parameterCallsByKey.set(key, scan.parameterCalls);
20076
+ }
20077
+ for (const position of scan.passedPositions) {
20078
+ passedPositions.add(position);
20079
+ }
20080
+ for (const target of scan.followed) {
20081
+ const calleeKey = keyOf(target);
20082
+ if (!functionByKey.has(calleeKey)) {
20083
+ functionByKey.set(calleeKey, target);
20084
+ }
20085
+ db.add("calls", [key, calleeKey]);
20086
+ }
20087
+ }
20088
+ }
20089
+ const summaries = [];
20090
+ const summariesByKey = /* @__PURE__ */ new Map();
20091
+ for (const [keyAtom] of db.facts("reachable")) {
20092
+ const key = String(keyAtom);
20093
+ const target = functionByKey.get(key);
20094
+ if (seedKeys.has(key) || target === void 0) {
20095
+ continue;
20096
+ }
20097
+ const summary = (0, import_extractor6.assembleSummary)(libraryUnit(target, options), {
20098
+ gapHandling: options.gapHandling
20099
+ });
20100
+ summary.confidence = { source: "inferred_static", level: "low" };
20101
+ if (options.gapHandling !== "silent") {
20102
+ summary.gaps.push(...(stopsByKey.get(key) ?? []).map(import_behavioral_ir3.unfollowedCallGap));
20103
+ }
20104
+ (0, import_behavioral_ir3.placeCalls)(summary, targetsByKey.get(key));
20105
+ (0, import_behavioral_ir3.placeArgTargets)(summary, argTargetsByKey.get(key));
20106
+ (0, import_behavioral_ir3.placeCalleeParameters)(summary, parameterCallsByKey.get(key));
20107
+ summariesByKey.set(key, [summary]);
20108
+ summaries.push(summary);
20109
+ }
20110
+ if (options.gapHandling !== "silent") {
20111
+ (0, import_behavioral_ir3.recordParameterGaps)(parameterCallsByKey, summariesByKey, passedPositions);
20112
+ }
20113
+ return {
20114
+ summaries,
20115
+ targetsByKey,
20116
+ stopsByKey,
20117
+ argTargetsByKey,
20118
+ parameterCallsByKey,
20119
+ passedPositions
20120
+ };
20121
+ }
20122
+ function keyOf(target) {
20123
+ return nodeId(target.file.file, target.node);
20124
+ }
20125
+ var EMPTY_SCAN = {
20126
+ followed: [],
20127
+ stops: [],
20128
+ targets: /* @__PURE__ */ new Map(),
20129
+ argTargets: /* @__PURE__ */ new Map(),
20130
+ parameterCalls: [],
20131
+ passedPositions: /* @__PURE__ */ new Set()
20132
+ };
20133
+ function scanBody(source, ctx) {
20134
+ const followed = [];
20135
+ const stops = [];
20136
+ const placements = new import_behavioral_ir3.TargetPlacements();
20137
+ const parameterCalls = [];
20138
+ const passedPositions = /* @__PURE__ */ new Set();
20139
+ const seen = /* @__PURE__ */ new Set();
20140
+ const parameterCallsSeen = /* @__PURE__ */ new Set();
20141
+ const { file: file2, node } = source;
20142
+ const body = field(node, "body");
20143
+ if (body === null) {
20144
+ return EMPTY_SCAN;
20145
+ }
20146
+ const ownParameters = callParameterNames(node, source.exportPath.length > 1);
20147
+ const recordPassedArgs = (call, callee, calleeKey, where) => {
20148
+ const args = field(call, "arguments");
20149
+ if (args === null) {
20150
+ return;
20151
+ }
20152
+ args.namedChildren.forEach((arg, position) => {
20153
+ if (arg === null || arg.type !== "identifier") {
20154
+ return;
20155
+ }
20156
+ const resolved = resolveNamedFunctionArgument(
20157
+ arg.text,
20158
+ { file: file2, scope: where.scope, rebound: where.rebound },
20159
+ ctx
20160
+ );
20161
+ if (resolved === null) {
20162
+ return;
20163
+ }
20164
+ const key = keyOf(resolved);
20165
+ if (!seen.has(key)) {
20166
+ seen.add(key);
20167
+ followed.push(resolved);
20168
+ }
20169
+ placements.placeArg(callee, position, {
20170
+ file: resolved.file.displayPath,
20171
+ span: spanOf(resolved.node)
20172
+ });
20173
+ if (calleeKey !== null) {
20174
+ passedPositions.add(`${calleeKey}#${position}`);
20175
+ }
20176
+ });
20177
+ };
20178
+ const record2 = (call, where) => {
20179
+ const callee = calleeText(call);
20180
+ const outcome = resolveCallee2(
20181
+ call,
20182
+ { file: file2, scope: where.scope, rebound: where.rebound },
20183
+ ctx
20184
+ );
20185
+ const placed = outcome.kind === "followed" ? {
20186
+ file: outcome.target.file.displayPath,
20187
+ span: spanOf(outcome.target.node)
20188
+ } : outcome.reason === "noDeclaration" ? null : { file: file2.displayPath, span: spanOf(call) };
20189
+ placements.place(callee, placed);
20190
+ recordPassedArgs(
20191
+ call,
20192
+ callee,
20193
+ outcome.kind === "followed" ? keyOf(outcome.target) : null,
20194
+ where
20195
+ );
20196
+ if (outcome.kind === "stopped") {
20197
+ const stopKey = `${outcome.reason}:${callee}`;
20198
+ if (!seen.has(stopKey) && (0, import_behavioral_ir3.worthRecording)(outcome.reason)) {
20199
+ seen.add(stopKey);
20200
+ stops.push({ callee, reason: outcome.reason });
20201
+ }
20202
+ if (outcome.reason === "callerSupplied" && !parameterCallsSeen.has(callee)) {
20203
+ const parameterIndex = ownParameters.indexOf(callee);
20204
+ if (parameterIndex !== -1) {
20205
+ parameterCallsSeen.add(callee);
20206
+ parameterCalls.push({ callee, parameterIndex });
20207
+ }
20208
+ }
20209
+ return;
20210
+ }
20211
+ const key = keyOf(outcome.target);
20212
+ if (!seen.has(key)) {
20213
+ seen.add(key);
20214
+ followed.push(outcome.target);
20215
+ }
20216
+ };
20217
+ (0, import_extractor6.walkDescendants)(
20218
+ body,
20219
+ { scope: scopeAt(file2, node), rebound: reboundNames(body) },
20220
+ {
20221
+ at: (child, where) => {
20222
+ if (child.type === "call") {
20223
+ record2(child, where);
20224
+ }
20225
+ },
20226
+ into: (child, where) => {
20227
+ if (child.type === "function_definition") {
20228
+ return import_extractor6.SKIP_CHILDREN;
20229
+ }
20230
+ if (child.type === "lambda") {
20231
+ return {
20232
+ scope: where.scope,
20233
+ rebound: /* @__PURE__ */ new Set([...where.rebound, ...lambdaParameters(child)])
20234
+ };
20235
+ }
20236
+ return {
20237
+ scope: file2.module.scopeFor.get(child.id) ?? where.scope,
20238
+ rebound: where.rebound
20239
+ };
20240
+ }
20241
+ }
20242
+ );
20243
+ return {
20244
+ followed,
20245
+ stops,
20246
+ targets: placements.targets,
20247
+ argTargets: placements.argTargets,
20248
+ parameterCalls,
20249
+ passedPositions
20250
+ };
20251
+ }
20252
+ function scopeAt(file2, node) {
20253
+ let current = node;
20254
+ while (current !== null) {
20255
+ const scope = file2.module.scopeFor.get(current.id);
20256
+ if (scope !== void 0) {
20257
+ return scope;
20258
+ }
20259
+ current = current.parent;
20260
+ }
20261
+ return file2.module.moduleScope;
20262
+ }
20263
+ var REBINDING_FIELDS = {
20264
+ assignment: "left",
20265
+ augmented_assignment: "left",
20266
+ named_expression: "name",
20267
+ for_statement: "left",
20268
+ for_in_clause: "left",
20269
+ as_pattern: "alias"
20270
+ };
20271
+ function reboundNames(body) {
20272
+ const names = /* @__PURE__ */ new Set();
20273
+ (0, import_extractor6.walkDescendants)(body, null, {
20274
+ at: (node) => {
20275
+ const target = REBINDING_FIELDS[node.type];
20276
+ if (target === void 0 || boundByBinder(node, body)) {
20277
+ return;
20278
+ }
20279
+ const written = field(node, target);
20280
+ if (written !== null) {
20281
+ for (const name of identifiersUnder(written)) {
20282
+ names.add(name);
20283
+ }
20284
+ }
20285
+ },
20286
+ into: (node) => node.type === "function_definition" ? import_extractor6.SKIP_CHILDREN : null
20287
+ });
20288
+ return names;
20289
+ }
20290
+ function boundByBinder(node, body) {
20291
+ if (node.type !== "assignment") {
20292
+ return false;
20293
+ }
20294
+ const statement = node.parent;
20295
+ return field(node, "left")?.type === "identifier" && statement?.type === "expression_statement" && statement.parent?.id === body.id;
20296
+ }
20297
+ function identifiersUnder(node, found = []) {
20298
+ if (node.type === "identifier") {
20299
+ found.push(node.text);
20300
+ return found;
20301
+ }
20302
+ for (const child of node.namedChildren) {
20303
+ if (child !== null && child.type !== "attribute" && child.type !== "subscript") {
20304
+ identifiersUnder(child, found);
20305
+ }
20306
+ }
20307
+ return found;
20308
+ }
20309
+ function lambdaParameters(lambda) {
20310
+ const parameters = field(lambda, "parameters");
20311
+ return parameters === null ? [] : identifiersUnder(parameters);
20312
+ }
20313
+ function libraryUnit(target, options) {
20314
+ const { file: file2, node, name, exportPath } = target;
20315
+ const body = field(node, "body");
20316
+ const extra = recognizedBodyEffects(
20317
+ node,
20318
+ file2.module,
20319
+ options.storageFor(file2)
20320
+ );
20321
+ const range = rangeOf(node);
20322
+ return {
20323
+ identity: {
20324
+ name,
20325
+ nameKind: "binding",
20326
+ kind: "library",
20327
+ file: file2.displayPath,
20328
+ range,
20329
+ span: spanOf(node),
20330
+ exportName: exportPath[0] ?? name,
20331
+ exportPath
20332
+ },
20333
+ boundaryBinding: (0, import_behavioral_ir3.functionCallBinding)({
20334
+ transport: "in-process",
20335
+ recognition: "reachable"
20336
+ }),
20337
+ parameters: positionalParameters(node, exportPath.length > 1),
20338
+ branches: [
20339
+ {
20340
+ conditions: [],
20341
+ terminal: {
20342
+ kind: "return",
20343
+ statusCode: null,
20344
+ body: null,
20345
+ exceptionType: null,
20346
+ message: null,
20347
+ component: null,
20348
+ renderTree: null,
20349
+ delegateTarget: null,
20350
+ emitEvent: null,
20351
+ location: range
20352
+ },
20353
+ effects: invocationEffects(node),
20354
+ ...extra.length === 0 ? {} : { extraEffects: extra },
20355
+ location: range,
20356
+ isDefault: true
20357
+ }
20358
+ ],
20359
+ bodyContent: body === null ? "absent" : bodyContentOf(body),
20360
+ dependencyCalls: [],
20361
+ declaredContract: null
20362
+ };
20363
+ }
20364
+ function positionalParameters(node, isMethod) {
20365
+ const parameters = field(node, "parameters");
20366
+ if (parameters === null) {
20367
+ return [];
20368
+ }
20369
+ const out = [];
20370
+ let position = 0;
20371
+ for (const param of parameters.namedChildren) {
20372
+ if (param === null) {
20373
+ continue;
20374
+ }
20375
+ const name = parameterName3(param);
20376
+ if (name === null) {
20377
+ continue;
20378
+ }
20379
+ if (isMethod && position === 0 && (name === "self" || name === "cls")) {
20380
+ position += 1;
20381
+ continue;
20382
+ }
20383
+ out.push({ name, position, role: name, typeText: null });
20384
+ position += 1;
20385
+ }
20386
+ return out;
20387
+ }
20388
+ function callParameterNames(node, isMethod) {
20389
+ const parameters = field(node, "parameters");
20390
+ if (parameters === null) {
20391
+ return [];
20392
+ }
20393
+ const names = [];
20394
+ for (const param of parameters.namedChildren) {
20395
+ if (param === null) {
20396
+ continue;
20397
+ }
20398
+ const name = parameterName3(param);
20399
+ if (name === null) {
20400
+ continue;
20401
+ }
20402
+ if (isMethod && names.length === 0 && (name === "self" || name === "cls")) {
20403
+ continue;
20404
+ }
20405
+ names.push(name);
20406
+ }
20407
+ return names;
20408
+ }
20409
+ function parameterName3(param) {
20410
+ if (param.type === "identifier") {
20411
+ return param.text;
20412
+ }
20413
+ const named = field(param, "name");
20414
+ if (named !== null && named.type === "identifier") {
20415
+ return named.text;
20416
+ }
20417
+ return identifiersUnder(param)[0] ?? null;
20418
+ }
19315
20419
 
19316
20420
  // src/routers.ts
19317
20421
  var UNSTATED_PREFIX = { kind: "unstated" };
@@ -19586,6 +20690,9 @@ function buildPatternIndex(files, importModule, composition, resolverOptions) {
19586
20690
  for (const bound of files) {
19587
20691
  collectConstructions(bound, importModule, composition, index);
19588
20692
  }
20693
+ for (const bound of files) {
20694
+ collectReturnedConstructions(bound, importModule, composition, index);
20695
+ }
19589
20696
  if (objectPrefix?.carrier !== void 0) {
19590
20697
  const carrier = objectPrefix.carrier;
19591
20698
  for (const bound of files) {
@@ -19761,6 +20868,37 @@ function collectConstructions(bound, importModule, composition, index) {
19761
20868
  );
19762
20869
  }
19763
20870
  }
20871
+ function collectReturnedConstructions(bound, importModule, composition, index) {
20872
+ for (const stmt of bodyStatements(bound.root)) {
20873
+ const definition = stripDecorators(stmt).definition;
20874
+ if (definition.type !== "function_definition") {
20875
+ continue;
20876
+ }
20877
+ const scope = bound.module.scopeFor.get(definition.id);
20878
+ for (const inner of nestedStatements(definition)) {
20879
+ if (inner.type !== "return_statement" || scope === void 0) {
20880
+ continue;
20881
+ }
20882
+ const returned = inner.namedChildren[0];
20883
+ if (returned?.type !== "call") {
20884
+ continue;
20885
+ }
20886
+ const callee = field(returned, "function");
20887
+ const constructorName = callee === null ? null : importedConstructorName(callee, scope, importModule);
20888
+ if (constructorName !== composition.routerConstructorName) {
20889
+ continue;
20890
+ }
20891
+ const { keywordArgs } = readCallArguments(field(returned, "arguments"));
20892
+ const valueKey2 = nodeId(bound.file, returned);
20893
+ index.byValueKey.set(valueKey2, {
20894
+ constructorName,
20895
+ valueKey: valueKey2,
20896
+ prefix: constructorPrefix(keywordArgs, composition, scope),
20897
+ reassigned: false
20898
+ });
20899
+ }
20900
+ }
20901
+ }
19764
20902
  function collectCarrierConstructions(carrier, composition, scan) {
19765
20903
  walkStatements(
19766
20904
  bodyStatements(scan.bound.root),
@@ -19819,6 +20957,21 @@ function constructionNamed(name, scope, scan, constructions = scan.index.constru
19819
20957
  };
19820
20958
  return resolvers[binding.kind]?.() ?? null;
19821
20959
  }
20960
+ function constructionThroughFacts(name, scope, scan) {
20961
+ const facts = scan.index.facts;
20962
+ const binding = resolveName(scope, name);
20963
+ if (facts === void 0 || binding === null) {
20964
+ return null;
20965
+ }
20966
+ if (binding.kind !== "assignment" && binding.kind !== "importFrom") {
20967
+ return null;
20968
+ }
20969
+ const nameKey = `${scan.bound.file}#${name}`;
20970
+ const constructionKey = subjectConstructions(facts, [nameKey]).get(
20971
+ nameKey
20972
+ )?.constructionKey;
20973
+ return constructionKey === void 0 ? null : scan.index.byValueKey.get(constructionKey) ?? null;
20974
+ }
19822
20975
  var MOUNT_STATE_BY_EFFECT = {
19823
20976
  prefixes: (statedPrefix, site) => ({
19824
20977
  kind: "mounted",
@@ -19988,10 +21141,10 @@ function constructionsReturnedBy(target, index) {
19988
21141
  return { kind: "unread" };
19989
21142
  }
19990
21143
  const contained = containedValues(facts, returned);
19991
- resolveCalls(facts, contained);
21144
+ const settled = subjectConstructions(facts, contained);
19992
21145
  const found = [];
19993
21146
  for (const value of contained) {
19994
- const resolved = resolvedValueKey(facts, value);
21147
+ const resolved = settled.get(value)?.constructionKey ?? value;
19995
21148
  const construction = index.byValueKey.get(resolved);
19996
21149
  if (construction === void 0) {
19997
21150
  return {
@@ -20006,10 +21159,6 @@ function constructionsReturnedBy(target, index) {
20006
21159
  }
20007
21160
  return { kind: "settled", found };
20008
21161
  }
20009
- function resolvedValueKey(facts, value) {
20010
- const row = facts.facts("wantedIsWrittenAs").find((entry) => String(entry[0]) === value);
20011
- return row === void 0 ? value : String(row[1]);
20012
- }
20013
21162
  function mountedConstructions(arg, position, scan) {
20014
21163
  if (arg?.kind === "attribute") {
20015
21164
  const construction = constructionThroughModule(arg, position.scope, scan);
@@ -20019,7 +21168,7 @@ function mountedConstructions(arg, position, scan) {
20019
21168
  return [];
20020
21169
  }
20021
21170
  const named = (name) => {
20022
- const construction = constructionNamed(name, position.scope, scan);
21171
+ const construction = constructionNamed(name, position.scope, scan) ?? constructionThroughFacts(name, position.scope, scan);
20023
21172
  return construction === null ? [] : [construction];
20024
21173
  };
20025
21174
  const target = position.loopBindings.get(arg.name);
@@ -20406,36 +21555,69 @@ function carrierPrefix(carrier, constructorArg, objectName, scope, scan) {
20406
21555
  return readings[construction.prefix.kind]();
20407
21556
  }
20408
21557
 
21558
+ // src/version.ts
21559
+ var import_extractor7 = require("@suss/extractor");
21560
+ var ADAPTER_VERSION = "0.1.0";
21561
+ var adapterStamp = (0, import_extractor7.createAdapterStamp)({
21562
+ moduleUrl: importMetaUrl,
21563
+ version: ADAPTER_VERSION
21564
+ });
21565
+
20409
21566
  // src/project.ts
20410
- function reportUnresolvedProjectModules(options) {
21567
+ function reportUnresolvedProjectModules(options, db) {
21568
+ const imported = new Set(db.facts("pyImport").map((row) => String(row[1])));
20411
21569
  for (const pack of options.packs) {
20412
21570
  for (const module2 of pack.projectModules ?? []) {
20413
- const resolved = resolveAbsoluteModule(module2, { roots: options.roots });
20414
- if (resolved.status === "resolved") {
21571
+ if (imported.has(module2)) {
20415
21572
  continue;
20416
21573
  }
20417
21574
  process.stderr.write(
20418
- `[suss] ${pack.name}: the configured module ${module2} does not resolve under ${options.roots.join(", ")}, so nothing it wraps will be discovered.
21575
+ `[suss] ${pack.name}: no file under ${options.roots.join(", ")} imports ${module2}, so the stub for it changes nothing.
20419
21576
  `
20420
21577
  );
20421
21578
  }
20422
21579
  }
20423
21580
  }
20424
21581
  async function extractPythonProject(options) {
20425
- const db = new import_datalog4.Database();
21582
+ const timer = options.onTiming !== void 0 ? (0, import_extractor8.createTimer)() : (0, import_extractor8.noopTimer)();
21583
+ const cacheDir = adapterStamp.declineWhenRunFromSource(
21584
+ options.cacheDir === null ? null : options.cacheDir ?? (options.projectRoot !== void 0 ? import_node_path3.default.join(options.projectRoot, ".suss", "cache") : null)
21585
+ );
21586
+ const cache = (0, import_extractor8.createCacheLayer)(cacheDir);
21587
+ const packsDigest = adapterStamp.packsDigest(
21588
+ options.packs.map(
21589
+ (pack) => pack.version !== void 0 ? { name: pack.name, version: pack.version } : { name: pack.name }
21590
+ )
21591
+ );
21592
+ const cacheInput = {
21593
+ files: cacheDir === null ? [] : options.files,
21594
+ adapterPacksDigest: cacheDir === null ? packsDigest : (0, import_extractor8.runDigest)(packsDigest, options.packs, options.files)
21595
+ };
21596
+ const lookup = await timer.timeAsync(
21597
+ "cache.lookup",
21598
+ () => cache.lookup(cacheInput)
21599
+ );
21600
+ options.onCacheDiagnostic?.(lookup.diagnostic);
21601
+ if (lookup.kind === "hit") {
21602
+ options.onTiming?.(timer.report());
21603
+ return { summaries: lookup.summaries, facts: new import_datalog6.Database() };
21604
+ }
21605
+ const db = new import_datalog6.Database();
20426
21606
  const summaries = [];
20427
21607
  const gapHandling = options.gapHandling ?? "permissive";
21608
+ const tallies = (0, import_extractor5.createPackTallies)(options.packs);
20428
21609
  const displayPathOf = (file2) => options.workspaceRoot !== void 0 ? import_node_path3.default.relative(options.workspaceRoot, file2) : file2;
20429
- reportUnresolvedProjectModules(options);
20430
21610
  const bound = [];
20431
21611
  for (const file2 of options.files) {
20432
- const source = import_node_fs3.default.readFileSync(file2, "utf8");
20433
- const tree = await parsePython(source);
20434
- bound.push({
20435
- file: file2,
20436
- displayPath: displayPathOf(file2),
20437
- root: tree.rootNode,
20438
- module: bindModule(tree.rootNode)
21612
+ await timer.timeAsync("parse", async () => {
21613
+ const source = import_node_fs3.default.readFileSync(file2, "utf8");
21614
+ const tree = await parsePython(source);
21615
+ bound.push({
21616
+ file: file2,
21617
+ displayPath: displayPathOf(file2),
21618
+ root: tree.rootNode,
21619
+ module: bindModule(tree.rootNode)
21620
+ });
20439
21621
  });
20440
21622
  }
20441
21623
  const mountsRouters = options.packs.some(
@@ -20446,81 +21628,172 @@ async function extractPythonProject(options) {
20446
21628
  const discovers = options.packs.some((pack) => pack.discovery.length > 0);
20447
21629
  const needsValues = discovers || mountsRouters || storagePatterns.length > 0;
20448
21630
  const definitions = /* @__PURE__ */ new Map();
20449
- for (const { file: file2, root, module: moduleBinding } of bound) {
20450
- emitModuleImportFacts(db, file2, moduleBinding, { roots: options.roots });
20451
- if (needsValues) {
20452
- emitValueFacts(db, file2, root);
20453
- }
20454
- if (storagePatterns.length > 0) {
21631
+ timer.time("discover", () => {
21632
+ for (const { file: file2, root, module: moduleBinding } of bound) {
21633
+ emitModuleImportFacts(db, file2, moduleBinding, { roots: options.roots });
21634
+ if (needsValues) {
21635
+ emitValueFacts(db, file2, root);
21636
+ }
20455
21637
  indexDefinitions(definitions, file2, root);
20456
21638
  }
20457
- }
20458
- const couldMatch = methodsDeclaredNear(db, storagePatterns, definitions);
20459
- const startsAQuery = /* @__PURE__ */ new Set([
20460
- ...couldMatch,
20461
- ...storagePatterns.flatMap((pattern) => pattern.queryFunctions ?? [])
20462
- ]);
20463
- const leadsToStorage = functionsReachingStorage(
20464
- db,
20465
- definitions,
20466
- startsAQuery
20467
- );
20468
- const routerIndex = buildRouterIndex(bound, options.packs, {
20469
- roots: options.roots,
20470
- ...mountsRouters ? { facts: db } : {}
20471
21639
  });
20472
- for (const { file: file2, root, module: moduleBinding } of bound) {
21640
+ reportUnresolvedProjectModules(options, db);
21641
+ const couldMatch = timer.time(
21642
+ "discover",
21643
+ () => methodsDeclaredNear(db, storagePatterns, definitions)
21644
+ );
21645
+ const routerIndex = timer.time(
21646
+ "discover",
21647
+ () => buildRouterIndex(bound, options.packs, {
21648
+ roots: options.roots,
21649
+ ...mountsRouters ? { facts: db } : {}
21650
+ })
21651
+ );
21652
+ const storageFor = (file2) => storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
21653
+ facts: db,
21654
+ factsPath: file2.file,
21655
+ patterns: storagePatterns,
21656
+ definitionAt: (key) => definitions.get(key),
21657
+ couldMatch,
21658
+ rawSql: rawSqlPatterns
21659
+ } : void 0;
21660
+ const seeds = [];
21661
+ const summariesBySeed = /* @__PURE__ */ new Map();
21662
+ for (const boundFile of bound) {
21663
+ const { file: file2, root, module: moduleBinding } = boundFile;
20473
21664
  const displayPath = displayPathOf(file2);
20474
- const rawUnits = discoverUnits(root, moduleBinding, {
20475
- packs: options.packs,
20476
- filePath: displayPath,
20477
- absoluteFile: file2,
20478
- routerIndex,
20479
- gapHandling,
20480
- ...needsValues ? { facts: db } : {},
20481
- ...storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
20482
- storage: {
20483
- facts: db,
20484
- factsPath: file2,
20485
- patterns: storagePatterns,
20486
- definitionAt: (key) => definitions.get(key),
20487
- couldMatch,
20488
- leadsToStorage,
20489
- rawSql: rawSqlPatterns
20490
- }
20491
- } : {}
20492
- });
21665
+ const storage = storageFor(boundFile);
21666
+ const rawUnits = timer.time(
21667
+ "discover",
21668
+ () => discoverUnits(root, moduleBinding, {
21669
+ packs: options.packs,
21670
+ filePath: displayPath,
21671
+ absoluteFile: file2,
21672
+ routerIndex,
21673
+ gapHandling,
21674
+ ...needsValues ? { facts: db } : {},
21675
+ ...storage === void 0 ? {} : { storage }
21676
+ })
21677
+ );
20493
21678
  for (const raw of rawUnits) {
20494
- const summary = (0, import_extractor4.assembleSummary)(raw, { gapHandling });
21679
+ const summary = timer.time(
21680
+ "summarize",
21681
+ () => (0, import_extractor8.assembleSummary)(raw, { gapHandling })
21682
+ );
20495
21683
  summary.confidence = { source: "inferred_static", level: "low" };
20496
21684
  summaries.push(summary);
20497
21685
  emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
21686
+ (0, import_extractor5.tallyUnit)(tallies, raw.boundaryBinding?.recognition);
21687
+ const span = raw.identity.span;
21688
+ const key = span === void 0 ? null : `${file2}:${span.start}-${span.end}`;
21689
+ const node = key === null ? void 0 : definitions.get(key);
21690
+ if (key === null || node === void 0) {
21691
+ continue;
21692
+ }
21693
+ const sharing = summariesBySeed.get(key);
21694
+ if (sharing === void 0) {
21695
+ seeds.push({ key, file: boundFile, node });
21696
+ summariesBySeed.set(key, [summary]);
21697
+ } else {
21698
+ sharing.push(summary);
21699
+ }
21700
+ }
21701
+ const loadTimeReads = timer.time(
21702
+ "discover",
21703
+ () => envReadEffects(root, moduleBinding)
21704
+ );
21705
+ if (loadTimeReads.length > 0) {
21706
+ const summary = timer.time(
21707
+ "summarize",
21708
+ () => (0, import_extractor8.assembleSummary)(
21709
+ (0, import_extractor8.moduleInitStructure)({
21710
+ name: import_node_path3.default.basename(displayPath),
21711
+ file: displayPath,
21712
+ range: rangeOf(root),
21713
+ effects: loadTimeReads
21714
+ }),
21715
+ { gapHandling }
21716
+ )
21717
+ );
21718
+ summary.confidence = { source: "inferred_static", level: "low" };
21719
+ summaries.push(summary);
21720
+ }
21721
+ }
21722
+ const reached = timer.time(
21723
+ "summarize",
21724
+ () => reachedFunctions(seeds, {
21725
+ files: bound,
21726
+ roots: options.roots,
21727
+ gapHandling,
21728
+ storageFor
21729
+ })
21730
+ );
21731
+ for (const [key, owners] of summariesBySeed) {
21732
+ for (const summary of owners) {
21733
+ if (gapHandling !== "silent") {
21734
+ summary.gaps.push(
21735
+ ...(reached.stopsByKey.get(key) ?? []).map(import_behavioral_ir4.unfollowedCallGap)
21736
+ );
21737
+ }
21738
+ (0, import_behavioral_ir4.placeCalls)(summary, reached.targetsByKey.get(key));
21739
+ (0, import_behavioral_ir4.placeArgTargets)(summary, reached.argTargetsByKey.get(key));
21740
+ (0, import_behavioral_ir4.placeCalleeParameters)(summary, reached.parameterCallsByKey.get(key));
20498
21741
  }
20499
21742
  }
20500
- stampModuleImports(summaries, db, displayPathOf);
21743
+ if (gapHandling !== "silent") {
21744
+ (0, import_behavioral_ir4.recordParameterGaps)(
21745
+ reached.parameterCallsByKey,
21746
+ summariesBySeed,
21747
+ reached.passedPositions
21748
+ );
21749
+ }
21750
+ summaries.push(...reached.summaries);
21751
+ const resolvedImports = resolvedImportsOf(db, displayPathOf);
21752
+ (0, import_extractor8.stampModuleImports)(summaries, (file2) => resolvedImports.get(file2) ?? []);
21753
+ const idRoot = options.projectRoot ?? options.workspaceRoot;
21754
+ for (const summary of summaries) {
21755
+ const absoluteFile = options.workspaceRoot === void 0 ? summary.location.file : import_node_path3.default.resolve(options.workspaceRoot, summary.location.file);
21756
+ summary.identity.id = (0, import_behavioral_ir4.summaryIdFromParts)({
21757
+ workspace: void 0,
21758
+ file: idRoot === void 0 ? absoluteFile : import_node_path3.default.relative(idRoot, absoluteFile),
21759
+ name: summary.identity.name,
21760
+ exportPath: summary.identity.exportPath
21761
+ });
21762
+ }
21763
+ (0, import_behavioral_ir4.disambiguateSummaryIds)(summaries);
21764
+ (0, import_behavioral_ir4.linkCallsToSummaries)(summaries);
21765
+ await timer.timeAsync("cache.write", async () => {
21766
+ if (cacheDir === null || summaries.length === 0) {
21767
+ return;
21768
+ }
21769
+ try {
21770
+ await cache.write(cacheInput, summaries);
21771
+ } catch {
21772
+ }
21773
+ });
21774
+ options.onExtractionReport?.(
21775
+ buildPythonExtractionReport({
21776
+ packs: options.packs,
21777
+ tallies,
21778
+ filesWalked: options.files.length,
21779
+ summaries
21780
+ })
21781
+ );
21782
+ options.onTiming?.(timer.report());
20501
21783
  return { summaries, facts: db };
20502
21784
  }
20503
- function stampModuleImports(summaries, db, displayPathOf) {
21785
+ function resolvedImportsOf(db, displayPathOf) {
20504
21786
  const importsByFile = /* @__PURE__ */ new Map();
20505
21787
  for (const [from, , to] of db.facts("pyImportResolved")) {
20506
21788
  if (typeof from !== "string" || typeof to !== "string") {
20507
21789
  continue;
20508
21790
  }
20509
21791
  const key = displayPathOf(from);
20510
- const seen = importsByFile.get(key) ?? /* @__PURE__ */ new Set();
20511
- seen.add(displayPathOf(to));
21792
+ const seen = importsByFile.get(key) ?? [];
21793
+ seen.push(displayPathOf(to));
20512
21794
  importsByFile.set(key, seen);
20513
21795
  }
20514
- for (const summary of summaries) {
20515
- const imports = importsByFile.get(summary.location.file);
20516
- if (imports === void 0 || imports.size === 0) {
20517
- continue;
20518
- }
20519
- summary.metadata = {
20520
- ...summary.metadata ?? {},
20521
- moduleImports: [...imports].sort()
20522
- };
20523
- }
21796
+ return importsByFile;
20524
21797
  }
20525
21798
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
20526
21799
  "__pycache__",
@@ -20575,55 +21848,272 @@ function methodsDeclaredNear(db, patterns, definitions) {
20575
21848
  }
20576
21849
  return found;
20577
21850
  }
20578
- function calledName(call) {
20579
- const callee = field(call, "function");
20580
- if (callee === null) {
20581
- return "";
21851
+
21852
+ // src/facts/why.ts
21853
+ var FUNCTION_TYPES2 = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
21854
+ var IMPORT_TYPES = /* @__PURE__ */ new Set(["import_statement", "import_from_statement"]);
21855
+ function namedChildrenOf(node) {
21856
+ return node.namedChildren.filter((child) => child !== null);
21857
+ }
21858
+ function indexImportNames(file2, stmt, locations) {
21859
+ for (const nameNode of fields(stmt, "name")) {
21860
+ if (nameNode.type === "aliased_import") {
21861
+ const alias = field(nameNode, "alias");
21862
+ if (alias !== null) {
21863
+ locations.set(`${file2}#${alias.text}`, { file: file2, node: alias });
21864
+ }
21865
+ continue;
21866
+ }
21867
+ const local = nameNode.type === "dotted_name" ? nameNode.namedChild(0) : nameNode;
21868
+ if (local !== null) {
21869
+ locations.set(`${file2}#${local.text}`, { file: file2, node: local });
21870
+ }
20582
21871
  }
20583
- return callee.type === "attribute" ? field(callee, "attribute")?.text ?? "" : callee.text;
20584
21872
  }
20585
- function callsUnder(node, found = []) {
20586
- for (const child of node.namedChildren) {
20587
- if (child === null) {
21873
+ function enclosingFunction(node) {
21874
+ let current = node.parent;
21875
+ while (current !== null) {
21876
+ if (FUNCTION_TYPES2.has(current.type)) {
21877
+ return current;
21878
+ }
21879
+ current = current.parent;
21880
+ }
21881
+ return null;
21882
+ }
21883
+ function indexFile(file2, root, locations) {
21884
+ const walk = (node) => {
21885
+ locations.set(nodeId(file2, node), { file: file2, node });
21886
+ if (IMPORT_TYPES.has(node.type)) {
21887
+ indexImportNames(file2, node, locations);
21888
+ }
21889
+ if (FUNCTION_TYPES2.has(node.type) || node.type === "class_definition") {
21890
+ const name = field(node, "name");
21891
+ if (name !== null) {
21892
+ locations.set(`${file2}#${name.text}`, { file: file2, node: name });
21893
+ }
21894
+ }
21895
+ if (node.type === "assignment") {
21896
+ const left = field(node, "left");
21897
+ if (left !== null && left.type === "identifier") {
21898
+ locations.set(`${file2}#${left.text}`, { file: file2, node: left });
21899
+ }
21900
+ }
21901
+ for (const child of namedChildrenOf(node)) {
21902
+ walk(child);
21903
+ }
21904
+ };
21905
+ walk(root);
21906
+ }
21907
+ function displayNameOf(node) {
21908
+ if (node.type === "identifier") {
21909
+ return node.text;
21910
+ }
21911
+ if (node.type === "call") {
21912
+ const callee = field(node, "function");
21913
+ return `${callee?.text ?? "call"}(...)`;
21914
+ }
21915
+ if (node.type === "function_definition" || node.type === "class_definition") {
21916
+ return field(node, "name")?.text ?? node.text;
21917
+ }
21918
+ const text = node.text.split("\n")[0].trim();
21919
+ return text.length > 40 ? `${text.slice(0, 40)}...` : text;
21920
+ }
21921
+ function width(node) {
21922
+ return node.endIndex - node.startIndex;
21923
+ }
21924
+ var PythonWhySession = class {
21925
+ root;
21926
+ db = new import_datalog7.Database();
21927
+ locations = /* @__PURE__ */ new Map();
21928
+ trees = /* @__PURE__ */ new Map();
21929
+ constructor(options) {
21930
+ this.root = import_node_path4.default.resolve(options.dir);
21931
+ const roots = [this.root];
21932
+ for (const file2 of findPythonFiles(this.root)) {
21933
+ const source = import_node_fs4.default.readFileSync(file2, "utf8");
21934
+ const root = parsePythonSync(source).rootNode;
21935
+ this.trees.set(file2, root);
21936
+ emitModuleImportFacts(this.db, file2, bindModule(root), { roots });
21937
+ emitValueFacts(this.db, file2, root);
21938
+ indexFile(file2, root, this.locations);
21939
+ }
21940
+ }
21941
+ /**
21942
+ * The smallest expression on `line` of `file` whose text is exactly
21943
+ * `text`, or null.
21944
+ */
21945
+ findExpression(file2, line, text) {
21946
+ const root = this.rootOf(file2);
21947
+ if (root === null) {
21948
+ return null;
21949
+ }
21950
+ let found = null;
21951
+ const visit = (node) => {
21952
+ if (node.startPosition.row + 1 === line && node.text === text) {
21953
+ if (found === null || width(node) <= width(found)) {
21954
+ found = node;
21955
+ }
21956
+ }
21957
+ for (const child of namedChildrenOf(node)) {
21958
+ visit(child);
21959
+ }
21960
+ };
21961
+ visit(root);
21962
+ return found === null ? null : { file: this.pathOf(file2), node: found };
21963
+ }
21964
+ /**
21965
+ * The callee of the call written as `calleeText` between `startLine`
21966
+ * and `endLine` of `file`, or null.
21967
+ */
21968
+ findCallee(file2, startLine, endLine, calleeText2) {
21969
+ const root = this.rootOf(file2);
21970
+ if (root === null) {
21971
+ return null;
21972
+ }
21973
+ let found = null;
21974
+ const visit = (node) => {
21975
+ if (node.type === "call") {
21976
+ const line = node.startPosition.row + 1;
21977
+ const callee = field(node, "function");
21978
+ if (line >= startLine && line <= endLine && callee !== null && callee.text === calleeText2) {
21979
+ found = callee;
21980
+ }
21981
+ }
21982
+ for (const child of namedChildrenOf(node)) {
21983
+ visit(child);
21984
+ }
21985
+ };
21986
+ visit(root);
21987
+ return found === null ? null : { file: this.pathOf(file2), node: found };
21988
+ }
21989
+ /**
21990
+ * Why `value` resolves to what it does: the witness proof, flattened
21991
+ * to the chain and rendered. Null when the value does not resolve,
21992
+ * which the caller says in its own words.
21993
+ */
21994
+ explain(value, options = {}) {
21995
+ return explainResolvedKey({
21996
+ db: this.db,
21997
+ rules: RESOLUTION_RULES,
21998
+ key: readKey(value.file, value.node, enclosingFunction(value.node)),
21999
+ locate: (key) => this.locate(key),
22000
+ displayPath: (key) => this.displayPath(key),
22001
+ ...options
22002
+ });
22003
+ }
22004
+ locate(key) {
22005
+ const located = this.locations.get(key);
22006
+ if (located === void 0) {
22007
+ return null;
22008
+ }
22009
+ return {
22010
+ name: displayNameOf(located.node),
22011
+ file: this.displayPath(located.file),
22012
+ line: located.node.startPosition.row + 1
22013
+ };
22014
+ }
22015
+ rootOf(file2) {
22016
+ return this.trees.get(this.pathOf(file2)) ?? null;
22017
+ }
22018
+ pathOf(file2) {
22019
+ return import_node_path4.default.isAbsolute(file2) ? file2 : import_node_path4.default.resolve(this.root, file2);
22020
+ }
22021
+ /** A file path said relative to the root, or as given when it is outside the root. */
22022
+ displayPath(key) {
22023
+ if (key.startsWith(this.root)) {
22024
+ return import_node_path4.default.relative(this.root, key);
22025
+ }
22026
+ return key;
22027
+ }
22028
+ };
22029
+
22030
+ // src/stubEvidence.ts
22031
+ var import_node_fs5 = __toESM(require("fs"), 1);
22032
+ var import_node_path5 = __toESM(require("path"), 1);
22033
+ function importsSubmoduleOf(module2, packageName) {
22034
+ return module2 === packageName || module2.startsWith(`${packageName}.`);
22035
+ }
22036
+ function importStatementModules(stmt) {
22037
+ const modules = [];
22038
+ for (const nameNode of fields(stmt, "name")) {
22039
+ if (nameNode.type === "aliased_import") {
22040
+ const dotted = field(nameNode, "name");
22041
+ if (dotted !== null) {
22042
+ modules.push(dotted.text);
22043
+ }
20588
22044
  continue;
20589
22045
  }
20590
- if (child.type === "call") {
20591
- found.push(child);
22046
+ if (nameNode.type === "dotted_name") {
22047
+ modules.push(nameNode.text);
20592
22048
  }
20593
- callsUnder(child, found);
20594
22049
  }
20595
- return found;
22050
+ return modules;
20596
22051
  }
20597
- var REACHES_STORAGE_RULES = [
20598
- (0, import_datalog4.rule)(
20599
- "reachesStorage",
20600
- [(0, import_datalog4.variable)("f")],
20601
- [(0, import_datalog4.lit)("defCallsName", (0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("m")), (0, import_datalog4.lit)("queryStart", (0, import_datalog4.variable)("m"))]
20602
- ),
20603
- (0, import_datalog4.rule)(
20604
- "reachesStorage",
20605
- [(0, import_datalog4.variable)("f")],
20606
- [(0, import_datalog4.lit)("defCallsName", (0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("m")), (0, import_datalog4.lit)("reachesStorage", (0, import_datalog4.variable)("m"))]
20607
- )
20608
- ];
20609
- function functionsReachingStorage(db, definitions, couldMatch) {
20610
- for (const node of definitions.values()) {
20611
- const name = field(node, "name");
20612
- if (name === null) {
22052
+ function importFromStatementEntries(stmt) {
22053
+ const moduleNode = field(stmt, "module_name");
22054
+ if (moduleNode === null || moduleNode.type === "relative_import") {
22055
+ return [];
22056
+ }
22057
+ const module2 = moduleNode.text;
22058
+ const entries = [];
22059
+ for (const nameNode of fields(stmt, "name")) {
22060
+ if (nameNode.type === "aliased_import") {
22061
+ const original = field(nameNode, "name");
22062
+ if (original !== null) {
22063
+ entries.push({ module: module2, name: original.text });
22064
+ }
20613
22065
  continue;
20614
22066
  }
20615
- for (const called of callsUnder(node).map(calledName)) {
20616
- db.add("defCallsName", [name.text, called]);
22067
+ if (nameNode.type === "dotted_name") {
22068
+ entries.push({ module: module2, name: nameNode.text });
20617
22069
  }
20618
22070
  }
20619
- for (const name of couldMatch) {
20620
- db.add("queryStart", [name]);
22071
+ return entries;
22072
+ }
22073
+ function importSitesIn(root) {
22074
+ const found = [];
22075
+ const visit = (node) => {
22076
+ if (node.type === "import_statement") {
22077
+ const line = rangeOf(node).start;
22078
+ for (const module2 of importStatementModules(node)) {
22079
+ found.push({ module: module2, name: null, line });
22080
+ }
22081
+ } else if (node.type === "import_from_statement") {
22082
+ const line = rangeOf(node).start;
22083
+ for (const entry of importFromStatementEntries(node)) {
22084
+ found.push({ module: entry.module, name: entry.name, line });
22085
+ }
22086
+ }
22087
+ for (const child of node.namedChildren) {
22088
+ if (child !== null) {
22089
+ visit(child);
22090
+ }
22091
+ }
22092
+ };
22093
+ visit(root);
22094
+ return found;
22095
+ }
22096
+ async function pythonImportEvidence(options) {
22097
+ const byModule = /* @__PURE__ */ new Map();
22098
+ for (const file2 of findPythonFiles(options.directory)) {
22099
+ const source = import_node_fs5.default.readFileSync(file2, "utf8");
22100
+ const tree = await parsePython(source);
22101
+ const displayPath = import_node_path5.default.relative(options.directory, file2);
22102
+ for (const site of importSitesIn(tree.rootNode)) {
22103
+ if (!importsSubmoduleOf(site.module, options.packageName)) {
22104
+ continue;
22105
+ }
22106
+ const sites = byModule.get(site.module) ?? [];
22107
+ sites.push({ name: site.name, file: displayPath, line: site.line });
22108
+ byModule.set(site.module, sites);
22109
+ }
20621
22110
  }
20622
- (0, import_datalog4.evaluate)(db, REACHES_STORAGE_RULES);
20623
- return new Set(db.facts("reachesStorage").map((row) => String(row[0])));
22111
+ return [...byModule.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([module2, sites]) => ({ module: module2, sites }));
20624
22112
  }
20625
22113
  // Annotate the CommonJS export names for ESM import in node:
20626
22114
  0 && (module.exports = {
22115
+ ADAPTER_VERSION,
22116
+ PythonWhySession,
20627
22117
  annotationToShape,
20628
22118
  bindModule,
20629
22119
  buildRouterIndex,
@@ -20638,6 +22128,10 @@ function functionsReachingStorage(db, definitions, couldMatch) {
20638
22128
  nodeId,
20639
22129
  objectReturnedBy,
20640
22130
  parsePython,
22131
+ parsePythonSync,
22132
+ preloadPythonGrammar,
22133
+ pythonImportEvidence,
22134
+ readKey,
20641
22135
  resolveAbsoluteModule,
20642
22136
  resolveCalls,
20643
22137
  resolveModule,