@suss/resolution 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,14 @@
1
1
  // src/program.ts
2
- import { clearRelations, deriveOnDemand, evaluate } from "@suss/datalog";
2
+ import {
3
+ clearRelations,
4
+ deriveOnDemand,
5
+ evaluate,
6
+ tupleKey
7
+ } from "@suss/datalog";
3
8
  var ASKING_RELATIONS = [
4
9
  "wanted",
10
+ "wantedUnder",
11
+ "wantedSites",
5
12
  "wantedOrigin",
6
13
  "wantedCallOrigin",
7
14
  "wantedExportsOf",
@@ -11,8 +18,12 @@ var ASKING_RELATIONS = [
11
18
  ];
12
19
  var NO_LANGUAGE_RULES = [];
13
20
  var programs = /* @__PURE__ */ new WeakMap();
14
- function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
15
- const built = programs.get(languageRules);
21
+ var underPrograms = /* @__PURE__ */ new WeakMap();
22
+ var contextFreeAnswers = () => ANSWER_RELATIONS.filter(
23
+ (relation) => !UNDER_ANSWER_RELATIONS.includes(relation)
24
+ );
25
+ function builtProgram(languageRules, cache, complete) {
26
+ const built = cache.get(languageRules);
16
27
  if (built !== void 0) {
17
28
  return built;
18
29
  }
@@ -21,10 +32,16 @@ function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
21
32
  ...languageRules,
22
33
  ...RESOLUTION_QUESTIONS
23
34
  ];
24
- const program = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? { rules, demandDriven: [], demands: [] } : deriveOnDemand(rules, ANSWER_RELATIONS);
25
- programs.set(languageRules, program);
35
+ const program = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? { rules, demandDriven: [], demands: [] } : deriveOnDemand(rules, complete);
36
+ cache.set(languageRules, program);
26
37
  return program;
27
38
  }
