@suss/resolution 0.8.0 → 0.9.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 +72 -9
- package/dist/index.cjs +217 -162
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +216 -163
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -57,14 +57,42 @@ demand nothing generates.
|
|
|
57
57
|
|
|
58
58
|
Node identity is the adapter's business. The rules only join on it.
|
|
59
59
|
|
|
60
|
+
## One relation of steps
|
|
61
|
+
|
|
62
|
+
Every construct states its hops once, in one relation:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
stepsTo(x, y, kind) following x leads to y in one hop
|
|
66
|
+
reaches(x, z, kind) the closure of those hops
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A value step goes to the value x is written as: a name to its
|
|
70
|
+
declaration, an import to what the module exports, a parameter to what a
|
|
71
|
+
call passes it, a property read to what the object contains under that
|
|
72
|
+
name, a construction to the class, a transparent wrapper to the argument
|
|
73
|
+
it wraps. A result step goes the other way, from a call to what the
|
|
74
|
+
function it invokes returns. A walk counts as a result walk once it has
|
|
75
|
+
run a call anywhere along it, which is what keeps a factory call out of
|
|
76
|
+
the answers that stop at a value.
|
|
77
|
+
|
|
78
|
+
A language with a hop of its own states it as a step too. JavaScript's
|
|
79
|
+
`.bind` and Ruby's `Const.new` are each one rule, and every question
|
|
80
|
+
below picks them up without being told.
|
|
81
|
+
|
|
82
|
+
Each question is that one closure with its own stopping condition. So
|
|
83
|
+
adding a construct is one step and every question gets it, and adding a
|
|
84
|
+
question is a stopping condition and no steps at all.
|
|
85
|
+
|
|
60
86
|
## What comes out
|
|
61
87
|
|
|
62
88
|
```
|
|
63
89
|
comesTo(x, z) following x arrives at the value z
|
|
64
90
|
resolves(x, z) comesTo narrowed to functions
|
|
91
|
+
givesBack(x, z) following x arrives at a call that returns z
|
|
65
92
|
isWrittenAs(x, z) x is written as the expression z
|
|
66
93
|
comesFrom(x, m, n) following x arrives at m's export n
|
|
67
94
|
callsInto(f, m, n) calling f ends up calling m's n
|
|
95
|
+
paramAt(r, p, z) the call r puts z in the parameter p
|
|
68
96
|
```
|
|
69
97
|
|
|
70
98
|
`resolves` is the question most callers ask. `comesTo` is the one
|
|
@@ -72,6 +100,21 @@ underneath it, and it can come back with an object, because a chain has
|
|
|
72
100
|
to pass through objects for `routes.list` to reach whatever `list`
|
|
73
101
|
contains.
|
|
74
102
|
|
|
103
|
+
`givesBack` is the other direction of function application. `comesTo`
|
|
104
|
+
says what a value comes down to, and it stops at a call on purpose: a
|
|
105
|
+
factory call is usually the wrapper itself, so resolving it to the
|
|
106
|
+
function it returned would fight the unwrapping answer. That leaves
|
|
107
|
+
nobody able to ask what a call returned, which is the whole question
|
|
108
|
+
when a factory builds a dependency. `givesBack` asks it, over the same
|
|
109
|
+
steps and with the same stopping condition, of the walks that ran a
|
|
110
|
+
call.
|
|
111
|
+
|
|
112
|
+
Both directions run at once without interfering, because they answer
|
|
113
|
+
different questions about the same call. `const dao = makeDao()` comes
|
|
114
|
+
to nothing and gives back the class `makeDao` constructed, so
|
|
115
|
+
`dao.findByCustomer` finds the method that class declares while
|
|
116
|
+
`withAuth(handler)` still comes to `handler`.
|
|
117
|
+
|
|
75
118
|
`isWrittenAs` follows the same names to the expression a value is
|
|
76
119
|
written as, whatever kind of expression that turns out to be. A GraphQL
|
|
77
120
|
document is neither a function nor an object, so `comesTo` never
|
|
@@ -80,8 +123,14 @@ reaches one.
|
|
|
80
123
|
`comesFrom` covers the direction `comesTo` cannot. Every `comesTo` chain
|
|
81
124
|
ends at something written out in the source suss is reading, so a name
|
|
82
125
|
for a library's own function ends nowhere, because the library's body is
|
|
83
|
-
not here. `comesFrom`
|
|
84
|
-
comes back with the module plus the name that module
|
|
126
|
+
not here. `comesFrom` walks the same steps and stops at the import
|
|
127
|
+
instead, so it comes back with the module plus the name that module
|
|
128
|
+
exports.
|
|
129
|
+
|
|
130
|
+
`paramAt` is the one question that keeps the call it went through.
|
|
131
|
+
`comesTo` merges call sites, so a function called from two places leaves
|
|
132
|
+
its parameter with two values and a caller wanting one gets nothing.
|
|
133
|
+
`paramAt` says which call put which value there.
|
|
85
134
|
|
|
86
135
|
`callsInto` puts that together with the calls a function makes. A
|
|
87
136
|
project writes its own decorator that calls `Resolver()` and applies
|
|
@@ -91,19 +140,24 @@ normal rather than ambiguous, because a wrapper that combines two
|
|
|
91
140
|
library decorators applies both of them, so a caller asks whether the
|
|
92
141
|
one it cares about is among them.
|
|
93
142
|
|
|
94
|
-
|
|
143
|
+
Two relations exist for the rules' own use rather than for callers:
|
|
95
144
|
|
|
96
145
|
```
|
|
97
146
|
objectOf(x, obj) x stands for the object literal obj
|
|
147
|
+
invokes(r, f) the call r runs the function f
|
|
98
148
|
```
|
|
99
149
|
|
|
100
150
|
An object arrives two ways, through a name or as what a factory call
|
|
101
|
-
|
|
102
|
-
`routes.list` and the rule for `make(body).handle` are the same rule.
|
|
103
|
-
|
|
104
|
-
answer
|
|
105
|
-
|
|
106
|
-
|
|
151
|
+
gives back. `objectOf` gives that step a name, so the rule for
|
|
152
|
+
`routes.list` and the rule for `make(body).handle` are the same rule.
|
|
153
|
+
This is where the two directions meet: a factory call gets an
|
|
154
|
+
`objectOf` answer without getting a `comesTo` answer.
|
|
155
|
+
|
|
156
|
+
`invokes` is the callee half of `givesBack`, kept apart so the case
|
|
157
|
+
where the callee is itself a call, `daoBuilder()()`, is one rule rather
|
|
158
|
+
than a copy of every other. It differs from `callsFunction`, which
|
|
159
|
+
starts from the function because a caller asking for call sites has the
|
|
160
|
+
function in hand.
|
|
107
161
|
|
|
108
162
|
## Why rules and not a walker
|
|
109
163
|
|
|
@@ -119,6 +173,15 @@ resolved 11 handlers and missed most of what the template declared. The
|
|
|
119
173
|
fix was one condition in fact extraction, and the existing rules found
|
|
120
174
|
the rest without any change.
|
|
121
175
|
|
|
176
|
+
## Changing a rule
|
|
177
|
+
|
|
178
|
+
Every rule here composes with every other one, so a change meant for one
|
|
179
|
+
construct lands on chains nobody had in mind. `@suss/resolution-fuzz`
|
|
180
|
+
generates four thousand fact bases, runs these rules over each, and
|
|
181
|
+
compares what came out against a committed baseline, which is what turns
|
|
182
|
+
a change into a diff a reviewer reads. Run `npm run resolution:baseline`
|
|
183
|
+
to accept one, and commit the rewritten file with the rule.
|
|
184
|
+
|
|
122
185
|
## What is not modelled
|
|
123
186
|
|
|
124
187
|
**A handler passed as one property of a config.** `make({ body:
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,8 @@ __export(index_exports, {
|
|
|
24
24
|
FACT_CONTRACT_CASES: () => FACT_CONTRACT_CASES,
|
|
25
25
|
RESOLUTION_QUESTIONS: () => RESOLUTION_QUESTIONS,
|
|
26
26
|
RESOLUTION_RULES: () => RESOLUTION_RULES,
|
|
27
|
+
RESULT_STEP: () => RESULT_STEP,
|
|
28
|
+
VALUE_STEP: () => VALUE_STEP,
|
|
27
29
|
checkFactContract: () => checkFactContract
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -202,35 +204,202 @@ function checkFactContract(sources, emit, options = {}) {
|
|
|
202
204
|
|
|
203
205
|
// src/index.ts
|
|
204
206
|
var import_datalog = require("@suss/datalog");
|
|
207
|
+
var VALUE_STEP = (0, import_datalog.constant)("value");
|
|
208
|
+
var RESULT_STEP = (0, import_datalog.constant)("result");
|
|
205
209
|
var RESOLUTION_RULES = [
|
|
206
|
-
//
|
|
207
|
-
//
|
|
210
|
+
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
211
|
+
// A language with a hop of its own, like JavaScript's `.bind`, states
|
|
212
|
+
// it as a step too, or every question but `comesTo` misses it.
|
|
213
|
+
(0, import_datalog.rule)("stepsTo", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"), VALUE_STEP], [(0, import_datalog.lit)("binds", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"))]),
|
|
214
|
+
// A name written more than once has the value the last write left
|
|
215
|
+
// there. The adapter works out which write that is and stays quiet
|
|
216
|
+
// when control flow decides, so such a name steps nowhere at all.
|
|
217
|
+
(0, import_datalog.rule)(
|
|
218
|
+
"stepsTo",
|
|
219
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"), VALUE_STEP],
|
|
220
|
+
[(0, import_datalog.lit)("endsHolding", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"))]
|
|
221
|
+
),
|
|
222
|
+
// An import steps to what the module exports under that name.
|
|
223
|
+
(0, import_datalog.rule)(
|
|
224
|
+
"stepsTo",
|
|
225
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("value"), VALUE_STEP],
|
|
226
|
+
[
|
|
227
|
+
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")),
|
|
228
|
+
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value"))
|
|
229
|
+
]
|
|
230
|
+
),
|
|
231
|
+
// A parameter steps to what a call passes it. A function called from
|
|
232
|
+
// several places leaves its parameter with more than one value, and a
|
|
233
|
+
// caller that needs those apart asks `paramAt`.
|
|
234
|
+
(0, import_datalog.rule)(
|
|
235
|
+
"stepsTo",
|
|
236
|
+
[(0, import_datalog.variable)("p"), (0, import_datalog.variable)("a"), VALUE_STEP],
|
|
237
|
+
[(0, import_datalog.lit)("passesArgument", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("p"), (0, import_datalog.variable)("a"))]
|
|
238
|
+
),
|
|
239
|
+
// Reading a property steps to what the object contains under that
|
|
240
|
+
// name, whichever way the object arrived: `routes.list` off a name,
|
|
241
|
+
// or `make(body).handle` off a call.
|
|
242
|
+
(0, import_datalog.rule)(
|
|
243
|
+
"stepsTo",
|
|
244
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("held"), VALUE_STEP],
|
|
245
|
+
[
|
|
246
|
+
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.variable)("n")),
|
|
247
|
+
(0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")),
|
|
248
|
+
(0, import_datalog.lit)("contains", (0, import_datalog.variable)("obj"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held"))
|
|
249
|
+
]
|
|
250
|
+
),
|
|
251
|
+
// Calling a class makes one of it, so the call steps to the class and
|
|
252
|
+
// a method read off the result is the one the class declares. The
|
|
253
|
+
// caveat below is about a factory function, and a class is not one.
|
|
254
|
+
(0, import_datalog.rule)(
|
|
255
|
+
"stepsTo",
|
|
256
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("cls"), VALUE_STEP],
|
|
257
|
+
[
|
|
258
|
+
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
259
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("cls")),
|
|
260
|
+
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("cls"))
|
|
261
|
+
]
|
|
262
|
+
),
|
|
263
|
+
// Wrapper transparency, derived: calling a factory that returns a
|
|
264
|
+
// function which calls its parameter k steps to argument k.
|
|
265
|
+
(0, import_datalog.rule)(
|
|
266
|
+
"stepsTo",
|
|
267
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("a"), VALUE_STEP],
|
|
268
|
+
[
|
|
269
|
+
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
270
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
271
|
+
(0, import_datalog.lit)("unwraps", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k")),
|
|
272
|
+
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a"))
|
|
273
|
+
]
|
|
274
|
+
),
|
|
275
|
+
// Wrapper transparency, declared: a pack says this callee wraps
|
|
276
|
+
// argument k. The callee has to come from the library the pack said,
|
|
277
|
+
// so a local object spelled the same way is not mistaken for it.
|
|
278
|
+
(0, import_datalog.rule)(
|
|
279
|
+
"stepsTo",
|
|
280
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("a"), VALUE_STEP],
|
|
281
|
+
[
|
|
282
|
+
(0, import_datalog.lit)("calleeName", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("n")),
|
|
283
|
+
(0, import_datalog.lit)("unwrapsByName", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("k")),
|
|
284
|
+
(0, import_datalog.lit)("wrapperModule", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("m")),
|
|
285
|
+
(0, import_datalog.lit)("calleeOrigin", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("m")),
|
|
286
|
+
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a"))
|
|
287
|
+
]
|
|
288
|
+
),
|
|
289
|
+
// The one step that runs a function forwards: a call steps to what
|
|
290
|
+
// the function it invokes returns.
|
|
291
|
+
(0, import_datalog.rule)(
|
|
292
|
+
"stepsTo",
|
|
293
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("ret"), RESULT_STEP],
|
|
294
|
+
[(0, import_datalog.lit)("invokes", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")), (0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret"))]
|
|
295
|
+
),
|
|
296
|
+
// Where a walk gets to, and whether it ran a call on the way. A value
|
|
297
|
+
// step keeps whatever the rest of the walk was, and a result step
|
|
298
|
+
// makes the whole walk one that ran a call.
|
|
299
|
+
(0, import_datalog.rule)(
|
|
300
|
+
"reaches",
|
|
301
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), (0, import_datalog.variable)("kind")],
|
|
302
|
+
[(0, import_datalog.lit)("stepsTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), (0, import_datalog.variable)("kind"))]
|
|
303
|
+
),
|
|
304
|
+
(0, import_datalog.rule)(
|
|
305
|
+
"reaches",
|
|
306
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), (0, import_datalog.variable)("kind")],
|
|
307
|
+
[
|
|
308
|
+
(0, import_datalog.lit)("stepsTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"), VALUE_STEP),
|
|
309
|
+
(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"), (0, import_datalog.variable)("kind"))
|
|
310
|
+
]
|
|
311
|
+
),
|
|
312
|
+
// Two rules rather than one leaving the rest of the walk's kind free.
|
|
313
|
+
// A free kind is a second question about the same relation, and a
|
|
314
|
+
// demand-driven run then derives both to answer either.
|
|
315
|
+
(0, import_datalog.rule)(
|
|
316
|
+
"reaches",
|
|
317
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), RESULT_STEP],
|
|
318
|
+
[
|
|
319
|
+
(0, import_datalog.lit)("stepsTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"), RESULT_STEP),
|
|
320
|
+
(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"), VALUE_STEP)
|
|
321
|
+
]
|
|
322
|
+
),
|
|
323
|
+
(0, import_datalog.rule)(
|
|
324
|
+
"reaches",
|
|
325
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), RESULT_STEP],
|
|
326
|
+
[
|
|
327
|
+
(0, import_datalog.lit)("stepsTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"), RESULT_STEP),
|
|
328
|
+
(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"), RESULT_STEP)
|
|
329
|
+
]
|
|
330
|
+
),
|
|
331
|
+
// What a value comes down to: the walk that never ran a call, stopped
|
|
332
|
+
// at a function or an object. Something already written out comes to
|
|
333
|
+
// itself, and every other walk ends at one of those.
|
|
208
334
|
(0, import_datalog.rule)("comesTo", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("func", (0, import_datalog.variable)("x"))]),
|
|
209
335
|
(0, import_datalog.rule)("comesTo", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("x"))]),
|
|
210
|
-
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
211
336
|
(0, import_datalog.rule)(
|
|
212
337
|
"comesTo",
|
|
213
338
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
214
|
-
[(0, import_datalog.lit)("
|
|
339
|
+
[(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), VALUE_STEP), (0, import_datalog.lit)("func", (0, import_datalog.variable)("z"))]
|
|
215
340
|
),
|
|
216
|
-
// A name written more than once has the value the last write left
|
|
217
|
-
// there, and `binds` says nothing about it. The adapter works out
|
|
218
|
-
// which write that is, in its own language's terms, and stays quiet
|
|
219
|
-
// when control flow decides. A name it stays quiet about comes to no
|
|
220
|
-
// value, which is the answer.
|
|
221
341
|
(0, import_datalog.rule)(
|
|
222
342
|
"comesTo",
|
|
223
343
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
224
|
-
[(0, import_datalog.lit)("
|
|
344
|
+
[(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), VALUE_STEP), (0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("z"))]
|
|
225
345
|
),
|
|
226
|
-
//
|
|
346
|
+
// The same stopping condition, for the walk that ran a call. A call
|
|
347
|
+
// gets no `comesTo` answer on purpose: coming back with what a
|
|
348
|
+
// factory returned would fight the unwrapping answer.
|
|
227
349
|
(0, import_datalog.rule)(
|
|
228
|
-
"
|
|
350
|
+
"givesBack",
|
|
351
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
352
|
+
[(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), RESULT_STEP), (0, import_datalog.lit)("func", (0, import_datalog.variable)("z"))]
|
|
353
|
+
),
|
|
354
|
+
(0, import_datalog.rule)(
|
|
355
|
+
"givesBack",
|
|
356
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
357
|
+
[(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), RESULT_STEP), (0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("z"))]
|
|
358
|
+
),
|
|
359
|
+
// Following a value to the expression it is written as, whatever kind
|
|
360
|
+
// that is. A GraphQL document is neither a function nor an object, so
|
|
361
|
+
// `comesTo` never reaches one.
|
|
362
|
+
(0, import_datalog.rule)("isWrittenAs", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("writtenValue", (0, import_datalog.variable)("x"))]),
|
|
363
|
+
(0, import_datalog.rule)("isWrittenAs", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("x"))]),
|
|
364
|
+
(0, import_datalog.rule)(
|
|
365
|
+
"isWrittenAs",
|
|
229
366
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
367
|
+
[(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), VALUE_STEP), (0, import_datalog.lit)("writtenValue", (0, import_datalog.variable)("z"))]
|
|
368
|
+
),
|
|
369
|
+
(0, import_datalog.rule)(
|
|
370
|
+
"isWrittenAs",
|
|
371
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
372
|
+
[(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"), VALUE_STEP), (0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("z"))]
|
|
373
|
+
),
|
|
374
|
+
// What one call site put in a parameter, told apart from what the
|
|
375
|
+
// other callers passed.
|
|
376
|
+
(0, import_datalog.rule)(
|
|
377
|
+
"paramAt",
|
|
378
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("p"), (0, import_datalog.variable)("z")],
|
|
230
379
|
[
|
|
231
|
-
(0, import_datalog.lit)("
|
|
232
|
-
(0, import_datalog.lit)("
|
|
233
|
-
|
|
380
|
+
(0, import_datalog.lit)("passesArgument", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("p"), (0, import_datalog.variable)("a")),
|
|
381
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("z"))
|
|
382
|
+
]
|
|
383
|
+
),
|
|
384
|
+
// An argument arriving at the parameter it is passed to, by position
|
|
385
|
+
// or by the name the caller wrote, keeping the call it went through
|
|
386
|
+
// so `paramAt` can tell two call sites apart.
|
|
387
|
+
(0, import_datalog.rule)(
|
|
388
|
+
"passesArgument",
|
|
389
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("p"), (0, import_datalog.variable)("a")],
|
|
390
|
+
[
|
|
391
|
+
(0, import_datalog.lit)("paramOf", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("p")),
|
|
392
|
+
(0, import_datalog.lit)("callsFunction", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")),
|
|
393
|
+
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a"))
|
|
394
|
+
]
|
|
395
|
+
),
|
|
396
|
+
(0, import_datalog.rule)(
|
|
397
|
+
"passesArgument",
|
|
398
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("p"), (0, import_datalog.variable)("a")],
|
|
399
|
+
[
|
|
400
|
+
(0, import_datalog.lit)("paramNamed", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("p")),
|
|
401
|
+
(0, import_datalog.lit)("callsFunction", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")),
|
|
402
|
+
(0, import_datalog.lit)("callKeywordArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("a"))
|
|
234
403
|
]
|
|
235
404
|
),
|
|
236
405
|
// What a module exports: directly, or through re-export chains.
|
|
@@ -255,12 +424,22 @@ var RESOLUTION_RULES = [
|
|
|
255
424
|
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m2"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value"))
|
|
256
425
|
]
|
|
257
426
|
),
|
|
258
|
-
// The
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
427
|
+
// The function a call runs, written from the call's side because a
|
|
428
|
+
// caller asking what a call gives back has the call in hand.
|
|
429
|
+
(0, import_datalog.rule)(
|
|
430
|
+
"invokes",
|
|
431
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")],
|
|
432
|
+
[(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")), (0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f"))]
|
|
433
|
+
),
|
|
434
|
+
// The callee is itself a call: `daoBuilder()()`.
|
|
435
|
+
(0, import_datalog.rule)(
|
|
436
|
+
"invokes",
|
|
437
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")],
|
|
438
|
+
[(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")), (0, import_datalog.lit)("givesBack", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f"))]
|
|
439
|
+
),
|
|
440
|
+
// The object an expression refers to: a name through `comesTo`, a
|
|
441
|
+
// factory call through what it gives back. Naming the step is what
|
|
442
|
+
// makes `routes.list` and `make(body).handle` one rule.
|
|
264
443
|
(0, import_datalog.rule)(
|
|
265
444
|
"objectOf",
|
|
266
445
|
[(0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")],
|
|
@@ -268,14 +447,8 @@ var RESOLUTION_RULES = [
|
|
|
268
447
|
),
|
|
269
448
|
(0, import_datalog.rule)(
|
|
270
449
|
"objectOf",
|
|
271
|
-
[(0, import_datalog.variable)("
|
|
272
|
-
[
|
|
273
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
274
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
275
|
-
(0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret")),
|
|
276
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("ret"), (0, import_datalog.variable)("obj")),
|
|
277
|
-
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("obj"))
|
|
278
|
-
]
|
|
450
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("obj")],
|
|
451
|
+
[(0, import_datalog.lit)("givesBack", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("obj")), (0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("obj"))]
|
|
279
452
|
),
|
|
280
453
|
// Which calls a function, found by the name the call is written as
|
|
281
454
|
// rather than by resolving every callee in the project. A caller knows
|
|
@@ -308,42 +481,6 @@ var RESOLUTION_RULES = [
|
|
|
308
481
|
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c"))
|
|
309
482
|
]
|
|
310
483
|
),
|
|
311
|
-
// A function whose return is itself a call arrives at the same object
|
|
312
|
-
// that call does, so a chain passing through two factories settles.
|
|
313
|
-
(0, import_datalog.rule)(
|
|
314
|
-
"objectOf",
|
|
315
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("obj")],
|
|
316
|
-
[
|
|
317
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
318
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
319
|
-
(0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret")),
|
|
320
|
-
(0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("ret"), (0, import_datalog.variable)("obj"))
|
|
321
|
-
]
|
|
322
|
-
),
|
|
323
|
-
// An argument reaches the parameter it is passed to, by position or
|
|
324
|
-
// by the name the caller wrote. A function called from several places
|
|
325
|
-
// leaves its parameter with more than one value, and the caller
|
|
326
|
-
// decides what to do about that.
|
|
327
|
-
(0, import_datalog.rule)(
|
|
328
|
-
"comesTo",
|
|
329
|
-
[(0, import_datalog.variable)("p"), (0, import_datalog.variable)("z")],
|
|
330
|
-
[
|
|
331
|
-
(0, import_datalog.lit)("paramOf", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("p")),
|
|
332
|
-
(0, import_datalog.lit)("callsFunction", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")),
|
|
333
|
-
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a")),
|
|
334
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("z"))
|
|
335
|
-
]
|
|
336
|
-
),
|
|
337
|
-
(0, import_datalog.rule)(
|
|
338
|
-
"comesTo",
|
|
339
|
-
[(0, import_datalog.variable)("p"), (0, import_datalog.variable)("z")],
|
|
340
|
-
[
|
|
341
|
-
(0, import_datalog.lit)("paramNamed", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("p")),
|
|
342
|
-
(0, import_datalog.lit)("callsFunction", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")),
|
|
343
|
-
(0, import_datalog.lit)("callKeywordArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("a")),
|
|
344
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("z"))
|
|
345
|
-
]
|
|
346
|
-
),
|
|
347
484
|
// What an object contains, its base class included, so a method the base
|
|
348
485
|
// declares is found on a subclass that never overrode it. A method both
|
|
349
486
|
// declare gives two, and the caller decides. This is its own relation
|
|
@@ -363,33 +500,8 @@ var RESOLUTION_RULES = [
|
|
|
363
500
|
(0, import_datalog.lit)("contains", (0, import_datalog.variable)("baseCls"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held"))
|
|
364
501
|
]
|
|
365
502
|
),
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
// about a factory function, and a class is not one.
|
|
369
|
-
(0, import_datalog.rule)(
|
|
370
|
-
"comesTo",
|
|
371
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("cls")],
|
|
372
|
-
[
|
|
373
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
374
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("cls")),
|
|
375
|
-
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("cls"))
|
|
376
|
-
]
|
|
377
|
-
),
|
|
378
|
-
// Reading a property comes to what the object holds under that name,
|
|
379
|
-
// whichever way the object arrived: `routes.list` off a name, or
|
|
380
|
-
// `make(body).handle` off a call.
|
|
381
|
-
(0, import_datalog.rule)(
|
|
382
|
-
"comesTo",
|
|
383
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
384
|
-
[
|
|
385
|
-
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.variable)("n")),
|
|
386
|
-
(0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")),
|
|
387
|
-
(0, import_datalog.lit)("contains", (0, import_datalog.variable)("obj"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held")),
|
|
388
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("held"), (0, import_datalog.variable)("z"))
|
|
389
|
-
]
|
|
390
|
-
),
|
|
391
|
-
// Wrapper transparency, derived: calling a factory that returns a
|
|
392
|
-
// function which calls its parameter k comes to argument k.
|
|
503
|
+
// Which function a factory returns, and which of its parameters that
|
|
504
|
+
// function calls: together they are what makes it a wrapper.
|
|
393
505
|
(0, import_datalog.rule)(
|
|
394
506
|
"returnsFunc",
|
|
395
507
|
[(0, import_datalog.variable)("f"), (0, import_datalog.variable)("g")],
|
|
@@ -450,56 +562,20 @@ var RESOLUTION_RULES = [
|
|
|
450
562
|
(0, import_datalog.lit)("paramOf", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("p"))
|
|
451
563
|
]
|
|
452
564
|
),
|
|
453
|
-
(0, import_datalog.rule)(
|
|
454
|
-
"comesTo",
|
|
455
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("h")],
|
|
456
|
-
[
|
|
457
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
458
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
459
|
-
(0, import_datalog.lit)("unwraps", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k")),
|
|
460
|
-
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a")),
|
|
461
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("h"))
|
|
462
|
-
]
|
|
463
|
-
),
|
|
464
|
-
// Wrapper transparency, declared: a pack says this callee wraps
|
|
465
|
-
// argument k, whatever its implementation looks like. The callee has
|
|
466
|
-
// to have been imported from the library the pack named, so a local
|
|
467
|
-
// object spelled the same way is not mistaken for it.
|
|
468
|
-
(0, import_datalog.rule)(
|
|
469
|
-
"comesTo",
|
|
470
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("h")],
|
|
471
|
-
[
|
|
472
|
-
(0, import_datalog.lit)("calleeName", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("n")),
|
|
473
|
-
(0, import_datalog.lit)("unwrapsByName", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("k")),
|
|
474
|
-
(0, import_datalog.lit)("wrapperModule", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("m")),
|
|
475
|
-
(0, import_datalog.lit)("calleeOrigin", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("m")),
|
|
476
|
-
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a")),
|
|
477
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("h"))
|
|
478
|
-
]
|
|
479
|
-
),
|
|
480
565
|
// Where a name comes from, when what it refers to lives outside the
|
|
481
|
-
// source being read.
|
|
482
|
-
// never reaches a library's own function
|
|
483
|
-
// library name has no answer at all. This follows the same aliases
|
|
484
|
-
// and barrels in the other direction and answers with the module and
|
|
485
|
-
// the name that module exports.
|
|
566
|
+
// source being read. A walk ends at something written out in source,
|
|
567
|
+
// so it never reaches a library's own function.
|
|
486
568
|
(0, import_datalog.rule)(
|
|
487
569
|
"comesFrom",
|
|
488
570
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
489
571
|
[(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))]
|
|
490
572
|
),
|
|
491
|
-
(0, import_datalog.rule)(
|
|
492
|
-
"comesFrom",
|
|
493
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
494
|
-
[(0, import_datalog.lit)("binds", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("comesFrom", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))]
|
|
495
|
-
),
|
|
496
573
|
(0, import_datalog.rule)(
|
|
497
574
|
"comesFrom",
|
|
498
575
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
499
576
|
[
|
|
500
|
-
(0, import_datalog.lit)("
|
|
501
|
-
(0, import_datalog.lit)("
|
|
502
|
-
(0, import_datalog.lit)("comesFrom", (0, import_datalog.variable)("value"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))
|
|
577
|
+
(0, import_datalog.lit)("reaches", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y"), VALUE_STEP),
|
|
578
|
+
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))
|
|
503
579
|
]
|
|
504
580
|
),
|
|
505
581
|
// Calling f ends up calling the name n that module m exports. One hop
|
|
@@ -535,36 +611,6 @@ var RESOLUTION_RULES = [
|
|
|
535
611
|
"resolves",
|
|
536
612
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
537
613
|
[(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")), (0, import_datalog.lit)("func", (0, import_datalog.variable)("z"))]
|
|
538
|
-
),
|
|
539
|
-
// Following a name to the expression it is written as. A separate
|
|
540
|
-
// relation rather than a widening of `comesTo`, so a caller asking
|
|
541
|
-
// the older question never sees a new answer: a factory call already
|
|
542
|
-
// has an unwrapping answer, and letting it also answer with itself
|
|
543
|
-
// would make that pair ambiguous.
|
|
544
|
-
//
|
|
545
|
-
// The two seeds are the two ways an expression can be written out. An
|
|
546
|
-
// object literal is one of them, and it is the shape a generated
|
|
547
|
-
// GraphQL document takes.
|
|
548
|
-
(0, import_datalog.rule)("isWrittenAs", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("writtenValue", (0, import_datalog.variable)("x"))]),
|
|
549
|
-
(0, import_datalog.rule)("isWrittenAs", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("x"))]),
|
|
550
|
-
(0, import_datalog.rule)(
|
|
551
|
-
"isWrittenAs",
|
|
552
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
553
|
-
[(0, import_datalog.lit)("binds", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"))]
|
|
554
|
-
),
|
|
555
|
-
(0, import_datalog.rule)(
|
|
556
|
-
"isWrittenAs",
|
|
557
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
558
|
-
[(0, import_datalog.lit)("endsHolding", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"))]
|
|
559
|
-
),
|
|
560
|
-
(0, import_datalog.rule)(
|
|
561
|
-
"isWrittenAs",
|
|
562
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
563
|
-
[
|
|
564
|
-
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")),
|
|
565
|
-
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value")),
|
|
566
|
-
(0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("value"), (0, import_datalog.variable)("z"))
|
|
567
|
-
]
|
|
568
614
|
)
|
|
569
615
|
];
|
|
570
616
|
var RESOLUTION_QUESTIONS = [
|
|
@@ -596,6 +642,13 @@ var RESOLUTION_QUESTIONS = [
|
|
|
596
642
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
597
643
|
[(0, import_datalog.lit)("wanted", (0, import_datalog.variable)("x")), (0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"))]
|
|
598
644
|
),
|
|
645
|
+
// Keyed by the parameter, since that is what a caller has in hand
|
|
646
|
+
// when it wants the call sites told apart.
|
|
647
|
+
(0, import_datalog.rule)(
|
|
648
|
+
"wantedParamAt",
|
|
649
|
+
[(0, import_datalog.variable)("p"), (0, import_datalog.variable)("r"), (0, import_datalog.variable)("z")],
|
|
650
|
+
[(0, import_datalog.lit)("wanted", (0, import_datalog.variable)("p")), (0, import_datalog.lit)("paramAt", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("p"), (0, import_datalog.variable)("z"))]
|
|
651
|
+
),
|
|
599
652
|
(0, import_datalog.rule)(
|
|
600
653
|
"wantedComesFrom",
|
|
601
654
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
@@ -620,5 +673,7 @@ var ANSWER_RELATIONS = [
|
|
|
620
673
|
FACT_CONTRACT_CASES,
|
|
621
674
|
RESOLUTION_QUESTIONS,
|
|
622
675
|
RESOLUTION_RULES,
|
|
676
|
+
RESULT_STEP,
|
|
677
|
+
VALUE_STEP,
|
|
623
678
|
checkFactContract
|
|
624
679
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -36,9 +36,23 @@ declare function checkFactContract(sources: Readonly<Record<string, CaseFiles>>,
|
|
|
36
36
|
problem: string;
|
|
37
37
|
}[]>;
|
|
38
38
|
|
|
39
|
+
/** A step to the value x is written as. */
|
|
40
|
+
declare const VALUE_STEP: _suss_datalog.Term;
|
|
41
|
+
/** A step to what running the call x is handed back. */
|
|
42
|
+
declare const RESULT_STEP: _suss_datalog.Term;
|
|
39
43
|
/**
|
|
40
44
|
* The rules every language adapter shares. Concatenate a language's own
|
|
41
45
|
* rules onto these before evaluating.
|
|
46
|
+
*
|
|
47
|
+
* Every construct states its hops once, as `stepsTo(x, y, kind)`, which
|
|
48
|
+
* says x leads to y. A value step goes to the value x is written as, and a
|
|
49
|
+
* result step runs the call x is and goes to what that call handed back.
|
|
50
|
+
* `reaches` is the closure of those steps, and a walk counts as a result
|
|
51
|
+
* walk once it has run a call anywhere along it.
|
|
52
|
+
*
|
|
53
|
+
* Each question is that one closure with its own stopping condition, so
|
|
54
|
+
* adding a construct is one step and every question gets it, and adding
|
|
55
|
+
* a question is a stopping condition and no steps at all.
|
|
42
56
|
*/
|
|
43
57
|
declare const RESOLUTION_RULES: _suss_datalog.Rule[];
|
|
44
58
|
/**
|
|
@@ -62,4 +76,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
|
|
|
62
76
|
/** The relations `RESOLUTION_QUESTIONS` answers into. */
|
|
63
77
|
declare const ANSWER_RELATIONS: string[];
|
|
64
78
|
|
|
65
|
-
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, checkFactContract };
|
|
79
|
+
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, VALUE_STEP, checkFactContract };
|
package/dist/index.d.ts
CHANGED
|
@@ -36,9 +36,23 @@ declare function checkFactContract(sources: Readonly<Record<string, CaseFiles>>,
|
|
|
36
36
|
problem: string;
|
|
37
37
|
}[]>;
|
|
38
38
|
|
|
39
|
+
/** A step to the value x is written as. */
|
|
40
|
+
declare const VALUE_STEP: _suss_datalog.Term;
|
|
41
|
+
/** A step to what running the call x is handed back. */
|
|
42
|
+
declare const RESULT_STEP: _suss_datalog.Term;
|
|
39
43
|
/**
|
|
40
44
|
* The rules every language adapter shares. Concatenate a language's own
|
|
41
45
|
* rules onto these before evaluating.
|
|
46
|
+
*
|
|
47
|
+
* Every construct states its hops once, as `stepsTo(x, y, kind)`, which
|
|
48
|
+
* says x leads to y. A value step goes to the value x is written as, and a
|
|
49
|
+
* result step runs the call x is and goes to what that call handed back.
|
|
50
|
+
* `reaches` is the closure of those steps, and a walk counts as a result
|
|
51
|
+
* walk once it has run a call anywhere along it.
|
|
52
|
+
*
|
|
53
|
+
* Each question is that one closure with its own stopping condition, so
|
|
54
|
+
* adding a construct is one step and every question gets it, and adding
|
|
55
|
+
* a question is a stopping condition and no steps at all.
|
|
42
56
|
*/
|
|
43
57
|
declare const RESOLUTION_RULES: _suss_datalog.Rule[];
|
|
44
58
|
/**
|
|
@@ -62,4 +76,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
|
|
|
62
76
|
/** The relations `RESOLUTION_QUESTIONS` answers into. */
|
|
63
77
|
declare const ANSWER_RELATIONS: string[];
|
|
64
78
|
|
|
65
|
-
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, checkFactContract };
|
|
79
|
+
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, VALUE_STEP, checkFactContract };
|
package/dist/index.js
CHANGED
|
@@ -171,36 +171,203 @@ function checkFactContract(sources, emit, options = {}) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
// src/index.ts
|
|
174
|
-
import { lit, rule, variable as v } from "@suss/datalog";
|
|
174
|
+
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
175
|
+
var VALUE_STEP = constant("value");
|
|
176
|
+
var RESULT_STEP = constant("result");
|
|
175
177
|
var RESOLUTION_RULES = [
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
+
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
179
|
+
// A language with a hop of its own, like JavaScript's `.bind`, states
|
|
180
|
+
// it as a step too, or every question but `comesTo` misses it.
|
|
181
|
+
rule("stepsTo", [v("x"), v("y"), VALUE_STEP], [lit("binds", v("x"), v("y"))]),
|
|
182
|
+
// A name written more than once has the value the last write left
|
|
183
|
+
// there. The adapter works out which write that is and stays quiet
|
|
184
|
+
// when control flow decides, so such a name steps nowhere at all.
|
|
185
|
+
rule(
|
|
186
|
+
"stepsTo",
|
|
187
|
+
[v("x"), v("y"), VALUE_STEP],
|
|
188
|
+
[lit("endsHolding", v("x"), v("y"))]
|
|
189
|
+
),
|
|
190
|
+
// An import steps to what the module exports under that name.
|
|
191
|
+
rule(
|
|
192
|
+
"stepsTo",
|
|
193
|
+
[v("x"), v("value"), VALUE_STEP],
|
|
194
|
+
[
|
|
195
|
+
lit("imports", v("x"), v("m"), v("n")),
|
|
196
|
+
lit("moduleExport", v("m"), v("n"), v("value"))
|
|
197
|
+
]
|
|
198
|
+
),
|
|
199
|
+
// A parameter steps to what a call passes it. A function called from
|
|
200
|
+
// several places leaves its parameter with more than one value, and a
|
|
201
|
+
// caller that needs those apart asks `paramAt`.
|
|
202
|
+
rule(
|
|
203
|
+
"stepsTo",
|
|
204
|
+
[v("p"), v("a"), VALUE_STEP],
|
|
205
|
+
[lit("passesArgument", v("r"), v("p"), v("a"))]
|
|
206
|
+
),
|
|
207
|
+
// Reading a property steps to what the object contains under that
|
|
208
|
+
// name, whichever way the object arrived: `routes.list` off a name,
|
|
209
|
+
// or `make(body).handle` off a call.
|
|
210
|
+
rule(
|
|
211
|
+
"stepsTo",
|
|
212
|
+
[v("x"), v("held"), VALUE_STEP],
|
|
213
|
+
[
|
|
214
|
+
lit("readsProperty", v("x"), v("o"), v("n")),
|
|
215
|
+
lit("objectOf", v("o"), v("obj")),
|
|
216
|
+
lit("contains", v("obj"), v("n"), v("held"))
|
|
217
|
+
]
|
|
218
|
+
),
|
|
219
|
+
// Calling a class makes one of it, so the call steps to the class and
|
|
220
|
+
// a method read off the result is the one the class declares. The
|
|
221
|
+
// caveat below is about a factory function, and a class is not one.
|
|
222
|
+
rule(
|
|
223
|
+
"stepsTo",
|
|
224
|
+
[v("r"), v("cls"), VALUE_STEP],
|
|
225
|
+
[
|
|
226
|
+
lit("call", v("r"), v("c")),
|
|
227
|
+
lit("comesTo", v("c"), v("cls")),
|
|
228
|
+
lit("objectValue", v("cls"))
|
|
229
|
+
]
|
|
230
|
+
),
|
|
231
|
+
// Wrapper transparency, derived: calling a factory that returns a
|
|
232
|
+
// function which calls its parameter k steps to argument k.
|
|
233
|
+
rule(
|
|
234
|
+
"stepsTo",
|
|
235
|
+
[v("r"), v("a"), VALUE_STEP],
|
|
236
|
+
[
|
|
237
|
+
lit("call", v("r"), v("c")),
|
|
238
|
+
lit("comesTo", v("c"), v("f")),
|
|
239
|
+
lit("unwraps", v("f"), v("k")),
|
|
240
|
+
lit("callArg", v("r"), v("k"), v("a"))
|
|
241
|
+
]
|
|
242
|
+
),
|
|
243
|
+
// Wrapper transparency, declared: a pack says this callee wraps
|
|
244
|
+
// argument k. The callee has to come from the library the pack said,
|
|
245
|
+
// so a local object spelled the same way is not mistaken for it.
|
|
246
|
+
rule(
|
|
247
|
+
"stepsTo",
|
|
248
|
+
[v("r"), v("a"), VALUE_STEP],
|
|
249
|
+
[
|
|
250
|
+
lit("calleeName", v("r"), v("n")),
|
|
251
|
+
lit("unwrapsByName", v("n"), v("k")),
|
|
252
|
+
lit("wrapperModule", v("n"), v("m")),
|
|
253
|
+
lit("calleeOrigin", v("r"), v("m")),
|
|
254
|
+
lit("callArg", v("r"), v("k"), v("a"))
|
|
255
|
+
]
|
|
256
|
+
),
|
|
257
|
+
// The one step that runs a function forwards: a call steps to what
|
|
258
|
+
// the function it invokes returns.
|
|
259
|
+
rule(
|
|
260
|
+
"stepsTo",
|
|
261
|
+
[v("r"), v("ret"), RESULT_STEP],
|
|
262
|
+
[lit("invokes", v("r"), v("f")), lit("returnsValue", v("f"), v("ret"))]
|
|
263
|
+
),
|
|
264
|
+
// Where a walk gets to, and whether it ran a call on the way. A value
|
|
265
|
+
// step keeps whatever the rest of the walk was, and a result step
|
|
266
|
+
// makes the whole walk one that ran a call.
|
|
267
|
+
rule(
|
|
268
|
+
"reaches",
|
|
269
|
+
[v("x"), v("z"), v("kind")],
|
|
270
|
+
[lit("stepsTo", v("x"), v("z"), v("kind"))]
|
|
271
|
+
),
|
|
272
|
+
rule(
|
|
273
|
+
"reaches",
|
|
274
|
+
[v("x"), v("z"), v("kind")],
|
|
275
|
+
[
|
|
276
|
+
lit("stepsTo", v("x"), v("y"), VALUE_STEP),
|
|
277
|
+
lit("reaches", v("y"), v("z"), v("kind"))
|
|
278
|
+
]
|
|
279
|
+
),
|
|
280
|
+
// Two rules rather than one leaving the rest of the walk's kind free.
|
|
281
|
+
// A free kind is a second question about the same relation, and a
|
|
282
|
+
// demand-driven run then derives both to answer either.
|
|
283
|
+
rule(
|
|
284
|
+
"reaches",
|
|
285
|
+
[v("x"), v("z"), RESULT_STEP],
|
|
286
|
+
[
|
|
287
|
+
lit("stepsTo", v("x"), v("y"), RESULT_STEP),
|
|
288
|
+
lit("reaches", v("y"), v("z"), VALUE_STEP)
|
|
289
|
+
]
|
|
290
|
+
),
|
|
291
|
+
rule(
|
|
292
|
+
"reaches",
|
|
293
|
+
[v("x"), v("z"), RESULT_STEP],
|
|
294
|
+
[
|
|
295
|
+
lit("stepsTo", v("x"), v("y"), RESULT_STEP),
|
|
296
|
+
lit("reaches", v("y"), v("z"), RESULT_STEP)
|
|
297
|
+
]
|
|
298
|
+
),
|
|
299
|
+
// What a value comes down to: the walk that never ran a call, stopped
|
|
300
|
+
// at a function or an object. Something already written out comes to
|
|
301
|
+
// itself, and every other walk ends at one of those.
|
|
178
302
|
rule("comesTo", [v("x"), v("x")], [lit("func", v("x"))]),
|
|
179
303
|
rule("comesTo", [v("x"), v("x")], [lit("objectValue", v("x"))]),
|
|
180
|
-
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
181
304
|
rule(
|
|
182
305
|
"comesTo",
|
|
183
306
|
[v("x"), v("z")],
|
|
184
|
-
[lit("
|
|
307
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("func", v("z"))]
|
|
185
308
|
),
|
|
186
|
-
// A name written more than once has the value the last write left
|
|
187
|
-
// there, and `binds` says nothing about it. The adapter works out
|
|
188
|
-
// which write that is, in its own language's terms, and stays quiet
|
|
189
|
-
// when control flow decides. A name it stays quiet about comes to no
|
|
190
|
-
// value, which is the answer.
|
|
191
309
|
rule(
|
|
192
310
|
"comesTo",
|
|
193
311
|
[v("x"), v("z")],
|
|
194
|
-
[lit("
|
|
312
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("objectValue", v("z"))]
|
|
195
313
|
),
|
|
196
|
-
//
|
|
314
|
+
// The same stopping condition, for the walk that ran a call. A call
|
|
315
|
+
// gets no `comesTo` answer on purpose: coming back with what a
|
|
316
|
+
// factory returned would fight the unwrapping answer.
|
|
197
317
|
rule(
|
|
198
|
-
"
|
|
318
|
+
"givesBack",
|
|
319
|
+
[v("x"), v("z")],
|
|
320
|
+
[lit("reaches", v("x"), v("z"), RESULT_STEP), lit("func", v("z"))]
|
|
321
|
+
),
|
|
322
|
+
rule(
|
|
323
|
+
"givesBack",
|
|
324
|
+
[v("x"), v("z")],
|
|
325
|
+
[lit("reaches", v("x"), v("z"), RESULT_STEP), lit("objectValue", v("z"))]
|
|
326
|
+
),
|
|
327
|
+
// Following a value to the expression it is written as, whatever kind
|
|
328
|
+
// that is. A GraphQL document is neither a function nor an object, so
|
|
329
|
+
// `comesTo` never reaches one.
|
|
330
|
+
rule("isWrittenAs", [v("x"), v("x")], [lit("writtenValue", v("x"))]),
|
|
331
|
+
rule("isWrittenAs", [v("x"), v("x")], [lit("objectValue", v("x"))]),
|
|
332
|
+
rule(
|
|
333
|
+
"isWrittenAs",
|
|
199
334
|
[v("x"), v("z")],
|
|
335
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("writtenValue", v("z"))]
|
|
336
|
+
),
|
|
337
|
+
rule(
|
|
338
|
+
"isWrittenAs",
|
|
339
|
+
[v("x"), v("z")],
|
|
340
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("objectValue", v("z"))]
|
|
341
|
+
),
|
|
342
|
+
// What one call site put in a parameter, told apart from what the
|
|
343
|
+
// other callers passed.
|
|
344
|
+
rule(
|
|
345
|
+
"paramAt",
|
|
346
|
+
[v("r"), v("p"), v("z")],
|
|
200
347
|
[
|
|
201
|
-
lit("
|
|
202
|
-
lit("
|
|
203
|
-
|
|
348
|
+
lit("passesArgument", v("r"), v("p"), v("a")),
|
|
349
|
+
lit("comesTo", v("a"), v("z"))
|
|
350
|
+
]
|
|
351
|
+
),
|
|
352
|
+
// An argument arriving at the parameter it is passed to, by position
|
|
353
|
+
// or by the name the caller wrote, keeping the call it went through
|
|
354
|
+
// so `paramAt` can tell two call sites apart.
|
|
355
|
+
rule(
|
|
356
|
+
"passesArgument",
|
|
357
|
+
[v("r"), v("p"), v("a")],
|
|
358
|
+
[
|
|
359
|
+
lit("paramOf", v("f"), v("k"), v("p")),
|
|
360
|
+
lit("callsFunction", v("r"), v("f")),
|
|
361
|
+
lit("callArg", v("r"), v("k"), v("a"))
|
|
362
|
+
]
|
|
363
|
+
),
|
|
364
|
+
rule(
|
|
365
|
+
"passesArgument",
|
|
366
|
+
[v("r"), v("p"), v("a")],
|
|
367
|
+
[
|
|
368
|
+
lit("paramNamed", v("f"), v("n"), v("p")),
|
|
369
|
+
lit("callsFunction", v("r"), v("f")),
|
|
370
|
+
lit("callKeywordArg", v("r"), v("n"), v("a"))
|
|
204
371
|
]
|
|
205
372
|
),
|
|
206
373
|
// What a module exports: directly, or through re-export chains.
|
|
@@ -225,12 +392,22 @@ var RESOLUTION_RULES = [
|
|
|
225
392
|
lit("moduleExport", v("m2"), v("n"), v("value"))
|
|
226
393
|
]
|
|
227
394
|
),
|
|
228
|
-
// The
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
395
|
+
// The function a call runs, written from the call's side because a
|
|
396
|
+
// caller asking what a call gives back has the call in hand.
|
|
397
|
+
rule(
|
|
398
|
+
"invokes",
|
|
399
|
+
[v("r"), v("f")],
|
|
400
|
+
[lit("call", v("r"), v("c")), lit("comesTo", v("c"), v("f"))]
|
|
401
|
+
),
|
|
402
|
+
// The callee is itself a call: `daoBuilder()()`.
|
|
403
|
+
rule(
|
|
404
|
+
"invokes",
|
|
405
|
+
[v("r"), v("f")],
|
|
406
|
+
[lit("call", v("r"), v("c")), lit("givesBack", v("c"), v("f"))]
|
|
407
|
+
),
|
|
408
|
+
// The object an expression refers to: a name through `comesTo`, a
|
|
409
|
+
// factory call through what it gives back. Naming the step is what
|
|
410
|
+
// makes `routes.list` and `make(body).handle` one rule.
|
|
234
411
|
rule(
|
|
235
412
|
"objectOf",
|
|
236
413
|
[v("o"), v("obj")],
|
|
@@ -238,14 +415,8 @@ var RESOLUTION_RULES = [
|
|
|
238
415
|
),
|
|
239
416
|
rule(
|
|
240
417
|
"objectOf",
|
|
241
|
-
[v("
|
|
242
|
-
[
|
|
243
|
-
lit("call", v("r"), v("c")),
|
|
244
|
-
lit("comesTo", v("c"), v("f")),
|
|
245
|
-
lit("returnsValue", v("f"), v("ret")),
|
|
246
|
-
lit("comesTo", v("ret"), v("obj")),
|
|
247
|
-
lit("objectValue", v("obj"))
|
|
248
|
-
]
|
|
418
|
+
[v("x"), v("obj")],
|
|
419
|
+
[lit("givesBack", v("x"), v("obj")), lit("objectValue", v("obj"))]
|
|
249
420
|
),
|
|
250
421
|
// Which calls a function, found by the name the call is written as
|
|
251
422
|
// rather than by resolving every callee in the project. A caller knows
|
|
@@ -278,42 +449,6 @@ var RESOLUTION_RULES = [
|
|
|
278
449
|
lit("call", v("r"), v("c"))
|
|
279
450
|
]
|
|
280
451
|
),
|
|
281
|
-
// A function whose return is itself a call arrives at the same object
|
|
282
|
-
// that call does, so a chain passing through two factories settles.
|
|
283
|
-
rule(
|
|
284
|
-
"objectOf",
|
|
285
|
-
[v("r"), v("obj")],
|
|
286
|
-
[
|
|
287
|
-
lit("call", v("r"), v("c")),
|
|
288
|
-
lit("comesTo", v("c"), v("f")),
|
|
289
|
-
lit("returnsValue", v("f"), v("ret")),
|
|
290
|
-
lit("objectOf", v("ret"), v("obj"))
|
|
291
|
-
]
|
|
292
|
-
),
|
|
293
|
-
// An argument reaches the parameter it is passed to, by position or
|
|
294
|
-
// by the name the caller wrote. A function called from several places
|
|
295
|
-
// leaves its parameter with more than one value, and the caller
|
|
296
|
-
// decides what to do about that.
|
|
297
|
-
rule(
|
|
298
|
-
"comesTo",
|
|
299
|
-
[v("p"), v("z")],
|
|
300
|
-
[
|
|
301
|
-
lit("paramOf", v("f"), v("k"), v("p")),
|
|
302
|
-
lit("callsFunction", v("r"), v("f")),
|
|
303
|
-
lit("callArg", v("r"), v("k"), v("a")),
|
|
304
|
-
lit("comesTo", v("a"), v("z"))
|
|
305
|
-
]
|
|
306
|
-
),
|
|
307
|
-
rule(
|
|
308
|
-
"comesTo",
|
|
309
|
-
[v("p"), v("z")],
|
|
310
|
-
[
|
|
311
|
-
lit("paramNamed", v("f"), v("n"), v("p")),
|
|
312
|
-
lit("callsFunction", v("r"), v("f")),
|
|
313
|
-
lit("callKeywordArg", v("r"), v("n"), v("a")),
|
|
314
|
-
lit("comesTo", v("a"), v("z"))
|
|
315
|
-
]
|
|
316
|
-
),
|
|
317
452
|
// What an object contains, its base class included, so a method the base
|
|
318
453
|
// declares is found on a subclass that never overrode it. A method both
|
|
319
454
|
// declare gives two, and the caller decides. This is its own relation
|
|
@@ -333,33 +468,8 @@ var RESOLUTION_RULES = [
|
|
|
333
468
|
lit("contains", v("baseCls"), v("n"), v("held"))
|
|
334
469
|
]
|
|
335
470
|
),
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
// about a factory function, and a class is not one.
|
|
339
|
-
rule(
|
|
340
|
-
"comesTo",
|
|
341
|
-
[v("r"), v("cls")],
|
|
342
|
-
[
|
|
343
|
-
lit("call", v("r"), v("c")),
|
|
344
|
-
lit("comesTo", v("c"), v("cls")),
|
|
345
|
-
lit("objectValue", v("cls"))
|
|
346
|
-
]
|
|
347
|
-
),
|
|
348
|
-
// Reading a property comes to what the object holds under that name,
|
|
349
|
-
// whichever way the object arrived: `routes.list` off a name, or
|
|
350
|
-
// `make(body).handle` off a call.
|
|
351
|
-
rule(
|
|
352
|
-
"comesTo",
|
|
353
|
-
[v("x"), v("z")],
|
|
354
|
-
[
|
|
355
|
-
lit("readsProperty", v("x"), v("o"), v("n")),
|
|
356
|
-
lit("objectOf", v("o"), v("obj")),
|
|
357
|
-
lit("contains", v("obj"), v("n"), v("held")),
|
|
358
|
-
lit("comesTo", v("held"), v("z"))
|
|
359
|
-
]
|
|
360
|
-
),
|
|
361
|
-
// Wrapper transparency, derived: calling a factory that returns a
|
|
362
|
-
// function which calls its parameter k comes to argument k.
|
|
471
|
+
// Which function a factory returns, and which of its parameters that
|
|
472
|
+
// function calls: together they are what makes it a wrapper.
|
|
363
473
|
rule(
|
|
364
474
|
"returnsFunc",
|
|
365
475
|
[v("f"), v("g")],
|
|
@@ -420,56 +530,20 @@ var RESOLUTION_RULES = [
|
|
|
420
530
|
lit("paramOf", v("f"), v("k"), v("p"))
|
|
421
531
|
]
|
|
422
532
|
),
|
|
423
|
-
rule(
|
|
424
|
-
"comesTo",
|
|
425
|
-
[v("r"), v("h")],
|
|
426
|
-
[
|
|
427
|
-
lit("call", v("r"), v("c")),
|
|
428
|
-
lit("comesTo", v("c"), v("f")),
|
|
429
|
-
lit("unwraps", v("f"), v("k")),
|
|
430
|
-
lit("callArg", v("r"), v("k"), v("a")),
|
|
431
|
-
lit("comesTo", v("a"), v("h"))
|
|
432
|
-
]
|
|
433
|
-
),
|
|
434
|
-
// Wrapper transparency, declared: a pack says this callee wraps
|
|
435
|
-
// argument k, whatever its implementation looks like. The callee has
|
|
436
|
-
// to have been imported from the library the pack named, so a local
|
|
437
|
-
// object spelled the same way is not mistaken for it.
|
|
438
|
-
rule(
|
|
439
|
-
"comesTo",
|
|
440
|
-
[v("r"), v("h")],
|
|
441
|
-
[
|
|
442
|
-
lit("calleeName", v("r"), v("n")),
|
|
443
|
-
lit("unwrapsByName", v("n"), v("k")),
|
|
444
|
-
lit("wrapperModule", v("n"), v("m")),
|
|
445
|
-
lit("calleeOrigin", v("r"), v("m")),
|
|
446
|
-
lit("callArg", v("r"), v("k"), v("a")),
|
|
447
|
-
lit("comesTo", v("a"), v("h"))
|
|
448
|
-
]
|
|
449
|
-
),
|
|
450
533
|
// Where a name comes from, when what it refers to lives outside the
|
|
451
|
-
// source being read.
|
|
452
|
-
// never reaches a library's own function
|
|
453
|
-
// library name has no answer at all. This follows the same aliases
|
|
454
|
-
// and barrels in the other direction and answers with the module and
|
|
455
|
-
// the name that module exports.
|
|
534
|
+
// source being read. A walk ends at something written out in source,
|
|
535
|
+
// so it never reaches a library's own function.
|
|
456
536
|
rule(
|
|
457
537
|
"comesFrom",
|
|
458
538
|
[v("x"), v("m"), v("n")],
|
|
459
539
|
[lit("imports", v("x"), v("m"), v("n"))]
|
|
460
540
|
),
|
|
461
|
-
rule(
|
|
462
|
-
"comesFrom",
|
|
463
|
-
[v("x"), v("m"), v("n")],
|
|
464
|
-
[lit("binds", v("x"), v("y")), lit("comesFrom", v("y"), v("m"), v("n"))]
|
|
465
|
-
),
|
|
466
541
|
rule(
|
|
467
542
|
"comesFrom",
|
|
468
543
|
[v("x"), v("m"), v("n")],
|
|
469
544
|
[
|
|
470
|
-
lit("
|
|
471
|
-
lit("
|
|
472
|
-
lit("comesFrom", v("value"), v("m"), v("n"))
|
|
545
|
+
lit("reaches", v("x"), v("y"), VALUE_STEP),
|
|
546
|
+
lit("imports", v("y"), v("m"), v("n"))
|
|
473
547
|
]
|
|
474
548
|
),
|
|
475
549
|
// Calling f ends up calling the name n that module m exports. One hop
|
|
@@ -505,36 +579,6 @@ var RESOLUTION_RULES = [
|
|
|
505
579
|
"resolves",
|
|
506
580
|
[v("x"), v("z")],
|
|
507
581
|
[lit("comesTo", v("x"), v("z")), lit("func", v("z"))]
|
|
508
|
-
),
|
|
509
|
-
// Following a name to the expression it is written as. A separate
|
|
510
|
-
// relation rather than a widening of `comesTo`, so a caller asking
|
|
511
|
-
// the older question never sees a new answer: a factory call already
|
|
512
|
-
// has an unwrapping answer, and letting it also answer with itself
|
|
513
|
-
// would make that pair ambiguous.
|
|
514
|
-
//
|
|
515
|
-
// The two seeds are the two ways an expression can be written out. An
|
|
516
|
-
// object literal is one of them, and it is the shape a generated
|
|
517
|
-
// GraphQL document takes.
|
|
518
|
-
rule("isWrittenAs", [v("x"), v("x")], [lit("writtenValue", v("x"))]),
|
|
519
|
-
rule("isWrittenAs", [v("x"), v("x")], [lit("objectValue", v("x"))]),
|
|
520
|
-
rule(
|
|
521
|
-
"isWrittenAs",
|
|
522
|
-
[v("x"), v("z")],
|
|
523
|
-
[lit("binds", v("x"), v("y")), lit("isWrittenAs", v("y"), v("z"))]
|
|
524
|
-
),
|
|
525
|
-
rule(
|
|
526
|
-
"isWrittenAs",
|
|
527
|
-
[v("x"), v("z")],
|
|
528
|
-
[lit("endsHolding", v("x"), v("y")), lit("isWrittenAs", v("y"), v("z"))]
|
|
529
|
-
),
|
|
530
|
-
rule(
|
|
531
|
-
"isWrittenAs",
|
|
532
|
-
[v("x"), v("z")],
|
|
533
|
-
[
|
|
534
|
-
lit("imports", v("x"), v("m"), v("n")),
|
|
535
|
-
lit("moduleExport", v("m"), v("n"), v("value")),
|
|
536
|
-
lit("isWrittenAs", v("value"), v("z"))
|
|
537
|
-
]
|
|
538
582
|
)
|
|
539
583
|
];
|
|
540
584
|
var RESOLUTION_QUESTIONS = [
|
|
@@ -566,6 +610,13 @@ var RESOLUTION_QUESTIONS = [
|
|
|
566
610
|
[v("x"), v("z")],
|
|
567
611
|
[lit("wanted", v("x")), lit("objectOf", v("x"), v("z"))]
|
|
568
612
|
),
|
|
613
|
+
// Keyed by the parameter, since that is what a caller has in hand
|
|
614
|
+
// when it wants the call sites told apart.
|
|
615
|
+
rule(
|
|
616
|
+
"wantedParamAt",
|
|
617
|
+
[v("p"), v("r"), v("z")],
|
|
618
|
+
[lit("wanted", v("p")), lit("paramAt", v("r"), v("p"), v("z"))]
|
|
619
|
+
),
|
|
569
620
|
rule(
|
|
570
621
|
"wantedComesFrom",
|
|
571
622
|
[v("x"), v("m"), v("n")],
|
|
@@ -589,5 +640,7 @@ export {
|
|
|
589
640
|
FACT_CONTRACT_CASES,
|
|
590
641
|
RESOLUTION_QUESTIONS,
|
|
591
642
|
RESOLUTION_RULES,
|
|
643
|
+
RESULT_STEP,
|
|
644
|
+
VALUE_STEP,
|
|
592
645
|
checkFactContract
|
|
593
646
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/resolution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Language-neutral rules for following a value to the function it comes down to.",
|
|
6
6
|
"keywords": [
|
|
@@ -53,6 +53,6 @@
|
|
|
53
53
|
"access": "public"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@suss/datalog": "0.
|
|
56
|
+
"@suss/datalog": "0.9.0"
|
|
57
57
|
}
|
|
58
58
|
}
|