@suss/resolution 0.7.2 → 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 +245 -158
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +244 -159
- 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);
|
|
@@ -123,6 +125,26 @@ var FACT_CONTRACT_CASES = [
|
|
|
123
125
|
return contained.some((row) => functions.has(row[2])) ? null : "what a class contains under a method name is not the node that declares the method";
|
|
124
126
|
}
|
|
125
127
|
},
|
|
128
|
+
{
|
|
129
|
+
name: "a class constructed with an argument",
|
|
130
|
+
requires: "a module-level class whose constructor takes one parameter, and a site that makes one of it, passing a name",
|
|
131
|
+
// However a language writes construction, it is a call of the class, and
|
|
132
|
+
// the constructor's parameters are the class's. Keying them on the
|
|
133
|
+
// constructor instead leaves the argument with nowhere to land.
|
|
134
|
+
check: (facts) => {
|
|
135
|
+
const classes = new Set(facts("objectValue").map((row) => row[0] ?? ""));
|
|
136
|
+
const parameters = facts("paramOf").filter(
|
|
137
|
+
(row) => classes.has(row[0] ?? "")
|
|
138
|
+
);
|
|
139
|
+
if (parameters.length === 0) {
|
|
140
|
+
return "a class lists none of its constructor's parameters, so an argument a construction site passes reaches nothing";
|
|
141
|
+
}
|
|
142
|
+
if (facts("call").length === 0) {
|
|
143
|
+
return "making one of a class is not written down as a call, so nothing says which class a construction arrives at";
|
|
144
|
+
}
|
|
145
|
+
return facts("callArg").length > 0 ? null : "a construction passes its argument nowhere, so the parameter it lands in has no value";
|
|
146
|
+
}
|
|
147
|
+
},
|
|
126
148
|
{
|
|
127
149
|
name: "a value another file declares",
|
|
128
150
|
requires: "two files, one declaring a value and the other reading it by name",
|
|
@@ -182,35 +204,202 @@ function checkFactContract(sources, emit, options = {}) {
|
|
|
182
204
|
|
|
183
205
|
// src/index.ts
|
|
184
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");
|
|
185
209
|
var RESOLUTION_RULES = [
|
|
186
|
-
//
|
|
187
|
-
//
|
|
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.
|
|
188
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"))]),
|
|
189
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"))]),
|
|
190
|
-
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
191
336
|
(0, import_datalog.rule)(
|
|
192
337
|
"comesTo",
|
|
193
338
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
194
|
-
[(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"))]
|
|
195
340
|
),
|
|
196
|
-
// A name written more than once has the value the last write left
|
|
197
|
-
// there, and `binds` says nothing about it. The adapter works out
|
|
198
|
-
// which write that is, in its own language's terms, and stays quiet
|
|
199
|
-
// when control flow decides. A name it stays quiet about comes to no
|
|
200
|
-
// value, which is the answer.
|
|
201
341
|
(0, import_datalog.rule)(
|
|
202
342
|
"comesTo",
|
|
203
343
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
204
|
-
[(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"))]
|
|
205
345
|
),
|
|
206
|
-
//
|
|
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.
|
|
207
349
|
(0, import_datalog.rule)(
|
|
208
|
-
"
|
|
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",
|
|
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",
|
|
209
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")],
|
|
210
379
|
[
|
|
211
|
-
(0, import_datalog.lit)("
|
|
212
|
-
(0, import_datalog.lit)("
|
|
213
|
-
|
|
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"))
|
|
214
403
|
]
|
|
215
404
|
),
|
|
216
405
|
// What a module exports: directly, or through re-export chains.
|
|
@@ -235,12 +424,22 @@ var RESOLUTION_RULES = [
|
|
|
235
424
|
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m2"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value"))
|
|
236
425
|
]
|
|
237
426
|
),
|
|
238
|
-
// The
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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.
|
|
244
443
|
(0, import_datalog.rule)(
|
|
245
444
|
"objectOf",
|
|
246
445
|
[(0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")],
|
|
@@ -248,14 +447,8 @@ var RESOLUTION_RULES = [
|
|
|
248
447
|
),
|
|
249
448
|
(0, import_datalog.rule)(
|
|
250
449
|
"objectOf",
|
|
251
|
-
[(0, import_datalog.variable)("
|
|
252
|
-
[
|
|
253
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
254
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
255
|
-
(0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret")),
|
|
256
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("ret"), (0, import_datalog.variable)("obj")),
|
|
257
|
-
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("obj"))
|
|
258
|
-
]
|
|
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"))]
|
|
259
452
|
),
|
|
260
453
|
// Which calls a function, found by the name the call is written as
|
|
261
454
|
// rather than by resolving every callee in the project. A caller knows
|
|
@@ -276,40 +469,16 @@ var RESOLUTION_RULES = [
|
|
|
276
469
|
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c"))
|
|
277
470
|
]
|
|
278
471
|
),
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
(0, import_datalog.rule)(
|
|
282
|
-
"objectOf",
|
|
283
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("obj")],
|
|
284
|
-
[
|
|
285
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
286
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
287
|
-
(0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret")),
|
|
288
|
-
(0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("ret"), (0, import_datalog.variable)("obj"))
|
|
289
|
-
]
|
|
290
|
-
),
|
|
291
|
-
// An argument reaches the parameter it is passed to, by position or
|
|
292
|
-
// by the name the caller wrote. A function called from several places
|
|
293
|
-
// leaves its parameter with more than one value, and the caller
|
|
294
|
-
// decides what to do about that.
|
|
295
|
-
(0, import_datalog.rule)(
|
|
296
|
-
"comesTo",
|
|
297
|
-
[(0, import_datalog.variable)("p"), (0, import_datalog.variable)("z")],
|
|
298
|
-
[
|
|
299
|
-
(0, import_datalog.lit)("paramOf", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("p")),
|
|
300
|
-
(0, import_datalog.lit)("callsFunction", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")),
|
|
301
|
-
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a")),
|
|
302
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("z"))
|
|
303
|
-
]
|
|
304
|
-
),
|
|
472
|
+
// The same, for a language whose adapter writes the import down as a
|
|
473
|
+
// declaration and the call's callee as the name referring to it.
|
|
305
474
|
(0, import_datalog.rule)(
|
|
306
|
-
"
|
|
307
|
-
[(0, import_datalog.variable)("
|
|
475
|
+
"callsFunction",
|
|
476
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("f")],
|
|
308
477
|
[
|
|
309
|
-
(0, import_datalog.lit)("
|
|
310
|
-
(0, import_datalog.lit)("
|
|
311
|
-
(0, import_datalog.lit)("
|
|
312
|
-
(0, import_datalog.lit)("
|
|
478
|
+
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("f")),
|
|
479
|
+
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("d"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")),
|
|
480
|
+
(0, import_datalog.lit)("binds", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("d")),
|
|
481
|
+
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c"))
|
|
313
482
|
]
|
|
314
483
|
),
|
|
315
484
|
// What an object contains, its base class included, so a method the base
|
|
@@ -331,33 +500,8 @@ var RESOLUTION_RULES = [
|
|
|
331
500
|
(0, import_datalog.lit)("contains", (0, import_datalog.variable)("baseCls"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held"))
|
|
332
501
|
]
|
|
333
502
|
),
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// about a factory function, and a class is not one.
|
|
337
|
-
(0, import_datalog.rule)(
|
|
338
|
-
"comesTo",
|
|
339
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("cls")],
|
|
340
|
-
[
|
|
341
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
342
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("cls")),
|
|
343
|
-
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("cls"))
|
|
344
|
-
]
|
|
345
|
-
),
|
|
346
|
-
// Reading a property comes to what the object holds under that name,
|
|
347
|
-
// whichever way the object arrived: `routes.list` off a name, or
|
|
348
|
-
// `make(body).handle` off a call.
|
|
349
|
-
(0, import_datalog.rule)(
|
|
350
|
-
"comesTo",
|
|
351
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
352
|
-
[
|
|
353
|
-
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.variable)("n")),
|
|
354
|
-
(0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")),
|
|
355
|
-
(0, import_datalog.lit)("contains", (0, import_datalog.variable)("obj"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held")),
|
|
356
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("held"), (0, import_datalog.variable)("z"))
|
|
357
|
-
]
|
|
358
|
-
),
|
|
359
|
-
// Wrapper transparency, derived: calling a factory that returns a
|
|
360
|
-
// 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.
|
|
361
505
|
(0, import_datalog.rule)(
|
|
362
506
|
"returnsFunc",
|
|
363
507
|
[(0, import_datalog.variable)("f"), (0, import_datalog.variable)("g")],
|
|
@@ -418,56 +562,20 @@ var RESOLUTION_RULES = [
|
|
|
418
562
|
(0, import_datalog.lit)("paramOf", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("p"))
|
|
419
563
|
]
|
|
420
564
|
),
|
|
421
|
-
(0, import_datalog.rule)(
|
|
422
|
-
"comesTo",
|
|
423
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("h")],
|
|
424
|
-
[
|
|
425
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
426
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
427
|
-
(0, import_datalog.lit)("unwraps", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("k")),
|
|
428
|
-
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a")),
|
|
429
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("h"))
|
|
430
|
-
]
|
|
431
|
-
),
|
|
432
|
-
// Wrapper transparency, declared: a pack says this callee wraps
|
|
433
|
-
// argument k, whatever its implementation looks like. The callee has
|
|
434
|
-
// to have been imported from the library the pack named, so a local
|
|
435
|
-
// object spelled the same way is not mistaken for it.
|
|
436
|
-
(0, import_datalog.rule)(
|
|
437
|
-
"comesTo",
|
|
438
|
-
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("h")],
|
|
439
|
-
[
|
|
440
|
-
(0, import_datalog.lit)("calleeName", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("n")),
|
|
441
|
-
(0, import_datalog.lit)("unwrapsByName", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("k")),
|
|
442
|
-
(0, import_datalog.lit)("wrapperModule", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("m")),
|
|
443
|
-
(0, import_datalog.lit)("calleeOrigin", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("m")),
|
|
444
|
-
(0, import_datalog.lit)("callArg", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("k"), (0, import_datalog.variable)("a")),
|
|
445
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("h"))
|
|
446
|
-
]
|
|
447
|
-
),
|
|
448
565
|
// Where a name comes from, when what it refers to lives outside the
|
|
449
|
-
// source being read.
|
|
450
|
-
// never reaches a library's own function
|
|
451
|
-
// library name has no answer at all. This follows the same aliases
|
|
452
|
-
// and barrels in the other direction and answers with the module and
|
|
453
|
-
// 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.
|
|
454
568
|
(0, import_datalog.rule)(
|
|
455
569
|
"comesFrom",
|
|
456
570
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
457
571
|
[(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))]
|
|
458
572
|
),
|
|
459
|
-
(0, import_datalog.rule)(
|
|
460
|
-
"comesFrom",
|
|
461
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
462
|
-
[(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"))]
|
|
463
|
-
),
|
|
464
573
|
(0, import_datalog.rule)(
|
|
465
574
|
"comesFrom",
|
|
466
575
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
467
576
|
[
|
|
468
|
-
(0, import_datalog.lit)("
|
|
469
|
-
(0, import_datalog.lit)("
|
|
470
|
-
(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"))
|
|
471
579
|
]
|
|
472
580
|
),
|
|
473
581
|
// Calling f ends up calling the name n that module m exports. One hop
|
|
@@ -503,36 +611,6 @@ var RESOLUTION_RULES = [
|
|
|
503
611
|
"resolves",
|
|
504
612
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
505
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"))]
|
|
506
|
-
),
|
|
507
|
-
// Following a name to the expression it is written as. A separate
|
|
508
|
-
// relation rather than a widening of `comesTo`, so a caller asking
|
|
509
|
-
// the older question never sees a new answer: a factory call already
|
|
510
|
-
// has an unwrapping answer, and letting it also answer with itself
|
|
511
|
-
// would make that pair ambiguous.
|
|
512
|
-
//
|
|
513
|
-
// The two seeds are the two ways an expression can be written out. An
|
|
514
|
-
// object literal is one of them, and it is the shape a generated
|
|
515
|
-
// GraphQL document takes.
|
|
516
|
-
(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"))]),
|
|
517
|
-
(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"))]),
|
|
518
|
-
(0, import_datalog.rule)(
|
|
519
|
-
"isWrittenAs",
|
|
520
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
521
|
-
[(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"))]
|
|
522
|
-
),
|
|
523
|
-
(0, import_datalog.rule)(
|
|
524
|
-
"isWrittenAs",
|
|
525
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
526
|
-
[(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"))]
|
|
527
|
-
),
|
|
528
|
-
(0, import_datalog.rule)(
|
|
529
|
-
"isWrittenAs",
|
|
530
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
531
|
-
[
|
|
532
|
-
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")),
|
|
533
|
-
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value")),
|
|
534
|
-
(0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("value"), (0, import_datalog.variable)("z"))
|
|
535
|
-
]
|
|
536
614
|
)
|
|
537
615
|
];
|
|
538
616
|
var RESOLUTION_QUESTIONS = [
|
|
@@ -564,6 +642,13 @@ var RESOLUTION_QUESTIONS = [
|
|
|
564
642
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
565
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"))]
|
|
566
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
|
+
),
|
|
567
652
|
(0, import_datalog.rule)(
|
|
568
653
|
"wantedComesFrom",
|
|
569
654
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
@@ -588,5 +673,7 @@ var ANSWER_RELATIONS = [
|
|
|
588
673
|
FACT_CONTRACT_CASES,
|
|
589
674
|
RESOLUTION_QUESTIONS,
|
|
590
675
|
RESOLUTION_RULES,
|
|
676
|
+
RESULT_STEP,
|
|
677
|
+
VALUE_STEP,
|
|
591
678
|
checkFactContract
|
|
592
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
|
@@ -93,6 +93,26 @@ var FACT_CONTRACT_CASES = [
|
|
|
93
93
|
return contained.some((row) => functions.has(row[2])) ? null : "what a class contains under a method name is not the node that declares the method";
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
|
+
{
|
|
97
|
+
name: "a class constructed with an argument",
|
|
98
|
+
requires: "a module-level class whose constructor takes one parameter, and a site that makes one of it, passing a name",
|
|
99
|
+
// However a language writes construction, it is a call of the class, and
|
|
100
|
+
// the constructor's parameters are the class's. Keying them on the
|
|
101
|
+
// constructor instead leaves the argument with nowhere to land.
|
|
102
|
+
check: (facts) => {
|
|
103
|
+
const classes = new Set(facts("objectValue").map((row) => row[0] ?? ""));
|
|
104
|
+
const parameters = facts("paramOf").filter(
|
|
105
|
+
(row) => classes.has(row[0] ?? "")
|
|
106
|
+
);
|
|
107
|
+
if (parameters.length === 0) {
|
|
108
|
+
return "a class lists none of its constructor's parameters, so an argument a construction site passes reaches nothing";
|
|
109
|
+
}
|
|
110
|
+
if (facts("call").length === 0) {
|
|
111
|
+
return "making one of a class is not written down as a call, so nothing says which class a construction arrives at";
|
|
112
|
+
}
|
|
113
|
+
return facts("callArg").length > 0 ? null : "a construction passes its argument nowhere, so the parameter it lands in has no value";
|
|
114
|
+
}
|
|
115
|
+
},
|
|
96
116
|
{
|
|
97
117
|
name: "a value another file declares",
|
|
98
118
|
requires: "two files, one declaring a value and the other reading it by name",
|
|
@@ -151,36 +171,203 @@ function checkFactContract(sources, emit, options = {}) {
|
|
|
151
171
|
}
|
|
152
172
|
|
|
153
173
|
// src/index.ts
|
|
154
|
-
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");
|
|
155
177
|
var RESOLUTION_RULES = [
|
|
156
|
-
//
|
|
157
|
-
//
|
|
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.
|
|
158
302
|
rule("comesTo", [v("x"), v("x")], [lit("func", v("x"))]),
|
|
159
303
|
rule("comesTo", [v("x"), v("x")], [lit("objectValue", v("x"))]),
|
|
160
|
-
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
161
304
|
rule(
|
|
162
305
|
"comesTo",
|
|
163
306
|
[v("x"), v("z")],
|
|
164
|
-
[lit("
|
|
307
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("func", v("z"))]
|
|
165
308
|
),
|
|
166
|
-
// A name written more than once has the value the last write left
|
|
167
|
-
// there, and `binds` says nothing about it. The adapter works out
|
|
168
|
-
// which write that is, in its own language's terms, and stays quiet
|
|
169
|
-
// when control flow decides. A name it stays quiet about comes to no
|
|
170
|
-
// value, which is the answer.
|
|
171
309
|
rule(
|
|
172
310
|
"comesTo",
|
|
173
311
|
[v("x"), v("z")],
|
|
174
|
-
[lit("
|
|
312
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("objectValue", v("z"))]
|
|
175
313
|
),
|
|
176
|
-
//
|
|
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.
|
|
177
317
|
rule(
|
|
178
|
-
"
|
|
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",
|
|
334
|
+
[v("x"), v("z")],
|
|
335
|
+
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("writtenValue", v("z"))]
|
|
336
|
+
),
|
|
337
|
+
rule(
|
|
338
|
+
"isWrittenAs",
|
|
179
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")],
|
|
180
347
|
[
|
|
181
|
-
lit("
|
|
182
|
-
lit("
|
|
183
|
-
|
|
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"))
|
|
184
371
|
]
|
|
185
372
|
),
|
|
186
373
|
// What a module exports: directly, or through re-export chains.
|
|
@@ -205,12 +392,22 @@ var RESOLUTION_RULES = [
|
|
|
205
392
|
lit("moduleExport", v("m2"), v("n"), v("value"))
|
|
206
393
|
]
|
|
207
394
|
),
|
|
208
|
-
// The
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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.
|
|
214
411
|
rule(
|
|
215
412
|
"objectOf",
|
|
216
413
|
[v("o"), v("obj")],
|
|
@@ -218,14 +415,8 @@ var RESOLUTION_RULES = [
|
|
|
218
415
|
),
|
|
219
416
|
rule(
|
|
220
417
|
"objectOf",
|
|
221
|
-
[v("
|
|
222
|
-
[
|
|
223
|
-
lit("call", v("r"), v("c")),
|
|
224
|
-
lit("comesTo", v("c"), v("f")),
|
|
225
|
-
lit("returnsValue", v("f"), v("ret")),
|
|
226
|
-
lit("comesTo", v("ret"), v("obj")),
|
|
227
|
-
lit("objectValue", v("obj"))
|
|
228
|
-
]
|
|
418
|
+
[v("x"), v("obj")],
|
|
419
|
+
[lit("givesBack", v("x"), v("obj")), lit("objectValue", v("obj"))]
|
|
229
420
|
),
|
|
230
421
|
// Which calls a function, found by the name the call is written as
|
|
231
422
|
// rather than by resolving every callee in the project. A caller knows
|
|
@@ -246,40 +437,16 @@ var RESOLUTION_RULES = [
|
|
|
246
437
|
lit("call", v("r"), v("c"))
|
|
247
438
|
]
|
|
248
439
|
),
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
rule(
|
|
252
|
-
"objectOf",
|
|
253
|
-
[v("r"), v("obj")],
|
|
254
|
-
[
|
|
255
|
-
lit("call", v("r"), v("c")),
|
|
256
|
-
lit("comesTo", v("c"), v("f")),
|
|
257
|
-
lit("returnsValue", v("f"), v("ret")),
|
|
258
|
-
lit("objectOf", v("ret"), v("obj"))
|
|
259
|
-
]
|
|
260
|
-
),
|
|
261
|
-
// An argument reaches the parameter it is passed to, by position or
|
|
262
|
-
// by the name the caller wrote. A function called from several places
|
|
263
|
-
// leaves its parameter with more than one value, and the caller
|
|
264
|
-
// decides what to do about that.
|
|
265
|
-
rule(
|
|
266
|
-
"comesTo",
|
|
267
|
-
[v("p"), v("z")],
|
|
268
|
-
[
|
|
269
|
-
lit("paramOf", v("f"), v("k"), v("p")),
|
|
270
|
-
lit("callsFunction", v("r"), v("f")),
|
|
271
|
-
lit("callArg", v("r"), v("k"), v("a")),
|
|
272
|
-
lit("comesTo", v("a"), v("z"))
|
|
273
|
-
]
|
|
274
|
-
),
|
|
440
|
+
// The same, for a language whose adapter writes the import down as a
|
|
441
|
+
// declaration and the call's callee as the name referring to it.
|
|
275
442
|
rule(
|
|
276
|
-
"
|
|
277
|
-
[v("
|
|
443
|
+
"callsFunction",
|
|
444
|
+
[v("r"), v("f")],
|
|
278
445
|
[
|
|
279
|
-
lit("
|
|
280
|
-
lit("
|
|
281
|
-
lit("
|
|
282
|
-
lit("
|
|
446
|
+
lit("moduleExport", v("m"), v("n"), v("f")),
|
|
447
|
+
lit("imports", v("d"), v("m"), v("n")),
|
|
448
|
+
lit("binds", v("c"), v("d")),
|
|
449
|
+
lit("call", v("r"), v("c"))
|
|
283
450
|
]
|
|
284
451
|
),
|
|
285
452
|
// What an object contains, its base class included, so a method the base
|
|
@@ -301,33 +468,8 @@ var RESOLUTION_RULES = [
|
|
|
301
468
|
lit("contains", v("baseCls"), v("n"), v("held"))
|
|
302
469
|
]
|
|
303
470
|
),
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
// about a factory function, and a class is not one.
|
|
307
|
-
rule(
|
|
308
|
-
"comesTo",
|
|
309
|
-
[v("r"), v("cls")],
|
|
310
|
-
[
|
|
311
|
-
lit("call", v("r"), v("c")),
|
|
312
|
-
lit("comesTo", v("c"), v("cls")),
|
|
313
|
-
lit("objectValue", v("cls"))
|
|
314
|
-
]
|
|
315
|
-
),
|
|
316
|
-
// Reading a property comes to what the object holds under that name,
|
|
317
|
-
// whichever way the object arrived: `routes.list` off a name, or
|
|
318
|
-
// `make(body).handle` off a call.
|
|
319
|
-
rule(
|
|
320
|
-
"comesTo",
|
|
321
|
-
[v("x"), v("z")],
|
|
322
|
-
[
|
|
323
|
-
lit("readsProperty", v("x"), v("o"), v("n")),
|
|
324
|
-
lit("objectOf", v("o"), v("obj")),
|
|
325
|
-
lit("contains", v("obj"), v("n"), v("held")),
|
|
326
|
-
lit("comesTo", v("held"), v("z"))
|
|
327
|
-
]
|
|
328
|
-
),
|
|
329
|
-
// Wrapper transparency, derived: calling a factory that returns a
|
|
330
|
-
// 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.
|
|
331
473
|
rule(
|
|
332
474
|
"returnsFunc",
|
|
333
475
|
[v("f"), v("g")],
|
|
@@ -388,56 +530,20 @@ var RESOLUTION_RULES = [
|
|
|
388
530
|
lit("paramOf", v("f"), v("k"), v("p"))
|
|
389
531
|
]
|
|
390
532
|
),
|
|
391
|
-
rule(
|
|
392
|
-
"comesTo",
|
|
393
|
-
[v("r"), v("h")],
|
|
394
|
-
[
|
|
395
|
-
lit("call", v("r"), v("c")),
|
|
396
|
-
lit("comesTo", v("c"), v("f")),
|
|
397
|
-
lit("unwraps", v("f"), v("k")),
|
|
398
|
-
lit("callArg", v("r"), v("k"), v("a")),
|
|
399
|
-
lit("comesTo", v("a"), v("h"))
|
|
400
|
-
]
|
|
401
|
-
),
|
|
402
|
-
// Wrapper transparency, declared: a pack says this callee wraps
|
|
403
|
-
// argument k, whatever its implementation looks like. The callee has
|
|
404
|
-
// to have been imported from the library the pack named, so a local
|
|
405
|
-
// object spelled the same way is not mistaken for it.
|
|
406
|
-
rule(
|
|
407
|
-
"comesTo",
|
|
408
|
-
[v("r"), v("h")],
|
|
409
|
-
[
|
|
410
|
-
lit("calleeName", v("r"), v("n")),
|
|
411
|
-
lit("unwrapsByName", v("n"), v("k")),
|
|
412
|
-
lit("wrapperModule", v("n"), v("m")),
|
|
413
|
-
lit("calleeOrigin", v("r"), v("m")),
|
|
414
|
-
lit("callArg", v("r"), v("k"), v("a")),
|
|
415
|
-
lit("comesTo", v("a"), v("h"))
|
|
416
|
-
]
|
|
417
|
-
),
|
|
418
533
|
// Where a name comes from, when what it refers to lives outside the
|
|
419
|
-
// source being read.
|
|
420
|
-
// never reaches a library's own function
|
|
421
|
-
// library name has no answer at all. This follows the same aliases
|
|
422
|
-
// and barrels in the other direction and answers with the module and
|
|
423
|
-
// 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.
|
|
424
536
|
rule(
|
|
425
537
|
"comesFrom",
|
|
426
538
|
[v("x"), v("m"), v("n")],
|
|
427
539
|
[lit("imports", v("x"), v("m"), v("n"))]
|
|
428
540
|
),
|
|
429
|
-
rule(
|
|
430
|
-
"comesFrom",
|
|
431
|
-
[v("x"), v("m"), v("n")],
|
|
432
|
-
[lit("binds", v("x"), v("y")), lit("comesFrom", v("y"), v("m"), v("n"))]
|
|
433
|
-
),
|
|
434
541
|
rule(
|
|
435
542
|
"comesFrom",
|
|
436
543
|
[v("x"), v("m"), v("n")],
|
|
437
544
|
[
|
|
438
|
-
lit("
|
|
439
|
-
lit("
|
|
440
|
-
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"))
|
|
441
547
|
]
|
|
442
548
|
),
|
|
443
549
|
// Calling f ends up calling the name n that module m exports. One hop
|
|
@@ -473,36 +579,6 @@ var RESOLUTION_RULES = [
|
|
|
473
579
|
"resolves",
|
|
474
580
|
[v("x"), v("z")],
|
|
475
581
|
[lit("comesTo", v("x"), v("z")), lit("func", v("z"))]
|
|
476
|
-
),
|
|
477
|
-
// Following a name to the expression it is written as. A separate
|
|
478
|
-
// relation rather than a widening of `comesTo`, so a caller asking
|
|
479
|
-
// the older question never sees a new answer: a factory call already
|
|
480
|
-
// has an unwrapping answer, and letting it also answer with itself
|
|
481
|
-
// would make that pair ambiguous.
|
|
482
|
-
//
|
|
483
|
-
// The two seeds are the two ways an expression can be written out. An
|
|
484
|
-
// object literal is one of them, and it is the shape a generated
|
|
485
|
-
// GraphQL document takes.
|
|
486
|
-
rule("isWrittenAs", [v("x"), v("x")], [lit("writtenValue", v("x"))]),
|
|
487
|
-
rule("isWrittenAs", [v("x"), v("x")], [lit("objectValue", v("x"))]),
|
|
488
|
-
rule(
|
|
489
|
-
"isWrittenAs",
|
|
490
|
-
[v("x"), v("z")],
|
|
491
|
-
[lit("binds", v("x"), v("y")), lit("isWrittenAs", v("y"), v("z"))]
|
|
492
|
-
),
|
|
493
|
-
rule(
|
|
494
|
-
"isWrittenAs",
|
|
495
|
-
[v("x"), v("z")],
|
|
496
|
-
[lit("endsHolding", v("x"), v("y")), lit("isWrittenAs", v("y"), v("z"))]
|
|
497
|
-
),
|
|
498
|
-
rule(
|
|
499
|
-
"isWrittenAs",
|
|
500
|
-
[v("x"), v("z")],
|
|
501
|
-
[
|
|
502
|
-
lit("imports", v("x"), v("m"), v("n")),
|
|
503
|
-
lit("moduleExport", v("m"), v("n"), v("value")),
|
|
504
|
-
lit("isWrittenAs", v("value"), v("z"))
|
|
505
|
-
]
|
|
506
582
|
)
|
|
507
583
|
];
|
|
508
584
|
var RESOLUTION_QUESTIONS = [
|
|
@@ -534,6 +610,13 @@ var RESOLUTION_QUESTIONS = [
|
|
|
534
610
|
[v("x"), v("z")],
|
|
535
611
|
[lit("wanted", v("x")), lit("objectOf", v("x"), v("z"))]
|
|
536
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
|
+
),
|
|
537
620
|
rule(
|
|
538
621
|
"wantedComesFrom",
|
|
539
622
|
[v("x"), v("m"), v("n")],
|
|
@@ -557,5 +640,7 @@ export {
|
|
|
557
640
|
FACT_CONTRACT_CASES,
|
|
558
641
|
RESOLUTION_QUESTIONS,
|
|
559
642
|
RESOLUTION_RULES,
|
|
643
|
+
RESULT_STEP,
|
|
644
|
+
VALUE_STEP,
|
|
560
645
|
checkFactContract
|
|
561
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
|
}
|