39
+ function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
40
+ return builtProgram(languageRules, programs, contextFreeAnswers());
41
+ }
42
+ function resolutionUnderProgram(languageRules = NO_LANGUAGE_RULES) {
43
+ return builtProgram(languageRules, underPrograms, ANSWER_RELATIONS);
44
+ }
28
45
  function queryFacts(program) {
29
46
  if (program.demandDriven.length === 0) {
30
47
  return [];
@@ -61,6 +78,29 @@ function askResolution(db, keys, asking = "wanted", program = resolutionProgram(
61
78
  clearRelations(db, program.rules, forget);
62
79
  }
63
80
  }
81
+ var askedUnderByDb = /* @__PURE__ */ new WeakMap();
82
+ function askResolutionUnder(db, pairs, program = resolutionUnderProgram()) {
83
+ let asked = askedUnderByDb.get(db);
84
+ if (asked === void 0) {
85
+ asked = /* @__PURE__ */ new Set();
86
+ askedUnderByDb.set(db, asked);
87
+ }
88
+ const fresh = [...pairs].filter(
89
+ ([key, site]) => !asked.has(tupleKey([key, site]))
90
+ );
91
+ if (fresh.length === 0) {
92
+ return;
93
+ }
94
+ for (const [key, site] of fresh) {
95
+ asked.add(tupleKey([key, site]));
96
+ db.add("wantedUnder", [key, site]);
97
+ }
98
+ evaluate(db, program.rules);
99
+ const forget = queryFacts(program);
100
+ if (forget.length > 0) {
101
+ clearRelations(db, program.rules, forget);
102
+ }
103
+ }
64
104
 
65
105
  // src/callee.ts
66
106
  var UNDECLARED = { kind: "undeclared" };
@@ -489,6 +529,12 @@ var DEFAULT_PHRASES = {
489
529
  "last write": ({ tuple, describe }) => ({
490
530
  reason: `${describe(tuple[0])} is written more than once, and the last write leaves it as ${describe(tuple[1])}`
491
531
  }),
532
+ "one of several writes": ({ tuple, describe }) => ({
533
+ reason: `${describe(tuple[0])} is written more than once, and one of those writes makes it ${describe(tuple[1])}`,
534
+ assumptions: [
535
+ `nothing says which write to ${describe(tuple[0])} ran last, so every write it has is a possible value`
536
+ ]
537
+ }),
492
538
  fallback: ({ tuple, describe }) => ({
493
539
  reason: `${describe(tuple[0])} is a fallback expression, and ${describe(tuple[1])} is the branch that resolves`
494
540
  }),
@@ -505,7 +551,7 @@ var DEFAULT_PHRASES = {
505
551
  "property read": ({ tuple, premises, describe }) => {
506
552
  const [, object, name] = premises[0].tuple;
507
553
  const contains = premises[2];
508
- const inherited = contains.kind === "derived" && contains.premises.length > 1;
554
+ const inherited = contains.kind === "derived" && ruleLabel(contains.rule) === BASE_CLASS_RULE;
509
555
  return {
510
556
  reason: `${describe(tuple[0])} reads ${String(name)} off ${describe(object)}, which contains ${describe(tuple[1])}`,
511
557
  notes: inherited ? [`${String(name)} comes from a base class`] : []
@@ -584,7 +630,8 @@ var CHAIN_RELATIONS = /* @__PURE__ */ new Set([
584
630
  "isWrittenAs",
585
631
  "comesFrom",
586
632
  "reaches",
587
- "stepsTo"
633
+ "stepsTo",
634
+ "hop"
588
635
  ]);
589
636
  function inlineChain(proof, state) {
590
637
  const explained = explainResolutionProof(proof, {
@@ -640,6 +687,17 @@ function importStep(proof, state) {
640
687
  assumptions: []
641
688
  };
642
689
  }
690
+ function namespaceMemberStep(proof, state) {
691
+ const [x, module, name] = proof.tuple;
692
+ return {
693
+ from: x,
694
+ to: module,
695
+ rule: NAMESPACE_MEMBER_RULE,
696
+ reason: `${state.describe(x)} reads ${String(name)} off ${state.describe(module)}, which is imported whole`,
697
+ notes: [],
698
+ assumptions: []
699
+ };
700
+ }
643
701
  var flattenInto = (proof, state) => {
644
702
  if (proof.kind === "truncated") {
645
703
  state.truncated = true;
@@ -649,7 +707,7 @@ var flattenInto = (proof, state) => {
649
707
  return;
650
708
  }
651
709
  const relation = proof.relation;
652
- if (relation === "stepsTo") {
710
+ if (relation === "stepsTo" || relation === "hop") {
653
711
  state.steps.push(stepFrom(proof, state));
654
712
  return;
655
713
  }
@@ -660,6 +718,10 @@ var flattenInto = (proof, state) => {
660
718
  return;
661
719
  }
662
720
  if (relation === "comesFrom") {
721
+ if (ruleLabel(proof.rule) === NAMESPACE_MEMBER_RULE) {
722
+ state.steps.push(namespaceMemberStep(proof, state));
723
+ return;
724
+ }
663
725
  if (proof.premises.length === 2) {
664
726
  flattenInto(proof.premises[0], state);
665
727
  state.steps.push(importStep(proof.premises[1], state));
@@ -836,6 +898,37 @@ function nodeOfKey(rootsByFile, key) {
836
898
  return null;
837
899
  }
838
900
 
901
+ // src/packWords.ts
902
+ function addPackWords(db, words) {
903
+ for (const word of words.givesBackOne ?? []) {
904
+ db.add("givesBackOne", [word.base, word.method]);
905
+ }
906
+ for (const word of words.givesBackOneOfArgument ?? []) {
907
+ db.add("givesBackOneOfArgument", [
908
+ word.base,
909
+ word.method,
910
+ String(word.argument)
911
+ ]);
912
+ }
913
+ for (const word of words.givesBackOneOfImport ?? []) {
914
+ db.add("givesBackOneOfImport", [
915
+ word.module,
916
+ word.name,
917
+ String(word.argument)
918
+ ]);
919
+ }
920
+ for (const word of words.entersAsSelf ?? []) {
921
+ db.add("entersAsSelf", [word.module, word.name]);
922
+ }
923
+ for (const word of words.unwrapsByName ?? []) {
924
+ db.add("unwrapsByName", [word.callee, String(word.argument)]);
925
+ db.add("wrapperModule", [word.callee, word.module]);
926
+ }
927
+ for (const word of words.associationConstructor ?? []) {
928
+ db.add("associationConstructor", [word.module, word.name]);
929
+ }
930
+ }
931
+
839
932
  // src/session.ts
840
933
  import { Database, evaluate as evaluate2, proofOf, witnesses } from "@suss/datalog";
841
934
  function explainResolvedKey(options) {
@@ -891,7 +984,7 @@ function explainResolvedKey(options) {
891
984
 
892
985
  // src/singleAnswer.ts
893
986
  var NO_PLACEHOLDERS = /* @__PURE__ */ new Set();
894
- function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
987
+ function answersByKey(rows, placeholders = NO_PLACEHOLDERS) {
895
988
  const candidates = /* @__PURE__ */ new Map();
896
989
  for (const row of rows) {
897
990
  const key = String(row[0]);
@@ -903,9 +996,15 @@ function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
903
996
  set.add(answer);
904
997
  candidates.set(key, set);
905
998
  }
906
- const settled = /* @__PURE__ */ new Map();
999
+ const answers = /* @__PURE__ */ new Map();
907
1000
  for (const [key, set] of candidates) {
908
- const answers = withoutPlaceholders(set, placeholders);
1001
+ answers.set(key, withoutPlaceholders(set, placeholders));
1002
+ }
1003
+ return answers;
1004
+ }
1005
+ function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1006
+ const settled = /* @__PURE__ */ new Map();
1007
+ for (const [key, answers] of answersByKey(rows, placeholders)) {
909
1008
  if (answers.length === 1) {
910
1009
  settled.set(key, answers[0]);
911
1010
  }
@@ -923,6 +1022,28 @@ function withoutPlaceholders(answers, placeholders) {
923
1022
  return kept;
924
1023
  }
925
1024
 
1025
+ // src/underContext.ts
1026
+ function answersUnder(db, relation, key, site) {
1027
+ return db.lookup(relation, 0, key).filter((row) => String(row[1]) === site).map((row) => String(row[2]));
1028
+ }
1029
+ function isWrittenAsUnder(db, key, site) {
1030
+ return answersUnder(db, "wantedIsWrittenAsUnder", key, site);
1031
+ }
1032
+ function comesToUnder(db, key, site) {
1033
+ return answersUnder(db, "wantedComesToUnder", key, site);
1034
+ }
1035
+ function objectOfUnder(db, key, site) {
1036
+ return answersUnder(db, "wantedObjectOfUnder", key, site);
1037
+ }
1038
+ function writtenValueUnder(db, key, site) {
1039
+ const rows = isWrittenAsUnder(db, key, site).map((answer) => [key, answer]);
1040
+ const answers = answersByKey(rows, placeholderValues(db)).get(key) ?? [];
1041
+ return answers.length === 1 ? answers[0] : null;
1042
+ }
1043
+ function allocationSitesOf(db, cls) {
1044
+ return db.lookup("wantedAllocatedAt", 0, cls).map((row) => String(row[1]));
1045
+ }
1046
+
926
1047
  // src/writes.ts
927
1048
  function valueLeftByWrites(writes, ordered) {
928
1049
  const deciding = writes.filter((write) => !write.narrowsName);
@@ -953,23 +1074,58 @@ function sharedConstruction(writes) {
953
1074
  }
954
1075
 
955
1076
  // 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;
1077
+ var WRITTEN_AS = "wantedIsWrittenAs";
1078
+ function writtenValueOf(db, key, ask, relation = WRITTEN_AS) {
1079
+ const answers = settledByKey(db, [key], ask, relation).get(key) ?? [];
1080
+ return answers.length === 1 ? answers[0] : null;
1081
+ }
1082
+ function writtenValuesOf(db, key, ask, relation = WRITTEN_AS) {
1083
+ const settled = settledByKey(db, [key], ask, relation).get(key) ?? [];
1084
+ return settled.length > 0 ? settled : writesLeft(db, key);
1085
+ }
1086
+ function writtenValuesByKey(db, keys, ask, relation = WRITTEN_AS) {
1087
+ const one = /* @__PURE__ */ new Map();
1088
+ for (const [key, answers] of settledByKey(db, keys, ask, relation)) {
1089
+ if (answers.length === 1) {
1090
+ one.set(key, answers[0]);
1091
+ }
963
1092
  }
964
- const isCall = db.facts("call").some((row) => String(row[0]) === direct);
965
- if (!isCall) {
1093
+ return one;
1094
+ }
1095
+ function settledByKey(db, keys, ask, relation) {
1096
+ const placeholders = placeholderValues(db);
1097
+ const byKey = answersByKey(db.facts(relation), placeholders);
1098
+ const direct = new Map(keys.map((key) => [key, byKey.get(key) ?? []]));
1099
+ const calls = new Set(db.facts("call").map((row) => String(row[0])));
1100
+ const throughCalls = [...direct.values()].flat().filter((answer) => calls.has(answer));
1101
+ if (throughCalls.length === 0) {
966
1102
  return direct;
967
1103
  }
968
- ask([direct]);
969
- const deeper = singleAnswers(db.facts("wantedIsWrittenAs"), placeholders).get(
970
- direct
1104
+ ask(throughCalls);
1105
+ const deeper = singleAnswers(db.facts(relation), placeholders);
1106
+ return new Map(
1107
+ [...direct].map(([key, answers]) => [
1108
+ key,
1109
+ collapseCalls(answers, calls, deeper)
1110
+ ])
971
1111
  );
972
- return deeper ?? direct;
1112
+ }
1113
+ function collapseCalls(direct, calls, deeper) {
1114
+ return [
1115
+ ...new Set(
1116
+ direct.map(
1117
+ (answer) => calls.has(answer) ? deeper.get(answer) ?? answer : answer
1118
+ )
1119
+ )
1120
+ ];
1121
+ }
1122
+ function writesLeft(db, key) {
1123
+ const unstated = db.facts("writesUnstated").some((row) => String(row[0]) === key);
1124
+ if (unstated) {
1125
+ return [];
1126
+ }
1127
+ const candidates = answersByKey(db.facts("mayHold"), placeholderValues(db)).get(key) ?? [];
1128
+ return candidates.length > 1 ? candidates : [];
973
1129
  }
974
1130
 
975
1131
  // src/index.ts
@@ -977,38 +1133,61 @@ import { constant, lit, rule, variable as v } from "@suss/datalog";
977
1133
  var VALUE_STEP = constant("value");
978
1134
  var NAMESPACE_IMPORT_NAME = "*";
979
1135
  var NAMESPACE_IMPORT = constant(NAMESPACE_IMPORT_NAME);
1136
+ var NAMESPACE_MEMBER_RULE = "namespace member";
1137
+ var BASE_CLASS_RULE = "base class";
1138
+ var INSTANCE_STEP = constant("instance");
980
1139
  var RESULT_STEP = constant("result");
981
- var RESOLUTION_RULES = [
1140
+ var NO_CONTEXT_NAME = "none";
1141
+ var NO_CONTEXT = constant(NO_CONTEXT_NAME);
1142
+ var STEP_LATTICE = [
1143
+ { soFar: VALUE_STEP, next: v("kind"), took: v("kind") },
1144
+ { soFar: INSTANCE_STEP, next: VALUE_STEP, took: INSTANCE_STEP },
1145
+ { soFar: INSTANCE_STEP, next: INSTANCE_STEP, took: INSTANCE_STEP },
1146
+ { soFar: INSTANCE_STEP, next: RESULT_STEP, took: RESULT_STEP },
1147
+ { soFar: RESULT_STEP, next: VALUE_STEP, took: RESULT_STEP },
1148
+ { soFar: RESULT_STEP, next: INSTANCE_STEP, took: RESULT_STEP },
1149
+ { soFar: RESULT_STEP, next: RESULT_STEP, took: RESULT_STEP }
1150
+ ];
1151
+ var STATED_RULES = [
982
1152
  // Aliasing: const x = y, or an identifier referencing a declaration.
983
1153
  // A language with a hop of its own, like JavaScript's `.bind`, states
984
1154
  // it as a step too, or every question but `comesTo` misses it.
985
1155
  rule(
986
- "stepsTo",
1156
+ "hop",
987
1157
  [v("x"), v("y"), VALUE_STEP],
988
1158
  [lit("binds", v("x"), v("y"))],
989
1159
  "alias"
990
1160
  ),
991
1161
  // 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.
1162
+ // there. The adapter works out which write that is, and stays quiet
1163
+ // when control flow decides; the rule below takes that name instead.
994
1164
  rule(
995
- "stepsTo",
1165
+ "hop",
996
1166
  [v("x"), v("y"), VALUE_STEP],
997
1167
  [lit("endsHolding", v("x"), v("y"))],
998
1168
  "last write"
999
1169
  ),
1170
+ // A name whose writes nothing orders leads to each of them, so a
1171
+ // caller that can use several values gets them all and one that needs
1172
+ // a single value gets none. A write stating no value stops every step.
1173
+ rule(
1174
+ "hop",
1175
+ [v("x"), v("y"), VALUE_STEP],
1176
+ [lit("mayHold", v("x"), v("y")), lit("writesAllStated", v("x"))],
1177
+ "one of several writes"
1178
+ ),
1000
1179
  // A fallback says the value is one of its branches, so each branch is
1001
1180
  // a step. A branch that resolves to nothing makes no claim, and two
1002
1181
  // branches resolving to different things fail the single-answer policy.
1003
1182
  rule(
1004
- "stepsTo",
1183
+ "hop",
1005
1184
  [v("x"), v("b"), VALUE_STEP],
1006
1185
  [lit("fallbackBranch", v("x"), v("b"))],
1007
1186
  "fallback"
1008
1187
  ),
1009
1188
  // An import steps to what the module exports under that name.
1010
1189
  rule(
1011
- "stepsTo",
1190
+ "hop",
1012
1191
  [v("x"), v("value"), VALUE_STEP],
1013
1192
  [
1014
1193
  lit("imports", v("x"), v("m"), v("n")),
@@ -1042,8 +1221,8 @@ var RESOLUTION_RULES = [
1042
1221
  // a method read off the result is the one the class declares. The
1043
1222
  // caveat below is about a factory function, and a class is not one.
1044
1223
  rule(
1045
- "stepsTo",
1046
- [v("r"), v("cls"), VALUE_STEP],
1224
+ "hop",
1225
+ [v("r"), v("cls"), INSTANCE_STEP],
1047
1226
  [
1048
1227
  lit("call", v("r"), v("c")),
1049
1228
  lit("comesTo", v("c"), v("cls")),
@@ -1051,12 +1230,21 @@ var RESOLUTION_RULES = [
1051
1230
  ],
1052
1231
  "class instance"
1053
1232
  ),
1233
+ // The receiver a method is called on. Being one of a class is the
1234
+ // same hop a construction takes, so a method read off `self` finds
1235
+ // what the class declares.
1236
+ rule(
1237
+ "hop",
1238
+ [v("x"), v("cls"), INSTANCE_STEP],
1239
+ [lit("instanceOf", v("x"), v("cls"))],
1240
+ "receiver instance"
1241
+ ),
1054
1242
  // A finder the library declares, keyed on the base a pack named so a
1055
1243
  // project class with a method of the same name on another hierarchy is
1056
1244
  // left alone. The DESIGN says what a chain of them composes into.
1057
1245
  rule(
1058
- "stepsTo",
1059
- [v("r"), v("cls"), VALUE_STEP],
1246
+ "hop",
1247
+ [v("r"), v("cls"), INSTANCE_STEP],
1060
1248
  [
1061
1249
  lit("call", v("r"), v("c")),
1062
1250
  lit("readsProperty", v("c"), v("o"), v("m")),
@@ -1073,8 +1261,8 @@ var RESOLUTION_RULES = [
1073
1261
  // instead of as the receiver. The argument having to reach the base a
1074
1262
  // pack named is what keeps an unrelated `get` out.
1075
1263
  rule(
1076
- "stepsTo",
1077
- [v("r"), v("cls"), VALUE_STEP],
1264
+ "hop",
1265
+ [v("r"), v("cls"), INSTANCE_STEP],
1078
1266
  [
1079
1267
  lit("call", v("r"), v("c")),
1080
1268
  lit("readsProperty", v("c"), v("o"), v("m")),
@@ -1091,8 +1279,8 @@ var RESOLUTION_RULES = [
1091
1279
  // anything, keyed on the module it was imported from so a project
1092
1280
  // function spelled the same way is not mistaken for it.
1093
1281
  rule(
1094
- "stepsTo",
1095
- [v("r"), v("cls"), VALUE_STEP],
1282
+ "hop",
1283
+ [v("r"), v("cls"), INSTANCE_STEP],
1096
1284
  [
1097
1285
  lit("call", v("r"), v("c")),
1098
1286
  lit("comesFrom", v("c"), v("mod"), v("n")),
@@ -1102,10 +1290,24 @@ var RESOLUTION_RULES = [
1102
1290
  ],
1103
1291
  "declared import finder"
1104
1292
  ),
1293
+ // A block opened over a library's own constructor. The pack says
1294
+ // entering one gives back the object it built, so the name the block
1295
+ // opens is that call.
1296
+ rule(
1297
+ "hop",
1298
+ [v("y"), v("r"), VALUE_STEP],
1299
+ [
1300
+ lit("entersAs", v("y"), v("r")),
1301
+ lit("call", v("r"), v("c")),
1302
+ lit("comesFrom", v("c"), v("mod"), v("n")),
1303
+ lit("entersAsSelf", v("mod"), v("n"))
1304
+ ],
1305
+ "context manager returns self"
1306
+ ),
1105
1307
  // Wrapper transparency, derived: calling a factory that returns a
1106
1308
  // function which calls its parameter k steps to argument k.
1107
1309
  rule(
1108
- "stepsTo",
1310
+ "hop",
1109
1311
  [v("r"), v("a"), VALUE_STEP],
1110
1312
  [
1111
1313
  lit("call", v("r"), v("c")),
@@ -1119,7 +1321,7 @@ var RESOLUTION_RULES = [
1119
1321
  // argument k. The callee has to come from the library the pack said,
1120
1322
  // so a local object spelled the same way is not mistaken for it.
1121
1323
  rule(
1122
- "stepsTo",
1324
+ "hop",
1123
1325
  [v("r"), v("a"), VALUE_STEP],
1124
1326
  [
1125
1327
  lit("calleeName", v("r"), v("n")),
@@ -1133,44 +1335,441 @@ var RESOLUTION_RULES = [
1133
1335
  // The one step that runs a function forwards: a call steps to what
1134
1336
  // the function it invokes returns.
1135
1337
  rule(
1136
- "stepsTo",
1338
+ "hop",
1137
1339
  [v("r"), v("ret"), RESULT_STEP],
1138
1340
  [lit("invokes", v("r"), v("f")), lit("returnsValue", v("f"), v("ret"))],
1139
1341
  "call result"
1140
1342
  ),
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.
1343
+ // What a function says it gives back when its body never states a
1344
+ // value. The adapter stays quiet about the annotation whenever the
1345
+ // body does state one, so the two never answer the same question.
1346
+ rule(
1347
+ "hop",
1348
+ [v("r"), v("cls"), RESULT_STEP],
1349
+ [
1350
+ lit("invokes", v("r"), v("f")),
1351
+ lit("returnsClass", v("f"), v("x")),
1352
+ lit("comesTo", v("x"), v("cls")),
1353
+ lit("objectValue", v("cls"))
1354
+ ],
1355
+ "declared return type"
1356
+ ),
1357
+ // Where a walk gets to, under the strongest step it took: value, then
1358
+ // instance, then result. The walk so far comes first, so demand stays
1359
+ // on the value asked about; the README says what the other order cost.
1144
1360
  rule(
1145
1361
  "reaches",
1146
1362
  [v("x"), v("z"), v("kind")],
1147
1363
  [lit("stepsTo", v("x"), v("z"), v("kind"))]
1148
1364
  ),
1365
+ // A rule per pair rather than one leaving the next step's kind free. A
1366
+ // free kind is a second question about the same relation, and a
1367
+ // demand-driven run then derives both to answer either.
1368
+ ...STEP_LATTICE.map(
1369
+ ({ soFar, next, took }) => rule(
1370
+ "reaches",
1371
+ [v("x"), v("z"), took],
1372
+ [
1373
+ lit("reaches", v("x"), v("y"), soFar),
1374
+ lit("stepsTo", v("y"), v("z"), next)
1375
+ ]
1376
+ )
1377
+ ),
1378
+ // A context is an allocation site, or nothing at all for a value read
1379
+ // outside every site. Both are stated, so a rule that starts a walk
1380
+ // binds its context from a premise rather than from a negation.
1381
+ rule("context", [NO_CONTEXT], [], "no context"),
1382
+ rule(
1383
+ "context",
1384
+ [v("site")],
1385
+ [lit("allocates", v("site"), v("cls"))],
1386
+ "site context"
1387
+ ),
1388
+ // The same closure, with the context the walk started under and the
1389
+ // context it arrived under. It takes `hop`, because an argument and a
1390
+ // property read have their own rules below.
1391
+ rule(
1392
+ "reachesUnder",
1393
+ [v("x"), v("c"), v("z"), v("c"), v("kind")],
1394
+ [lit("context", v("c")), lit("hop", v("x"), v("z"), v("kind"))],
1395
+ "under one hop"
1396
+ ),
1397
+ ...STEP_LATTICE.map(
1398
+ ({ soFar, next, took }) => rule(
1399
+ "reachesUnder",
1400
+ [v("x"), v("c"), v("z"), v("c2"), took],
1401
+ [
1402
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), soFar),
1403
+ lit("hop", v("y"), v("z"), next)
1404
+ ]
1405
+ )
1406
+ ),
1407
+ // The receiver read under a site is that site. The ordinary hop from a
1408
+ // receiver to its class stays in `hop`, so `self` under any context
1409
+ // still comes to the class a single-answer reader wants.
1149
1410
  rule(
1150
- "reaches",
1151
- [v("x"), v("z"), v("kind")],
1411
+ "reachesUnder",
1412
+ [v("x"), v("c"), v("c"), v("c"), VALUE_STEP],
1413
+ [lit("instanceOf", v("x"), v("cls")), lit("allocates", v("c"), v("cls"))],
1414
+ "receiver is the site"
1415
+ ),
1416
+ rule(
1417
+ "reachesUnder",
1418
+ [v("x"), v("c"), v("c2"), v("c2"), v("kind")],
1152
1419
  [
1153
- lit("reaches", v("x"), v("y"), VALUE_STEP),
1154
- lit("stepsTo", v("y"), v("z"), v("kind"))
1420
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), v("kind")),
1421
+ lit("instanceOf", v("y"), v("cls")),
1422
+ lit("allocates", v("c2"), v("cls"))
1423
+ ],
1424
+ "reached receiver is the site"
1425
+ ),
1426
+ // The object an expression refers to under a context. A site is an
1427
+ // answer here, unlike `objectOf`, and the class an instance is one of
1428
+ // is an answer only under no context.
1429
+ rule(
1430
+ "objectOfUnder",
1431
+ [v("site"), v("c"), v("site")],
1432
+ [lit("context", v("c")), lit("allocates", v("site"), v("cls"))],
1433
+ "site is its own object"
1434
+ ),
1435
+ rule(
1436
+ "objectOfUnder",
1437
+ [v("o"), v("c"), v("site")],
1438
+ [
1439
+ lit("reachesUnder", v("o"), v("c"), v("site"), v("c2"), VALUE_STEP),
1440
+ lit("allocates", v("site"), v("cls"))
1441
+ ],
1442
+ "name of a site"
1443
+ ),
1444
+ rule(
1445
+ "objectOfUnder",
1446
+ [v("o"), v("c"), v("obj")],
1447
+ [
1448
+ lit("reachesUnder", v("o"), v("c"), v("obj"), v("c2"), VALUE_STEP),
1449
+ lit("objectValue", v("obj"))
1450
+ ],
1451
+ "written object under a context"
1452
+ ),
1453
+ rule(
1454
+ "objectOfUnder",
1455
+ [v("o"), v("c"), v("obj")],
1456
+ [
1457
+ lit("reachesUnder", v("o"), v("c"), v("obj"), v("c2"), RESULT_STEP),
1458
+ lit("objectValue", v("obj"))
1459
+ ],
1460
+ "returned object under a context"
1461
+ ),
1462
+ rule(
1463
+ "objectOfUnder",
1464
+ [v("o"), NO_CONTEXT, v("cls")],
1465
+ [
1466
+ lit(
1467
+ "reachesUnder",
1468
+ v("o"),
1469
+ NO_CONTEXT,
1470
+ v("cls"),
1471
+ NO_CONTEXT,
1472
+ INSTANCE_STEP
1473
+ ),
1474
+ lit("objectValue", v("cls"))
1475
+ ],
1476
+ "class reached with no context"
1477
+ ),
1478
+ rule(
1479
+ "objectOfUnder",
1480
+ [v("o"), NO_CONTEXT, v("cls")],
1481
+ [lit("instanceOf", v("o"), v("cls"))],
1482
+ "receiver class with no context"
1483
+ ),
1484
+ rule(
1485
+ "objectOfUnder",
1486
+ [v("o"), v("c"), v("o")],
1487
+ [lit("context", v("c")), lit("objectValue", v("o"))],
1488
+ "object under a context is itself"
1489
+ ),
1490
+ // A property read goes on under the site the object was made at, so a
1491
+ // field read off two constructions gives each one its own value. A
1492
+ // class or a literal object keeps the context the walk was already in.
1493
+ rule(
1494
+ "reachesUnder",
1495
+ [v("x"), v("c"), v("held"), v("site"), VALUE_STEP],
1496
+ [
1497
+ lit("context", v("c")),
1498
+ lit("readsProperty", v("x"), v("o"), v("n")),
1499
+ lit("objectOfUnder", v("o"), v("c"), v("site")),
1500
+ lit("allocates", v("site"), v("cls")),
1501
+ lit("contains", v("site"), v("n"), v("held"))
1502
+ ],
1503
+ "property read at a site"
1504
+ ),
1505
+ rule(
1506
+ "reachesUnder",
1507
+ [v("x"), v("c"), v("held"), v("c"), VALUE_STEP],
1508
+ [
1509
+ lit("context", v("c")),
1510
+ lit("readsProperty", v("x"), v("o"), v("n")),
1511
+ lit("objectOfUnder", v("o"), v("c"), v("obj")),
1512
+ lit("objectValue", v("obj")),
1513
+ lit("contains", v("obj"), v("n"), v("held"))
1514
+ ],
1515
+ "property read off an object"
1516
+ ),
1517
+ rule(
1518
+ "reachesUnder",
1519
+ [v("x"), v("c"), v("held"), v("site"), v("kind")],
1520
+ [
1521
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), v("kind")),
1522
+ lit("readsProperty", v("y"), v("o"), v("n")),
1523
+ lit("objectOfUnder", v("o"), v("c2"), v("site")),
1524
+ lit("allocates", v("site"), v("cls")),
1525
+ lit("contains", v("site"), v("n"), v("held"))
1526
+ ],
1527
+ "reached property read at a site"
1528
+ ),
1529
+ rule(
1530
+ "reachesUnder",
1531
+ [v("x"), v("c"), v("held"), v("c2"), v("kind")],
1532
+ [
1533
+ lit("reachesUnder", v("x"), v("c"), v("y"), v("c2"), v("kind")),
1534
+ lit("readsProperty", v("y"), v("o"), v("n")),
1535
+ lit("objectOfUnder", v("o"), v("c2"), v("obj")),
1536
+ lit("objectValue", v("obj")),
1537
+ lit("contains", v("obj"), v("n"), v("held"))
1538
+ ],
1539
+ "reached property read off an object"
1540
+ ),
1541
+ // A parameter goes on at the argument, under the context its caller
1542
+ // was read in, and only from the calls that run the function under the
1543
+ // context the walk is in. This is the one hop that changes context.
1544
+ rule(
1545
+ "reachesUnder",
1546
+ [v("p"), v("c"), v("a"), v("c3"), VALUE_STEP],
1547
+ [
1548
+ lit("context", v("c")),
1549
+ lit("paramOf", v("f"), v("i"), v("p")),
1550
+ lit("entersUnder", v("r"), v("f"), v("c"), v("c3")),
1551
+ lit("callArg", v("r"), v("i"), v("a"))
1552
+ ],
1553
+ "argument under a context"
1554
+ ),
1555
+ rule(
1556
+ "reachesUnder",
1557
+ [v("p"), v("c"), v("a"), v("c3"), VALUE_STEP],
1558
+ [
1559
+ lit("context", v("c")),
1560
+ lit("paramNamed", v("f"), v("n"), v("p")),
1561
+ lit("entersUnder", v("r"), v("f"), v("c"), v("c3")),
1562
+ lit("callKeywordArg", v("r"), v("n"), v("a"))
1563
+ ],
1564
+ "keyword argument under a context"
1565
+ ),
1566
+ rule(
1567
+ "reachesUnder",
1568
+ [v("x"), v("c"), v("a"), v("c3"), v("kind")],
1569
+ [
1570
+ lit("reachesUnder", v("x"), v("c"), v("p"), v("c2"), v("kind")),
1571
+ lit("paramOf", v("f"), v("i"), v("p")),
1572
+ lit("entersUnder", v("r"), v("f"), v("c2"), v("c3")),
1573
+ lit("callArg", v("r"), v("i"), v("a"))
1574
+ ],
1575
+ "reached argument under a context"
1576
+ ),
1577
+ rule(
1578
+ "reachesUnder",
1579
+ [v("x"), v("c"), v("a"), v("c3"), v("kind")],
1580
+ [
1581
+ lit("reachesUnder", v("x"), v("c"), v("p"), v("c2"), v("kind")),
1582
+ lit("paramNamed", v("f"), v("n"), v("p")),
1583
+ lit("entersUnder", v("r"), v("f"), v("c2"), v("c3")),
1584
+ lit("callKeywordArg", v("r"), v("n"), v("a"))
1585
+ ],
1586
+ "reached keyword argument under a context"
1587
+ ),
1588
+ // Which call runs which function under which context. A construction
1589
+ // runs its constructor under the site it makes, a method call runs
1590
+ // under the site its receiver is, and anything else under no context.
1591
+ rule(
1592
+ "entersUnder",
1593
+ [v("site"), v("ctor"), v("site"), v("caller")],
1594
+ [
1595
+ lit("allocates", v("site"), v("cls")),
1596
+ lit("runsConstructor", v("cls"), v("ctor")),
1597
+ lit("callUnder", v("site"), v("caller"))
1598
+ ],
1599
+ "construction enters its constructor"
1600
+ ),
1601
+ rule(
1602
+ "entersUnder",
1603
+ [v("r"), v("f"), v("site"), v("caller")],
1604
+ [
1605
+ lit("call", v("r"), v("cal")),
1606
+ lit("readsProperty", v("cal"), v("o"), v("m")),
1607
+ lit("objectOfUnder", v("o"), v("caller"), v("site")),
1608
+ lit("allocates", v("site"), v("cls")),
1609
+ lit("contains", v("site"), v("m"), v("f")),
1610
+ lit("callUnder", v("r"), v("caller"))
1611
+ ],
1612
+ "method call enters under its receiver"
1613
+ ),
1614
+ // A call written as a name runs with whatever receiver the body
1615
+ // around it has, so a plain function called from a method keeps the
1616
+ // site rather than taking every caller of it.
1617
+ rule(
1618
+ "entersUnder",
1619
+ [v("r"), v("f"), v("c"), v("c")],
1620
+ [
1621
+ lit("context", v("c")),
1622
+ lit("callsNamed", v("r"), v("f")),
1623
+ lit("callUnder", v("r"), v("c"))
1624
+ ],
1625
+ "named call enters under the site it is made in"
1626
+ ),
1627
+ rule(
1628
+ "entersUnder",
1629
+ [v("r"), v("f"), NO_CONTEXT, v("caller")],
1630
+ [
1631
+ lit("callsFunction", v("r"), v("f")),
1632
+ lit("callUnder", v("r"), v("caller"))
1633
+ ],
1634
+ "call enters with no context"
1635
+ ),
1636
+ // The constructor a construction runs, the ancestry included, so a
1637
+ // subclass that declares none still fills in what it inherits.
1638
+ rule(
1639
+ "runsConstructor",
1640
+ [v("cls"), v("f")],
1641
+ [lit("initializes", v("cls"), v("f"))]
1642
+ ),
1643
+ rule(
1644
+ "runsConstructor",
1645
+ [v("cls"), v("f")],
1646
+ [
1647
+ lit("extends", v("cls"), v("b")),
1648
+ lit("comesTo", v("b"), v("base")),
1649
+ lit("runsConstructor", v("base"), v("f"))
1155
1650
  ]
1156
1651
  ),
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.
1652
+ // Which body a call is written in. One adapter states the call it
1653
+ // found and another states the callee, so both spellings are read.
1160
1654
  rule(
1161
- "reaches",
1162
- [v("x"), v("z"), RESULT_STEP],
1655
+ "callInBody",
1656
+ [v("f"), v("r")],
1657
+ [lit("bodyCallsDeep", v("f"), v("r")), lit("call", v("r"), v("c"))]
1658
+ ),
1659
+ rule(
1660
+ "callInBody",
1661
+ [v("f"), v("r")],
1662
+ [lit("bodyCallsDeep", v("f"), v("c")), lit("call", v("r"), v("c"))]
1663
+ ),
1664
+ // The context a call is made under: every site of the class whose
1665
+ // method or constructor it is written in, and no context for a call
1666
+ // outside every method body.
1667
+ rule(
1668
+ "callUnder",
1669
+ [v("r"), v("site")],
1670
+ [
1671
+ lit("callInBody", v("f"), v("r")),
1672
+ lit("holdsProperty", v("cls"), v("m"), v("f")),
1673
+ lit("allocates", v("site"), v("cls"))
1674
+ ],
1675
+ "call in a method"
1676
+ ),
1677
+ rule(
1678
+ "callUnder",
1679
+ [v("r"), v("site")],
1680
+ [
1681
+ lit("callInBody", v("f"), v("r")),
1682
+ lit("initializes", v("cls"), v("f")),
1683
+ lit("allocates", v("site"), v("cls"))
1684
+ ],
1685
+ "call in a constructor"
1686
+ ),
1687
+ // A plain function entered under a site makes its own calls under
1688
+ // that site, so a chain of plain functions off one method keeps it.
1689
+ rule(
1690
+ "callUnder",
1691
+ [v("r"), v("c")],
1692
+ [
1693
+ lit("callOutsideMethod", v("r")),
1694
+ lit("callInBody", v("f"), v("r")),
1695
+ lit("entersUnder", v("into"), v("f"), v("c"), v("c4"))
1696
+ ],
1697
+ "call in a function entered under a site"
1698
+ ),
1699
+ rule(
1700
+ "callUnder",
1701
+ [v("r"), NO_CONTEXT],
1702
+ [lit("callOutsideMethod", v("r"))],
1703
+ "call outside every method"
1704
+ ),
1705
+ // The two questions a caller puts under a context, stopping where
1706
+ // `isWrittenAs` and `comesTo` stop, whatever context the walk ended in.
1707
+ rule(
1708
+ "isWrittenAsUnder",
1709
+ [v("x"), v("c"), v("x")],
1710
+ [lit("context", v("c")), lit("writtenValue", v("x"))]
1711
+ ),
1712
+ rule(
1713
+ "isWrittenAsUnder",
1714
+ [v("x"), v("c"), v("x")],
1715
+ [lit("context", v("c")), lit("objectValue", v("x"))]
1716
+ ),
1717
+ rule(
1718
+ "isWrittenAsUnder",
1719
+ [v("x"), v("c"), v("z")],
1163
1720
  [
1164
- lit("reaches", v("x"), v("y"), RESULT_STEP),
1165
- lit("stepsTo", v("y"), v("z"), VALUE_STEP)
1721
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1722
+ lit("writtenValue", v("z"))
1166
1723
  ]
1167
1724
  ),
1168
1725
  rule(
1169
- "reaches",
1170
- [v("x"), v("z"), RESULT_STEP],
1726
+ "isWrittenAsUnder",
1727
+ [v("x"), v("c"), v("z")],
1171
1728
  [
1172
- lit("reaches", v("x"), v("y"), RESULT_STEP),
1173
- lit("stepsTo", v("y"), v("z"), RESULT_STEP)
1729
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1730
+ lit("objectValue", v("z"))
1731
+ ]
1732
+ ),
1733
+ rule(
1734
+ "comesToUnder",
1735
+ [v("x"), v("c"), v("x")],
1736
+ [lit("context", v("c")), lit("func", v("x"))]
1737
+ ),
1738
+ rule(
1739
+ "comesToUnder",
1740
+ [v("x"), v("c"), v("x")],
1741
+ [lit("context", v("c")), lit("objectValue", v("x"))]
1742
+ ),
1743
+ rule(
1744
+ "comesToUnder",
1745
+ [v("x"), v("c"), v("z")],
1746
+ [
1747
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1748
+ lit("func", v("z"))
1749
+ ]
1750
+ ),
1751
+ rule(
1752
+ "comesToUnder",
1753
+ [v("x"), v("c"), v("z")],
1754
+ [
1755
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), VALUE_STEP),
1756
+ lit("objectValue", v("z"))
1757
+ ]
1758
+ ),
1759
+ rule(
1760
+ "comesToUnder",
1761
+ [v("x"), v("c"), v("z")],
1762
+ [
1763
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), INSTANCE_STEP),
1764
+ lit("func", v("z"))
1765
+ ]
1766
+ ),
1767
+ rule(
1768
+ "comesToUnder",
1769
+ [v("x"), v("c"), v("z")],
1770
+ [
1771
+ lit("reachesUnder", v("x"), v("c"), v("z"), v("c2"), INSTANCE_STEP),
1772
+ lit("objectValue", v("z"))
1174
1773
  ]
1175
1774
  ),
1176
1775
  // What a value comes down to: the walk that never ran a call, stopped
@@ -1188,6 +1787,18 @@ var RESOLUTION_RULES = [
1188
1787
  [v("x"), v("z")],
1189
1788
  [lit("reaches", v("x"), v("z"), VALUE_STEP), lit("objectValue", v("z"))]
1190
1789
  ),
1790
+ // An instance walk stops the same way, so a method read off
1791
+ // `new App()` still finds what the class declares.
1792
+ rule(
1793
+ "comesTo",
1794
+ [v("x"), v("z")],
1795
+ [lit("reaches", v("x"), v("z"), INSTANCE_STEP), lit("func", v("z"))]
1796
+ ),
1797
+ rule(
1798
+ "comesTo",
1799
+ [v("x"), v("z")],
1800
+ [lit("reaches", v("x"), v("z"), INSTANCE_STEP), lit("objectValue", v("z"))]
1801
+ ),
1191
1802
  // The same stopping condition, for the walk that ran a call. A call
1192
1803
  // gets no `comesTo` answer on purpose: coming back with what a
1193
1804
  // factory returned would fight the unwrapping answer.
@@ -1228,6 +1839,13 @@ var RESOLUTION_RULES = [
1228
1839
  lit("isWrittenAs", v("ret"), v("z"))
1229
1840
  ]
1230
1841
  ),
1842
+ // Which expression running f hands back, whether f returns it, writes
1843
+ // it into a name first, or ends on it as a shorthand body.
1844
+ rule(
1845
+ "returnsCall",
1846
+ [v("f"), v("c")],
1847
+ [lit("returnsValue", v("f"), v("v")), lit("isWrittenAs", v("v"), v("c"))]
1848
+ ),
1231
1849
  // What one call site put in a parameter, told apart from what the
1232
1850
  // other callers passed.
1233
1851
  rule(
@@ -1325,18 +1943,34 @@ var RESOLUTION_RULES = [
1325
1943
  [v("x"), v("obj")],
1326
1944
  [lit("givesBack", v("x"), v("obj")), lit("objectValue", v("obj"))]
1327
1945
  ),
1946
+ // A construction is the object it made, and so is any name for it.
1947
+ // The class stays an answer too, so an instance whose site is not in
1948
+ // the run still reads what the class stores.
1949
+ rule(
1950
+ "objectOf",
1951
+ [v("site"), v("site")],
1952
+ [lit("allocates", v("site"), v("c"))]
1953
+ ),
1954
+ rule(
1955
+ "objectOf",
1956
+ [v("o"), v("site")],
1957
+ [
1958
+ lit("reaches", v("o"), v("site"), VALUE_STEP),
1959
+ lit("allocates", v("site"), v("c"))
1960
+ ]
1961
+ ),
1328
1962
  // Which calls a function, found by the name the call is written as
1329
1963
  // rather than by resolving every callee in the project. A caller knows
1330
1964
  // the function and wants its call sites, so both of these start from
1331
1965
  // the function. Starting from `call` instead asks what every call in
1332
1966
  // the project imports, which was 72% of everything derived.
1333
1967
  rule(
1334
- "callsFunction",
1968
+ "callsNamed",
1335
1969
  [v("r"), v("f")],
1336
1970
  [lit("binds", v("c"), v("f")), lit("call", v("r"), v("c"))]
1337
1971
  ),
1338
1972
  rule(
1339
- "callsFunction",
1973
+ "callsNamed",
1340
1974
  [v("r"), v("f")],
1341
1975
  [
1342
1976
  lit("moduleExport", v("m"), v("n"), v("f")),
@@ -1347,7 +1981,7 @@ var RESOLUTION_RULES = [
1347
1981
  // The same, for a language whose adapter writes the import down as a
1348
1982
  // declaration and the call's callee as the name referring to it.
1349
1983
  rule(
1350
- "callsFunction",
1984
+ "callsNamed",
1351
1985
  [v("r"), v("f")],
1352
1986
  [
1353
1987
  lit("moduleExport", v("m"), v("n"), v("f")),
@@ -1356,6 +1990,36 @@ var RESOLUTION_RULES = [
1356
1990
  lit("call", v("r"), v("c"))
1357
1991
  ]
1358
1992
  ),
1993
+ // `const f = (x) => ...` declares the name and puts the parameters on
1994
+ // the arrow, so everything above arrives at the declaration and
1995
+ // `paramOf` is about the arrow. One binds hop joins the two.
1996
+ rule(
1997
+ "callsNamed",
1998
+ [v("r"), v("f")],
1999
+ [lit("binds", v("g"), v("f")), lit("callsNamed", v("r"), v("g"))]
2000
+ ),
2001
+ // Every one of those, and the binds hop again over the whole
2002
+ // relation, so a property holding a name for a function is reached
2003
+ // the way it was before the two were told apart.
2004
+ rule("callsFunction", [v("r"), v("f")], [lit("callsNamed", v("r"), v("f"))]),
2005
+ rule(
2006
+ "callsFunction",
2007
+ [v("r"), v("f")],
2008
+ [lit("binds", v("g"), v("f")), lit("callsFunction", v("r"), v("g"))]
2009
+ ),
2010
+ // A function written on an object, called off it: `client.get(o)`.
2011
+ // Every rule above joins on a name the callee binds to, and a read of
2012
+ // a property binds to nothing.
2013
+ rule(
2014
+ "callsFunction",
2015
+ [v("r"), v("f")],
2016
+ [
2017
+ lit("holdsProperty", v("obj"), v("n"), v("f")),
2018
+ lit("readsProperty", v("c"), v("o"), v("n")),
2019
+ lit("objectOf", v("o"), v("obj")),
2020
+ lit("call", v("r"), v("c"))
2021
+ ]
2022
+ ),
1359
2023
  // What an object contains, its base class included, so a method the base
1360
2024
  // declares is found on a subclass that never overrode it. A method both
1361
2025
  // declare gives two, and the caller decides. This is its own relation
@@ -1373,7 +2037,93 @@ var RESOLUTION_RULES = [
1373
2037
  lit("extends", v("cls"), v("base")),
1374
2038
  lit("comesTo", v("base"), v("baseCls")),
1375
2039
  lit("contains", v("baseCls"), v("n"), v("held"))
1376
- ]
2040
+ ],
2041
+ BASE_CLASS_RULE
2042
+ ),
2043
+ // An association is read off an instance as a property, and stating
2044
+ // it as `contains` is what puts it on the ancestry rule, so a concern
2045
+ // or a base class can be the one that declares it.
2046
+ rule(
2047
+ "contains",
2048
+ [v("cls"), v("n"), v("t")],
2049
+ [lit("declaresAssociation", v("cls"), v("n"), v("t"))],
2050
+ "declared association"
2051
+ ),
2052
+ // The same, for a language that buries the declaration in an ordinary
2053
+ // assignment. Keying on the module the callable came from is what
2054
+ // keeps a project function spelled the same way out.
2055
+ rule(
2056
+ "contains",
2057
+ [v("cls"), v("n"), v("t")],
2058
+ [
2059
+ lit("fieldCall", v("cls"), v("n"), v("c"), v("t")),
2060
+ lit("comesFrom", v("c"), v("m"), v("name")),
2061
+ lit("associationConstructor", v("m"), v("name"))
2062
+ ],
2063
+ "constructed association"
2064
+ ),
2065
+ // What the constructor put on the receiver. The class is the object
2066
+ // for an instance whose own site is not in the run.
2067
+ rule(
2068
+ "contains",
2069
+ [v("cls"), v("n"), v("held")],
2070
+ [
2071
+ lit("initializes", v("cls"), v("f")),
2072
+ lit("storesProperty", v("f"), v("n"), v("held"))
2073
+ ],
2074
+ "constructor store"
2075
+ ),
2076
+ // What any other method of the class put there. Two methods writing
2077
+ // one name give two rows, since nothing here orders them.
2078
+ rule(
2079
+ "contains",
2080
+ [v("cls"), v("n"), v("held")],
2081
+ [
2082
+ lit("holdsProperty", v("cls"), v("m"), v("f")),
2083
+ lit("storesProperty", v("f"), v("n"), v("held"))
2084
+ ],
2085
+ "method store"
2086
+ ),
2087
+ // A write through a name rather than through the receiver,
2088
+ // `client.timeout = 5`, which lands on whatever that name refers to.
2089
+ rule(
2090
+ "contains",
2091
+ [v("obj"), v("n"), v("held")],
2092
+ [
2093
+ lit("storesProperty", v("r"), v("n"), v("held")),
2094
+ lit("objectOf", v("r"), v("obj"))
2095
+ ],
2096
+ "named receiver store"
2097
+ ),
2098
+ // A call written with a class's own name, which is how most languages
2099
+ // spell a construction. A language that spells one some other way
2100
+ // states its own rule for this, the way Ruby does for `Const.new`.
2101
+ rule(
2102
+ "constructsNamed",
2103
+ [v("r"), v("cls")],
2104
+ [lit("objectValue", v("cls")), lit("callsNamed", v("r"), v("cls"))]
2105
+ ),
2106
+ // Each construction is an object of its own containing what its
2107
+ // class stores, so two sites are two objects.
2108
+ rule(
2109
+ "contains",
2110
+ [v("site"), v("n"), v("held")],
2111
+ [
2112
+ lit("allocates", v("site"), v("cls")),
2113
+ lit("contains", v("cls"), v("n"), v("held"))
2114
+ ],
2115
+ "allocated instance"
2116
+ ),
2117
+ // A call that makes one of a class. Every finder a pack declared is
2118
+ // one too, so `User.find(1)` is a site the same way `User.new` is.
2119
+ rule(
2120
+ "allocates",
2121
+ [v("site"), v("cls")],
2122
+ [
2123
+ lit("call", v("site"), v("c")),
2124
+ lit("stepsTo", v("site"), v("cls"), INSTANCE_STEP)
2125
+ ],
2126
+ "allocation site"
1377
2127
  ),
1378
2128
  // The library base a class's ancestry arrives at, however many of a
1379
2129
  // project's own classes sit in between. A base the run declares has a
@@ -1464,8 +2214,8 @@ var RESOLUTION_RULES = [
1464
2214
  ]
1465
2215
  ),
1466
2216
  // Where a name comes from, when what it refers to lives outside the
1467
- // source being read. A walk ends at something written out in source,
1468
- // so it never reaches a library's own function.
2217
+ // source being read. A walk ends at what the source writes out, so it
2218
+ // never reaches a library's own function; these rules do.
1469
2219
  rule(
1470
2220
  "comesFrom",
1471
2221
  [v("x"), v("m"), v("n")],
@@ -1479,6 +2229,28 @@ var RESOLUTION_RULES = [
1479
2229
  lit("imports", v("y"), v("m"), v("n"))
1480
2230
  ]
1481
2231
  ),
2232
+ // A member read off a whole-module import: the member's own name is
2233
+ // what the module exports, so `fastapi.APIRouter` comes from
2234
+ // fastapi's `APIRouter`. Directly, or through a name for the module.
2235
+ rule(
2236
+ "comesFrom",
2237
+ [v("x"), v("m"), v("n")],
2238
+ [
2239
+ lit("readsProperty", v("x"), v("ns"), v("n")),
2240
+ lit("imports", v("ns"), v("m"), NAMESPACE_IMPORT)
2241
+ ],
2242
+ NAMESPACE_MEMBER_RULE
2243
+ ),
2244
+ rule(
2245
+ "comesFrom",
2246
+ [v("x"), v("m"), v("n")],
2247
+ [
2248
+ lit("readsProperty", v("x"), v("o"), v("n")),
2249
+ lit("reaches", v("o"), v("ns"), VALUE_STEP),
2250
+ lit("imports", v("ns"), v("m"), NAMESPACE_IMPORT)
2251
+ ],
2252
+ NAMESPACE_MEMBER_RULE
2253
+ ),
1482
2254
  // Calling f ends up calling the name n that module m exports, one
1483
2255
  // hop through f's own body or deeper through a wrapper of a wrapper.
1484
2256
  // Several results is normal: a composed decorator applies them all.
@@ -1508,6 +2280,13 @@ var RESOLUTION_RULES = [
1508
2280
  [lit("comesTo", v("x"), v("z")), lit("func", v("z"))]
1509
2281
  )
1510
2282
  ];
2283
+ function alsoSteps(rules) {
2284
+ return [
2285
+ ...rules,
2286
+ ...rules.filter((r) => r.head.relation === "hop").map((r) => ({ ...r, head: { ...r.head, relation: "stepsTo" } }))
2287
+ ];
2288
+ }
2289
+ var RESOLUTION_RULES = alsoSteps(STATED_RULES);
1511
2290
  var RESOLUTION_QUESTIONS = [
1512
2291
  rule(
1513
2292
  "wantedResolves",
@@ -1530,12 +2309,55 @@ var RESOLUTION_QUESTIONS = [
1530
2309
  [lit("wanted", v("x")), lit("isWrittenAs", v("x"), v("z"))]
1531
2310
  ),
1532
2311
  // A call is given no `comesTo`, so this is the only way to ask what
1533
- // object one arrives at, and without it a demand-driven run derives
1534
- // `objectOf` nowhere.
2312
+ // object one arrives at, and a demand-driven run derives `objectOf`
2313
+ // nowhere without it. An allocation site is not one of the answers.
1535
2314
  rule(
1536
2315
  "wantedObjectOf",
1537
2316
  [v("x"), v("z")],
1538
- [lit("wanted", v("x")), lit("objectOf", v("x"), v("z"))]
2317
+ [
2318
+ lit("wanted", v("x")),
2319
+ lit("objectOf", v("x"), v("z")),
2320
+ lit("objectValue", v("z"))
2321
+ ]
2322
+ ),
2323
+ // The same three questions under one allocation site, keyed by the
2324
+ // value and the site both. An allocation site is an answer here,
2325
+ // unlike `wantedObjectOf`, since a site is what a context is.
2326
+ rule(
2327
+ "wantedIsWrittenAsUnder",
2328
+ [v("x"), v("c"), v("z")],
2329
+ [
2330
+ lit("wantedUnder", v("x"), v("c")),
2331
+ lit("isWrittenAsUnder", v("x"), v("c"), v("z"))
2332
+ ]
2333
+ ),
2334
+ // Where a class was made, for a caller about to ask a value question
2335
+ // under each. Going through `constructsNamed` keeps the demand on the
2336
+ // calls written with the class's name rather than on every call.
2337
+ rule(
2338
+ "wantedAllocatedAt",
2339
+ [v("cls"), v("site")],
2340
+ [
2341
+ lit("wantedSites", v("cls")),
2342
+ lit("constructsNamed", v("site"), v("cls")),
2343
+ lit("allocates", v("site"), v("cls"))
2344
+ ]
2345
+ ),
2346
+ rule(
2347
+ "wantedComesToUnder",
2348
+ [v("x"), v("c"), v("z")],
2349
+ [
2350
+ lit("wantedUnder", v("x"), v("c")),
2351
+ lit("comesToUnder", v("x"), v("c"), v("z"))
2352
+ ]
2353
+ ),
2354
+ rule(
2355
+ "wantedObjectOfUnder",
2356
+ [v("x"), v("c"), v("z")],
2357
+ [
2358
+ lit("wantedUnder", v("x"), v("c")),
2359
+ lit("objectOfUnder", v("x"), v("c"), v("z"))
2360
+ ]
1539
2361
  ),
1540
2362
  // The same for the function a call returns: `app.use(requireCaller(config))`
1541
2363
  // registers what the factory gives back, and `resolves` on the call
@@ -1552,6 +2374,19 @@ var RESOLUTION_QUESTIONS = [
1552
2374
  [v("p"), v("r"), v("z")],
1553
2375
  [lit("wanted", v("p")), lit("paramAt", v("r"), v("p"), v("z"))]
1554
2376
  ),
2377
+ // The argument as the caller wrote it. A parameter given a GraphQL
2378
+ // document has no `paramAt` answer, since that settles through
2379
+ // `comesTo`, which stops at a function or an object.
2380
+ rule(
2381
+ "wantedPassesArgument",
2382
+ [v("p"), v("r"), v("a")],
2383
+ [lit("wanted", v("p")), lit("passesArgument", v("r"), v("p"), v("a"))]
2384
+ ),
2385
+ rule(
2386
+ "wantedReturnsCall",
2387
+ [v("f"), v("c")],
2388
+ [lit("wanted", v("f")), lit("returnsCall", v("f"), v("c"))]
2389
+ ),
1555
2390
  rule(
1556
2391
  "wantedComesFrom",
1557
2392
  [v("x"), v("m"), v("n")],
@@ -1760,6 +2595,14 @@ var RESOLUTION_QUESTIONS = [
1760
2595
  lit("holdsProperty", v("b"), v("n"), v("held"))
1761
2596
  ]
1762
2597
  ),
2598
+ // A method the class gets under a name the source computes rather than
2599
+ // writes out. The adapter settles the name against the same facts and
2600
+ // says so, and the chain reports it beside the ones written out.
2601
+ rule(
2602
+ "wantedDeclaredName",
2603
+ [v("c"), v("n")],
2604
+ [lit("ancestryChain", v("c"), v("b")), lit("declaresName", v("b"), v("n"))]
2605
+ ),
1763
2606
  rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
1764
2607
  rule(
1765
2608
  "anchorChain",
@@ -1807,35 +2650,58 @@ var RESOLUTION_QUESTIONS = [
1807
2650
  var ANSWER_RELATIONS = [
1808
2651
  ...new Set(RESOLUTION_QUESTIONS.map((r) => r.head.relation))
1809
2652
  ];
2653
+ var UNDER_ANSWER_RELATIONS = [
2654
+ "wantedIsWrittenAsUnder",
2655
+ "wantedComesToUnder",
2656
+ "wantedObjectOfUnder"
2657
+ ];
1810
2658
  export {
1811
2659
  ANSWER_RELATIONS,
1812
2660
  ASKING_RELATIONS,
2661
+ BASE_CLASS_RULE,
1813
2662
  FACT_CONTRACT_CASES,
2663
+ INSTANCE_STEP,
1814
2664
  NAMESPACE_IMPORT,
1815
2665
  NAMESPACE_IMPORT_NAME,
2666
+ NAMESPACE_MEMBER_RULE,
2667
+ NO_CONTEXT,
2668
+ NO_CONTEXT_NAME,
1816
2669
  RESOLUTION_QUESTIONS,
1817
2670
  RESOLUTION_RULES,
1818
2671
  RESULT_STEP,
2672
+ UNDER_ANSWER_RELATIONS,
1819
2673
  VALUE_STEP,
2674
+ addPackWords,
1820
2675
  agreedMountPrefix,
2676
+ allocationSitesOf,
2677
+ alsoSteps,
2678
+ answersByKey,
1821
2679
  askResolution,
2680
+ askResolutionUnder,
1822
2681
  calleeOutcomeOf,
1823
2682
  calleeOutcomes,
1824
2683
  checkFactContract,
2684
+ comesToUnder,
1825
2685
  couldBeSettled,
1826
2686
  explainResolutionProof,
1827
2687
  explainResolvedKey,
2688
+ isWrittenAsUnder,
1828
2689
  joinMountedPath,
1829
2690
  mountPathsOf,
1830
2691
  nodeOfKey,
2692
+ objectOfUnder,
1831
2693
  placeholderValues,
1832
2694
  queryFacts,
1833
2695
  renderExplanation,
1834
2696
  resolutionProgram,
2697
+ resolutionUnderProgram,
1835
2698
  singleAnswers,
1836
2699
  startsAtName,
1837
2700
  valueLeftByWrites,
1838
2701
  writesRunInOrder,
1839
2702
  writtenSourcesOf,
1840
- writtenValueOf
2703
+ writtenValueOf,
2704
+ writtenValueUnder,
2705
+ writtenValuesByKey,
2706
+ writtenValuesOf
1841
2707
  };