@suss/resolution 0.30.0 → 0.31.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.js CHANGED
@@ -1,7 +1,18 @@
1
1
  // src/program.ts
2
- import { clearRelations, deriveOnDemand, evaluate } from "@suss/datalog";
2
+ import {
3
+ BudgetExhausted,
4
+ chargeAbandoned,
5
+ chargeQuestion,
6
+ clearRelations,
7
+ deriveOnDemand,
8
+ evaluate,
9
+ rowBudget,
10
+ tupleKey
11
+ } from "@suss/datalog";
3
12
  var ASKING_RELATIONS = [
4
13
  "wanted",
14
+ "wantedUnder",
15
+ "wantedSites",
5
16
  "wantedOrigin",
6
17
  "wantedCallOrigin",
7
18
  "wantedExportsOf",
@@ -11,8 +22,12 @@ var ASKING_RELATIONS = [
11
22
  ];
12
23
  var NO_LANGUAGE_RULES = [];
13
24
  var programs = /* @__PURE__ */ new WeakMap();
14
- function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
15
- const built = programs.get(languageRules);
25
+ var underPrograms = /* @__PURE__ */ new WeakMap();
26
+ var contextFreeAnswers = () => ANSWER_RELATIONS.filter(
27
+ (relation) => !UNDER_ANSWER_RELATIONS.includes(relation)
28
+ );
29
+ function builtProgram(languageRules, cache, complete) {
30
+ const built = cache.get(languageRules);
16
31
  if (built !== void 0) {
17
32
  return built;
18
33
  }
@@ -21,10 +36,16 @@ function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
21
36
  ...languageRules,
22
37
  ...RESOLUTION_QUESTIONS
23
38
  ];
24
- const program = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? { rules, demandDriven: [], demands: [] } : deriveOnDemand(rules, ANSWER_RELATIONS);
25
- programs.set(languageRules, program);
39
+ const program = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? { rules, demandDriven: [], demands: [] } : deriveOnDemand(rules, complete);
40
+ cache.set(languageRules, program);
26
41
  return program;
27
42
  }
