@suss/resolution 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/index.cjs +824 -352
- package/dist/index.d.cts +242 -2
- package/dist/index.d.ts +242 -2
- package/dist/index.js +463 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,263 @@
|
|
|
1
|
+
// src/program.ts
|
|
2
|
+
import { clearRelations, deriveOnDemand, evaluate } from "@suss/datalog";
|
|
3
|
+
var ASKING_RELATIONS = [
|
|
4
|
+
"wanted",
|
|
5
|
+
"wantedOrigin",
|
|
6
|
+
"wantedCallOrigin",
|
|
7
|
+
"wantedExportsOf",
|
|
8
|
+
"wantedSubject",
|
|
9
|
+
"wantedAnchor",
|
|
10
|
+
"wantedAncestry"
|
|
11
|
+
];
|
|
12
|
+
var NO_LANGUAGE_RULES = [];
|
|
13
|
+
var programs = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
function resolutionProgram(languageRules = NO_LANGUAGE_RULES) {
|
|
15
|
+
const built = programs.get(languageRules);
|
|
16
|
+
if (built !== void 0) {
|
|
17
|
+
return built;
|
|
18
|
+
}
|
|
19
|
+
const rules = [
|
|
20
|
+
...RESOLUTION_RULES,
|
|
21
|
+
...languageRules,
|
|
22
|
+
...RESOLUTION_QUESTIONS
|
|
23
|
+
];
|
|
24
|
+
const program = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? { rules, demandDriven: [], demands: [] } : deriveOnDemand(rules, ANSWER_RELATIONS);
|
|
25
|
+
programs.set(languageRules, program);
|
|
26
|
+
return program;
|
|
27
|
+
}
|
|
28
|
+
function queryFacts(program) {
|
|
29
|
+
if (program.demandDriven.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return [...program.demandDriven, ...ASKING_RELATIONS];
|
|
33
|
+
}
|
|
34
|
+
var askedByDb = /* @__PURE__ */ new WeakMap();
|
|
35
|
+
function askedOf(db, asking) {
|
|
36
|
+
let byRelation = askedByDb.get(db);
|
|
37
|
+
if (byRelation === void 0) {
|
|
38
|
+
byRelation = /* @__PURE__ */ new Map();
|
|
39
|
+
askedByDb.set(db, byRelation);
|
|
40
|
+
}
|
|
41
|
+
let asked = byRelation.get(asking);
|
|
42
|
+
if (asked === void 0) {
|
|
43
|
+
asked = /* @__PURE__ */ new Set();
|
|
44
|
+
byRelation.set(asking, asked);
|
|
45
|
+
}
|
|
46
|
+
return asked;
|
|
47
|
+
}
|
|
48
|
+
function askResolution(db, keys, asking = "wanted", program = resolutionProgram()) {
|
|
49
|
+
const asked = askedOf(db, asking);
|
|
50
|
+
const fresh = [...keys].filter((key) => !asked.has(key));
|
|
51
|
+
if (fresh.length === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const key of fresh) {
|
|
55
|
+
asked.add(key);
|
|
56
|
+
db.add(asking, [key]);
|
|
57
|
+
}
|
|
58
|
+
evaluate(db, program.rules);
|
|
59
|
+
const forget = queryFacts(program);
|
|
60
|
+
if (forget.length > 0) {
|
|
61
|
+
clearRelations(db, program.rules, forget);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/callee.ts
|
|
66
|
+
var UNDECLARED = { kind: "undeclared" };
|
|
67
|
+
var UNSETTLED = { kind: "unsettled" };
|
|
68
|
+
var OUTSIDE_RUN = { kind: "outsideRun" };
|
|
69
|
+
function calleeOutcomes(db, keys, program = resolutionProgram()) {
|
|
70
|
+
askAboutSources(db, keys, program);
|
|
71
|
+
const reading = new Reading(db);
|
|
72
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const key of keys) {
|
|
74
|
+
outcomes.set(key, reading.outcomeOf(key));
|
|
75
|
+
}
|
|
76
|
+
return outcomes;
|
|
77
|
+
}
|
|
78
|
+
function calleeOutcomeOf(db, key, program = resolutionProgram()) {
|
|
79
|
+
return calleeOutcomes(db, [key], program).get(key) ?? UNDECLARED;
|
|
80
|
+
}
|
|
81
|
+
function askAboutSources(db, keys, program) {
|
|
82
|
+
let wave = [...new Set(keys)];
|
|
83
|
+
const asked = new Set(wave);
|
|
84
|
+
while (wave.length > 0) {
|
|
85
|
+
askResolution(db, wave, "wanted", program);
|
|
86
|
+
const next = [];
|
|
87
|
+
for (const key of wave) {
|
|
88
|
+
if (!decidedByCallers(db, key) && answersFor(db, key).length > 0) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
for (const source of sourcesOf(db, key)) {
|
|
92
|
+
if (!asked.has(source)) {
|
|
93
|
+
asked.add(source);
|
|
94
|
+
next.push(source);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
wave = next;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function couldBeSettled(db, key) {
|
|
102
|
+
return db.has("func", [key]) || db.has("objectValue", [key]) || sourcesOf(db, key).length > 0;
|
|
103
|
+
}
|
|
104
|
+
function sourcesOf(db, key) {
|
|
105
|
+
const base = db.lookup("readsProperty", 0, key)[0];
|
|
106
|
+
if (base !== void 0) {
|
|
107
|
+
return [String(base[1])];
|
|
108
|
+
}
|
|
109
|
+
const call = db.lookup("call", 0, key)[0];
|
|
110
|
+
if (call !== void 0) {
|
|
111
|
+
return [String(call[1])];
|
|
112
|
+
}
|
|
113
|
+
return writtenSourcesOf(db, key);
|
|
114
|
+
}
|
|
115
|
+
var WRITE_RELATIONS = ["binds", "endsHolding", "mayHold"];
|
|
116
|
+
function writtenSourcesOf(db, key) {
|
|
117
|
+
const found = [];
|
|
118
|
+
for (const relation of WRITE_RELATIONS) {
|
|
119
|
+
for (const row of db.lookup(relation, 0, key)) {
|
|
120
|
+
found.push(String(row[1]));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return found;
|
|
124
|
+
}
|
|
125
|
+
function answersFor(db, key) {
|
|
126
|
+
const found = /* @__PURE__ */ new Map();
|
|
127
|
+
for (const row of db.lookup("wantedComesTo", 0, key)) {
|
|
128
|
+
const answer = String(row[1]);
|
|
129
|
+
if (db.has("func", [answer])) {
|
|
130
|
+
found.set(answer, { kind: "function", key: answer });
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (db.has("objectValue", [answer])) {
|
|
134
|
+
found.set(answer, { kind: "object", key: answer });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return [...found.values()];
|
|
138
|
+
}
|
|
139
|
+
function outcomeKey(outcome) {
|
|
140
|
+
if (outcome.kind === "function" || outcome.kind === "object") {
|
|
141
|
+
return `${outcome.kind}:${outcome.key}`;
|
|
142
|
+
}
|
|
143
|
+
return outcome.kind;
|
|
144
|
+
}
|
|
145
|
+
var THROUGH_BASE = {
|
|
146
|
+
function: UNDECLARED,
|
|
147
|
+
object: UNDECLARED
|
|
148
|
+
};
|
|
149
|
+
var THROUGH_CALL = {
|
|
150
|
+
function: UNSETTLED,
|
|
151
|
+
object: UNSETTLED
|
|
152
|
+
};
|
|
153
|
+
var Reading = class {
|
|
154
|
+
constructor(db) {
|
|
155
|
+
this.db = db;
|
|
156
|
+
}
|
|
157
|
+
db;
|
|
158
|
+
memo = /* @__PURE__ */ new Map();
|
|
159
|
+
walking = /* @__PURE__ */ new Set();
|
|
160
|
+
outcomeOf(key) {
|
|
161
|
+
return this.walk(key).outcome;
|
|
162
|
+
}
|
|
163
|
+
walk(key) {
|
|
164
|
+
const known = this.memo.get(key);
|
|
165
|
+
if (known !== void 0) {
|
|
166
|
+
return { outcome: known, looped: false };
|
|
167
|
+
}
|
|
168
|
+
if (this.walking.has(key)) {
|
|
169
|
+
return { outcome: UNDECLARED, looped: true };
|
|
170
|
+
}
|
|
171
|
+
this.walking.add(key);
|
|
172
|
+
const found = this.decide(key);
|
|
173
|
+
this.walking.delete(key);
|
|
174
|
+
if (!found.looped) {
|
|
175
|
+
this.memo.set(key, found.outcome);
|
|
176
|
+
}
|
|
177
|
+
return found;
|
|
178
|
+
}
|
|
179
|
+
decide(key) {
|
|
180
|
+
if (decidedByCallers(this.db, key)) {
|
|
181
|
+
return this.fromWrites(key);
|
|
182
|
+
}
|
|
183
|
+
const answers = answersFor(this.db, key);
|
|
184
|
+
if (answers.length > 1) {
|
|
185
|
+
return {
|
|
186
|
+
outcome: { kind: "severalSources", sources: answers.map(outcomeKey) },
|
|
187
|
+
looped: false
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const only = answers[0];
|
|
191
|
+
if (only !== void 0) {
|
|
192
|
+
return { outcome: only, looped: false };
|
|
193
|
+
}
|
|
194
|
+
return this.refusal(key);
|
|
195
|
+
}
|
|
196
|
+
refusal(key) {
|
|
197
|
+
const base = this.db.lookup("readsProperty", 0, key)[0];
|
|
198
|
+
if (base !== void 0) {
|
|
199
|
+
return this.through(THROUGH_BASE, String(base[1]));
|
|
200
|
+
}
|
|
201
|
+
const call = this.db.lookup("call", 0, key)[0];
|
|
202
|
+
if (call !== void 0) {
|
|
203
|
+
return this.through(THROUGH_CALL, String(call[1]));
|
|
204
|
+
}
|
|
205
|
+
return this.fromWrites(key);
|
|
206
|
+
}
|
|
207
|
+
through(table, source) {
|
|
208
|
+
const found = this.walk(source);
|
|
209
|
+
return {
|
|
210
|
+
outcome: table[found.outcome.kind] ?? found.outcome,
|
|
211
|
+
looped: found.looped
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* What the writes to a name, and the arguments a call put in a
|
|
216
|
+
* parameter, agree the value is. A source that says nothing is left
|
|
217
|
+
* out, so a name narrowed through a second name keeps the outcome it
|
|
218
|
+
* started with rather than counting as a second source.
|
|
219
|
+
*/
|
|
220
|
+
fromWrites(key) {
|
|
221
|
+
const found = /* @__PURE__ */ new Map();
|
|
222
|
+
let looped = false;
|
|
223
|
+
for (const source of writtenSourcesOf(this.db, key)) {
|
|
224
|
+
const from = this.walk(source);
|
|
225
|
+
looped = looped || from.looped;
|
|
226
|
+
if (from.outcome.kind !== "undeclared") {
|
|
227
|
+
found.set(outcomeKey(from.outcome), from.outcome);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (isParameter(this.db, key)) {
|
|
231
|
+
found.set("callerSupplied", { kind: "callerSupplied", key });
|
|
232
|
+
}
|
|
233
|
+
if (found.size > 1) {
|
|
234
|
+
return {
|
|
235
|
+
outcome: { kind: "severalSources", sources: [...found.keys()] },
|
|
236
|
+
looped
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const only = [...found.values()][0];
|
|
240
|
+
return { outcome: only ?? whereItCameFrom(this.db, key), looped };
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
function isParameter(db, key) {
|
|
244
|
+
return db.lookup("paramNamed", 2, key).length > 0 || db.lookup("paramOf", 2, key).length > 0;
|
|
245
|
+
}
|
|
246
|
+
function decidedByCallers(db, key) {
|
|
247
|
+
return isParameter(db, key) && db.lookup("binds", 0, key).length === 0;
|
|
248
|
+
}
|
|
249
|
+
function whereItCameFrom(db, key) {
|
|
250
|
+
for (const row of db.lookup("imports", 0, key)) {
|
|
251
|
+
if (db.lookup("exportsAs", 0, String(row[1])).length === 0) {
|
|
252
|
+
return OUTSIDE_RUN;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (db.has("writtenValue", [key])) {
|
|
256
|
+
return OUTSIDE_RUN;
|
|
257
|
+
}
|
|
258
|
+
return db.has("writesUnstated", [key]) ? UNSETTLED : UNDECLARED;
|
|
259
|
+
}
|
|
260
|
+
|
|
1
261
|
// src/contract.ts
|
|
2
262
|
var distinct = (values) => new Set(values).size;
|
|
3
263
|
var RELATIONS_THAT_CAN_CROSS = ["imports", "binds", "reExports"];
|
|
@@ -273,6 +533,38 @@ var DEFAULT_PHRASES = {
|
|
|
273
533
|
]
|
|
274
534
|
};
|
|
275
535
|
},
|
|
536
|
+
"declared finder": ({ tuple, premises, describe }) => {
|
|
537
|
+
const method = String(premises[1].tuple[2]);
|
|
538
|
+
const base = String(premises[4].tuple[0]);
|
|
539
|
+
return {
|
|
540
|
+
reason: `${describe(tuple[0])} calls ${method} on ${describe(tuple[1])}, which gives back one of it`,
|
|
541
|
+
assumptions: [
|
|
542
|
+
`a pack declares that ${method} on a class extending ${base} gives back one of that class`
|
|
543
|
+
]
|
|
544
|
+
};
|
|
545
|
+
},
|
|
546
|
+
"declared argument finder": ({ tuple, premises, describe }) => {
|
|
547
|
+
const method = String(premises[1].tuple[2]);
|
|
548
|
+
const position = String(premises[2].tuple[1]);
|
|
549
|
+
const base = String(premises[3].tuple[0]);
|
|
550
|
+
return {
|
|
551
|
+
reason: `${describe(tuple[0])} calls ${method} with ${describe(tuple[1])} at argument ${position}, which gives back one of it`,
|
|
552
|
+
assumptions: [
|
|
553
|
+
`a pack declares that ${method} with a class extending ${base} at argument ${position} gives back one of that class`
|
|
554
|
+
]
|
|
555
|
+
};
|
|
556
|
+
},
|
|
557
|
+
"declared import finder": ({ tuple, premises, describe }) => {
|
|
558
|
+
const module = String(premises[1].tuple[1]);
|
|
559
|
+
const name = String(premises[1].tuple[2]);
|
|
560
|
+
const position = String(premises[2].tuple[2]);
|
|
561
|
+
return {
|
|
562
|
+
reason: `${describe(tuple[0])} calls ${name} from ${module} with ${describe(tuple[1])} at argument ${position}, which gives back one of it`,
|
|
563
|
+
assumptions: [
|
|
564
|
+
`a pack declares that ${name} from ${module} gives back one of the class at argument ${position}`
|
|
565
|
+
]
|
|
566
|
+
};
|
|
567
|
+
},
|
|
276
568
|
"call result": ({ tuple, premises, describe, inline }) => {
|
|
277
569
|
const invokes = premises[0];
|
|
278
570
|
const invoked = invokes.tuple[1];
|
|
@@ -477,6 +769,47 @@ function pathsOf(edges, memo, childId, visiting) {
|
|
|
477
769
|
return result;
|
|
478
770
|
}
|
|
479
771
|
|
|
772
|
+
// src/nameReads.ts
|
|
773
|
+
function writesRunInOrder(scope, name, writes, rules) {
|
|
774
|
+
const last = writes[writes.length - 1];
|
|
775
|
+
if (scope === null || last === void 0) {
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
if (!writes.every((write) => write.direct)) {
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
return !isReadBefore(scope, name, last.at.startIndex, rules);
|
|
782
|
+
}
|
|
783
|
+
function isReadBefore(scope, name, position, rules) {
|
|
784
|
+
let found = false;
|
|
785
|
+
const visit = (node) => {
|
|
786
|
+
if (found || node.startIndex >= position) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (rules.laterBodies.has(node.type)) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (rules.nameTypes.has(node.type) && node.text === name) {
|
|
793
|
+
found = rules.isRead(node);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
for (const child of rules.childrenOf(node)) {
|
|
797
|
+
visit(child);
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
for (const child of rules.childrenOf(scope)) {
|
|
801
|
+
visit(child);
|
|
802
|
+
}
|
|
803
|
+
return found;
|
|
804
|
+
}
|
|
805
|
+
function startsAtName(node, name, rules) {
|
|
806
|
+
if (rules.nameTypes.has(node.type)) {
|
|
807
|
+
return node.text === name;
|
|
808
|
+
}
|
|
809
|
+
const inner = rules.readFirst(node);
|
|
810
|
+
return inner !== null && startsAtName(inner, name, rules);
|
|
811
|
+
}
|
|
812
|
+
|
|
480
813
|
// src/nodeKey.ts
|
|
481
814
|
var SPAN_KEY = /^(.*):(\d+)-(\d+)$/;
|
|
482
815
|
function nodeOfKey(rootsByFile, key) {
|
|
@@ -504,7 +837,7 @@ function nodeOfKey(rootsByFile, key) {
|
|
|
504
837
|
}
|
|
505
838
|
|
|
506
839
|
// src/session.ts
|
|
507
|
-
import { Database, evaluate, proofOf, witnesses } from "@suss/datalog";
|
|
840
|
+
import { Database, evaluate as evaluate2, proofOf, witnesses } from "@suss/datalog";
|
|
508
841
|
function explainResolvedKey(options) {
|
|
509
842
|
const { db, rules, key, locate, displayPath } = options;
|
|
510
843
|
const proofDb = new Database();
|
|
@@ -516,7 +849,7 @@ function explainResolvedKey(options) {
|
|
|
516
849
|
}
|
|
517
850
|
}
|
|
518
851
|
const started = performance.now();
|
|
519
|
-
|
|
852
|
+
evaluate2(proofDb, rules, witnesses);
|
|
520
853
|
const evaluateMs = performance.now() - started;
|
|
521
854
|
const derivedFacts = proofDb.relationNames().reduce((count, relation) => count + proofDb.size(relation), 0) - baseFacts;
|
|
522
855
|
const targets = new Set(
|
|
@@ -590,6 +923,35 @@ function withoutPlaceholders(answers, placeholders) {
|
|
|
590
923
|
return kept;
|
|
591
924
|
}
|
|
592
925
|
|
|
926
|
+
// src/writes.ts
|
|
927
|
+
function valueLeftByWrites(writes, ordered) {
|
|
928
|
+
const deciding = writes.filter((write) => !write.narrowsName);
|
|
929
|
+
const left = deciding.length === 0 ? writes : deciding;
|
|
930
|
+
if (left.some((write) => write.value === null)) {
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
if (ordered) {
|
|
934
|
+
return left[left.length - 1]?.value ?? null;
|
|
935
|
+
}
|
|
936
|
+
return sharedConstruction(left);
|
|
937
|
+
}
|
|
938
|
+
function sharedConstruction(writes) {
|
|
939
|
+
let settled = null;
|
|
940
|
+
for (const write of writes) {
|
|
941
|
+
if (write.placeholder) {
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
if (write.construction === null) {
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
if (settled !== null && write.construction !== settled.construction) {
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
settled = write;
|
|
951
|
+
}
|
|
952
|
+
return settled?.value ?? null;
|
|
953
|
+
}
|
|
954
|
+
|
|
593
955
|
// src/writtenValue.ts
|
|
594
956
|
function writtenValueOf(db, key, ask) {
|
|
595
957
|
const placeholders = placeholderValues(db);
|
|
@@ -689,6 +1051,57 @@ var RESOLUTION_RULES = [
|
|
|
689
1051
|
],
|
|
690
1052
|
"class instance"
|
|
691
1053
|
),
|
|
1054
|
+
// A finder the library declares, keyed on the base a pack named so a
|
|
1055
|
+
// project class with a method of the same name on another hierarchy is
|
|
1056
|
+
// left alone. The DESIGN says what a chain of them composes into.
|
|
1057
|
+
rule(
|
|
1058
|
+
"stepsTo",
|
|
1059
|
+
[v("r"), v("cls"), VALUE_STEP],
|
|
1060
|
+
[
|
|
1061
|
+
lit("call", v("r"), v("c")),
|
|
1062
|
+
lit("readsProperty", v("c"), v("o"), v("m")),
|
|
1063
|
+
lit("objectOf", v("o"), v("cls")),
|
|
1064
|
+
lit("objectValue", v("cls")),
|
|
1065
|
+
// Before `libraryBase`, so the demand rewrite asks it with the base
|
|
1066
|
+
// bound and walks an ancestry only for a method a pack declared.
|
|
1067
|
+
lit("givesBackOne", v("n"), v("m")),
|
|
1068
|
+
lit("libraryBase", v("cls"), v("n"))
|
|
1069
|
+
],
|
|
1070
|
+
"declared finder"
|
|
1071
|
+
),
|
|
1072
|
+
// The same word, for a library that takes the class as an argument
|
|
1073
|
+
// instead of as the receiver. The argument having to reach the base a
|
|
1074
|
+
// pack named is what keeps an unrelated `get` out.
|
|
1075
|
+
rule(
|
|
1076
|
+
"stepsTo",
|
|
1077
|
+
[v("r"), v("cls"), VALUE_STEP],
|
|
1078
|
+
[
|
|
1079
|
+
lit("call", v("r"), v("c")),
|
|
1080
|
+
lit("readsProperty", v("c"), v("o"), v("m")),
|
|
1081
|
+
lit("callArg", v("r"), v("k"), v("a")),
|
|
1082
|
+
// Before `libraryBase`, so the demand rewrite asks it with the base
|
|
1083
|
+
// bound and walks an ancestry only for a method a pack declared.
|
|
1084
|
+
lit("givesBackOneOfArgument", v("n"), v("m"), v("k")),
|
|
1085
|
+
lit("objectOf", v("a"), v("cls")),
|
|
1086
|
+
lit("libraryBase", v("cls"), v("n"))
|
|
1087
|
+
],
|
|
1088
|
+
"declared argument finder"
|
|
1089
|
+
),
|
|
1090
|
+
// The same again for a function called on its own rather than read off
|
|
1091
|
+
// anything, keyed on the module it was imported from so a project
|
|
1092
|
+
// function spelled the same way is not mistaken for it.
|
|
1093
|
+
rule(
|
|
1094
|
+
"stepsTo",
|
|
1095
|
+
[v("r"), v("cls"), VALUE_STEP],
|
|
1096
|
+
[
|
|
1097
|
+
lit("call", v("r"), v("c")),
|
|
1098
|
+
lit("comesFrom", v("c"), v("mod"), v("n")),
|
|
1099
|
+
lit("givesBackOneOfImport", v("mod"), v("n"), v("k")),
|
|
1100
|
+
lit("callArg", v("r"), v("k"), v("a")),
|
|
1101
|
+
lit("objectOf", v("a"), v("cls"))
|
|
1102
|
+
],
|
|
1103
|
+
"declared import finder"
|
|
1104
|
+
),
|
|
692
1105
|
// Wrapper transparency, derived: calling a factory that returns a
|
|
693
1106
|
// function which calls its parameter k steps to argument k.
|
|
694
1107
|
rule(
|
|
@@ -962,6 +1375,32 @@ var RESOLUTION_RULES = [
|
|
|
962
1375
|
lit("contains", v("baseCls"), v("n"), v("held"))
|
|
963
1376
|
]
|
|
964
1377
|
),
|
|
1378
|
+
// The library base a class's ancestry arrives at, however many of a
|
|
1379
|
+
// project's own classes sit in between. A base the run declares has a
|
|
1380
|
+
// node to keep walking from; one it does not has only a name.
|
|
1381
|
+
rule("libraryBase", [v("c"), v("n")], [lit("extendsNamed", v("c"), v("n"))]),
|
|
1382
|
+
rule(
|
|
1383
|
+
"libraryBase",
|
|
1384
|
+
[v("c"), v("n")],
|
|
1385
|
+
[
|
|
1386
|
+
lit("extends", v("c"), v("x")),
|
|
1387
|
+
lit("comesTo", v("x"), v("b")),
|
|
1388
|
+
lit("libraryBase", v("b"), v("n"))
|
|
1389
|
+
]
|
|
1390
|
+
),
|
|
1391
|
+
// A base a library hands back from a call rather than exporting as a
|
|
1392
|
+
// class, SQLAlchemy's `Base = declarative_base()`. The walk stops at
|
|
1393
|
+
// the call, so the function it called is the name to match on.
|
|
1394
|
+
rule(
|
|
1395
|
+
"libraryBase",
|
|
1396
|
+
[v("c"), v("n")],
|
|
1397
|
+
[
|
|
1398
|
+
lit("extends", v("c"), v("x")),
|
|
1399
|
+
lit("isWrittenAs", v("x"), v("r")),
|
|
1400
|
+
lit("call", v("r"), v("cc")),
|
|
1401
|
+
lit("comesFrom", v("cc"), v("m"), v("n"))
|
|
1402
|
+
]
|
|
1403
|
+
),
|
|
965
1404
|
// Which function a factory returns, and which of its parameters that
|
|
966
1405
|
// function calls: together they are what makes it a wrapper.
|
|
967
1406
|
rule(
|
|
@@ -1310,6 +1749,17 @@ var RESOLUTION_QUESTIONS = [
|
|
|
1310
1749
|
[v("c"), v("n")],
|
|
1311
1750
|
[lit("ancestryChain", v("c"), v("b")), lit("extendsNamed", v("b"), v("n"))]
|
|
1312
1751
|
),
|
|
1752
|
+
// Each method the same chain declares, so a caller that settled a
|
|
1753
|
+
// receiver on a class can tell a method the project writes itself
|
|
1754
|
+
// from one only the library provides.
|
|
1755
|
+
rule(
|
|
1756
|
+
"wantedDeclaredName",
|
|
1757
|
+
[v("c"), v("n")],
|
|
1758
|
+
[
|
|
1759
|
+
lit("ancestryChain", v("c"), v("b")),
|
|
1760
|
+
lit("holdsProperty", v("b"), v("n"), v("held"))
|
|
1761
|
+
]
|
|
1762
|
+
),
|
|
1313
1763
|
rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
|
|
1314
1764
|
rule(
|
|
1315
1765
|
"anchorChain",
|
|
@@ -1359,6 +1809,7 @@ var ANSWER_RELATIONS = [
|
|
|
1359
1809
|
];
|
|
1360
1810
|
export {
|
|
1361
1811
|
ANSWER_RELATIONS,
|
|
1812
|
+
ASKING_RELATIONS,
|
|
1362
1813
|
FACT_CONTRACT_CASES,
|
|
1363
1814
|
NAMESPACE_IMPORT,
|
|
1364
1815
|
NAMESPACE_IMPORT_NAME,
|
|
@@ -1367,14 +1818,24 @@ export {
|
|
|
1367
1818
|
RESULT_STEP,
|
|
1368
1819
|
VALUE_STEP,
|
|
1369
1820
|
agreedMountPrefix,
|
|
1821
|
+
askResolution,
|
|
1822
|
+
calleeOutcomeOf,
|
|
1823
|
+
calleeOutcomes,
|
|
1370
1824
|
checkFactContract,
|
|
1825
|
+
couldBeSettled,
|
|
1371
1826
|
explainResolutionProof,
|
|
1372
1827
|
explainResolvedKey,
|
|
1373
1828
|
joinMountedPath,
|
|
1374
1829
|
mountPathsOf,
|
|
1375
1830
|
nodeOfKey,
|
|
1376
1831
|
placeholderValues,
|
|
1832
|
+
queryFacts,
|
|
1377
1833
|
renderExplanation,
|
|
1834
|
+
resolutionProgram,
|
|
1378
1835
|
singleAnswers,
|
|
1836
|
+
startsAtName,
|
|
1837
|
+
valueLeftByWrites,
|
|
1838
|
+
writesRunInOrder,
|
|
1839
|
+
writtenSourcesOf,
|
|
1379
1840
|
writtenValueOf
|
|
1380
1841
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/resolution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The rules suss uses to follow a name in TypeScript, Python or Ruby to the function it comes down to.",
|
|
6
6
|
"keywords": [
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@suss/datalog": "0.
|
|
59
|
+
"@suss/datalog": "0.29.0"
|
|
60
60
|
}
|
|
61
61
|
}
|