@suss/adapter-ruby 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.cjs +1251 -575
- package/dist/index.d.cts +192 -17
- package/dist/index.d.ts +192 -17
- package/dist/index.js +1249 -567
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -63,7 +63,52 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
63
63
|
"singleton_class"
|
|
64
64
|
]);
|
|
65
65
|
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
66
|
+
var INCLUDE_CALL = "include";
|
|
67
|
+
var PREPEND_CALL = "prepend";
|
|
66
68
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
69
|
+
var BLOCK_TYPES = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
70
|
+
var NO_BODY_BLOCKS = /* @__PURE__ */ new Map();
|
|
71
|
+
function isModuleBody(body) {
|
|
72
|
+
return body.parent?.type === "module";
|
|
73
|
+
}
|
|
74
|
+
function declaredBlockAt(statement, isModule, blocks) {
|
|
75
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const method = field(statement, "method");
|
|
79
|
+
const kind = method === null ? void 0 : blocks.get(method.text);
|
|
80
|
+
if (kind === void 0 || kind.moduleOnly && !isModule) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return field(statement, "block") === null ? null : kind;
|
|
84
|
+
}
|
|
85
|
+
function bodyStatementsRun(body, isModule, blocks) {
|
|
86
|
+
return bodyStatements(body).flatMap((statement) => {
|
|
87
|
+
if (declaredBlockAt(statement, isModule, blocks) === null) {
|
|
88
|
+
return [statement];
|
|
89
|
+
}
|
|
90
|
+
const block = field(statement, "block");
|
|
91
|
+
const inner = block === null ? null : field(block, "body");
|
|
92
|
+
return inner === null ? [] : bodyStatementsRun(inner, isModule, blocks);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function definedAtClassLevel(node, body, blocks) {
|
|
96
|
+
if (blocks.size === 0) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const isModule = isModuleBody(body);
|
|
100
|
+
let current = node.parent;
|
|
101
|
+
while (current !== null && current.id !== body.id) {
|
|
102
|
+
const parent = current.parent;
|
|
103
|
+
if (BLOCK_TYPES.has(current.type) && parent !== null) {
|
|
104
|
+
if (declaredBlockAt(parent, isModule, blocks)?.definesClassMethods) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
current = parent;
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
67
112
|
function runStatements(body, blockConfigures = () => false) {
|
|
68
113
|
const found = [];
|
|
69
114
|
const readsItsChildren = (node) => {
|
|
@@ -99,10 +144,10 @@ function stringLiteralValue(node) {
|
|
|
99
144
|
function symbolValue(node) {
|
|
100
145
|
return node.type === "simple_symbol" ? node.text.slice(1) : null;
|
|
101
146
|
}
|
|
102
|
-
function instanceMethodsByName(body) {
|
|
147
|
+
function instanceMethodsByName(body, blocks = NO_BODY_BLOCKS) {
|
|
103
148
|
const methods = /* @__PURE__ */ new Map();
|
|
104
149
|
for (const stmt of runStatements(body)) {
|
|
105
|
-
if (stmt.type !== "method") {
|
|
150
|
+
if (stmt.type !== "method" || definedAtClassLevel(stmt, body, blocks)) {
|
|
106
151
|
continue;
|
|
107
152
|
}
|
|
108
153
|
const name = field(stmt, "name")?.text;
|
|
@@ -164,10 +209,11 @@ function instanceMethodVisibility(body) {
|
|
|
164
209
|
}
|
|
165
210
|
return visibility;
|
|
166
211
|
}
|
|
167
|
-
function singletonMethodsByName(body) {
|
|
212
|
+
function singletonMethodsByName(body, blocks = NO_BODY_BLOCKS) {
|
|
168
213
|
const methods = /* @__PURE__ */ new Map();
|
|
169
214
|
for (const stmt of runStatements(body)) {
|
|
170
|
-
|
|
215
|
+
const classLevel = stmt.type === "method" && definedAtClassLevel(stmt, body, blocks);
|
|
216
|
+
if (stmt.type !== "singleton_method" && !classLevel) {
|
|
171
217
|
continue;
|
|
172
218
|
}
|
|
173
219
|
const name = field(stmt, "name")?.text;
|
|
@@ -280,7 +326,8 @@ function autoloadDirectories(root) {
|
|
|
280
326
|
}
|
|
281
327
|
|
|
282
328
|
// src/defineMethod.ts
|
|
283
|
-
import {
|
|
329
|
+
import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
|
|
330
|
+
import { hole as hole2, literalOf as literalOf3, piecesOf } from "@suss/values";
|
|
284
331
|
|
|
285
332
|
// src/values/evaluator.ts
|
|
286
333
|
import { nodeOfKey } from "@suss/resolution";
|
|
@@ -289,40 +336,56 @@ import { Evaluator, force, literalOf, text } from "@suss/values";
|
|
|
289
336
|
// src/facts/resolve.ts
|
|
290
337
|
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
291
338
|
import {
|
|
339
|
+
allocationSitesOf,
|
|
340
|
+
alsoSteps,
|
|
292
341
|
askResolution,
|
|
342
|
+
askResolutionUnder,
|
|
343
|
+
INSTANCE_STEP,
|
|
293
344
|
resolutionProgram,
|
|
345
|
+
resolutionUnderProgram,
|
|
294
346
|
writtenValueOf as sharedWrittenValueOf,
|
|
347
|
+
writtenValueUnder as sharedWrittenValueUnder,
|
|
295
348
|
VALUE_STEP
|
|
296
349
|
} from "@suss/resolution";
|
|
297
|
-
var RUBY_RULES = [
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
350
|
+
var RUBY_RULES = alsoSteps([
|
|
351
|
+
// Making one of a class is a call of the class, which the shared rules
|
|
352
|
+
// already say. Ruby writes the callee as `new` read off the constant, so
|
|
353
|
+
// what this adds is that the callee is the class.
|
|
301
354
|
rule(
|
|
302
|
-
"
|
|
303
|
-
[v("x"), v("cls"),
|
|
355
|
+
"hop",
|
|
356
|
+
[v("x"), v("cls"), INSTANCE_STEP],
|
|
304
357
|
[
|
|
305
358
|
lit("readsProperty", v("x"), v("o"), constant("new")),
|
|
306
359
|
lit("comesTo", v("o"), v("cls")),
|
|
307
360
|
lit("objectValue", v("cls"))
|
|
308
361
|
]
|
|
309
362
|
),
|
|
310
|
-
// The
|
|
311
|
-
//
|
|
312
|
-
//
|
|
363
|
+
// The same spelling again, for the caller asking where a class was
|
|
364
|
+
// made. `callsNamed` reaches a callee written as the name itself, and
|
|
365
|
+
// Ruby writes `new` off the constant instead.
|
|
313
366
|
rule(
|
|
314
|
-
"
|
|
315
|
-
[v("
|
|
367
|
+
"constructsNamed",
|
|
368
|
+
[v("r"), v("cls")],
|
|
316
369
|
[
|
|
317
|
-
lit("
|
|
318
|
-
lit("
|
|
319
|
-
lit("
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
370
|
+
lit("binds", v("o"), v("cls")),
|
|
371
|
+
lit("readsProperty", v("c"), v("o"), constant("new")),
|
|
372
|
+
lit("call", v("r"), v("c"))
|
|
373
|
+
]
|
|
374
|
+
),
|
|
375
|
+
// `%i[a b].freeze` is worth the list it was written as. The evaluator
|
|
376
|
+
// says the same in its row table, for a value it reads in one file.
|
|
377
|
+
...["freeze", "dup"].map(
|
|
378
|
+
(method) => rule(
|
|
379
|
+
"hop",
|
|
380
|
+
[v("r"), v("o"), VALUE_STEP],
|
|
381
|
+
[
|
|
382
|
+
lit("call", v("r"), v("c")),
|
|
383
|
+
lit("readsProperty", v("c"), v("o"), constant(method))
|
|
384
|
+
],
|
|
385
|
+
"hands back the receiver"
|
|
386
|
+
)
|
|
324
387
|
)
|
|
325
|
-
];
|
|
388
|
+
]);
|
|
326
389
|
var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
327
390
|
function resolveValues(db, keys) {
|
|
328
391
|
askResolution(db, keys, "wanted", RUBY_PROGRAM);
|
|
@@ -334,14 +397,122 @@ function writtenValueOf(db, key) {
|
|
|
334
397
|
resolveValues(db, [key]);
|
|
335
398
|
return sharedWrittenValueOf(db, key, (keys) => resolveValues(db, keys));
|
|
336
399
|
}
|
|
400
|
+
function writtenValueUnder(db, key, site) {
|
|
401
|
+
askResolutionUnder(db, [[key, site]], resolutionUnderProgram(RUBY_RULES));
|
|
402
|
+
return sharedWrittenValueUnder(db, key, site);
|
|
403
|
+
}
|
|
404
|
+
function constructionSites(db, classKey) {
|
|
405
|
+
askResolution(db, [classKey], "wantedSites", RUBY_PROGRAM);
|
|
406
|
+
return allocationSitesOf(db, classKey);
|
|
407
|
+
}
|
|
337
408
|
|
|
338
409
|
// src/facts/values.ts
|
|
339
410
|
import { startsAtName, valueLeftByWrites } from "@suss/resolution";
|
|
340
411
|
|
|
412
|
+
// src/paths/bareCalls.ts
|
|
413
|
+
var PARAMETER_LIST_TYPES = [
|
|
414
|
+
"method_parameters",
|
|
415
|
+
"block_parameters",
|
|
416
|
+
"lambda_parameters",
|
|
417
|
+
"parameters"
|
|
418
|
+
];
|
|
419
|
+
function isFieldOf(parent, node, name) {
|
|
420
|
+
return field(parent, name)?.id === node.id;
|
|
421
|
+
}
|
|
422
|
+
function namedAt(parent, node) {
|
|
423
|
+
return isFieldOf(parent, node, "name");
|
|
424
|
+
}
|
|
425
|
+
function always() {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
var SPELLS_A_NAME = {
|
|
429
|
+
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
430
|
+
method: namedAt,
|
|
431
|
+
singleton_method: namedAt,
|
|
432
|
+
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
433
|
+
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
434
|
+
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
435
|
+
optional_parameter: namedAt,
|
|
436
|
+
splat_parameter: namedAt,
|
|
437
|
+
hash_splat_parameter: namedAt,
|
|
438
|
+
keyword_parameter: namedAt,
|
|
439
|
+
block_parameter: namedAt,
|
|
440
|
+
destructured_parameter: always,
|
|
441
|
+
left_assignment_list: always,
|
|
442
|
+
rest_assignment: always,
|
|
443
|
+
destructured_left_assignment: always,
|
|
444
|
+
exception_variable: always,
|
|
445
|
+
undef: always,
|
|
446
|
+
alias: always,
|
|
447
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
448
|
+
};
|
|
449
|
+
function addNamesUnder(node, names) {
|
|
450
|
+
if (node === null) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (node.type === "identifier") {
|
|
454
|
+
names.add(node.text);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
for (const child of bodyStatements(node)) {
|
|
458
|
+
addNamesUnder(child, names);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function parameterNames(node, names) {
|
|
462
|
+
for (const parameter of bodyStatements(node)) {
|
|
463
|
+
if (parameter.type === "identifier") {
|
|
464
|
+
names.add(parameter.text);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const declared = field(parameter, "name");
|
|
468
|
+
if (declared !== null && declared.type === "identifier") {
|
|
469
|
+
names.add(declared.text);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
addNamesUnder(parameter, names);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function assignedNames(node, names) {
|
|
476
|
+
addNamesUnder(field(node, "left"), names);
|
|
477
|
+
}
|
|
478
|
+
var BINDS_A_NAME = {
|
|
479
|
+
assignment: assignedNames,
|
|
480
|
+
operator_assignment: assignedNames,
|
|
481
|
+
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
482
|
+
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
483
|
+
...Object.fromEntries(
|
|
484
|
+
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
485
|
+
)
|
|
486
|
+
};
|
|
487
|
+
function localNamesIn(definitionNode) {
|
|
488
|
+
const names = /* @__PURE__ */ new Set();
|
|
489
|
+
const visit = (node) => {
|
|
490
|
+
BINDS_A_NAME[node.type]?.(node, names);
|
|
491
|
+
for (const child of bodyStatements(node)) {
|
|
492
|
+
visit(child);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
visit(definitionNode);
|
|
496
|
+
return names;
|
|
497
|
+
}
|
|
498
|
+
function spellsAName(node) {
|
|
499
|
+
const parent = node.parent;
|
|
500
|
+
if (parent === null) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
504
|
+
}
|
|
505
|
+
function isBareMethodCall(node, locals) {
|
|
506
|
+
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return !spellsAName(node);
|
|
510
|
+
}
|
|
511
|
+
|
|
341
512
|
// src/facts/locals.ts
|
|
342
513
|
import { writesRunInOrder } from "@suss/resolution";
|
|
343
|
-
var
|
|
344
|
-
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([...OWN_BODY_TYPES, ...
|
|
514
|
+
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
515
|
+
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([...OWN_BODY_TYPES, ...BLOCK_TYPES2]);
|
|
345
516
|
var RUBY_NAME_TYPES = /* @__PURE__ */ new Set([
|
|
346
517
|
"identifier",
|
|
347
518
|
"instance_variable"
|
|
@@ -362,7 +533,7 @@ function parametersOf(owner) {
|
|
|
362
533
|
const params = field(owner, "parameters");
|
|
363
534
|
return params === null ? [] : bodyStatements(params);
|
|
364
535
|
}
|
|
365
|
-
function
|
|
536
|
+
function parameterNames2(owner) {
|
|
366
537
|
const names = /* @__PURE__ */ new Set();
|
|
367
538
|
for (const param of parametersOf(owner)) {
|
|
368
539
|
const name = paramNameOf(param);
|
|
@@ -410,6 +581,13 @@ function readForVariable(node) {
|
|
|
410
581
|
}
|
|
411
582
|
return targetsOf(pattern).map((target) => writeOf(target, null, node, false));
|
|
412
583
|
}
|
|
584
|
+
function readRescueVariable(node) {
|
|
585
|
+
const variable = field(node, "variable");
|
|
586
|
+
if (variable === null) {
|
|
587
|
+
return [];
|
|
588
|
+
}
|
|
589
|
+
return bodyStatements(variable).flatMap(targetsOf).map((target) => writeOf(target, null, node, false));
|
|
590
|
+
}
|
|
413
591
|
function readBlockParameters(node) {
|
|
414
592
|
const found = [];
|
|
415
593
|
for (const param of parametersOf(node)) {
|
|
@@ -424,6 +602,7 @@ var WRITE_READERS = {
|
|
|
424
602
|
assignment: readAssignment,
|
|
425
603
|
operator_assignment: readOperatorAssignment,
|
|
426
604
|
for: readForVariable,
|
|
605
|
+
rescue: readRescueVariable,
|
|
427
606
|
block: readBlockParameters,
|
|
428
607
|
do_block: readBlockParameters
|
|
429
608
|
};
|
|
@@ -469,7 +648,7 @@ function namesOfMethod(method) {
|
|
|
469
648
|
if (remembered !== void 0) {
|
|
470
649
|
return remembered;
|
|
471
650
|
}
|
|
472
|
-
const names = scopeNames(
|
|
651
|
+
const names = scopeNames(parameterNames2(method), field(method, "body"));
|
|
473
652
|
cache.byMethod.set(method, names);
|
|
474
653
|
return names;
|
|
475
654
|
}
|
|
@@ -491,10 +670,17 @@ function ownerOfName(node, name, enclosing) {
|
|
|
491
670
|
}
|
|
492
671
|
return enclosing !== null && names.locals.has(name) ? enclosing : null;
|
|
493
672
|
}
|
|
673
|
+
function isLocalName(node, name, enclosing) {
|
|
674
|
+
const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
|
|
675
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
return names.locals.has(name);
|
|
679
|
+
}
|
|
494
680
|
function blockDeclaring(node, name) {
|
|
495
681
|
let current = node.parent;
|
|
496
682
|
while (current !== null) {
|
|
497
|
-
if (
|
|
683
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames2(current).has(name)) {
|
|
498
684
|
return current;
|
|
499
685
|
}
|
|
500
686
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -550,6 +736,17 @@ function collectWrites(method, body) {
|
|
|
550
736
|
}
|
|
551
737
|
return [...groups.values()];
|
|
552
738
|
}
|
|
739
|
+
function instanceWritesRunInOrder(body, name, targets) {
|
|
740
|
+
return writesRunInOrder(
|
|
741
|
+
body,
|
|
742
|
+
name,
|
|
743
|
+
targets.map((target) => ({
|
|
744
|
+
at: target,
|
|
745
|
+
direct: target.parent?.parent?.id === body.id
|
|
746
|
+
})),
|
|
747
|
+
nameReads(new Set(targets.map((target) => target.id)))
|
|
748
|
+
);
|
|
749
|
+
}
|
|
553
750
|
function orderedWrite(write, scope) {
|
|
554
751
|
return {
|
|
555
752
|
at: write.at,
|
|
@@ -595,12 +792,28 @@ function readKey(filePath, written, enclosing) {
|
|
|
595
792
|
if (node.type !== "identifier" && node.type !== "constant") {
|
|
596
793
|
return nodeId(filePath, node);
|
|
597
794
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
795
|
+
if (isMethodOfReceiver(node) || isBareCall(node, enclosing)) {
|
|
796
|
+
return nodeId(filePath, node);
|
|
601
797
|
}
|
|
602
798
|
return nameKey(filePath, node, enclosing);
|
|
603
799
|
}
|
|
800
|
+
function isMethodOfReceiver(node) {
|
|
801
|
+
const parent = node.parent;
|
|
802
|
+
return parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null;
|
|
803
|
+
}
|
|
804
|
+
function isReceiverOfCall(node) {
|
|
805
|
+
const parent = node.parent;
|
|
806
|
+
return parent !== null && parent.type === "call" && field(parent, "receiver")?.id === node.id;
|
|
807
|
+
}
|
|
808
|
+
function isBareCall(node, enclosing) {
|
|
809
|
+
if (node.type !== "identifier") {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
if (spellsAName(node) && !isReceiverOfCall(node)) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
return !isLocalName(node, node.text, enclosing);
|
|
816
|
+
}
|
|
604
817
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
605
818
|
"string",
|
|
606
819
|
"integer",
|
|
@@ -629,11 +842,7 @@ function add(emitter, relation, ...tuple) {
|
|
|
629
842
|
emitter.db.add(relation, tuple);
|
|
630
843
|
}
|
|
631
844
|
function valueKey(emitter, written) {
|
|
632
|
-
|
|
633
|
-
if (value.type !== "identifier" && value.type !== "constant") {
|
|
634
|
-
return nodeId(emitter.filePath, value);
|
|
635
|
-
}
|
|
636
|
-
return nameKey(emitter.filePath, value, emitter.enclosing);
|
|
845
|
+
return readKey(emitter.filePath, written, emitter.enclosing);
|
|
637
846
|
}
|
|
638
847
|
function pairKeyText(key) {
|
|
639
848
|
if (key.type === "hash_key_symbol") {
|
|
@@ -647,35 +856,29 @@ function pairKeyText(key) {
|
|
|
647
856
|
}
|
|
648
857
|
return null;
|
|
649
858
|
}
|
|
650
|
-
function
|
|
651
|
-
|
|
652
|
-
(
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
function emitPropertyRead(emitter, node) {
|
|
656
|
-
const receiver = field(node, "receiver");
|
|
657
|
-
const method = field(node, "method");
|
|
658
|
-
if (receiver === null || method === null) {
|
|
659
|
-
return;
|
|
859
|
+
function calleeKeyOf(filePath, node, enclosing) {
|
|
860
|
+
if (isBareCall(node, enclosing)) {
|
|
861
|
+
return nameKey(filePath, node, enclosing);
|
|
660
862
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
method.text
|
|
667
|
-
);
|
|
863
|
+
const method = node.type === "call" ? field(node, "method") : null;
|
|
864
|
+
if (method === null) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
668
868
|
}
|
|
669
869
|
function emitCall(emitter, call) {
|
|
670
870
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
671
|
-
const method = field(call, "method");
|
|
672
|
-
|
|
871
|
+
const method = call.type === "identifier" ? call : field(call, "method");
|
|
872
|
+
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
873
|
+
if (method === null || calleeKey === null) {
|
|
673
874
|
return;
|
|
674
875
|
}
|
|
675
876
|
const receiver = field(call, "receiver");
|
|
676
877
|
const callKey = nodeId(emitter.filePath, call);
|
|
677
|
-
const calleeKey = receiver === null ? valueKey(emitter, method) : nodeId(emitter.filePath, method);
|
|
678
878
|
add(emitter, "call", callKey, calleeKey);
|
|
879
|
+
if (!emitter.insideMethod) {
|
|
880
|
+
add(emitter, "callOutsideMethod", callKey);
|
|
881
|
+
}
|
|
679
882
|
if (receiver !== null) {
|
|
680
883
|
add(
|
|
681
884
|
emitter,
|
|
@@ -684,6 +887,8 @@ function emitCall(emitter, call) {
|
|
|
684
887
|
valueKey(emitter, receiver),
|
|
685
888
|
method.text
|
|
686
889
|
);
|
|
890
|
+
} else if (emitter.selfKey !== null) {
|
|
891
|
+
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
687
892
|
}
|
|
688
893
|
const args = field(call, "arguments");
|
|
689
894
|
let position = 0;
|
|
@@ -744,20 +949,71 @@ function emitHash(emitter, hash) {
|
|
|
744
949
|
add(emitter, "holdsProperty", objectKey, keyText, valueKey(emitter, value));
|
|
745
950
|
}
|
|
746
951
|
}
|
|
747
|
-
|
|
952
|
+
var BLOCK_TYPES3 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
953
|
+
var LOOP_METHODS = /* @__PURE__ */ new Set(["each", "each_with_index", "map"]);
|
|
954
|
+
var DEFINE_METHOD_CALL = "define_method";
|
|
955
|
+
function loopTurnAt(emitter, block) {
|
|
956
|
+
const call = block.parent;
|
|
957
|
+
if (call?.type !== "call") {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
const receiver = field(call, "receiver");
|
|
961
|
+
const method = field(call, "method");
|
|
962
|
+
if (receiver === null || method === null || !LOOP_METHODS.has(method.text)) {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
const parameters = field(block, "parameters");
|
|
966
|
+
const names = parameters === null ? [] : children(parameters).map((p) => p.text);
|
|
967
|
+
const element = names[0];
|
|
968
|
+
if (element === void 0) {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
return {
|
|
972
|
+
element,
|
|
973
|
+
index: names[1] ?? "",
|
|
974
|
+
overKey: nodeId(emitter.filePath, receiver)
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
function emitDynamicDefinition(emitter, call, turns) {
|
|
978
|
+
if (emitter.selfKey === null || // A call inside a method runs when that method does, not at load time.
|
|
979
|
+
emitter.enclosing !== null || call.type !== "call" || field(call, "receiver") !== null || field(call, "method")?.text !== DEFINE_METHOD_CALL) {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
const args = field(call, "arguments");
|
|
983
|
+
const first = args === null ? void 0 : children(args)[0];
|
|
984
|
+
const nameKey2 = nodeId(emitter.filePath, first ?? call);
|
|
985
|
+
add(emitter, "definesMethodFrom", emitter.selfKey, nameKey2);
|
|
986
|
+
for (const turn of turns) {
|
|
987
|
+
add(
|
|
988
|
+
emitter,
|
|
989
|
+
"nameTurnsOn",
|
|
990
|
+
nameKey2,
|
|
991
|
+
turn.element,
|
|
992
|
+
turn.index,
|
|
993
|
+
turn.overKey
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function walkExpressions(node, emitter, visit, turns = []) {
|
|
748
998
|
for (const child of children(node)) {
|
|
749
999
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
750
1000
|
continue;
|
|
751
1001
|
}
|
|
752
|
-
visit(child);
|
|
753
|
-
|
|
1002
|
+
visit(child, turns);
|
|
1003
|
+
const opened = BLOCK_TYPES3.has(child.type) ? loopTurnAt(emitter, child) : null;
|
|
1004
|
+
walkExpressions(
|
|
1005
|
+
child,
|
|
1006
|
+
emitter,
|
|
1007
|
+
visit,
|
|
1008
|
+
opened === null ? turns : [...turns, opened]
|
|
1009
|
+
);
|
|
754
1010
|
}
|
|
755
1011
|
}
|
|
756
1012
|
function emitExpressionFacts(emitter, node) {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
1013
|
+
emitDynamicDefinition(emitter, node, []);
|
|
1014
|
+
walkExpressions(node, emitter, (child, turns) => {
|
|
1015
|
+
emitDynamicDefinition(emitter, child, turns);
|
|
1016
|
+
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
761
1017
|
emitCall(emitter, child);
|
|
762
1018
|
}
|
|
763
1019
|
if (ARRAY_TYPES.has(child.type)) {
|
|
@@ -813,28 +1069,52 @@ function collectInstanceWrite(emitter, node) {
|
|
|
813
1069
|
const operator = field(node, "operator")?.text;
|
|
814
1070
|
const value = node.type === "assignment" || WHOLE_VALUE_OPERATORS.has(operator ?? "") ? right : null;
|
|
815
1071
|
const written = collector.get(left.text) ?? [];
|
|
816
|
-
written.push(
|
|
817
|
-
describeWrite(emitter, {
|
|
1072
|
+
written.push({
|
|
1073
|
+
write: describeWrite(emitter, {
|
|
818
1074
|
name: left.text,
|
|
819
1075
|
target: left,
|
|
820
1076
|
value,
|
|
821
1077
|
at: node,
|
|
822
1078
|
fromParameter: false
|
|
823
|
-
})
|
|
824
|
-
|
|
1079
|
+
}),
|
|
1080
|
+
target: left
|
|
1081
|
+
});
|
|
825
1082
|
collector.set(left.text, written);
|
|
826
1083
|
}
|
|
1084
|
+
function settledWrites(writes, ordered) {
|
|
1085
|
+
const described = writes.map((written) => written.write);
|
|
1086
|
+
const settled = valueLeftByWrites(described, ordered);
|
|
1087
|
+
if (settled !== null) {
|
|
1088
|
+
return [settled];
|
|
1089
|
+
}
|
|
1090
|
+
const left = [];
|
|
1091
|
+
for (const write of described) {
|
|
1092
|
+
if (write.value !== null && !write.narrowsName) {
|
|
1093
|
+
left.push(write.value);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return left;
|
|
1097
|
+
}
|
|
827
1098
|
function emitInstanceWrites(emitter, classKey, collected) {
|
|
828
1099
|
for (const [name, writes] of collected) {
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
1100
|
+
for (const value of settledWrites(writes, false)) {
|
|
1101
|
+
add(emitter, "holdsProperty", classKey, name, value);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function emitInstanceStores(emitter, funcKey, body, collected) {
|
|
1106
|
+
for (const [name, writes] of collected) {
|
|
1107
|
+
const deciding = writes.filter((written) => !written.write.narrowsName);
|
|
1108
|
+
if (deciding.length === 0) {
|
|
832
1109
|
continue;
|
|
833
1110
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
1111
|
+
const ordered = instanceWritesRunInOrder(
|
|
1112
|
+
body,
|
|
1113
|
+
name,
|
|
1114
|
+
deciding.map((written) => written.target)
|
|
1115
|
+
);
|
|
1116
|
+
for (const value of settledWrites(deciding, ordered)) {
|
|
1117
|
+
add(emitter, "storesProperty", funcKey, name, value);
|
|
838
1118
|
}
|
|
839
1119
|
}
|
|
840
1120
|
}
|
|
@@ -843,7 +1123,20 @@ function implicitReturn(body) {
|
|
|
843
1123
|
(child) => child.type !== "rescue" && child.type !== "ensure"
|
|
844
1124
|
);
|
|
845
1125
|
const last = statements[statements.length - 1];
|
|
846
|
-
|
|
1126
|
+
if (last === void 0 || last.type === "return") {
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
return assignedValueOf(last) ?? last;
|
|
1130
|
+
}
|
|
1131
|
+
function assignedValueOf(node) {
|
|
1132
|
+
if (!ASSIGNMENT_TYPES.has(node.type)) {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
const operator = field(node, "operator")?.text;
|
|
1136
|
+
if (node.type !== "assignment" && !WHOLE_VALUE_OPERATORS.has(operator ?? "")) {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
return field(node, "right");
|
|
847
1140
|
}
|
|
848
1141
|
function emitMethodFacts(emitter, method) {
|
|
849
1142
|
const funcKey = nodeId(emitter.filePath, method);
|
|
@@ -873,7 +1166,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
873
1166
|
}
|
|
874
1167
|
};
|
|
875
1168
|
recordNested(body);
|
|
876
|
-
walkExpressions(body, (child) => {
|
|
1169
|
+
walkExpressions(body, inside, (child) => {
|
|
877
1170
|
if (child.type === "return") {
|
|
878
1171
|
const first = children(child)[0];
|
|
879
1172
|
const returned = first?.type === "argument_list" ? children(first)[0] : first;
|
|
@@ -881,7 +1174,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
881
1174
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
882
1175
|
}
|
|
883
1176
|
}
|
|
884
|
-
if (child.type === "call") {
|
|
1177
|
+
if (child.type === "call" || isBareCall(child, method)) {
|
|
885
1178
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
886
1179
|
}
|
|
887
1180
|
});
|
|
@@ -949,37 +1242,64 @@ function emitScopeWrites(emitter, method, body) {
|
|
|
949
1242
|
}
|
|
950
1243
|
}
|
|
951
1244
|
function emitCandidates(emitter, key, group) {
|
|
1245
|
+
let unstated = false;
|
|
1246
|
+
let stated = 0;
|
|
952
1247
|
for (const write of group.writes) {
|
|
953
1248
|
if (write.value === null) {
|
|
954
1249
|
add(emitter, "writesUnstated", key);
|
|
1250
|
+
unstated = true;
|
|
955
1251
|
continue;
|
|
956
1252
|
}
|
|
957
1253
|
if (write.fromParameter) {
|
|
1254
|
+
unstated = true;
|
|
958
1255
|
continue;
|
|
959
1256
|
}
|
|
960
1257
|
if (!describeWrite(emitter, write).narrowsName) {
|
|
961
1258
|
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
1259
|
+
stated += 1;
|
|
962
1260
|
}
|
|
963
1261
|
}
|
|
1262
|
+
if (!unstated && stated > 0) {
|
|
1263
|
+
add(emitter, "writesAllStated", key);
|
|
1264
|
+
}
|
|
964
1265
|
}
|
|
965
1266
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1267
|
+
var INITIALIZE_METHOD = "initialize";
|
|
1268
|
+
function receiverKeyOf(filePath, method, classKey) {
|
|
1269
|
+
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
1270
|
+
}
|
|
1271
|
+
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1272
|
+
function mixedInConstants(body, callName) {
|
|
1273
|
+
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
1274
|
+
}
|
|
1275
|
+
function emitMixinFacts(emitter, classKey, body, callName) {
|
|
1276
|
+
for (const mixin of mixedInConstants(body, callName)) {
|
|
1277
|
+
add(emitter, "extends", classKey, valueKey(emitter, mixin));
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
966
1280
|
function emitClassFacts(emitter, cls) {
|
|
967
1281
|
const classKey = nodeId(emitter.filePath, cls);
|
|
968
1282
|
add(emitter, "objectValue", classKey);
|
|
1283
|
+
const body = field(cls, "body");
|
|
1284
|
+
if (body !== null) {
|
|
1285
|
+
emitMixinFacts(emitter, classKey, body, PREPEND_CALL);
|
|
1286
|
+
emitMixinFacts(emitter, classKey, body, INCLUDE_CALL);
|
|
1287
|
+
}
|
|
969
1288
|
const superclass = field(cls, "superclass");
|
|
970
1289
|
const base = superclass === null ? null : children(superclass)[0] ?? null;
|
|
971
1290
|
if (base !== null) {
|
|
972
1291
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
973
1292
|
add(emitter, "extendsNamed", classKey, base.text);
|
|
974
1293
|
}
|
|
975
|
-
const body = field(cls, "body");
|
|
976
1294
|
const collected = /* @__PURE__ */ new Map();
|
|
977
1295
|
const within = {
|
|
978
1296
|
...emitter,
|
|
979
1297
|
selfKey: classKey,
|
|
1298
|
+
insideMethod: false,
|
|
980
1299
|
instanceWrites: collected
|
|
981
1300
|
};
|
|
982
|
-
|
|
1301
|
+
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
1302
|
+
for (const statement of statements) {
|
|
983
1303
|
if (statement.type === "assignment") {
|
|
984
1304
|
const left = field(statement, "left");
|
|
985
1305
|
const right = field(statement, "right");
|
|
@@ -1000,24 +1320,47 @@ function emitClassFacts(emitter, cls) {
|
|
|
1000
1320
|
continue;
|
|
1001
1321
|
}
|
|
1002
1322
|
if (!METHOD_TYPES.has(statement.type)) {
|
|
1323
|
+
emitExpressionFacts(within, statement);
|
|
1003
1324
|
continue;
|
|
1004
1325
|
}
|
|
1005
|
-
const
|
|
1326
|
+
const stored = /* @__PURE__ */ new Map();
|
|
1327
|
+
const receiverKey = receiverKeyOf(emitter.filePath, statement, classKey);
|
|
1328
|
+
if (receiverKey !== classKey) {
|
|
1329
|
+
add(emitter, "instanceOf", receiverKey, classKey);
|
|
1330
|
+
}
|
|
1331
|
+
const funcKey = emitMethodFacts(
|
|
1332
|
+
{
|
|
1333
|
+
...within,
|
|
1334
|
+
selfKey: receiverKey,
|
|
1335
|
+
insideMethod: receiverKey !== classKey,
|
|
1336
|
+
instanceWrites: stored
|
|
1337
|
+
},
|
|
1338
|
+
statement
|
|
1339
|
+
);
|
|
1006
1340
|
const name = field(statement, "name");
|
|
1007
1341
|
if (name !== null) {
|
|
1008
1342
|
add(emitter, "holdsProperty", classKey, name.text, funcKey);
|
|
1343
|
+
if (name.text === INITIALIZE_METHOD) {
|
|
1344
|
+
add(emitter, "initializes", classKey, funcKey);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const methodBody = field(statement, "body");
|
|
1348
|
+
if (methodBody !== null) {
|
|
1349
|
+
emitInstanceStores(within, funcKey, methodBody, stored);
|
|
1009
1350
|
}
|
|
1010
1351
|
}
|
|
1011
1352
|
emitInstanceWrites(within, classKey, collected);
|
|
1012
1353
|
return classKey;
|
|
1013
1354
|
}
|
|
1014
|
-
function emitValueFacts(db, filePath, root) {
|
|
1355
|
+
function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
1015
1356
|
const emitter = {
|
|
1016
1357
|
db,
|
|
1017
1358
|
filePath,
|
|
1018
1359
|
enclosing: null,
|
|
1019
1360
|
selfKey: null,
|
|
1020
|
-
|
|
1361
|
+
insideMethod: false,
|
|
1362
|
+
instanceWrites: null,
|
|
1363
|
+
bodyBlocks
|
|
1021
1364
|
};
|
|
1022
1365
|
const declaresName = (child, key) => {
|
|
1023
1366
|
const name = field(child, "name");
|
|
@@ -1057,7 +1400,7 @@ var OWN_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1057
1400
|
"module",
|
|
1058
1401
|
"singleton_class"
|
|
1059
1402
|
]);
|
|
1060
|
-
var
|
|
1403
|
+
var BLOCK_TYPES4 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1061
1404
|
var METHOD_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1062
1405
|
var NAME_TYPES = /* @__PURE__ */ new Set([
|
|
1063
1406
|
"identifier",
|
|
@@ -1096,7 +1439,7 @@ function rubyLowering(options) {
|
|
|
1096
1439
|
statement: statementOf,
|
|
1097
1440
|
siteOf,
|
|
1098
1441
|
functionOf,
|
|
1099
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1442
|
+
writtenTo: (node, site) => context === null ? null : context.writtenTo(node, site),
|
|
1100
1443
|
callable: (node) => {
|
|
1101
1444
|
if (!isInlinableCall(node)) {
|
|
1102
1445
|
return null;
|
|
@@ -1566,7 +1909,7 @@ function isTailExpression(node) {
|
|
|
1566
1909
|
if (owner.type === "block" && owner.parent?.type === "lambda") {
|
|
1567
1910
|
return true;
|
|
1568
1911
|
}
|
|
1569
|
-
if (METHOD_TYPES2.has(owner.type) ||
|
|
1912
|
+
if (METHOD_TYPES2.has(owner.type) || BLOCK_TYPES4.has(owner.type)) {
|
|
1570
1913
|
return true;
|
|
1571
1914
|
}
|
|
1572
1915
|
if (owner.type === "when") {
|
|
@@ -1585,7 +1928,7 @@ function isRoot(node) {
|
|
|
1585
1928
|
if (OWN_ROOT_TYPES.has(node.type)) {
|
|
1586
1929
|
return true;
|
|
1587
1930
|
}
|
|
1588
|
-
return
|
|
1931
|
+
return BLOCK_TYPES4.has(node.type) && node.parent?.type === "call";
|
|
1589
1932
|
}
|
|
1590
1933
|
function isPathStatement(node, parent) {
|
|
1591
1934
|
if (node.type === "elsif") {
|
|
@@ -1613,17 +1956,32 @@ function siteOf(node) {
|
|
|
1613
1956
|
current = parent;
|
|
1614
1957
|
}
|
|
1615
1958
|
}
|
|
1959
|
+
var BARE_SPLAT_TYPES = /* @__PURE__ */ new Set(["splat_parameter", "hash_splat_parameter"]);
|
|
1616
1960
|
function parametersOf2(parameters) {
|
|
1617
1961
|
if (parameters === null) {
|
|
1618
1962
|
return [];
|
|
1619
1963
|
}
|
|
1620
|
-
|
|
1964
|
+
const declared = [];
|
|
1965
|
+
let position = 0;
|
|
1966
|
+
for (const parameter of named(parameters)) {
|
|
1967
|
+
if (BARE_SPLAT_TYPES.has(parameter.type) && field(parameter, "name") === null) {
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1621
1970
|
if (parameter.type === "identifier") {
|
|
1622
|
-
|
|
1971
|
+
declared.push({ name: parameter.text, default: null, position });
|
|
1972
|
+
} else {
|
|
1973
|
+
const name = field(parameter, "name");
|
|
1974
|
+
if (name !== null) {
|
|
1975
|
+
declared.push({
|
|
1976
|
+
name: name.text,
|
|
1977
|
+
default: field(parameter, "value"),
|
|
1978
|
+
position
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1623
1981
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1982
|
+
position += 1;
|
|
1983
|
+
}
|
|
1984
|
+
return declared;
|
|
1627
1985
|
}
|
|
1628
1986
|
function functionOf(node) {
|
|
1629
1987
|
if (node.type === "program") {
|
|
@@ -1639,7 +1997,7 @@ function functionOf(node) {
|
|
|
1639
1997
|
body: bodyOf(block)
|
|
1640
1998
|
};
|
|
1641
1999
|
}
|
|
1642
|
-
if (
|
|
2000
|
+
if (BLOCK_TYPES4.has(node.type)) {
|
|
1643
2001
|
return {
|
|
1644
2002
|
parameters: parametersOf2(field(node, "parameters")),
|
|
1645
2003
|
body: bodyOf(node)
|
|
@@ -2031,25 +2389,56 @@ var rubyRows = [
|
|
|
2031
2389
|
|
|
2032
2390
|
// src/values/evaluator.ts
|
|
2033
2391
|
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2392
|
+
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2034
2393
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2394
|
+
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2035
2395
|
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2036
2396
|
function bindEvaluator(db, nodes) {
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
);
|
|
2397
|
+
for (const entry of nodes.files) {
|
|
2398
|
+
filesByTree.set(entry.root.tree, entry.file);
|
|
2399
|
+
}
|
|
2400
|
+
const context = contextOver(db, nodes);
|
|
2401
|
+
contexts.set(db, context);
|
|
2402
|
+
evaluators.set(db, new Evaluator(rubyLowering({ context, rows: rubyRows })));
|
|
2403
|
+
}
|
|
2404
|
+
function writtenNodeOf(node, db, site) {
|
|
2405
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2406
|
+
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2043
2407
|
}
|
|
2044
|
-
function evaluatedValue(node, db, bindings) {
|
|
2408
|
+
function evaluatedValue(node, db, bindings, site) {
|
|
2045
2409
|
const evaluator = evaluatorFor(node, db);
|
|
2046
2410
|
if (bindings === void 0) {
|
|
2047
|
-
return force(
|
|
2411
|
+
return force(
|
|
2412
|
+
site === void 0 ? evaluator.evaluate(node) : evaluator.evaluate(node, { site })
|
|
2413
|
+
);
|
|
2048
2414
|
}
|
|
2049
2415
|
const supplied = new Map(
|
|
2050
2416
|
[...bindings].map(([name, value]) => [name, text(value)])
|
|
2051
2417
|
);
|
|
2052
|
-
return force(
|
|
2418
|
+
return force(
|
|
2419
|
+
evaluator.evaluate(node, {
|
|
2420
|
+
bindings: supplied,
|
|
2421
|
+
...site === void 0 ? {} : { site }
|
|
2422
|
+
})
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
function constructionSitesOf(node, db) {
|
|
2426
|
+
const cls = enclosingClass(node);
|
|
2427
|
+
const file = cls === null ? void 0 : filesByTree.get(cls.tree);
|
|
2428
|
+
if (db === void 0 || cls === null || file === void 0) {
|
|
2429
|
+
return [];
|
|
2430
|
+
}
|
|
2431
|
+
return constructionSites(db, nodeId(file, cls));
|
|
2432
|
+
}
|
|
2433
|
+
function enclosingClass(node) {
|
|
2434
|
+
let current = node.parent;
|
|
2435
|
+
while (current !== null) {
|
|
2436
|
+
if (current.type === "class") {
|
|
2437
|
+
return current;
|
|
2438
|
+
}
|
|
2439
|
+
current = current.parent;
|
|
2440
|
+
}
|
|
2441
|
+
return null;
|
|
2053
2442
|
}
|
|
2054
2443
|
function stringValueOf(node, db, bindings) {
|
|
2055
2444
|
return literalOf(evaluatedValue(node, db, bindings));
|
|
@@ -2101,15 +2490,13 @@ function contextOver(db, nodes) {
|
|
|
2101
2490
|
}
|
|
2102
2491
|
const fileOf = (node) => filesByRoot.get(node.tree.rootNode.id)?.file ?? null;
|
|
2103
2492
|
return {
|
|
2104
|
-
writtenTo: (node) => {
|
|
2493
|
+
writtenTo: (node, site) => {
|
|
2105
2494
|
const file = fileOf(node);
|
|
2106
2495
|
if (file === null) {
|
|
2107
2496
|
return null;
|
|
2108
2497
|
}
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2111
|
-
readKey(file, node, enclosingMethod(node))
|
|
2112
|
-
);
|
|
2498
|
+
const key = readKey(file, node, enclosingMethod(node));
|
|
2499
|
+
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2113
2500
|
return answer === null ? null : nodeOfKey(rootsByFile, answer);
|
|
2114
2501
|
},
|
|
2115
2502
|
callable: (call) => {
|
|
@@ -2127,125 +2514,141 @@ function contextOver(db, nodes) {
|
|
|
2127
2514
|
};
|
|
2128
2515
|
}
|
|
2129
2516
|
|
|
2130
|
-
// src/
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
var byTree = /* @__PURE__ */ new WeakMap();
|
|
2136
|
-
function defineMethodNames(body) {
|
|
2137
|
-
let perTree = byTree.get(body.tree);
|
|
2138
|
-
if (perTree === void 0) {
|
|
2139
|
-
perTree = /* @__PURE__ */ new Map();
|
|
2140
|
-
byTree.set(body.tree, perTree);
|
|
2141
|
-
}
|
|
2142
|
-
const cached = perTree.get(body.id);
|
|
2143
|
-
if (cached !== void 0) {
|
|
2144
|
-
return cached;
|
|
2145
|
-
}
|
|
2146
|
-
const read = readNames(body);
|
|
2147
|
-
perTree.set(body.id, read);
|
|
2148
|
-
return read;
|
|
2149
|
-
}
|
|
2150
|
-
function readNames(body) {
|
|
2151
|
-
const calls = bareCalls(body, DEFINE_METHOD_CALL);
|
|
2152
|
-
if (calls.length === 0) {
|
|
2153
|
-
return NOTHING;
|
|
2517
|
+
// src/values/literals.ts
|
|
2518
|
+
import { force as force2, literalOf as literalOf2 } from "@suss/values";
|
|
2519
|
+
function literalElementsIn(value) {
|
|
2520
|
+
if (value.kind !== "sequence") {
|
|
2521
|
+
return null;
|
|
2154
2522
|
}
|
|
2155
|
-
const
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
unreadable = true;
|
|
2161
|
-
continue;
|
|
2523
|
+
const elements = [];
|
|
2524
|
+
for (const item of value.items) {
|
|
2525
|
+
const literal = literalOf2(force2(item.value));
|
|
2526
|
+
if (literal === null) {
|
|
2527
|
+
return null;
|
|
2162
2528
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2529
|
+
elements.push(literal);
|
|
2530
|
+
}
|
|
2531
|
+
return elements;
|
|
2532
|
+
}
|
|
2533
|
+
function literalElementsOf(node, db) {
|
|
2534
|
+
return literalElementsIn(evaluatedValue(node, db));
|
|
2535
|
+
}
|
|
2536
|
+
function namesOf(node, db) {
|
|
2537
|
+
const value = evaluatedValue(node, db);
|
|
2538
|
+
const single = literalOf2(value);
|
|
2539
|
+
return single === null ? literalElementsIn(value) : [single];
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
// src/defineMethod.ts
|
|
2543
|
+
var NOTHING = {
|
|
2544
|
+
names: /* @__PURE__ */ new Set(),
|
|
2545
|
+
patterns: [],
|
|
2546
|
+
unreadable: false
|
|
2547
|
+
};
|
|
2548
|
+
function couldBeDefined(defined, name) {
|
|
2549
|
+
return defined.unreadable || defined.patterns.some((pattern) => pattern.test(name));
|
|
2550
|
+
}
|
|
2551
|
+
function definedNamesOf(dynamic, classKey) {
|
|
2552
|
+
return dynamic?.get(classKey) ?? NOTHING;
|
|
2553
|
+
}
|
|
2554
|
+
function readDynamicNames(db, rootsByFile) {
|
|
2555
|
+
const byClass = /* @__PURE__ */ new Map();
|
|
2556
|
+
for (const [classKey, nameKey2] of db.facts("definesMethodFrom")) {
|
|
2557
|
+
const key = String(classKey);
|
|
2558
|
+
byClass.set(
|
|
2559
|
+
key,
|
|
2560
|
+
foldReading(
|
|
2561
|
+
byClass.get(key) ?? NOTHING,
|
|
2562
|
+
readOne(db, rootsByFile, String(nameKey2))
|
|
2563
|
+
)
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
for (const [classKey, reading] of byClass) {
|
|
2567
|
+
for (const name of reading.names) {
|
|
2568
|
+
db.add("declaresName", [classKey, name]);
|
|
2165
2569
|
}
|
|
2166
2570
|
}
|
|
2167
|
-
return
|
|
2571
|
+
return byClass;
|
|
2168
2572
|
}
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
if (first === void 0) {
|
|
2173
|
-
return null;
|
|
2573
|
+
function foldReading(soFar, reading) {
|
|
2574
|
+
if (reading === null) {
|
|
2575
|
+
return { ...soFar, unreadable: true };
|
|
2174
2576
|
}
|
|
2577
|
+
return {
|
|
2578
|
+
names: /* @__PURE__ */ new Set([...soFar.names, ...reading.names]),
|
|
2579
|
+
patterns: [...soFar.patterns, ...reading.patterns],
|
|
2580
|
+
unreadable: soFar.unreadable
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
function readOne(db, rootsByFile, nameKey2) {
|
|
2584
|
+
const node = nodeOfKey2(rootsByFile, nameKey2);
|
|
2175
2585
|
const names = [];
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2586
|
+
const patterns = [];
|
|
2587
|
+
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2588
|
+
const value = node === null ? hole2(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2589
|
+
const name = literalOf3(value);
|
|
2590
|
+
if (name !== null) {
|
|
2591
|
+
names.push(name);
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
2594
|
+
const pattern = patternOf(value);
|
|
2595
|
+
if (pattern === null) {
|
|
2183
2596
|
return null;
|
|
2184
2597
|
}
|
|
2185
|
-
|
|
2598
|
+
patterns.push(pattern);
|
|
2186
2599
|
}
|
|
2187
|
-
return names;
|
|
2600
|
+
return { names, patterns };
|
|
2188
2601
|
}
|
|
2189
|
-
function
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2602
|
+
function patternOf(value) {
|
|
2603
|
+
const pieces = piecesOf(value);
|
|
2604
|
+
let source = "^";
|
|
2605
|
+
let readSomething = false;
|
|
2606
|
+
for (const piece of pieces) {
|
|
2607
|
+
if (piece.kind === "hole") {
|
|
2608
|
+
source += "[\\s\\S]*";
|
|
2194
2609
|
continue;
|
|
2195
2610
|
}
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
}
|
|
2200
|
-
return turns;
|
|
2201
|
-
}
|
|
2202
|
-
function blocksAround(call) {
|
|
2203
|
-
const blocks = [];
|
|
2204
|
-
let current = call.parent;
|
|
2205
|
-
while (current !== null && !OWN_BODY_TYPES.has(current.type)) {
|
|
2206
|
-
const parent = current.parent;
|
|
2207
|
-
if (BLOCK_TYPES3.has(current.type) && parent?.type === "call") {
|
|
2208
|
-
blocks.unshift({ block: current, call: parent });
|
|
2611
|
+
const options = piece.options.filter((option) => option.length > 0);
|
|
2612
|
+
if (options.length === 0) {
|
|
2613
|
+
continue;
|
|
2209
2614
|
}
|
|
2210
|
-
|
|
2615
|
+
readSomething = true;
|
|
2616
|
+
source += `(?:${options.map(escapeForPattern).join("|")})`;
|
|
2211
2617
|
}
|
|
2212
|
-
return
|
|
2618
|
+
return readSomething ? new RegExp(`${source}$`) : null;
|
|
2213
2619
|
}
|
|
2214
|
-
function
|
|
2215
|
-
|
|
2216
|
-
const receiver = field(call, "receiver");
|
|
2217
|
-
const method = field(call, "method")?.text;
|
|
2218
|
-
if (receiver === null || method === void 0 || !LOOP_METHODS.has(method)) {
|
|
2219
|
-
return null;
|
|
2220
|
-
}
|
|
2221
|
-
const parameters = field(block, "parameters");
|
|
2222
|
-
const [element, index] = parameters === null ? [] : bodyStatements(parameters).map((parameter) => parameter.text);
|
|
2223
|
-
const elements = element === void 0 ? null : literalElementsOf(receiver);
|
|
2224
|
-
if (element === void 0 || elements === null) {
|
|
2225
|
-
return null;
|
|
2226
|
-
}
|
|
2227
|
-
return elements.map((value, position) => {
|
|
2228
|
-
const binding = /* @__PURE__ */ new Map([[element, value]]);
|
|
2229
|
-
if (index !== void 0) {
|
|
2230
|
-
binding.set(index, String(position));
|
|
2231
|
-
}
|
|
2232
|
-
return binding;
|
|
2233
|
-
});
|
|
2620
|
+
function escapeForPattern(text2) {
|
|
2621
|
+
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2234
2622
|
}
|
|
2235
|
-
function
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2623
|
+
function loopTurns(db, rootsByFile, nameKey2) {
|
|
2624
|
+
let turns = [/* @__PURE__ */ new Map()];
|
|
2625
|
+
for (const over of listsBehind(db, rootsByFile, nameKey2)) {
|
|
2626
|
+
turns = turns.flatMap(
|
|
2627
|
+
(base) => over.elements.map((value, position) => {
|
|
2628
|
+
const binding = new Map([...base, [over.element, value]]);
|
|
2629
|
+
if (over.index !== "") {
|
|
2630
|
+
binding.set(over.index, String(position));
|
|
2631
|
+
}
|
|
2632
|
+
return binding;
|
|
2633
|
+
})
|
|
2634
|
+
);
|
|
2239
2635
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2636
|
+
return turns;
|
|
2637
|
+
}
|
|
2638
|
+
function listsBehind(db, rootsByFile, nameKey2) {
|
|
2639
|
+
const found = [];
|
|
2640
|
+
for (const [, element, index, overKey] of db.lookup(
|
|
2641
|
+
"nameTurnsOn",
|
|
2642
|
+
0,
|
|
2643
|
+
nameKey2
|
|
2644
|
+
)) {
|
|
2645
|
+
const node = nodeOfKey2(rootsByFile, String(overKey));
|
|
2646
|
+
const elements = node === null ? null : literalElementsOf(node, db);
|
|
2647
|
+
if (elements !== null) {
|
|
2648
|
+
found.push({ element: String(element), index: String(index), elements });
|
|
2245
2649
|
}
|
|
2246
|
-
elements.push(literal);
|
|
2247
2650
|
}
|
|
2248
|
-
return
|
|
2651
|
+
return found;
|
|
2249
2652
|
}
|
|
2250
2653
|
|
|
2251
2654
|
// src/scope.ts
|
|
@@ -2374,8 +2777,6 @@ function graphqlTypeNameFromQualified(qualifiedName, convention) {
|
|
|
2374
2777
|
}
|
|
2375
2778
|
|
|
2376
2779
|
// src/ancestry.ts
|
|
2377
|
-
var INCLUDE_CALL = "include";
|
|
2378
|
-
var PREPEND_CALL = "prepend";
|
|
2379
2780
|
async function reachDefinition(qualifiedName, lookup) {
|
|
2380
2781
|
const filePath = resolveConstantFile(
|
|
2381
2782
|
lookup.root,
|
|
@@ -2502,7 +2903,7 @@ function superclassCandidatesOf(blocks) {
|
|
|
2502
2903
|
}
|
|
2503
2904
|
return [];
|
|
2504
2905
|
}
|
|
2505
|
-
function methodInAncestry(ancestry, name) {
|
|
2906
|
+
function methodInAncestry(ancestry, name, read = {}) {
|
|
2506
2907
|
for (const entry of ancestry) {
|
|
2507
2908
|
if (entry.type === "root") {
|
|
2508
2909
|
return { type: "none" };
|
|
@@ -2514,7 +2915,7 @@ function methodInAncestry(ancestry, name) {
|
|
|
2514
2915
|
cause: "unreadAncestor"
|
|
2515
2916
|
};
|
|
2516
2917
|
}
|
|
2517
|
-
const found = definitionIn(entry.blocks, name);
|
|
2918
|
+
const found = definitionIn(entry.blocks, name, read);
|
|
2518
2919
|
if (found.method !== null && found.block !== null) {
|
|
2519
2920
|
return { type: "found", method: found.method, block: found.block };
|
|
2520
2921
|
}
|
|
@@ -2528,7 +2929,7 @@ function methodInAncestry(ancestry, name) {
|
|
|
2528
2929
|
}
|
|
2529
2930
|
return { type: "none" };
|
|
2530
2931
|
}
|
|
2531
|
-
function definitionIn(blocks, name) {
|
|
2932
|
+
function definitionIn(blocks, name, read) {
|
|
2532
2933
|
let method = null;
|
|
2533
2934
|
let block = null;
|
|
2534
2935
|
let definedDynamically = false;
|
|
@@ -2538,14 +2939,17 @@ function definitionIn(blocks, name) {
|
|
|
2538
2939
|
if (body === null) {
|
|
2539
2940
|
continue;
|
|
2540
2941
|
}
|
|
2541
|
-
const found = instanceMethodsByName(body).get(name);
|
|
2942
|
+
const found = instanceMethodsByName(body, read.bodyBlocks).get(name);
|
|
2542
2943
|
if (found !== void 0) {
|
|
2543
2944
|
method = found;
|
|
2544
2945
|
block = candidate;
|
|
2545
2946
|
}
|
|
2546
|
-
const defined =
|
|
2947
|
+
const defined = definedNamesOf(
|
|
2948
|
+
read.dynamicNames,
|
|
2949
|
+
nodeId(candidate.file, candidate.info.node)
|
|
2950
|
+
);
|
|
2547
2951
|
definedDynamically ||= defined.names.has(name);
|
|
2548
|
-
unreadableDefine ||= defined
|
|
2952
|
+
unreadableDefine ||= couldBeDefined(defined, name);
|
|
2549
2953
|
}
|
|
2550
2954
|
return { method, block, definedDynamically, unreadableDefine };
|
|
2551
2955
|
}
|
|
@@ -2577,108 +2981,12 @@ import {
|
|
|
2577
2981
|
import { absentReading as absentReading2, unreadableReading as unreadableReading2 } from "@suss/extractor";
|
|
2578
2982
|
|
|
2579
2983
|
// src/clientCalls.ts
|
|
2580
|
-
import { namesNothing, restBinding } from "@suss/behavioral-ir";
|
|
2984
|
+
import { hasNameHole, namesNothing, restBinding } from "@suss/behavioral-ir";
|
|
2581
2985
|
import { pathOf } from "@suss/values";
|
|
2582
2986
|
|
|
2583
2987
|
// src/paths/effects.ts
|
|
2584
2988
|
import { enumerateOrDegrade, sharedGatingConditions } from "@suss/extractor";
|
|
2585
|
-
|
|
2586
|
-
// src/paths/bareCalls.ts
|
|
2587
|
-
var PARAMETER_LIST_TYPES = [
|
|
2588
|
-
"method_parameters",
|
|
2589
|
-
"block_parameters",
|
|
2590
|
-
"lambda_parameters",
|
|
2591
|
-
"parameters"
|
|
2592
|
-
];
|
|
2593
|
-
function isFieldOf(parent, node, name) {
|
|
2594
|
-
return field(parent, name)?.id === node.id;
|
|
2595
|
-
}
|
|
2596
|
-
function namedAt(parent, node) {
|
|
2597
|
-
return isFieldOf(parent, node, "name");
|
|
2598
|
-
}
|
|
2599
|
-
function always() {
|
|
2600
|
-
return true;
|
|
2601
|
-
}
|
|
2602
|
-
var SPELLS_A_NAME = {
|
|
2603
|
-
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
2604
|
-
method: namedAt,
|
|
2605
|
-
singleton_method: namedAt,
|
|
2606
|
-
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2607
|
-
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2608
|
-
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
2609
|
-
optional_parameter: namedAt,
|
|
2610
|
-
splat_parameter: namedAt,
|
|
2611
|
-
hash_splat_parameter: namedAt,
|
|
2612
|
-
keyword_parameter: namedAt,
|
|
2613
|
-
block_parameter: namedAt,
|
|
2614
|
-
destructured_parameter: always,
|
|
2615
|
-
left_assignment_list: always,
|
|
2616
|
-
rest_assignment: always,
|
|
2617
|
-
destructured_left_assignment: always,
|
|
2618
|
-
exception_variable: always,
|
|
2619
|
-
undef: always,
|
|
2620
|
-
alias: always,
|
|
2621
|
-
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
2622
|
-
};
|
|
2623
|
-
function addNamesUnder(node, names) {
|
|
2624
|
-
if (node === null) {
|
|
2625
|
-
return;
|
|
2626
|
-
}
|
|
2627
|
-
if (node.type === "identifier") {
|
|
2628
|
-
names.add(node.text);
|
|
2629
|
-
return;
|
|
2630
|
-
}
|
|
2631
|
-
for (const child of bodyStatements(node)) {
|
|
2632
|
-
addNamesUnder(child, names);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
function parameterNames2(node, names) {
|
|
2636
|
-
for (const parameter of bodyStatements(node)) {
|
|
2637
|
-
if (parameter.type === "identifier") {
|
|
2638
|
-
names.add(parameter.text);
|
|
2639
|
-
continue;
|
|
2640
|
-
}
|
|
2641
|
-
const declared = field(parameter, "name");
|
|
2642
|
-
if (declared !== null && declared.type === "identifier") {
|
|
2643
|
-
names.add(declared.text);
|
|
2644
|
-
continue;
|
|
2645
|
-
}
|
|
2646
|
-
addNamesUnder(parameter, names);
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
function assignedNames(node, names) {
|
|
2650
|
-
addNamesUnder(field(node, "left"), names);
|
|
2651
|
-
}
|
|
2652
|
-
var BINDS_A_NAME = {
|
|
2653
|
-
assignment: assignedNames,
|
|
2654
|
-
operator_assignment: assignedNames,
|
|
2655
|
-
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
2656
|
-
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
2657
|
-
...Object.fromEntries(
|
|
2658
|
-
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames2])
|
|
2659
|
-
)
|
|
2660
|
-
};
|
|
2661
|
-
function localNamesIn(definitionNode) {
|
|
2662
|
-
const names = /* @__PURE__ */ new Set();
|
|
2663
|
-
const visit = (node) => {
|
|
2664
|
-
BINDS_A_NAME[node.type]?.(node, names);
|
|
2665
|
-
for (const child of bodyStatements(node)) {
|
|
2666
|
-
visit(child);
|
|
2667
|
-
}
|
|
2668
|
-
};
|
|
2669
|
-
visit(definitionNode);
|
|
2670
|
-
return names;
|
|
2671
|
-
}
|
|
2672
|
-
function isBareMethodCall(node, locals) {
|
|
2673
|
-
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
2674
|
-
return false;
|
|
2675
|
-
}
|
|
2676
|
-
const parent = node.parent;
|
|
2677
|
-
if (parent === null) {
|
|
2678
|
-
return false;
|
|
2679
|
-
}
|
|
2680
|
-
return SPELLS_A_NAME[parent.type]?.(parent, node) !== true;
|
|
2681
|
-
}
|
|
2989
|
+
import { constantOf, literalOf as literalOf4 } from "@suss/values";
|
|
2682
2990
|
|
|
2683
2991
|
// src/paths/lowering.ts
|
|
2684
2992
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -2959,30 +3267,28 @@ function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
|
2959
3267
|
|
|
2960
3268
|
// src/paths/effects.ts
|
|
2961
3269
|
var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
2962
|
-
var
|
|
3270
|
+
var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
2963
3271
|
"body_statement",
|
|
2964
3272
|
"then",
|
|
2965
|
-
"else",
|
|
2966
|
-
"do_block",
|
|
2967
|
-
"block"
|
|
2968
|
-
]);
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
return
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
2985
|
-
};
|
|
3273
|
+
"else",
|
|
3274
|
+
"do_block",
|
|
3275
|
+
"block"
|
|
3276
|
+
]);
|
|
3277
|
+
function literalArgOf(node, facts) {
|
|
3278
|
+
const value = evaluatedValue(node, facts);
|
|
3279
|
+
const text2 = literalOf4(value);
|
|
3280
|
+
if (text2 !== null) {
|
|
3281
|
+
return { kind: "string", value: text2 };
|
|
3282
|
+
}
|
|
3283
|
+
const constant2 = constantOf(value);
|
|
3284
|
+
if (typeof constant2 === "number") {
|
|
3285
|
+
return { kind: "number", value: constant2 };
|
|
3286
|
+
}
|
|
3287
|
+
if (typeof constant2 === "boolean") {
|
|
3288
|
+
return { kind: "boolean", value: constant2 };
|
|
3289
|
+
}
|
|
3290
|
+
return null;
|
|
3291
|
+
}
|
|
2986
3292
|
function children2(node) {
|
|
2987
3293
|
return node.namedChildren.filter((child) => child !== null);
|
|
2988
3294
|
}
|
|
@@ -3066,36 +3372,40 @@ function calleeText(call) {
|
|
|
3066
3372
|
}
|
|
3067
3373
|
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
3068
3374
|
}
|
|
3069
|
-
function argOf(node) {
|
|
3070
|
-
const literal =
|
|
3071
|
-
if (literal !== null
|
|
3375
|
+
function argOf(node, facts) {
|
|
3376
|
+
const literal = literalArgOf(node, facts);
|
|
3377
|
+
if (literal !== null) {
|
|
3072
3378
|
return literal;
|
|
3073
3379
|
}
|
|
3074
3380
|
if (node.type === "call" && !isArglessReceiverCall(node)) {
|
|
3075
|
-
return {
|
|
3381
|
+
return {
|
|
3382
|
+
kind: "call",
|
|
3383
|
+
callee: calleeText(node),
|
|
3384
|
+
args: argsOf(node, facts)
|
|
3385
|
+
};
|
|
3076
3386
|
}
|
|
3077
3387
|
return { kind: "identifier", name: node.text };
|
|
3078
3388
|
}
|
|
3079
|
-
function argsOf(call) {
|
|
3389
|
+
function argsOf(call, facts) {
|
|
3080
3390
|
const args = field(call, "arguments");
|
|
3081
3391
|
if (args === null) {
|
|
3082
3392
|
return [];
|
|
3083
3393
|
}
|
|
3084
3394
|
return children2(args).map(
|
|
3085
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3395
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child, facts) : argOf(child, facts)
|
|
3086
3396
|
);
|
|
3087
3397
|
}
|
|
3088
3398
|
function enclosingStatement(call, body) {
|
|
3089
3399
|
let current = call;
|
|
3090
3400
|
while (current.parent !== null) {
|
|
3091
|
-
if (
|
|
3401
|
+
if (BLOCK_TYPES5.has(current.parent.type) || current.parent.id === body.id) {
|
|
3092
3402
|
return current;
|
|
3093
3403
|
}
|
|
3094
3404
|
current = current.parent;
|
|
3095
3405
|
}
|
|
3096
3406
|
return current;
|
|
3097
3407
|
}
|
|
3098
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS) {
|
|
3408
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3099
3409
|
const body = field(definitionNode, "body");
|
|
3100
3410
|
if (body === null) {
|
|
3101
3411
|
return [];
|
|
@@ -3135,7 +3445,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3135
3445
|
return {
|
|
3136
3446
|
type: "invocation",
|
|
3137
3447
|
callee: calleeText(call),
|
|
3138
|
-
args: argsOf(call),
|
|
3448
|
+
args: argsOf(call, facts),
|
|
3139
3449
|
async: false,
|
|
3140
3450
|
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3141
3451
|
};
|
|
@@ -3150,7 +3460,7 @@ import {
|
|
|
3150
3460
|
unreadableReading,
|
|
3151
3461
|
writtenReading
|
|
3152
3462
|
} from "@suss/extractor";
|
|
3153
|
-
import { constantOf, literalOf as
|
|
3463
|
+
import { constantOf as constantOf2, literalOf as literalOf6 } from "@suss/values";
|
|
3154
3464
|
|
|
3155
3465
|
// src/paths/predicates.ts
|
|
3156
3466
|
var OPERATORS = {
|
|
@@ -3219,7 +3529,7 @@ function binaryOf(node) {
|
|
|
3219
3529
|
return opaqueOf(node);
|
|
3220
3530
|
}
|
|
3221
3531
|
function valueRefOf(node) {
|
|
3222
|
-
const literal =
|
|
3532
|
+
const literal = literalOf5(node);
|
|
3223
3533
|
if (literal !== null) {
|
|
3224
3534
|
return literal;
|
|
3225
3535
|
}
|
|
@@ -3232,7 +3542,7 @@ function valueRefOf(node) {
|
|
|
3232
3542
|
}
|
|
3233
3543
|
return { type: "unresolved", sourceText: node.text };
|
|
3234
3544
|
}
|
|
3235
|
-
function
|
|
3545
|
+
function literalOf5(node) {
|
|
3236
3546
|
if (node.type === "integer") {
|
|
3237
3547
|
const value = Number.parseInt(node.text, 10);
|
|
3238
3548
|
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
@@ -3275,13 +3585,13 @@ function statusArgumentOf(call, declaration) {
|
|
|
3275
3585
|
}
|
|
3276
3586
|
return args.positional[declaration.statusArgument] ?? null;
|
|
3277
3587
|
}
|
|
3278
|
-
function statusNumberOf(node, names) {
|
|
3279
|
-
const value = evaluatedValue(node);
|
|
3280
|
-
const constant2 =
|
|
3588
|
+
function statusNumberOf(node, names, facts) {
|
|
3589
|
+
const value = evaluatedValue(node, facts);
|
|
3590
|
+
const constant2 = constantOf2(value);
|
|
3281
3591
|
if (typeof constant2 === "number") {
|
|
3282
3592
|
return constant2;
|
|
3283
3593
|
}
|
|
3284
|
-
const name =
|
|
3594
|
+
const name = literalOf6(value);
|
|
3285
3595
|
return name === null ? null : names[name] ?? null;
|
|
3286
3596
|
}
|
|
3287
3597
|
function declarationsByName(declarations) {
|
|
@@ -3325,12 +3635,12 @@ function collectReturns(node, found) {
|
|
|
3325
3635
|
}
|
|
3326
3636
|
return found;
|
|
3327
3637
|
}
|
|
3328
|
-
function readingOfCall(call, declaration, names) {
|
|
3638
|
+
function readingOfCall(call, declaration, names, facts) {
|
|
3329
3639
|
const argument = statusArgumentOf(call, declaration);
|
|
3330
3640
|
if (argument === null) {
|
|
3331
3641
|
return declaration.defaultStatusCode === void 0 ? absentReading : writtenReading(declaration.defaultStatusCode, rangeOf(call));
|
|
3332
3642
|
}
|
|
3333
|
-
const status = statusNumberOf(argument, names);
|
|
3643
|
+
const status = statusNumberOf(argument, names, facts);
|
|
3334
3644
|
if (status === null) {
|
|
3335
3645
|
return unreadableReading(
|
|
3336
3646
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
@@ -3474,7 +3784,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
3474
3784
|
const outcomes = [];
|
|
3475
3785
|
for (const terminal of terminals) {
|
|
3476
3786
|
const declaration = declarationOf(terminal, byName, locals);
|
|
3477
|
-
const reading = declaration === void 0 ? absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
3787
|
+
const reading = declaration === void 0 ? absentReading : readingOfCall(terminal, declaration, statusNames, options.facts);
|
|
3478
3788
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
3479
3789
|
outcomes.push({
|
|
3480
3790
|
conditions: path7.map(conditionOf),
|
|
@@ -3508,11 +3818,9 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3508
3818
|
continue;
|
|
3509
3819
|
}
|
|
3510
3820
|
for (const call of callsUnder(method)) {
|
|
3511
|
-
const request
|
|
3512
|
-
|
|
3513
|
-
continue;
|
|
3821
|
+
for (const request of requestCalls(call, pattern, options)) {
|
|
3822
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3514
3823
|
}
|
|
3515
|
-
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3516
3824
|
}
|
|
3517
3825
|
}
|
|
3518
3826
|
return units;
|
|
@@ -3541,36 +3849,60 @@ function callsUnder(node, found = []) {
|
|
|
3541
3849
|
}
|
|
3542
3850
|
return found;
|
|
3543
3851
|
}
|
|
3544
|
-
function
|
|
3852
|
+
function requestCalls(call, pattern, options) {
|
|
3545
3853
|
const receiver = field(call, "receiver");
|
|
3546
3854
|
const called = field(call, "method")?.text;
|
|
3547
|
-
if (receiver === null || called === void 0) {
|
|
3548
|
-
return
|
|
3855
|
+
if (receiver === null || called === void 0 || !isLibraryReceiver(receiver, pattern, options)) {
|
|
3856
|
+
return [];
|
|
3549
3857
|
}
|
|
3550
|
-
const
|
|
3551
|
-
|
|
3552
|
-
|
|
3858
|
+
const read = (site) => requestCall(
|
|
3859
|
+
call,
|
|
3860
|
+
called,
|
|
3861
|
+
receiverPrefix(receiver, pattern, options, site),
|
|
3862
|
+
pattern,
|
|
3863
|
+
options,
|
|
3864
|
+
site
|
|
3865
|
+
);
|
|
3866
|
+
const plain = read();
|
|
3867
|
+
if (plain !== null && !hasNameHole(plain.path)) {
|
|
3868
|
+
return [plain];
|
|
3869
|
+
}
|
|
3870
|
+
const byBoundary = /* @__PURE__ */ new Map();
|
|
3871
|
+
for (const site of constructionSitesOf(call, options.facts)) {
|
|
3872
|
+
const stated = read(site);
|
|
3873
|
+
if (stated !== null) {
|
|
3874
|
+
byBoundary.set(`${stated.method} ${stated.path}`, stated);
|
|
3875
|
+
}
|
|
3553
3876
|
}
|
|
3877
|
+
if (byBoundary.size > 0) {
|
|
3878
|
+
return [...byBoundary.values()];
|
|
3879
|
+
}
|
|
3880
|
+
return plain === null ? [] : [plain];
|
|
3881
|
+
}
|
|
3882
|
+
function requestCall(call, called, prefix, pattern, options, site) {
|
|
3554
3883
|
const args = readCallArgs(field(call, "arguments"));
|
|
3555
3884
|
const verb = pattern.verbMethodNames[called];
|
|
3556
3885
|
if (verb !== void 0) {
|
|
3557
|
-
const path8 = urlIn(args, pattern, options);
|
|
3886
|
+
const path8 = urlIn(args, pattern, options, site);
|
|
3558
3887
|
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
3559
3888
|
}
|
|
3560
3889
|
const sent = pattern.requestObject;
|
|
3561
3890
|
if (sent === void 0 || sent.attribute !== called) {
|
|
3562
3891
|
return null;
|
|
3563
3892
|
}
|
|
3564
|
-
const built = requestBuilt(args.positional[0],
|
|
3893
|
+
const built = requestBuilt(args.positional[0], sent, options, site);
|
|
3565
3894
|
if (built === null) {
|
|
3566
3895
|
return null;
|
|
3567
3896
|
}
|
|
3568
|
-
const path7 = pathAt(built.url, options);
|
|
3897
|
+
const path7 = pathAt(built.url, options, site);
|
|
3569
3898
|
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3570
3899
|
}
|
|
3571
|
-
function requestBuilt(argument,
|
|
3572
|
-
|
|
3573
|
-
|
|
3900
|
+
function requestBuilt(argument, sent, options, site) {
|
|
3901
|
+
if (argument === void 0) {
|
|
3902
|
+
return null;
|
|
3903
|
+
}
|
|
3904
|
+
const written = writtenNodeOf(argument, options.facts, site) ?? argument;
|
|
3905
|
+
if (written.type !== "call") {
|
|
3574
3906
|
return null;
|
|
3575
3907
|
}
|
|
3576
3908
|
const requestClass = field(written, "receiver")?.text;
|
|
@@ -3581,28 +3913,18 @@ function requestBuilt(argument, method, sent) {
|
|
|
3581
3913
|
const args = readCallArgs(field(written, "arguments"));
|
|
3582
3914
|
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3583
3915
|
}
|
|
3584
|
-
function
|
|
3585
|
-
|
|
3586
|
-
return null;
|
|
3587
|
-
}
|
|
3588
|
-
const value = assignedValue(node.text, method);
|
|
3589
|
-
return value !== null && value.type === "call" ? value : null;
|
|
3916
|
+
function isLibraryReceiver(receiver, pattern, options) {
|
|
3917
|
+
return namesConstant(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
3590
3918
|
}
|
|
3591
|
-
function receiverPrefix(receiver,
|
|
3592
|
-
|
|
3593
|
-
return "";
|
|
3594
|
-
}
|
|
3595
|
-
if (receiver.type !== "identifier") {
|
|
3596
|
-
return null;
|
|
3597
|
-
}
|
|
3598
|
-
const built = builtBy(receiver.text, method, root, pattern);
|
|
3919
|
+
function receiverPrefix(receiver, pattern, options, site) {
|
|
3920
|
+
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
3599
3921
|
if (built === null) {
|
|
3600
|
-
return
|
|
3922
|
+
return "";
|
|
3601
3923
|
}
|
|
3602
3924
|
const args = readCallArgs(field(built, "arguments"));
|
|
3603
3925
|
const keyword = pattern.builderUrlKeyword;
|
|
3604
3926
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
3605
|
-
const path7 = base === void 0 ? null : pathOf(evaluatedValue(base, options.facts));
|
|
3927
|
+
const path7 = base === void 0 ? null : pathOf(evaluatedValue(base, options.facts, void 0, site));
|
|
3606
3928
|
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3607
3929
|
}
|
|
3608
3930
|
function trimmed(path7) {
|
|
@@ -3614,61 +3936,28 @@ function namesConstant(receiver, constantName2) {
|
|
|
3614
3936
|
}
|
|
3615
3937
|
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
3616
3938
|
}
|
|
3617
|
-
function
|
|
3939
|
+
function builderCallBehind(receiver, pattern, facts) {
|
|
3618
3940
|
const builders = pattern.receiverBuilders ?? [];
|
|
3619
3941
|
if (builders.length === 0) {
|
|
3620
3942
|
return null;
|
|
3621
3943
|
}
|
|
3622
|
-
const
|
|
3623
|
-
if (
|
|
3624
|
-
return null;
|
|
3625
|
-
}
|
|
3626
|
-
const receiver = field(value, "receiver");
|
|
3627
|
-
return receiver !== null && namesConstant(receiver, pattern.constantName) ? value : null;
|
|
3628
|
-
}
|
|
3629
|
-
function valueMethodNamedReturns(name, root) {
|
|
3630
|
-
const defined = methodDefinitions(root).find(
|
|
3631
|
-
(candidate) => field(candidate, "name")?.text === name
|
|
3632
|
-
);
|
|
3633
|
-
if (defined === void 0) {
|
|
3634
|
-
return null;
|
|
3635
|
-
}
|
|
3636
|
-
const body = field(defined, "body");
|
|
3637
|
-
const statements = body === null ? [] : body.namedChildren;
|
|
3638
|
-
const last = statements[statements.length - 1];
|
|
3639
|
-
if (last === void 0) {
|
|
3640
|
-
return null;
|
|
3641
|
-
}
|
|
3642
|
-
if (last.type === "operator_assignment" || last.type === "assignment") {
|
|
3643
|
-
return field(last, "right");
|
|
3644
|
-
}
|
|
3645
|
-
return last.type === "return" ? last.namedChildren[0] ?? null : last;
|
|
3646
|
-
}
|
|
3647
|
-
function assignedValue(name, method) {
|
|
3648
|
-
const body = field(method, "body");
|
|
3649
|
-
if (body === null) {
|
|
3944
|
+
const written = writtenNodeOf(receiver, facts);
|
|
3945
|
+
if (written === null || written.type !== "call" || !builders.includes(field(written, "method")?.text ?? "")) {
|
|
3650
3946
|
return null;
|
|
3651
3947
|
}
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
continue;
|
|
3655
|
-
}
|
|
3656
|
-
if (field(statement, "left")?.text === name) {
|
|
3657
|
-
return field(statement, "right");
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
return null;
|
|
3948
|
+
const constant2 = field(written, "receiver");
|
|
3949
|
+
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3661
3950
|
}
|
|
3662
|
-
function urlIn(args, pattern, options) {
|
|
3951
|
+
function urlIn(args, pattern, options, site) {
|
|
3663
3952
|
const keyword = pattern.url.keyword;
|
|
3664
3953
|
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3665
|
-
return pathAt(written, options);
|
|
3954
|
+
return pathAt(written, options, site);
|
|
3666
3955
|
}
|
|
3667
|
-
function pathAt(written, options) {
|
|
3956
|
+
function pathAt(written, options, site) {
|
|
3668
3957
|
if (written === void 0) {
|
|
3669
3958
|
return null;
|
|
3670
3959
|
}
|
|
3671
|
-
const path7 = pathOf(evaluatedValue(written, options.facts));
|
|
3960
|
+
const path7 = pathOf(evaluatedValue(written, options.facts, void 0, site));
|
|
3672
3961
|
return path7 === void 0 || namesNothing(path7) ? null : path7;
|
|
3673
3962
|
}
|
|
3674
3963
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
@@ -3691,15 +3980,15 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
3691
3980
|
recognition: pack.name
|
|
3692
3981
|
}),
|
|
3693
3982
|
parameters: [],
|
|
3694
|
-
branches: callerBranches(method, range),
|
|
3983
|
+
branches: callerBranches(method, range, options.facts),
|
|
3695
3984
|
...responseAccessors(pattern),
|
|
3696
3985
|
bodyContent: "statements",
|
|
3697
3986
|
dependencyCalls: [],
|
|
3698
3987
|
declaredContract: null
|
|
3699
3988
|
};
|
|
3700
3989
|
}
|
|
3701
|
-
function callerBranches(method, range) {
|
|
3702
|
-
const effects = invocationEffects(method);
|
|
3990
|
+
function callerBranches(method, range, facts) {
|
|
3991
|
+
const effects = invocationEffects(method, void 0, void 0, facts);
|
|
3703
3992
|
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3704
3993
|
}
|
|
3705
3994
|
function responseAccessors(pattern) {
|
|
@@ -3837,7 +4126,7 @@ function isDefaultedAt(node) {
|
|
|
3837
4126
|
}
|
|
3838
4127
|
|
|
3839
4128
|
// src/filters.ts
|
|
3840
|
-
function controllerFilters(pattern, ancestry) {
|
|
4129
|
+
function controllerFilters(pattern, ancestry, read = {}) {
|
|
3841
4130
|
const forms = pattern.filters ?? [];
|
|
3842
4131
|
if (forms.length === 0) {
|
|
3843
4132
|
return [];
|
|
@@ -3857,7 +4146,7 @@ function controllerFilters(pattern, ancestry) {
|
|
|
3857
4146
|
}
|
|
3858
4147
|
const form = byName.get(called);
|
|
3859
4148
|
if (form !== void 0) {
|
|
3860
|
-
for (const declaration of declarationsOf(statement, form)) {
|
|
4149
|
+
for (const declaration of declarationsOf(statement, form, read.facts)) {
|
|
3861
4150
|
declared = [
|
|
3862
4151
|
...declared.filter((earlier) => !sameFilter(earlier, declaration)),
|
|
3863
4152
|
declaration
|
|
@@ -3867,12 +4156,12 @@ function controllerFilters(pattern, ancestry) {
|
|
|
3867
4156
|
}
|
|
3868
4157
|
const skipped = skipNames.get(called);
|
|
3869
4158
|
if (skipped !== void 0) {
|
|
3870
|
-
declared = applySkips(declared, skipsOf(statement, skipped));
|
|
4159
|
+
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
3871
4160
|
}
|
|
3872
4161
|
}
|
|
3873
4162
|
const resolved = [];
|
|
3874
4163
|
for (const declaration of declared) {
|
|
3875
|
-
const found = methodInAncestry(ancestry, declaration.methodName);
|
|
4164
|
+
const found = methodInAncestry(ancestry, declaration.methodName, read);
|
|
3876
4165
|
if (found.type !== "found") {
|
|
3877
4166
|
continue;
|
|
3878
4167
|
}
|
|
@@ -3904,14 +4193,14 @@ function filterReference(filter, displayPath) {
|
|
|
3904
4193
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
3905
4194
|
};
|
|
3906
4195
|
}
|
|
3907
|
-
function filterUnit(filter, pattern, displayPath, body) {
|
|
4196
|
+
function filterUnit(filter, pattern, displayPath, body, facts) {
|
|
3908
4197
|
const range = rangeOf(filter.method);
|
|
3909
4198
|
const branches = responseBranches(
|
|
3910
4199
|
filter.method,
|
|
3911
4200
|
pattern,
|
|
3912
4201
|
body.effects ?? [],
|
|
3913
4202
|
body.extraEffects,
|
|
3914
|
-
{ fallthrough: "handOn" }
|
|
4203
|
+
{ fallthrough: "handOn", facts }
|
|
3915
4204
|
);
|
|
3916
4205
|
return {
|
|
3917
4206
|
identity: {
|
|
@@ -3959,30 +4248,30 @@ function calledName(statement) {
|
|
|
3959
4248
|
}
|
|
3960
4249
|
return field(statement, "method")?.text ?? null;
|
|
3961
4250
|
}
|
|
3962
|
-
function declarationsOf(statement, filter) {
|
|
4251
|
+
function declarationsOf(statement, filter, facts) {
|
|
3963
4252
|
const args = readCallArgs(field(statement, "arguments"));
|
|
3964
4253
|
const keywords = filter.actionKeywords;
|
|
3965
|
-
const only = keywords === void 0 ? null :
|
|
3966
|
-
const except = keywords === void 0 ? null :
|
|
3967
|
-
return methodNamesOf(args, filter).map((methodName) => ({
|
|
4254
|
+
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
4255
|
+
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
4256
|
+
return methodNamesOf(args, filter, facts).map((methodName) => ({
|
|
3968
4257
|
filter,
|
|
3969
4258
|
methodName,
|
|
3970
4259
|
only,
|
|
3971
4260
|
except: except ?? /* @__PURE__ */ new Set()
|
|
3972
4261
|
}));
|
|
3973
4262
|
}
|
|
3974
|
-
function methodNamesOf(args, filter) {
|
|
4263
|
+
function methodNamesOf(args, filter, facts) {
|
|
3975
4264
|
if (filter.methodFrom === "withKeyword") {
|
|
3976
4265
|
const named2 = args.keyword.with;
|
|
3977
|
-
const value = named2 === void 0 ? null :
|
|
4266
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
3978
4267
|
return value === null ? [] : [value];
|
|
3979
4268
|
}
|
|
3980
|
-
return args.positional.map((arg) =>
|
|
4269
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
3981
4270
|
}
|
|
3982
|
-
function skipsOf(statement, filterName) {
|
|
4271
|
+
function skipsOf(statement, filterName, facts) {
|
|
3983
4272
|
const args = readCallArgs(field(statement, "arguments"));
|
|
3984
|
-
const actions =
|
|
3985
|
-
return args.positional.map((arg) =>
|
|
4273
|
+
const actions = actionsUnder(args.keyword.only, facts);
|
|
4274
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
3986
4275
|
}
|
|
3987
4276
|
function sameFilter(one, other) {
|
|
3988
4277
|
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
@@ -4006,26 +4295,12 @@ function applySkips(declared, skips) {
|
|
|
4006
4295
|
}
|
|
4007
4296
|
return left;
|
|
4008
4297
|
}
|
|
4009
|
-
function
|
|
4298
|
+
function actionsUnder(node, facts) {
|
|
4010
4299
|
if (node === void 0) {
|
|
4011
4300
|
return null;
|
|
4012
4301
|
}
|
|
4013
|
-
const
|
|
4014
|
-
|
|
4015
|
-
return /* @__PURE__ */ new Set([single]);
|
|
4016
|
-
}
|
|
4017
|
-
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
4018
|
-
return null;
|
|
4019
|
-
}
|
|
4020
|
-
const names = /* @__PURE__ */ new Set();
|
|
4021
|
-
for (const child of bodyStatements(node)) {
|
|
4022
|
-
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
4023
|
-
const name = symbolValue(child) ?? bare;
|
|
4024
|
-
if (name !== null) {
|
|
4025
|
-
names.add(name);
|
|
4026
|
-
}
|
|
4027
|
-
}
|
|
4028
|
-
return names;
|
|
4302
|
+
const found = namesOf(node, facts);
|
|
4303
|
+
return found === null ? null : new Set(found);
|
|
4029
4304
|
}
|
|
4030
4305
|
|
|
4031
4306
|
// src/storage.ts
|
|
@@ -4076,28 +4351,95 @@ function modelPattern(constant2, file, options) {
|
|
|
4076
4351
|
(candidate) => reachesBase(options.facts, classKey, candidate.baseClasses)
|
|
4077
4352
|
);
|
|
4078
4353
|
}
|
|
4079
|
-
function
|
|
4080
|
-
const
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4354
|
+
function chainLinks(call) {
|
|
4355
|
+
const links = [];
|
|
4356
|
+
let current = call;
|
|
4357
|
+
while (current !== null && current.type === "call") {
|
|
4358
|
+
links.push(current);
|
|
4359
|
+
current = receiverOf(current);
|
|
4360
|
+
}
|
|
4361
|
+
return links;
|
|
4362
|
+
}
|
|
4363
|
+
function kindOfCall(pattern, method) {
|
|
4364
|
+
if (pattern.writes.includes(method)) {
|
|
4365
|
+
return "write";
|
|
4366
|
+
}
|
|
4367
|
+
if (pattern.reads.includes(method)) {
|
|
4368
|
+
return "read";
|
|
4369
|
+
}
|
|
4370
|
+
return void 0;
|
|
4371
|
+
}
|
|
4372
|
+
function someLibraryDefines(options, method) {
|
|
4373
|
+
return options.patterns.some(
|
|
4374
|
+
(pattern) => kindOfCall(pattern, method) !== void 0
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4377
|
+
function argumentsOf2(call) {
|
|
4378
|
+
const args = field(call, "arguments");
|
|
4379
|
+
return args === null ? [] : children3(args);
|
|
4380
|
+
}
|
|
4381
|
+
function recordFieldsOf(argument, facts) {
|
|
4382
|
+
const value = evaluatedValue(argument, facts);
|
|
4383
|
+
return value.kind === "record" ? [...value.fields.keys()] : [];
|
|
4384
|
+
}
|
|
4385
|
+
function keywordKeys(call, facts) {
|
|
4386
|
+
const keys = [];
|
|
4387
|
+
for (const argument of argumentsOf2(call)) {
|
|
4388
|
+
if (argument.type !== "pair") {
|
|
4389
|
+
keys.push(...recordFieldsOf(argument, facts));
|
|
4390
|
+
continue;
|
|
4091
4391
|
}
|
|
4092
|
-
const
|
|
4093
|
-
|
|
4094
|
-
|
|
4392
|
+
const key = field(argument, "key");
|
|
4393
|
+
const name = key === null ? null : keyNameOf(key);
|
|
4394
|
+
if (name !== null) {
|
|
4395
|
+
keys.push(name);
|
|
4095
4396
|
}
|
|
4096
|
-
}
|
|
4097
|
-
|
|
4397
|
+
}
|
|
4398
|
+
return keys;
|
|
4399
|
+
}
|
|
4400
|
+
function keyNameOf(key) {
|
|
4401
|
+
return hashKeySymbolName(key) ?? symbolValue(key) ?? stringLiteralValue(key);
|
|
4402
|
+
}
|
|
4403
|
+
function hasPositionalArgument(call, facts) {
|
|
4404
|
+
return argumentsOf2(call).some(
|
|
4405
|
+
(argument) => argument.type !== "pair" && recordFieldsOf(argument, facts).length === 0
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
function columnsAskedFor(call, pattern, facts) {
|
|
4409
|
+
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4410
|
+
return [];
|
|
4411
|
+
}
|
|
4412
|
+
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
4413
|
+
}
|
|
4414
|
+
function primaryKeySelector(call, pattern, facts) {
|
|
4415
|
+
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4416
|
+
if (byPrimaryKey === void 0) {
|
|
4417
|
+
return [];
|
|
4418
|
+
}
|
|
4419
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
|
|
4420
|
+
return picks ? [byPrimaryKey.column] : [];
|
|
4421
|
+
}
|
|
4422
|
+
function selectorOf(call, pattern, facts) {
|
|
4423
|
+
const picked = [];
|
|
4424
|
+
for (const link of chainLinks(call)) {
|
|
4425
|
+
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4426
|
+
if (pattern.reads.includes(methodOf(link))) {
|
|
4427
|
+
picked.push(...keywordKeys(link, facts));
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4098
4430
|
return [...new Set(picked)];
|
|
4099
4431
|
}
|
|
4100
|
-
function
|
|
4432
|
+
function fieldsOf(call, pattern, kind, facts) {
|
|
4433
|
+
if (kind === "write") {
|
|
4434
|
+
return [...new Set(keywordKeys(call, facts))];
|
|
4435
|
+
}
|
|
4436
|
+
return [
|
|
4437
|
+
...new Set(
|
|
4438
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern, facts))
|
|
4439
|
+
)
|
|
4440
|
+
];
|
|
4441
|
+
}
|
|
4442
|
+
function storageEffect(call, container, pattern, kind, selector, fields) {
|
|
4101
4443
|
const operation = methodOf(call);
|
|
4102
4444
|
return {
|
|
4103
4445
|
type: "interaction",
|
|
@@ -4111,35 +4453,12 @@ function storageEffect(call, container, pattern, kind, selector) {
|
|
|
4111
4453
|
interaction: {
|
|
4112
4454
|
class: "storage-access",
|
|
4113
4455
|
kind,
|
|
4114
|
-
fields
|
|
4456
|
+
fields,
|
|
4115
4457
|
operation,
|
|
4116
4458
|
...selector.length > 0 ? { selector } : {}
|
|
4117
4459
|
}
|
|
4118
4460
|
};
|
|
4119
4461
|
}
|
|
4120
|
-
function modelCallEffects(call, file, options) {
|
|
4121
|
-
const constant2 = rootConstant(call);
|
|
4122
|
-
if (constant2 === null) {
|
|
4123
|
-
return [];
|
|
4124
|
-
}
|
|
4125
|
-
const pattern = modelPattern(constant2, file, options);
|
|
4126
|
-
if (pattern === void 0) {
|
|
4127
|
-
return [];
|
|
4128
|
-
}
|
|
4129
|
-
const kind = pattern.writes.includes(methodOf(call)) ? "write" : "read";
|
|
4130
|
-
return [
|
|
4131
|
-
storageEffect(
|
|
4132
|
-
call,
|
|
4133
|
-
constantName(constant2),
|
|
4134
|
-
pattern,
|
|
4135
|
-
kind,
|
|
4136
|
-
selectorOf(call)
|
|
4137
|
-
)
|
|
4138
|
-
];
|
|
4139
|
-
}
|
|
4140
|
-
function writeMethods(patterns) {
|
|
4141
|
-
return new Set(patterns.flatMap((pattern) => pattern.writes));
|
|
4142
|
-
}
|
|
4143
4462
|
function classSettledOn(facts, key) {
|
|
4144
4463
|
askResolution2(facts, [key], "wanted", RUBY_PROGRAM);
|
|
4145
4464
|
const objects = new Set(
|
|
@@ -4156,27 +4475,64 @@ function declaredName(facts, classKey) {
|
|
|
4156
4475
|
function projectDeclares(facts, classKey, method) {
|
|
4157
4476
|
return facts.lookup("wantedDeclaredName", 0, classKey).some((row) => String(row[1]) === method);
|
|
4158
4477
|
}
|
|
4159
|
-
function
|
|
4478
|
+
function constantReceiver(constant2, file, facts) {
|
|
4479
|
+
const classKey = classBehind(facts, file, constant2);
|
|
4480
|
+
return classKey === void 0 ? void 0 : { classKey, container: constantName(constant2) };
|
|
4481
|
+
}
|
|
4482
|
+
function settledReceiver(receiver, file, facts, enclosing) {
|
|
4483
|
+
const classKey = classSettledOn(facts, readKey(file, receiver, enclosing));
|
|
4484
|
+
if (classKey === void 0) {
|
|
4485
|
+
return void 0;
|
|
4486
|
+
}
|
|
4487
|
+
const container = declaredName(facts, classKey);
|
|
4488
|
+
return container === void 0 ? void 0 : { classKey, container };
|
|
4489
|
+
}
|
|
4490
|
+
function receiverClass(call, file, options, enclosing) {
|
|
4491
|
+
const constant2 = rootConstant(call);
|
|
4492
|
+
if (constant2 !== null) {
|
|
4493
|
+
return constantReceiver(constant2, file, options.facts);
|
|
4494
|
+
}
|
|
4160
4495
|
const receiver = receiverOf(call);
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
return [];
|
|
4496
|
+
if (receiver === null) {
|
|
4497
|
+
return void 0;
|
|
4164
4498
|
}
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
)
|
|
4169
|
-
|
|
4499
|
+
return settledReceiver(receiver, file, options.facts, enclosing);
|
|
4500
|
+
}
|
|
4501
|
+
function libraryCallIn(call, options) {
|
|
4502
|
+
return chainLinks(call).find(
|
|
4503
|
+
(link) => someLibraryDefines(options, methodOf(link))
|
|
4504
|
+
) ?? null;
|
|
4505
|
+
}
|
|
4506
|
+
function modelCallEffects(call, file, options, enclosing) {
|
|
4507
|
+
const worked = libraryCallIn(call, options);
|
|
4508
|
+
if (worked === null) {
|
|
4170
4509
|
return [];
|
|
4171
4510
|
}
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
)
|
|
4175
|
-
if (pattern === void 0 || projectDeclares(options.facts, classKey, method)) {
|
|
4511
|
+
const method = methodOf(worked);
|
|
4512
|
+
const target = receiverClass(worked, file, options, enclosing);
|
|
4513
|
+
if (target === void 0) {
|
|
4176
4514
|
return [];
|
|
4177
4515
|
}
|
|
4178
|
-
const
|
|
4179
|
-
|
|
4516
|
+
for (const pattern of options.patterns) {
|
|
4517
|
+
const kind = kindOfCall(pattern, method);
|
|
4518
|
+
if (kind === void 0 || !reachesBase(options.facts, target.classKey, pattern.baseClasses)) {
|
|
4519
|
+
continue;
|
|
4520
|
+
}
|
|
4521
|
+
if (projectDeclares(options.facts, target.classKey, method)) {
|
|
4522
|
+
return [];
|
|
4523
|
+
}
|
|
4524
|
+
return [
|
|
4525
|
+
storageEffect(
|
|
4526
|
+
worked,
|
|
4527
|
+
target.container,
|
|
4528
|
+
pattern,
|
|
4529
|
+
kind,
|
|
4530
|
+
selectorOf(worked, pattern, options.facts),
|
|
4531
|
+
fieldsOf(worked, pattern, kind, options.facts)
|
|
4532
|
+
)
|
|
4533
|
+
];
|
|
4534
|
+
}
|
|
4535
|
+
return [];
|
|
4180
4536
|
}
|
|
4181
4537
|
function isLoader(node, loader) {
|
|
4182
4538
|
if (node === null) {
|
|
@@ -4214,7 +4570,7 @@ function loaderCallEffects(call, file, options) {
|
|
|
4214
4570
|
const pattern = modelPattern(argument, file, options);
|
|
4215
4571
|
if (pattern !== void 0) {
|
|
4216
4572
|
effects.push(
|
|
4217
|
-
storageEffect(call, constantName(argument), pattern, "read", [])
|
|
4573
|
+
storageEffect(call, constantName(argument), pattern, "read", [], [])
|
|
4218
4574
|
);
|
|
4219
4575
|
}
|
|
4220
4576
|
}
|
|
@@ -4222,22 +4578,21 @@ function loaderCallEffects(call, file, options) {
|
|
|
4222
4578
|
return effects;
|
|
4223
4579
|
}
|
|
4224
4580
|
function effectsOfCall(call, file, options, enclosing) {
|
|
4225
|
-
const onModel = modelCallEffects(call, file, options);
|
|
4226
|
-
|
|
4227
|
-
return onModel;
|
|
4228
|
-
}
|
|
4229
|
-
const onInstance = rootConstant(call) === null ? instanceWriteEffects(call, file, options, enclosing) : [];
|
|
4230
|
-
return onInstance.length > 0 ? onInstance : loaderCallEffects(call, file, options);
|
|
4581
|
+
const onModel = modelCallEffects(call, file, options, enclosing);
|
|
4582
|
+
return onModel.length > 0 ? onModel : loaderCallEffects(call, file, options);
|
|
4231
4583
|
}
|
|
4232
4584
|
function storageClaims(call, file, options, enclosing = null) {
|
|
4233
4585
|
return options.patterns.length > 0 && effectsOfCall(call, file, options, enclosing).length > 0;
|
|
4234
4586
|
}
|
|
4235
4587
|
function receiverKeysToAsk(chains, file, options, enclosing) {
|
|
4236
|
-
const writes = writeMethods(options.patterns);
|
|
4237
4588
|
const keys = [];
|
|
4238
4589
|
for (const call of chains) {
|
|
4239
|
-
const
|
|
4240
|
-
if (
|
|
4590
|
+
const worked = libraryCallIn(call, options);
|
|
4591
|
+
if (worked === null || rootConstant(worked) !== null) {
|
|
4592
|
+
continue;
|
|
4593
|
+
}
|
|
4594
|
+
const receiver = receiverOf(worked);
|
|
4595
|
+
if (receiver !== null) {
|
|
4241
4596
|
keys.push(readKey(file, receiver, enclosing));
|
|
4242
4597
|
}
|
|
4243
4598
|
}
|
|
@@ -4334,11 +4689,17 @@ function createFileCache(parse, readFile) {
|
|
|
4334
4689
|
}
|
|
4335
4690
|
};
|
|
4336
4691
|
}
|
|
4337
|
-
function fieldReadContext(pattern, cache, fileBlocks, bodyRead) {
|
|
4692
|
+
function fieldReadContext(pattern, cache, fileBlocks, bodyRead, facts) {
|
|
4338
4693
|
return {
|
|
4339
4694
|
pattern,
|
|
4340
4695
|
cache,
|
|
4341
4696
|
bodyRead,
|
|
4697
|
+
facts,
|
|
4698
|
+
read: {
|
|
4699
|
+
facts,
|
|
4700
|
+
bodyBlocks: bodyRead.bodyBlocks,
|
|
4701
|
+
dynamicNames: bodyRead.dynamicNames
|
|
4702
|
+
},
|
|
4342
4703
|
lookup: {
|
|
4343
4704
|
root: pattern.root,
|
|
4344
4705
|
pathConvention: pattern.pathConvention,
|
|
@@ -4421,7 +4782,13 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
4421
4782
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
4422
4783
|
return [];
|
|
4423
4784
|
}
|
|
4424
|
-
const ctx = fieldReadContext(
|
|
4785
|
+
const ctx = fieldReadContext(
|
|
4786
|
+
pattern,
|
|
4787
|
+
options.cache,
|
|
4788
|
+
fileBlocks,
|
|
4789
|
+
options,
|
|
4790
|
+
options.facts
|
|
4791
|
+
);
|
|
4425
4792
|
const ancestry = await ancestryOf(info.qualifiedName, ownBlocks, ctx.lookup);
|
|
4426
4793
|
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
4427
4794
|
return [];
|
|
@@ -4469,7 +4836,11 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4469
4836
|
])) {
|
|
4470
4837
|
return [];
|
|
4471
4838
|
}
|
|
4472
|
-
const filters = controllerFilters(pattern, ancestry
|
|
4839
|
+
const filters = controllerFilters(pattern, ancestry, {
|
|
4840
|
+
facts: options.facts,
|
|
4841
|
+
bodyBlocks: options.bodyBlocks,
|
|
4842
|
+
dynamicNames: options.dynamicNames
|
|
4843
|
+
});
|
|
4473
4844
|
const units = [];
|
|
4474
4845
|
for (const filter of filters) {
|
|
4475
4846
|
const displayPath = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
@@ -4477,7 +4848,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4477
4848
|
filter,
|
|
4478
4849
|
pattern,
|
|
4479
4850
|
displayPath,
|
|
4480
|
-
bodyOfMethod(filter.method, filter.file, options)
|
|
4851
|
+
bodyOfMethod(filter.method, filter.file, options),
|
|
4852
|
+
options.facts
|
|
4481
4853
|
);
|
|
4482
4854
|
units.push(raw);
|
|
4483
4855
|
options.onReachSeed?.(raw, {
|
|
@@ -4514,7 +4886,10 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4514
4886
|
});
|
|
4515
4887
|
};
|
|
4516
4888
|
const own = /* @__PURE__ */ new Set();
|
|
4517
|
-
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4889
|
+
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4890
|
+
ownBlocks,
|
|
4891
|
+
options.bodyBlocks
|
|
4892
|
+
)) {
|
|
4518
4893
|
own.add(actionName);
|
|
4519
4894
|
emitAction(actionName, method, block);
|
|
4520
4895
|
}
|
|
@@ -4524,7 +4899,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4524
4899
|
continue;
|
|
4525
4900
|
}
|
|
4526
4901
|
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4527
|
-
entry.blocks
|
|
4902
|
+
entry.blocks,
|
|
4903
|
+
options.bodyBlocks
|
|
4528
4904
|
)) {
|
|
4529
4905
|
if (seen.has(actionName)) {
|
|
4530
4906
|
continue;
|
|
@@ -4538,14 +4914,17 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4538
4914
|
}
|
|
4539
4915
|
return units;
|
|
4540
4916
|
}
|
|
4541
|
-
function publicInstanceMethods(blocks) {
|
|
4917
|
+
function publicInstanceMethods(blocks, bodyBlocks) {
|
|
4542
4918
|
const found = [];
|
|
4543
4919
|
for (const block of blocks) {
|
|
4544
4920
|
if (block.info.bodyNode === null) {
|
|
4545
4921
|
continue;
|
|
4546
4922
|
}
|
|
4547
4923
|
const visibility = instanceMethodVisibility(block.info.bodyNode);
|
|
4548
|
-
for (const [name, method] of instanceMethodsByName(
|
|
4924
|
+
for (const [name, method] of instanceMethodsByName(
|
|
4925
|
+
block.info.bodyNode,
|
|
4926
|
+
bodyBlocks
|
|
4927
|
+
)) {
|
|
4549
4928
|
if ((visibility.get(name) ?? "public") === "public") {
|
|
4550
4929
|
found.push([name, method, block]);
|
|
4551
4930
|
}
|
|
@@ -4561,7 +4940,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
4561
4940
|
method,
|
|
4562
4941
|
pattern,
|
|
4563
4942
|
body.effects ?? [],
|
|
4564
|
-
body.extraEffects
|
|
4943
|
+
body.extraEffects,
|
|
4944
|
+
{ facts: bodyRead.facts }
|
|
4565
4945
|
);
|
|
4566
4946
|
return {
|
|
4567
4947
|
identity: {
|
|
@@ -4691,7 +5071,8 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
4691
5071
|
const effects = invocationEffects(
|
|
4692
5072
|
method,
|
|
4693
5073
|
bodyRead.inheritedMethods,
|
|
4694
|
-
EVERY_ARGLESS_CALL
|
|
5074
|
+
EVERY_ARGLESS_CALL,
|
|
5075
|
+
bodyRead.facts
|
|
4695
5076
|
);
|
|
4696
5077
|
const storage = bodyRead.storage;
|
|
4697
5078
|
const extra = [
|
|
@@ -4808,7 +5189,7 @@ async function readFieldShape(symbol, callArgs, scope, ctx, ancestry, range) {
|
|
|
4808
5189
|
return {
|
|
4809
5190
|
contract: literalContract(callArgs, scope, ctx),
|
|
4810
5191
|
body: bodyFromLookup(
|
|
4811
|
-
methodInAncestry(ancestry, symbol),
|
|
5192
|
+
methodInAncestry(ancestry, symbol, ctx.read),
|
|
4812
5193
|
range,
|
|
4813
5194
|
"This field",
|
|
4814
5195
|
NO_METHOD_BEHIND_IT,
|
|
@@ -4853,7 +5234,7 @@ async function readWiredClass(ref, scope, ctx) {
|
|
|
4853
5234
|
return {
|
|
4854
5235
|
contract: readClassContract(ancestry, ctx.pattern),
|
|
4855
5236
|
body: bodyFromLookup(
|
|
4856
|
-
methodInAncestry(ancestry, ctx.pattern.resolverMethodName),
|
|
5237
|
+
methodInAncestry(ancestry, ctx.pattern.resolverMethodName, ctx.read),
|
|
4857
5238
|
range,
|
|
4858
5239
|
`This field's ${targetQualifiedName}`,
|
|
4859
5240
|
methodNotSettled(
|
|
@@ -5025,6 +5406,202 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
5025
5406
|
};
|
|
5026
5407
|
}
|
|
5027
5408
|
|
|
5409
|
+
// src/inflect.ts
|
|
5410
|
+
var SINGULAR_RULES = [
|
|
5411
|
+
[/s$/i, ""],
|
|
5412
|
+
[/(ss)$/i, "$1"],
|
|
5413
|
+
[/(n)ews$/i, "$1ews"],
|
|
5414
|
+
[/([ti])a$/i, "$1um"],
|
|
5415
|
+
[
|
|
5416
|
+
/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i,
|
|
5417
|
+
"$1sis"
|
|
5418
|
+
],
|
|
5419
|
+
[/(^analy)(sis|ses)$/i, "$1sis"],
|
|
5420
|
+
[/([^f])ves$/i, "$1fe"],
|
|
5421
|
+
[/(hive)s$/i, "$1"],
|
|
5422
|
+
[/(tive)s$/i, "$1"],
|
|
5423
|
+
[/([lr])ves$/i, "$1f"],
|
|
5424
|
+
[/([^aeiouy]|qu)ies$/i, "$1y"],
|
|
5425
|
+
[/(s)eries$/i, "$1eries"],
|
|
5426
|
+
[/(m)ovies$/i, "$1ovie"],
|
|
5427
|
+
[/(x|ch|ss|sh)es$/i, "$1"],
|
|
5428
|
+
[/^(m|l)ice$/i, "$1ouse"],
|
|
5429
|
+
[/(bus)(es)?$/i, "$1"],
|
|
5430
|
+
[/(o)es$/i, "$1"],
|
|
5431
|
+
[/(shoe)s$/i, "$1"],
|
|
5432
|
+
[/(cris|test)(is|es)$/i, "$1is"],
|
|
5433
|
+
[/^(a)x[ie]s$/i, "$1xis"],
|
|
5434
|
+
[/(octop|vir)(us|i)$/i, "$1us"],
|
|
5435
|
+
[/(alias|status)(es)?$/i, "$1"],
|
|
5436
|
+
[/^(ox)en/i, "$1"],
|
|
5437
|
+
[/(vert|ind)ices$/i, "$1ex"],
|
|
5438
|
+
[/(matr)ices$/i, "$1ix"],
|
|
5439
|
+
[/(quiz)zes$/i, "$1"],
|
|
5440
|
+
[/(database)s$/i, "$1"],
|
|
5441
|
+
// The words ActiveSupport calls irregular. It declares them after the
|
|
5442
|
+
// rules above, which is what puts them ahead in the search.
|
|
5443
|
+
[/(p)erson$/i, "$1erson"],
|
|
5444
|
+
[/(p)eople$/i, "$1erson"],
|
|
5445
|
+
[/(m)an$/i, "$1an"],
|
|
5446
|
+
[/(m)en$/i, "$1an"],
|
|
5447
|
+
[/(c)hild$/i, "$1hild"],
|
|
5448
|
+
[/(c)hildren$/i, "$1hild"],
|
|
5449
|
+
[/(s)ex$/i, "$1ex"],
|
|
5450
|
+
[/(s)exes$/i, "$1ex"],
|
|
5451
|
+
[/(m)ove$/i, "$1ove"],
|
|
5452
|
+
[/(m)oves$/i, "$1ove"],
|
|
5453
|
+
[/(z)ombie$/i, "$1ombie"],
|
|
5454
|
+
[/(z)ombies$/i, "$1ombie"]
|
|
5455
|
+
];
|
|
5456
|
+
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
5457
|
+
"equipment",
|
|
5458
|
+
"information",
|
|
5459
|
+
"rice",
|
|
5460
|
+
"money",
|
|
5461
|
+
"species",
|
|
5462
|
+
"series",
|
|
5463
|
+
"fish",
|
|
5464
|
+
"sheep",
|
|
5465
|
+
"jeans",
|
|
5466
|
+
"police"
|
|
5467
|
+
]);
|
|
5468
|
+
function singularize(word, project) {
|
|
5469
|
+
if (UNCOUNTABLE.has(word.toLowerCase()) || (project?.uncountable ?? []).some(
|
|
5470
|
+
(uncountable) => uncountable.toLowerCase() === word.toLowerCase()
|
|
5471
|
+
)) {
|
|
5472
|
+
return word;
|
|
5473
|
+
}
|
|
5474
|
+
const own = projectSingular(word, project);
|
|
5475
|
+
if (own !== null) {
|
|
5476
|
+
return own;
|
|
5477
|
+
}
|
|
5478
|
+
for (let at = SINGULAR_RULES.length - 1; at >= 0; at--) {
|
|
5479
|
+
const [pattern, replacement] = SINGULAR_RULES[at];
|
|
5480
|
+
if (pattern.test(word)) {
|
|
5481
|
+
return word.replace(pattern, replacement);
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
return word;
|
|
5485
|
+
}
|
|
5486
|
+
function projectSingular(word, project) {
|
|
5487
|
+
const irregular = [...project?.irregular ?? []].reverse();
|
|
5488
|
+
for (const [plural, singular] of irregular) {
|
|
5489
|
+
if (plural.toLowerCase() === word.toLowerCase()) {
|
|
5490
|
+
return matchingCase(word, singular);
|
|
5491
|
+
}
|
|
5492
|
+
}
|
|
5493
|
+
const rules = [...project?.singular ?? []].reverse();
|
|
5494
|
+
for (const [rule3, replacement] of rules) {
|
|
5495
|
+
const applied = applyRule(word, rule3, replacement);
|
|
5496
|
+
if (applied !== null) {
|
|
5497
|
+
return applied;
|
|
5498
|
+
}
|
|
5499
|
+
}
|
|
5500
|
+
return null;
|
|
5501
|
+
}
|
|
5502
|
+
function applyRule(word, rule3, replacement) {
|
|
5503
|
+
const written = /^\/(.*)\/([a-z]*)$/s.exec(rule3);
|
|
5504
|
+
if (written === null) {
|
|
5505
|
+
return word.includes(rule3) ? word.replace(rule3, replacement) : null;
|
|
5506
|
+
}
|
|
5507
|
+
const flags = (written[2] ?? "").replace("m", "s").replace(/[^is]/g, "");
|
|
5508
|
+
const pattern = new RegExp(written[1] ?? "", flags);
|
|
5509
|
+
return pattern.test(word) ? word.replace(pattern, replacement.replace(/\\(\d)/g, "$$$1")) : null;
|
|
5510
|
+
}
|
|
5511
|
+
function matchingCase(word, replacement) {
|
|
5512
|
+
const first = word[0];
|
|
5513
|
+
if (first === void 0 || first !== first.toUpperCase()) {
|
|
5514
|
+
return replacement;
|
|
5515
|
+
}
|
|
5516
|
+
return replacement.charAt(0).toUpperCase() + replacement.slice(1);
|
|
5517
|
+
}
|
|
5518
|
+
function camelize(word, acronyms = []) {
|
|
5519
|
+
return word.split("_").map((part) => capitalize(part, acronyms)).join("");
|
|
5520
|
+
}
|
|
5521
|
+
function capitalize(part, acronyms) {
|
|
5522
|
+
if (part === "") {
|
|
5523
|
+
return "";
|
|
5524
|
+
}
|
|
5525
|
+
const acronym = acronyms.find(
|
|
5526
|
+
(known) => known.toLowerCase() === part.toLowerCase()
|
|
5527
|
+
);
|
|
5528
|
+
return acronym ?? part[0]?.toUpperCase() + part.slice(1);
|
|
5529
|
+
}
|
|
5530
|
+
function associationTargetName(name, plural, project) {
|
|
5531
|
+
return camelize(
|
|
5532
|
+
plural ? singularize(name, project) : name,
|
|
5533
|
+
project?.acronyms ?? []
|
|
5534
|
+
);
|
|
5535
|
+
}
|
|
5536
|
+
|
|
5537
|
+
// src/facts/associations.ts
|
|
5538
|
+
function declaredName2(argument) {
|
|
5539
|
+
if (argument === void 0) {
|
|
5540
|
+
return null;
|
|
5541
|
+
}
|
|
5542
|
+
if (argument.type === "simple_symbol") {
|
|
5543
|
+
return argument.text.slice(1);
|
|
5544
|
+
}
|
|
5545
|
+
return stringLiteralValue(argument);
|
|
5546
|
+
}
|
|
5547
|
+
function targetReference(key, written, nesting) {
|
|
5548
|
+
if (written.startsWith("::")) {
|
|
5549
|
+
return { key, written: written.slice(2), nesting: [] };
|
|
5550
|
+
}
|
|
5551
|
+
return { key, written, nesting };
|
|
5552
|
+
}
|
|
5553
|
+
function targetName(call, calls, name, plural, inflections) {
|
|
5554
|
+
const override = readCallArgs(field(call, "arguments")).keyword[calls.classNameKeyword];
|
|
5555
|
+
if (override === void 0) {
|
|
5556
|
+
return associationTargetName(name, plural, inflections);
|
|
5557
|
+
}
|
|
5558
|
+
return stringLiteralValue(override);
|
|
5559
|
+
}
|
|
5560
|
+
function associationsDeclaredIn(cls, classKey, nesting, calls, inflections) {
|
|
5561
|
+
const body = field(cls, "body");
|
|
5562
|
+
if (body === null || calls.length === 0) {
|
|
5563
|
+
return [];
|
|
5564
|
+
}
|
|
5565
|
+
const found = [];
|
|
5566
|
+
for (const statement of runStatements(body)) {
|
|
5567
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
5568
|
+
continue;
|
|
5569
|
+
}
|
|
5570
|
+
const method = field(statement, "method")?.text ?? "";
|
|
5571
|
+
for (const declaration of calls) {
|
|
5572
|
+
const plural = declaration.plural.includes(method);
|
|
5573
|
+
if (!plural && !declaration.singular.includes(method)) {
|
|
5574
|
+
continue;
|
|
5575
|
+
}
|
|
5576
|
+
const positional = readCallArgs(field(statement, "arguments")).positional;
|
|
5577
|
+
const name = declaredName2(positional[0]);
|
|
5578
|
+
if (name === null) {
|
|
5579
|
+
continue;
|
|
5580
|
+
}
|
|
5581
|
+
const written = targetName(
|
|
5582
|
+
statement,
|
|
5583
|
+
declaration,
|
|
5584
|
+
name,
|
|
5585
|
+
plural,
|
|
5586
|
+
inflections
|
|
5587
|
+
);
|
|
5588
|
+
if (written === null) {
|
|
5589
|
+
continue;
|
|
5590
|
+
}
|
|
5591
|
+
found.push({
|
|
5592
|
+
classKey,
|
|
5593
|
+
name,
|
|
5594
|
+
target: targetReference(
|
|
5595
|
+
`${classKey}#association:${name}`,
|
|
5596
|
+
written,
|
|
5597
|
+
nesting
|
|
5598
|
+
)
|
|
5599
|
+
});
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
return found;
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5028
5605
|
// src/facts/constants.ts
|
|
5029
5606
|
function children4(node) {
|
|
5030
5607
|
return node.namedChildren.filter((child) => child !== null);
|
|
@@ -5059,20 +5636,32 @@ function isDeclaration(node, parent) {
|
|
|
5059
5636
|
}
|
|
5060
5637
|
return parent.type === "scope_resolution";
|
|
5061
5638
|
}
|
|
5062
|
-
function collectFileConstants(filePath, root) {
|
|
5639
|
+
function collectFileConstants(filePath, root, associationCalls = [], inflections) {
|
|
5063
5640
|
const definitions = [];
|
|
5064
5641
|
const references = [];
|
|
5642
|
+
const associations = [];
|
|
5065
5643
|
const walk = (node, parent, nesting) => {
|
|
5066
5644
|
if (NESTING_TYPES.has(node.type)) {
|
|
5067
5645
|
const name = field(node, "name");
|
|
5068
5646
|
const written = name === null ? null : writtenName(name);
|
|
5069
5647
|
if (written !== null) {
|
|
5648
|
+
const classKey = nodeId(filePath, node);
|
|
5070
5649
|
definitions.push({
|
|
5071
5650
|
qualifiedName: qualify(nesting, written),
|
|
5072
|
-
key:
|
|
5651
|
+
key: classKey,
|
|
5073
5652
|
kind: "declaration"
|
|
5074
5653
|
});
|
|
5075
5654
|
const inside = [...nesting, ...written.split("::")];
|
|
5655
|
+
for (const declared of associationsDeclaredIn(
|
|
5656
|
+
node,
|
|
5657
|
+
classKey,
|
|
5658
|
+
inside,
|
|
5659
|
+
associationCalls,
|
|
5660
|
+
inflections
|
|
5661
|
+
)) {
|
|
5662
|
+
associations.push(declared);
|
|
5663
|
+
references.push(declared.target);
|
|
5664
|
+
}
|
|
5076
5665
|
for (const child of children4(node)) {
|
|
5077
5666
|
walk(child, node, inside);
|
|
5078
5667
|
}
|
|
@@ -5104,7 +5693,7 @@ function collectFileConstants(filePath, root) {
|
|
|
5104
5693
|
}
|
|
5105
5694
|
};
|
|
5106
5695
|
walk(root, null, []);
|
|
5107
|
-
return { filePath, definitions, references };
|
|
5696
|
+
return { filePath, definitions, references, associations };
|
|
5108
5697
|
}
|
|
5109
5698
|
function emitConstantBindings(db, perFile) {
|
|
5110
5699
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -5128,6 +5717,13 @@ function emitConstantBindings(db, perFile) {
|
|
|
5128
5717
|
return found.length === 1 ? found[0].key : null;
|
|
5129
5718
|
};
|
|
5130
5719
|
for (const file of files) {
|
|
5720
|
+
for (const declared of file.associations) {
|
|
5721
|
+
db.add("declaresAssociation", [
|
|
5722
|
+
declared.classKey,
|
|
5723
|
+
declared.name,
|
|
5724
|
+
declared.target.key
|
|
5725
|
+
]);
|
|
5726
|
+
}
|
|
5131
5727
|
for (const reference of file.references) {
|
|
5132
5728
|
for (let depth = reference.nesting.length; depth >= 0; depth--) {
|
|
5133
5729
|
const candidate = qualify(
|
|
@@ -5214,6 +5810,7 @@ function parseWith(language, source) {
|
|
|
5214
5810
|
import fs3 from "fs";
|
|
5215
5811
|
import path4 from "path";
|
|
5216
5812
|
import {
|
|
5813
|
+
boundaryKey,
|
|
5217
5814
|
disambiguateSummaryIds,
|
|
5218
5815
|
linkCallsToSummaries,
|
|
5219
5816
|
placeArgTargets as placeArgTargets2,
|
|
@@ -5234,6 +5831,7 @@ import {
|
|
|
5234
5831
|
runDigest,
|
|
5235
5832
|
stampModuleImports
|
|
5236
5833
|
} from "@suss/extractor";
|
|
5834
|
+
import { addPackWords } from "@suss/resolution";
|
|
5237
5835
|
|
|
5238
5836
|
// src/diagnostics.ts
|
|
5239
5837
|
import {
|
|
@@ -5289,6 +5887,35 @@ function requiredRelativePath(node) {
|
|
|
5289
5887
|
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
5290
5888
|
}
|
|
5291
5889
|
|
|
5890
|
+
// src/pack.ts
|
|
5891
|
+
function inflectionsIn(packs) {
|
|
5892
|
+
const pooled = {
|
|
5893
|
+
acronyms: [],
|
|
5894
|
+
irregular: [],
|
|
5895
|
+
uncountable: [],
|
|
5896
|
+
singular: []
|
|
5897
|
+
};
|
|
5898
|
+
for (const pack of packs) {
|
|
5899
|
+
pooled.acronyms?.push(...pack.inflections?.acronyms ?? []);
|
|
5900
|
+
pooled.irregular?.push(...pack.inflections?.irregular ?? []);
|
|
5901
|
+
pooled.uncountable?.push(...pack.inflections?.uncountable ?? []);
|
|
5902
|
+
pooled.singular?.push(...pack.inflections?.singular ?? []);
|
|
5903
|
+
}
|
|
5904
|
+
return pooled;
|
|
5905
|
+
}
|
|
5906
|
+
function bodyBlocksIn(packs) {
|
|
5907
|
+
const pooled = /* @__PURE__ */ new Map();
|
|
5908
|
+
for (const pack of packs) {
|
|
5909
|
+
for (const declared of pack.bodyBlocks ?? []) {
|
|
5910
|
+
pooled.set(declared.name, {
|
|
5911
|
+
moduleOnly: declared.moduleOnly ?? false,
|
|
5912
|
+
definesClassMethods: declared.definesClassMethods ?? false
|
|
5913
|
+
});
|
|
5914
|
+
}
|
|
5915
|
+
}
|
|
5916
|
+
return pooled;
|
|
5917
|
+
}
|
|
5918
|
+
|
|
5292
5919
|
// src/reach/closure.ts
|
|
5293
5920
|
import {
|
|
5294
5921
|
functionCallBinding,
|
|
@@ -5466,7 +6093,11 @@ function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
|
5466
6093
|
if (ancestry === void 0) {
|
|
5467
6094
|
return stop("outsideRun");
|
|
5468
6095
|
}
|
|
5469
|
-
const found = methodInAncestry(ancestry, methodName
|
|
6096
|
+
const found = methodInAncestry(ancestry, methodName, {
|
|
6097
|
+
facts: ctx.facts,
|
|
6098
|
+
bodyBlocks: ctx.bodyBlocks,
|
|
6099
|
+
dynamicNames: ctx.dynamicNames
|
|
6100
|
+
});
|
|
5470
6101
|
if (found.type === "found") {
|
|
5471
6102
|
return followed(reachedMethod(found.method, found.block, methodName));
|
|
5472
6103
|
}
|
|
@@ -5495,7 +6126,10 @@ function singletonMethodOn(qualifiedName, methodName, ctx) {
|
|
|
5495
6126
|
if (block.info.bodyNode === null) {
|
|
5496
6127
|
continue;
|
|
5497
6128
|
}
|
|
5498
|
-
const found = singletonMethodsByName(
|
|
6129
|
+
const found = singletonMethodsByName(
|
|
6130
|
+
block.info.bodyNode,
|
|
6131
|
+
ctx.bodyBlocks
|
|
6132
|
+
).get(methodName);
|
|
5499
6133
|
if (found !== void 0) {
|
|
5500
6134
|
return followed(reachedMethod(found, block, methodName));
|
|
5501
6135
|
}
|
|
@@ -5849,7 +6483,7 @@ function parameterName(param) {
|
|
|
5849
6483
|
|
|
5850
6484
|
// src/reach/context.ts
|
|
5851
6485
|
var METHOD_TYPES4 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
5852
|
-
async function buildReachContext(files, facts) {
|
|
6486
|
+
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
|
|
5853
6487
|
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
5854
6488
|
const classes = [];
|
|
5855
6489
|
const classNames = /* @__PURE__ */ new Map();
|
|
@@ -5923,7 +6557,9 @@ async function buildReachContext(files, facts) {
|
|
|
5923
6557
|
topLevelMethods,
|
|
5924
6558
|
facts,
|
|
5925
6559
|
classNames,
|
|
5926
|
-
definitions
|
|
6560
|
+
definitions,
|
|
6561
|
+
bodyBlocks,
|
|
6562
|
+
dynamicNames
|
|
5927
6563
|
};
|
|
5928
6564
|
}
|
|
5929
6565
|
function namedChildren(node) {
|
|
@@ -5969,20 +6605,48 @@ function inheritedMethodsIn(packs) {
|
|
|
5969
6605
|
}
|
|
5970
6606
|
return found;
|
|
5971
6607
|
}
|
|
5972
|
-
function
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
6608
|
+
function associationCallsIn(packs) {
|
|
6609
|
+
return packs.flatMap(
|
|
6610
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6611
|
+
(pattern) => pattern.associations === void 0 ? [] : [pattern.associations]
|
|
6612
|
+
)
|
|
6613
|
+
);
|
|
6614
|
+
}
|
|
6615
|
+
function packWordsOf(packs) {
|
|
6616
|
+
return {
|
|
6617
|
+
givesBackOne: packs.flatMap(
|
|
6618
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6619
|
+
(pattern) => pattern.baseClasses.flatMap(
|
|
6620
|
+
(base) => pattern.givesBack.map((method) => ({ base, method }))
|
|
6621
|
+
)
|
|
6622
|
+
)
|
|
6623
|
+
)
|
|
6624
|
+
};
|
|
6625
|
+
}
|
|
6626
|
+
function factsForFile(options) {
|
|
6627
|
+
const db = new Database2();
|
|
6628
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6629
|
+
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6630
|
+
emitConstantBindings(db, [
|
|
6631
|
+
collectFileConstants(
|
|
6632
|
+
options.file,
|
|
6633
|
+
options.root,
|
|
6634
|
+
associationCallsIn(options.packs),
|
|
6635
|
+
inflectionsIn(options.packs)
|
|
6636
|
+
)
|
|
6637
|
+
]);
|
|
6638
|
+
emitRequireFacts(db, options.file, options.root, /* @__PURE__ */ new Set([options.file]));
|
|
6639
|
+
bindEvaluator(db, {
|
|
6640
|
+
files: [{ file: options.file, root: options.root }],
|
|
6641
|
+
definitions: new Map(methodDefinitionsIn(options.file, options.root))
|
|
6642
|
+
});
|
|
6643
|
+
addPackWords(db, packWordsOf(options.packs));
|
|
6644
|
+
return db;
|
|
5982
6645
|
}
|
|
5983
6646
|
function alreadyDiscovered(seen, raw) {
|
|
5984
6647
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
5985
|
-
const
|
|
6648
|
+
const boundary = raw.boundaryBinding === null ? "" : boundaryKey(raw.boundaryBinding) ?? "";
|
|
6649
|
+
const key = `${raw.identity.file}::${reported}::${raw.identity.range.start}::${boundary}`;
|
|
5986
6650
|
if (seen.has(key)) {
|
|
5987
6651
|
return true;
|
|
5988
6652
|
}
|
|
@@ -6023,6 +6687,9 @@ async function extractRubyProject(options) {
|
|
|
6023
6687
|
);
|
|
6024
6688
|
const parsed = [];
|
|
6025
6689
|
const definitions = /* @__PURE__ */ new Map();
|
|
6690
|
+
const associationCalls = associationCallsIn(options.packs);
|
|
6691
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6692
|
+
const inflections = inflectionsIn(options.packs);
|
|
6026
6693
|
for (const file of options.files) {
|
|
6027
6694
|
await timer.timeAsync("parse", async () => {
|
|
6028
6695
|
const root = await cache.get(file);
|
|
@@ -6030,21 +6697,27 @@ async function extractRubyProject(options) {
|
|
|
6030
6697
|
return;
|
|
6031
6698
|
}
|
|
6032
6699
|
parsed.push({ file, root });
|
|
6033
|
-
emitValueFacts(db, file, root);
|
|
6700
|
+
emitValueFacts(db, file, root, bodyBlocks);
|
|
6034
6701
|
for (const [key, method] of methodDefinitionsIn(file, root)) {
|
|
6035
6702
|
definitions.set(key, method);
|
|
6036
6703
|
}
|
|
6037
|
-
constants.push(
|
|
6704
|
+
constants.push(
|
|
6705
|
+
collectFileConstants(file, root, associationCalls, inflections)
|
|
6706
|
+
);
|
|
6038
6707
|
});
|
|
6039
6708
|
}
|
|
6040
|
-
timer.time("discover", () => {
|
|
6709
|
+
const dynamicNames = timer.time("discover", () => {
|
|
6041
6710
|
emitConstantBindings(db, constants);
|
|
6042
6711
|
const known = new Set(parsed.map(({ file }) => file));
|
|
6043
6712
|
for (const { file, root } of parsed) {
|
|
6044
6713
|
emitRequireFacts(db, file, root, known);
|
|
6045
6714
|
}
|
|
6046
6715
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6047
|
-
|
|
6716
|
+
addPackWords(db, packWordsOf(options.packs));
|
|
6717
|
+
return readDynamicNames(
|
|
6718
|
+
db,
|
|
6719
|
+
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
6720
|
+
);
|
|
6048
6721
|
});
|
|
6049
6722
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
6050
6723
|
const loaderPatterns = options.packs.flatMap((pack) => pack.loaders ?? []);
|
|
@@ -6052,7 +6725,7 @@ async function extractRubyProject(options) {
|
|
|
6052
6725
|
const inheritedMethods = inheritedMethodsIn(options.packs);
|
|
6053
6726
|
const reachContext = await timer.timeAsync(
|
|
6054
6727
|
"discover",
|
|
6055
|
-
() => buildReachContext(parsed, db)
|
|
6728
|
+
() => buildReachContext(parsed, db, bodyBlocks, dynamicNames)
|
|
6056
6729
|
);
|
|
6057
6730
|
const displayPathOf = (file) => options.workspaceRoot !== void 0 ? path4.relative(options.workspaceRoot, file) : file;
|
|
6058
6731
|
const seeds = [];
|
|
@@ -6072,6 +6745,8 @@ async function extractRubyProject(options) {
|
|
|
6072
6745
|
cache,
|
|
6073
6746
|
...storage === void 0 ? {} : { storage },
|
|
6074
6747
|
inheritedMethods,
|
|
6748
|
+
bodyBlocks,
|
|
6749
|
+
dynamicNames,
|
|
6075
6750
|
displayPathOf,
|
|
6076
6751
|
facts: db,
|
|
6077
6752
|
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
@@ -6143,7 +6818,9 @@ async function extractRubyProject(options) {
|
|
|
6143
6818
|
context: reachContext,
|
|
6144
6819
|
displayPathOf,
|
|
6145
6820
|
...storage === void 0 ? {} : { storage },
|
|
6146
|
-
inheritedMethods
|
|
6821
|
+
inheritedMethods,
|
|
6822
|
+
bodyBlocks,
|
|
6823
|
+
dynamicNames
|
|
6147
6824
|
})
|
|
6148
6825
|
);
|
|
6149
6826
|
for (const entry of found) {
|
|
@@ -6309,6 +6986,10 @@ function displayNameOf(node) {
|
|
|
6309
6986
|
function width(node) {
|
|
6310
6987
|
return node.endIndex - node.startIndex;
|
|
6311
6988
|
}
|
|
6989
|
+
function askedKey(value) {
|
|
6990
|
+
const enclosing = enclosingMethod2(value.node);
|
|
6991
|
+
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6992
|
+
}
|
|
6312
6993
|
var RubyWhySession = class {
|
|
6313
6994
|
root;
|
|
6314
6995
|
db = new Database3();
|
|
@@ -6384,7 +7065,7 @@ var RubyWhySession = class {
|
|
|
6384
7065
|
return explainResolvedKey({
|
|
6385
7066
|
db: this.db,
|
|
6386
7067
|
rules: WITNESS_RULES,
|
|
6387
|
-
key:
|
|
7068
|
+
key: askedKey(value),
|
|
6388
7069
|
locate: (key) => this.locate(key),
|
|
6389
7070
|
displayPath: (key) => this.displayPath(key),
|
|
6390
7071
|
...options
|
|
@@ -6501,6 +7182,7 @@ export {
|
|
|
6501
7182
|
emitValueFacts,
|
|
6502
7183
|
evaluatedValue,
|
|
6503
7184
|
extractRubyProject,
|
|
7185
|
+
factsForFile,
|
|
6504
7186
|
field,
|
|
6505
7187
|
findRubyFiles,
|
|
6506
7188
|
graphqlTypeNameFromQualified,
|