43
+ function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
44
+ return builtProgram(languageRules, programs, contextFreeAnswers());
45
+ }
46
+ function resolutionUnderProgram(languageRules = NO_LANGUAGE_RULES) {
47
+ return builtProgram(languageRules, underPrograms, ANSWER_RELATIONS);
48
+ }
28
49
  function queryFacts(program) {
29
50
  if (program.demandDriven.length === 0) {
30
51
  return [];
@@ -61,6 +82,75 @@ function askResolution(db, keys, asking = "wanted", program = resolutionProgram(
61
82
  clearRelations(db, program.rules, forget);
62
83
  }
63
84
  }
85
+ var askedUnderByDb = /* @__PURE__ */ new WeakMap();
86
+ var UNDER_QUESTION_ROW_BUDGET = 2e7;
87
+ var UNDER_RUN_ROW_BUDGET = 4e7;
88
+ var spentUnderByDb = /* @__PURE__ */ new WeakMap();
89
+ function spendOf(db) {
90
+ let spend = spentUnderByDb.get(db);
91
+ if (spend === void 0) {
92
+ spend = { rows: 0, asked: 0, abandoned: 0, skipped: 0 };
93
+ spentUnderByDb.set(db, spend);
94
+ }
95
+ return spend;
96
+ }
97
+ function underQuestionSpend(db) {
98
+ return { ...spendOf(db) };
99
+ }
100
+ function askResolutionUnder(db, pairs, program = resolutionUnderProgram(), questionBudget = UNDER_QUESTION_ROW_BUDGET, runBudget = UNDER_RUN_ROW_BUDGET) {
101
+ let asked = askedUnderByDb.get(db);
102
+ if (asked === void 0) {
103
+ asked = /* @__PURE__ */ new Set();
104
+ askedUnderByDb.set(db, asked);
105
+ }
106
+ const fresh = [...pairs].filter(
107
+ ([key, site]) => !asked.has(tupleKey([key, site]))
108
+ );
109
+ if (fresh.length === 0) {
110
+ return "answered";
111
+ }
112
+ const spend = spendOf(db);
113
+ for (const [key, site] of fresh) {
114
+ asked.add(tupleKey([key, site]));
115
+ }
116
+ spend.asked += 1;
117
+ if (spend.rows >= runBudget) {
118
+ spend.skipped += 1;
119
+ chargeQuestion("skipped");
120
+ return "abandoned";
121
+ }
122
+ for (const [key, site] of fresh) {
123
+ db.add("wantedUnder", [key, site]);
124
+ }
125
+ const forget = queryFacts(program);
126
+ const clearDerived = () => {
127
+ if (forget.length > 0) {
128
+ clearRelations(db, program.rules, forget);
129
+ }
130
+ };
131
+ const budget = rowBudget(questionBudget);
132
+ try {
133
+ evaluate(db, program.rules, void 0, budget);
134
+ } catch (error) {
135
+ if (!(error instanceof BudgetExhausted)) {
136
+ throw error;
137
+ }
138
+ spend.rows += error.examined;
139
+ spend.abandoned += 1;
140
+ chargeQuestion("abandoned");
141
+ db.retract("wantedUnder", fresh);
142
+ clearDerived();
143
+ chargeAbandoned(
144
+ fresh.map(([key, site]) => `${key} under ${site}`).join(", "),
145
+ error.examined
146
+ );
147
+ return "abandoned";
148
+ }
149
+ spend.rows += budget.examined;
150
+ chargeQuestion();
151
+ clearDerived();
152
+ return "answered";
153
+ }
64
154
 
65
155
  // src/callee.ts
66
156
  var UNDECLARED = { kind: "undeclared" };
@@ -489,6 +579,12 @@ var DEFAULT_PHRASES = {
489
579
  "last write": ({ tuple, describe }) => ({
490
580
  reason: `${describe(tuple[0])} is written more than once, and the last write leaves it as ${describe(tuple[1])}`
491
581
  }),
582
+ "one of several writes": ({ tuple, describe }) => ({
583
+ reason: `${describe(tuple[0])} is written more than once, and one of those writes makes it ${describe(tuple[1])}`,
584
+ assumptions: [
585
+ `nothing says which write to ${describe(tuple[0])} ran last, so every write it has is a possible value`
586
+ ]
587
+ }),
492
588
  fallback: ({ tuple, describe }) => ({
493
589
  reason: `${describe(tuple[0])} is a fallback expression, and ${describe(tuple[1])} is the branch that resolves`
494
590
  }),
@@ -505,7 +601,7 @@ var DEFAULT_PHRASES = {
505
601
  "property read": ({ tuple, premises, describe }) => {
506
602
  const [, object, name] = premises[0].tuple;
507
603
  const contains = premises[2];
508
- const inherited = contains.kind === "derived" && contains.premises.length > 1;
604
+ const inherited = contains.kind === "derived" && ruleLabel(contains.rule) === BASE_CLASS_RULE;
509
605
  return {
510
606
  reason: `${describe(tuple[0])} reads ${String(name)} off ${describe(object)}, which contains ${describe(tuple[1])}`,
511
607
  notes: inherited ? [`${String(name)} comes from a base class`] : []
@@ -584,7 +680,8 @@ var CHAIN_RELATIONS = /* @__PURE__ */ new Set([
584
680
  "isWrittenAs",
585
681
  "comesFrom",
586
682
  "reaches",
587
- "stepsTo"
683
+ "stepsTo",
684
+ "hop"
588
685
  ]);
589
686
  function inlineChain(proof, state) {
590
687
  const explained = explainResolutionProof(proof, {
@@ -640,6 +737,17 @@ function importStep(proof, state) {
640
737
  assumptions: []
641
738
  };
642
739
  }
740
+ function namespaceMemberStep(proof, state) {
741
+ const [x, module, name] = proof.tuple;
742
+ return {
743
+ from: x,
744
+ to: module,
745
+ rule: NAMESPACE_MEMBER_RULE,
746
+ reason: `${state.describe(x)} reads ${String(name)} off ${state.describe(module)}, which is imported whole`,
747
+ notes: [],
748
+ assumptions: []
749
+ };
750
+ }
643
751
  var flattenInto = (proof, state) => {
644
752
  if (proof.kind === "truncated") {
645
753
  state.truncated = true;
@@ -649,7 +757,7 @@ var flattenInto = (proof, state) => {
649
757
  return;
650
758
  }
651
759
  const relation = proof.relation;
652
- if (relation === "stepsTo") {
760
+ if (relation === "stepsTo" || relation === "hop") {
653
761
  state.steps.push(stepFrom(proof, state));
654
762
  return;
655
763
  }
@@ -660,6 +768,10 @@ var flattenInto = (proof, state) => {
660
768
  return;
661
769
  }
662
770
  if (relation === "comesFrom") {
771
+ if (ruleLabel(proof.rule) === NAMESPACE_MEMBER_RULE) {
772
+ state.steps.push(namespaceMemberStep(proof, state));
773
+ return;
774
+ }
663
775
  if (proof.premises.length === 2) {
664
776
  flattenInto(proof.premises[0], state);
665
777
  state.steps.push(importStep(proof.premises[1], state));
@@ -836,6 +948,37 @@ function nodeOfKey(rootsByFile, key) {
836
948
  return null;
837
949
  }
838
950
 
951
+ // src/packWords.ts
952
+ function addPackWords(db, words) {
953
+ for (const word of words.givesBackOne ?? []) {
954
+ db.add("givesBackOne", [word.base, word.method]);
955
+ }
956
+ for (const word of words.givesBackOneOfArgument ?? []) {
957
+ db.add("givesBackOneOfArgument", [
958
+ word.base,
959
+ word.method,
960
+ String(word.argument)
961
+ ]);
962
+ }
963
+ for (const word of words.givesBackOneOfImport ?? []) {
964
+ db.add("givesBackOneOfImport", [
965
+ word.module,
966
+ word.name,
967
+ String(word.argument)
968
+ ]);
969
+ }
970
+ for (const word of words.entersAsSelf ?? []) {
971
+ db.add("entersAsSelf", [word.module, word.name]);
972
+ }
973
+ for (const word of words.unwrapsByName ?? []) {
974
+ db.add("unwrapsByName", [word.callee, String(word.argument)]);
975
+ db.add("wrapperModule", [word.callee, word.module]);
976
+ }
977
+ for (const word of words.associationConstructor ?? []) {
978
+ db.add("associationConstructor", [word.module, word.name]);
979
+ }
980
+ }
981
+
839
982
  // src/session.ts
840
983
  import { Database, evaluate as evaluate2, proofOf, witnesses } from "@suss/datalog";
841
984
  function explainResolvedKey(options) {
@@ -891,7 +1034,7 @@ function explainResolvedKey(options) {
891
1034
 
892
1035
  // src/singleAnswer.ts
893
1036
  var NO_PLACEHOLDERS = /* @__PURE__ */ new Set();
894
- function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1037
+ function answersByKey(rows, placeholders = NO_PLACEHOLDERS) {
895
1038
  const candidates = /* @__PURE__ */ new Map();
896
1039
  for (const row of rows) {
897
1040
  const key = String(row[0]);
@@ -903,9 +1046,15 @@ function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
903
1046
  set.add(answer);
904
1047
  candidates.set(key, set);
905
1048
  }
906
- const settled = /* @__PURE__ */ new Map();
1049
+ const answers = /* @__PURE__ */ new Map();
907
1050
  for (const [key, set] of candidates) {
908
- const answers = withoutPlaceholders(set, placeholders);
1051
+ answers.set(key, withoutPlaceholders(set, placeholders));
1052
+ }
1053
+ return answers;
1054
+ }
1055
+ function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1056
+ const settled = /* @__PURE__ */ new Map();
1057
+ for (const [key, answers] of answersByKey(rows, placeholders)) {
909
1058
  if (answers.length === 1) {
910
1059
  settled.set(key, answers[0]);
911
1060
  }
@@ -923,6 +1072,28 @@ function withoutPlaceholders(answers, placeholders) {
923
1072
  return kept;
924
1073
  }
925
1074
 
1075
+ // src/underContext.ts
1076
+ function answersUnder(db, relation, key, site) {
1077
+ return db.lookup(relation, 0, key).filter((row) => String(row[1]) === site).map((row) => String(row[2]));
1078
+ }
1079
+ function isWrittenAsUnder(db, key, site) {
1080
+ return answersUnder(db, "wantedIsWrittenAsUnder", key, site);
1081
+ }
1082
+ function comesToUnder(db, key, site) {
1083
+ return answersUnder(db, "wantedComesToUnder", key, site);
1084
+ }
1085
+ function objectOfUnder(db, key, site) {
1086
+ return answersUnder(db, "wantedObjectOfUnder", key, site);
1087
+ }
1088
+ function writtenValueUnder(db, key, site) {
1089
+ const rows = isWrittenAsUnder(db, key, site).map((answer) => [key, answer]);
1090
+ const answers = answersByKey(rows, placeholderValues(db)).get(key) ?? [];
1091
+ return answers.length === 1 ? answers[0] : null;
1092
+ }
1093
+ function allocationSitesOf(db, cls) {
1094
+ return db.lookup("wantedAllocatedAt", 0, cls).map((row) => String(row[1]));
1095
+ }
1096
+
926
1097
  // src/writes.ts
927
1098
  function valueLeftByWrites(writes, ordered) {
928
1099
  const deciding = writes.filter((write) => !write.narrowsName);
@@ -953,23 +1124,58 @@ function sharedConstruction(writes) {
953
1124
  }
954
1125
 
955
1126
  // src/writtenValue.ts
956
- function writtenValueOf(db, key, ask) {
957
- const placeholders = placeholderValues(db);
958
- const direct = singleAnswers(db.facts("wantedIsWrittenAs"), placeholders).get(
959
- key
960
- );
961
- if (direct === void 0) {
962
- return null;
1127
+ var WRITTEN_AS = "wantedIsWrittenAs";
1128
+ function writtenValueOf(db, key, ask, relation = WRITTEN_AS) {
1129
+ const answers = settledByKey(db, [key], ask, relation).get(key) ?? [];
1130
+ return answers.length === 1 ? answers[0] : null;
1131
+ }
1132
+ function writtenValuesOf(db, key, ask, relation = WRITTEN_AS) {
1133
+ const settled = settledByKey(db, [key], ask, relation).get(key) ?? [];
1134
+ return settled.length > 0 ? settled : writesLeft(db, key);
1135
+ }
1136
+ function writtenValuesByKey(db, keys, ask, relation = WRITTEN_AS) {
1137
+ const one = /* @__PURE__ */ new Map();
1138
+ for (const [key, answers] of settledByKey(db, keys, ask, relation)) {
1139
+ if (answers.length === 1) {
1140
+ one.set(key, answers[0]);
1141
+ }
963
1142
  }
964
- const isCall = db.facts("call").some((row) => String(row[0]) === direct);
965
- if (!isCall) {
1143
+ return one;
1144
+ }
1145
+ function settledByKey(db, keys, ask, relation) {
1146
+ const placeholders = placeholderValues(db);
1147
+ const byKey = answersByKey(db.facts(relation), placeholders);
1148
+ const direct = new Map(keys.map((key) => [key, byKey.get(key) ?? []]));
1149
+ const calls = new Set(db.facts("call").map((row) => String(row[0])));
1150
+ const throughCalls = [...direct.values()].flat().filter((answer) => calls.has(answer));
1151
+ if (throughCalls.length === 0) {
966
1152
  return direct;
967
1153
  }
968
- ask([direct]);
969
- const deeper = singleAnswers(db.facts("wantedIsWrittenAs"), placeholders).get(
970
- direct
1154
+ ask(throughCalls);
1155
+ const deeper = singleAnswers(db.facts(relation), placeholders);
1156
+ return new Map(
1157
+ [...direct].map(([key, answers]) => [
1158
+ key,
1159
+ collapseCalls(answers, calls, deeper)
1160
+ ])
971
1161
  );
972
- return deeper ?? direct;
1162
+ }
1163
+ function collapseCalls(direct, calls, deeper) {
1164
+ return [
1165
+ ...new Set(
1166
+ direct.map(
1167
+ (answer) => calls.has(answer) ? deeper.get(answer) ?? answer : answer
1168
+ )
1169
+ )
1170
+ ];
1171
+ }
1172
+ function writesLeft(db, key) {
1173
+ const unstated = db.facts("writesUnstated").some((row) => String(row[0]) === key);
1174
+ if (unstated) {
1175
+ return [];
1176
+ }
1177
+ const candidates = answersByKey(db.facts("mayHold"), placeholderValues(db)).get(key) ?? [];
1178
+ return candidates.length > 1 ? candidates : [];
973
1179
  }
974
1180
 
975
1181
  // src/index.ts
@@ -977,38 +1183,61 @@ import { constant, lit, rule, variable as v } from "@suss/datalog";
977
1183
  var VALUE_STEP = constant("value");
978
1184
  var NAMESPACE_IMPORT_NAME = "*";
979
1185
  var NAMESPACE_IMPORT = constant(NAMESPACE_IMPORT_NAME);
1186
+ var NAMESPACE_MEMBER_RULE = "namespace member";
1187
+ var BASE_CLASS_RULE = "base class";
1188
+ var INSTANCE_STEP = constant("instance");
980
1189
  var RESULT_STEP = constant("result");
981
- var RESOLUTION_RULES = [
1190
+ var NO_CONTEXT_NAME = "none";
1191
+ var NO_CONTEXT = constant(NO_CONTEXT_NAME);
1192
+ var STEP_LATTICE = [
1193
+ { soFar: VALUE_STEP, next: v("kind"), took: v("kind") },
1194
+ { soFar: INSTANCE_STEP, next: VALUE_STEP, took: INSTANCE_STEP },
1195
+ { soFar: INSTANCE_STEP, next: INSTANCE_STEP, took: INSTANCE_STEP },
1196
+ { soFar: INSTANCE_STEP, next: RESULT_STEP, took: RESULT_STEP },
1197
+ { soFar: RESULT_STEP, next: VALUE_STEP, took: RESULT_STEP },
1198
+ { soFar: RESULT_STEP, next: INSTANCE_STEP, took: RESULT_STEP },
1199
+ { soFar: RESULT_STEP, next: RESULT_STEP, took: RESULT_STEP }
1200
+ ];
1201
+ var STATED_RULES = [
982
1202
  // Aliasing: const x = y, or an identifier referencing a declaration.
983
1203
  // A language with a hop of its own, like JavaScript's `.bind`, states
984
1204
  // it as a step too, or every question but `comesTo` misses it.
985
1205
  rule(
986
- "stepsTo",
1206
+ "hop",
987
1207
  [v("x"), v("y"), VALUE_STEP],
988
1208
  [lit("binds", v("x"), v("y"))],
989
1209
  "alias"
990
1210
  ),
991
1211
  // A name written more than once has the value the last write left
992
- // there. The adapter works out which write that is and stays quiet
993
- // when control flow decides, so such a name steps nowhere at all.
1212
+ // there. The adapter works out which write that is, and stays quiet
1213
+ // when control flow decides; the rule below takes that name instead.
994
1214
  rule(
995
- "stepsTo",
1215
+ "hop",
996
1216
  [v("x"), v("y"), VALUE_STEP],
997
1217
  [lit("endsHolding", v("x"), v("y"))],
998
1218
  "last write"
999
1219
  ),
1220
+ // A name whose writes nothing orders leads to each of them, so a
1221
+ // caller that can use several values gets them all and one that needs
1222
+ // a single value gets none. A write stating no value stops every step.
1223
+ rule(
1224
+ "hop",
1225
+ [v("x"), v("y"), VALUE_STEP],
1226
+ [lit("mayHold", v("x"), v("y")), lit("writesAllStated", v("x"))],
1227
+ "one of several writes"
1228
+ ),
1000
1229
  // A fallback says the value is one of its branches, so each branch is
1001
1230
  // a step. A branch that resolves to nothing makes no claim, and two
1002
1231
  // branches resolving to different things fail the single-answer policy.
1003
1232
  rule(
1004
- "stepsTo",
1233
+ "hop",
1005
1234
  [v("x"), v("b"), VALUE_STEP],
1006
1235
  [lit("fallbackBranch", v("x"), v("b"))],
1007
1236
  "fallback"
1008
1237
  ),
1009
1238
  // An import steps to what the module exports under that name.
1010
1239
  rule(
1011
- "stepsTo",
1240
+ "hop",
1012
1241
  [v("x"), v("value"), VALUE_STEP],
1013
1242
  [
1014
1243
  lit("imports", v("x"), v("m"), v("n")),
@@ -1042,8 +1271,8 @@ var RESOLUTION_RULES = [
1042
1271
  // a method read off the result is the one the class declares. The
1043
1272
  // caveat below is about a factory function, and a class is not one.
1044
1273
  rule(
1045
- "stepsTo",
1046
- [v("r"), v("cls"), VALUE_STEP],
1274
+ "hop",
1275
+ [v("r"), v("cls"), INSTANCE_STEP],
1047
1276
  [
1048
1277
  lit("call", v("r"), v("c")),
1049
1278
  lit("comesTo", v("c"), v("cls")),
@@ -1051,12 +1280,21 @@ var RESOLUTION_RULES = [
1051
1280
  ],
1052
1281
  "class instance"
1053
1282
  ),
1283
+ // The receiver a method is called on. Being one of a class is the
1284
+ // same hop a construction takes, so a method read off `self` finds
1285
+ // what the class declares.
1286
+ rule(
1287
+ "hop",
1288
+ [v("x"), v("cls"), INSTANCE_STEP],
1289
+ [lit("instanceOf", v("x"), v("cls"))],
1290
+ "receiver instance"
1291
+ ),
1054
1292
  // A finder the library declares, keyed on the base a pack named so a
1055
1293
  // project class with a method of the same name on another hierarchy is
1056
1294
  // left alone. The DESIGN says what a chain of them composes into.
1057
1295
  rule(
1058
- "stepsTo",
1059
- [v("r"), v("cls"), VALUE_STEP],
1296
+ "hop",
1297
+ [v("r"), v("cls"), INSTANCE_STEP],
1060
1298
  [
1061
1299
  lit("call", v("r"), v("c")),
1062
1300
  lit("readsProperty", v("c"), v("o"), v("m")),
@@ -1073,8 +1311,8 @@ var RESOLUTION_RULES = [
1073
1311
  // instead of as the receiver. The argument having to reach the base a
1074
1312
  // pack named is what keeps an unrelated `get` out.
1075
1313
  rule(
1076
- "stepsTo",
1077
- [v("r"), v("cls"), VALUE_STEP],
1314
+ "hop",
1315
+ [v("r"), v("cls"), INSTANCE_STEP],
1078
1316
  [
1079
1317
  lit("call", v("r"), v("c")),
1080
1318
  lit("readsProperty", v("c"), v("o"), v("m")),
@@ -1091,8 +1329,8 @@ var RESOLUTION_RULES = [
1091
1329
  // anything, keyed on the module it was imported from so a project
1092
1330
  // function spelled the same way is not mistaken for it.
1093
1331
  rule(
1094
- "stepsTo",
1095
- [v("r"), v("cls"), VALUE_STEP],
1332
+ "hop",
1333
+ [v("r"), v("cls"), INSTANCE_STEP],
1096
1334
  [
1097
1335
  lit("call", v("r"), v("c")),
1098
1336
  lit("comesFrom", v("c"), v("mod"), v("n")),
@@ -1102,10 +1340,24 @@ var RESOLUTION_RULES = [
1102
1340
  ],
1103
1341
  "declared import finder"
1104
1342
  ),
1343
+ // A block opened over a library's own constructor. The pack says
1344
+ // entering one gives back the object it built, so the name the block
1345
+ // opens is that call.
1346
+ rule(
1347
+ "hop",
1348
+ [v("y"), v("r"), VALUE_STEP],
1349
+ [
1350
+ lit("entersAs", v("y"), v("r")),
1351
+ lit("call", v("r"), v("c")),
1352
+ lit("comesFrom", v("c"), v("mod"), v("n")),
1353
+ lit("entersAsSelf", v("mod"), v("n"))
1354
+ ],
1355
+ "context manager returns self"
1356
+ ),
1105
1357
  // Wrapper transparency, derived: calling a factory that returns a
1106
1358
  // function which calls its parameter k steps to argument k.
1107
1359
  rule(
1108
- "stepsTo",
1360
+ "hop",
1109
1361
  [v("r"), v("a"), VALUE_STEP],
1110
1362
  [
1111
1363
  lit("call", v("r"), v("c")),
@@ -1119,7 +1371,7 @@ var RESOLUTION_RULES = [
1119
1371
  // argument k. The callee has to come from the library the pack said,
1120
1372
  // so a local object spelled the same way is not mistaken for it.
1121
1373
  rule(
1122
- "stepsTo",
1374
+ "hop",
1123
1375
  [v("r"), v("a"), VALUE_STEP],
1124
1376
  [
1125
1377
  lit("calleeName", v("r"), v("n")),
@@ -1133,44 +1385,441 @@ var RESOLUTION_RULES = [
1133
1385
  // The one step that runs a function forwards: a call steps to what
1134
1386
  // the function it invokes returns.
1135
1387
  rule(
1136
- "stepsTo",
1388
+ "hop",
1137
1389
  [v("r"), v("ret"), RESULT_STEP],
1138
1390
  [lit("invokes", v("r"), v("f")), lit("returnsValue", v("f"), v("ret"))],
1139
1391
  "call result"
1140
1392
  ),
1141
- // Where a walk gets to, and whether it ran a call on the way. The walk
1142
- // so far comes first, so demand stays on the value asked about; the
1143
- // README says what the other order cost.
1393
+ // What a function says it gives back when its body never states a
1394
+ // value. The adapter stays quiet about the annotation whenever the
1395
+ // body does state one, so the two never answer the same question.
1396
+ rule(
1397
+ "hop",
1398
+ [v("r"), v("cls"), RESULT_STEP],
1399
+ [
1400
+ lit("invokes", v("r"), v("f")),
1401
+ lit("returnsClass", v("f"), v("x")),
1402
+ lit("comesTo", v("x"), v("cls")),
1403
+ lit("objectValue", v("cls"))
1404
+ ],
1405
+ "declared return type"
1406
+ ),
1407
+ // Where a walk gets to, under the strongest step it took: value, then
1408
+ // instance, then result. The walk so far comes first, so demand stays
1409
+ // on the value asked about; the README says what the other order cost.
1144
1410
  rule(
1145
1411
  "reaches",
1146
1412
  [v("x"), v("z"), v("kind")],
1147
1413
  [lit("stepsTo", v("x"), v("z"), v("kind"))]
1148
1414
  ),
1415
+ // A rule per pair rather than one leaving the next step's kind free. A
1416
+ // free kind is a second question about the same relation, and a
1417
+ // demand-driven run then derives both to answer either.
1418
+ ...STEP_LATTICE.map(
1419
+ ({ soFar, next, took }) => rule(
1420
+ "reaches",
1421
+ [v("x"), v("z"), took],
1422
+ [
1423
+ lit("reaches", v("x"), v("y"), soFar),
1424
+ lit("stepsTo", v("y"), v("z"), next)
1425
+ ]
1426
+ )
1427
+ ),
1428
+ // A context is an allocation site, or nothing at all for a value read
1429
+ // outside every site. Both are stated, so a rule that starts a walk
1430
+ // binds its context from a premise rather than from a negation.
1431
+ rule("context", [NO_CONTEXT], [], "no context"),
1432
+ rule(
1433
+ "context",
1434
+ [v("site")],
1435
+ [lit("allocates", v("site"), v("cls"))],
1436
+ "site context"
1437
+ ),
1438
+ // The same closure, with the context the walk started under and the
1439
+ // context it arrived under. It takes `hop`, because an argument and a
1440
+ // property read have their own rules below.
1441
+ rule(
1442
+ "reachesUnder",
1443
+ [v("x"), v("c"), v("z"), v("c"), v("kind")],
1444
+ [lit("context", v("c")), lit("hop", v("x"), v("z"), v("kind"))],
1445
+ "under one hop"
1446
+ ),
1447
+ ...STEP_LATTICE.map(
1448
+ ({ soFar, next, took }) => rule(
1449
+ "reachesUnder",
1450
+ [v("x"), v("c"), v("z"), v("c2"), took],
1451
+ [
1452
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), soFar),
1453
+ lit("hop", v("y"), v("z"), next)
1454
+ ]
1455
+ )
1456
+ ),
1457
+ // The receiver read under a site is that site. The ordinary hop from a
1458
+ // receiver to its class stays in `hop`, so `self` under any context
1459
+ // still comes to the class a single-answer reader wants.
1149
1460
  rule(
1150
- "reaches",
1151
- [v("x"), v("z"), v("kind")],
1461
+ "reachesUnder",
1462
+ [v("x"), v("c"), v("c"), v("c"), VALUE_STEP],
1463
+ [lit("instanceOf", v("x"), v("cls")), lit("allocates", v("c"), v("cls"))],
1464
+ "receiver is the site"
1465
+ ),
1466
+ rule(
1467
+ "reachesUnder",
1468
+ [v("x"), v("c"), v("c2"), v("c2"), v("kind")],
1152
1469
  [
1153
- lit("reaches", v("x"), v("y"), VALUE_STEP),
1154
- lit("stepsTo", v("y"), v("z"), v("kind"))
1470
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), v("kind")),
1471
+ lit("instanceOf", v("y"), v("cls")),
1472
+ lit("allocates", v("c2"), v("cls"))
1473
+ ],
1474
+ "reached receiver is the site"
1475
+ ),
1476
+ // The object an expression refers to under a context. A site is an
1477
+ // answer here, unlike `objectOf`, and the class an instance is one of
1478
+ // is an answer only under no context.
1479
+ rule(
1480
+ "objectOfUnder",
1481
+ [v("site"), v("c"), v("site")],
1482
+ [lit("context", v("c")), lit("allocates", v("site"), v("cls"))],
1483
+ "site is its own object"
1484
+ ),
1485
+ rule(
1486
+ "objectOfUnder",
1487
+ [v("o"), v("c"), v("site")],
1488
+ [
1489
+ lit("reachesUnder", v("o"), v("c"), v("site"), v("c2"), VALUE_STEP),
1490
+ lit("allocates", v("site"), v("cls"))
1491
+ ],
1492
+ "name of a site"
1493
+ ),
1494
+ rule(
1495
+ "objectOfUnder",
1496
+ [v("o"), v("c"), v("obj")],
1497
+ [
1498
+ lit("reachesUnder", v("o"), v("c"), v("obj"), v("c2"), VALUE_STEP),
1499
+ lit("objectValue", v("obj"))
1500
+ ],
1501
+ "written object under a context"
1502
+ ),
1503
+ rule(
1504
+ "objectOfUnder",
1505
+ [v("o"), v("c"), v("obj")],
1506
+ [
1507
+ lit("reachesUnder", v("o"), v("c"), v("obj"), v("c2"), RESULT_STEP),
1508
+ lit("objectValue", v("obj"))
1509
+ ],
1510
+ "returned object under a context"
1511
+ ),
1512
+ rule(
1513
+ "objectOfUnder",
1514
+ [v("o"), NO_CONTEXT, v("cls")],
1515
+ [
1516
+ lit(
1517
+ "reachesUnder",
1518
+ v("o"),
1519
+ NO_CONTEXT,
1520
+ v("cls"),
1521
+ NO_CONTEXT,
1522
+ INSTANCE_STEP
1523
+ ),
1524
+ lit("objectValue", v("cls"))
1525
+ ],
1526
+ "class reached with no context"
1527
+ ),
1528
+ rule(
1529
+ "objectOfUnder",
1530
+ [v("o"), NO_CONTEXT, v("cls")],
1531
+ [lit("instanceOf", v("o"), v("cls"))],
1532
+ "receiver class with no context"
1533
+ ),
1534
+ rule(
1535
+ "objectOfUnder",
1536
+ [v("o"), v("c"), v("o")],
1537
+ [lit("context", v("c")), lit("objectValue", v("o"))],
1538
+ "object under a context is itself"
1539
+ ),
1540
+ // A property read goes on under the site the object was made at, so a
1541
+ // field read off two constructions gives each one its own value. A
1542
+ // class or a literal object keeps the context the walk was already in.
1543
+ rule(
1544
+ "reachesUnder",
1545
+ [v("x"), v("c"), v("held"), v("site"), VALUE_STEP],
1546
+ [
1547
+ lit("context", v("c")),
1548
+ lit("readsProperty", v("x"), v("o"), v("n")),
1549
+ lit("objectOfUnder", v("o"), v("c"), v("site")),
1550
+ lit("allocates", v("site"), v("cls")),
1551
+ lit("contains", v("site"), v("n"), v("held"))
1552
+ ],
1553
+ "property read at a site"
1554
+ ),
1555
+ rule(
1556
+ "reachesUnder",
1557
+ [v("x"), v("c"), v("held"), v("c"), VALUE_STEP],
1558
+ [
1559
+ lit("context", v("c")),
1560
+ lit("readsProperty", v("x"), v("o"), v("n")),
1561
+ lit("objectOfUnder", v("o"), v("c"), v("obj")),
1562
+ lit("objectValue", v("obj")),
1563
+ lit("contains", v("obj"), v("n"), v("held"))
1564
+ ],
1565
+ "property read off an object"
1566
+ ),
1567
+ rule(
1568
+ "reachesUnder",
1569
+ [v("x"), v("c"), v("held"), v("site"), v("kind")],
1570
+ [
1571
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), v("kind")),
1572
+ lit("readsProperty", v("y"), v("o"), v("n")),
1573
+ lit("objectOfUnder", v("o"), v("c2"), v("site")),
1574
+ lit("allocates", v("site"), v("cls")),
1575
+ lit("contains", v("site"), v("n"), v("held"))
1576
+ ],
1577
+ "reached property read at a site"
1578
+ ),
1579
+ rule(
1580
+ "reachesUnder",
1581
+ [v("x"), v("c"), v("held"), v("c2"), v("kind")],
1582
+ [
1583
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), v("kind")),
1584
+ lit("readsProperty", v("y"), v("o"), v("n")),
1585
+ lit("objectOfUnder", v("o"), v("c2"), v("obj")),
1586
+ lit("objectValue", v("obj")),
1587
+ lit("contains", v("obj"), v("n"), v("held"))
1588
+ ],
1589
+ "reached property read off an object"
1590
+ ),
1591
+ // A parameter goes on at the argument, under the context its caller
1592
+ // was read in, and only from the calls that run the function under the
1593
+ // context the walk is in. This is the one hop that changes context.
1594
+ rule(
1595
+ "reachesUnder",
1596
+ [v("p"), v("c"), v("a"), v("c3"), VALUE_STEP],
1597
+ [
1598
+ lit("context", v("c")),
1599
+ lit("paramOf", v("f"), v("i"), v("p")),
1600
+ lit("entersUnder", v("r"), v("f"), v("c"), v("c3")),
1601
+ lit("callArg", v("r"), v("i"), v("a"))
1602
+ ],
1603
+ "argument under a context"
1604
+ ),
1605
+ rule(
1606
+ "reachesUnder",
1607
+ [v("p"), v("c"), v("a"), v("c3"), VALUE_STEP],
1608
+ [
1609
+ lit("context", v("c")),
1610
+ lit("paramNamed", v("f"), v("n"), v("p")),
1611
+ lit("entersUnder", v("r"), v("f"), v("c"), v("c3")),
1612
+ lit("callKeywordArg", v("r"), v("n"), v("a"))
1613
+ ],
1614
+ "keyword argument under a context"
1615
+ ),
1616
+ rule(
1617
+ "reachesUnder",
1618
+ [v("x"), v("c"), v("a"), v("c3"), v("kind")],
1619
+ [
1620
+ lit("reachesUnder", v("x"), v("c"), v("p"), v("c2"), v("kind")),
1621
+ lit("paramOf", v("f"), v("i"), v("p")),
1622
+ lit("entersUnder", v("r"), v("f"), v("c2"), v("c3")),
1623
+ lit("callArg", v("r"), v("i"), v("a"))
1624
+ ],
1625
+ "reached argument under a context"
1626
+ ),
1627
+ rule(
1628
+ "reachesUnder",
1629
+ [v("x"), v("c"), v("a"), v("c3"), v("kind")],
1630
+ [
1631
+ lit("reachesUnder", v("x"), v("c"), v("p"), v("c2"), v("kind")),
1632
+ lit("paramNamed", v("f"), v("n"), v("p")),
1633
+ lit("entersUnder", v("r"), v("f"), v("c2"), v("c3")),
1634
+ lit("callKeywordArg", v("r"), v("n"), v("a"))
1635
+ ],
1636
+ "reached keyword argument under a context"
1637
+ ),
1638
+ // Which call runs which function under which context. A construction
1639
+ // runs its constructor under the site it makes, a method call runs
1640
+ // under the site its receiver is, and anything else under no context.
1641
+ rule(
1642
+ "entersUnder",
1643
+ [v("site"), v("ctor"), v("site"), v("caller")],
1644
+ [
1645
+ lit("allocates", v("site"), v("cls")),
1646
+ lit("runsConstructor", v("cls"), v("ctor")),
1647
+ lit("callUnder", v("site"), v("caller"))
1648
+ ],
1649
+ "construction enters its constructor"
1650
+ ),
1651
+ rule(
1652
+ "entersUnder",
1653
+ [v("r"), v("f"), v("site"), v("caller")],
1654
+ [
1655
+ lit("call", v("r"), v("cal")),
1656
+ lit("readsProperty", v("cal"), v("o"), v("m")),
1657
+ lit("objectOfUnder", v("o"), v("caller"), v("site")),
1658
+ lit("allocates", v("site"), v("cls")),
1659
+ lit("contains", v("site"), v("m"), v("f")),
1660
+ lit("callUnder", v("r"), v("caller"))
1661
+ ],
1662
+ "method call enters under its receiver"
1663
+ ),
1664
+ // A call written as a name runs with whatever receiver the body
1665
+ // around it has, so a plain function called from a method keeps the
1666
+ // site rather than taking every caller of it.
1667
+ rule(
1668
+ "entersUnder",
1669
+ [v("r"), v("f"), v("c"), v("c")],
1670
+ [
1671
+ lit("context", v("c")),
1672
+ lit("callsNamed", v("r"), v("f")),
1673
+ lit("callUnder", v("r"), v("c"))
1674
+ ],
1675
+ "named call enters under the site it is made in"
1676
+ ),
1677
+ rule(
1678
+ "entersUnder",
1679
+ [v("r"), v("f"), NO_CONTEXT, v("caller")],
1680
+ [
1681
+ lit("callsFunction", v("r"), v("f")),
1682
+ lit("callUnder", v("r"), v("caller"))
1683
+ ],
1684
+ "call enters with no context"
1685
+ ),
1686
+ // The constructor a construction runs, the ancestry included, so a
1687
+ // subclass that declares none still fills in what it inherits.
1688
+ rule(
1689
+ "runsConstructor",
1690
+ [v("cls"), v("f")],
1691
+ [lit("initializes", v("cls"), v("f"))]
1692
+ ),
1693
+ rule(
1694
+ "runsConstructor",
1695
+ [v("cls"), v("f")],
1696
+ [
1697
+ lit("extends", v("cls"), v("b")),
1698
+ lit("comesTo", v("b"), v("base")),
1699
+ lit("runsConstructor", v("base"), v("f"))
1155
1700
  ]
1156
1701
  ),
1157
- // Two rules rather than one leaving the next step's kind free. A free
1158
- // kind is a second question about the same relation, and a
1159
- // demand-driven run then derives both to answer either.
1702
+ // Which body a call is written in. One adapter states the call it
1703
+ // found and another states the callee, so both spellings are read.
1160
1704
  rule(
1161
- "reaches",
1162
- [v("x"), v("z"), RESULT_STEP],
1705
+ "callInBody",
1706
+ [v("f"), v("r")],
1707
+ [lit("bodyCallsDeep", v("f"), v("r")), lit("call", v("r"), v("c"))]
1708
+ ),
1709
+ rule(
1710
+ "callInBody",
1711
+ [v("f"), v("r")],
1712
+ [lit("bodyCallsDeep", v("f"), v("c")), lit("call", v("r"), v("c"))]
1713
+ ),
1714
+ // The context a call is made under: every site of the class whose
1715
+ // method or constructor it is written in, and no context for a call
1716
+ // outside every method body.
1717
+ rule(
1718
+ "callUnder",
1719
+ [v("r"), v("site")],
1720
+ [
1721
+ lit("callInBody", v("f"), v("r")),
1722
+ lit("holdsProperty", v("cls"), v("m"), v("f")),
1723
+ lit("allocates", v("site"), v("cls"))
1724
+ ],
1725
+ "call in a method"
1726
+ ),
1727
+ rule(
1728
+ "callUnder",
1729
+ [v("r"), v("site")],
1163
1730
  [
1164
- lit("reaches", v("x"), v("y"), RESULT_STEP),
1165
- lit("stepsTo", v("y"), v("z"), VALUE_STEP)
1731
+ lit("callInBody", v("f"), v("r")),
1732
+ lit("initializes", v("cls"), v("f")),
1733
+ lit("allocates", v("site"), v("cls"))
1734
+ ],
1735
+ "call in a constructor"
1736
+ ),
1737
+ // A plain function entered under a site makes its own calls under
1738
+ // that site, so a chain of plain functions off one method keeps it.
1739
+ rule(
1740
+ "callUnder",
1741
+ [v("r"), v("c")],
1742
+ [
1743
+ lit("callOutsideMethod", v("r")),
1744
+ lit("callInBody", v("f"), v("r")),
1745
+ lit("entersUnder", v("into"), v("f"), v("c"), v("c4"))
1746
+ ],
1747
+ "call in a function entered under a site"
1748
+ ),
1749
+ rule(
1750
+ "callUnder",
1751
+ [v("r"), NO_CONTEXT],
1752
+ [lit("callOutsideMethod", v("r"))],
1753
+ "call outside every method"
1754
+ ),
1755
+ // The two questions a caller puts under a context, stopping where
1756
+ // `isWrittenAs` and `comesTo` stop, whatever context the walk ended in.
1757
+ rule(
1758
+ "isWrittenAsUnder",
1759
+ [v("x"), v("c"), v("x")],
1760
+ [lit("context", v("c")), lit("writtenValue", v("x"))]
1761
+ ),
1762
+ rule(
1763
+ "isWrittenAsUnder",
1764
+ [v("x"), v("c"), v("x")],
1765
+ [lit("context", v("c")), lit("objectValue", v("x"))]
1766
+ ),
1767
+ rule(
1768
+ "isWrittenAsUnder",
1769
+ [v("x"), v("c"), v("z")],
1770
+ [
1771
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1772
+ lit("writtenValue", v("z"))
1166
1773
  ]
1167
1774
  ),
1168
1775
  rule(
1169
- "reaches",
1170
- [v("x"), v("z"), RESULT_STEP],
1776
+ "isWrittenAsUnder",
1777
+ [v("x"), v("c"), v("z")],
1778
+ [
1779
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1780
+ lit("objectValue", v("z"))
1781
+ ]
1782
+ ),
1783
+ rule(
1784
+ "comesToUnder",
1785
+ [v("x"), v("c"), v("x")],
1786
+ [lit("context", v("c")), lit("func", v("x"))]
1787
+ ),
1788
+ rule(
1789
+ "comesToUnder",
1790
+ [v("x"), v("c"), v("x")],
1791
+ [lit("context", v("c")), lit("objectValue", v("x"))]
1792
+ ),
1793
+ rule(
1794
+ "comesToUnder",
1795
+ [v("x"), v("c"), v("z")],
1796
+ [
1797
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1798
+ lit("func", v("z"))
1799
+ ]
1800
+ ),
1801
+ rule(
1802
+ "comesToUnder",
1803
+ [v("x"), v("c"), v("z")],
1804
+ [
1805
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1806
+ lit("objectValue", v("z"))
1807
+ ]
1808
+ ),
1809
+ rule(
1810
+ "comesToUnder",
1811
+ [v("x"), v("c"), v("z")],
1812
+ [
1813
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), INSTANCE_STEP),
1814
+ lit("func", v("z"))
1815
+ ]
1816
+ ),
1817
+ rule(
1818
+ "comesToUnder",
1819
+ [v("x"), v("c"), v("z")],
1171
1820
  [
1172
- lit("reaches", v("x"), v("y"), RESULT_STEP),
1173
- lit("stepsTo", v("y"), v("z"), RESULT_STEP)
1821
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), INSTANCE_STEP),
1822
+ lit("objectValue", v("z"))
1174
1823
  ]
1175
1824
  ),
1176
1825
  // What a value comes down to: the walk that never ran a call, stopped
@@ -1188,6 +1837,18 @@ var RESOLUTION_RULES = [
1188
1837
  [v("x"), v("z")],
1189
1838
  [lit("reaches", v("x"), v("z"), VALUE_STEP), lit("objectValue", v("z"))]
1190
1839
  ),
1840
+ // An instance walk stops the same way, so a method read off
1841
+ // `new App()` still finds what the class declares.
1842
+ rule(
1843
+ "comesTo",
1844
+ [v("x"), v("z")],
1845
+ [lit("reaches", v("x"), v("z"), INSTANCE_STEP), lit("func", v("z"))]
1846
+ ),
1847
+ rule(
1848
+ "comesTo",
1849
+ [v("x"), v("z")],
1850
+ [lit("reaches", v("x"), v("z"), INSTANCE_STEP), lit("objectValue", v("z"))]
1851
+ ),
1191
1852
  // The same stopping condition, for the walk that ran a call. A call
1192
1853
  // gets no `comesTo` answer on purpose: coming back with what a
1193
1854
  // factory returned would fight the unwrapping answer.
@@ -1228,6 +1889,13 @@ var RESOLUTION_RULES = [
1228
1889
  lit("isWrittenAs", v("ret"), v("z"))
1229
1890
  ]
1230
1891
  ),
1892
+ // Which expression running f hands back, whether f returns it, writes
1893
+ // it into a name first, or ends on it as a shorthand body.
1894
+ rule(
1895
+ "returnsCall",
1896
+ [v("f"), v("c")],
1897
+ [lit("returnsValue", v("f"), v("v")), lit("isWrittenAs", v("v"), v("c"))]
1898
+ ),
1231
1899
  // What one call site put in a parameter, told apart from what the
1232
1900
  // other callers passed.
1233
1901
  rule(
@@ -1325,18 +1993,34 @@ var RESOLUTION_RULES = [
1325
1993
  [v("x"), v("obj")],
1326
1994
  [lit("givesBack", v("x"), v("obj")), lit("objectValue", v("obj"))]
1327
1995
  ),
1996
+ // A construction is the object it made, and so is any name for it.
1997
+ // The class stays an answer too, so an instance whose site is not in
1998
+ // the run still reads what the class stores.
1999
+ rule(
2000
+ "objectOf",
2001
+ [v("site"), v("site")],
2002
+ [lit("allocates", v("site"), v("c"))]
2003
+ ),
2004
+ rule(
2005
+ "objectOf",
2006
+ [v("o"), v("site")],
2007
+ [
2008
+ lit("reaches", v("o"), v("site"), VALUE_STEP),
2009
+ lit("allocates", v("site"), v("c"))
2010
+ ]
2011
+ ),
1328
2012
  // Which calls a function, found by the name the call is written as
1329
2013
  // rather than by resolving every callee in the project. A caller knows
1330
2014
  // the function and wants its call sites, so both of these start from
1331
2015
  // the function. Starting from `call` instead asks what every call in
1332
2016
  // the project imports, which was 72% of everything derived.
1333
2017
  rule(
1334
- "callsFunction",
2018
+ "callsNamed",
1335
2019
  [v("r"), v("f")],
1336
2020
  [lit("binds", v("c"), v("f")), lit("call", v("r"), v("c"))]
1337
2021
  ),
1338
2022
  rule(
1339
- "callsFunction",
2023
+ "callsNamed",
1340
2024
  [v("r"), v("f")],
1341
2025
  [
1342
2026
  lit("moduleExport", v("m"), v("n"), v("f")),
@@ -1347,7 +2031,7 @@ var RESOLUTION_RULES = [
1347
2031
  // The same, for a language whose adapter writes the import down as a
1348
2032
  // declaration and the call's callee as the name referring to it.
1349
2033
  rule(
1350
- "callsFunction",
2034
+ "callsNamed",
1351
2035
  [v("r"), v("f")],
1352
2036
  [
1353
2037
  lit("moduleExport", v("m"), v("n"), v("f")),
@@ -1356,6 +2040,62 @@ var RESOLUTION_RULES = [
1356
2040
  lit("call", v("r"), v("c"))
1357
2041
  ]
1358
2042
  ),
2043
+ // `const f = (x) => ...` declares the name and puts the parameters on
2044
+ // the arrow, so everything above arrives at the declaration and
2045
+ // `paramOf` is about the arrow. One binds hop joins the two.
2046
+ rule(
2047
+ "callsNamed",
2048
+ [v("r"), v("f")],
2049
+ [lit("binds", v("g"), v("f")), lit("callsNamed", v("r"), v("g"))]
2050
+ ),
2051
+ // Every one of those, and the binds hop again over the whole
2052
+ // relation, so a property holding a name for a function is reached
2053
+ // the way it was before the two were told apart.
2054
+ rule("callsFunction", [v("r"), v("f")], [lit("callsNamed", v("r"), v("f"))]),
2055
+ rule(
2056
+ "callsFunction",
2057
+ [v("r"), v("f")],
2058
+ [lit("binds", v("g"), v("f")), lit("callsFunction", v("r"), v("g"))]
2059
+ ),
2060
+ // A function written on an object, called off it: `client.get(o)`.
2061
+ // The receiver comes from the object through `refersToObject`, not from
2062
+ // `objectOf`: a walk from every reader of the name was most of a run.
2063
+ rule(
2064
+ "callsFunction",
2065
+ [v("r"), v("f")],
2066
+ [
2067
+ lit("holdsProperty", v("obj"), v("n"), v("f")),
2068
+ lit("refersToObject", v("o"), v("obj")),
2069
+ lit("readsProperty", v("c"), v("o"), v("n")),
2070
+ lit("call", v("r"), v("c"))
2071
+ ]
2072
+ ),
2073
+ // The expressions that refer to an object literal by binding alone: the
2074
+ // declaration written as it, a reference or import of that, and a
2075
+ // fallback over any of those. Asked from the object, so it visits only them.
2076
+ rule("refersToObject", [v("obj"), v("obj")], [lit("objectValue", v("obj"))]),
2077
+ rule(
2078
+ "refersToObject",
2079
+ [v("x"), v("obj")],
2080
+ [lit("refersToObject", v("y"), v("obj")), lit("binds", v("x"), v("y"))]
2081
+ ),
2082
+ rule(
2083
+ "refersToObject",
2084
+ [v("x"), v("obj")],
2085
+ [
2086
+ lit("refersToObject", v("y"), v("obj")),
2087
+ lit("moduleExport", v("m"), v("n"), v("y")),
2088
+ lit("imports", v("x"), v("m"), v("n"))
2089
+ ]
2090
+ ),
2091
+ rule(
2092
+ "refersToObject",
2093
+ [v("x"), v("obj")],
2094
+ [
2095
+ lit("refersToObject", v("b"), v("obj")),
2096
+ lit("fallbackBranch", v("x"), v("b"))
2097
+ ]
2098
+ ),
1359
2099
  // What an object contains, its base class included, so a method the base
1360
2100
  // declares is found on a subclass that never overrode it. A method both
1361
2101
  // declare gives two, and the caller decides. This is its own relation
@@ -1373,7 +2113,8 @@ var RESOLUTION_RULES = [
1373
2113
  lit("extends", v("cls"), v("base")),
1374
2114
  lit("comesTo", v("base"), v("baseCls")),
1375
2115
  lit("contains", v("baseCls"), v("n"), v("held"))
1376
- ]
2116
+ ],
2117
+ BASE_CLASS_RULE
1377
2118
  ),
1378
2119
  // An association is read off an instance as a property, and stating
1379
2120
  // it as `contains` is what puts it on the ancestry rule, so a concern
@@ -1397,6 +2138,69 @@ var RESOLUTION_RULES = [
1397
2138
  ],
1398
2139
  "constructed association"
1399
2140
  ),
2141
+ // What the constructor put on the receiver. The class is the object
2142
+ // for an instance whose own site is not in the run.
2143
+ rule(
2144
+ "contains",
2145
+ [v("cls"), v("n"), v("held")],
2146
+ [
2147
+ lit("initializes", v("cls"), v("f")),
2148
+ lit("storesProperty", v("f"), v("n"), v("held"))
2149
+ ],
2150
+ "constructor store"
2151
+ ),
2152
+ // What any other method of the class put there. Two methods writing
2153
+ // one name give two rows, since nothing here orders them.
2154
+ rule(
2155
+ "contains",
2156
+ [v("cls"), v("n"), v("held")],
2157
+ [
2158
+ lit("holdsProperty", v("cls"), v("m"), v("f")),
2159
+ lit("storesProperty", v("f"), v("n"), v("held"))
2160
+ ],
2161
+ "method store"
2162
+ ),
2163
+ // A write through a name rather than through the receiver,
2164
+ // `client.timeout = 5`, which lands on whatever that name refers to.
2165
+ rule(
2166
+ "contains",
2167
+ [v("obj"), v("n"), v("held")],
2168
+ [
2169
+ lit("storesProperty", v("r"), v("n"), v("held")),
2170
+ lit("objectOf", v("r"), v("obj"))
2171
+ ],
2172
+ "named receiver store"
2173
+ ),
2174
+ // A call written with a class's own name, which is how most languages
2175
+ // spell a construction. A language that spells one some other way
2176
+ // states its own rule for this, the way Ruby does for `Const.new`.
2177
+ rule(
2178
+ "constructsNamed",
2179
+ [v("r"), v("cls")],
2180
+ [lit("objectValue", v("cls")), lit("callsNamed", v("r"), v("cls"))]
2181
+ ),
2182
+ // Each construction is an object of its own containing what its
2183
+ // class stores, so two sites are two objects.
2184
+ rule(
2185
+ "contains",
2186
+ [v("site"), v("n"), v("held")],
2187
+ [
2188
+ lit("allocates", v("site"), v("cls")),
2189
+ lit("contains", v("cls"), v("n"), v("held"))
2190
+ ],
2191
+ "allocated instance"
2192
+ ),
2193
+ // A call that makes one of a class. Every finder a pack declared is
2194
+ // one too, so `User.find(1)` is a site the same way `User.new` is.
2195
+ rule(
2196
+ "allocates",
2197
+ [v("site"), v("cls")],
2198
+ [
2199
+ lit("call", v("site"), v("c")),
2200
+ lit("stepsTo", v("site"), v("cls"), INSTANCE_STEP)
2201
+ ],
2202
+ "allocation site"
2203
+ ),
1400
2204
  // The library base a class's ancestry arrives at, however many of a
1401
2205
  // project's own classes sit in between. A base the run declares has a
1402
2206
  // node to keep walking from; one it does not has only a name.
@@ -1486,8 +2290,8 @@ var RESOLUTION_RULES = [
1486
2290
  ]
1487
2291
  ),
1488
2292
  // Where a name comes from, when what it refers to lives outside the
1489
- // source being read. A walk ends at something written out in source,
1490
- // so it never reaches a library's own function.
2293
+ // source being read. A walk ends at what the source writes out, so it
2294
+ // never reaches a library's own function; these rules do.
1491
2295
  rule(
1492
2296
  "comesFrom",
1493
2297
  [v("x"), v("m"), v("n")],
@@ -1501,6 +2305,28 @@ var RESOLUTION_RULES = [
1501
2305
  lit("imports", v("y"), v("m"), v("n"))
1502
2306
  ]
1503
2307
  ),
2308
+ // A member read off a whole-module import: the member's own name is
2309
+ // what the module exports, so `fastapi.APIRouter` comes from
2310
+ // fastapi's `APIRouter`. Directly, or through a name for the module.
2311
+ rule(
2312
+ "comesFrom",
2313
+ [v("x"), v("m"), v("n")],
2314
+ [
2315
+ lit("readsProperty", v("x"), v("ns"), v("n")),
2316
+ lit("imports", v("ns"), v("m"), NAMESPACE_IMPORT)
2317
+ ],
2318
+ NAMESPACE_MEMBER_RULE
2319
+ ),
2320
+ rule(
2321
+ "comesFrom",
2322
+ [v("x"), v("m"), v("n")],
2323
+ [
2324
+ lit("readsProperty", v("x"), v("o"), v("n")),
2325
+ lit("reaches", v("o"), v("ns"), VALUE_STEP),
2326
+ lit("imports", v("ns"), v("m"), NAMESPACE_IMPORT)
2327
+ ],
2328
+ NAMESPACE_MEMBER_RULE
2329
+ ),
1504
2330
  // Calling f ends up calling the name n that module m exports, one
1505
2331
  // hop through f's own body or deeper through a wrapper of a wrapper.
1506
2332
  // Several results is normal: a composed decorator applies them all.
@@ -1530,6 +2356,13 @@ var RESOLUTION_RULES = [
1530
2356
  [lit("comesTo", v("x"), v("z")), lit("func", v("z"))]
1531
2357
  )
1532
2358
  ];
2359
+ function alsoSteps(rules) {
2360
+ return [
2361
+ ...rules,
2362
+ ...rules.filter((r) => r.head.relation === "hop").map((r) => ({ ...r, head: { ...r.head, relation: "stepsTo" } }))
2363
+ ];
2364
+ }
2365
+ var RESOLUTION_RULES = alsoSteps(STATED_RULES);
1533
2366
  var RESOLUTION_QUESTIONS = [
1534
2367
  rule(
1535
2368
  "wantedResolves",
@@ -1552,12 +2385,55 @@ var RESOLUTION_QUESTIONS = [
1552
2385
  [lit("wanted", v("x")), lit("isWrittenAs", v("x"), v("z"))]
1553
2386
  ),
1554
2387
  // A call is given no `comesTo`, so this is the only way to ask what
1555
- // object one arrives at, and without it a demand-driven run derives
1556
- // `objectOf` nowhere.
2388
+ // object one arrives at, and a demand-driven run derives `objectOf`
2389
+ // nowhere without it. An allocation site is not one of the answers.
1557
2390
  rule(
1558
2391
  "wantedObjectOf",
1559
2392
  [v("x"), v("z")],
1560
- [lit("wanted", v("x")), lit("objectOf", v("x"), v("z"))]
2393
+ [
2394
+ lit("wanted", v("x")),
2395
+ lit("objectOf", v("x"), v("z")),
2396
+ lit("objectValue", v("z"))
2397
+ ]
2398
+ ),
2399
+ // The same three questions under one allocation site, keyed by the
2400
+ // value and the site both. An allocation site is an answer here,
2401
+ // unlike `wantedObjectOf`, since a site is what a context is.
2402
+ rule(
2403
+ "wantedIsWrittenAsUnder",
2404
+ [v("x"), v("c"), v("z")],
2405
+ [
2406
+ lit("wantedUnder", v("x"), v("c")),
2407
+ lit("isWrittenAsUnder", v("x"), v("c"), v("z"))
2408
+ ]
2409
+ ),
2410
+ // Where a class was made, for a caller about to ask a value question
2411
+ // under each. Going through `constructsNamed` keeps the demand on the
2412
+ // calls written with the class's name rather than on every call.
2413
+ rule(
2414
+ "wantedAllocatedAt",
2415
+ [v("cls"), v("site")],
2416
+ [
2417
+ lit("wantedSites", v("cls")),
2418
+ lit("constructsNamed", v("site"), v("cls")),
2419
+ lit("allocates", v("site"), v("cls"))
2420
+ ]
2421
+ ),
2422
+ rule(
2423
+ "wantedComesToUnder",
2424
+ [v("x"), v("c"), v("z")],
2425
+ [
2426
+ lit("wantedUnder", v("x"), v("c")),
2427
+ lit("comesToUnder", v("x"), v("c"), v("z"))
2428
+ ]
2429
+ ),
2430
+ rule(
2431
+ "wantedObjectOfUnder",
2432
+ [v("x"), v("c"), v("z")],
2433
+ [
2434
+ lit("wantedUnder", v("x"), v("c")),
2435
+ lit("objectOfUnder", v("x"), v("c"), v("z"))
2436
+ ]
1561
2437
  ),
1562
2438
  // The same for the function a call returns: `app.use(requireCaller(config))`
1563
2439
  // registers what the factory gives back, and `resolves` on the call
@@ -1574,6 +2450,19 @@ var RESOLUTION_QUESTIONS = [
1574
2450
  [v("p"), v("r"), v("z")],
1575
2451
  [lit("wanted", v("p")), lit("paramAt", v("r"), v("p"), v("z"))]
1576
2452
  ),
2453
+ // The argument as the caller wrote it. A parameter given a GraphQL
2454
+ // document has no `paramAt` answer, since that settles through
2455
+ // `comesTo`, which stops at a function or an object.
2456
+ rule(
2457
+ "wantedPassesArgument",
2458
+ [v("p"), v("r"), v("a")],
2459
+ [lit("wanted", v("p")), lit("passesArgument", v("r"), v("p"), v("a"))]
2460
+ ),
2461
+ rule(
2462
+ "wantedReturnsCall",
2463
+ [v("f"), v("c")],
2464
+ [lit("wanted", v("f")), lit("returnsCall", v("f"), v("c"))]
2465
+ ),
1577
2466
  rule(
1578
2467
  "wantedComesFrom",
1579
2468
  [v("x"), v("m"), v("n")],
@@ -1837,35 +2726,61 @@ var RESOLUTION_QUESTIONS = [
1837
2726
  var ANSWER_RELATIONS = [
1838
2727
  ...new Set(RESOLUTION_QUESTIONS.map((r) => r.head.relation))
1839
2728
  ];
2729
+ var UNDER_ANSWER_RELATIONS = [
2730
+ "wantedIsWrittenAsUnder",
2731
+ "wantedComesToUnder",
2732
+ "wantedObjectOfUnder"
2733
+ ];
1840
2734
  export {
1841
2735
  ANSWER_RELATIONS,
1842
2736
  ASKING_RELATIONS,
2737
+ BASE_CLASS_RULE,
1843
2738
  FACT_CONTRACT_CASES,
2739
+ INSTANCE_STEP,
1844
2740
  NAMESPACE_IMPORT,
1845
2741
  NAMESPACE_IMPORT_NAME,
2742
+ NAMESPACE_MEMBER_RULE,
2743
+ NO_CONTEXT,
2744
+ NO_CONTEXT_NAME,
1846
2745
  RESOLUTION_QUESTIONS,
1847
2746
  RESOLUTION_RULES,
1848
2747
  RESULT_STEP,
2748
+ UNDER_ANSWER_RELATIONS,
2749
+ UNDER_QUESTION_ROW_BUDGET,
2750
+ UNDER_RUN_ROW_BUDGET,
1849
2751
  VALUE_STEP,
2752
+ addPackWords,
1850
2753
  agreedMountPrefix,
2754
+ allocationSitesOf,
2755
+ alsoSteps,
2756
+ answersByKey,
1851
2757
  askResolution,
2758
+ askResolutionUnder,
1852
2759
  calleeOutcomeOf,
1853
2760
  calleeOutcomes,
1854
2761
  checkFactContract,
2762
+ comesToUnder,
1855
2763
  couldBeSettled,
1856
2764
  explainResolutionProof,
1857
2765
  explainResolvedKey,
2766
+ isWrittenAsUnder,
1858
2767
  joinMountedPath,
1859
2768
  mountPathsOf,
1860
2769
  nodeOfKey,
2770
+ objectOfUnder,
1861
2771
  placeholderValues,
1862
2772
  queryFacts,
1863
2773
  renderExplanation,
1864
2774
  resolutionProgram,
2775
+ resolutionUnderProgram,
1865
2776
  singleAnswers,
1866
2777
  startsAtName,
2778
+ underQuestionSpend,
1867
2779
  valueLeftByWrites,
1868
2780
  writesRunInOrder,
1869
2781
  writtenSourcesOf,
1870
- writtenValueOf
2782
+ writtenValueOf,
2783
+ writtenValueUnder,
2784
+ writtenValuesByKey,
2785
+ writtenValuesOf
1871
2786
  };