@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.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(index_exports, {
|
|
|
44
44
|
emitValueFacts: () => emitValueFacts,
|
|
45
45
|
evaluatedValue: () => evaluatedValue,
|
|
46
46
|
extractRubyProject: () => extractRubyProject,
|
|
47
|
+
factsForFile: () => factsForFile,
|
|
47
48
|
field: () => field,
|
|
48
49
|
findRubyFiles: () => findRubyFiles,
|
|
49
50
|
graphqlTypeNameFromQualified: () => graphqlTypeNameFromQualified,
|
|
@@ -143,7 +144,52 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
143
144
|
"singleton_class"
|
|
144
145
|
]);
|
|
145
146
|
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
147
|
+
var INCLUDE_CALL = "include";
|
|
148
|
+
var PREPEND_CALL = "prepend";
|
|
146
149
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
150
|
+
var BLOCK_TYPES = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
151
|
+
var NO_BODY_BLOCKS = /* @__PURE__ */ new Map();
|
|
152
|
+
function isModuleBody(body) {
|
|
153
|
+
return body.parent?.type === "module";
|
|
154
|
+
}
|
|
155
|
+
function declaredBlockAt(statement, isModule, blocks) {
|
|
156
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const method = field(statement, "method");
|
|
160
|
+
const kind = method === null ? void 0 : blocks.get(method.text);
|
|
161
|
+
if (kind === void 0 || kind.moduleOnly && !isModule) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return field(statement, "block") === null ? null : kind;
|
|
165
|
+
}
|
|
166
|
+
function bodyStatementsRun(body, isModule, blocks) {
|
|
167
|
+
return bodyStatements(body).flatMap((statement) => {
|
|
168
|
+
if (declaredBlockAt(statement, isModule, blocks) === null) {
|
|
169
|
+
return [statement];
|
|
170
|
+
}
|
|
171
|
+
const block = field(statement, "block");
|
|
172
|
+
const inner = block === null ? null : field(block, "body");
|
|
173
|
+
return inner === null ? [] : bodyStatementsRun(inner, isModule, blocks);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function definedAtClassLevel(node, body, blocks) {
|
|
177
|
+
if (blocks.size === 0) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const isModule = isModuleBody(body);
|
|
181
|
+
let current = node.parent;
|
|
182
|
+
while (current !== null && current.id !== body.id) {
|
|
183
|
+
const parent = current.parent;
|
|
184
|
+
if (BLOCK_TYPES.has(current.type) && parent !== null) {
|
|
185
|
+
if (declaredBlockAt(parent, isModule, blocks)?.definesClassMethods) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
current = parent;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
147
193
|
function runStatements(body, blockConfigures = () => false) {
|
|
148
194
|
const found = [];
|
|
149
195
|
const readsItsChildren = (node) => {
|
|
@@ -179,10 +225,10 @@ function stringLiteralValue(node) {
|
|
|
179
225
|
function symbolValue(node) {
|
|
180
226
|
return node.type === "simple_symbol" ? node.text.slice(1) : null;
|
|
181
227
|
}
|
|
182
|
-
function instanceMethodsByName(body) {
|
|
228
|
+
function instanceMethodsByName(body, blocks = NO_BODY_BLOCKS) {
|
|
183
229
|
const methods = /* @__PURE__ */ new Map();
|
|
184
230
|
for (const stmt of runStatements(body)) {
|
|
185
|
-
if (stmt.type !== "method") {
|
|
231
|
+
if (stmt.type !== "method" || definedAtClassLevel(stmt, body, blocks)) {
|
|
186
232
|
continue;
|
|
187
233
|
}
|
|
188
234
|
const name = field(stmt, "name")?.text;
|
|
@@ -244,10 +290,11 @@ function instanceMethodVisibility(body) {
|
|
|
244
290
|
}
|
|
245
291
|
return visibility;
|
|
246
292
|
}
|
|
247
|
-
function singletonMethodsByName(body) {
|
|
293
|
+
function singletonMethodsByName(body, blocks = NO_BODY_BLOCKS) {
|
|
248
294
|
const methods = /* @__PURE__ */ new Map();
|
|
249
295
|
for (const stmt of runStatements(body)) {
|
|
250
|
-
|
|
296
|
+
const classLevel = stmt.type === "method" && definedAtClassLevel(stmt, body, blocks);
|
|
297
|
+
if (stmt.type !== "singleton_method" && !classLevel) {
|
|
251
298
|
continue;
|
|
252
299
|
}
|
|
253
300
|
const name = field(stmt, "name")?.text;
|
|
@@ -360,7 +407,8 @@ function autoloadDirectories(root) {
|
|
|
360
407
|
}
|
|
361
408
|
|
|
362
409
|
// src/defineMethod.ts
|
|
363
|
-
var
|
|
410
|
+
var import_resolution5 = require("@suss/resolution");
|
|
411
|
+
var import_values5 = require("@suss/values");
|
|
364
412
|
|
|
365
413
|
// src/values/evaluator.ts
|
|
366
414
|
var import_resolution4 = require("@suss/resolution");
|
|
@@ -369,35 +417,45 @@ var import_values2 = require("@suss/values");
|
|
|
369
417
|
// src/facts/resolve.ts
|
|
370
418
|
var import_datalog = require("@suss/datalog");
|
|
371
419
|
var import_resolution = require("@suss/resolution");
|
|
372
|
-
var RUBY_RULES = [
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
420
|
+
var RUBY_RULES = (0, import_resolution.alsoSteps)([
|
|
421
|
+
// Making one of a class is a call of the class, which the shared rules
|
|
422
|
+
// already say. Ruby writes the callee as `new` read off the constant, so
|
|
423
|
+
// what this adds is that the callee is the class.
|
|
376
424
|
(0, import_datalog.rule)(
|
|
377
|
-
"
|
|
378
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("cls"), import_resolution.
|
|
425
|
+
"hop",
|
|
426
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("cls"), import_resolution.INSTANCE_STEP],
|
|
379
427
|
[
|
|
380
428
|
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.constant)("new")),
|
|
381
429
|
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("cls")),
|
|
382
430
|
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("cls"))
|
|
383
431
|
]
|
|
384
432
|
),
|
|
385
|
-
// The
|
|
386
|
-
//
|
|
387
|
-
//
|
|
433
|
+
// The same spelling again, for the caller asking where a class was
|
|
434
|
+
// made. `callsNamed` reaches a callee written as the name itself, and
|
|
435
|
+
// Ruby writes `new` off the constant instead.
|
|
388
436
|
(0, import_datalog.rule)(
|
|
389
|
-
"
|
|
390
|
-
[(0, import_datalog.variable)("
|
|
437
|
+
"constructsNamed",
|
|
438
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("cls")],
|
|
391
439
|
[
|
|
392
|
-
(0, import_datalog.lit)("
|
|
393
|
-
(0, import_datalog.lit)("
|
|
394
|
-
(0, import_datalog.lit)("
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
440
|
+
(0, import_datalog.lit)("binds", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("cls")),
|
|
441
|
+
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("o"), (0, import_datalog.constant)("new")),
|
|
442
|
+
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c"))
|
|
443
|
+
]
|
|
444
|
+
),
|
|
445
|
+
// `%i[a b].freeze` is worth the list it was written as. The evaluator
|
|
446
|
+
// says the same in its row table, for a value it reads in one file.
|
|
447
|
+
...["freeze", "dup"].map(
|
|
448
|
+
(method) => (0, import_datalog.rule)(
|
|
449
|
+
"hop",
|
|
450
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("o"), import_resolution.VALUE_STEP],
|
|
451
|
+
[
|
|
452
|
+
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
453
|
+
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("o"), (0, import_datalog.constant)(method))
|
|
454
|
+
],
|
|
455
|
+
"hands back the receiver"
|
|
456
|
+
)
|
|
399
457
|
)
|
|
400
|
-
];
|
|
458
|
+
]);
|
|
401
459
|
var RUBY_PROGRAM = (0, import_resolution.resolutionProgram)(RUBY_RULES);
|
|
402
460
|
function resolveValues(db, keys) {
|
|
403
461
|
(0, import_resolution.askResolution)(db, keys, "wanted", RUBY_PROGRAM);
|
|
@@ -409,14 +467,122 @@ function writtenValueOf(db, key) {
|
|
|
409
467
|
resolveValues(db, [key]);
|
|
410
468
|
return (0, import_resolution.writtenValueOf)(db, key, (keys) => resolveValues(db, keys));
|
|
411
469
|
}
|
|
470
|
+
function writtenValueUnder(db, key, site) {
|
|
471
|
+
(0, import_resolution.askResolutionUnder)(db, [[key, site]], (0, import_resolution.resolutionUnderProgram)(RUBY_RULES));
|
|
472
|
+
return (0, import_resolution.writtenValueUnder)(db, key, site);
|
|
473
|
+
}
|
|
474
|
+
function constructionSites(db, classKey) {
|
|
475
|
+
(0, import_resolution.askResolution)(db, [classKey], "wantedSites", RUBY_PROGRAM);
|
|
476
|
+
return (0, import_resolution.allocationSitesOf)(db, classKey);
|
|
477
|
+
}
|
|
412
478
|
|
|
413
479
|
// src/facts/values.ts
|
|
414
480
|
var import_resolution3 = require("@suss/resolution");
|
|
415
481
|
|
|
482
|
+
// src/paths/bareCalls.ts
|
|
483
|
+
var PARAMETER_LIST_TYPES = [
|
|
484
|
+
"method_parameters",
|
|
485
|
+
"block_parameters",
|
|
486
|
+
"lambda_parameters",
|
|
487
|
+
"parameters"
|
|
488
|
+
];
|
|
489
|
+
function isFieldOf(parent, node, name) {
|
|
490
|
+
return field(parent, name)?.id === node.id;
|
|
491
|
+
}
|
|
492
|
+
function namedAt(parent, node) {
|
|
493
|
+
return isFieldOf(parent, node, "name");
|
|
494
|
+
}
|
|
495
|
+
function always() {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
var SPELLS_A_NAME = {
|
|
499
|
+
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
500
|
+
method: namedAt,
|
|
501
|
+
singleton_method: namedAt,
|
|
502
|
+
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
503
|
+
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
504
|
+
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
505
|
+
optional_parameter: namedAt,
|
|
506
|
+
splat_parameter: namedAt,
|
|
507
|
+
hash_splat_parameter: namedAt,
|
|
508
|
+
keyword_parameter: namedAt,
|
|
509
|
+
block_parameter: namedAt,
|
|
510
|
+
destructured_parameter: always,
|
|
511
|
+
left_assignment_list: always,
|
|
512
|
+
rest_assignment: always,
|
|
513
|
+
destructured_left_assignment: always,
|
|
514
|
+
exception_variable: always,
|
|
515
|
+
undef: always,
|
|
516
|
+
alias: always,
|
|
517
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
518
|
+
};
|
|
519
|
+
function addNamesUnder(node, names) {
|
|
520
|
+
if (node === null) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (node.type === "identifier") {
|
|
524
|
+
names.add(node.text);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
for (const child of bodyStatements(node)) {
|
|
528
|
+
addNamesUnder(child, names);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function parameterNames(node, names) {
|
|
532
|
+
for (const parameter of bodyStatements(node)) {
|
|
533
|
+
if (parameter.type === "identifier") {
|
|
534
|
+
names.add(parameter.text);
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
const declared = field(parameter, "name");
|
|
538
|
+
if (declared !== null && declared.type === "identifier") {
|
|
539
|
+
names.add(declared.text);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
addNamesUnder(parameter, names);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function assignedNames(node, names) {
|
|
546
|
+
addNamesUnder(field(node, "left"), names);
|
|
547
|
+
}
|
|
548
|
+
var BINDS_A_NAME = {
|
|
549
|
+
assignment: assignedNames,
|
|
550
|
+
operator_assignment: assignedNames,
|
|
551
|
+
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
552
|
+
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
553
|
+
...Object.fromEntries(
|
|
554
|
+
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
555
|
+
)
|
|
556
|
+
};
|
|
557
|
+
function localNamesIn(definitionNode) {
|
|
558
|
+
const names = /* @__PURE__ */ new Set();
|
|
559
|
+
const visit = (node) => {
|
|
560
|
+
BINDS_A_NAME[node.type]?.(node, names);
|
|
561
|
+
for (const child of bodyStatements(node)) {
|
|
562
|
+
visit(child);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
visit(definitionNode);
|
|
566
|
+
return names;
|
|
567
|
+
}
|
|
568
|
+
function spellsAName(node) {
|
|
569
|
+
const parent = node.parent;
|
|
570
|
+
if (parent === null) {
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
574
|
+
}
|
|
575
|
+
function isBareMethodCall(node, locals) {
|
|
576
|
+
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
return !spellsAName(node);
|
|
580
|
+
}
|
|
581
|
+
|
|
416
582
|
// src/facts/locals.ts
|
|
417
583
|
var import_resolution2 = require("@suss/resolution");
|
|
418
|
-
var
|
|
419
|
-
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([...OWN_BODY_TYPES, ...
|
|
584
|
+
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
585
|
+
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([...OWN_BODY_TYPES, ...BLOCK_TYPES2]);
|
|
420
586
|
var RUBY_NAME_TYPES = /* @__PURE__ */ new Set([
|
|
421
587
|
"identifier",
|
|
422
588
|
"instance_variable"
|
|
@@ -437,7 +603,7 @@ function parametersOf(owner) {
|
|
|
437
603
|
const params = field(owner, "parameters");
|
|
438
604
|
return params === null ? [] : bodyStatements(params);
|
|
439
605
|
}
|
|
440
|
-
function
|
|
606
|
+
function parameterNames2(owner) {
|
|
441
607
|
const names = /* @__PURE__ */ new Set();
|
|
442
608
|
for (const param of parametersOf(owner)) {
|
|
443
609
|
const name = paramNameOf(param);
|
|
@@ -485,6 +651,13 @@ function readForVariable(node) {
|
|
|
485
651
|
}
|
|
486
652
|
return targetsOf(pattern).map((target) => writeOf(target, null, node, false));
|
|
487
653
|
}
|
|
654
|
+
function readRescueVariable(node) {
|
|
655
|
+
const variable = field(node, "variable");
|
|
656
|
+
if (variable === null) {
|
|
657
|
+
return [];
|
|
658
|
+
}
|
|
659
|
+
return bodyStatements(variable).flatMap(targetsOf).map((target) => writeOf(target, null, node, false));
|
|
660
|
+
}
|
|
488
661
|
function readBlockParameters(node) {
|
|
489
662
|
const found = [];
|
|
490
663
|
for (const param of parametersOf(node)) {
|
|
@@ -499,6 +672,7 @@ var WRITE_READERS = {
|
|
|
499
672
|
assignment: readAssignment,
|
|
500
673
|
operator_assignment: readOperatorAssignment,
|
|
501
674
|
for: readForVariable,
|
|
675
|
+
rescue: readRescueVariable,
|
|
502
676
|
block: readBlockParameters,
|
|
503
677
|
do_block: readBlockParameters
|
|
504
678
|
};
|
|
@@ -544,7 +718,7 @@ function namesOfMethod(method) {
|
|
|
544
718
|
if (remembered !== void 0) {
|
|
545
719
|
return remembered;
|
|
546
720
|
}
|
|
547
|
-
const names = scopeNames(
|
|
721
|
+
const names = scopeNames(parameterNames2(method), field(method, "body"));
|
|
548
722
|
cache.byMethod.set(method, names);
|
|
549
723
|
return names;
|
|
550
724
|
}
|
|
@@ -566,10 +740,17 @@ function ownerOfName(node, name, enclosing) {
|
|
|
566
740
|
}
|
|
567
741
|
return enclosing !== null && names.locals.has(name) ? enclosing : null;
|
|
568
742
|
}
|
|
743
|
+
function isLocalName(node, name, enclosing) {
|
|
744
|
+
const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
|
|
745
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
return names.locals.has(name);
|
|
749
|
+
}
|
|
569
750
|
function blockDeclaring(node, name) {
|
|
570
751
|
let current = node.parent;
|
|
571
752
|
while (current !== null) {
|
|
572
|
-
if (
|
|
753
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames2(current).has(name)) {
|
|
573
754
|
return current;
|
|
574
755
|
}
|
|
575
756
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -625,6 +806,17 @@ function collectWrites(method, body) {
|
|
|
625
806
|
}
|
|
626
807
|
return [...groups.values()];
|
|
627
808
|
}
|
|
809
|
+
function instanceWritesRunInOrder(body, name, targets) {
|
|
810
|
+
return (0, import_resolution2.writesRunInOrder)(
|
|
811
|
+
body,
|
|
812
|
+
name,
|
|
813
|
+
targets.map((target) => ({
|
|
814
|
+
at: target,
|
|
815
|
+
direct: target.parent?.parent?.id === body.id
|
|
816
|
+
})),
|
|
817
|
+
nameReads(new Set(targets.map((target) => target.id)))
|
|
818
|
+
);
|
|
819
|
+
}
|
|
628
820
|
function orderedWrite(write, scope) {
|
|
629
821
|
return {
|
|
630
822
|
at: write.at,
|
|
@@ -670,12 +862,28 @@ function readKey(filePath, written, enclosing) {
|
|
|
670
862
|
if (node.type !== "identifier" && node.type !== "constant") {
|
|
671
863
|
return nodeId(filePath, node);
|
|
672
864
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
865
|
+
if (isMethodOfReceiver(node) || isBareCall(node, enclosing)) {
|
|
866
|
+
return nodeId(filePath, node);
|
|
676
867
|
}
|
|
677
868
|
return nameKey(filePath, node, enclosing);
|
|
678
869
|
}
|
|
870
|
+
function isMethodOfReceiver(node) {
|
|
871
|
+
const parent = node.parent;
|
|
872
|
+
return parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null;
|
|
873
|
+
}
|
|
874
|
+
function isReceiverOfCall(node) {
|
|
875
|
+
const parent = node.parent;
|
|
876
|
+
return parent !== null && parent.type === "call" && field(parent, "receiver")?.id === node.id;
|
|
877
|
+
}
|
|
878
|
+
function isBareCall(node, enclosing) {
|
|
879
|
+
if (node.type !== "identifier") {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
if (spellsAName(node) && !isReceiverOfCall(node)) {
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
return !isLocalName(node, node.text, enclosing);
|
|
886
|
+
}
|
|
679
887
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
680
888
|
"string",
|
|
681
889
|
"integer",
|
|
@@ -704,11 +912,7 @@ function add(emitter, relation, ...tuple) {
|
|
|
704
912
|
emitter.db.add(relation, tuple);
|
|
705
913
|
}
|
|
706
914
|
function valueKey(emitter, written) {
|
|
707
|
-
|
|
708
|
-
if (value.type !== "identifier" && value.type !== "constant") {
|
|
709
|
-
return nodeId(emitter.filePath, value);
|
|
710
|
-
}
|
|
711
|
-
return nameKey(emitter.filePath, value, emitter.enclosing);
|
|
915
|
+
return readKey(emitter.filePath, written, emitter.enclosing);
|
|
712
916
|
}
|
|
713
917
|
function pairKeyText(key) {
|
|
714
918
|
if (key.type === "hash_key_symbol") {
|
|
@@ -722,35 +926,29 @@ function pairKeyText(key) {
|
|
|
722
926
|
}
|
|
723
927
|
return null;
|
|
724
928
|
}
|
|
725
|
-
function
|
|
726
|
-
|
|
727
|
-
(
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
function emitPropertyRead(emitter, node) {
|
|
731
|
-
const receiver = field(node, "receiver");
|
|
732
|
-
const method = field(node, "method");
|
|
733
|
-
if (receiver === null || method === null) {
|
|
734
|
-
return;
|
|
929
|
+
function calleeKeyOf(filePath, node, enclosing) {
|
|
930
|
+
if (isBareCall(node, enclosing)) {
|
|
931
|
+
return nameKey(filePath, node, enclosing);
|
|
735
932
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
method.text
|
|
742
|
-
);
|
|
933
|
+
const method = node.type === "call" ? field(node, "method") : null;
|
|
934
|
+
if (method === null) {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
743
938
|
}
|
|
744
939
|
function emitCall(emitter, call) {
|
|
745
940
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
746
|
-
const method = field(call, "method");
|
|
747
|
-
|
|
941
|
+
const method = call.type === "identifier" ? call : field(call, "method");
|
|
942
|
+
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
943
|
+
if (method === null || calleeKey === null) {
|
|
748
944
|
return;
|
|
749
945
|
}
|
|
750
946
|
const receiver = field(call, "receiver");
|
|
751
947
|
const callKey = nodeId(emitter.filePath, call);
|
|
752
|
-
const calleeKey = receiver === null ? valueKey(emitter, method) : nodeId(emitter.filePath, method);
|
|
753
948
|
add(emitter, "call", callKey, calleeKey);
|
|
949
|
+
if (!emitter.insideMethod) {
|
|
950
|
+
add(emitter, "callOutsideMethod", callKey);
|
|
951
|
+
}
|
|
754
952
|
if (receiver !== null) {
|
|
755
953
|
add(
|
|
756
954
|
emitter,
|
|
@@ -759,6 +957,8 @@ function emitCall(emitter, call) {
|
|
|
759
957
|
valueKey(emitter, receiver),
|
|
760
958
|
method.text
|
|
761
959
|
);
|
|
960
|
+
} else if (emitter.selfKey !== null) {
|
|
961
|
+
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
762
962
|
}
|
|
763
963
|
const args = field(call, "arguments");
|
|
764
964
|
let position = 0;
|
|
@@ -819,20 +1019,71 @@ function emitHash(emitter, hash) {
|
|
|
819
1019
|
add(emitter, "holdsProperty", objectKey, keyText, valueKey(emitter, value));
|
|
820
1020
|
}
|
|
821
1021
|
}
|
|
822
|
-
|
|
1022
|
+
var BLOCK_TYPES3 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1023
|
+
var LOOP_METHODS = /* @__PURE__ */ new Set(["each", "each_with_index", "map"]);
|
|
1024
|
+
var DEFINE_METHOD_CALL = "define_method";
|
|
1025
|
+
function loopTurnAt(emitter, block) {
|
|
1026
|
+
const call = block.parent;
|
|
1027
|
+
if (call?.type !== "call") {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
const receiver = field(call, "receiver");
|
|
1031
|
+
const method = field(call, "method");
|
|
1032
|
+
if (receiver === null || method === null || !LOOP_METHODS.has(method.text)) {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
const parameters = field(block, "parameters");
|
|
1036
|
+
const names = parameters === null ? [] : children(parameters).map((p) => p.text);
|
|
1037
|
+
const element = names[0];
|
|
1038
|
+
if (element === void 0) {
|
|
1039
|
+
return null;
|
|
1040
|
+
}
|
|
1041
|
+
return {
|
|
1042
|
+
element,
|
|
1043
|
+
index: names[1] ?? "",
|
|
1044
|
+
overKey: nodeId(emitter.filePath, receiver)
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
function emitDynamicDefinition(emitter, call, turns) {
|
|
1048
|
+
if (emitter.selfKey === null || // A call inside a method runs when that method does, not at load time.
|
|
1049
|
+
emitter.enclosing !== null || call.type !== "call" || field(call, "receiver") !== null || field(call, "method")?.text !== DEFINE_METHOD_CALL) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
const args = field(call, "arguments");
|
|
1053
|
+
const first = args === null ? void 0 : children(args)[0];
|
|
1054
|
+
const nameKey2 = nodeId(emitter.filePath, first ?? call);
|
|
1055
|
+
add(emitter, "definesMethodFrom", emitter.selfKey, nameKey2);
|
|
1056
|
+
for (const turn of turns) {
|
|
1057
|
+
add(
|
|
1058
|
+
emitter,
|
|
1059
|
+
"nameTurnsOn",
|
|
1060
|
+
nameKey2,
|
|
1061
|
+
turn.element,
|
|
1062
|
+
turn.index,
|
|
1063
|
+
turn.overKey
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function walkExpressions(node, emitter, visit, turns = []) {
|
|
823
1068
|
for (const child of children(node)) {
|
|
824
1069
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
825
1070
|
continue;
|
|
826
1071
|
}
|
|
827
|
-
visit(child);
|
|
828
|
-
|
|
1072
|
+
visit(child, turns);
|
|
1073
|
+
const opened = BLOCK_TYPES3.has(child.type) ? loopTurnAt(emitter, child) : null;
|
|
1074
|
+
walkExpressions(
|
|
1075
|
+
child,
|
|
1076
|
+
emitter,
|
|
1077
|
+
visit,
|
|
1078
|
+
opened === null ? turns : [...turns, opened]
|
|
1079
|
+
);
|
|
829
1080
|
}
|
|
830
1081
|
}
|
|
831
1082
|
function emitExpressionFacts(emitter, node) {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1083
|
+
emitDynamicDefinition(emitter, node, []);
|
|
1084
|
+
walkExpressions(node, emitter, (child, turns) => {
|
|
1085
|
+
emitDynamicDefinition(emitter, child, turns);
|
|
1086
|
+
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
836
1087
|
emitCall(emitter, child);
|
|
837
1088
|
}
|
|
838
1089
|
if (ARRAY_TYPES.has(child.type)) {
|
|
@@ -888,28 +1139,52 @@ function collectInstanceWrite(emitter, node) {
|
|
|
888
1139
|
const operator = field(node, "operator")?.text;
|
|
889
1140
|
const value = node.type === "assignment" || WHOLE_VALUE_OPERATORS.has(operator ?? "") ? right : null;
|
|
890
1141
|
const written = collector.get(left.text) ?? [];
|
|
891
|
-
written.push(
|
|
892
|
-
describeWrite(emitter, {
|
|
1142
|
+
written.push({
|
|
1143
|
+
write: describeWrite(emitter, {
|
|
893
1144
|
name: left.text,
|
|
894
1145
|
target: left,
|
|
895
1146
|
value,
|
|
896
1147
|
at: node,
|
|
897
1148
|
fromParameter: false
|
|
898
|
-
})
|
|
899
|
-
|
|
1149
|
+
}),
|
|
1150
|
+
target: left
|
|
1151
|
+
});
|
|
900
1152
|
collector.set(left.text, written);
|
|
901
1153
|
}
|
|
1154
|
+
function settledWrites(writes, ordered) {
|
|
1155
|
+
const described = writes.map((written) => written.write);
|
|
1156
|
+
const settled = (0, import_resolution3.valueLeftByWrites)(described, ordered);
|
|
1157
|
+
if (settled !== null) {
|
|
1158
|
+
return [settled];
|
|
1159
|
+
}
|
|
1160
|
+
const left = [];
|
|
1161
|
+
for (const write of described) {
|
|
1162
|
+
if (write.value !== null && !write.narrowsName) {
|
|
1163
|
+
left.push(write.value);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return left;
|
|
1167
|
+
}
|
|
902
1168
|
function emitInstanceWrites(emitter, classKey, collected) {
|
|
903
1169
|
for (const [name, writes] of collected) {
|
|
904
|
-
const
|
|
905
|
-
|
|
906
|
-
|
|
1170
|
+
for (const value of settledWrites(writes, false)) {
|
|
1171
|
+
add(emitter, "holdsProperty", classKey, name, value);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function emitInstanceStores(emitter, funcKey, body, collected) {
|
|
1176
|
+
for (const [name, writes] of collected) {
|
|
1177
|
+
const deciding = writes.filter((written) => !written.write.narrowsName);
|
|
1178
|
+
if (deciding.length === 0) {
|
|
907
1179
|
continue;
|
|
908
1180
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1181
|
+
const ordered = instanceWritesRunInOrder(
|
|
1182
|
+
body,
|
|
1183
|
+
name,
|
|
1184
|
+
deciding.map((written) => written.target)
|
|
1185
|
+
);
|
|
1186
|
+
for (const value of settledWrites(deciding, ordered)) {
|
|
1187
|
+
add(emitter, "storesProperty", funcKey, name, value);
|
|
913
1188
|
}
|
|
914
1189
|
}
|
|
915
1190
|
}
|
|
@@ -918,7 +1193,20 @@ function implicitReturn(body) {
|
|
|
918
1193
|
(child) => child.type !== "rescue" && child.type !== "ensure"
|
|
919
1194
|
);
|
|
920
1195
|
const last = statements[statements.length - 1];
|
|
921
|
-
|
|
1196
|
+
if (last === void 0 || last.type === "return") {
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
return assignedValueOf(last) ?? last;
|
|
1200
|
+
}
|
|
1201
|
+
function assignedValueOf(node) {
|
|
1202
|
+
if (!ASSIGNMENT_TYPES.has(node.type)) {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
const operator = field(node, "operator")?.text;
|
|
1206
|
+
if (node.type !== "assignment" && !WHOLE_VALUE_OPERATORS.has(operator ?? "")) {
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
return field(node, "right");
|
|
922
1210
|
}
|
|
923
1211
|
function emitMethodFacts(emitter, method) {
|
|
924
1212
|
const funcKey = nodeId(emitter.filePath, method);
|
|
@@ -948,7 +1236,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
948
1236
|
}
|
|
949
1237
|
};
|
|
950
1238
|
recordNested(body);
|
|
951
|
-
walkExpressions(body, (child) => {
|
|
1239
|
+
walkExpressions(body, inside, (child) => {
|
|
952
1240
|
if (child.type === "return") {
|
|
953
1241
|
const first = children(child)[0];
|
|
954
1242
|
const returned = first?.type === "argument_list" ? children(first)[0] : first;
|
|
@@ -956,7 +1244,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
956
1244
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
957
1245
|
}
|
|
958
1246
|
}
|
|
959
|
-
if (child.type === "call") {
|
|
1247
|
+
if (child.type === "call" || isBareCall(child, method)) {
|
|
960
1248
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
961
1249
|
}
|
|
962
1250
|
});
|
|
@@ -1024,37 +1312,64 @@ function emitScopeWrites(emitter, method, body) {
|
|
|
1024
1312
|
}
|
|
1025
1313
|
}
|
|
1026
1314
|
function emitCandidates(emitter, key, group) {
|
|
1315
|
+
let unstated = false;
|
|
1316
|
+
let stated = 0;
|
|
1027
1317
|
for (const write of group.writes) {
|
|
1028
1318
|
if (write.value === null) {
|
|
1029
1319
|
add(emitter, "writesUnstated", key);
|
|
1320
|
+
unstated = true;
|
|
1030
1321
|
continue;
|
|
1031
1322
|
}
|
|
1032
1323
|
if (write.fromParameter) {
|
|
1324
|
+
unstated = true;
|
|
1033
1325
|
continue;
|
|
1034
1326
|
}
|
|
1035
1327
|
if (!describeWrite(emitter, write).narrowsName) {
|
|
1036
1328
|
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
1329
|
+
stated += 1;
|
|
1037
1330
|
}
|
|
1038
1331
|
}
|
|
1332
|
+
if (!unstated && stated > 0) {
|
|
1333
|
+
add(emitter, "writesAllStated", key);
|
|
1334
|
+
}
|
|
1039
1335
|
}
|
|
1040
1336
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1337
|
+
var INITIALIZE_METHOD = "initialize";
|
|
1338
|
+
function receiverKeyOf(filePath, method, classKey) {
|
|
1339
|
+
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
1340
|
+
}
|
|
1341
|
+
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1342
|
+
function mixedInConstants(body, callName) {
|
|
1343
|
+
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
1344
|
+
}
|
|
1345
|
+
function emitMixinFacts(emitter, classKey, body, callName) {
|
|
1346
|
+
for (const mixin of mixedInConstants(body, callName)) {
|
|
1347
|
+
add(emitter, "extends", classKey, valueKey(emitter, mixin));
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1041
1350
|
function emitClassFacts(emitter, cls) {
|
|
1042
1351
|
const classKey = nodeId(emitter.filePath, cls);
|
|
1043
1352
|
add(emitter, "objectValue", classKey);
|
|
1353
|
+
const body = field(cls, "body");
|
|
1354
|
+
if (body !== null) {
|
|
1355
|
+
emitMixinFacts(emitter, classKey, body, PREPEND_CALL);
|
|
1356
|
+
emitMixinFacts(emitter, classKey, body, INCLUDE_CALL);
|
|
1357
|
+
}
|
|
1044
1358
|
const superclass = field(cls, "superclass");
|
|
1045
1359
|
const base = superclass === null ? null : children(superclass)[0] ?? null;
|
|
1046
1360
|
if (base !== null) {
|
|
1047
1361
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
1048
1362
|
add(emitter, "extendsNamed", classKey, base.text);
|
|
1049
1363
|
}
|
|
1050
|
-
const body = field(cls, "body");
|
|
1051
1364
|
const collected = /* @__PURE__ */ new Map();
|
|
1052
1365
|
const within = {
|
|
1053
1366
|
...emitter,
|
|
1054
1367
|
selfKey: classKey,
|
|
1368
|
+
insideMethod: false,
|
|
1055
1369
|
instanceWrites: collected
|
|
1056
1370
|
};
|
|
1057
|
-
|
|
1371
|
+
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
1372
|
+
for (const statement of statements) {
|
|
1058
1373
|
if (statement.type === "assignment") {
|
|
1059
1374
|
const left = field(statement, "left");
|
|
1060
1375
|
const right = field(statement, "right");
|
|
@@ -1075,24 +1390,47 @@ function emitClassFacts(emitter, cls) {
|
|
|
1075
1390
|
continue;
|
|
1076
1391
|
}
|
|
1077
1392
|
if (!METHOD_TYPES.has(statement.type)) {
|
|
1393
|
+
emitExpressionFacts(within, statement);
|
|
1078
1394
|
continue;
|
|
1079
1395
|
}
|
|
1080
|
-
const
|
|
1396
|
+
const stored = /* @__PURE__ */ new Map();
|
|
1397
|
+
const receiverKey = receiverKeyOf(emitter.filePath, statement, classKey);
|
|
1398
|
+
if (receiverKey !== classKey) {
|
|
1399
|
+
add(emitter, "instanceOf", receiverKey, classKey);
|
|
1400
|
+
}
|
|
1401
|
+
const funcKey = emitMethodFacts(
|
|
1402
|
+
{
|
|
1403
|
+
...within,
|
|
1404
|
+
selfKey: receiverKey,
|
|
1405
|
+
insideMethod: receiverKey !== classKey,
|
|
1406
|
+
instanceWrites: stored
|
|
1407
|
+
},
|
|
1408
|
+
statement
|
|
1409
|
+
);
|
|
1081
1410
|
const name = field(statement, "name");
|
|
1082
1411
|
if (name !== null) {
|
|
1083
1412
|
add(emitter, "holdsProperty", classKey, name.text, funcKey);
|
|
1413
|
+
if (name.text === INITIALIZE_METHOD) {
|
|
1414
|
+
add(emitter, "initializes", classKey, funcKey);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
const methodBody = field(statement, "body");
|
|
1418
|
+
if (methodBody !== null) {
|
|
1419
|
+
emitInstanceStores(within, funcKey, methodBody, stored);
|
|
1084
1420
|
}
|
|
1085
1421
|
}
|
|
1086
1422
|
emitInstanceWrites(within, classKey, collected);
|
|
1087
1423
|
return classKey;
|
|
1088
1424
|
}
|
|
1089
|
-
function emitValueFacts(db, filePath, root) {
|
|
1425
|
+
function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
1090
1426
|
const emitter = {
|
|
1091
1427
|
db,
|
|
1092
1428
|
filePath,
|
|
1093
1429
|
enclosing: null,
|
|
1094
1430
|
selfKey: null,
|
|
1095
|
-
|
|
1431
|
+
insideMethod: false,
|
|
1432
|
+
instanceWrites: null,
|
|
1433
|
+
bodyBlocks
|
|
1096
1434
|
};
|
|
1097
1435
|
const declaresName = (child, key) => {
|
|
1098
1436
|
const name = field(child, "name");
|
|
@@ -1132,7 +1470,7 @@ var OWN_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1132
1470
|
"module",
|
|
1133
1471
|
"singleton_class"
|
|
1134
1472
|
]);
|
|
1135
|
-
var
|
|
1473
|
+
var BLOCK_TYPES4 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1136
1474
|
var METHOD_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1137
1475
|
var NAME_TYPES = /* @__PURE__ */ new Set([
|
|
1138
1476
|
"identifier",
|
|
@@ -1171,7 +1509,7 @@ function rubyLowering(options) {
|
|
|
1171
1509
|
statement: statementOf,
|
|
1172
1510
|
siteOf,
|
|
1173
1511
|
functionOf,
|
|
1174
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1512
|
+
writtenTo: (node, site) => context === null ? null : context.writtenTo(node, site),
|
|
1175
1513
|
callable: (node) => {
|
|
1176
1514
|
if (!isInlinableCall(node)) {
|
|
1177
1515
|
return null;
|
|
@@ -1641,7 +1979,7 @@ function isTailExpression(node) {
|
|
|
1641
1979
|
if (owner.type === "block" && owner.parent?.type === "lambda") {
|
|
1642
1980
|
return true;
|
|
1643
1981
|
}
|
|
1644
|
-
if (METHOD_TYPES2.has(owner.type) ||
|
|
1982
|
+
if (METHOD_TYPES2.has(owner.type) || BLOCK_TYPES4.has(owner.type)) {
|
|
1645
1983
|
return true;
|
|
1646
1984
|
}
|
|
1647
1985
|
if (owner.type === "when") {
|
|
@@ -1660,7 +1998,7 @@ function isRoot(node) {
|
|
|
1660
1998
|
if (OWN_ROOT_TYPES.has(node.type)) {
|
|
1661
1999
|
return true;
|
|
1662
2000
|
}
|
|
1663
|
-
return
|
|
2001
|
+
return BLOCK_TYPES4.has(node.type) && node.parent?.type === "call";
|
|
1664
2002
|
}
|
|
1665
2003
|
function isPathStatement(node, parent) {
|
|
1666
2004
|
if (node.type === "elsif") {
|
|
@@ -1688,17 +2026,32 @@ function siteOf(node) {
|
|
|
1688
2026
|
current = parent;
|
|
1689
2027
|
}
|
|
1690
2028
|
}
|
|
2029
|
+
var BARE_SPLAT_TYPES = /* @__PURE__ */ new Set(["splat_parameter", "hash_splat_parameter"]);
|
|
1691
2030
|
function parametersOf2(parameters) {
|
|
1692
2031
|
if (parameters === null) {
|
|
1693
2032
|
return [];
|
|
1694
2033
|
}
|
|
1695
|
-
|
|
2034
|
+
const declared = [];
|
|
2035
|
+
let position = 0;
|
|
2036
|
+
for (const parameter of named(parameters)) {
|
|
2037
|
+
if (BARE_SPLAT_TYPES.has(parameter.type) && field(parameter, "name") === null) {
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
1696
2040
|
if (parameter.type === "identifier") {
|
|
1697
|
-
|
|
2041
|
+
declared.push({ name: parameter.text, default: null, position });
|
|
2042
|
+
} else {
|
|
2043
|
+
const name = field(parameter, "name");
|
|
2044
|
+
if (name !== null) {
|
|
2045
|
+
declared.push({
|
|
2046
|
+
name: name.text,
|
|
2047
|
+
default: field(parameter, "value"),
|
|
2048
|
+
position
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
1698
2051
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
2052
|
+
position += 1;
|
|
2053
|
+
}
|
|
2054
|
+
return declared;
|
|
1702
2055
|
}
|
|
1703
2056
|
function functionOf(node) {
|
|
1704
2057
|
if (node.type === "program") {
|
|
@@ -1714,7 +2067,7 @@ function functionOf(node) {
|
|
|
1714
2067
|
body: bodyOf(block)
|
|
1715
2068
|
};
|
|
1716
2069
|
}
|
|
1717
|
-
if (
|
|
2070
|
+
if (BLOCK_TYPES4.has(node.type)) {
|
|
1718
2071
|
return {
|
|
1719
2072
|
parameters: parametersOf2(field(node, "parameters")),
|
|
1720
2073
|
body: bodyOf(node)
|
|
@@ -2088,25 +2441,56 @@ var rubyRows = [
|
|
|
2088
2441
|
|
|
2089
2442
|
// src/values/evaluator.ts
|
|
2090
2443
|
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2444
|
+
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2091
2445
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2446
|
+
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2092
2447
|
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2093
2448
|
function bindEvaluator(db, nodes) {
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
);
|
|
2449
|
+
for (const entry of nodes.files) {
|
|
2450
|
+
filesByTree.set(entry.root.tree, entry.file);
|
|
2451
|
+
}
|
|
2452
|
+
const context = contextOver(db, nodes);
|
|
2453
|
+
contexts.set(db, context);
|
|
2454
|
+
evaluators.set(db, new import_values2.Evaluator(rubyLowering({ context, rows: rubyRows })));
|
|
2455
|
+
}
|
|
2456
|
+
function writtenNodeOf(node, db, site) {
|
|
2457
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2458
|
+
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2100
2459
|
}
|
|
2101
|
-
function evaluatedValue(node, db, bindings) {
|
|
2460
|
+
function evaluatedValue(node, db, bindings, site) {
|
|
2102
2461
|
const evaluator = evaluatorFor(node, db);
|
|
2103
2462
|
if (bindings === void 0) {
|
|
2104
|
-
return (0, import_values2.force)(
|
|
2463
|
+
return (0, import_values2.force)(
|
|
2464
|
+
site === void 0 ? evaluator.evaluate(node) : evaluator.evaluate(node, { site })
|
|
2465
|
+
);
|
|
2105
2466
|
}
|
|
2106
2467
|
const supplied = new Map(
|
|
2107
2468
|
[...bindings].map(([name, value]) => [name, (0, import_values2.text)(value)])
|
|
2108
2469
|
);
|
|
2109
|
-
return (0, import_values2.force)(
|
|
2470
|
+
return (0, import_values2.force)(
|
|
2471
|
+
evaluator.evaluate(node, {
|
|
2472
|
+
bindings: supplied,
|
|
2473
|
+
...site === void 0 ? {} : { site }
|
|
2474
|
+
})
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2477
|
+
function constructionSitesOf(node, db) {
|
|
2478
|
+
const cls = enclosingClass(node);
|
|
2479
|
+
const file = cls === null ? void 0 : filesByTree.get(cls.tree);
|
|
2480
|
+
if (db === void 0 || cls === null || file === void 0) {
|
|
2481
|
+
return [];
|
|
2482
|
+
}
|
|
2483
|
+
return constructionSites(db, nodeId(file, cls));
|
|
2484
|
+
}
|
|
2485
|
+
function enclosingClass(node) {
|
|
2486
|
+
let current = node.parent;
|
|
2487
|
+
while (current !== null) {
|
|
2488
|
+
if (current.type === "class") {
|
|
2489
|
+
return current;
|
|
2490
|
+
}
|
|
2491
|
+
current = current.parent;
|
|
2492
|
+
}
|
|
2493
|
+
return null;
|
|
2110
2494
|
}
|
|
2111
2495
|
function stringValueOf(node, db, bindings) {
|
|
2112
2496
|
return (0, import_values2.literalOf)(evaluatedValue(node, db, bindings));
|
|
@@ -2158,15 +2542,13 @@ function contextOver(db, nodes) {
|
|
|
2158
2542
|
}
|
|
2159
2543
|
const fileOf = (node) => filesByRoot.get(node.tree.rootNode.id)?.file ?? null;
|
|
2160
2544
|
return {
|
|
2161
|
-
writtenTo: (node) => {
|
|
2545
|
+
writtenTo: (node, site) => {
|
|
2162
2546
|
const file = fileOf(node);
|
|
2163
2547
|
if (file === null) {
|
|
2164
2548
|
return null;
|
|
2165
2549
|
}
|
|
2166
|
-
const
|
|
2167
|
-
|
|
2168
|
-
readKey(file, node, enclosingMethod(node))
|
|
2169
|
-
);
|
|
2550
|
+
const key = readKey(file, node, enclosingMethod(node));
|
|
2551
|
+
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2170
2552
|
return answer === null ? null : (0, import_resolution4.nodeOfKey)(rootsByFile, answer);
|
|
2171
2553
|
},
|
|
2172
2554
|
callable: (call) => {
|
|
@@ -2184,125 +2566,141 @@ function contextOver(db, nodes) {
|
|
|
2184
2566
|
};
|
|
2185
2567
|
}
|
|
2186
2568
|
|
|
2187
|
-
// src/
|
|
2188
|
-
var
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
var byTree = /* @__PURE__ */ new WeakMap();
|
|
2193
|
-
function defineMethodNames(body) {
|
|
2194
|
-
let perTree = byTree.get(body.tree);
|
|
2195
|
-
if (perTree === void 0) {
|
|
2196
|
-
perTree = /* @__PURE__ */ new Map();
|
|
2197
|
-
byTree.set(body.tree, perTree);
|
|
2198
|
-
}
|
|
2199
|
-
const cached = perTree.get(body.id);
|
|
2200
|
-
if (cached !== void 0) {
|
|
2201
|
-
return cached;
|
|
2202
|
-
}
|
|
2203
|
-
const read = readNames(body);
|
|
2204
|
-
perTree.set(body.id, read);
|
|
2205
|
-
return read;
|
|
2206
|
-
}
|
|
2207
|
-
function readNames(body) {
|
|
2208
|
-
const calls = bareCalls(body, DEFINE_METHOD_CALL);
|
|
2209
|
-
if (calls.length === 0) {
|
|
2210
|
-
return NOTHING;
|
|
2569
|
+
// src/values/literals.ts
|
|
2570
|
+
var import_values4 = require("@suss/values");
|
|
2571
|
+
function literalElementsIn(value) {
|
|
2572
|
+
if (value.kind !== "sequence") {
|
|
2573
|
+
return null;
|
|
2211
2574
|
}
|
|
2212
|
-
const
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
unreadable = true;
|
|
2218
|
-
continue;
|
|
2575
|
+
const elements = [];
|
|
2576
|
+
for (const item of value.items) {
|
|
2577
|
+
const literal = (0, import_values4.literalOf)((0, import_values4.force)(item.value));
|
|
2578
|
+
if (literal === null) {
|
|
2579
|
+
return null;
|
|
2219
2580
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2581
|
+
elements.push(literal);
|
|
2582
|
+
}
|
|
2583
|
+
return elements;
|
|
2584
|
+
}
|
|
2585
|
+
function literalElementsOf(node, db) {
|
|
2586
|
+
return literalElementsIn(evaluatedValue(node, db));
|
|
2587
|
+
}
|
|
2588
|
+
function namesOf(node, db) {
|
|
2589
|
+
const value = evaluatedValue(node, db);
|
|
2590
|
+
const single = (0, import_values4.literalOf)(value);
|
|
2591
|
+
return single === null ? literalElementsIn(value) : [single];
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
// src/defineMethod.ts
|
|
2595
|
+
var NOTHING = {
|
|
2596
|
+
names: /* @__PURE__ */ new Set(),
|
|
2597
|
+
patterns: [],
|
|
2598
|
+
unreadable: false
|
|
2599
|
+
};
|
|
2600
|
+
function couldBeDefined(defined, name) {
|
|
2601
|
+
return defined.unreadable || defined.patterns.some((pattern) => pattern.test(name));
|
|
2602
|
+
}
|
|
2603
|
+
function definedNamesOf(dynamic, classKey) {
|
|
2604
|
+
return dynamic?.get(classKey) ?? NOTHING;
|
|
2605
|
+
}
|
|
2606
|
+
function readDynamicNames(db, rootsByFile) {
|
|
2607
|
+
const byClass = /* @__PURE__ */ new Map();
|
|
2608
|
+
for (const [classKey, nameKey2] of db.facts("definesMethodFrom")) {
|
|
2609
|
+
const key = String(classKey);
|
|
2610
|
+
byClass.set(
|
|
2611
|
+
key,
|
|
2612
|
+
foldReading(
|
|
2613
|
+
byClass.get(key) ?? NOTHING,
|
|
2614
|
+
readOne(db, rootsByFile, String(nameKey2))
|
|
2615
|
+
)
|
|
2616
|
+
);
|
|
2617
|
+
}
|
|
2618
|
+
for (const [classKey, reading] of byClass) {
|
|
2619
|
+
for (const name of reading.names) {
|
|
2620
|
+
db.add("declaresName", [classKey, name]);
|
|
2222
2621
|
}
|
|
2223
2622
|
}
|
|
2224
|
-
return
|
|
2623
|
+
return byClass;
|
|
2225
2624
|
}
|
|
2226
|
-
function
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
if (first === void 0) {
|
|
2230
|
-
return null;
|
|
2625
|
+
function foldReading(soFar, reading) {
|
|
2626
|
+
if (reading === null) {
|
|
2627
|
+
return { ...soFar, unreadable: true };
|
|
2231
2628
|
}
|
|
2629
|
+
return {
|
|
2630
|
+
names: /* @__PURE__ */ new Set([...soFar.names, ...reading.names]),
|
|
2631
|
+
patterns: [...soFar.patterns, ...reading.patterns],
|
|
2632
|
+
unreadable: soFar.unreadable
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
function readOne(db, rootsByFile, nameKey2) {
|
|
2636
|
+
const node = (0, import_resolution5.nodeOfKey)(rootsByFile, nameKey2);
|
|
2232
2637
|
const names = [];
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2638
|
+
const patterns = [];
|
|
2639
|
+
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2640
|
+
const value = node === null ? (0, import_values5.hole)(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2641
|
+
const name = (0, import_values5.literalOf)(value);
|
|
2642
|
+
if (name !== null) {
|
|
2643
|
+
names.push(name);
|
|
2644
|
+
continue;
|
|
2645
|
+
}
|
|
2646
|
+
const pattern = patternOf(value);
|
|
2647
|
+
if (pattern === null) {
|
|
2240
2648
|
return null;
|
|
2241
2649
|
}
|
|
2242
|
-
|
|
2650
|
+
patterns.push(pattern);
|
|
2243
2651
|
}
|
|
2244
|
-
return names;
|
|
2652
|
+
return { names, patterns };
|
|
2245
2653
|
}
|
|
2246
|
-
function
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2654
|
+
function patternOf(value) {
|
|
2655
|
+
const pieces = (0, import_values5.piecesOf)(value);
|
|
2656
|
+
let source = "^";
|
|
2657
|
+
let readSomething = false;
|
|
2658
|
+
for (const piece of pieces) {
|
|
2659
|
+
if (piece.kind === "hole") {
|
|
2660
|
+
source += "[\\s\\S]*";
|
|
2251
2661
|
continue;
|
|
2252
2662
|
}
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
}
|
|
2257
|
-
return turns;
|
|
2258
|
-
}
|
|
2259
|
-
function blocksAround(call) {
|
|
2260
|
-
const blocks = [];
|
|
2261
|
-
let current = call.parent;
|
|
2262
|
-
while (current !== null && !OWN_BODY_TYPES.has(current.type)) {
|
|
2263
|
-
const parent = current.parent;
|
|
2264
|
-
if (BLOCK_TYPES3.has(current.type) && parent?.type === "call") {
|
|
2265
|
-
blocks.unshift({ block: current, call: parent });
|
|
2663
|
+
const options = piece.options.filter((option) => option.length > 0);
|
|
2664
|
+
if (options.length === 0) {
|
|
2665
|
+
continue;
|
|
2266
2666
|
}
|
|
2267
|
-
|
|
2667
|
+
readSomething = true;
|
|
2668
|
+
source += `(?:${options.map(escapeForPattern).join("|")})`;
|
|
2268
2669
|
}
|
|
2269
|
-
return
|
|
2670
|
+
return readSomething ? new RegExp(`${source}$`) : null;
|
|
2270
2671
|
}
|
|
2271
|
-
function
|
|
2272
|
-
|
|
2273
|
-
const receiver = field(call, "receiver");
|
|
2274
|
-
const method = field(call, "method")?.text;
|
|
2275
|
-
if (receiver === null || method === void 0 || !LOOP_METHODS.has(method)) {
|
|
2276
|
-
return null;
|
|
2277
|
-
}
|
|
2278
|
-
const parameters = field(block, "parameters");
|
|
2279
|
-
const [element, index] = parameters === null ? [] : bodyStatements(parameters).map((parameter) => parameter.text);
|
|
2280
|
-
const elements = element === void 0 ? null : literalElementsOf(receiver);
|
|
2281
|
-
if (element === void 0 || elements === null) {
|
|
2282
|
-
return null;
|
|
2283
|
-
}
|
|
2284
|
-
return elements.map((value, position) => {
|
|
2285
|
-
const binding = /* @__PURE__ */ new Map([[element, value]]);
|
|
2286
|
-
if (index !== void 0) {
|
|
2287
|
-
binding.set(index, String(position));
|
|
2288
|
-
}
|
|
2289
|
-
return binding;
|
|
2290
|
-
});
|
|
2672
|
+
function escapeForPattern(text2) {
|
|
2673
|
+
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2291
2674
|
}
|
|
2292
|
-
function
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2675
|
+
function loopTurns(db, rootsByFile, nameKey2) {
|
|
2676
|
+
let turns = [/* @__PURE__ */ new Map()];
|
|
2677
|
+
for (const over of listsBehind(db, rootsByFile, nameKey2)) {
|
|
2678
|
+
turns = turns.flatMap(
|
|
2679
|
+
(base) => over.elements.map((value, position) => {
|
|
2680
|
+
const binding = new Map([...base, [over.element, value]]);
|
|
2681
|
+
if (over.index !== "") {
|
|
2682
|
+
binding.set(over.index, String(position));
|
|
2683
|
+
}
|
|
2684
|
+
return binding;
|
|
2685
|
+
})
|
|
2686
|
+
);
|
|
2296
2687
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2688
|
+
return turns;
|
|
2689
|
+
}
|
|
2690
|
+
function listsBehind(db, rootsByFile, nameKey2) {
|
|
2691
|
+
const found = [];
|
|
2692
|
+
for (const [, element, index, overKey] of db.lookup(
|
|
2693
|
+
"nameTurnsOn",
|
|
2694
|
+
0,
|
|
2695
|
+
nameKey2
|
|
2696
|
+
)) {
|
|
2697
|
+
const node = (0, import_resolution5.nodeOfKey)(rootsByFile, String(overKey));
|
|
2698
|
+
const elements = node === null ? null : literalElementsOf(node, db);
|
|
2699
|
+
if (elements !== null) {
|
|
2700
|
+
found.push({ element: String(element), index: String(index), elements });
|
|
2302
2701
|
}
|
|
2303
|
-
elements.push(literal);
|
|
2304
2702
|
}
|
|
2305
|
-
return
|
|
2703
|
+
return found;
|
|
2306
2704
|
}
|
|
2307
2705
|
|
|
2308
2706
|
// src/scope.ts
|
|
@@ -2431,8 +2829,6 @@ function graphqlTypeNameFromQualified(qualifiedName, convention) {
|
|
|
2431
2829
|
}
|
|
2432
2830
|
|
|
2433
2831
|
// src/ancestry.ts
|
|
2434
|
-
var INCLUDE_CALL = "include";
|
|
2435
|
-
var PREPEND_CALL = "prepend";
|
|
2436
2832
|
async function reachDefinition(qualifiedName, lookup) {
|
|
2437
2833
|
const filePath = resolveConstantFile(
|
|
2438
2834
|
lookup.root,
|
|
@@ -2559,7 +2955,7 @@ function superclassCandidatesOf(blocks) {
|
|
|
2559
2955
|
}
|
|
2560
2956
|
return [];
|
|
2561
2957
|
}
|
|
2562
|
-
function methodInAncestry(ancestry, name) {
|
|
2958
|
+
function methodInAncestry(ancestry, name, read = {}) {
|
|
2563
2959
|
for (const entry of ancestry) {
|
|
2564
2960
|
if (entry.type === "root") {
|
|
2565
2961
|
return { type: "none" };
|
|
@@ -2571,7 +2967,7 @@ function methodInAncestry(ancestry, name) {
|
|
|
2571
2967
|
cause: "unreadAncestor"
|
|
2572
2968
|
};
|
|
2573
2969
|
}
|
|
2574
|
-
const found = definitionIn(entry.blocks, name);
|
|
2970
|
+
const found = definitionIn(entry.blocks, name, read);
|
|
2575
2971
|
if (found.method !== null && found.block !== null) {
|
|
2576
2972
|
return { type: "found", method: found.method, block: found.block };
|
|
2577
2973
|
}
|
|
@@ -2585,7 +2981,7 @@ function methodInAncestry(ancestry, name) {
|
|
|
2585
2981
|
}
|
|
2586
2982
|
return { type: "none" };
|
|
2587
2983
|
}
|
|
2588
|
-
function definitionIn(blocks, name) {
|
|
2984
|
+
function definitionIn(blocks, name, read) {
|
|
2589
2985
|
let method = null;
|
|
2590
2986
|
let block = null;
|
|
2591
2987
|
let definedDynamically = false;
|
|
@@ -2595,14 +2991,17 @@ function definitionIn(blocks, name) {
|
|
|
2595
2991
|
if (body === null) {
|
|
2596
2992
|
continue;
|
|
2597
2993
|
}
|
|
2598
|
-
const found = instanceMethodsByName(body).get(name);
|
|
2994
|
+
const found = instanceMethodsByName(body, read.bodyBlocks).get(name);
|
|
2599
2995
|
if (found !== void 0) {
|
|
2600
2996
|
method = found;
|
|
2601
2997
|
block = candidate;
|
|
2602
2998
|
}
|
|
2603
|
-
const defined =
|
|
2999
|
+
const defined = definedNamesOf(
|
|
3000
|
+
read.dynamicNames,
|
|
3001
|
+
nodeId(candidate.file, candidate.info.node)
|
|
3002
|
+
);
|
|
2604
3003
|
definedDynamically ||= defined.names.has(name);
|
|
2605
|
-
unreadableDefine ||= defined
|
|
3004
|
+
unreadableDefine ||= couldBeDefined(defined, name);
|
|
2606
3005
|
}
|
|
2607
3006
|
return { method, block, definedDynamically, unreadableDefine };
|
|
2608
3007
|
}
|
|
@@ -2631,107 +3030,11 @@ var import_extractor5 = require("@suss/extractor");
|
|
|
2631
3030
|
|
|
2632
3031
|
// src/clientCalls.ts
|
|
2633
3032
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
2634
|
-
var
|
|
3033
|
+
var import_values9 = require("@suss/values");
|
|
2635
3034
|
|
|
2636
3035
|
// src/paths/effects.ts
|
|
2637
3036
|
var import_extractor2 = require("@suss/extractor");
|
|
2638
|
-
|
|
2639
|
-
// src/paths/bareCalls.ts
|
|
2640
|
-
var PARAMETER_LIST_TYPES = [
|
|
2641
|
-
"method_parameters",
|
|
2642
|
-
"block_parameters",
|
|
2643
|
-
"lambda_parameters",
|
|
2644
|
-
"parameters"
|
|
2645
|
-
];
|
|
2646
|
-
function isFieldOf(parent, node, name) {
|
|
2647
|
-
return field(parent, name)?.id === node.id;
|
|
2648
|
-
}
|
|
2649
|
-
function namedAt(parent, node) {
|
|
2650
|
-
return isFieldOf(parent, node, "name");
|
|
2651
|
-
}
|
|
2652
|
-
function always() {
|
|
2653
|
-
return true;
|
|
2654
|
-
}
|
|
2655
|
-
var SPELLS_A_NAME = {
|
|
2656
|
-
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
2657
|
-
method: namedAt,
|
|
2658
|
-
singleton_method: namedAt,
|
|
2659
|
-
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2660
|
-
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2661
|
-
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
2662
|
-
optional_parameter: namedAt,
|
|
2663
|
-
splat_parameter: namedAt,
|
|
2664
|
-
hash_splat_parameter: namedAt,
|
|
2665
|
-
keyword_parameter: namedAt,
|
|
2666
|
-
block_parameter: namedAt,
|
|
2667
|
-
destructured_parameter: always,
|
|
2668
|
-
left_assignment_list: always,
|
|
2669
|
-
rest_assignment: always,
|
|
2670
|
-
destructured_left_assignment: always,
|
|
2671
|
-
exception_variable: always,
|
|
2672
|
-
undef: always,
|
|
2673
|
-
alias: always,
|
|
2674
|
-
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
2675
|
-
};
|
|
2676
|
-
function addNamesUnder(node, names) {
|
|
2677
|
-
if (node === null) {
|
|
2678
|
-
return;
|
|
2679
|
-
}
|
|
2680
|
-
if (node.type === "identifier") {
|
|
2681
|
-
names.add(node.text);
|
|
2682
|
-
return;
|
|
2683
|
-
}
|
|
2684
|
-
for (const child of bodyStatements(node)) {
|
|
2685
|
-
addNamesUnder(child, names);
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
function parameterNames2(node, names) {
|
|
2689
|
-
for (const parameter of bodyStatements(node)) {
|
|
2690
|
-
if (parameter.type === "identifier") {
|
|
2691
|
-
names.add(parameter.text);
|
|
2692
|
-
continue;
|
|
2693
|
-
}
|
|
2694
|
-
const declared = field(parameter, "name");
|
|
2695
|
-
if (declared !== null && declared.type === "identifier") {
|
|
2696
|
-
names.add(declared.text);
|
|
2697
|
-
continue;
|
|
2698
|
-
}
|
|
2699
|
-
addNamesUnder(parameter, names);
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
function assignedNames(node, names) {
|
|
2703
|
-
addNamesUnder(field(node, "left"), names);
|
|
2704
|
-
}
|
|
2705
|
-
var BINDS_A_NAME = {
|
|
2706
|
-
assignment: assignedNames,
|
|
2707
|
-
operator_assignment: assignedNames,
|
|
2708
|
-
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
2709
|
-
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
2710
|
-
...Object.fromEntries(
|
|
2711
|
-
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames2])
|
|
2712
|
-
)
|
|
2713
|
-
};
|
|
2714
|
-
function localNamesIn(definitionNode) {
|
|
2715
|
-
const names = /* @__PURE__ */ new Set();
|
|
2716
|
-
const visit = (node) => {
|
|
2717
|
-
BINDS_A_NAME[node.type]?.(node, names);
|
|
2718
|
-
for (const child of bodyStatements(node)) {
|
|
2719
|
-
visit(child);
|
|
2720
|
-
}
|
|
2721
|
-
};
|
|
2722
|
-
visit(definitionNode);
|
|
2723
|
-
return names;
|
|
2724
|
-
}
|
|
2725
|
-
function isBareMethodCall(node, locals) {
|
|
2726
|
-
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
2727
|
-
return false;
|
|
2728
|
-
}
|
|
2729
|
-
const parent = node.parent;
|
|
2730
|
-
if (parent === null) {
|
|
2731
|
-
return false;
|
|
2732
|
-
}
|
|
2733
|
-
return SPELLS_A_NAME[parent.type]?.(parent, node) !== true;
|
|
2734
|
-
}
|
|
3037
|
+
var import_values7 = require("@suss/values");
|
|
2735
3038
|
|
|
2736
3039
|
// src/paths/lowering.ts
|
|
2737
3040
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -3012,30 +3315,28 @@ function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
|
3012
3315
|
|
|
3013
3316
|
// src/paths/effects.ts
|
|
3014
3317
|
var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
3015
|
-
var
|
|
3318
|
+
var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
3016
3319
|
"body_statement",
|
|
3017
3320
|
"then",
|
|
3018
3321
|
"else",
|
|
3019
3322
|
"do_block",
|
|
3020
3323
|
"block"
|
|
3021
|
-
]);
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
return
|
|
3034
|
-
}
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
3038
|
-
};
|
|
3324
|
+
]);
|
|
3325
|
+
function literalArgOf(node, facts) {
|
|
3326
|
+
const value = evaluatedValue(node, facts);
|
|
3327
|
+
const text2 = (0, import_values7.literalOf)(value);
|
|
3328
|
+
if (text2 !== null) {
|
|
3329
|
+
return { kind: "string", value: text2 };
|
|
3330
|
+
}
|
|
3331
|
+
const constant2 = (0, import_values7.constantOf)(value);
|
|
3332
|
+
if (typeof constant2 === "number") {
|
|
3333
|
+
return { kind: "number", value: constant2 };
|
|
3334
|
+
}
|
|
3335
|
+
if (typeof constant2 === "boolean") {
|
|
3336
|
+
return { kind: "boolean", value: constant2 };
|
|
3337
|
+
}
|
|
3338
|
+
return null;
|
|
3339
|
+
}
|
|
3039
3340
|
function children2(node) {
|
|
3040
3341
|
return node.namedChildren.filter((child) => child !== null);
|
|
3041
3342
|
}
|
|
@@ -3119,36 +3420,40 @@ function calleeText(call) {
|
|
|
3119
3420
|
}
|
|
3120
3421
|
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
3121
3422
|
}
|
|
3122
|
-
function argOf(node) {
|
|
3123
|
-
const literal =
|
|
3124
|
-
if (literal !== null
|
|
3423
|
+
function argOf(node, facts) {
|
|
3424
|
+
const literal = literalArgOf(node, facts);
|
|
3425
|
+
if (literal !== null) {
|
|
3125
3426
|
return literal;
|
|
3126
3427
|
}
|
|
3127
3428
|
if (node.type === "call" && !isArglessReceiverCall(node)) {
|
|
3128
|
-
return {
|
|
3429
|
+
return {
|
|
3430
|
+
kind: "call",
|
|
3431
|
+
callee: calleeText(node),
|
|
3432
|
+
args: argsOf(node, facts)
|
|
3433
|
+
};
|
|
3129
3434
|
}
|
|
3130
3435
|
return { kind: "identifier", name: node.text };
|
|
3131
3436
|
}
|
|
3132
|
-
function argsOf(call) {
|
|
3437
|
+
function argsOf(call, facts) {
|
|
3133
3438
|
const args = field(call, "arguments");
|
|
3134
3439
|
if (args === null) {
|
|
3135
3440
|
return [];
|
|
3136
3441
|
}
|
|
3137
3442
|
return children2(args).map(
|
|
3138
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3443
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child, facts) : argOf(child, facts)
|
|
3139
3444
|
);
|
|
3140
3445
|
}
|
|
3141
3446
|
function enclosingStatement(call, body) {
|
|
3142
3447
|
let current = call;
|
|
3143
3448
|
while (current.parent !== null) {
|
|
3144
|
-
if (
|
|
3449
|
+
if (BLOCK_TYPES5.has(current.parent.type) || current.parent.id === body.id) {
|
|
3145
3450
|
return current;
|
|
3146
3451
|
}
|
|
3147
3452
|
current = current.parent;
|
|
3148
3453
|
}
|
|
3149
3454
|
return current;
|
|
3150
3455
|
}
|
|
3151
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS) {
|
|
3456
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3152
3457
|
const body = field(definitionNode, "body");
|
|
3153
3458
|
if (body === null) {
|
|
3154
3459
|
return [];
|
|
@@ -3188,7 +3493,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3188
3493
|
return {
|
|
3189
3494
|
type: "invocation",
|
|
3190
3495
|
callee: calleeText(call),
|
|
3191
|
-
args: argsOf(call),
|
|
3496
|
+
args: argsOf(call, facts),
|
|
3192
3497
|
async: false,
|
|
3193
3498
|
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3194
3499
|
};
|
|
@@ -3197,7 +3502,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3197
3502
|
|
|
3198
3503
|
// src/responseStatus.ts
|
|
3199
3504
|
var import_extractor3 = require("@suss/extractor");
|
|
3200
|
-
var
|
|
3505
|
+
var import_values8 = require("@suss/values");
|
|
3201
3506
|
|
|
3202
3507
|
// src/paths/predicates.ts
|
|
3203
3508
|
var OPERATORS = {
|
|
@@ -3266,7 +3571,7 @@ function binaryOf(node) {
|
|
|
3266
3571
|
return opaqueOf(node);
|
|
3267
3572
|
}
|
|
3268
3573
|
function valueRefOf(node) {
|
|
3269
|
-
const literal =
|
|
3574
|
+
const literal = literalOf5(node);
|
|
3270
3575
|
if (literal !== null) {
|
|
3271
3576
|
return literal;
|
|
3272
3577
|
}
|
|
@@ -3279,7 +3584,7 @@ function valueRefOf(node) {
|
|
|
3279
3584
|
}
|
|
3280
3585
|
return { type: "unresolved", sourceText: node.text };
|
|
3281
3586
|
}
|
|
3282
|
-
function
|
|
3587
|
+
function literalOf5(node) {
|
|
3283
3588
|
if (node.type === "integer") {
|
|
3284
3589
|
const value = Number.parseInt(node.text, 10);
|
|
3285
3590
|
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
@@ -3322,13 +3627,13 @@ function statusArgumentOf(call, declaration) {
|
|
|
3322
3627
|
}
|
|
3323
3628
|
return args.positional[declaration.statusArgument] ?? null;
|
|
3324
3629
|
}
|
|
3325
|
-
function statusNumberOf(node, names) {
|
|
3326
|
-
const value = evaluatedValue(node);
|
|
3327
|
-
const constant2 = (0,
|
|
3630
|
+
function statusNumberOf(node, names, facts) {
|
|
3631
|
+
const value = evaluatedValue(node, facts);
|
|
3632
|
+
const constant2 = (0, import_values8.constantOf)(value);
|
|
3328
3633
|
if (typeof constant2 === "number") {
|
|
3329
3634
|
return constant2;
|
|
3330
3635
|
}
|
|
3331
|
-
const name = (0,
|
|
3636
|
+
const name = (0, import_values8.literalOf)(value);
|
|
3332
3637
|
return name === null ? null : names[name] ?? null;
|
|
3333
3638
|
}
|
|
3334
3639
|
function declarationsByName(declarations) {
|
|
@@ -3372,12 +3677,12 @@ function collectReturns(node, found) {
|
|
|
3372
3677
|
}
|
|
3373
3678
|
return found;
|
|
3374
3679
|
}
|
|
3375
|
-
function readingOfCall(call, declaration, names) {
|
|
3680
|
+
function readingOfCall(call, declaration, names, facts) {
|
|
3376
3681
|
const argument = statusArgumentOf(call, declaration);
|
|
3377
3682
|
if (argument === null) {
|
|
3378
3683
|
return declaration.defaultStatusCode === void 0 ? import_extractor3.absentReading : (0, import_extractor3.writtenReading)(declaration.defaultStatusCode, rangeOf(call));
|
|
3379
3684
|
}
|
|
3380
|
-
const status = statusNumberOf(argument, names);
|
|
3685
|
+
const status = statusNumberOf(argument, names, facts);
|
|
3381
3686
|
if (status === null) {
|
|
3382
3687
|
return (0, import_extractor3.unreadableReading)(
|
|
3383
3688
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
@@ -3521,7 +3826,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
3521
3826
|
const outcomes = [];
|
|
3522
3827
|
for (const terminal of terminals) {
|
|
3523
3828
|
const declaration = declarationOf(terminal, byName, locals);
|
|
3524
|
-
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
3829
|
+
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames, options.facts);
|
|
3525
3830
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
3526
3831
|
outcomes.push({
|
|
3527
3832
|
conditions: path7.map(conditionOf),
|
|
@@ -3555,11 +3860,9 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3555
3860
|
continue;
|
|
3556
3861
|
}
|
|
3557
3862
|
for (const call of callsUnder(method)) {
|
|
3558
|
-
const request
|
|
3559
|
-
|
|
3560
|
-
continue;
|
|
3863
|
+
for (const request of requestCalls(call, pattern, options)) {
|
|
3864
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3561
3865
|
}
|
|
3562
|
-
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3563
3866
|
}
|
|
3564
3867
|
}
|
|
3565
3868
|
return units;
|
|
@@ -3588,36 +3891,60 @@ function callsUnder(node, found = []) {
|
|
|
3588
3891
|
}
|
|
3589
3892
|
return found;
|
|
3590
3893
|
}
|
|
3591
|
-
function
|
|
3894
|
+
function requestCalls(call, pattern, options) {
|
|
3592
3895
|
const receiver = field(call, "receiver");
|
|
3593
3896
|
const called = field(call, "method")?.text;
|
|
3594
|
-
if (receiver === null || called === void 0) {
|
|
3595
|
-
return
|
|
3897
|
+
if (receiver === null || called === void 0 || !isLibraryReceiver(receiver, pattern, options)) {
|
|
3898
|
+
return [];
|
|
3596
3899
|
}
|
|
3597
|
-
const
|
|
3598
|
-
|
|
3599
|
-
|
|
3900
|
+
const read = (site) => requestCall(
|
|
3901
|
+
call,
|
|
3902
|
+
called,
|
|
3903
|
+
receiverPrefix(receiver, pattern, options, site),
|
|
3904
|
+
pattern,
|
|
3905
|
+
options,
|
|
3906
|
+
site
|
|
3907
|
+
);
|
|
3908
|
+
const plain = read();
|
|
3909
|
+
if (plain !== null && !(0, import_behavioral_ir.hasNameHole)(plain.path)) {
|
|
3910
|
+
return [plain];
|
|
3911
|
+
}
|
|
3912
|
+
const byBoundary = /* @__PURE__ */ new Map();
|
|
3913
|
+
for (const site of constructionSitesOf(call, options.facts)) {
|
|
3914
|
+
const stated = read(site);
|
|
3915
|
+
if (stated !== null) {
|
|
3916
|
+
byBoundary.set(`${stated.method} ${stated.path}`, stated);
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
if (byBoundary.size > 0) {
|
|
3920
|
+
return [...byBoundary.values()];
|
|
3600
3921
|
}
|
|
3922
|
+
return plain === null ? [] : [plain];
|
|
3923
|
+
}
|
|
3924
|
+
function requestCall(call, called, prefix, pattern, options, site) {
|
|
3601
3925
|
const args = readCallArgs(field(call, "arguments"));
|
|
3602
3926
|
const verb = pattern.verbMethodNames[called];
|
|
3603
3927
|
if (verb !== void 0) {
|
|
3604
|
-
const path8 = urlIn(args, pattern, options);
|
|
3928
|
+
const path8 = urlIn(args, pattern, options, site);
|
|
3605
3929
|
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
3606
3930
|
}
|
|
3607
3931
|
const sent = pattern.requestObject;
|
|
3608
3932
|
if (sent === void 0 || sent.attribute !== called) {
|
|
3609
3933
|
return null;
|
|
3610
3934
|
}
|
|
3611
|
-
const built = requestBuilt(args.positional[0],
|
|
3935
|
+
const built = requestBuilt(args.positional[0], sent, options, site);
|
|
3612
3936
|
if (built === null) {
|
|
3613
3937
|
return null;
|
|
3614
3938
|
}
|
|
3615
|
-
const path7 = pathAt(built.url, options);
|
|
3939
|
+
const path7 = pathAt(built.url, options, site);
|
|
3616
3940
|
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3617
3941
|
}
|
|
3618
|
-
function requestBuilt(argument,
|
|
3619
|
-
|
|
3620
|
-
|
|
3942
|
+
function requestBuilt(argument, sent, options, site) {
|
|
3943
|
+
if (argument === void 0) {
|
|
3944
|
+
return null;
|
|
3945
|
+
}
|
|
3946
|
+
const written = writtenNodeOf(argument, options.facts, site) ?? argument;
|
|
3947
|
+
if (written.type !== "call") {
|
|
3621
3948
|
return null;
|
|
3622
3949
|
}
|
|
3623
3950
|
const requestClass = field(written, "receiver")?.text;
|
|
@@ -3628,28 +3955,18 @@ function requestBuilt(argument, method, sent) {
|
|
|
3628
3955
|
const args = readCallArgs(field(written, "arguments"));
|
|
3629
3956
|
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3630
3957
|
}
|
|
3631
|
-
function
|
|
3632
|
-
|
|
3633
|
-
return null;
|
|
3634
|
-
}
|
|
3635
|
-
const value = assignedValue(node.text, method);
|
|
3636
|
-
return value !== null && value.type === "call" ? value : null;
|
|
3958
|
+
function isLibraryReceiver(receiver, pattern, options) {
|
|
3959
|
+
return namesConstant(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
3637
3960
|
}
|
|
3638
|
-
function receiverPrefix(receiver,
|
|
3639
|
-
|
|
3640
|
-
return "";
|
|
3641
|
-
}
|
|
3642
|
-
if (receiver.type !== "identifier") {
|
|
3643
|
-
return null;
|
|
3644
|
-
}
|
|
3645
|
-
const built = builtBy(receiver.text, method, root, pattern);
|
|
3961
|
+
function receiverPrefix(receiver, pattern, options, site) {
|
|
3962
|
+
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
3646
3963
|
if (built === null) {
|
|
3647
|
-
return
|
|
3964
|
+
return "";
|
|
3648
3965
|
}
|
|
3649
3966
|
const args = readCallArgs(field(built, "arguments"));
|
|
3650
3967
|
const keyword = pattern.builderUrlKeyword;
|
|
3651
3968
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
3652
|
-
const path7 = base === void 0 ? null : (0,
|
|
3969
|
+
const path7 = base === void 0 ? null : (0, import_values9.pathOf)(evaluatedValue(base, options.facts, void 0, site));
|
|
3653
3970
|
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3654
3971
|
}
|
|
3655
3972
|
function trimmed(path7) {
|
|
@@ -3661,61 +3978,28 @@ function namesConstant(receiver, constantName2) {
|
|
|
3661
3978
|
}
|
|
3662
3979
|
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
3663
3980
|
}
|
|
3664
|
-
function
|
|
3981
|
+
function builderCallBehind(receiver, pattern, facts) {
|
|
3665
3982
|
const builders = pattern.receiverBuilders ?? [];
|
|
3666
3983
|
if (builders.length === 0) {
|
|
3667
3984
|
return null;
|
|
3668
3985
|
}
|
|
3669
|
-
const
|
|
3670
|
-
if (
|
|
3986
|
+
const written = writtenNodeOf(receiver, facts);
|
|
3987
|
+
if (written === null || written.type !== "call" || !builders.includes(field(written, "method")?.text ?? "")) {
|
|
3671
3988
|
return null;
|
|
3672
3989
|
}
|
|
3673
|
-
const
|
|
3674
|
-
return
|
|
3990
|
+
const constant2 = field(written, "receiver");
|
|
3991
|
+
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3675
3992
|
}
|
|
3676
|
-
function
|
|
3677
|
-
const defined = methodDefinitions(root).find(
|
|
3678
|
-
(candidate) => field(candidate, "name")?.text === name
|
|
3679
|
-
);
|
|
3680
|
-
if (defined === void 0) {
|
|
3681
|
-
return null;
|
|
3682
|
-
}
|
|
3683
|
-
const body = field(defined, "body");
|
|
3684
|
-
const statements = body === null ? [] : body.namedChildren;
|
|
3685
|
-
const last = statements[statements.length - 1];
|
|
3686
|
-
if (last === void 0) {
|
|
3687
|
-
return null;
|
|
3688
|
-
}
|
|
3689
|
-
if (last.type === "operator_assignment" || last.type === "assignment") {
|
|
3690
|
-
return field(last, "right");
|
|
3691
|
-
}
|
|
3692
|
-
return last.type === "return" ? last.namedChildren[0] ?? null : last;
|
|
3693
|
-
}
|
|
3694
|
-
function assignedValue(name, method) {
|
|
3695
|
-
const body = field(method, "body");
|
|
3696
|
-
if (body === null) {
|
|
3697
|
-
return null;
|
|
3698
|
-
}
|
|
3699
|
-
for (const statement of runStatements(body)) {
|
|
3700
|
-
if (statement.type !== "assignment") {
|
|
3701
|
-
continue;
|
|
3702
|
-
}
|
|
3703
|
-
if (field(statement, "left")?.text === name) {
|
|
3704
|
-
return field(statement, "right");
|
|
3705
|
-
}
|
|
3706
|
-
}
|
|
3707
|
-
return null;
|
|
3708
|
-
}
|
|
3709
|
-
function urlIn(args, pattern, options) {
|
|
3993
|
+
function urlIn(args, pattern, options, site) {
|
|
3710
3994
|
const keyword = pattern.url.keyword;
|
|
3711
3995
|
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3712
|
-
return pathAt(written, options);
|
|
3996
|
+
return pathAt(written, options, site);
|
|
3713
3997
|
}
|
|
3714
|
-
function pathAt(written, options) {
|
|
3998
|
+
function pathAt(written, options, site) {
|
|
3715
3999
|
if (written === void 0) {
|
|
3716
4000
|
return null;
|
|
3717
4001
|
}
|
|
3718
|
-
const path7 = (0,
|
|
4002
|
+
const path7 = (0, import_values9.pathOf)(evaluatedValue(written, options.facts, void 0, site));
|
|
3719
4003
|
return path7 === void 0 || (0, import_behavioral_ir.namesNothing)(path7) ? null : path7;
|
|
3720
4004
|
}
|
|
3721
4005
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
@@ -3738,15 +4022,15 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
3738
4022
|
recognition: pack.name
|
|
3739
4023
|
}),
|
|
3740
4024
|
parameters: [],
|
|
3741
|
-
branches: callerBranches(method, range),
|
|
4025
|
+
branches: callerBranches(method, range, options.facts),
|
|
3742
4026
|
...responseAccessors(pattern),
|
|
3743
4027
|
bodyContent: "statements",
|
|
3744
4028
|
dependencyCalls: [],
|
|
3745
4029
|
declaredContract: null
|
|
3746
4030
|
};
|
|
3747
4031
|
}
|
|
3748
|
-
function callerBranches(method, range) {
|
|
3749
|
-
const effects = invocationEffects(method);
|
|
4032
|
+
function callerBranches(method, range, facts) {
|
|
4033
|
+
const effects = invocationEffects(method, void 0, void 0, facts);
|
|
3750
4034
|
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3751
4035
|
}
|
|
3752
4036
|
function responseAccessors(pattern) {
|
|
@@ -3884,7 +4168,7 @@ function isDefaultedAt(node) {
|
|
|
3884
4168
|
}
|
|
3885
4169
|
|
|
3886
4170
|
// src/filters.ts
|
|
3887
|
-
function controllerFilters(pattern, ancestry) {
|
|
4171
|
+
function controllerFilters(pattern, ancestry, read = {}) {
|
|
3888
4172
|
const forms = pattern.filters ?? [];
|
|
3889
4173
|
if (forms.length === 0) {
|
|
3890
4174
|
return [];
|
|
@@ -3904,7 +4188,7 @@ function controllerFilters(pattern, ancestry) {
|
|
|
3904
4188
|
}
|
|
3905
4189
|
const form = byName.get(called);
|
|
3906
4190
|
if (form !== void 0) {
|
|
3907
|
-
for (const declaration of declarationsOf(statement, form)) {
|
|
4191
|
+
for (const declaration of declarationsOf(statement, form, read.facts)) {
|
|
3908
4192
|
declared = [
|
|
3909
4193
|
...declared.filter((earlier) => !sameFilter(earlier, declaration)),
|
|
3910
4194
|
declaration
|
|
@@ -3914,12 +4198,12 @@ function controllerFilters(pattern, ancestry) {
|
|
|
3914
4198
|
}
|
|
3915
4199
|
const skipped = skipNames.get(called);
|
|
3916
4200
|
if (skipped !== void 0) {
|
|
3917
|
-
declared = applySkips(declared, skipsOf(statement, skipped));
|
|
4201
|
+
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
3918
4202
|
}
|
|
3919
4203
|
}
|
|
3920
4204
|
const resolved = [];
|
|
3921
4205
|
for (const declaration of declared) {
|
|
3922
|
-
const found = methodInAncestry(ancestry, declaration.methodName);
|
|
4206
|
+
const found = methodInAncestry(ancestry, declaration.methodName, read);
|
|
3923
4207
|
if (found.type !== "found") {
|
|
3924
4208
|
continue;
|
|
3925
4209
|
}
|
|
@@ -3951,14 +4235,14 @@ function filterReference(filter, displayPath) {
|
|
|
3951
4235
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
3952
4236
|
};
|
|
3953
4237
|
}
|
|
3954
|
-
function filterUnit(filter, pattern, displayPath, body) {
|
|
4238
|
+
function filterUnit(filter, pattern, displayPath, body, facts) {
|
|
3955
4239
|
const range = rangeOf(filter.method);
|
|
3956
4240
|
const branches = responseBranches(
|
|
3957
4241
|
filter.method,
|
|
3958
4242
|
pattern,
|
|
3959
4243
|
body.effects ?? [],
|
|
3960
4244
|
body.extraEffects,
|
|
3961
|
-
{ fallthrough: "handOn" }
|
|
4245
|
+
{ fallthrough: "handOn", facts }
|
|
3962
4246
|
);
|
|
3963
4247
|
return {
|
|
3964
4248
|
identity: {
|
|
@@ -4006,30 +4290,30 @@ function calledName(statement) {
|
|
|
4006
4290
|
}
|
|
4007
4291
|
return field(statement, "method")?.text ?? null;
|
|
4008
4292
|
}
|
|
4009
|
-
function declarationsOf(statement, filter) {
|
|
4293
|
+
function declarationsOf(statement, filter, facts) {
|
|
4010
4294
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4011
4295
|
const keywords = filter.actionKeywords;
|
|
4012
|
-
const only = keywords === void 0 ? null :
|
|
4013
|
-
const except = keywords === void 0 ? null :
|
|
4014
|
-
return methodNamesOf(args, filter).map((methodName) => ({
|
|
4296
|
+
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
4297
|
+
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
4298
|
+
return methodNamesOf(args, filter, facts).map((methodName) => ({
|
|
4015
4299
|
filter,
|
|
4016
4300
|
methodName,
|
|
4017
4301
|
only,
|
|
4018
4302
|
except: except ?? /* @__PURE__ */ new Set()
|
|
4019
4303
|
}));
|
|
4020
4304
|
}
|
|
4021
|
-
function methodNamesOf(args, filter) {
|
|
4305
|
+
function methodNamesOf(args, filter, facts) {
|
|
4022
4306
|
if (filter.methodFrom === "withKeyword") {
|
|
4023
4307
|
const named2 = args.keyword.with;
|
|
4024
|
-
const value = named2 === void 0 ? null :
|
|
4308
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
4025
4309
|
return value === null ? [] : [value];
|
|
4026
4310
|
}
|
|
4027
|
-
return args.positional.map((arg) =>
|
|
4311
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
4028
4312
|
}
|
|
4029
|
-
function skipsOf(statement, filterName) {
|
|
4313
|
+
function skipsOf(statement, filterName, facts) {
|
|
4030
4314
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4031
|
-
const actions =
|
|
4032
|
-
return args.positional.map((arg) =>
|
|
4315
|
+
const actions = actionsUnder(args.keyword.only, facts);
|
|
4316
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
4033
4317
|
}
|
|
4034
4318
|
function sameFilter(one, other) {
|
|
4035
4319
|
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
@@ -4053,31 +4337,17 @@ function applySkips(declared, skips) {
|
|
|
4053
4337
|
}
|
|
4054
4338
|
return left;
|
|
4055
4339
|
}
|
|
4056
|
-
function
|
|
4340
|
+
function actionsUnder(node, facts) {
|
|
4057
4341
|
if (node === void 0) {
|
|
4058
4342
|
return null;
|
|
4059
4343
|
}
|
|
4060
|
-
const
|
|
4061
|
-
|
|
4062
|
-
return /* @__PURE__ */ new Set([single]);
|
|
4063
|
-
}
|
|
4064
|
-
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
4065
|
-
return null;
|
|
4066
|
-
}
|
|
4067
|
-
const names = /* @__PURE__ */ new Set();
|
|
4068
|
-
for (const child of bodyStatements(node)) {
|
|
4069
|
-
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
4070
|
-
const name = symbolValue(child) ?? bare;
|
|
4071
|
-
if (name !== null) {
|
|
4072
|
-
names.add(name);
|
|
4073
|
-
}
|
|
4074
|
-
}
|
|
4075
|
-
return names;
|
|
4344
|
+
const found = namesOf(node, facts);
|
|
4345
|
+
return found === null ? null : new Set(found);
|
|
4076
4346
|
}
|
|
4077
4347
|
|
|
4078
4348
|
// src/storage.ts
|
|
4079
4349
|
var import_ir_core = require("@suss/ir-core");
|
|
4080
|
-
var
|
|
4350
|
+
var import_resolution6 = require("@suss/resolution");
|
|
4081
4351
|
function children3(node) {
|
|
4082
4352
|
return node.namedChildren.filter((child) => child !== null);
|
|
4083
4353
|
}
|
|
@@ -4104,7 +4374,7 @@ function constantName(constant2) {
|
|
|
4104
4374
|
return constant2.type === "constant" ? constant2.text : compoundName(constant2);
|
|
4105
4375
|
}
|
|
4106
4376
|
function reachesBase(facts, classKey, bases) {
|
|
4107
|
-
(0,
|
|
4377
|
+
(0, import_resolution6.askResolution)(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
|
|
4108
4378
|
return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
|
|
4109
4379
|
}
|
|
4110
4380
|
function classBehind(facts, file, constant2) {
|
|
@@ -4123,28 +4393,95 @@ function modelPattern(constant2, file, options) {
|
|
|
4123
4393
|
(candidate) => reachesBase(options.facts, classKey, candidate.baseClasses)
|
|
4124
4394
|
);
|
|
4125
4395
|
}
|
|
4126
|
-
function
|
|
4127
|
-
const
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4396
|
+
function chainLinks(call) {
|
|
4397
|
+
const links = [];
|
|
4398
|
+
let current = call;
|
|
4399
|
+
while (current !== null && current.type === "call") {
|
|
4400
|
+
links.push(current);
|
|
4401
|
+
current = receiverOf(current);
|
|
4402
|
+
}
|
|
4403
|
+
return links;
|
|
4404
|
+
}
|
|
4405
|
+
function kindOfCall(pattern, method) {
|
|
4406
|
+
if (pattern.writes.includes(method)) {
|
|
4407
|
+
return "write";
|
|
4408
|
+
}
|
|
4409
|
+
if (pattern.reads.includes(method)) {
|
|
4410
|
+
return "read";
|
|
4411
|
+
}
|
|
4412
|
+
return void 0;
|
|
4413
|
+
}
|
|
4414
|
+
function someLibraryDefines(options, method) {
|
|
4415
|
+
return options.patterns.some(
|
|
4416
|
+
(pattern) => kindOfCall(pattern, method) !== void 0
|
|
4417
|
+
);
|
|
4418
|
+
}
|
|
4419
|
+
function argumentsOf2(call) {
|
|
4420
|
+
const args = field(call, "arguments");
|
|
4421
|
+
return args === null ? [] : children3(args);
|
|
4422
|
+
}
|
|
4423
|
+
function recordFieldsOf(argument, facts) {
|
|
4424
|
+
const value = evaluatedValue(argument, facts);
|
|
4425
|
+
return value.kind === "record" ? [...value.fields.keys()] : [];
|
|
4426
|
+
}
|
|
4427
|
+
function keywordKeys(call, facts) {
|
|
4428
|
+
const keys = [];
|
|
4429
|
+
for (const argument of argumentsOf2(call)) {
|
|
4430
|
+
if (argument.type !== "pair") {
|
|
4431
|
+
keys.push(...recordFieldsOf(argument, facts));
|
|
4432
|
+
continue;
|
|
4138
4433
|
}
|
|
4139
|
-
const
|
|
4140
|
-
|
|
4141
|
-
|
|
4434
|
+
const key = field(argument, "key");
|
|
4435
|
+
const name = key === null ? null : keyNameOf(key);
|
|
4436
|
+
if (name !== null) {
|
|
4437
|
+
keys.push(name);
|
|
4142
4438
|
}
|
|
4143
|
-
}
|
|
4144
|
-
|
|
4439
|
+
}
|
|
4440
|
+
return keys;
|
|
4441
|
+
}
|
|
4442
|
+
function keyNameOf(key) {
|
|
4443
|
+
return hashKeySymbolName(key) ?? symbolValue(key) ?? stringLiteralValue(key);
|
|
4444
|
+
}
|
|
4445
|
+
function hasPositionalArgument(call, facts) {
|
|
4446
|
+
return argumentsOf2(call).some(
|
|
4447
|
+
(argument) => argument.type !== "pair" && recordFieldsOf(argument, facts).length === 0
|
|
4448
|
+
);
|
|
4449
|
+
}
|
|
4450
|
+
function columnsAskedFor(call, pattern, facts) {
|
|
4451
|
+
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4452
|
+
return [];
|
|
4453
|
+
}
|
|
4454
|
+
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
4455
|
+
}
|
|
4456
|
+
function primaryKeySelector(call, pattern, facts) {
|
|
4457
|
+
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4458
|
+
if (byPrimaryKey === void 0) {
|
|
4459
|
+
return [];
|
|
4460
|
+
}
|
|
4461
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
|
|
4462
|
+
return picks ? [byPrimaryKey.column] : [];
|
|
4463
|
+
}
|
|
4464
|
+
function selectorOf(call, pattern, facts) {
|
|
4465
|
+
const picked = [];
|
|
4466
|
+
for (const link of chainLinks(call)) {
|
|
4467
|
+
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4468
|
+
if (pattern.reads.includes(methodOf(link))) {
|
|
4469
|
+
picked.push(...keywordKeys(link, facts));
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4145
4472
|
return [...new Set(picked)];
|
|
4146
4473
|
}
|
|
4147
|
-
function
|
|
4474
|
+
function fieldsOf(call, pattern, kind, facts) {
|
|
4475
|
+
if (kind === "write") {
|
|
4476
|
+
return [...new Set(keywordKeys(call, facts))];
|
|
4477
|
+
}
|
|
4478
|
+
return [
|
|
4479
|
+
...new Set(
|
|
4480
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern, facts))
|
|
4481
|
+
)
|
|
4482
|
+
];
|
|
4483
|
+
}
|
|
4484
|
+
function storageEffect(call, container, pattern, kind, selector, fields) {
|
|
4148
4485
|
const operation = methodOf(call);
|
|
4149
4486
|
return {
|
|
4150
4487
|
type: "interaction",
|
|
@@ -4158,37 +4495,14 @@ function storageEffect(call, container, pattern, kind, selector) {
|
|
|
4158
4495
|
interaction: {
|
|
4159
4496
|
class: "storage-access",
|
|
4160
4497
|
kind,
|
|
4161
|
-
fields
|
|
4498
|
+
fields,
|
|
4162
4499
|
operation,
|
|
4163
4500
|
...selector.length > 0 ? { selector } : {}
|
|
4164
4501
|
}
|
|
4165
4502
|
};
|
|
4166
4503
|
}
|
|
4167
|
-
function modelCallEffects(call, file, options) {
|
|
4168
|
-
const constant2 = rootConstant(call);
|
|
4169
|
-
if (constant2 === null) {
|
|
4170
|
-
return [];
|
|
4171
|
-
}
|
|
4172
|
-
const pattern = modelPattern(constant2, file, options);
|
|
4173
|
-
if (pattern === void 0) {
|
|
4174
|
-
return [];
|
|
4175
|
-
}
|
|
4176
|
-
const kind = pattern.writes.includes(methodOf(call)) ? "write" : "read";
|
|
4177
|
-
return [
|
|
4178
|
-
storageEffect(
|
|
4179
|
-
call,
|
|
4180
|
-
constantName(constant2),
|
|
4181
|
-
pattern,
|
|
4182
|
-
kind,
|
|
4183
|
-
selectorOf(call)
|
|
4184
|
-
)
|
|
4185
|
-
];
|
|
4186
|
-
}
|
|
4187
|
-
function writeMethods(patterns) {
|
|
4188
|
-
return new Set(patterns.flatMap((pattern) => pattern.writes));
|
|
4189
|
-
}
|
|
4190
4504
|
function classSettledOn(facts, key) {
|
|
4191
|
-
(0,
|
|
4505
|
+
(0, import_resolution6.askResolution)(facts, [key], "wanted", RUBY_PROGRAM);
|
|
4192
4506
|
const objects = new Set(
|
|
4193
4507
|
facts.lookup("wantedObjectOf", 0, key).map((row) => String(row[1]))
|
|
4194
4508
|
);
|
|
@@ -4203,27 +4517,64 @@ function declaredName(facts, classKey) {
|
|
|
4203
4517
|
function projectDeclares(facts, classKey, method) {
|
|
4204
4518
|
return facts.lookup("wantedDeclaredName", 0, classKey).some((row) => String(row[1]) === method);
|
|
4205
4519
|
}
|
|
4206
|
-
function
|
|
4520
|
+
function constantReceiver(constant2, file, facts) {
|
|
4521
|
+
const classKey = classBehind(facts, file, constant2);
|
|
4522
|
+
return classKey === void 0 ? void 0 : { classKey, container: constantName(constant2) };
|
|
4523
|
+
}
|
|
4524
|
+
function settledReceiver(receiver, file, facts, enclosing) {
|
|
4525
|
+
const classKey = classSettledOn(facts, readKey(file, receiver, enclosing));
|
|
4526
|
+
if (classKey === void 0) {
|
|
4527
|
+
return void 0;
|
|
4528
|
+
}
|
|
4529
|
+
const container = declaredName(facts, classKey);
|
|
4530
|
+
return container === void 0 ? void 0 : { classKey, container };
|
|
4531
|
+
}
|
|
4532
|
+
function receiverClass(call, file, options, enclosing) {
|
|
4533
|
+
const constant2 = rootConstant(call);
|
|
4534
|
+
if (constant2 !== null) {
|
|
4535
|
+
return constantReceiver(constant2, file, options.facts);
|
|
4536
|
+
}
|
|
4207
4537
|
const receiver = receiverOf(call);
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
return [];
|
|
4538
|
+
if (receiver === null) {
|
|
4539
|
+
return void 0;
|
|
4211
4540
|
}
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
)
|
|
4216
|
-
|
|
4541
|
+
return settledReceiver(receiver, file, options.facts, enclosing);
|
|
4542
|
+
}
|
|
4543
|
+
function libraryCallIn(call, options) {
|
|
4544
|
+
return chainLinks(call).find(
|
|
4545
|
+
(link) => someLibraryDefines(options, methodOf(link))
|
|
4546
|
+
) ?? null;
|
|
4547
|
+
}
|
|
4548
|
+
function modelCallEffects(call, file, options, enclosing) {
|
|
4549
|
+
const worked = libraryCallIn(call, options);
|
|
4550
|
+
if (worked === null) {
|
|
4217
4551
|
return [];
|
|
4218
4552
|
}
|
|
4219
|
-
const
|
|
4220
|
-
|
|
4221
|
-
)
|
|
4222
|
-
if (pattern === void 0 || projectDeclares(options.facts, classKey, method)) {
|
|
4553
|
+
const method = methodOf(worked);
|
|
4554
|
+
const target = receiverClass(worked, file, options, enclosing);
|
|
4555
|
+
if (target === void 0) {
|
|
4223
4556
|
return [];
|
|
4224
4557
|
}
|
|
4225
|
-
const
|
|
4226
|
-
|
|
4558
|
+
for (const pattern of options.patterns) {
|
|
4559
|
+
const kind = kindOfCall(pattern, method);
|
|
4560
|
+
if (kind === void 0 || !reachesBase(options.facts, target.classKey, pattern.baseClasses)) {
|
|
4561
|
+
continue;
|
|
4562
|
+
}
|
|
4563
|
+
if (projectDeclares(options.facts, target.classKey, method)) {
|
|
4564
|
+
return [];
|
|
4565
|
+
}
|
|
4566
|
+
return [
|
|
4567
|
+
storageEffect(
|
|
4568
|
+
worked,
|
|
4569
|
+
target.container,
|
|
4570
|
+
pattern,
|
|
4571
|
+
kind,
|
|
4572
|
+
selectorOf(worked, pattern, options.facts),
|
|
4573
|
+
fieldsOf(worked, pattern, kind, options.facts)
|
|
4574
|
+
)
|
|
4575
|
+
];
|
|
4576
|
+
}
|
|
4577
|
+
return [];
|
|
4227
4578
|
}
|
|
4228
4579
|
function isLoader(node, loader) {
|
|
4229
4580
|
if (node === null) {
|
|
@@ -4261,7 +4612,7 @@ function loaderCallEffects(call, file, options) {
|
|
|
4261
4612
|
const pattern = modelPattern(argument, file, options);
|
|
4262
4613
|
if (pattern !== void 0) {
|
|
4263
4614
|
effects.push(
|
|
4264
|
-
storageEffect(call, constantName(argument), pattern, "read", [])
|
|
4615
|
+
storageEffect(call, constantName(argument), pattern, "read", [], [])
|
|
4265
4616
|
);
|
|
4266
4617
|
}
|
|
4267
4618
|
}
|
|
@@ -4269,22 +4620,21 @@ function loaderCallEffects(call, file, options) {
|
|
|
4269
4620
|
return effects;
|
|
4270
4621
|
}
|
|
4271
4622
|
function effectsOfCall(call, file, options, enclosing) {
|
|
4272
|
-
const onModel = modelCallEffects(call, file, options);
|
|
4273
|
-
|
|
4274
|
-
return onModel;
|
|
4275
|
-
}
|
|
4276
|
-
const onInstance = rootConstant(call) === null ? instanceWriteEffects(call, file, options, enclosing) : [];
|
|
4277
|
-
return onInstance.length > 0 ? onInstance : loaderCallEffects(call, file, options);
|
|
4623
|
+
const onModel = modelCallEffects(call, file, options, enclosing);
|
|
4624
|
+
return onModel.length > 0 ? onModel : loaderCallEffects(call, file, options);
|
|
4278
4625
|
}
|
|
4279
4626
|
function storageClaims(call, file, options, enclosing = null) {
|
|
4280
4627
|
return options.patterns.length > 0 && effectsOfCall(call, file, options, enclosing).length > 0;
|
|
4281
4628
|
}
|
|
4282
4629
|
function receiverKeysToAsk(chains, file, options, enclosing) {
|
|
4283
|
-
const writes = writeMethods(options.patterns);
|
|
4284
4630
|
const keys = [];
|
|
4285
4631
|
for (const call of chains) {
|
|
4286
|
-
const
|
|
4287
|
-
if (
|
|
4632
|
+
const worked = libraryCallIn(call, options);
|
|
4633
|
+
if (worked === null || rootConstant(worked) !== null) {
|
|
4634
|
+
continue;
|
|
4635
|
+
}
|
|
4636
|
+
const receiver = receiverOf(worked);
|
|
4637
|
+
if (receiver !== null) {
|
|
4288
4638
|
keys.push(readKey(file, receiver, enclosing));
|
|
4289
4639
|
}
|
|
4290
4640
|
}
|
|
@@ -4302,7 +4652,7 @@ function storageEffects(calls, file, options, enclosing = null) {
|
|
|
4302
4652
|
}
|
|
4303
4653
|
}
|
|
4304
4654
|
const chains = calls.filter((call) => !partOfOne.has(call.id));
|
|
4305
|
-
(0,
|
|
4655
|
+
(0, import_resolution6.askResolution)(
|
|
4306
4656
|
options.facts,
|
|
4307
4657
|
receiverKeysToAsk(chains, file, options, enclosing),
|
|
4308
4658
|
"wanted",
|
|
@@ -4381,11 +4731,17 @@ function createFileCache(parse, readFile) {
|
|
|
4381
4731
|
}
|
|
4382
4732
|
};
|
|
4383
4733
|
}
|
|
4384
|
-
function fieldReadContext(pattern, cache, fileBlocks, bodyRead) {
|
|
4734
|
+
function fieldReadContext(pattern, cache, fileBlocks, bodyRead, facts) {
|
|
4385
4735
|
return {
|
|
4386
4736
|
pattern,
|
|
4387
4737
|
cache,
|
|
4388
4738
|
bodyRead,
|
|
4739
|
+
facts,
|
|
4740
|
+
read: {
|
|
4741
|
+
facts,
|
|
4742
|
+
bodyBlocks: bodyRead.bodyBlocks,
|
|
4743
|
+
dynamicNames: bodyRead.dynamicNames
|
|
4744
|
+
},
|
|
4389
4745
|
lookup: {
|
|
4390
4746
|
root: pattern.root,
|
|
4391
4747
|
pathConvention: pattern.pathConvention,
|
|
@@ -4468,7 +4824,13 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
4468
4824
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
4469
4825
|
return [];
|
|
4470
4826
|
}
|
|
4471
|
-
const ctx = fieldReadContext(
|
|
4827
|
+
const ctx = fieldReadContext(
|
|
4828
|
+
pattern,
|
|
4829
|
+
options.cache,
|
|
4830
|
+
fileBlocks,
|
|
4831
|
+
options,
|
|
4832
|
+
options.facts
|
|
4833
|
+
);
|
|
4472
4834
|
const ancestry = await ancestryOf(info.qualifiedName, ownBlocks, ctx.lookup);
|
|
4473
4835
|
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
4474
4836
|
return [];
|
|
@@ -4516,7 +4878,11 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4516
4878
|
])) {
|
|
4517
4879
|
return [];
|
|
4518
4880
|
}
|
|
4519
|
-
const filters = controllerFilters(pattern, ancestry
|
|
4881
|
+
const filters = controllerFilters(pattern, ancestry, {
|
|
4882
|
+
facts: options.facts,
|
|
4883
|
+
bodyBlocks: options.bodyBlocks,
|
|
4884
|
+
dynamicNames: options.dynamicNames
|
|
4885
|
+
});
|
|
4520
4886
|
const units = [];
|
|
4521
4887
|
for (const filter of filters) {
|
|
4522
4888
|
const displayPath = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
@@ -4524,7 +4890,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4524
4890
|
filter,
|
|
4525
4891
|
pattern,
|
|
4526
4892
|
displayPath,
|
|
4527
|
-
bodyOfMethod(filter.method, filter.file, options)
|
|
4893
|
+
bodyOfMethod(filter.method, filter.file, options),
|
|
4894
|
+
options.facts
|
|
4528
4895
|
);
|
|
4529
4896
|
units.push(raw);
|
|
4530
4897
|
options.onReachSeed?.(raw, {
|
|
@@ -4561,7 +4928,10 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4561
4928
|
});
|
|
4562
4929
|
};
|
|
4563
4930
|
const own = /* @__PURE__ */ new Set();
|
|
4564
|
-
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4931
|
+
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4932
|
+
ownBlocks,
|
|
4933
|
+
options.bodyBlocks
|
|
4934
|
+
)) {
|
|
4565
4935
|
own.add(actionName);
|
|
4566
4936
|
emitAction(actionName, method, block);
|
|
4567
4937
|
}
|
|
@@ -4571,7 +4941,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4571
4941
|
continue;
|
|
4572
4942
|
}
|
|
4573
4943
|
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4574
|
-
entry.blocks
|
|
4944
|
+
entry.blocks,
|
|
4945
|
+
options.bodyBlocks
|
|
4575
4946
|
)) {
|
|
4576
4947
|
if (seen.has(actionName)) {
|
|
4577
4948
|
continue;
|
|
@@ -4585,14 +4956,17 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4585
4956
|
}
|
|
4586
4957
|
return units;
|
|
4587
4958
|
}
|
|
4588
|
-
function publicInstanceMethods(blocks) {
|
|
4959
|
+
function publicInstanceMethods(blocks, bodyBlocks) {
|
|
4589
4960
|
const found = [];
|
|
4590
4961
|
for (const block of blocks) {
|
|
4591
4962
|
if (block.info.bodyNode === null) {
|
|
4592
4963
|
continue;
|
|
4593
4964
|
}
|
|
4594
4965
|
const visibility = instanceMethodVisibility(block.info.bodyNode);
|
|
4595
|
-
for (const [name, method] of instanceMethodsByName(
|
|
4966
|
+
for (const [name, method] of instanceMethodsByName(
|
|
4967
|
+
block.info.bodyNode,
|
|
4968
|
+
bodyBlocks
|
|
4969
|
+
)) {
|
|
4596
4970
|
if ((visibility.get(name) ?? "public") === "public") {
|
|
4597
4971
|
found.push([name, method, block]);
|
|
4598
4972
|
}
|
|
@@ -4608,7 +4982,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
4608
4982
|
method,
|
|
4609
4983
|
pattern,
|
|
4610
4984
|
body.effects ?? [],
|
|
4611
|
-
body.extraEffects
|
|
4985
|
+
body.extraEffects,
|
|
4986
|
+
{ facts: bodyRead.facts }
|
|
4612
4987
|
);
|
|
4613
4988
|
return {
|
|
4614
4989
|
identity: {
|
|
@@ -4738,7 +5113,8 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
4738
5113
|
const effects = invocationEffects(
|
|
4739
5114
|
method,
|
|
4740
5115
|
bodyRead.inheritedMethods,
|
|
4741
|
-
EVERY_ARGLESS_CALL
|
|
5116
|
+
EVERY_ARGLESS_CALL,
|
|
5117
|
+
bodyRead.facts
|
|
4742
5118
|
);
|
|
4743
5119
|
const storage = bodyRead.storage;
|
|
4744
5120
|
const extra = [
|
|
@@ -4855,7 +5231,7 @@ async function readFieldShape(symbol, callArgs, scope, ctx, ancestry, range) {
|
|
|
4855
5231
|
return {
|
|
4856
5232
|
contract: literalContract(callArgs, scope, ctx),
|
|
4857
5233
|
body: bodyFromLookup(
|
|
4858
|
-
methodInAncestry(ancestry, symbol),
|
|
5234
|
+
methodInAncestry(ancestry, symbol, ctx.read),
|
|
4859
5235
|
range,
|
|
4860
5236
|
"This field",
|
|
4861
5237
|
NO_METHOD_BEHIND_IT,
|
|
@@ -4900,7 +5276,7 @@ async function readWiredClass(ref, scope, ctx) {
|
|
|
4900
5276
|
return {
|
|
4901
5277
|
contract: readClassContract(ancestry, ctx.pattern),
|
|
4902
5278
|
body: bodyFromLookup(
|
|
4903
|
-
methodInAncestry(ancestry, ctx.pattern.resolverMethodName),
|
|
5279
|
+
methodInAncestry(ancestry, ctx.pattern.resolverMethodName, ctx.read),
|
|
4904
5280
|
range,
|
|
4905
5281
|
`This field's ${targetQualifiedName}`,
|
|
4906
5282
|
methodNotSettled(
|
|
@@ -5072,6 +5448,202 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
5072
5448
|
};
|
|
5073
5449
|
}
|
|
5074
5450
|
|
|
5451
|
+
// src/inflect.ts
|
|
5452
|
+
var SINGULAR_RULES = [
|
|
5453
|
+
[/s$/i, ""],
|
|
5454
|
+
[/(ss)$/i, "$1"],
|
|
5455
|
+
[/(n)ews$/i, "$1ews"],
|
|
5456
|
+
[/([ti])a$/i, "$1um"],
|
|
5457
|
+
[
|
|
5458
|
+
/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i,
|
|
5459
|
+
"$1sis"
|
|
5460
|
+
],
|
|
5461
|
+
[/(^analy)(sis|ses)$/i, "$1sis"],
|
|
5462
|
+
[/([^f])ves$/i, "$1fe"],
|
|
5463
|
+
[/(hive)s$/i, "$1"],
|
|
5464
|
+
[/(tive)s$/i, "$1"],
|
|
5465
|
+
[/([lr])ves$/i, "$1f"],
|
|
5466
|
+
[/([^aeiouy]|qu)ies$/i, "$1y"],
|
|
5467
|
+
[/(s)eries$/i, "$1eries"],
|
|
5468
|
+
[/(m)ovies$/i, "$1ovie"],
|
|
5469
|
+
[/(x|ch|ss|sh)es$/i, "$1"],
|
|
5470
|
+
[/^(m|l)ice$/i, "$1ouse"],
|
|
5471
|
+
[/(bus)(es)?$/i, "$1"],
|
|
5472
|
+
[/(o)es$/i, "$1"],
|
|
5473
|
+
[/(shoe)s$/i, "$1"],
|
|
5474
|
+
[/(cris|test)(is|es)$/i, "$1is"],
|
|
5475
|
+
[/^(a)x[ie]s$/i, "$1xis"],
|
|
5476
|
+
[/(octop|vir)(us|i)$/i, "$1us"],
|
|
5477
|
+
[/(alias|status)(es)?$/i, "$1"],
|
|
5478
|
+
[/^(ox)en/i, "$1"],
|
|
5479
|
+
[/(vert|ind)ices$/i, "$1ex"],
|
|
5480
|
+
[/(matr)ices$/i, "$1ix"],
|
|
5481
|
+
[/(quiz)zes$/i, "$1"],
|
|
5482
|
+
[/(database)s$/i, "$1"],
|
|
5483
|
+
// The words ActiveSupport calls irregular. It declares them after the
|
|
5484
|
+
// rules above, which is what puts them ahead in the search.
|
|
5485
|
+
[/(p)erson$/i, "$1erson"],
|
|
5486
|
+
[/(p)eople$/i, "$1erson"],
|
|
5487
|
+
[/(m)an$/i, "$1an"],
|
|
5488
|
+
[/(m)en$/i, "$1an"],
|
|
5489
|
+
[/(c)hild$/i, "$1hild"],
|
|
5490
|
+
[/(c)hildren$/i, "$1hild"],
|
|
5491
|
+
[/(s)ex$/i, "$1ex"],
|
|
5492
|
+
[/(s)exes$/i, "$1ex"],
|
|
5493
|
+
[/(m)ove$/i, "$1ove"],
|
|
5494
|
+
[/(m)oves$/i, "$1ove"],
|
|
5495
|
+
[/(z)ombie$/i, "$1ombie"],
|
|
5496
|
+
[/(z)ombies$/i, "$1ombie"]
|
|
5497
|
+
];
|
|
5498
|
+
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
5499
|
+
"equipment",
|
|
5500
|
+
"information",
|
|
5501
|
+
"rice",
|
|
5502
|
+
"money",
|
|
5503
|
+
"species",
|
|
5504
|
+
"series",
|
|
5505
|
+
"fish",
|
|
5506
|
+
"sheep",
|
|
5507
|
+
"jeans",
|
|
5508
|
+
"police"
|
|
5509
|
+
]);
|
|
5510
|
+
function singularize(word, project) {
|
|
5511
|
+
if (UNCOUNTABLE.has(word.toLowerCase()) || (project?.uncountable ?? []).some(
|
|
5512
|
+
(uncountable) => uncountable.toLowerCase() === word.toLowerCase()
|
|
5513
|
+
)) {
|
|
5514
|
+
return word;
|
|
5515
|
+
}
|
|
5516
|
+
const own = projectSingular(word, project);
|
|
5517
|
+
if (own !== null) {
|
|
5518
|
+
return own;
|
|
5519
|
+
}
|
|
5520
|
+
for (let at = SINGULAR_RULES.length - 1; at >= 0; at--) {
|
|
5521
|
+
const [pattern, replacement] = SINGULAR_RULES[at];
|
|
5522
|
+
if (pattern.test(word)) {
|
|
5523
|
+
return word.replace(pattern, replacement);
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
return word;
|
|
5527
|
+
}
|
|
5528
|
+
function projectSingular(word, project) {
|
|
5529
|
+
const irregular = [...project?.irregular ?? []].reverse();
|
|
5530
|
+
for (const [plural, singular] of irregular) {
|
|
5531
|
+
if (plural.toLowerCase() === word.toLowerCase()) {
|
|
5532
|
+
return matchingCase(word, singular);
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
const rules = [...project?.singular ?? []].reverse();
|
|
5536
|
+
for (const [rule3, replacement] of rules) {
|
|
5537
|
+
const applied = applyRule(word, rule3, replacement);
|
|
5538
|
+
if (applied !== null) {
|
|
5539
|
+
return applied;
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5542
|
+
return null;
|
|
5543
|
+
}
|
|
5544
|
+
function applyRule(word, rule3, replacement) {
|
|
5545
|
+
const written = /^\/(.*)\/([a-z]*)$/s.exec(rule3);
|
|
5546
|
+
if (written === null) {
|
|
5547
|
+
return word.includes(rule3) ? word.replace(rule3, replacement) : null;
|
|
5548
|
+
}
|
|
5549
|
+
const flags = (written[2] ?? "").replace("m", "s").replace(/[^is]/g, "");
|
|
5550
|
+
const pattern = new RegExp(written[1] ?? "", flags);
|
|
5551
|
+
return pattern.test(word) ? word.replace(pattern, replacement.replace(/\\(\d)/g, "$$$1")) : null;
|
|
5552
|
+
}
|
|
5553
|
+
function matchingCase(word, replacement) {
|
|
5554
|
+
const first = word[0];
|
|
5555
|
+
if (first === void 0 || first !== first.toUpperCase()) {
|
|
5556
|
+
return replacement;
|
|
5557
|
+
}
|
|
5558
|
+
return replacement.charAt(0).toUpperCase() + replacement.slice(1);
|
|
5559
|
+
}
|
|
5560
|
+
function camelize(word, acronyms = []) {
|
|
5561
|
+
return word.split("_").map((part) => capitalize(part, acronyms)).join("");
|
|
5562
|
+
}
|
|
5563
|
+
function capitalize(part, acronyms) {
|
|
5564
|
+
if (part === "") {
|
|
5565
|
+
return "";
|
|
5566
|
+
}
|
|
5567
|
+
const acronym = acronyms.find(
|
|
5568
|
+
(known) => known.toLowerCase() === part.toLowerCase()
|
|
5569
|
+
);
|
|
5570
|
+
return acronym ?? part[0]?.toUpperCase() + part.slice(1);
|
|
5571
|
+
}
|
|
5572
|
+
function associationTargetName(name, plural, project) {
|
|
5573
|
+
return camelize(
|
|
5574
|
+
plural ? singularize(name, project) : name,
|
|
5575
|
+
project?.acronyms ?? []
|
|
5576
|
+
);
|
|
5577
|
+
}
|
|
5578
|
+
|
|
5579
|
+
// src/facts/associations.ts
|
|
5580
|
+
function declaredName2(argument) {
|
|
5581
|
+
if (argument === void 0) {
|
|
5582
|
+
return null;
|
|
5583
|
+
}
|
|
5584
|
+
if (argument.type === "simple_symbol") {
|
|
5585
|
+
return argument.text.slice(1);
|
|
5586
|
+
}
|
|
5587
|
+
return stringLiteralValue(argument);
|
|
5588
|
+
}
|
|
5589
|
+
function targetReference(key, written, nesting) {
|
|
5590
|
+
if (written.startsWith("::")) {
|
|
5591
|
+
return { key, written: written.slice(2), nesting: [] };
|
|
5592
|
+
}
|
|
5593
|
+
return { key, written, nesting };
|
|
5594
|
+
}
|
|
5595
|
+
function targetName(call, calls, name, plural, inflections) {
|
|
5596
|
+
const override = readCallArgs(field(call, "arguments")).keyword[calls.classNameKeyword];
|
|
5597
|
+
if (override === void 0) {
|
|
5598
|
+
return associationTargetName(name, plural, inflections);
|
|
5599
|
+
}
|
|
5600
|
+
return stringLiteralValue(override);
|
|
5601
|
+
}
|
|
5602
|
+
function associationsDeclaredIn(cls, classKey, nesting, calls, inflections) {
|
|
5603
|
+
const body = field(cls, "body");
|
|
5604
|
+
if (body === null || calls.length === 0) {
|
|
5605
|
+
return [];
|
|
5606
|
+
}
|
|
5607
|
+
const found = [];
|
|
5608
|
+
for (const statement of runStatements(body)) {
|
|
5609
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
5610
|
+
continue;
|
|
5611
|
+
}
|
|
5612
|
+
const method = field(statement, "method")?.text ?? "";
|
|
5613
|
+
for (const declaration of calls) {
|
|
5614
|
+
const plural = declaration.plural.includes(method);
|
|
5615
|
+
if (!plural && !declaration.singular.includes(method)) {
|
|
5616
|
+
continue;
|
|
5617
|
+
}
|
|
5618
|
+
const positional = readCallArgs(field(statement, "arguments")).positional;
|
|
5619
|
+
const name = declaredName2(positional[0]);
|
|
5620
|
+
if (name === null) {
|
|
5621
|
+
continue;
|
|
5622
|
+
}
|
|
5623
|
+
const written = targetName(
|
|
5624
|
+
statement,
|
|
5625
|
+
declaration,
|
|
5626
|
+
name,
|
|
5627
|
+
plural,
|
|
5628
|
+
inflections
|
|
5629
|
+
);
|
|
5630
|
+
if (written === null) {
|
|
5631
|
+
continue;
|
|
5632
|
+
}
|
|
5633
|
+
found.push({
|
|
5634
|
+
classKey,
|
|
5635
|
+
name,
|
|
5636
|
+
target: targetReference(
|
|
5637
|
+
`${classKey}#association:${name}`,
|
|
5638
|
+
written,
|
|
5639
|
+
nesting
|
|
5640
|
+
)
|
|
5641
|
+
});
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
return found;
|
|
5645
|
+
}
|
|
5646
|
+
|
|
5075
5647
|
// src/facts/constants.ts
|
|
5076
5648
|
function children4(node) {
|
|
5077
5649
|
return node.namedChildren.filter((child) => child !== null);
|
|
@@ -5106,20 +5678,32 @@ function isDeclaration(node, parent) {
|
|
|
5106
5678
|
}
|
|
5107
5679
|
return parent.type === "scope_resolution";
|
|
5108
5680
|
}
|
|
5109
|
-
function collectFileConstants(filePath, root) {
|
|
5681
|
+
function collectFileConstants(filePath, root, associationCalls = [], inflections) {
|
|
5110
5682
|
const definitions = [];
|
|
5111
5683
|
const references = [];
|
|
5684
|
+
const associations = [];
|
|
5112
5685
|
const walk = (node, parent, nesting) => {
|
|
5113
5686
|
if (NESTING_TYPES.has(node.type)) {
|
|
5114
5687
|
const name = field(node, "name");
|
|
5115
5688
|
const written = name === null ? null : writtenName(name);
|
|
5116
5689
|
if (written !== null) {
|
|
5690
|
+
const classKey = nodeId(filePath, node);
|
|
5117
5691
|
definitions.push({
|
|
5118
5692
|
qualifiedName: qualify(nesting, written),
|
|
5119
|
-
key:
|
|
5693
|
+
key: classKey,
|
|
5120
5694
|
kind: "declaration"
|
|
5121
5695
|
});
|
|
5122
5696
|
const inside = [...nesting, ...written.split("::")];
|
|
5697
|
+
for (const declared of associationsDeclaredIn(
|
|
5698
|
+
node,
|
|
5699
|
+
classKey,
|
|
5700
|
+
inside,
|
|
5701
|
+
associationCalls,
|
|
5702
|
+
inflections
|
|
5703
|
+
)) {
|
|
5704
|
+
associations.push(declared);
|
|
5705
|
+
references.push(declared.target);
|
|
5706
|
+
}
|
|
5123
5707
|
for (const child of children4(node)) {
|
|
5124
5708
|
walk(child, node, inside);
|
|
5125
5709
|
}
|
|
@@ -5151,7 +5735,7 @@ function collectFileConstants(filePath, root) {
|
|
|
5151
5735
|
}
|
|
5152
5736
|
};
|
|
5153
5737
|
walk(root, null, []);
|
|
5154
|
-
return { filePath, definitions, references };
|
|
5738
|
+
return { filePath, definitions, references, associations };
|
|
5155
5739
|
}
|
|
5156
5740
|
function emitConstantBindings(db, perFile) {
|
|
5157
5741
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -5175,6 +5759,13 @@ function emitConstantBindings(db, perFile) {
|
|
|
5175
5759
|
return found.length === 1 ? found[0].key : null;
|
|
5176
5760
|
};
|
|
5177
5761
|
for (const file of files) {
|
|
5762
|
+
for (const declared of file.associations) {
|
|
5763
|
+
db.add("declaresAssociation", [
|
|
5764
|
+
declared.classKey,
|
|
5765
|
+
declared.name,
|
|
5766
|
+
declared.target.key
|
|
5767
|
+
]);
|
|
5768
|
+
}
|
|
5178
5769
|
for (const reference of file.references) {
|
|
5179
5770
|
for (let depth = reference.nesting.length; depth >= 0; depth--) {
|
|
5180
5771
|
const candidate = qualify(
|
|
@@ -5199,7 +5790,7 @@ function emitConstantBindings(db, perFile) {
|
|
|
5199
5790
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
5200
5791
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
5201
5792
|
var import_datalog4 = require("@suss/datalog");
|
|
5202
|
-
var
|
|
5793
|
+
var import_resolution9 = require("@suss/resolution");
|
|
5203
5794
|
|
|
5204
5795
|
// src/parser.ts
|
|
5205
5796
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -5263,6 +5854,7 @@ var import_node_path4 = __toESM(require("path"), 1);
|
|
|
5263
5854
|
var import_behavioral_ir5 = require("@suss/behavioral-ir");
|
|
5264
5855
|
var import_datalog3 = require("@suss/datalog");
|
|
5265
5856
|
var import_extractor10 = require("@suss/extractor");
|
|
5857
|
+
var import_resolution8 = require("@suss/resolution");
|
|
5266
5858
|
|
|
5267
5859
|
// src/diagnostics.ts
|
|
5268
5860
|
var import_extractor6 = require("@suss/extractor");
|
|
@@ -5314,13 +5906,42 @@ function requiredRelativePath(node) {
|
|
|
5314
5906
|
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
5315
5907
|
}
|
|
5316
5908
|
|
|
5909
|
+
// src/pack.ts
|
|
5910
|
+
function inflectionsIn(packs) {
|
|
5911
|
+
const pooled = {
|
|
5912
|
+
acronyms: [],
|
|
5913
|
+
irregular: [],
|
|
5914
|
+
uncountable: [],
|
|
5915
|
+
singular: []
|
|
5916
|
+
};
|
|
5917
|
+
for (const pack of packs) {
|
|
5918
|
+
pooled.acronyms?.push(...pack.inflections?.acronyms ?? []);
|
|
5919
|
+
pooled.irregular?.push(...pack.inflections?.irregular ?? []);
|
|
5920
|
+
pooled.uncountable?.push(...pack.inflections?.uncountable ?? []);
|
|
5921
|
+
pooled.singular?.push(...pack.inflections?.singular ?? []);
|
|
5922
|
+
}
|
|
5923
|
+
return pooled;
|
|
5924
|
+
}
|
|
5925
|
+
function bodyBlocksIn(packs) {
|
|
5926
|
+
const pooled = /* @__PURE__ */ new Map();
|
|
5927
|
+
for (const pack of packs) {
|
|
5928
|
+
for (const declared of pack.bodyBlocks ?? []) {
|
|
5929
|
+
pooled.set(declared.name, {
|
|
5930
|
+
moduleOnly: declared.moduleOnly ?? false,
|
|
5931
|
+
definesClassMethods: declared.definesClassMethods ?? false
|
|
5932
|
+
});
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
return pooled;
|
|
5936
|
+
}
|
|
5937
|
+
|
|
5317
5938
|
// src/reach/closure.ts
|
|
5318
5939
|
var import_behavioral_ir4 = require("@suss/behavioral-ir");
|
|
5319
5940
|
var import_datalog2 = require("@suss/datalog");
|
|
5320
5941
|
var import_extractor8 = require("@suss/extractor");
|
|
5321
5942
|
|
|
5322
5943
|
// src/reach/resolveCallee.ts
|
|
5323
|
-
var
|
|
5944
|
+
var import_resolution7 = require("@suss/resolution");
|
|
5324
5945
|
var DYNAMIC_SEND_NAMES = /* @__PURE__ */ new Set(["send", "public_send", "__send__"]);
|
|
5325
5946
|
var INVOKES_RECEIVER = "call";
|
|
5326
5947
|
var STOP_FOR = {
|
|
@@ -5349,7 +5970,7 @@ function calleeSpellings(calls, ctx) {
|
|
|
5349
5970
|
}
|
|
5350
5971
|
return {
|
|
5351
5972
|
spellingOf,
|
|
5352
|
-
outcomes: (0,
|
|
5973
|
+
outcomes: (0, import_resolution7.calleeOutcomes)(ctx.facts, [...keys], RUBY_PROGRAM)
|
|
5353
5974
|
};
|
|
5354
5975
|
}
|
|
5355
5976
|
function resolveCallee(call, site, ctx, read) {
|
|
@@ -5363,11 +5984,11 @@ function resolveCallee(call, site, ctx, read) {
|
|
|
5363
5984
|
return asCallee(spelling, outcomeFor(spelling.key, ctx, read), site, ctx);
|
|
5364
5985
|
}
|
|
5365
5986
|
function outcomeFor(key, ctx, read) {
|
|
5366
|
-
return read?.outcomes.get(key) ?? (0,
|
|
5987
|
+
return read?.outcomes.get(key) ?? (0, import_resolution7.calleeOutcomeOf)(ctx.facts, key, RUBY_PROGRAM);
|
|
5367
5988
|
}
|
|
5368
5989
|
function mightReadAsACall(call, site, ctx) {
|
|
5369
5990
|
const spelling = spellingFor(call, site);
|
|
5370
|
-
return spelling.kind === "receiver" && (0,
|
|
5991
|
+
return spelling.kind === "receiver" && (0, import_resolution7.couldBeSettled)(ctx.facts, spelling.key);
|
|
5371
5992
|
}
|
|
5372
5993
|
function readsAsACall(call, site, ctx, read) {
|
|
5373
5994
|
const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site);
|
|
@@ -5478,7 +6099,11 @@ function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
|
5478
6099
|
if (ancestry === void 0) {
|
|
5479
6100
|
return stop("outsideRun");
|
|
5480
6101
|
}
|
|
5481
|
-
const found = methodInAncestry(ancestry, methodName
|
|
6102
|
+
const found = methodInAncestry(ancestry, methodName, {
|
|
6103
|
+
facts: ctx.facts,
|
|
6104
|
+
bodyBlocks: ctx.bodyBlocks,
|
|
6105
|
+
dynamicNames: ctx.dynamicNames
|
|
6106
|
+
});
|
|
5482
6107
|
if (found.type === "found") {
|
|
5483
6108
|
return followed(reachedMethod(found.method, found.block, methodName));
|
|
5484
6109
|
}
|
|
@@ -5507,7 +6132,10 @@ function singletonMethodOn(qualifiedName, methodName, ctx) {
|
|
|
5507
6132
|
if (block.info.bodyNode === null) {
|
|
5508
6133
|
continue;
|
|
5509
6134
|
}
|
|
5510
|
-
const found = singletonMethodsByName(
|
|
6135
|
+
const found = singletonMethodsByName(
|
|
6136
|
+
block.info.bodyNode,
|
|
6137
|
+
ctx.bodyBlocks
|
|
6138
|
+
).get(methodName);
|
|
5511
6139
|
if (found !== void 0) {
|
|
5512
6140
|
return followed(reachedMethod(found, block, methodName));
|
|
5513
6141
|
}
|
|
@@ -5861,7 +6489,7 @@ function parameterName(param) {
|
|
|
5861
6489
|
|
|
5862
6490
|
// src/reach/context.ts
|
|
5863
6491
|
var METHOD_TYPES4 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
5864
|
-
async function buildReachContext(files, facts) {
|
|
6492
|
+
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
|
|
5865
6493
|
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
5866
6494
|
const classes = [];
|
|
5867
6495
|
const classNames = /* @__PURE__ */ new Map();
|
|
@@ -5935,7 +6563,9 @@ async function buildReachContext(files, facts) {
|
|
|
5935
6563
|
topLevelMethods,
|
|
5936
6564
|
facts,
|
|
5937
6565
|
classNames,
|
|
5938
|
-
definitions
|
|
6566
|
+
definitions,
|
|
6567
|
+
bodyBlocks,
|
|
6568
|
+
dynamicNames
|
|
5939
6569
|
};
|
|
5940
6570
|
}
|
|
5941
6571
|
function namedChildren(node) {
|
|
@@ -5981,20 +6611,48 @@ function inheritedMethodsIn(packs) {
|
|
|
5981
6611
|
}
|
|
5982
6612
|
return found;
|
|
5983
6613
|
}
|
|
5984
|
-
function
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
6614
|
+
function associationCallsIn(packs) {
|
|
6615
|
+
return packs.flatMap(
|
|
6616
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6617
|
+
(pattern) => pattern.associations === void 0 ? [] : [pattern.associations]
|
|
6618
|
+
)
|
|
6619
|
+
);
|
|
6620
|
+
}
|
|
6621
|
+
function packWordsOf(packs) {
|
|
6622
|
+
return {
|
|
6623
|
+
givesBackOne: packs.flatMap(
|
|
6624
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6625
|
+
(pattern) => pattern.baseClasses.flatMap(
|
|
6626
|
+
(base) => pattern.givesBack.map((method) => ({ base, method }))
|
|
6627
|
+
)
|
|
6628
|
+
)
|
|
6629
|
+
)
|
|
6630
|
+
};
|
|
6631
|
+
}
|
|
6632
|
+
function factsForFile(options) {
|
|
6633
|
+
const db = new import_datalog3.Database();
|
|
6634
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6635
|
+
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6636
|
+
emitConstantBindings(db, [
|
|
6637
|
+
collectFileConstants(
|
|
6638
|
+
options.file,
|
|
6639
|
+
options.root,
|
|
6640
|
+
associationCallsIn(options.packs),
|
|
6641
|
+
inflectionsIn(options.packs)
|
|
6642
|
+
)
|
|
6643
|
+
]);
|
|
6644
|
+
emitRequireFacts(db, options.file, options.root, /* @__PURE__ */ new Set([options.file]));
|
|
6645
|
+
bindEvaluator(db, {
|
|
6646
|
+
files: [{ file: options.file, root: options.root }],
|
|
6647
|
+
definitions: new Map(methodDefinitionsIn(options.file, options.root))
|
|
6648
|
+
});
|
|
6649
|
+
(0, import_resolution8.addPackWords)(db, packWordsOf(options.packs));
|
|
6650
|
+
return db;
|
|
5994
6651
|
}
|
|
5995
6652
|
function alreadyDiscovered(seen, raw) {
|
|
5996
6653
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
5997
|
-
const
|
|
6654
|
+
const boundary = raw.boundaryBinding === null ? "" : (0, import_behavioral_ir5.boundaryKey)(raw.boundaryBinding) ?? "";
|
|
6655
|
+
const key = `${raw.identity.file}::${reported}::${raw.identity.range.start}::${boundary}`;
|
|
5998
6656
|
if (seen.has(key)) {
|
|
5999
6657
|
return true;
|
|
6000
6658
|
}
|
|
@@ -6035,6 +6693,9 @@ async function extractRubyProject(options) {
|
|
|
6035
6693
|
);
|
|
6036
6694
|
const parsed = [];
|
|
6037
6695
|
const definitions = /* @__PURE__ */ new Map();
|
|
6696
|
+
const associationCalls = associationCallsIn(options.packs);
|
|
6697
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6698
|
+
const inflections = inflectionsIn(options.packs);
|
|
6038
6699
|
for (const file of options.files) {
|
|
6039
6700
|
await timer.timeAsync("parse", async () => {
|
|
6040
6701
|
const root = await cache.get(file);
|
|
@@ -6042,21 +6703,27 @@ async function extractRubyProject(options) {
|
|
|
6042
6703
|
return;
|
|
6043
6704
|
}
|
|
6044
6705
|
parsed.push({ file, root });
|
|
6045
|
-
emitValueFacts(db, file, root);
|
|
6706
|
+
emitValueFacts(db, file, root, bodyBlocks);
|
|
6046
6707
|
for (const [key, method] of methodDefinitionsIn(file, root)) {
|
|
6047
6708
|
definitions.set(key, method);
|
|
6048
6709
|
}
|
|
6049
|
-
constants.push(
|
|
6710
|
+
constants.push(
|
|
6711
|
+
collectFileConstants(file, root, associationCalls, inflections)
|
|
6712
|
+
);
|
|
6050
6713
|
});
|
|
6051
6714
|
}
|
|
6052
|
-
timer.time("discover", () => {
|
|
6715
|
+
const dynamicNames = timer.time("discover", () => {
|
|
6053
6716
|
emitConstantBindings(db, constants);
|
|
6054
6717
|
const known = new Set(parsed.map(({ file }) => file));
|
|
6055
6718
|
for (const { file, root } of parsed) {
|
|
6056
6719
|
emitRequireFacts(db, file, root, known);
|
|
6057
6720
|
}
|
|
6058
6721
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6059
|
-
|
|
6722
|
+
(0, import_resolution8.addPackWords)(db, packWordsOf(options.packs));
|
|
6723
|
+
return readDynamicNames(
|
|
6724
|
+
db,
|
|
6725
|
+
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
6726
|
+
);
|
|
6060
6727
|
});
|
|
6061
6728
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
6062
6729
|
const loaderPatterns = options.packs.flatMap((pack) => pack.loaders ?? []);
|
|
@@ -6064,7 +6731,7 @@ async function extractRubyProject(options) {
|
|
|
6064
6731
|
const inheritedMethods = inheritedMethodsIn(options.packs);
|
|
6065
6732
|
const reachContext = await timer.timeAsync(
|
|
6066
6733
|
"discover",
|
|
6067
|
-
() => buildReachContext(parsed, db)
|
|
6734
|
+
() => buildReachContext(parsed, db, bodyBlocks, dynamicNames)
|
|
6068
6735
|
);
|
|
6069
6736
|
const displayPathOf = (file) => options.workspaceRoot !== void 0 ? import_node_path4.default.relative(options.workspaceRoot, file) : file;
|
|
6070
6737
|
const seeds = [];
|
|
@@ -6084,6 +6751,8 @@ async function extractRubyProject(options) {
|
|
|
6084
6751
|
cache,
|
|
6085
6752
|
...storage === void 0 ? {} : { storage },
|
|
6086
6753
|
inheritedMethods,
|
|
6754
|
+
bodyBlocks,
|
|
6755
|
+
dynamicNames,
|
|
6087
6756
|
displayPathOf,
|
|
6088
6757
|
facts: db,
|
|
6089
6758
|
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
@@ -6155,7 +6824,9 @@ async function extractRubyProject(options) {
|
|
|
6155
6824
|
context: reachContext,
|
|
6156
6825
|
displayPathOf,
|
|
6157
6826
|
...storage === void 0 ? {} : { storage },
|
|
6158
|
-
inheritedMethods
|
|
6827
|
+
inheritedMethods,
|
|
6828
|
+
bodyBlocks,
|
|
6829
|
+
dynamicNames
|
|
6159
6830
|
})
|
|
6160
6831
|
);
|
|
6161
6832
|
for (const entry of found) {
|
|
@@ -6272,7 +6943,7 @@ function findRubyFiles(root) {
|
|
|
6272
6943
|
}
|
|
6273
6944
|
|
|
6274
6945
|
// src/facts/why.ts
|
|
6275
|
-
var WITNESS_RULES = [...
|
|
6946
|
+
var WITNESS_RULES = [...import_resolution9.RESOLUTION_RULES, ...RUBY_RULES];
|
|
6276
6947
|
var METHOD_TYPES5 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6277
6948
|
function namedChildrenOf(node) {
|
|
6278
6949
|
return node.namedChildren.filter((child) => child !== null);
|
|
@@ -6321,6 +6992,10 @@ function displayNameOf(node) {
|
|
|
6321
6992
|
function width(node) {
|
|
6322
6993
|
return node.endIndex - node.startIndex;
|
|
6323
6994
|
}
|
|
6995
|
+
function askedKey(value) {
|
|
6996
|
+
const enclosing = enclosingMethod2(value.node);
|
|
6997
|
+
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6998
|
+
}
|
|
6324
6999
|
var RubyWhySession = class {
|
|
6325
7000
|
root;
|
|
6326
7001
|
db = new import_datalog4.Database();
|
|
@@ -6393,10 +7068,10 @@ var RubyWhySession = class {
|
|
|
6393
7068
|
* which the caller says in its own words.
|
|
6394
7069
|
*/
|
|
6395
7070
|
explain(value, options = {}) {
|
|
6396
|
-
return (0,
|
|
7071
|
+
return (0, import_resolution9.explainResolvedKey)({
|
|
6397
7072
|
db: this.db,
|
|
6398
7073
|
rules: WITNESS_RULES,
|
|
6399
|
-
key:
|
|
7074
|
+
key: askedKey(value),
|
|
6400
7075
|
locate: (key) => this.locate(key),
|
|
6401
7076
|
displayPath: (key) => this.displayPath(key),
|
|
6402
7077
|
...options
|
|
@@ -6514,6 +7189,7 @@ async function rubyStubEvidence(options) {
|
|
|
6514
7189
|
emitValueFacts,
|
|
6515
7190
|
evaluatedValue,
|
|
6516
7191
|
extractRubyProject,
|
|
7192
|
+
factsForFile,
|
|
6517
7193
|
field,
|
|
6518
7194
|
findRubyFiles,
|
|
6519
7195
|
graphqlTypeNameFromQualified,
|