@suss/adapter-ruby 0.30.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 +555 -417
- package/dist/index.d.cts +29 -5
- package/dist/index.d.ts +29 -5
- package/dist/index.js +556 -412
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -327,7 +327,7 @@ function autoloadDirectories(root) {
|
|
|
327
327
|
|
|
328
328
|
// src/defineMethod.ts
|
|
329
329
|
import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
|
|
330
|
-
import {
|
|
330
|
+
import { hole as hole2, literalOf as literalOf3, piecesOf } from "@suss/values";
|
|
331
331
|
|
|
332
332
|
// src/values/evaluator.ts
|
|
333
333
|
import { nodeOfKey } from "@suss/resolution";
|
|
@@ -336,63 +336,56 @@ import { Evaluator, force, literalOf, text } from "@suss/values";
|
|
|
336
336
|
// src/facts/resolve.ts
|
|
337
337
|
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
338
338
|
import {
|
|
339
|
+
allocationSitesOf,
|
|
340
|
+
alsoSteps,
|
|
339
341
|
askResolution,
|
|
342
|
+
askResolutionUnder,
|
|
343
|
+
INSTANCE_STEP,
|
|
340
344
|
resolutionProgram,
|
|
345
|
+
resolutionUnderProgram,
|
|
341
346
|
writtenValueOf as sharedWrittenValueOf,
|
|
347
|
+
writtenValueUnder as sharedWrittenValueUnder,
|
|
342
348
|
VALUE_STEP
|
|
343
349
|
} from "@suss/resolution";
|
|
344
|
-
var RUBY_RULES = [
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
350
|
+
var RUBY_RULES = alsoSteps([
|
|
351
|
+
// Making one of a class is a call of the class, which the shared rules
|
|
352
|
+
// already say. Ruby writes the callee as `new` read off the constant, so
|
|
353
|
+
// what this adds is that the callee is the class.
|
|
348
354
|
rule(
|
|
349
|
-
"
|
|
350
|
-
[v("x"), v("cls"),
|
|
355
|
+
"hop",
|
|
356
|
+
[v("x"), v("cls"), INSTANCE_STEP],
|
|
351
357
|
[
|
|
352
358
|
lit("readsProperty", v("x"), v("o"), constant("new")),
|
|
353
359
|
lit("comesTo", v("o"), v("cls")),
|
|
354
360
|
lit("objectValue", v("cls"))
|
|
355
361
|
]
|
|
356
362
|
),
|
|
357
|
-
// The
|
|
358
|
-
//
|
|
359
|
-
//
|
|
363
|
+
// The same spelling again, for the caller asking where a class was
|
|
364
|
+
// made. `callsNamed` reaches a callee written as the name itself, and
|
|
365
|
+
// Ruby writes `new` off the constant instead.
|
|
360
366
|
rule(
|
|
361
|
-
"
|
|
362
|
-
[v("
|
|
367
|
+
"constructsNamed",
|
|
368
|
+
[v("r"), v("cls")],
|
|
363
369
|
[
|
|
364
|
-
lit("
|
|
365
|
-
lit("
|
|
366
|
-
lit("
|
|
367
|
-
|
|
368
|
-
lit("givesBackOne", v("n"), v("m"))
|
|
369
|
-
],
|
|
370
|
-
"declared finder"
|
|
371
|
-
),
|
|
372
|
-
// Ruby cannot refer to a method without running it, so
|
|
373
|
-
// `Settings.filters` is worth what the method returns. The shared
|
|
374
|
-
// rules hang that step on a `call` fact this spelling never gets.
|
|
375
|
-
rule(
|
|
376
|
-
"invokes",
|
|
377
|
-
[v("x"), v("f")],
|
|
378
|
-
[
|
|
379
|
-
lit("readsProperty", v("x"), v("o"), v("m")),
|
|
380
|
-
lit("comesTo", v("x"), v("f")),
|
|
381
|
-
lit("func", v("f"))
|
|
382
|
-
],
|
|
383
|
-
"property read runs a method"
|
|
370
|
+
lit("binds", v("o"), v("cls")),
|
|
371
|
+
lit("readsProperty", v("c"), v("o"), constant("new")),
|
|
372
|
+
lit("call", v("r"), v("c"))
|
|
373
|
+
]
|
|
384
374
|
),
|
|
385
375
|
// `%i[a b].freeze` is worth the list it was written as. The evaluator
|
|
386
376
|
// says the same in its row table, for a value it reads in one file.
|
|
387
377
|
...["freeze", "dup"].map(
|
|
388
378
|
(method) => rule(
|
|
389
|
-
"
|
|
390
|
-
[v("
|
|
391
|
-
[
|
|
379
|
+
"hop",
|
|
380
|
+
[v("r"), v("o"), VALUE_STEP],
|
|
381
|
+
[
|
|
382
|
+
lit("call", v("r"), v("c")),
|
|
383
|
+
lit("readsProperty", v("c"), v("o"), constant(method))
|
|
384
|
+
],
|
|
392
385
|
"hands back the receiver"
|
|
393
386
|
)
|
|
394
387
|
)
|
|
395
|
-
];
|
|
388
|
+
]);
|
|
396
389
|
var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
397
390
|
function resolveValues(db, keys) {
|
|
398
391
|
askResolution(db, keys, "wanted", RUBY_PROGRAM);
|
|
@@ -404,10 +397,118 @@ function writtenValueOf(db, key) {
|
|
|
404
397
|
resolveValues(db, [key]);
|
|
405
398
|
return sharedWrittenValueOf(db, key, (keys) => resolveValues(db, keys));
|
|
406
399
|
}
|
|
400
|
+
function writtenValueUnder(db, key, site) {
|
|
401
|
+
askResolutionUnder(db, [[key, site]], resolutionUnderProgram(RUBY_RULES));
|
|
402
|
+
return sharedWrittenValueUnder(db, key, site);
|
|
403
|
+
}
|
|
404
|
+
function constructionSites(db, classKey) {
|
|
405
|
+
askResolution(db, [classKey], "wantedSites", RUBY_PROGRAM);
|
|
406
|
+
return allocationSitesOf(db, classKey);
|
|
407
|
+
}
|
|
407
408
|
|
|
408
409
|
// src/facts/values.ts
|
|
409
410
|
import { startsAtName, valueLeftByWrites } from "@suss/resolution";
|
|
410
411
|
|
|
412
|
+
// src/paths/bareCalls.ts
|
|
413
|
+
var PARAMETER_LIST_TYPES = [
|
|
414
|
+
"method_parameters",
|
|
415
|
+
"block_parameters",
|
|
416
|
+
"lambda_parameters",
|
|
417
|
+
"parameters"
|
|
418
|
+
];
|
|
419
|
+
function isFieldOf(parent, node, name) {
|
|
420
|
+
return field(parent, name)?.id === node.id;
|
|
421
|
+
}
|
|
422
|
+
function namedAt(parent, node) {
|
|
423
|
+
return isFieldOf(parent, node, "name");
|
|
424
|
+
}
|
|
425
|
+
function always() {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
var SPELLS_A_NAME = {
|
|
429
|
+
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
430
|
+
method: namedAt,
|
|
431
|
+
singleton_method: namedAt,
|
|
432
|
+
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
433
|
+
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
434
|
+
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
435
|
+
optional_parameter: namedAt,
|
|
436
|
+
splat_parameter: namedAt,
|
|
437
|
+
hash_splat_parameter: namedAt,
|
|
438
|
+
keyword_parameter: namedAt,
|
|
439
|
+
block_parameter: namedAt,
|
|
440
|
+
destructured_parameter: always,
|
|
441
|
+
left_assignment_list: always,
|
|
442
|
+
rest_assignment: always,
|
|
443
|
+
destructured_left_assignment: always,
|
|
444
|
+
exception_variable: always,
|
|
445
|
+
undef: always,
|
|
446
|
+
alias: always,
|
|
447
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
448
|
+
};
|
|
449
|
+
function addNamesUnder(node, names) {
|
|
450
|
+
if (node === null) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (node.type === "identifier") {
|
|
454
|
+
names.add(node.text);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
for (const child of bodyStatements(node)) {
|
|
458
|
+
addNamesUnder(child, names);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function parameterNames(node, names) {
|
|
462
|
+
for (const parameter of bodyStatements(node)) {
|
|
463
|
+
if (parameter.type === "identifier") {
|
|
464
|
+
names.add(parameter.text);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const declared = field(parameter, "name");
|
|
468
|
+
if (declared !== null && declared.type === "identifier") {
|
|
469
|
+
names.add(declared.text);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
addNamesUnder(parameter, names);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function assignedNames(node, names) {
|
|
476
|
+
addNamesUnder(field(node, "left"), names);
|
|
477
|
+
}
|
|
478
|
+
var BINDS_A_NAME = {
|
|
479
|
+
assignment: assignedNames,
|
|
480
|
+
operator_assignment: assignedNames,
|
|
481
|
+
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
482
|
+
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
483
|
+
...Object.fromEntries(
|
|
484
|
+
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
485
|
+
)
|
|
486
|
+
};
|
|
487
|
+
function localNamesIn(definitionNode) {
|
|
488
|
+
const names = /* @__PURE__ */ new Set();
|
|
489
|
+
const visit = (node) => {
|
|
490
|
+
BINDS_A_NAME[node.type]?.(node, names);
|
|
491
|
+
for (const child of bodyStatements(node)) {
|
|
492
|
+
visit(child);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
visit(definitionNode);
|
|
496
|
+
return names;
|
|
497
|
+
}
|
|
498
|
+
function spellsAName(node) {
|
|
499
|
+
const parent = node.parent;
|
|
500
|
+
if (parent === null) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
504
|
+
}
|
|
505
|
+
function isBareMethodCall(node, locals) {
|
|
506
|
+
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return !spellsAName(node);
|
|
510
|
+
}
|
|
511
|
+
|
|
411
512
|
// src/facts/locals.ts
|
|
412
513
|
import { writesRunInOrder } from "@suss/resolution";
|
|
413
514
|
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
@@ -432,7 +533,7 @@ function parametersOf(owner) {
|
|
|
432
533
|
const params = field(owner, "parameters");
|
|
433
534
|
return params === null ? [] : bodyStatements(params);
|
|
434
535
|
}
|
|
435
|
-
function
|
|
536
|
+
function parameterNames2(owner) {
|
|
436
537
|
const names = /* @__PURE__ */ new Set();
|
|
437
538
|
for (const param of parametersOf(owner)) {
|
|
438
539
|
const name = paramNameOf(param);
|
|
@@ -480,6 +581,13 @@ function readForVariable(node) {
|
|
|
480
581
|
}
|
|
481
582
|
return targetsOf(pattern).map((target) => writeOf(target, null, node, false));
|
|
482
583
|
}
|
|
584
|
+
function readRescueVariable(node) {
|
|
585
|
+
const variable = field(node, "variable");
|
|
586
|
+
if (variable === null) {
|
|
587
|
+
return [];
|
|
588
|
+
}
|
|
589
|
+
return bodyStatements(variable).flatMap(targetsOf).map((target) => writeOf(target, null, node, false));
|
|
590
|
+
}
|
|
483
591
|
function readBlockParameters(node) {
|
|
484
592
|
const found = [];
|
|
485
593
|
for (const param of parametersOf(node)) {
|
|
@@ -494,6 +602,7 @@ var WRITE_READERS = {
|
|
|
494
602
|
assignment: readAssignment,
|
|
495
603
|
operator_assignment: readOperatorAssignment,
|
|
496
604
|
for: readForVariable,
|
|
605
|
+
rescue: readRescueVariable,
|
|
497
606
|
block: readBlockParameters,
|
|
498
607
|
do_block: readBlockParameters
|
|
499
608
|
};
|
|
@@ -539,7 +648,7 @@ function namesOfMethod(method) {
|
|
|
539
648
|
if (remembered !== void 0) {
|
|
540
649
|
return remembered;
|
|
541
650
|
}
|
|
542
|
-
const names = scopeNames(
|
|
651
|
+
const names = scopeNames(parameterNames2(method), field(method, "body"));
|
|
543
652
|
cache.byMethod.set(method, names);
|
|
544
653
|
return names;
|
|
545
654
|
}
|
|
@@ -561,10 +670,17 @@ function ownerOfName(node, name, enclosing) {
|
|
|
561
670
|
}
|
|
562
671
|
return enclosing !== null && names.locals.has(name) ? enclosing : null;
|
|
563
672
|
}
|
|
673
|
+
function isLocalName(node, name, enclosing) {
|
|
674
|
+
const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
|
|
675
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
return names.locals.has(name);
|
|
679
|
+
}
|
|
564
680
|
function blockDeclaring(node, name) {
|
|
565
681
|
let current = node.parent;
|
|
566
682
|
while (current !== null) {
|
|
567
|
-
if (BLOCK_TYPES2.has(current.type) &&
|
|
683
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames2(current).has(name)) {
|
|
568
684
|
return current;
|
|
569
685
|
}
|
|
570
686
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -620,6 +736,17 @@ function collectWrites(method, body) {
|
|
|
620
736
|
}
|
|
621
737
|
return [...groups.values()];
|
|
622
738
|
}
|
|
739
|
+
function instanceWritesRunInOrder(body, name, targets) {
|
|
740
|
+
return writesRunInOrder(
|
|
741
|
+
body,
|
|
742
|
+
name,
|
|
743
|
+
targets.map((target) => ({
|
|
744
|
+
at: target,
|
|
745
|
+
direct: target.parent?.parent?.id === body.id
|
|
746
|
+
})),
|
|
747
|
+
nameReads(new Set(targets.map((target) => target.id)))
|
|
748
|
+
);
|
|
749
|
+
}
|
|
623
750
|
function orderedWrite(write, scope) {
|
|
624
751
|
return {
|
|
625
752
|
at: write.at,
|
|
@@ -665,12 +792,28 @@ function readKey(filePath, written, enclosing) {
|
|
|
665
792
|
if (node.type !== "identifier" && node.type !== "constant") {
|
|
666
793
|
return nodeId(filePath, node);
|
|
667
794
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
795
|
+
if (isMethodOfReceiver(node) || isBareCall(node, enclosing)) {
|
|
796
|
+
return nodeId(filePath, node);
|
|
671
797
|
}
|
|
672
798
|
return nameKey(filePath, node, enclosing);
|
|
673
799
|
}
|
|
800
|
+
function isMethodOfReceiver(node) {
|
|
801
|
+
const parent = node.parent;
|
|
802
|
+
return parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null;
|
|
803
|
+
}
|
|
804
|
+
function isReceiverOfCall(node) {
|
|
805
|
+
const parent = node.parent;
|
|
806
|
+
return parent !== null && parent.type === "call" && field(parent, "receiver")?.id === node.id;
|
|
807
|
+
}
|
|
808
|
+
function isBareCall(node, enclosing) {
|
|
809
|
+
if (node.type !== "identifier") {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
if (spellsAName(node) && !isReceiverOfCall(node)) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
return !isLocalName(node, node.text, enclosing);
|
|
816
|
+
}
|
|
674
817
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
675
818
|
"string",
|
|
676
819
|
"integer",
|
|
@@ -699,11 +842,7 @@ function add(emitter, relation, ...tuple) {
|
|
|
699
842
|
emitter.db.add(relation, tuple);
|
|
700
843
|
}
|
|
701
844
|
function valueKey(emitter, written) {
|
|
702
|
-
|
|
703
|
-
if (value.type !== "identifier" && value.type !== "constant") {
|
|
704
|
-
return nodeId(emitter.filePath, value);
|
|
705
|
-
}
|
|
706
|
-
return nameKey(emitter.filePath, value, emitter.enclosing);
|
|
845
|
+
return readKey(emitter.filePath, written, emitter.enclosing);
|
|
707
846
|
}
|
|
708
847
|
function pairKeyText(key) {
|
|
709
848
|
if (key.type === "hash_key_symbol") {
|
|
@@ -717,35 +856,29 @@ function pairKeyText(key) {
|
|
|
717
856
|
}
|
|
718
857
|
return null;
|
|
719
858
|
}
|
|
720
|
-
function
|
|
721
|
-
|
|
722
|
-
(
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
function emitPropertyRead(emitter, node) {
|
|
726
|
-
const receiver = field(node, "receiver");
|
|
727
|
-
const method = field(node, "method");
|
|
728
|
-
if (receiver === null || method === null) {
|
|
729
|
-
return;
|
|
859
|
+
function calleeKeyOf(filePath, node, enclosing) {
|
|
860
|
+
if (isBareCall(node, enclosing)) {
|
|
861
|
+
return nameKey(filePath, node, enclosing);
|
|
730
862
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
method.text
|
|
737
|
-
);
|
|
863
|
+
const method = node.type === "call" ? field(node, "method") : null;
|
|
864
|
+
if (method === null) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
738
868
|
}
|
|
739
869
|
function emitCall(emitter, call) {
|
|
740
870
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
741
|
-
const method = field(call, "method");
|
|
742
|
-
|
|
871
|
+
const method = call.type === "identifier" ? call : field(call, "method");
|
|
872
|
+
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
873
|
+
if (method === null || calleeKey === null) {
|
|
743
874
|
return;
|
|
744
875
|
}
|
|
745
876
|
const receiver = field(call, "receiver");
|
|
746
877
|
const callKey = nodeId(emitter.filePath, call);
|
|
747
|
-
const calleeKey = receiver === null ? valueKey(emitter, method) : nodeId(emitter.filePath, method);
|
|
748
878
|
add(emitter, "call", callKey, calleeKey);
|
|
879
|
+
if (!emitter.insideMethod) {
|
|
880
|
+
add(emitter, "callOutsideMethod", callKey);
|
|
881
|
+
}
|
|
749
882
|
if (receiver !== null) {
|
|
750
883
|
add(
|
|
751
884
|
emitter,
|
|
@@ -754,6 +887,8 @@ function emitCall(emitter, call) {
|
|
|
754
887
|
valueKey(emitter, receiver),
|
|
755
888
|
method.text
|
|
756
889
|
);
|
|
890
|
+
} else if (emitter.selfKey !== null) {
|
|
891
|
+
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
757
892
|
}
|
|
758
893
|
const args = field(call, "arguments");
|
|
759
894
|
let position = 0;
|
|
@@ -878,9 +1013,7 @@ function emitExpressionFacts(emitter, node) {
|
|
|
878
1013
|
emitDynamicDefinition(emitter, node, []);
|
|
879
1014
|
walkExpressions(node, emitter, (child, turns) => {
|
|
880
1015
|
emitDynamicDefinition(emitter, child, turns);
|
|
881
|
-
if (
|
|
882
|
-
emitPropertyRead(emitter, child);
|
|
883
|
-
} else if (child.type === "call") {
|
|
1016
|
+
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
884
1017
|
emitCall(emitter, child);
|
|
885
1018
|
}
|
|
886
1019
|
if (ARRAY_TYPES.has(child.type)) {
|
|
@@ -936,28 +1069,52 @@ function collectInstanceWrite(emitter, node) {
|
|
|
936
1069
|
const operator = field(node, "operator")?.text;
|
|
937
1070
|
const value = node.type === "assignment" || WHOLE_VALUE_OPERATORS.has(operator ?? "") ? right : null;
|
|
938
1071
|
const written = collector.get(left.text) ?? [];
|
|
939
|
-
written.push(
|
|
940
|
-
describeWrite(emitter, {
|
|
1072
|
+
written.push({
|
|
1073
|
+
write: describeWrite(emitter, {
|
|
941
1074
|
name: left.text,
|
|
942
1075
|
target: left,
|
|
943
1076
|
value,
|
|
944
1077
|
at: node,
|
|
945
1078
|
fromParameter: false
|
|
946
|
-
})
|
|
947
|
-
|
|
1079
|
+
}),
|
|
1080
|
+
target: left
|
|
1081
|
+
});
|
|
948
1082
|
collector.set(left.text, written);
|
|
949
1083
|
}
|
|
1084
|
+
function settledWrites(writes, ordered) {
|
|
1085
|
+
const described = writes.map((written) => written.write);
|
|
1086
|
+
const settled = valueLeftByWrites(described, ordered);
|
|
1087
|
+
if (settled !== null) {
|
|
1088
|
+
return [settled];
|
|
1089
|
+
}
|
|
1090
|
+
const left = [];
|
|
1091
|
+
for (const write of described) {
|
|
1092
|
+
if (write.value !== null && !write.narrowsName) {
|
|
1093
|
+
left.push(write.value);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return left;
|
|
1097
|
+
}
|
|
950
1098
|
function emitInstanceWrites(emitter, classKey, collected) {
|
|
951
1099
|
for (const [name, writes] of collected) {
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
1100
|
+
for (const value of settledWrites(writes, false)) {
|
|
1101
|
+
add(emitter, "holdsProperty", classKey, name, value);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function emitInstanceStores(emitter, funcKey, body, collected) {
|
|
1106
|
+
for (const [name, writes] of collected) {
|
|
1107
|
+
const deciding = writes.filter((written) => !written.write.narrowsName);
|
|
1108
|
+
if (deciding.length === 0) {
|
|
955
1109
|
continue;
|
|
956
1110
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1111
|
+
const ordered = instanceWritesRunInOrder(
|
|
1112
|
+
body,
|
|
1113
|
+
name,
|
|
1114
|
+
deciding.map((written) => written.target)
|
|
1115
|
+
);
|
|
1116
|
+
for (const value of settledWrites(deciding, ordered)) {
|
|
1117
|
+
add(emitter, "storesProperty", funcKey, name, value);
|
|
961
1118
|
}
|
|
962
1119
|
}
|
|
963
1120
|
}
|
|
@@ -1017,7 +1174,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1017
1174
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
1018
1175
|
}
|
|
1019
1176
|
}
|
|
1020
|
-
if (child.type === "call") {
|
|
1177
|
+
if (child.type === "call" || isBareCall(child, method)) {
|
|
1021
1178
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1022
1179
|
}
|
|
1023
1180
|
});
|
|
@@ -1085,20 +1242,32 @@ function emitScopeWrites(emitter, method, body) {
|
|
|
1085
1242
|
}
|
|
1086
1243
|
}
|
|
1087
1244
|
function emitCandidates(emitter, key, group) {
|
|
1245
|
+
let unstated = false;
|
|
1246
|
+
let stated = 0;
|
|
1088
1247
|
for (const write of group.writes) {
|
|
1089
1248
|
if (write.value === null) {
|
|
1090
1249
|
add(emitter, "writesUnstated", key);
|
|
1250
|
+
unstated = true;
|
|
1091
1251
|
continue;
|
|
1092
1252
|
}
|
|
1093
1253
|
if (write.fromParameter) {
|
|
1254
|
+
unstated = true;
|
|
1094
1255
|
continue;
|
|
1095
1256
|
}
|
|
1096
1257
|
if (!describeWrite(emitter, write).narrowsName) {
|
|
1097
1258
|
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
1259
|
+
stated += 1;
|
|
1098
1260
|
}
|
|
1099
1261
|
}
|
|
1262
|
+
if (!unstated && stated > 0) {
|
|
1263
|
+
add(emitter, "writesAllStated", key);
|
|
1264
|
+
}
|
|
1100
1265
|
}
|
|
1101
1266
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1267
|
+
var INITIALIZE_METHOD = "initialize";
|
|
1268
|
+
function receiverKeyOf(filePath, method, classKey) {
|
|
1269
|
+
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
1270
|
+
}
|
|
1102
1271
|
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1103
1272
|
function mixedInConstants(body, callName) {
|
|
1104
1273
|
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
@@ -1126,6 +1295,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1126
1295
|
const within = {
|
|
1127
1296
|
...emitter,
|
|
1128
1297
|
selfKey: classKey,
|
|
1298
|
+
insideMethod: false,
|
|
1129
1299
|
instanceWrites: collected
|
|
1130
1300
|
};
|
|
1131
1301
|
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
@@ -1153,10 +1323,30 @@ function emitClassFacts(emitter, cls) {
|
|
|
1153
1323
|
emitExpressionFacts(within, statement);
|
|
1154
1324
|
continue;
|
|
1155
1325
|
}
|
|
1156
|
-
const
|
|
1326
|
+
const stored = /* @__PURE__ */ new Map();
|
|
1327
|
+
const receiverKey = receiverKeyOf(emitter.filePath, statement, classKey);
|
|
1328
|
+
if (receiverKey !== classKey) {
|
|
1329
|
+
add(emitter, "instanceOf", receiverKey, classKey);
|
|
1330
|
+
}
|
|
1331
|
+
const funcKey = emitMethodFacts(
|
|
1332
|
+
{
|
|
1333
|
+
...within,
|
|
1334
|
+
selfKey: receiverKey,
|
|
1335
|
+
insideMethod: receiverKey !== classKey,
|
|
1336
|
+
instanceWrites: stored
|
|
1337
|
+
},
|
|
1338
|
+
statement
|
|
1339
|
+
);
|
|
1157
1340
|
const name = field(statement, "name");
|
|
1158
1341
|
if (name !== null) {
|
|
1159
1342
|
add(emitter, "holdsProperty", classKey, name.text, funcKey);
|
|
1343
|
+
if (name.text === INITIALIZE_METHOD) {
|
|
1344
|
+
add(emitter, "initializes", classKey, funcKey);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const methodBody = field(statement, "body");
|
|
1348
|
+
if (methodBody !== null) {
|
|
1349
|
+
emitInstanceStores(within, funcKey, methodBody, stored);
|
|
1160
1350
|
}
|
|
1161
1351
|
}
|
|
1162
1352
|
emitInstanceWrites(within, classKey, collected);
|
|
@@ -1168,6 +1358,7 @@ function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
|
1168
1358
|
filePath,
|
|
1169
1359
|
enclosing: null,
|
|
1170
1360
|
selfKey: null,
|
|
1361
|
+
insideMethod: false,
|
|
1171
1362
|
instanceWrites: null,
|
|
1172
1363
|
bodyBlocks
|
|
1173
1364
|
};
|
|
@@ -1248,7 +1439,7 @@ function rubyLowering(options) {
|
|
|
1248
1439
|
statement: statementOf,
|
|
1249
1440
|
siteOf,
|
|
1250
1441
|
functionOf,
|
|
1251
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1442
|
+
writtenTo: (node, site) => context === null ? null : context.writtenTo(node, site),
|
|
1252
1443
|
callable: (node) => {
|
|
1253
1444
|
if (!isInlinableCall(node)) {
|
|
1254
1445
|
return null;
|
|
@@ -1765,17 +1956,32 @@ function siteOf(node) {
|
|
|
1765
1956
|
current = parent;
|
|
1766
1957
|
}
|
|
1767
1958
|
}
|
|
1959
|
+
var BARE_SPLAT_TYPES = /* @__PURE__ */ new Set(["splat_parameter", "hash_splat_parameter"]);
|
|
1768
1960
|
function parametersOf2(parameters) {
|
|
1769
1961
|
if (parameters === null) {
|
|
1770
1962
|
return [];
|
|
1771
1963
|
}
|
|
1772
|
-
|
|
1964
|
+
const declared = [];
|
|
1965
|
+
let position = 0;
|
|
1966
|
+
for (const parameter of named(parameters)) {
|
|
1967
|
+
if (BARE_SPLAT_TYPES.has(parameter.type) && field(parameter, "name") === null) {
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1773
1970
|
if (parameter.type === "identifier") {
|
|
1774
|
-
|
|
1971
|
+
declared.push({ name: parameter.text, default: null, position });
|
|
1972
|
+
} else {
|
|
1973
|
+
const name = field(parameter, "name");
|
|
1974
|
+
if (name !== null) {
|
|
1975
|
+
declared.push({
|
|
1976
|
+
name: name.text,
|
|
1977
|
+
default: field(parameter, "value"),
|
|
1978
|
+
position
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1775
1981
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1982
|
+
position += 1;
|
|
1983
|
+
}
|
|
1984
|
+
return declared;
|
|
1779
1985
|
}
|
|
1780
1986
|
function functionOf(node) {
|
|
1781
1987
|
if (node.type === "program") {
|
|
@@ -2183,25 +2389,56 @@ var rubyRows = [
|
|
|
2183
2389
|
|
|
2184
2390
|
// src/values/evaluator.ts
|
|
2185
2391
|
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2392
|
+
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2186
2393
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2394
|
+
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2187
2395
|
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2188
2396
|
function bindEvaluator(db, nodes) {
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
);
|
|
2397
|
+
for (const entry of nodes.files) {
|
|
2398
|
+
filesByTree.set(entry.root.tree, entry.file);
|
|
2399
|
+
}
|
|
2400
|
+
const context = contextOver(db, nodes);
|
|
2401
|
+
contexts.set(db, context);
|
|
2402
|
+
evaluators.set(db, new Evaluator(rubyLowering({ context, rows: rubyRows })));
|
|
2195
2403
|
}
|
|
2196
|
-
function
|
|
2404
|
+
function writtenNodeOf(node, db, site) {
|
|
2405
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2406
|
+
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2407
|
+
}
|
|
2408
|
+
function evaluatedValue(node, db, bindings, site) {
|
|
2197
2409
|
const evaluator = evaluatorFor(node, db);
|
|
2198
2410
|
if (bindings === void 0) {
|
|
2199
|
-
return force(
|
|
2411
|
+
return force(
|
|
2412
|
+
site === void 0 ? evaluator.evaluate(node) : evaluator.evaluate(node, { site })
|
|
2413
|
+
);
|
|
2200
2414
|
}
|
|
2201
2415
|
const supplied = new Map(
|
|
2202
2416
|
[...bindings].map(([name, value]) => [name, text(value)])
|
|
2203
2417
|
);
|
|
2204
|
-
return force(
|
|
2418
|
+
return force(
|
|
2419
|
+
evaluator.evaluate(node, {
|
|
2420
|
+
bindings: supplied,
|
|
2421
|
+
...site === void 0 ? {} : { site }
|
|
2422
|
+
})
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
function constructionSitesOf(node, db) {
|
|
2426
|
+
const cls = enclosingClass(node);
|
|
2427
|
+
const file = cls === null ? void 0 : filesByTree.get(cls.tree);
|
|
2428
|
+
if (db === void 0 || cls === null || file === void 0) {
|
|
2429
|
+
return [];
|
|
2430
|
+
}
|
|
2431
|
+
return constructionSites(db, nodeId(file, cls));
|
|
2432
|
+
}
|
|
2433
|
+
function enclosingClass(node) {
|
|
2434
|
+
let current = node.parent;
|
|
2435
|
+
while (current !== null) {
|
|
2436
|
+
if (current.type === "class") {
|
|
2437
|
+
return current;
|
|
2438
|
+
}
|
|
2439
|
+
current = current.parent;
|
|
2440
|
+
}
|
|
2441
|
+
return null;
|
|
2205
2442
|
}
|
|
2206
2443
|
function stringValueOf(node, db, bindings) {
|
|
2207
2444
|
return literalOf(evaluatedValue(node, db, bindings));
|
|
@@ -2253,15 +2490,13 @@ function contextOver(db, nodes) {
|
|
|
2253
2490
|
}
|
|
2254
2491
|
const fileOf = (node) => filesByRoot.get(node.tree.rootNode.id)?.file ?? null;
|
|
2255
2492
|
return {
|
|
2256
|
-
writtenTo: (node) => {
|
|
2493
|
+
writtenTo: (node, site) => {
|
|
2257
2494
|
const file = fileOf(node);
|
|
2258
2495
|
if (file === null) {
|
|
2259
2496
|
return null;
|
|
2260
2497
|
}
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2263
|
-
readKey(file, node, enclosingMethod(node))
|
|
2264
|
-
);
|
|
2498
|
+
const key = readKey(file, node, enclosingMethod(node));
|
|
2499
|
+
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2265
2500
|
return answer === null ? null : nodeOfKey(rootsByFile, answer);
|
|
2266
2501
|
},
|
|
2267
2502
|
callable: (call) => {
|
|
@@ -2279,6 +2514,31 @@ function contextOver(db, nodes) {
|
|
|
2279
2514
|
};
|
|
2280
2515
|
}
|
|
2281
2516
|
|
|
2517
|
+
// src/values/literals.ts
|
|
2518
|
+
import { force as force2, literalOf as literalOf2 } from "@suss/values";
|
|
2519
|
+
function literalElementsIn(value) {
|
|
2520
|
+
if (value.kind !== "sequence") {
|
|
2521
|
+
return null;
|
|
2522
|
+
}
|
|
2523
|
+
const elements = [];
|
|
2524
|
+
for (const item of value.items) {
|
|
2525
|
+
const literal = literalOf2(force2(item.value));
|
|
2526
|
+
if (literal === null) {
|
|
2527
|
+
return null;
|
|
2528
|
+
}
|
|
2529
|
+
elements.push(literal);
|
|
2530
|
+
}
|
|
2531
|
+
return elements;
|
|
2532
|
+
}
|
|
2533
|
+
function literalElementsOf(node, db) {
|
|
2534
|
+
return literalElementsIn(evaluatedValue(node, db));
|
|
2535
|
+
}
|
|
2536
|
+
function namesOf(node, db) {
|
|
2537
|
+
const value = evaluatedValue(node, db);
|
|
2538
|
+
const single = literalOf2(value);
|
|
2539
|
+
return single === null ? literalElementsIn(value) : [single];
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2282
2542
|
// src/defineMethod.ts
|
|
2283
2543
|
var NOTHING = {
|
|
2284
2544
|
names: /* @__PURE__ */ new Set(),
|
|
@@ -2326,7 +2586,7 @@ function readOne(db, rootsByFile, nameKey2) {
|
|
|
2326
2586
|
const patterns = [];
|
|
2327
2587
|
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2328
2588
|
const value = node === null ? hole2(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2329
|
-
const name =
|
|
2589
|
+
const name = literalOf3(value);
|
|
2330
2590
|
if (name !== null) {
|
|
2331
2591
|
names.push(name);
|
|
2332
2592
|
continue;
|
|
@@ -2382,29 +2642,14 @@ function listsBehind(db, rootsByFile, nameKey2) {
|
|
|
2382
2642
|
0,
|
|
2383
2643
|
nameKey2
|
|
2384
2644
|
)) {
|
|
2385
|
-
const
|
|
2645
|
+
const node = nodeOfKey2(rootsByFile, String(overKey));
|
|
2646
|
+
const elements = node === null ? null : literalElementsOf(node, db);
|
|
2386
2647
|
if (elements !== null) {
|
|
2387
2648
|
found.push({ element: String(element), index: String(index), elements });
|
|
2388
2649
|
}
|
|
2389
2650
|
}
|
|
2390
2651
|
return found;
|
|
2391
2652
|
}
|
|
2392
|
-
function literalElementsOf(rootsByFile, key, db) {
|
|
2393
|
-
const node = nodeOfKey2(rootsByFile, key);
|
|
2394
|
-
const value = node === null ? null : evaluatedValue(node, db);
|
|
2395
|
-
if (value === null || value.kind !== "sequence") {
|
|
2396
|
-
return null;
|
|
2397
|
-
}
|
|
2398
|
-
const elements = [];
|
|
2399
|
-
for (const item of value.items) {
|
|
2400
|
-
const literal = literalOf2(force2(item.value));
|
|
2401
|
-
if (literal === null) {
|
|
2402
|
-
return null;
|
|
2403
|
-
}
|
|
2404
|
-
elements.push(literal);
|
|
2405
|
-
}
|
|
2406
|
-
return elements;
|
|
2407
|
-
}
|
|
2408
2653
|
|
|
2409
2654
|
// src/scope.ts
|
|
2410
2655
|
function qualifyConstantRef(node, nesting) {
|
|
@@ -2736,108 +2981,12 @@ import {
|
|
|
2736
2981
|
import { absentReading as absentReading2, unreadableReading as unreadableReading2 } from "@suss/extractor";
|
|
2737
2982
|
|
|
2738
2983
|
// src/clientCalls.ts
|
|
2739
|
-
import { namesNothing, restBinding } from "@suss/behavioral-ir";
|
|
2984
|
+
import { hasNameHole, namesNothing, restBinding } from "@suss/behavioral-ir";
|
|
2740
2985
|
import { pathOf } from "@suss/values";
|
|
2741
2986
|
|
|
2742
2987
|
// src/paths/effects.ts
|
|
2743
2988
|
import { enumerateOrDegrade, sharedGatingConditions } from "@suss/extractor";
|
|
2744
|
-
|
|
2745
|
-
// src/paths/bareCalls.ts
|
|
2746
|
-
var PARAMETER_LIST_TYPES = [
|
|
2747
|
-
"method_parameters",
|
|
2748
|
-
"block_parameters",
|
|
2749
|
-
"lambda_parameters",
|
|
2750
|
-
"parameters"
|
|
2751
|
-
];
|
|
2752
|
-
function isFieldOf(parent, node, name) {
|
|
2753
|
-
return field(parent, name)?.id === node.id;
|
|
2754
|
-
}
|
|
2755
|
-
function namedAt(parent, node) {
|
|
2756
|
-
return isFieldOf(parent, node, "name");
|
|
2757
|
-
}
|
|
2758
|
-
function always() {
|
|
2759
|
-
return true;
|
|
2760
|
-
}
|
|
2761
|
-
var SPELLS_A_NAME = {
|
|
2762
|
-
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
2763
|
-
method: namedAt,
|
|
2764
|
-
singleton_method: namedAt,
|
|
2765
|
-
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2766
|
-
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2767
|
-
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
2768
|
-
optional_parameter: namedAt,
|
|
2769
|
-
splat_parameter: namedAt,
|
|
2770
|
-
hash_splat_parameter: namedAt,
|
|
2771
|
-
keyword_parameter: namedAt,
|
|
2772
|
-
block_parameter: namedAt,
|
|
2773
|
-
destructured_parameter: always,
|
|
2774
|
-
left_assignment_list: always,
|
|
2775
|
-
rest_assignment: always,
|
|
2776
|
-
destructured_left_assignment: always,
|
|
2777
|
-
exception_variable: always,
|
|
2778
|
-
undef: always,
|
|
2779
|
-
alias: always,
|
|
2780
|
-
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
2781
|
-
};
|
|
2782
|
-
function addNamesUnder(node, names) {
|
|
2783
|
-
if (node === null) {
|
|
2784
|
-
return;
|
|
2785
|
-
}
|
|
2786
|
-
if (node.type === "identifier") {
|
|
2787
|
-
names.add(node.text);
|
|
2788
|
-
return;
|
|
2789
|
-
}
|
|
2790
|
-
for (const child of bodyStatements(node)) {
|
|
2791
|
-
addNamesUnder(child, names);
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
function parameterNames2(node, names) {
|
|
2795
|
-
for (const parameter of bodyStatements(node)) {
|
|
2796
|
-
if (parameter.type === "identifier") {
|
|
2797
|
-
names.add(parameter.text);
|
|
2798
|
-
continue;
|
|
2799
|
-
}
|
|
2800
|
-
const declared = field(parameter, "name");
|
|
2801
|
-
if (declared !== null && declared.type === "identifier") {
|
|
2802
|
-
names.add(declared.text);
|
|
2803
|
-
continue;
|
|
2804
|
-
}
|
|
2805
|
-
addNamesUnder(parameter, names);
|
|
2806
|
-
}
|
|
2807
|
-
}
|
|
2808
|
-
function assignedNames(node, names) {
|
|
2809
|
-
addNamesUnder(field(node, "left"), names);
|
|
2810
|
-
}
|
|
2811
|
-
var BINDS_A_NAME = {
|
|
2812
|
-
assignment: assignedNames,
|
|
2813
|
-
operator_assignment: assignedNames,
|
|
2814
|
-
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
2815
|
-
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
2816
|
-
...Object.fromEntries(
|
|
2817
|
-
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames2])
|
|
2818
|
-
)
|
|
2819
|
-
};
|
|
2820
|
-
function localNamesIn(definitionNode) {
|
|
2821
|
-
const names = /* @__PURE__ */ new Set();
|
|
2822
|
-
const visit = (node) => {
|
|
2823
|
-
BINDS_A_NAME[node.type]?.(node, names);
|
|
2824
|
-
for (const child of bodyStatements(node)) {
|
|
2825
|
-
visit(child);
|
|
2826
|
-
}
|
|
2827
|
-
};
|
|
2828
|
-
visit(definitionNode);
|
|
2829
|
-
return names;
|
|
2830
|
-
}
|
|
2831
|
-
function isBareMethodCall(node, locals) {
|
|
2832
|
-
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
2833
|
-
return false;
|
|
2834
|
-
}
|
|
2835
|
-
const parent = node.parent;
|
|
2836
|
-
if (parent === null) {
|
|
2837
|
-
return false;
|
|
2838
|
-
}
|
|
2839
|
-
return SPELLS_A_NAME[parent.type]?.(parent, node) !== true;
|
|
2840
|
-
}
|
|
2989
|
+
import { constantOf, literalOf as literalOf4 } from "@suss/values";
|
|
2841
2990
|
|
|
2842
2991
|
// src/paths/lowering.ts
|
|
2843
2992
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -3125,23 +3274,21 @@ var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
|
3125
3274
|
"do_block",
|
|
3126
3275
|
"block"
|
|
3127
3276
|
]);
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
return
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
3144
|
-
};
|
|
3277
|
+
function literalArgOf(node, facts) {
|
|
3278
|
+
const value = evaluatedValue(node, facts);
|
|
3279
|
+
const text2 = literalOf4(value);
|
|
3280
|
+
if (text2 !== null) {
|
|
3281
|
+
return { kind: "string", value: text2 };
|
|
3282
|
+
}
|
|
3283
|
+
const constant2 = constantOf(value);
|
|
3284
|
+
if (typeof constant2 === "number") {
|
|
3285
|
+
return { kind: "number", value: constant2 };
|
|
3286
|
+
}
|
|
3287
|
+
if (typeof constant2 === "boolean") {
|
|
3288
|
+
return { kind: "boolean", value: constant2 };
|
|
3289
|
+
}
|
|
3290
|
+
return null;
|
|
3291
|
+
}
|
|
3145
3292
|
function children2(node) {
|
|
3146
3293
|
return node.namedChildren.filter((child) => child !== null);
|
|
3147
3294
|
}
|
|
@@ -3225,23 +3372,27 @@ function calleeText(call) {
|
|
|
3225
3372
|
}
|
|
3226
3373
|
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
3227
3374
|
}
|
|
3228
|
-
function argOf(node) {
|
|
3229
|
-
const literal =
|
|
3230
|
-
if (literal !== null
|
|
3375
|
+
function argOf(node, facts) {
|
|
3376
|
+
const literal = literalArgOf(node, facts);
|
|
3377
|
+
if (literal !== null) {
|
|
3231
3378
|
return literal;
|
|
3232
3379
|
}
|
|
3233
3380
|
if (node.type === "call" && !isArglessReceiverCall(node)) {
|
|
3234
|
-
return {
|
|
3381
|
+
return {
|
|
3382
|
+
kind: "call",
|
|
3383
|
+
callee: calleeText(node),
|
|
3384
|
+
args: argsOf(node, facts)
|
|
3385
|
+
};
|
|
3235
3386
|
}
|
|
3236
3387
|
return { kind: "identifier", name: node.text };
|
|
3237
3388
|
}
|
|
3238
|
-
function argsOf(call) {
|
|
3389
|
+
function argsOf(call, facts) {
|
|
3239
3390
|
const args = field(call, "arguments");
|
|
3240
3391
|
if (args === null) {
|
|
3241
3392
|
return [];
|
|
3242
3393
|
}
|
|
3243
3394
|
return children2(args).map(
|
|
3244
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3395
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child, facts) : argOf(child, facts)
|
|
3245
3396
|
);
|
|
3246
3397
|
}
|
|
3247
3398
|
function enclosingStatement(call, body) {
|
|
@@ -3254,7 +3405,7 @@ function enclosingStatement(call, body) {
|
|
|
3254
3405
|
}
|
|
3255
3406
|
return current;
|
|
3256
3407
|
}
|
|
3257
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS) {
|
|
3408
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3258
3409
|
const body = field(definitionNode, "body");
|
|
3259
3410
|
if (body === null) {
|
|
3260
3411
|
return [];
|
|
@@ -3294,7 +3445,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3294
3445
|
return {
|
|
3295
3446
|
type: "invocation",
|
|
3296
3447
|
callee: calleeText(call),
|
|
3297
|
-
args: argsOf(call),
|
|
3448
|
+
args: argsOf(call, facts),
|
|
3298
3449
|
async: false,
|
|
3299
3450
|
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3300
3451
|
};
|
|
@@ -3309,7 +3460,7 @@ import {
|
|
|
3309
3460
|
unreadableReading,
|
|
3310
3461
|
writtenReading
|
|
3311
3462
|
} from "@suss/extractor";
|
|
3312
|
-
import { constantOf, literalOf as
|
|
3463
|
+
import { constantOf as constantOf2, literalOf as literalOf6 } from "@suss/values";
|
|
3313
3464
|
|
|
3314
3465
|
// src/paths/predicates.ts
|
|
3315
3466
|
var OPERATORS = {
|
|
@@ -3378,7 +3529,7 @@ function binaryOf(node) {
|
|
|
3378
3529
|
return opaqueOf(node);
|
|
3379
3530
|
}
|
|
3380
3531
|
function valueRefOf(node) {
|
|
3381
|
-
const literal =
|
|
3532
|
+
const literal = literalOf5(node);
|
|
3382
3533
|
if (literal !== null) {
|
|
3383
3534
|
return literal;
|
|
3384
3535
|
}
|
|
@@ -3391,7 +3542,7 @@ function valueRefOf(node) {
|
|
|
3391
3542
|
}
|
|
3392
3543
|
return { type: "unresolved", sourceText: node.text };
|
|
3393
3544
|
}
|
|
3394
|
-
function
|
|
3545
|
+
function literalOf5(node) {
|
|
3395
3546
|
if (node.type === "integer") {
|
|
3396
3547
|
const value = Number.parseInt(node.text, 10);
|
|
3397
3548
|
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
@@ -3434,13 +3585,13 @@ function statusArgumentOf(call, declaration) {
|
|
|
3434
3585
|
}
|
|
3435
3586
|
return args.positional[declaration.statusArgument] ?? null;
|
|
3436
3587
|
}
|
|
3437
|
-
function statusNumberOf(node, names) {
|
|
3438
|
-
const value = evaluatedValue(node);
|
|
3439
|
-
const constant2 =
|
|
3588
|
+
function statusNumberOf(node, names, facts) {
|
|
3589
|
+
const value = evaluatedValue(node, facts);
|
|
3590
|
+
const constant2 = constantOf2(value);
|
|
3440
3591
|
if (typeof constant2 === "number") {
|
|
3441
3592
|
return constant2;
|
|
3442
3593
|
}
|
|
3443
|
-
const name =
|
|
3594
|
+
const name = literalOf6(value);
|
|
3444
3595
|
return name === null ? null : names[name] ?? null;
|
|
3445
3596
|
}
|
|
3446
3597
|
function declarationsByName(declarations) {
|
|
@@ -3484,12 +3635,12 @@ function collectReturns(node, found) {
|
|
|
3484
3635
|
}
|
|
3485
3636
|
return found;
|
|
3486
3637
|
}
|
|
3487
|
-
function readingOfCall(call, declaration, names) {
|
|
3638
|
+
function readingOfCall(call, declaration, names, facts) {
|
|
3488
3639
|
const argument = statusArgumentOf(call, declaration);
|
|
3489
3640
|
if (argument === null) {
|
|
3490
3641
|
return declaration.defaultStatusCode === void 0 ? absentReading : writtenReading(declaration.defaultStatusCode, rangeOf(call));
|
|
3491
3642
|
}
|
|
3492
|
-
const status = statusNumberOf(argument, names);
|
|
3643
|
+
const status = statusNumberOf(argument, names, facts);
|
|
3493
3644
|
if (status === null) {
|
|
3494
3645
|
return unreadableReading(
|
|
3495
3646
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
@@ -3633,7 +3784,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
3633
3784
|
const outcomes = [];
|
|
3634
3785
|
for (const terminal of terminals) {
|
|
3635
3786
|
const declaration = declarationOf(terminal, byName, locals);
|
|
3636
|
-
const reading = declaration === void 0 ? absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
3787
|
+
const reading = declaration === void 0 ? absentReading : readingOfCall(terminal, declaration, statusNames, options.facts);
|
|
3637
3788
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
3638
3789
|
outcomes.push({
|
|
3639
3790
|
conditions: path7.map(conditionOf),
|
|
@@ -3667,11 +3818,9 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3667
3818
|
continue;
|
|
3668
3819
|
}
|
|
3669
3820
|
for (const call of callsUnder(method)) {
|
|
3670
|
-
const request
|
|
3671
|
-
|
|
3672
|
-
continue;
|
|
3821
|
+
for (const request of requestCalls(call, pattern, options)) {
|
|
3822
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3673
3823
|
}
|
|
3674
|
-
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3675
3824
|
}
|
|
3676
3825
|
}
|
|
3677
3826
|
return units;
|
|
@@ -3700,36 +3849,60 @@ function callsUnder(node, found = []) {
|
|
|
3700
3849
|
}
|
|
3701
3850
|
return found;
|
|
3702
3851
|
}
|
|
3703
|
-
function
|
|
3852
|
+
function requestCalls(call, pattern, options) {
|
|
3704
3853
|
const receiver = field(call, "receiver");
|
|
3705
3854
|
const called = field(call, "method")?.text;
|
|
3706
|
-
if (receiver === null || called === void 0) {
|
|
3707
|
-
return
|
|
3855
|
+
if (receiver === null || called === void 0 || !isLibraryReceiver(receiver, pattern, options)) {
|
|
3856
|
+
return [];
|
|
3708
3857
|
}
|
|
3709
|
-
const
|
|
3710
|
-
|
|
3711
|
-
|
|
3858
|
+
const read = (site) => requestCall(
|
|
3859
|
+
call,
|
|
3860
|
+
called,
|
|
3861
|
+
receiverPrefix(receiver, pattern, options, site),
|
|
3862
|
+
pattern,
|
|
3863
|
+
options,
|
|
3864
|
+
site
|
|
3865
|
+
);
|
|
3866
|
+
const plain = read();
|
|
3867
|
+
if (plain !== null && !hasNameHole(plain.path)) {
|
|
3868
|
+
return [plain];
|
|
3869
|
+
}
|
|
3870
|
+
const byBoundary = /* @__PURE__ */ new Map();
|
|
3871
|
+
for (const site of constructionSitesOf(call, options.facts)) {
|
|
3872
|
+
const stated = read(site);
|
|
3873
|
+
if (stated !== null) {
|
|
3874
|
+
byBoundary.set(`${stated.method} ${stated.path}`, stated);
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
if (byBoundary.size > 0) {
|
|
3878
|
+
return [...byBoundary.values()];
|
|
3712
3879
|
}
|
|
3880
|
+
return plain === null ? [] : [plain];
|
|
3881
|
+
}
|
|
3882
|
+
function requestCall(call, called, prefix, pattern, options, site) {
|
|
3713
3883
|
const args = readCallArgs(field(call, "arguments"));
|
|
3714
3884
|
const verb = pattern.verbMethodNames[called];
|
|
3715
3885
|
if (verb !== void 0) {
|
|
3716
|
-
const path8 = urlIn(args, pattern, options);
|
|
3886
|
+
const path8 = urlIn(args, pattern, options, site);
|
|
3717
3887
|
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
3718
3888
|
}
|
|
3719
3889
|
const sent = pattern.requestObject;
|
|
3720
3890
|
if (sent === void 0 || sent.attribute !== called) {
|
|
3721
3891
|
return null;
|
|
3722
3892
|
}
|
|
3723
|
-
const built = requestBuilt(args.positional[0],
|
|
3893
|
+
const built = requestBuilt(args.positional[0], sent, options, site);
|
|
3724
3894
|
if (built === null) {
|
|
3725
3895
|
return null;
|
|
3726
3896
|
}
|
|
3727
|
-
const path7 = pathAt(built.url, options);
|
|
3897
|
+
const path7 = pathAt(built.url, options, site);
|
|
3728
3898
|
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3729
3899
|
}
|
|
3730
|
-
function requestBuilt(argument,
|
|
3731
|
-
|
|
3732
|
-
|
|
3900
|
+
function requestBuilt(argument, sent, options, site) {
|
|
3901
|
+
if (argument === void 0) {
|
|
3902
|
+
return null;
|
|
3903
|
+
}
|
|
3904
|
+
const written = writtenNodeOf(argument, options.facts, site) ?? argument;
|
|
3905
|
+
if (written.type !== "call") {
|
|
3733
3906
|
return null;
|
|
3734
3907
|
}
|
|
3735
3908
|
const requestClass = field(written, "receiver")?.text;
|
|
@@ -3740,28 +3913,18 @@ function requestBuilt(argument, method, sent) {
|
|
|
3740
3913
|
const args = readCallArgs(field(written, "arguments"));
|
|
3741
3914
|
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3742
3915
|
}
|
|
3743
|
-
function
|
|
3744
|
-
|
|
3745
|
-
return null;
|
|
3746
|
-
}
|
|
3747
|
-
const value = assignedValue(node.text, method);
|
|
3748
|
-
return value !== null && value.type === "call" ? value : null;
|
|
3916
|
+
function isLibraryReceiver(receiver, pattern, options) {
|
|
3917
|
+
return namesConstant(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
3749
3918
|
}
|
|
3750
|
-
function receiverPrefix(receiver,
|
|
3751
|
-
|
|
3752
|
-
return "";
|
|
3753
|
-
}
|
|
3754
|
-
if (receiver.type !== "identifier") {
|
|
3755
|
-
return null;
|
|
3756
|
-
}
|
|
3757
|
-
const built = builtBy(receiver.text, method, root, pattern);
|
|
3919
|
+
function receiverPrefix(receiver, pattern, options, site) {
|
|
3920
|
+
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
3758
3921
|
if (built === null) {
|
|
3759
|
-
return
|
|
3922
|
+
return "";
|
|
3760
3923
|
}
|
|
3761
3924
|
const args = readCallArgs(field(built, "arguments"));
|
|
3762
3925
|
const keyword = pattern.builderUrlKeyword;
|
|
3763
3926
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
3764
|
-
const path7 = base === void 0 ? null : pathOf(evaluatedValue(base, options.facts));
|
|
3927
|
+
const path7 = base === void 0 ? null : pathOf(evaluatedValue(base, options.facts, void 0, site));
|
|
3765
3928
|
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3766
3929
|
}
|
|
3767
3930
|
function trimmed(path7) {
|
|
@@ -3773,61 +3936,28 @@ function namesConstant(receiver, constantName2) {
|
|
|
3773
3936
|
}
|
|
3774
3937
|
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
3775
3938
|
}
|
|
3776
|
-
function
|
|
3939
|
+
function builderCallBehind(receiver, pattern, facts) {
|
|
3777
3940
|
const builders = pattern.receiverBuilders ?? [];
|
|
3778
3941
|
if (builders.length === 0) {
|
|
3779
3942
|
return null;
|
|
3780
3943
|
}
|
|
3781
|
-
const
|
|
3782
|
-
if (
|
|
3944
|
+
const written = writtenNodeOf(receiver, facts);
|
|
3945
|
+
if (written === null || written.type !== "call" || !builders.includes(field(written, "method")?.text ?? "")) {
|
|
3783
3946
|
return null;
|
|
3784
3947
|
}
|
|
3785
|
-
const
|
|
3786
|
-
return
|
|
3948
|
+
const constant2 = field(written, "receiver");
|
|
3949
|
+
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3787
3950
|
}
|
|
3788
|
-
function
|
|
3789
|
-
const defined = methodDefinitions(root).find(
|
|
3790
|
-
(candidate) => field(candidate, "name")?.text === name
|
|
3791
|
-
);
|
|
3792
|
-
if (defined === void 0) {
|
|
3793
|
-
return null;
|
|
3794
|
-
}
|
|
3795
|
-
const body = field(defined, "body");
|
|
3796
|
-
const statements = body === null ? [] : body.namedChildren;
|
|
3797
|
-
const last = statements[statements.length - 1];
|
|
3798
|
-
if (last === void 0) {
|
|
3799
|
-
return null;
|
|
3800
|
-
}
|
|
3801
|
-
if (last.type === "operator_assignment" || last.type === "assignment") {
|
|
3802
|
-
return field(last, "right");
|
|
3803
|
-
}
|
|
3804
|
-
return last.type === "return" ? last.namedChildren[0] ?? null : last;
|
|
3805
|
-
}
|
|
3806
|
-
function assignedValue(name, method) {
|
|
3807
|
-
const body = field(method, "body");
|
|
3808
|
-
if (body === null) {
|
|
3809
|
-
return null;
|
|
3810
|
-
}
|
|
3811
|
-
for (const statement of runStatements(body)) {
|
|
3812
|
-
if (statement.type !== "assignment") {
|
|
3813
|
-
continue;
|
|
3814
|
-
}
|
|
3815
|
-
if (field(statement, "left")?.text === name) {
|
|
3816
|
-
return field(statement, "right");
|
|
3817
|
-
}
|
|
3818
|
-
}
|
|
3819
|
-
return null;
|
|
3820
|
-
}
|
|
3821
|
-
function urlIn(args, pattern, options) {
|
|
3951
|
+
function urlIn(args, pattern, options, site) {
|
|
3822
3952
|
const keyword = pattern.url.keyword;
|
|
3823
3953
|
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3824
|
-
return pathAt(written, options);
|
|
3954
|
+
return pathAt(written, options, site);
|
|
3825
3955
|
}
|
|
3826
|
-
function pathAt(written, options) {
|
|
3956
|
+
function pathAt(written, options, site) {
|
|
3827
3957
|
if (written === void 0) {
|
|
3828
3958
|
return null;
|
|
3829
3959
|
}
|
|
3830
|
-
const path7 = pathOf(evaluatedValue(written, options.facts));
|
|
3960
|
+
const path7 = pathOf(evaluatedValue(written, options.facts, void 0, site));
|
|
3831
3961
|
return path7 === void 0 || namesNothing(path7) ? null : path7;
|
|
3832
3962
|
}
|
|
3833
3963
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
@@ -3850,15 +3980,15 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
3850
3980
|
recognition: pack.name
|
|
3851
3981
|
}),
|
|
3852
3982
|
parameters: [],
|
|
3853
|
-
branches: callerBranches(method, range),
|
|
3983
|
+
branches: callerBranches(method, range, options.facts),
|
|
3854
3984
|
...responseAccessors(pattern),
|
|
3855
3985
|
bodyContent: "statements",
|
|
3856
3986
|
dependencyCalls: [],
|
|
3857
3987
|
declaredContract: null
|
|
3858
3988
|
};
|
|
3859
3989
|
}
|
|
3860
|
-
function callerBranches(method, range) {
|
|
3861
|
-
const effects = invocationEffects(method);
|
|
3990
|
+
function callerBranches(method, range, facts) {
|
|
3991
|
+
const effects = invocationEffects(method, void 0, void 0, facts);
|
|
3862
3992
|
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3863
3993
|
}
|
|
3864
3994
|
function responseAccessors(pattern) {
|
|
@@ -4016,7 +4146,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4016
4146
|
}
|
|
4017
4147
|
const form = byName.get(called);
|
|
4018
4148
|
if (form !== void 0) {
|
|
4019
|
-
for (const declaration of declarationsOf(statement, form)) {
|
|
4149
|
+
for (const declaration of declarationsOf(statement, form, read.facts)) {
|
|
4020
4150
|
declared = [
|
|
4021
4151
|
...declared.filter((earlier) => !sameFilter(earlier, declaration)),
|
|
4022
4152
|
declaration
|
|
@@ -4026,7 +4156,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4026
4156
|
}
|
|
4027
4157
|
const skipped = skipNames.get(called);
|
|
4028
4158
|
if (skipped !== void 0) {
|
|
4029
|
-
declared = applySkips(declared, skipsOf(statement, skipped));
|
|
4159
|
+
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
4030
4160
|
}
|
|
4031
4161
|
}
|
|
4032
4162
|
const resolved = [];
|
|
@@ -4063,14 +4193,14 @@ function filterReference(filter, displayPath) {
|
|
|
4063
4193
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
4064
4194
|
};
|
|
4065
4195
|
}
|
|
4066
|
-
function filterUnit(filter, pattern, displayPath, body) {
|
|
4196
|
+
function filterUnit(filter, pattern, displayPath, body, facts) {
|
|
4067
4197
|
const range = rangeOf(filter.method);
|
|
4068
4198
|
const branches = responseBranches(
|
|
4069
4199
|
filter.method,
|
|
4070
4200
|
pattern,
|
|
4071
4201
|
body.effects ?? [],
|
|
4072
4202
|
body.extraEffects,
|
|
4073
|
-
{ fallthrough: "handOn" }
|
|
4203
|
+
{ fallthrough: "handOn", facts }
|
|
4074
4204
|
);
|
|
4075
4205
|
return {
|
|
4076
4206
|
identity: {
|
|
@@ -4118,30 +4248,30 @@ function calledName(statement) {
|
|
|
4118
4248
|
}
|
|
4119
4249
|
return field(statement, "method")?.text ?? null;
|
|
4120
4250
|
}
|
|
4121
|
-
function declarationsOf(statement, filter) {
|
|
4251
|
+
function declarationsOf(statement, filter, facts) {
|
|
4122
4252
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4123
4253
|
const keywords = filter.actionKeywords;
|
|
4124
|
-
const only = keywords === void 0 ? null :
|
|
4125
|
-
const except = keywords === void 0 ? null :
|
|
4126
|
-
return methodNamesOf(args, filter).map((methodName) => ({
|
|
4254
|
+
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
4255
|
+
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
4256
|
+
return methodNamesOf(args, filter, facts).map((methodName) => ({
|
|
4127
4257
|
filter,
|
|
4128
4258
|
methodName,
|
|
4129
4259
|
only,
|
|
4130
4260
|
except: except ?? /* @__PURE__ */ new Set()
|
|
4131
4261
|
}));
|
|
4132
4262
|
}
|
|
4133
|
-
function methodNamesOf(args, filter) {
|
|
4263
|
+
function methodNamesOf(args, filter, facts) {
|
|
4134
4264
|
if (filter.methodFrom === "withKeyword") {
|
|
4135
4265
|
const named2 = args.keyword.with;
|
|
4136
|
-
const value = named2 === void 0 ? null :
|
|
4266
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
4137
4267
|
return value === null ? [] : [value];
|
|
4138
4268
|
}
|
|
4139
|
-
return args.positional.map((arg) =>
|
|
4269
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
4140
4270
|
}
|
|
4141
|
-
function skipsOf(statement, filterName) {
|
|
4271
|
+
function skipsOf(statement, filterName, facts) {
|
|
4142
4272
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4143
|
-
const actions =
|
|
4144
|
-
return args.positional.map((arg) =>
|
|
4273
|
+
const actions = actionsUnder(args.keyword.only, facts);
|
|
4274
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
4145
4275
|
}
|
|
4146
4276
|
function sameFilter(one, other) {
|
|
4147
4277
|
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
@@ -4165,26 +4295,12 @@ function applySkips(declared, skips) {
|
|
|
4165
4295
|
}
|
|
4166
4296
|
return left;
|
|
4167
4297
|
}
|
|
4168
|
-
function
|
|
4298
|
+
function actionsUnder(node, facts) {
|
|
4169
4299
|
if (node === void 0) {
|
|
4170
4300
|
return null;
|
|
4171
4301
|
}
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
return /* @__PURE__ */ new Set([single]);
|
|
4175
|
-
}
|
|
4176
|
-
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
4177
|
-
return null;
|
|
4178
|
-
}
|
|
4179
|
-
const names = /* @__PURE__ */ new Set();
|
|
4180
|
-
for (const child of bodyStatements(node)) {
|
|
4181
|
-
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
4182
|
-
const name = symbolValue(child) ?? bare;
|
|
4183
|
-
if (name !== null) {
|
|
4184
|
-
names.add(name);
|
|
4185
|
-
}
|
|
4186
|
-
}
|
|
4187
|
-
return names;
|
|
4302
|
+
const found = namesOf(node, facts);
|
|
4303
|
+
return found === null ? null : new Set(found);
|
|
4188
4304
|
}
|
|
4189
4305
|
|
|
4190
4306
|
// src/storage.ts
|
|
@@ -4262,67 +4378,64 @@ function argumentsOf2(call) {
|
|
|
4262
4378
|
const args = field(call, "arguments");
|
|
4263
4379
|
return args === null ? [] : children3(args);
|
|
4264
4380
|
}
|
|
4265
|
-
function
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
function bareName(node) {
|
|
4269
|
-
return node.text.replace(/^:/, "").replace(/:$/, "");
|
|
4270
|
-
}
|
|
4271
|
-
function pairsIn(argument) {
|
|
4272
|
-
if (argument.type === "pair") {
|
|
4273
|
-
return [argument];
|
|
4274
|
-
}
|
|
4275
|
-
if (argument.type === "hash") {
|
|
4276
|
-
return children3(argument).filter((child) => child.type === "pair");
|
|
4277
|
-
}
|
|
4278
|
-
return [];
|
|
4381
|
+
function recordFieldsOf(argument, facts) {
|
|
4382
|
+
const value = evaluatedValue(argument, facts);
|
|
4383
|
+
return value.kind === "record" ? [...value.fields.keys()] : [];
|
|
4279
4384
|
}
|
|
4280
|
-
function keywordKeys(call) {
|
|
4385
|
+
function keywordKeys(call, facts) {
|
|
4281
4386
|
const keys = [];
|
|
4282
4387
|
for (const argument of argumentsOf2(call)) {
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4388
|
+
if (argument.type !== "pair") {
|
|
4389
|
+
keys.push(...recordFieldsOf(argument, facts));
|
|
4390
|
+
continue;
|
|
4391
|
+
}
|
|
4392
|
+
const key = field(argument, "key");
|
|
4393
|
+
const name = key === null ? null : keyNameOf(key);
|
|
4394
|
+
if (name !== null) {
|
|
4395
|
+
keys.push(name);
|
|
4288
4396
|
}
|
|
4289
4397
|
}
|
|
4290
4398
|
return keys;
|
|
4291
4399
|
}
|
|
4292
|
-
function
|
|
4293
|
-
return
|
|
4400
|
+
function keyNameOf(key) {
|
|
4401
|
+
return hashKeySymbolName(key) ?? symbolValue(key) ?? stringLiteralValue(key);
|
|
4294
4402
|
}
|
|
4295
|
-
function
|
|
4403
|
+
function hasPositionalArgument(call, facts) {
|
|
4404
|
+
return argumentsOf2(call).some(
|
|
4405
|
+
(argument) => argument.type !== "pair" && recordFieldsOf(argument, facts).length === 0
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
function columnsAskedFor(call, pattern, facts) {
|
|
4296
4409
|
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4297
4410
|
return [];
|
|
4298
4411
|
}
|
|
4299
|
-
return argumentsOf2(call).
|
|
4412
|
+
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
4300
4413
|
}
|
|
4301
|
-
function primaryKeySelector(call, pattern) {
|
|
4414
|
+
function primaryKeySelector(call, pattern, facts) {
|
|
4302
4415
|
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4303
4416
|
if (byPrimaryKey === void 0) {
|
|
4304
4417
|
return [];
|
|
4305
4418
|
}
|
|
4306
|
-
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call);
|
|
4419
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
|
|
4307
4420
|
return picks ? [byPrimaryKey.column] : [];
|
|
4308
4421
|
}
|
|
4309
|
-
function selectorOf(call, pattern) {
|
|
4422
|
+
function selectorOf(call, pattern, facts) {
|
|
4310
4423
|
const picked = [];
|
|
4311
4424
|
for (const link of chainLinks(call)) {
|
|
4312
|
-
picked.push(...primaryKeySelector(link, pattern));
|
|
4425
|
+
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4313
4426
|
if (pattern.reads.includes(methodOf(link))) {
|
|
4314
|
-
picked.push(...keywordKeys(link));
|
|
4427
|
+
picked.push(...keywordKeys(link, facts));
|
|
4315
4428
|
}
|
|
4316
4429
|
}
|
|
4317
4430
|
return [...new Set(picked)];
|
|
4318
4431
|
}
|
|
4319
|
-
function fieldsOf(call, pattern, kind) {
|
|
4432
|
+
function fieldsOf(call, pattern, kind, facts) {
|
|
4320
4433
|
if (kind === "write") {
|
|
4321
|
-
return [...new Set(keywordKeys(call))];
|
|
4434
|
+
return [...new Set(keywordKeys(call, facts))];
|
|
4322
4435
|
}
|
|
4323
4436
|
return [
|
|
4324
4437
|
...new Set(
|
|
4325
|
-
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern))
|
|
4438
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern, facts))
|
|
4326
4439
|
)
|
|
4327
4440
|
];
|
|
4328
4441
|
}
|
|
@@ -4414,8 +4527,8 @@ function modelCallEffects(call, file, options, enclosing) {
|
|
|
4414
4527
|
target.container,
|
|
4415
4528
|
pattern,
|
|
4416
4529
|
kind,
|
|
4417
|
-
selectorOf(worked, pattern),
|
|
4418
|
-
fieldsOf(worked, pattern, kind)
|
|
4530
|
+
selectorOf(worked, pattern, options.facts),
|
|
4531
|
+
fieldsOf(worked, pattern, kind, options.facts)
|
|
4419
4532
|
)
|
|
4420
4533
|
];
|
|
4421
4534
|
}
|
|
@@ -4735,7 +4848,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4735
4848
|
filter,
|
|
4736
4849
|
pattern,
|
|
4737
4850
|
displayPath,
|
|
4738
|
-
bodyOfMethod(filter.method, filter.file, options)
|
|
4851
|
+
bodyOfMethod(filter.method, filter.file, options),
|
|
4852
|
+
options.facts
|
|
4739
4853
|
);
|
|
4740
4854
|
units.push(raw);
|
|
4741
4855
|
options.onReachSeed?.(raw, {
|
|
@@ -4826,7 +4940,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
4826
4940
|
method,
|
|
4827
4941
|
pattern,
|
|
4828
4942
|
body.effects ?? [],
|
|
4829
|
-
body.extraEffects
|
|
4943
|
+
body.extraEffects,
|
|
4944
|
+
{ facts: bodyRead.facts }
|
|
4830
4945
|
);
|
|
4831
4946
|
return {
|
|
4832
4947
|
identity: {
|
|
@@ -4956,7 +5071,8 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
4956
5071
|
const effects = invocationEffects(
|
|
4957
5072
|
method,
|
|
4958
5073
|
bodyRead.inheritedMethods,
|
|
4959
|
-
EVERY_ARGLESS_CALL
|
|
5074
|
+
EVERY_ARGLESS_CALL,
|
|
5075
|
+
bodyRead.facts
|
|
4960
5076
|
);
|
|
4961
5077
|
const storage = bodyRead.storage;
|
|
4962
5078
|
const extra = [
|
|
@@ -5694,6 +5810,7 @@ function parseWith(language, source) {
|
|
|
5694
5810
|
import fs3 from "fs";
|
|
5695
5811
|
import path4 from "path";
|
|
5696
5812
|
import {
|
|
5813
|
+
boundaryKey,
|
|
5697
5814
|
disambiguateSummaryIds,
|
|
5698
5815
|
linkCallsToSummaries,
|
|
5699
5816
|
placeArgTargets as placeArgTargets2,
|
|
@@ -5714,6 +5831,7 @@ import {
|
|
|
5714
5831
|
runDigest,
|
|
5715
5832
|
stampModuleImports
|
|
5716
5833
|
} from "@suss/extractor";
|
|
5834
|
+
import { addPackWords } from "@suss/resolution";
|
|
5717
5835
|
|
|
5718
5836
|
// src/diagnostics.ts
|
|
5719
5837
|
import {
|
|
@@ -6494,20 +6612,41 @@ function associationCallsIn(packs) {
|
|
|
6494
6612
|
)
|
|
6495
6613
|
);
|
|
6496
6614
|
}
|
|
6497
|
-
function
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
}
|
|
6615
|
+
function packWordsOf(packs) {
|
|
6616
|
+
return {
|
|
6617
|
+
givesBackOne: packs.flatMap(
|
|
6618
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6619
|
+
(pattern) => pattern.baseClasses.flatMap(
|
|
6620
|
+
(base) => pattern.givesBack.map((method) => ({ base, method }))
|
|
6621
|
+
)
|
|
6622
|
+
)
|
|
6623
|
+
)
|
|
6624
|
+
};
|
|
6625
|
+
}
|
|
6626
|
+
function factsForFile(options) {
|
|
6627
|
+
const db = new Database2();
|
|
6628
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6629
|
+
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6630
|
+
emitConstantBindings(db, [
|
|
6631
|
+
collectFileConstants(
|
|
6632
|
+
options.file,
|
|
6633
|
+
options.root,
|
|
6634
|
+
associationCallsIn(options.packs),
|
|
6635
|
+
inflectionsIn(options.packs)
|
|
6636
|
+
)
|
|
6637
|
+
]);
|
|
6638
|
+
emitRequireFacts(db, options.file, options.root, /* @__PURE__ */ new Set([options.file]));
|
|
6639
|
+
bindEvaluator(db, {
|
|
6640
|
+
files: [{ file: options.file, root: options.root }],
|
|
6641
|
+
definitions: new Map(methodDefinitionsIn(options.file, options.root))
|
|
6642
|
+
});
|
|
6643
|
+
addPackWords(db, packWordsOf(options.packs));
|
|
6644
|
+
return db;
|
|
6507
6645
|
}
|
|
6508
6646
|
function alreadyDiscovered(seen, raw) {
|
|
6509
6647
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
6510
|
-
const
|
|
6648
|
+
const boundary = raw.boundaryBinding === null ? "" : boundaryKey(raw.boundaryBinding) ?? "";
|
|
6649
|
+
const key = `${raw.identity.file}::${reported}::${raw.identity.range.start}::${boundary}`;
|
|
6511
6650
|
if (seen.has(key)) {
|
|
6512
6651
|
return true;
|
|
6513
6652
|
}
|
|
@@ -6574,7 +6713,7 @@ async function extractRubyProject(options) {
|
|
|
6574
6713
|
emitRequireFacts(db, file, root, known);
|
|
6575
6714
|
}
|
|
6576
6715
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6577
|
-
|
|
6716
|
+
addPackWords(db, packWordsOf(options.packs));
|
|
6578
6717
|
return readDynamicNames(
|
|
6579
6718
|
db,
|
|
6580
6719
|
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
@@ -6847,6 +6986,10 @@ function displayNameOf(node) {
|
|
|
6847
6986
|
function width(node) {
|
|
6848
6987
|
return node.endIndex - node.startIndex;
|
|
6849
6988
|
}
|
|
6989
|
+
function askedKey(value) {
|
|
6990
|
+
const enclosing = enclosingMethod2(value.node);
|
|
6991
|
+
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6992
|
+
}
|
|
6850
6993
|
var RubyWhySession = class {
|
|
6851
6994
|
root;
|
|
6852
6995
|
db = new Database3();
|
|
@@ -6922,7 +7065,7 @@ var RubyWhySession = class {
|
|
|
6922
7065
|
return explainResolvedKey({
|
|
6923
7066
|
db: this.db,
|
|
6924
7067
|
rules: WITNESS_RULES,
|
|
6925
|
-
key:
|
|
7068
|
+
key: askedKey(value),
|
|
6926
7069
|
locate: (key) => this.locate(key),
|
|
6927
7070
|
displayPath: (key) => this.displayPath(key),
|
|
6928
7071
|
...options
|
|
@@ -7039,6 +7182,7 @@ export {
|
|
|
7039
7182
|
emitValueFacts,
|
|
7040
7183
|
evaluatedValue,
|
|
7041
7184
|
extractRubyProject,
|
|
7185
|
+
factsForFile,
|
|
7042
7186
|
field,
|
|
7043
7187
|
findRubyFiles,
|
|
7044
7188
|
graphqlTypeNameFromQualified,
|