@suss/adapter-ruby 0.30.0 → 0.31.1
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 +562 -417
- package/dist/index.d.cts +29 -5
- package/dist/index.d.ts +29 -5
- package/dist/index.js +563 -412
- 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,
|
|
@@ -407,7 +408,7 @@ function autoloadDirectories(root) {
|
|
|
407
408
|
|
|
408
409
|
// src/defineMethod.ts
|
|
409
410
|
var import_resolution5 = require("@suss/resolution");
|
|
410
|
-
var
|
|
411
|
+
var import_values5 = require("@suss/values");
|
|
411
412
|
|
|
412
413
|
// src/values/evaluator.ts
|
|
413
414
|
var import_resolution4 = require("@suss/resolution");
|
|
@@ -416,58 +417,45 @@ var import_values2 = require("@suss/values");
|
|
|
416
417
|
// src/facts/resolve.ts
|
|
417
418
|
var import_datalog = require("@suss/datalog");
|
|
418
419
|
var import_resolution = require("@suss/resolution");
|
|
419
|
-
var RUBY_RULES = [
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
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.
|
|
423
424
|
(0, import_datalog.rule)(
|
|
424
|
-
"
|
|
425
|
-
[(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],
|
|
426
427
|
[
|
|
427
428
|
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.constant)("new")),
|
|
428
429
|
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("cls")),
|
|
429
430
|
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("cls"))
|
|
430
431
|
]
|
|
431
432
|
),
|
|
432
|
-
// The
|
|
433
|
-
//
|
|
434
|
-
//
|
|
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.
|
|
435
436
|
(0, import_datalog.rule)(
|
|
436
|
-
"
|
|
437
|
-
[(0, import_datalog.variable)("
|
|
437
|
+
"constructsNamed",
|
|
438
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("cls")],
|
|
438
439
|
[
|
|
439
|
-
(0, import_datalog.lit)("
|
|
440
|
-
(0, import_datalog.lit)("
|
|
441
|
-
(0, import_datalog.lit)("
|
|
442
|
-
|
|
443
|
-
(0, import_datalog.lit)("givesBackOne", (0, import_datalog.variable)("n"), (0, import_datalog.variable)("m"))
|
|
444
|
-
],
|
|
445
|
-
"declared finder"
|
|
446
|
-
),
|
|
447
|
-
// Ruby cannot refer to a method without running it, so
|
|
448
|
-
// `Settings.filters` is worth what the method returns. The shared
|
|
449
|
-
// rules hang that step on a `call` fact this spelling never gets.
|
|
450
|
-
(0, import_datalog.rule)(
|
|
451
|
-
"invokes",
|
|
452
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("f")],
|
|
453
|
-
[
|
|
454
|
-
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.variable)("m")),
|
|
455
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("f")),
|
|
456
|
-
(0, import_datalog.lit)("func", (0, import_datalog.variable)("f"))
|
|
457
|
-
],
|
|
458
|
-
"property read runs a method"
|
|
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
|
+
]
|
|
459
444
|
),
|
|
460
445
|
// `%i[a b].freeze` is worth the list it was written as. The evaluator
|
|
461
446
|
// says the same in its row table, for a value it reads in one file.
|
|
462
447
|
...["freeze", "dup"].map(
|
|
463
448
|
(method) => (0, import_datalog.rule)(
|
|
464
|
-
"
|
|
465
|
-
[(0, import_datalog.variable)("
|
|
466
|
-
[
|
|
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
|
+
],
|
|
467
455
|
"hands back the receiver"
|
|
468
456
|
)
|
|
469
457
|
)
|
|
470
|
-
];
|
|
458
|
+
]);
|
|
471
459
|
var RUBY_PROGRAM = (0, import_resolution.resolutionProgram)(RUBY_RULES);
|
|
472
460
|
function resolveValues(db, keys) {
|
|
473
461
|
(0, import_resolution.askResolution)(db, keys, "wanted", RUBY_PROGRAM);
|
|
@@ -479,10 +467,125 @@ function writtenValueOf(db, key) {
|
|
|
479
467
|
resolveValues(db, [key]);
|
|
480
468
|
return (0, import_resolution.writtenValueOf)(db, key, (keys) => resolveValues(db, keys));
|
|
481
469
|
}
|
|
470
|
+
function writtenValueUnder(db, key, site) {
|
|
471
|
+
const outcome = (0, import_resolution.askResolutionUnder)(
|
|
472
|
+
db,
|
|
473
|
+
[[key, site]],
|
|
474
|
+
(0, import_resolution.resolutionUnderProgram)(RUBY_RULES)
|
|
475
|
+
);
|
|
476
|
+
if (outcome === "abandoned") {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
return (0, import_resolution.writtenValueUnder)(db, key, site);
|
|
480
|
+
}
|
|
481
|
+
function constructionSites(db, classKey) {
|
|
482
|
+
(0, import_resolution.askResolution)(db, [classKey], "wantedSites", RUBY_PROGRAM);
|
|
483
|
+
return (0, import_resolution.allocationSitesOf)(db, classKey);
|
|
484
|
+
}
|
|
482
485
|
|
|
483
486
|
// src/facts/values.ts
|
|
484
487
|
var import_resolution3 = require("@suss/resolution");
|
|
485
488
|
|
|
489
|
+
// src/paths/bareCalls.ts
|
|
490
|
+
var PARAMETER_LIST_TYPES = [
|
|
491
|
+
"method_parameters",
|
|
492
|
+
"block_parameters",
|
|
493
|
+
"lambda_parameters",
|
|
494
|
+
"parameters"
|
|
495
|
+
];
|
|
496
|
+
function isFieldOf(parent, node, name) {
|
|
497
|
+
return field(parent, name)?.id === node.id;
|
|
498
|
+
}
|
|
499
|
+
function namedAt(parent, node) {
|
|
500
|
+
return isFieldOf(parent, node, "name");
|
|
501
|
+
}
|
|
502
|
+
function always() {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
var SPELLS_A_NAME = {
|
|
506
|
+
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
507
|
+
method: namedAt,
|
|
508
|
+
singleton_method: namedAt,
|
|
509
|
+
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
510
|
+
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
511
|
+
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
512
|
+
optional_parameter: namedAt,
|
|
513
|
+
splat_parameter: namedAt,
|
|
514
|
+
hash_splat_parameter: namedAt,
|
|
515
|
+
keyword_parameter: namedAt,
|
|
516
|
+
block_parameter: namedAt,
|
|
517
|
+
destructured_parameter: always,
|
|
518
|
+
left_assignment_list: always,
|
|
519
|
+
rest_assignment: always,
|
|
520
|
+
destructured_left_assignment: always,
|
|
521
|
+
exception_variable: always,
|
|
522
|
+
undef: always,
|
|
523
|
+
alias: always,
|
|
524
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
525
|
+
};
|
|
526
|
+
function addNamesUnder(node, names) {
|
|
527
|
+
if (node === null) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (node.type === "identifier") {
|
|
531
|
+
names.add(node.text);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
for (const child of bodyStatements(node)) {
|
|
535
|
+
addNamesUnder(child, names);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function parameterNames(node, names) {
|
|
539
|
+
for (const parameter of bodyStatements(node)) {
|
|
540
|
+
if (parameter.type === "identifier") {
|
|
541
|
+
names.add(parameter.text);
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
const declared = field(parameter, "name");
|
|
545
|
+
if (declared !== null && declared.type === "identifier") {
|
|
546
|
+
names.add(declared.text);
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
addNamesUnder(parameter, names);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function assignedNames(node, names) {
|
|
553
|
+
addNamesUnder(field(node, "left"), names);
|
|
554
|
+
}
|
|
555
|
+
var BINDS_A_NAME = {
|
|
556
|
+
assignment: assignedNames,
|
|
557
|
+
operator_assignment: assignedNames,
|
|
558
|
+
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
559
|
+
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
560
|
+
...Object.fromEntries(
|
|
561
|
+
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
562
|
+
)
|
|
563
|
+
};
|
|
564
|
+
function localNamesIn(definitionNode) {
|
|
565
|
+
const names = /* @__PURE__ */ new Set();
|
|
566
|
+
const visit = (node) => {
|
|
567
|
+
BINDS_A_NAME[node.type]?.(node, names);
|
|
568
|
+
for (const child of bodyStatements(node)) {
|
|
569
|
+
visit(child);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
visit(definitionNode);
|
|
573
|
+
return names;
|
|
574
|
+
}
|
|
575
|
+
function spellsAName(node) {
|
|
576
|
+
const parent = node.parent;
|
|
577
|
+
if (parent === null) {
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
581
|
+
}
|
|
582
|
+
function isBareMethodCall(node, locals) {
|
|
583
|
+
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
return !spellsAName(node);
|
|
587
|
+
}
|
|
588
|
+
|
|
486
589
|
// src/facts/locals.ts
|
|
487
590
|
var import_resolution2 = require("@suss/resolution");
|
|
488
591
|
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
@@ -507,7 +610,7 @@ function parametersOf(owner) {
|
|
|
507
610
|
const params = field(owner, "parameters");
|
|
508
611
|
return params === null ? [] : bodyStatements(params);
|
|
509
612
|
}
|
|
510
|
-
function
|
|
613
|
+
function parameterNames2(owner) {
|
|
511
614
|
const names = /* @__PURE__ */ new Set();
|
|
512
615
|
for (const param of parametersOf(owner)) {
|
|
513
616
|
const name = paramNameOf(param);
|
|
@@ -555,6 +658,13 @@ function readForVariable(node) {
|
|
|
555
658
|
}
|
|
556
659
|
return targetsOf(pattern).map((target) => writeOf(target, null, node, false));
|
|
557
660
|
}
|
|
661
|
+
function readRescueVariable(node) {
|
|
662
|
+
const variable = field(node, "variable");
|
|
663
|
+
if (variable === null) {
|
|
664
|
+
return [];
|
|
665
|
+
}
|
|
666
|
+
return bodyStatements(variable).flatMap(targetsOf).map((target) => writeOf(target, null, node, false));
|
|
667
|
+
}
|
|
558
668
|
function readBlockParameters(node) {
|
|
559
669
|
const found = [];
|
|
560
670
|
for (const param of parametersOf(node)) {
|
|
@@ -569,6 +679,7 @@ var WRITE_READERS = {
|
|
|
569
679
|
assignment: readAssignment,
|
|
570
680
|
operator_assignment: readOperatorAssignment,
|
|
571
681
|
for: readForVariable,
|
|
682
|
+
rescue: readRescueVariable,
|
|
572
683
|
block: readBlockParameters,
|
|
573
684
|
do_block: readBlockParameters
|
|
574
685
|
};
|
|
@@ -614,7 +725,7 @@ function namesOfMethod(method) {
|
|
|
614
725
|
if (remembered !== void 0) {
|
|
615
726
|
return remembered;
|
|
616
727
|
}
|
|
617
|
-
const names = scopeNames(
|
|
728
|
+
const names = scopeNames(parameterNames2(method), field(method, "body"));
|
|
618
729
|
cache.byMethod.set(method, names);
|
|
619
730
|
return names;
|
|
620
731
|
}
|
|
@@ -636,10 +747,17 @@ function ownerOfName(node, name, enclosing) {
|
|
|
636
747
|
}
|
|
637
748
|
return enclosing !== null && names.locals.has(name) ? enclosing : null;
|
|
638
749
|
}
|
|
750
|
+
function isLocalName(node, name, enclosing) {
|
|
751
|
+
const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
|
|
752
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
return names.locals.has(name);
|
|
756
|
+
}
|
|
639
757
|
function blockDeclaring(node, name) {
|
|
640
758
|
let current = node.parent;
|
|
641
759
|
while (current !== null) {
|
|
642
|
-
if (BLOCK_TYPES2.has(current.type) &&
|
|
760
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames2(current).has(name)) {
|
|
643
761
|
return current;
|
|
644
762
|
}
|
|
645
763
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -695,6 +813,17 @@ function collectWrites(method, body) {
|
|
|
695
813
|
}
|
|
696
814
|
return [...groups.values()];
|
|
697
815
|
}
|
|
816
|
+
function instanceWritesRunInOrder(body, name, targets) {
|
|
817
|
+
return (0, import_resolution2.writesRunInOrder)(
|
|
818
|
+
body,
|
|
819
|
+
name,
|
|
820
|
+
targets.map((target) => ({
|
|
821
|
+
at: target,
|
|
822
|
+
direct: target.parent?.parent?.id === body.id
|
|
823
|
+
})),
|
|
824
|
+
nameReads(new Set(targets.map((target) => target.id)))
|
|
825
|
+
);
|
|
826
|
+
}
|
|
698
827
|
function orderedWrite(write, scope) {
|
|
699
828
|
return {
|
|
700
829
|
at: write.at,
|
|
@@ -740,12 +869,28 @@ function readKey(filePath, written, enclosing) {
|
|
|
740
869
|
if (node.type !== "identifier" && node.type !== "constant") {
|
|
741
870
|
return nodeId(filePath, node);
|
|
742
871
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
872
|
+
if (isMethodOfReceiver(node) || isBareCall(node, enclosing)) {
|
|
873
|
+
return nodeId(filePath, node);
|
|
746
874
|
}
|
|
747
875
|
return nameKey(filePath, node, enclosing);
|
|
748
876
|
}
|
|
877
|
+
function isMethodOfReceiver(node) {
|
|
878
|
+
const parent = node.parent;
|
|
879
|
+
return parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null;
|
|
880
|
+
}
|
|
881
|
+
function isReceiverOfCall(node) {
|
|
882
|
+
const parent = node.parent;
|
|
883
|
+
return parent !== null && parent.type === "call" && field(parent, "receiver")?.id === node.id;
|
|
884
|
+
}
|
|
885
|
+
function isBareCall(node, enclosing) {
|
|
886
|
+
if (node.type !== "identifier") {
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
if (spellsAName(node) && !isReceiverOfCall(node)) {
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
892
|
+
return !isLocalName(node, node.text, enclosing);
|
|
893
|
+
}
|
|
749
894
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
750
895
|
"string",
|
|
751
896
|
"integer",
|
|
@@ -774,11 +919,7 @@ function add(emitter, relation, ...tuple) {
|
|
|
774
919
|
emitter.db.add(relation, tuple);
|
|
775
920
|
}
|
|
776
921
|
function valueKey(emitter, written) {
|
|
777
|
-
|
|
778
|
-
if (value.type !== "identifier" && value.type !== "constant") {
|
|
779
|
-
return nodeId(emitter.filePath, value);
|
|
780
|
-
}
|
|
781
|
-
return nameKey(emitter.filePath, value, emitter.enclosing);
|
|
922
|
+
return readKey(emitter.filePath, written, emitter.enclosing);
|
|
782
923
|
}
|
|
783
924
|
function pairKeyText(key) {
|
|
784
925
|
if (key.type === "hash_key_symbol") {
|
|
@@ -792,35 +933,29 @@ function pairKeyText(key) {
|
|
|
792
933
|
}
|
|
793
934
|
return null;
|
|
794
935
|
}
|
|
795
|
-
function
|
|
796
|
-
|
|
797
|
-
(
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
function emitPropertyRead(emitter, node) {
|
|
801
|
-
const receiver = field(node, "receiver");
|
|
802
|
-
const method = field(node, "method");
|
|
803
|
-
if (receiver === null || method === null) {
|
|
804
|
-
return;
|
|
936
|
+
function calleeKeyOf(filePath, node, enclosing) {
|
|
937
|
+
if (isBareCall(node, enclosing)) {
|
|
938
|
+
return nameKey(filePath, node, enclosing);
|
|
805
939
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
method.text
|
|
812
|
-
);
|
|
940
|
+
const method = node.type === "call" ? field(node, "method") : null;
|
|
941
|
+
if (method === null) {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
813
945
|
}
|
|
814
946
|
function emitCall(emitter, call) {
|
|
815
947
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
816
|
-
const method = field(call, "method");
|
|
817
|
-
|
|
948
|
+
const method = call.type === "identifier" ? call : field(call, "method");
|
|
949
|
+
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
950
|
+
if (method === null || calleeKey === null) {
|
|
818
951
|
return;
|
|
819
952
|
}
|
|
820
953
|
const receiver = field(call, "receiver");
|
|
821
954
|
const callKey = nodeId(emitter.filePath, call);
|
|
822
|
-
const calleeKey = receiver === null ? valueKey(emitter, method) : nodeId(emitter.filePath, method);
|
|
823
955
|
add(emitter, "call", callKey, calleeKey);
|
|
956
|
+
if (!emitter.insideMethod) {
|
|
957
|
+
add(emitter, "callOutsideMethod", callKey);
|
|
958
|
+
}
|
|
824
959
|
if (receiver !== null) {
|
|
825
960
|
add(
|
|
826
961
|
emitter,
|
|
@@ -829,6 +964,8 @@ function emitCall(emitter, call) {
|
|
|
829
964
|
valueKey(emitter, receiver),
|
|
830
965
|
method.text
|
|
831
966
|
);
|
|
967
|
+
} else if (emitter.selfKey !== null) {
|
|
968
|
+
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
832
969
|
}
|
|
833
970
|
const args = field(call, "arguments");
|
|
834
971
|
let position = 0;
|
|
@@ -953,9 +1090,7 @@ function emitExpressionFacts(emitter, node) {
|
|
|
953
1090
|
emitDynamicDefinition(emitter, node, []);
|
|
954
1091
|
walkExpressions(node, emitter, (child, turns) => {
|
|
955
1092
|
emitDynamicDefinition(emitter, child, turns);
|
|
956
|
-
if (
|
|
957
|
-
emitPropertyRead(emitter, child);
|
|
958
|
-
} else if (child.type === "call") {
|
|
1093
|
+
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
959
1094
|
emitCall(emitter, child);
|
|
960
1095
|
}
|
|
961
1096
|
if (ARRAY_TYPES.has(child.type)) {
|
|
@@ -1011,28 +1146,52 @@ function collectInstanceWrite(emitter, node) {
|
|
|
1011
1146
|
const operator = field(node, "operator")?.text;
|
|
1012
1147
|
const value = node.type === "assignment" || WHOLE_VALUE_OPERATORS.has(operator ?? "") ? right : null;
|
|
1013
1148
|
const written = collector.get(left.text) ?? [];
|
|
1014
|
-
written.push(
|
|
1015
|
-
describeWrite(emitter, {
|
|
1149
|
+
written.push({
|
|
1150
|
+
write: describeWrite(emitter, {
|
|
1016
1151
|
name: left.text,
|
|
1017
1152
|
target: left,
|
|
1018
1153
|
value,
|
|
1019
1154
|
at: node,
|
|
1020
1155
|
fromParameter: false
|
|
1021
|
-
})
|
|
1022
|
-
|
|
1156
|
+
}),
|
|
1157
|
+
target: left
|
|
1158
|
+
});
|
|
1023
1159
|
collector.set(left.text, written);
|
|
1024
1160
|
}
|
|
1161
|
+
function settledWrites(writes, ordered) {
|
|
1162
|
+
const described = writes.map((written) => written.write);
|
|
1163
|
+
const settled = (0, import_resolution3.valueLeftByWrites)(described, ordered);
|
|
1164
|
+
if (settled !== null) {
|
|
1165
|
+
return [settled];
|
|
1166
|
+
}
|
|
1167
|
+
const left = [];
|
|
1168
|
+
for (const write of described) {
|
|
1169
|
+
if (write.value !== null && !write.narrowsName) {
|
|
1170
|
+
left.push(write.value);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return left;
|
|
1174
|
+
}
|
|
1025
1175
|
function emitInstanceWrites(emitter, classKey, collected) {
|
|
1026
1176
|
for (const [name, writes] of collected) {
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1177
|
+
for (const value of settledWrites(writes, false)) {
|
|
1178
|
+
add(emitter, "holdsProperty", classKey, name, value);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
function emitInstanceStores(emitter, funcKey, body, collected) {
|
|
1183
|
+
for (const [name, writes] of collected) {
|
|
1184
|
+
const deciding = writes.filter((written) => !written.write.narrowsName);
|
|
1185
|
+
if (deciding.length === 0) {
|
|
1030
1186
|
continue;
|
|
1031
1187
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1188
|
+
const ordered = instanceWritesRunInOrder(
|
|
1189
|
+
body,
|
|
1190
|
+
name,
|
|
1191
|
+
deciding.map((written) => written.target)
|
|
1192
|
+
);
|
|
1193
|
+
for (const value of settledWrites(deciding, ordered)) {
|
|
1194
|
+
add(emitter, "storesProperty", funcKey, name, value);
|
|
1036
1195
|
}
|
|
1037
1196
|
}
|
|
1038
1197
|
}
|
|
@@ -1092,7 +1251,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1092
1251
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
1093
1252
|
}
|
|
1094
1253
|
}
|
|
1095
|
-
if (child.type === "call") {
|
|
1254
|
+
if (child.type === "call" || isBareCall(child, method)) {
|
|
1096
1255
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1097
1256
|
}
|
|
1098
1257
|
});
|
|
@@ -1160,20 +1319,32 @@ function emitScopeWrites(emitter, method, body) {
|
|
|
1160
1319
|
}
|
|
1161
1320
|
}
|
|
1162
1321
|
function emitCandidates(emitter, key, group) {
|
|
1322
|
+
let unstated = false;
|
|
1323
|
+
let stated = 0;
|
|
1163
1324
|
for (const write of group.writes) {
|
|
1164
1325
|
if (write.value === null) {
|
|
1165
1326
|
add(emitter, "writesUnstated", key);
|
|
1327
|
+
unstated = true;
|
|
1166
1328
|
continue;
|
|
1167
1329
|
}
|
|
1168
1330
|
if (write.fromParameter) {
|
|
1331
|
+
unstated = true;
|
|
1169
1332
|
continue;
|
|
1170
1333
|
}
|
|
1171
1334
|
if (!describeWrite(emitter, write).narrowsName) {
|
|
1172
1335
|
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
1336
|
+
stated += 1;
|
|
1173
1337
|
}
|
|
1174
1338
|
}
|
|
1339
|
+
if (!unstated && stated > 0) {
|
|
1340
|
+
add(emitter, "writesAllStated", key);
|
|
1341
|
+
}
|
|
1175
1342
|
}
|
|
1176
1343
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1344
|
+
var INITIALIZE_METHOD = "initialize";
|
|
1345
|
+
function receiverKeyOf(filePath, method, classKey) {
|
|
1346
|
+
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
1347
|
+
}
|
|
1177
1348
|
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1178
1349
|
function mixedInConstants(body, callName) {
|
|
1179
1350
|
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
@@ -1201,6 +1372,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1201
1372
|
const within = {
|
|
1202
1373
|
...emitter,
|
|
1203
1374
|
selfKey: classKey,
|
|
1375
|
+
insideMethod: false,
|
|
1204
1376
|
instanceWrites: collected
|
|
1205
1377
|
};
|
|
1206
1378
|
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
@@ -1228,10 +1400,30 @@ function emitClassFacts(emitter, cls) {
|
|
|
1228
1400
|
emitExpressionFacts(within, statement);
|
|
1229
1401
|
continue;
|
|
1230
1402
|
}
|
|
1231
|
-
const
|
|
1403
|
+
const stored = /* @__PURE__ */ new Map();
|
|
1404
|
+
const receiverKey = receiverKeyOf(emitter.filePath, statement, classKey);
|
|
1405
|
+
if (receiverKey !== classKey) {
|
|
1406
|
+
add(emitter, "instanceOf", receiverKey, classKey);
|
|
1407
|
+
}
|
|
1408
|
+
const funcKey = emitMethodFacts(
|
|
1409
|
+
{
|
|
1410
|
+
...within,
|
|
1411
|
+
selfKey: receiverKey,
|
|
1412
|
+
insideMethod: receiverKey !== classKey,
|
|
1413
|
+
instanceWrites: stored
|
|
1414
|
+
},
|
|
1415
|
+
statement
|
|
1416
|
+
);
|
|
1232
1417
|
const name = field(statement, "name");
|
|
1233
1418
|
if (name !== null) {
|
|
1234
1419
|
add(emitter, "holdsProperty", classKey, name.text, funcKey);
|
|
1420
|
+
if (name.text === INITIALIZE_METHOD) {
|
|
1421
|
+
add(emitter, "initializes", classKey, funcKey);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const methodBody = field(statement, "body");
|
|
1425
|
+
if (methodBody !== null) {
|
|
1426
|
+
emitInstanceStores(within, funcKey, methodBody, stored);
|
|
1235
1427
|
}
|
|
1236
1428
|
}
|
|
1237
1429
|
emitInstanceWrites(within, classKey, collected);
|
|
@@ -1243,6 +1435,7 @@ function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
|
1243
1435
|
filePath,
|
|
1244
1436
|
enclosing: null,
|
|
1245
1437
|
selfKey: null,
|
|
1438
|
+
insideMethod: false,
|
|
1246
1439
|
instanceWrites: null,
|
|
1247
1440
|
bodyBlocks
|
|
1248
1441
|
};
|
|
@@ -1323,7 +1516,7 @@ function rubyLowering(options) {
|
|
|
1323
1516
|
statement: statementOf,
|
|
1324
1517
|
siteOf,
|
|
1325
1518
|
functionOf,
|
|
1326
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1519
|
+
writtenTo: (node, site) => context === null ? null : context.writtenTo(node, site),
|
|
1327
1520
|
callable: (node) => {
|
|
1328
1521
|
if (!isInlinableCall(node)) {
|
|
1329
1522
|
return null;
|
|
@@ -1840,17 +2033,32 @@ function siteOf(node) {
|
|
|
1840
2033
|
current = parent;
|
|
1841
2034
|
}
|
|
1842
2035
|
}
|
|
2036
|
+
var BARE_SPLAT_TYPES = /* @__PURE__ */ new Set(["splat_parameter", "hash_splat_parameter"]);
|
|
1843
2037
|
function parametersOf2(parameters) {
|
|
1844
2038
|
if (parameters === null) {
|
|
1845
2039
|
return [];
|
|
1846
2040
|
}
|
|
1847
|
-
|
|
2041
|
+
const declared = [];
|
|
2042
|
+
let position = 0;
|
|
2043
|
+
for (const parameter of named(parameters)) {
|
|
2044
|
+
if (BARE_SPLAT_TYPES.has(parameter.type) && field(parameter, "name") === null) {
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
1848
2047
|
if (parameter.type === "identifier") {
|
|
1849
|
-
|
|
2048
|
+
declared.push({ name: parameter.text, default: null, position });
|
|
2049
|
+
} else {
|
|
2050
|
+
const name = field(parameter, "name");
|
|
2051
|
+
if (name !== null) {
|
|
2052
|
+
declared.push({
|
|
2053
|
+
name: name.text,
|
|
2054
|
+
default: field(parameter, "value"),
|
|
2055
|
+
position
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
1850
2058
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
2059
|
+
position += 1;
|
|
2060
|
+
}
|
|
2061
|
+
return declared;
|
|
1854
2062
|
}
|
|
1855
2063
|
function functionOf(node) {
|
|
1856
2064
|
if (node.type === "program") {
|
|
@@ -2240,25 +2448,56 @@ var rubyRows = [
|
|
|
2240
2448
|
|
|
2241
2449
|
// src/values/evaluator.ts
|
|
2242
2450
|
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2451
|
+
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2243
2452
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2453
|
+
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2244
2454
|
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2245
2455
|
function bindEvaluator(db, nodes) {
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
);
|
|
2456
|
+
for (const entry of nodes.files) {
|
|
2457
|
+
filesByTree.set(entry.root.tree, entry.file);
|
|
2458
|
+
}
|
|
2459
|
+
const context = contextOver(db, nodes);
|
|
2460
|
+
contexts.set(db, context);
|
|
2461
|
+
evaluators.set(db, new import_values2.Evaluator(rubyLowering({ context, rows: rubyRows })));
|
|
2252
2462
|
}
|
|
2253
|
-
function
|
|
2463
|
+
function writtenNodeOf(node, db, site) {
|
|
2464
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2465
|
+
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2466
|
+
}
|
|
2467
|
+
function evaluatedValue(node, db, bindings, site) {
|
|
2254
2468
|
const evaluator = evaluatorFor(node, db);
|
|
2255
2469
|
if (bindings === void 0) {
|
|
2256
|
-
return (0, import_values2.force)(
|
|
2470
|
+
return (0, import_values2.force)(
|
|
2471
|
+
site === void 0 ? evaluator.evaluate(node) : evaluator.evaluate(node, { site })
|
|
2472
|
+
);
|
|
2257
2473
|
}
|
|
2258
2474
|
const supplied = new Map(
|
|
2259
2475
|
[...bindings].map(([name, value]) => [name, (0, import_values2.text)(value)])
|
|
2260
2476
|
);
|
|
2261
|
-
return (0, import_values2.force)(
|
|
2477
|
+
return (0, import_values2.force)(
|
|
2478
|
+
evaluator.evaluate(node, {
|
|
2479
|
+
bindings: supplied,
|
|
2480
|
+
...site === void 0 ? {} : { site }
|
|
2481
|
+
})
|
|
2482
|
+
);
|
|
2483
|
+
}
|
|
2484
|
+
function constructionSitesOf(node, db) {
|
|
2485
|
+
const cls = enclosingClass(node);
|
|
2486
|
+
const file = cls === null ? void 0 : filesByTree.get(cls.tree);
|
|
2487
|
+
if (db === void 0 || cls === null || file === void 0) {
|
|
2488
|
+
return [];
|
|
2489
|
+
}
|
|
2490
|
+
return constructionSites(db, nodeId(file, cls));
|
|
2491
|
+
}
|
|
2492
|
+
function enclosingClass(node) {
|
|
2493
|
+
let current = node.parent;
|
|
2494
|
+
while (current !== null) {
|
|
2495
|
+
if (current.type === "class") {
|
|
2496
|
+
return current;
|
|
2497
|
+
}
|
|
2498
|
+
current = current.parent;
|
|
2499
|
+
}
|
|
2500
|
+
return null;
|
|
2262
2501
|
}
|
|
2263
2502
|
function stringValueOf(node, db, bindings) {
|
|
2264
2503
|
return (0, import_values2.literalOf)(evaluatedValue(node, db, bindings));
|
|
@@ -2310,15 +2549,13 @@ function contextOver(db, nodes) {
|
|
|
2310
2549
|
}
|
|
2311
2550
|
const fileOf = (node) => filesByRoot.get(node.tree.rootNode.id)?.file ?? null;
|
|
2312
2551
|
return {
|
|
2313
|
-
writtenTo: (node) => {
|
|
2552
|
+
writtenTo: (node, site) => {
|
|
2314
2553
|
const file = fileOf(node);
|
|
2315
2554
|
if (file === null) {
|
|
2316
2555
|
return null;
|
|
2317
2556
|
}
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
readKey(file, node, enclosingMethod(node))
|
|
2321
|
-
);
|
|
2557
|
+
const key = readKey(file, node, enclosingMethod(node));
|
|
2558
|
+
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2322
2559
|
return answer === null ? null : (0, import_resolution4.nodeOfKey)(rootsByFile, answer);
|
|
2323
2560
|
},
|
|
2324
2561
|
callable: (call) => {
|
|
@@ -2336,6 +2573,31 @@ function contextOver(db, nodes) {
|
|
|
2336
2573
|
};
|
|
2337
2574
|
}
|
|
2338
2575
|
|
|
2576
|
+
// src/values/literals.ts
|
|
2577
|
+
var import_values4 = require("@suss/values");
|
|
2578
|
+
function literalElementsIn(value) {
|
|
2579
|
+
if (value.kind !== "sequence") {
|
|
2580
|
+
return null;
|
|
2581
|
+
}
|
|
2582
|
+
const elements = [];
|
|
2583
|
+
for (const item of value.items) {
|
|
2584
|
+
const literal = (0, import_values4.literalOf)((0, import_values4.force)(item.value));
|
|
2585
|
+
if (literal === null) {
|
|
2586
|
+
return null;
|
|
2587
|
+
}
|
|
2588
|
+
elements.push(literal);
|
|
2589
|
+
}
|
|
2590
|
+
return elements;
|
|
2591
|
+
}
|
|
2592
|
+
function literalElementsOf(node, db) {
|
|
2593
|
+
return literalElementsIn(evaluatedValue(node, db));
|
|
2594
|
+
}
|
|
2595
|
+
function namesOf(node, db) {
|
|
2596
|
+
const value = evaluatedValue(node, db);
|
|
2597
|
+
const single = (0, import_values4.literalOf)(value);
|
|
2598
|
+
return single === null ? literalElementsIn(value) : [single];
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2339
2601
|
// src/defineMethod.ts
|
|
2340
2602
|
var NOTHING = {
|
|
2341
2603
|
names: /* @__PURE__ */ new Set(),
|
|
@@ -2382,8 +2644,8 @@ function readOne(db, rootsByFile, nameKey2) {
|
|
|
2382
2644
|
const names = [];
|
|
2383
2645
|
const patterns = [];
|
|
2384
2646
|
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2385
|
-
const value = node === null ? (0,
|
|
2386
|
-
const name = (0,
|
|
2647
|
+
const value = node === null ? (0, import_values5.hole)(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2648
|
+
const name = (0, import_values5.literalOf)(value);
|
|
2387
2649
|
if (name !== null) {
|
|
2388
2650
|
names.push(name);
|
|
2389
2651
|
continue;
|
|
@@ -2397,7 +2659,7 @@ function readOne(db, rootsByFile, nameKey2) {
|
|
|
2397
2659
|
return { names, patterns };
|
|
2398
2660
|
}
|
|
2399
2661
|
function patternOf(value) {
|
|
2400
|
-
const pieces = (0,
|
|
2662
|
+
const pieces = (0, import_values5.piecesOf)(value);
|
|
2401
2663
|
let source = "^";
|
|
2402
2664
|
let readSomething = false;
|
|
2403
2665
|
for (const piece of pieces) {
|
|
@@ -2439,29 +2701,14 @@ function listsBehind(db, rootsByFile, nameKey2) {
|
|
|
2439
2701
|
0,
|
|
2440
2702
|
nameKey2
|
|
2441
2703
|
)) {
|
|
2442
|
-
const
|
|
2704
|
+
const node = (0, import_resolution5.nodeOfKey)(rootsByFile, String(overKey));
|
|
2705
|
+
const elements = node === null ? null : literalElementsOf(node, db);
|
|
2443
2706
|
if (elements !== null) {
|
|
2444
2707
|
found.push({ element: String(element), index: String(index), elements });
|
|
2445
2708
|
}
|
|
2446
2709
|
}
|
|
2447
2710
|
return found;
|
|
2448
2711
|
}
|
|
2449
|
-
function literalElementsOf(rootsByFile, key, db) {
|
|
2450
|
-
const node = (0, import_resolution5.nodeOfKey)(rootsByFile, key);
|
|
2451
|
-
const value = node === null ? null : evaluatedValue(node, db);
|
|
2452
|
-
if (value === null || value.kind !== "sequence") {
|
|
2453
|
-
return null;
|
|
2454
|
-
}
|
|
2455
|
-
const elements = [];
|
|
2456
|
-
for (const item of value.items) {
|
|
2457
|
-
const literal = (0, import_values4.literalOf)((0, import_values4.force)(item.value));
|
|
2458
|
-
if (literal === null) {
|
|
2459
|
-
return null;
|
|
2460
|
-
}
|
|
2461
|
-
elements.push(literal);
|
|
2462
|
-
}
|
|
2463
|
-
return elements;
|
|
2464
|
-
}
|
|
2465
2712
|
|
|
2466
2713
|
// src/scope.ts
|
|
2467
2714
|
function qualifyConstantRef(node, nesting) {
|
|
@@ -2790,107 +3037,11 @@ var import_extractor5 = require("@suss/extractor");
|
|
|
2790
3037
|
|
|
2791
3038
|
// src/clientCalls.ts
|
|
2792
3039
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
2793
|
-
var
|
|
3040
|
+
var import_values9 = require("@suss/values");
|
|
2794
3041
|
|
|
2795
3042
|
// src/paths/effects.ts
|
|
2796
3043
|
var import_extractor2 = require("@suss/extractor");
|
|
2797
|
-
|
|
2798
|
-
// src/paths/bareCalls.ts
|
|
2799
|
-
var PARAMETER_LIST_TYPES = [
|
|
2800
|
-
"method_parameters",
|
|
2801
|
-
"block_parameters",
|
|
2802
|
-
"lambda_parameters",
|
|
2803
|
-
"parameters"
|
|
2804
|
-
];
|
|
2805
|
-
function isFieldOf(parent, node, name) {
|
|
2806
|
-
return field(parent, name)?.id === node.id;
|
|
2807
|
-
}
|
|
2808
|
-
function namedAt(parent, node) {
|
|
2809
|
-
return isFieldOf(parent, node, "name");
|
|
2810
|
-
}
|
|
2811
|
-
function always() {
|
|
2812
|
-
return true;
|
|
2813
|
-
}
|
|
2814
|
-
var SPELLS_A_NAME = {
|
|
2815
|
-
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
2816
|
-
method: namedAt,
|
|
2817
|
-
singleton_method: namedAt,
|
|
2818
|
-
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2819
|
-
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2820
|
-
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
2821
|
-
optional_parameter: namedAt,
|
|
2822
|
-
splat_parameter: namedAt,
|
|
2823
|
-
hash_splat_parameter: namedAt,
|
|
2824
|
-
keyword_parameter: namedAt,
|
|
2825
|
-
block_parameter: namedAt,
|
|
2826
|
-
destructured_parameter: always,
|
|
2827
|
-
left_assignment_list: always,
|
|
2828
|
-
rest_assignment: always,
|
|
2829
|
-
destructured_left_assignment: always,
|
|
2830
|
-
exception_variable: always,
|
|
2831
|
-
undef: always,
|
|
2832
|
-
alias: always,
|
|
2833
|
-
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
2834
|
-
};
|
|
2835
|
-
function addNamesUnder(node, names) {
|
|
2836
|
-
if (node === null) {
|
|
2837
|
-
return;
|
|
2838
|
-
}
|
|
2839
|
-
if (node.type === "identifier") {
|
|
2840
|
-
names.add(node.text);
|
|
2841
|
-
return;
|
|
2842
|
-
}
|
|
2843
|
-
for (const child of bodyStatements(node)) {
|
|
2844
|
-
addNamesUnder(child, names);
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
function parameterNames2(node, names) {
|
|
2848
|
-
for (const parameter of bodyStatements(node)) {
|
|
2849
|
-
if (parameter.type === "identifier") {
|
|
2850
|
-
names.add(parameter.text);
|
|
2851
|
-
continue;
|
|
2852
|
-
}
|
|
2853
|
-
const declared = field(parameter, "name");
|
|
2854
|
-
if (declared !== null && declared.type === "identifier") {
|
|
2855
|
-
names.add(declared.text);
|
|
2856
|
-
continue;
|
|
2857
|
-
}
|
|
2858
|
-
addNamesUnder(parameter, names);
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
function assignedNames(node, names) {
|
|
2862
|
-
addNamesUnder(field(node, "left"), names);
|
|
2863
|
-
}
|
|
2864
|
-
var BINDS_A_NAME = {
|
|
2865
|
-
assignment: assignedNames,
|
|
2866
|
-
operator_assignment: assignedNames,
|
|
2867
|
-
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
2868
|
-
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
2869
|
-
...Object.fromEntries(
|
|
2870
|
-
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames2])
|
|
2871
|
-
)
|
|
2872
|
-
};
|
|
2873
|
-
function localNamesIn(definitionNode) {
|
|
2874
|
-
const names = /* @__PURE__ */ new Set();
|
|
2875
|
-
const visit = (node) => {
|
|
2876
|
-
BINDS_A_NAME[node.type]?.(node, names);
|
|
2877
|
-
for (const child of bodyStatements(node)) {
|
|
2878
|
-
visit(child);
|
|
2879
|
-
}
|
|
2880
|
-
};
|
|
2881
|
-
visit(definitionNode);
|
|
2882
|
-
return names;
|
|
2883
|
-
}
|
|
2884
|
-
function isBareMethodCall(node, locals) {
|
|
2885
|
-
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
2886
|
-
return false;
|
|
2887
|
-
}
|
|
2888
|
-
const parent = node.parent;
|
|
2889
|
-
if (parent === null) {
|
|
2890
|
-
return false;
|
|
2891
|
-
}
|
|
2892
|
-
return SPELLS_A_NAME[parent.type]?.(parent, node) !== true;
|
|
2893
|
-
}
|
|
3044
|
+
var import_values7 = require("@suss/values");
|
|
2894
3045
|
|
|
2895
3046
|
// src/paths/lowering.ts
|
|
2896
3047
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -3178,23 +3329,21 @@ var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
|
3178
3329
|
"do_block",
|
|
3179
3330
|
"block"
|
|
3180
3331
|
]);
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
return
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
3197
|
-
};
|
|
3332
|
+
function literalArgOf(node, facts) {
|
|
3333
|
+
const value = evaluatedValue(node, facts);
|
|
3334
|
+
const text2 = (0, import_values7.literalOf)(value);
|
|
3335
|
+
if (text2 !== null) {
|
|
3336
|
+
return { kind: "string", value: text2 };
|
|
3337
|
+
}
|
|
3338
|
+
const constant2 = (0, import_values7.constantOf)(value);
|
|
3339
|
+
if (typeof constant2 === "number") {
|
|
3340
|
+
return { kind: "number", value: constant2 };
|
|
3341
|
+
}
|
|
3342
|
+
if (typeof constant2 === "boolean") {
|
|
3343
|
+
return { kind: "boolean", value: constant2 };
|
|
3344
|
+
}
|
|
3345
|
+
return null;
|
|
3346
|
+
}
|
|
3198
3347
|
function children2(node) {
|
|
3199
3348
|
return node.namedChildren.filter((child) => child !== null);
|
|
3200
3349
|
}
|
|
@@ -3278,23 +3427,27 @@ function calleeText(call) {
|
|
|
3278
3427
|
}
|
|
3279
3428
|
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
3280
3429
|
}
|
|
3281
|
-
function argOf(node) {
|
|
3282
|
-
const literal =
|
|
3283
|
-
if (literal !== null
|
|
3430
|
+
function argOf(node, facts) {
|
|
3431
|
+
const literal = literalArgOf(node, facts);
|
|
3432
|
+
if (literal !== null) {
|
|
3284
3433
|
return literal;
|
|
3285
3434
|
}
|
|
3286
3435
|
if (node.type === "call" && !isArglessReceiverCall(node)) {
|
|
3287
|
-
return {
|
|
3436
|
+
return {
|
|
3437
|
+
kind: "call",
|
|
3438
|
+
callee: calleeText(node),
|
|
3439
|
+
args: argsOf(node, facts)
|
|
3440
|
+
};
|
|
3288
3441
|
}
|
|
3289
3442
|
return { kind: "identifier", name: node.text };
|
|
3290
3443
|
}
|
|
3291
|
-
function argsOf(call) {
|
|
3444
|
+
function argsOf(call, facts) {
|
|
3292
3445
|
const args = field(call, "arguments");
|
|
3293
3446
|
if (args === null) {
|
|
3294
3447
|
return [];
|
|
3295
3448
|
}
|
|
3296
3449
|
return children2(args).map(
|
|
3297
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3450
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child, facts) : argOf(child, facts)
|
|
3298
3451
|
);
|
|
3299
3452
|
}
|
|
3300
3453
|
function enclosingStatement(call, body) {
|
|
@@ -3307,7 +3460,7 @@ function enclosingStatement(call, body) {
|
|
|
3307
3460
|
}
|
|
3308
3461
|
return current;
|
|
3309
3462
|
}
|
|
3310
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS) {
|
|
3463
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3311
3464
|
const body = field(definitionNode, "body");
|
|
3312
3465
|
if (body === null) {
|
|
3313
3466
|
return [];
|
|
@@ -3347,7 +3500,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3347
3500
|
return {
|
|
3348
3501
|
type: "invocation",
|
|
3349
3502
|
callee: calleeText(call),
|
|
3350
|
-
args: argsOf(call),
|
|
3503
|
+
args: argsOf(call, facts),
|
|
3351
3504
|
async: false,
|
|
3352
3505
|
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3353
3506
|
};
|
|
@@ -3356,7 +3509,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3356
3509
|
|
|
3357
3510
|
// src/responseStatus.ts
|
|
3358
3511
|
var import_extractor3 = require("@suss/extractor");
|
|
3359
|
-
var
|
|
3512
|
+
var import_values8 = require("@suss/values");
|
|
3360
3513
|
|
|
3361
3514
|
// src/paths/predicates.ts
|
|
3362
3515
|
var OPERATORS = {
|
|
@@ -3425,7 +3578,7 @@ function binaryOf(node) {
|
|
|
3425
3578
|
return opaqueOf(node);
|
|
3426
3579
|
}
|
|
3427
3580
|
function valueRefOf(node) {
|
|
3428
|
-
const literal =
|
|
3581
|
+
const literal = literalOf5(node);
|
|
3429
3582
|
if (literal !== null) {
|
|
3430
3583
|
return literal;
|
|
3431
3584
|
}
|
|
@@ -3438,7 +3591,7 @@ function valueRefOf(node) {
|
|
|
3438
3591
|
}
|
|
3439
3592
|
return { type: "unresolved", sourceText: node.text };
|
|
3440
3593
|
}
|
|
3441
|
-
function
|
|
3594
|
+
function literalOf5(node) {
|
|
3442
3595
|
if (node.type === "integer") {
|
|
3443
3596
|
const value = Number.parseInt(node.text, 10);
|
|
3444
3597
|
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
@@ -3481,13 +3634,13 @@ function statusArgumentOf(call, declaration) {
|
|
|
3481
3634
|
}
|
|
3482
3635
|
return args.positional[declaration.statusArgument] ?? null;
|
|
3483
3636
|
}
|
|
3484
|
-
function statusNumberOf(node, names) {
|
|
3485
|
-
const value = evaluatedValue(node);
|
|
3486
|
-
const constant2 = (0,
|
|
3637
|
+
function statusNumberOf(node, names, facts) {
|
|
3638
|
+
const value = evaluatedValue(node, facts);
|
|
3639
|
+
const constant2 = (0, import_values8.constantOf)(value);
|
|
3487
3640
|
if (typeof constant2 === "number") {
|
|
3488
3641
|
return constant2;
|
|
3489
3642
|
}
|
|
3490
|
-
const name = (0,
|
|
3643
|
+
const name = (0, import_values8.literalOf)(value);
|
|
3491
3644
|
return name === null ? null : names[name] ?? null;
|
|
3492
3645
|
}
|
|
3493
3646
|
function declarationsByName(declarations) {
|
|
@@ -3531,12 +3684,12 @@ function collectReturns(node, found) {
|
|
|
3531
3684
|
}
|
|
3532
3685
|
return found;
|
|
3533
3686
|
}
|
|
3534
|
-
function readingOfCall(call, declaration, names) {
|
|
3687
|
+
function readingOfCall(call, declaration, names, facts) {
|
|
3535
3688
|
const argument = statusArgumentOf(call, declaration);
|
|
3536
3689
|
if (argument === null) {
|
|
3537
3690
|
return declaration.defaultStatusCode === void 0 ? import_extractor3.absentReading : (0, import_extractor3.writtenReading)(declaration.defaultStatusCode, rangeOf(call));
|
|
3538
3691
|
}
|
|
3539
|
-
const status = statusNumberOf(argument, names);
|
|
3692
|
+
const status = statusNumberOf(argument, names, facts);
|
|
3540
3693
|
if (status === null) {
|
|
3541
3694
|
return (0, import_extractor3.unreadableReading)(
|
|
3542
3695
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
@@ -3680,7 +3833,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
3680
3833
|
const outcomes = [];
|
|
3681
3834
|
for (const terminal of terminals) {
|
|
3682
3835
|
const declaration = declarationOf(terminal, byName, locals);
|
|
3683
|
-
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
3836
|
+
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames, options.facts);
|
|
3684
3837
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
3685
3838
|
outcomes.push({
|
|
3686
3839
|
conditions: path7.map(conditionOf),
|
|
@@ -3714,11 +3867,9 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3714
3867
|
continue;
|
|
3715
3868
|
}
|
|
3716
3869
|
for (const call of callsUnder(method)) {
|
|
3717
|
-
const request
|
|
3718
|
-
|
|
3719
|
-
continue;
|
|
3870
|
+
for (const request of requestCalls(call, pattern, options)) {
|
|
3871
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3720
3872
|
}
|
|
3721
|
-
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3722
3873
|
}
|
|
3723
3874
|
}
|
|
3724
3875
|
return units;
|
|
@@ -3747,36 +3898,60 @@ function callsUnder(node, found = []) {
|
|
|
3747
3898
|
}
|
|
3748
3899
|
return found;
|
|
3749
3900
|
}
|
|
3750
|
-
function
|
|
3901
|
+
function requestCalls(call, pattern, options) {
|
|
3751
3902
|
const receiver = field(call, "receiver");
|
|
3752
3903
|
const called = field(call, "method")?.text;
|
|
3753
|
-
if (receiver === null || called === void 0) {
|
|
3754
|
-
return
|
|
3904
|
+
if (receiver === null || called === void 0 || !isLibraryReceiver(receiver, pattern, options)) {
|
|
3905
|
+
return [];
|
|
3755
3906
|
}
|
|
3756
|
-
const
|
|
3757
|
-
|
|
3758
|
-
|
|
3907
|
+
const read = (site) => requestCall(
|
|
3908
|
+
call,
|
|
3909
|
+
called,
|
|
3910
|
+
receiverPrefix(receiver, pattern, options, site),
|
|
3911
|
+
pattern,
|
|
3912
|
+
options,
|
|
3913
|
+
site
|
|
3914
|
+
);
|
|
3915
|
+
const plain = read();
|
|
3916
|
+
if (plain !== null && !(0, import_behavioral_ir.hasNameHole)(plain.path)) {
|
|
3917
|
+
return [plain];
|
|
3759
3918
|
}
|
|
3919
|
+
const byBoundary = /* @__PURE__ */ new Map();
|
|
3920
|
+
for (const site of constructionSitesOf(call, options.facts)) {
|
|
3921
|
+
const stated = read(site);
|
|
3922
|
+
if (stated !== null) {
|
|
3923
|
+
byBoundary.set(`${stated.method} ${stated.path}`, stated);
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
if (byBoundary.size > 0) {
|
|
3927
|
+
return [...byBoundary.values()];
|
|
3928
|
+
}
|
|
3929
|
+
return plain === null ? [] : [plain];
|
|
3930
|
+
}
|
|
3931
|
+
function requestCall(call, called, prefix, pattern, options, site) {
|
|
3760
3932
|
const args = readCallArgs(field(call, "arguments"));
|
|
3761
3933
|
const verb = pattern.verbMethodNames[called];
|
|
3762
3934
|
if (verb !== void 0) {
|
|
3763
|
-
const path8 = urlIn(args, pattern, options);
|
|
3935
|
+
const path8 = urlIn(args, pattern, options, site);
|
|
3764
3936
|
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
3765
3937
|
}
|
|
3766
3938
|
const sent = pattern.requestObject;
|
|
3767
3939
|
if (sent === void 0 || sent.attribute !== called) {
|
|
3768
3940
|
return null;
|
|
3769
3941
|
}
|
|
3770
|
-
const built = requestBuilt(args.positional[0],
|
|
3942
|
+
const built = requestBuilt(args.positional[0], sent, options, site);
|
|
3771
3943
|
if (built === null) {
|
|
3772
3944
|
return null;
|
|
3773
3945
|
}
|
|
3774
|
-
const path7 = pathAt(built.url, options);
|
|
3946
|
+
const path7 = pathAt(built.url, options, site);
|
|
3775
3947
|
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3776
3948
|
}
|
|
3777
|
-
function requestBuilt(argument,
|
|
3778
|
-
|
|
3779
|
-
|
|
3949
|
+
function requestBuilt(argument, sent, options, site) {
|
|
3950
|
+
if (argument === void 0) {
|
|
3951
|
+
return null;
|
|
3952
|
+
}
|
|
3953
|
+
const written = writtenNodeOf(argument, options.facts, site) ?? argument;
|
|
3954
|
+
if (written.type !== "call") {
|
|
3780
3955
|
return null;
|
|
3781
3956
|
}
|
|
3782
3957
|
const requestClass = field(written, "receiver")?.text;
|
|
@@ -3787,28 +3962,18 @@ function requestBuilt(argument, method, sent) {
|
|
|
3787
3962
|
const args = readCallArgs(field(written, "arguments"));
|
|
3788
3963
|
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3789
3964
|
}
|
|
3790
|
-
function
|
|
3791
|
-
|
|
3792
|
-
return null;
|
|
3793
|
-
}
|
|
3794
|
-
const value = assignedValue(node.text, method);
|
|
3795
|
-
return value !== null && value.type === "call" ? value : null;
|
|
3965
|
+
function isLibraryReceiver(receiver, pattern, options) {
|
|
3966
|
+
return namesConstant(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
3796
3967
|
}
|
|
3797
|
-
function receiverPrefix(receiver,
|
|
3798
|
-
|
|
3799
|
-
return "";
|
|
3800
|
-
}
|
|
3801
|
-
if (receiver.type !== "identifier") {
|
|
3802
|
-
return null;
|
|
3803
|
-
}
|
|
3804
|
-
const built = builtBy(receiver.text, method, root, pattern);
|
|
3968
|
+
function receiverPrefix(receiver, pattern, options, site) {
|
|
3969
|
+
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
3805
3970
|
if (built === null) {
|
|
3806
|
-
return
|
|
3971
|
+
return "";
|
|
3807
3972
|
}
|
|
3808
3973
|
const args = readCallArgs(field(built, "arguments"));
|
|
3809
3974
|
const keyword = pattern.builderUrlKeyword;
|
|
3810
3975
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
3811
|
-
const path7 = base === void 0 ? null : (0,
|
|
3976
|
+
const path7 = base === void 0 ? null : (0, import_values9.pathOf)(evaluatedValue(base, options.facts, void 0, site));
|
|
3812
3977
|
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3813
3978
|
}
|
|
3814
3979
|
function trimmed(path7) {
|
|
@@ -3820,61 +3985,28 @@ function namesConstant(receiver, constantName2) {
|
|
|
3820
3985
|
}
|
|
3821
3986
|
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
3822
3987
|
}
|
|
3823
|
-
function
|
|
3988
|
+
function builderCallBehind(receiver, pattern, facts) {
|
|
3824
3989
|
const builders = pattern.receiverBuilders ?? [];
|
|
3825
3990
|
if (builders.length === 0) {
|
|
3826
3991
|
return null;
|
|
3827
3992
|
}
|
|
3828
|
-
const
|
|
3829
|
-
if (
|
|
3830
|
-
return null;
|
|
3831
|
-
}
|
|
3832
|
-
const receiver = field(value, "receiver");
|
|
3833
|
-
return receiver !== null && namesConstant(receiver, pattern.constantName) ? value : null;
|
|
3834
|
-
}
|
|
3835
|
-
function valueMethodNamedReturns(name, root) {
|
|
3836
|
-
const defined = methodDefinitions(root).find(
|
|
3837
|
-
(candidate) => field(candidate, "name")?.text === name
|
|
3838
|
-
);
|
|
3839
|
-
if (defined === void 0) {
|
|
3840
|
-
return null;
|
|
3841
|
-
}
|
|
3842
|
-
const body = field(defined, "body");
|
|
3843
|
-
const statements = body === null ? [] : body.namedChildren;
|
|
3844
|
-
const last = statements[statements.length - 1];
|
|
3845
|
-
if (last === void 0) {
|
|
3846
|
-
return null;
|
|
3847
|
-
}
|
|
3848
|
-
if (last.type === "operator_assignment" || last.type === "assignment") {
|
|
3849
|
-
return field(last, "right");
|
|
3850
|
-
}
|
|
3851
|
-
return last.type === "return" ? last.namedChildren[0] ?? null : last;
|
|
3852
|
-
}
|
|
3853
|
-
function assignedValue(name, method) {
|
|
3854
|
-
const body = field(method, "body");
|
|
3855
|
-
if (body === null) {
|
|
3993
|
+
const written = writtenNodeOf(receiver, facts);
|
|
3994
|
+
if (written === null || written.type !== "call" || !builders.includes(field(written, "method")?.text ?? "")) {
|
|
3856
3995
|
return null;
|
|
3857
3996
|
}
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
continue;
|
|
3861
|
-
}
|
|
3862
|
-
if (field(statement, "left")?.text === name) {
|
|
3863
|
-
return field(statement, "right");
|
|
3864
|
-
}
|
|
3865
|
-
}
|
|
3866
|
-
return null;
|
|
3997
|
+
const constant2 = field(written, "receiver");
|
|
3998
|
+
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3867
3999
|
}
|
|
3868
|
-
function urlIn(args, pattern, options) {
|
|
4000
|
+
function urlIn(args, pattern, options, site) {
|
|
3869
4001
|
const keyword = pattern.url.keyword;
|
|
3870
4002
|
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3871
|
-
return pathAt(written, options);
|
|
4003
|
+
return pathAt(written, options, site);
|
|
3872
4004
|
}
|
|
3873
|
-
function pathAt(written, options) {
|
|
4005
|
+
function pathAt(written, options, site) {
|
|
3874
4006
|
if (written === void 0) {
|
|
3875
4007
|
return null;
|
|
3876
4008
|
}
|
|
3877
|
-
const path7 = (0,
|
|
4009
|
+
const path7 = (0, import_values9.pathOf)(evaluatedValue(written, options.facts, void 0, site));
|
|
3878
4010
|
return path7 === void 0 || (0, import_behavioral_ir.namesNothing)(path7) ? null : path7;
|
|
3879
4011
|
}
|
|
3880
4012
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
@@ -3897,15 +4029,15 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
3897
4029
|
recognition: pack.name
|
|
3898
4030
|
}),
|
|
3899
4031
|
parameters: [],
|
|
3900
|
-
branches: callerBranches(method, range),
|
|
4032
|
+
branches: callerBranches(method, range, options.facts),
|
|
3901
4033
|
...responseAccessors(pattern),
|
|
3902
4034
|
bodyContent: "statements",
|
|
3903
4035
|
dependencyCalls: [],
|
|
3904
4036
|
declaredContract: null
|
|
3905
4037
|
};
|
|
3906
4038
|
}
|
|
3907
|
-
function callerBranches(method, range) {
|
|
3908
|
-
const effects = invocationEffects(method);
|
|
4039
|
+
function callerBranches(method, range, facts) {
|
|
4040
|
+
const effects = invocationEffects(method, void 0, void 0, facts);
|
|
3909
4041
|
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3910
4042
|
}
|
|
3911
4043
|
function responseAccessors(pattern) {
|
|
@@ -4063,7 +4195,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4063
4195
|
}
|
|
4064
4196
|
const form = byName.get(called);
|
|
4065
4197
|
if (form !== void 0) {
|
|
4066
|
-
for (const declaration of declarationsOf(statement, form)) {
|
|
4198
|
+
for (const declaration of declarationsOf(statement, form, read.facts)) {
|
|
4067
4199
|
declared = [
|
|
4068
4200
|
...declared.filter((earlier) => !sameFilter(earlier, declaration)),
|
|
4069
4201
|
declaration
|
|
@@ -4073,7 +4205,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4073
4205
|
}
|
|
4074
4206
|
const skipped = skipNames.get(called);
|
|
4075
4207
|
if (skipped !== void 0) {
|
|
4076
|
-
declared = applySkips(declared, skipsOf(statement, skipped));
|
|
4208
|
+
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
4077
4209
|
}
|
|
4078
4210
|
}
|
|
4079
4211
|
const resolved = [];
|
|
@@ -4110,14 +4242,14 @@ function filterReference(filter, displayPath) {
|
|
|
4110
4242
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
4111
4243
|
};
|
|
4112
4244
|
}
|
|
4113
|
-
function filterUnit(filter, pattern, displayPath, body) {
|
|
4245
|
+
function filterUnit(filter, pattern, displayPath, body, facts) {
|
|
4114
4246
|
const range = rangeOf(filter.method);
|
|
4115
4247
|
const branches = responseBranches(
|
|
4116
4248
|
filter.method,
|
|
4117
4249
|
pattern,
|
|
4118
4250
|
body.effects ?? [],
|
|
4119
4251
|
body.extraEffects,
|
|
4120
|
-
{ fallthrough: "handOn" }
|
|
4252
|
+
{ fallthrough: "handOn", facts }
|
|
4121
4253
|
);
|
|
4122
4254
|
return {
|
|
4123
4255
|
identity: {
|
|
@@ -4165,30 +4297,30 @@ function calledName(statement) {
|
|
|
4165
4297
|
}
|
|
4166
4298
|
return field(statement, "method")?.text ?? null;
|
|
4167
4299
|
}
|
|
4168
|
-
function declarationsOf(statement, filter) {
|
|
4300
|
+
function declarationsOf(statement, filter, facts) {
|
|
4169
4301
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4170
4302
|
const keywords = filter.actionKeywords;
|
|
4171
|
-
const only = keywords === void 0 ? null :
|
|
4172
|
-
const except = keywords === void 0 ? null :
|
|
4173
|
-
return methodNamesOf(args, filter).map((methodName) => ({
|
|
4303
|
+
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
4304
|
+
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
4305
|
+
return methodNamesOf(args, filter, facts).map((methodName) => ({
|
|
4174
4306
|
filter,
|
|
4175
4307
|
methodName,
|
|
4176
4308
|
only,
|
|
4177
4309
|
except: except ?? /* @__PURE__ */ new Set()
|
|
4178
4310
|
}));
|
|
4179
4311
|
}
|
|
4180
|
-
function methodNamesOf(args, filter) {
|
|
4312
|
+
function methodNamesOf(args, filter, facts) {
|
|
4181
4313
|
if (filter.methodFrom === "withKeyword") {
|
|
4182
4314
|
const named2 = args.keyword.with;
|
|
4183
|
-
const value = named2 === void 0 ? null :
|
|
4315
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
4184
4316
|
return value === null ? [] : [value];
|
|
4185
4317
|
}
|
|
4186
|
-
return args.positional.map((arg) =>
|
|
4318
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
4187
4319
|
}
|
|
4188
|
-
function skipsOf(statement, filterName) {
|
|
4320
|
+
function skipsOf(statement, filterName, facts) {
|
|
4189
4321
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4190
|
-
const actions =
|
|
4191
|
-
return args.positional.map((arg) =>
|
|
4322
|
+
const actions = actionsUnder(args.keyword.only, facts);
|
|
4323
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
4192
4324
|
}
|
|
4193
4325
|
function sameFilter(one, other) {
|
|
4194
4326
|
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
@@ -4212,26 +4344,12 @@ function applySkips(declared, skips) {
|
|
|
4212
4344
|
}
|
|
4213
4345
|
return left;
|
|
4214
4346
|
}
|
|
4215
|
-
function
|
|
4347
|
+
function actionsUnder(node, facts) {
|
|
4216
4348
|
if (node === void 0) {
|
|
4217
4349
|
return null;
|
|
4218
4350
|
}
|
|
4219
|
-
const
|
|
4220
|
-
|
|
4221
|
-
return /* @__PURE__ */ new Set([single]);
|
|
4222
|
-
}
|
|
4223
|
-
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
4224
|
-
return null;
|
|
4225
|
-
}
|
|
4226
|
-
const names = /* @__PURE__ */ new Set();
|
|
4227
|
-
for (const child of bodyStatements(node)) {
|
|
4228
|
-
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
4229
|
-
const name = symbolValue(child) ?? bare;
|
|
4230
|
-
if (name !== null) {
|
|
4231
|
-
names.add(name);
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
return names;
|
|
4351
|
+
const found = namesOf(node, facts);
|
|
4352
|
+
return found === null ? null : new Set(found);
|
|
4235
4353
|
}
|
|
4236
4354
|
|
|
4237
4355
|
// src/storage.ts
|
|
@@ -4309,67 +4427,64 @@ function argumentsOf2(call) {
|
|
|
4309
4427
|
const args = field(call, "arguments");
|
|
4310
4428
|
return args === null ? [] : children3(args);
|
|
4311
4429
|
}
|
|
4312
|
-
function
|
|
4313
|
-
|
|
4430
|
+
function recordFieldsOf(argument, facts) {
|
|
4431
|
+
const value = evaluatedValue(argument, facts);
|
|
4432
|
+
return value.kind === "record" ? [...value.fields.keys()] : [];
|
|
4314
4433
|
}
|
|
4315
|
-
function
|
|
4316
|
-
return node.text.replace(/^:/, "").replace(/:$/, "");
|
|
4317
|
-
}
|
|
4318
|
-
function pairsIn(argument) {
|
|
4319
|
-
if (argument.type === "pair") {
|
|
4320
|
-
return [argument];
|
|
4321
|
-
}
|
|
4322
|
-
if (argument.type === "hash") {
|
|
4323
|
-
return children3(argument).filter((child) => child.type === "pair");
|
|
4324
|
-
}
|
|
4325
|
-
return [];
|
|
4326
|
-
}
|
|
4327
|
-
function keywordKeys(call) {
|
|
4434
|
+
function keywordKeys(call, facts) {
|
|
4328
4435
|
const keys = [];
|
|
4329
4436
|
for (const argument of argumentsOf2(call)) {
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4437
|
+
if (argument.type !== "pair") {
|
|
4438
|
+
keys.push(...recordFieldsOf(argument, facts));
|
|
4439
|
+
continue;
|
|
4440
|
+
}
|
|
4441
|
+
const key = field(argument, "key");
|
|
4442
|
+
const name = key === null ? null : keyNameOf(key);
|
|
4443
|
+
if (name !== null) {
|
|
4444
|
+
keys.push(name);
|
|
4335
4445
|
}
|
|
4336
4446
|
}
|
|
4337
4447
|
return keys;
|
|
4338
4448
|
}
|
|
4339
|
-
function
|
|
4340
|
-
return
|
|
4449
|
+
function keyNameOf(key) {
|
|
4450
|
+
return hashKeySymbolName(key) ?? symbolValue(key) ?? stringLiteralValue(key);
|
|
4341
4451
|
}
|
|
4342
|
-
function
|
|
4452
|
+
function hasPositionalArgument(call, facts) {
|
|
4453
|
+
return argumentsOf2(call).some(
|
|
4454
|
+
(argument) => argument.type !== "pair" && recordFieldsOf(argument, facts).length === 0
|
|
4455
|
+
);
|
|
4456
|
+
}
|
|
4457
|
+
function columnsAskedFor(call, pattern, facts) {
|
|
4343
4458
|
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4344
4459
|
return [];
|
|
4345
4460
|
}
|
|
4346
|
-
return argumentsOf2(call).
|
|
4461
|
+
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
4347
4462
|
}
|
|
4348
|
-
function primaryKeySelector(call, pattern) {
|
|
4463
|
+
function primaryKeySelector(call, pattern, facts) {
|
|
4349
4464
|
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4350
4465
|
if (byPrimaryKey === void 0) {
|
|
4351
4466
|
return [];
|
|
4352
4467
|
}
|
|
4353
|
-
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call);
|
|
4468
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
|
|
4354
4469
|
return picks ? [byPrimaryKey.column] : [];
|
|
4355
4470
|
}
|
|
4356
|
-
function selectorOf(call, pattern) {
|
|
4471
|
+
function selectorOf(call, pattern, facts) {
|
|
4357
4472
|
const picked = [];
|
|
4358
4473
|
for (const link of chainLinks(call)) {
|
|
4359
|
-
picked.push(...primaryKeySelector(link, pattern));
|
|
4474
|
+
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4360
4475
|
if (pattern.reads.includes(methodOf(link))) {
|
|
4361
|
-
picked.push(...keywordKeys(link));
|
|
4476
|
+
picked.push(...keywordKeys(link, facts));
|
|
4362
4477
|
}
|
|
4363
4478
|
}
|
|
4364
4479
|
return [...new Set(picked)];
|
|
4365
4480
|
}
|
|
4366
|
-
function fieldsOf(call, pattern, kind) {
|
|
4481
|
+
function fieldsOf(call, pattern, kind, facts) {
|
|
4367
4482
|
if (kind === "write") {
|
|
4368
|
-
return [...new Set(keywordKeys(call))];
|
|
4483
|
+
return [...new Set(keywordKeys(call, facts))];
|
|
4369
4484
|
}
|
|
4370
4485
|
return [
|
|
4371
4486
|
...new Set(
|
|
4372
|
-
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern))
|
|
4487
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern, facts))
|
|
4373
4488
|
)
|
|
4374
4489
|
];
|
|
4375
4490
|
}
|
|
@@ -4461,8 +4576,8 @@ function modelCallEffects(call, file, options, enclosing) {
|
|
|
4461
4576
|
target.container,
|
|
4462
4577
|
pattern,
|
|
4463
4578
|
kind,
|
|
4464
|
-
selectorOf(worked, pattern),
|
|
4465
|
-
fieldsOf(worked, pattern, kind)
|
|
4579
|
+
selectorOf(worked, pattern, options.facts),
|
|
4580
|
+
fieldsOf(worked, pattern, kind, options.facts)
|
|
4466
4581
|
)
|
|
4467
4582
|
];
|
|
4468
4583
|
}
|
|
@@ -4782,7 +4897,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4782
4897
|
filter,
|
|
4783
4898
|
pattern,
|
|
4784
4899
|
displayPath,
|
|
4785
|
-
bodyOfMethod(filter.method, filter.file, options)
|
|
4900
|
+
bodyOfMethod(filter.method, filter.file, options),
|
|
4901
|
+
options.facts
|
|
4786
4902
|
);
|
|
4787
4903
|
units.push(raw);
|
|
4788
4904
|
options.onReachSeed?.(raw, {
|
|
@@ -4873,7 +4989,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
4873
4989
|
method,
|
|
4874
4990
|
pattern,
|
|
4875
4991
|
body.effects ?? [],
|
|
4876
|
-
body.extraEffects
|
|
4992
|
+
body.extraEffects,
|
|
4993
|
+
{ facts: bodyRead.facts }
|
|
4877
4994
|
);
|
|
4878
4995
|
return {
|
|
4879
4996
|
identity: {
|
|
@@ -5003,7 +5120,8 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
5003
5120
|
const effects = invocationEffects(
|
|
5004
5121
|
method,
|
|
5005
5122
|
bodyRead.inheritedMethods,
|
|
5006
|
-
EVERY_ARGLESS_CALL
|
|
5123
|
+
EVERY_ARGLESS_CALL,
|
|
5124
|
+
bodyRead.facts
|
|
5007
5125
|
);
|
|
5008
5126
|
const storage = bodyRead.storage;
|
|
5009
5127
|
const extra = [
|
|
@@ -5679,7 +5797,7 @@ function emitConstantBindings(db, perFile) {
|
|
|
5679
5797
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
5680
5798
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
5681
5799
|
var import_datalog4 = require("@suss/datalog");
|
|
5682
|
-
var
|
|
5800
|
+
var import_resolution9 = require("@suss/resolution");
|
|
5683
5801
|
|
|
5684
5802
|
// src/parser.ts
|
|
5685
5803
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -5743,6 +5861,7 @@ var import_node_path4 = __toESM(require("path"), 1);
|
|
|
5743
5861
|
var import_behavioral_ir5 = require("@suss/behavioral-ir");
|
|
5744
5862
|
var import_datalog3 = require("@suss/datalog");
|
|
5745
5863
|
var import_extractor10 = require("@suss/extractor");
|
|
5864
|
+
var import_resolution8 = require("@suss/resolution");
|
|
5746
5865
|
|
|
5747
5866
|
// src/diagnostics.ts
|
|
5748
5867
|
var import_extractor6 = require("@suss/extractor");
|
|
@@ -6506,20 +6625,41 @@ function associationCallsIn(packs) {
|
|
|
6506
6625
|
)
|
|
6507
6626
|
);
|
|
6508
6627
|
}
|
|
6509
|
-
function
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
}
|
|
6628
|
+
function packWordsOf(packs) {
|
|
6629
|
+
return {
|
|
6630
|
+
givesBackOne: packs.flatMap(
|
|
6631
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6632
|
+
(pattern) => pattern.baseClasses.flatMap(
|
|
6633
|
+
(base) => pattern.givesBack.map((method) => ({ base, method }))
|
|
6634
|
+
)
|
|
6635
|
+
)
|
|
6636
|
+
)
|
|
6637
|
+
};
|
|
6638
|
+
}
|
|
6639
|
+
function factsForFile(options) {
|
|
6640
|
+
const db = new import_datalog3.Database();
|
|
6641
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6642
|
+
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6643
|
+
emitConstantBindings(db, [
|
|
6644
|
+
collectFileConstants(
|
|
6645
|
+
options.file,
|
|
6646
|
+
options.root,
|
|
6647
|
+
associationCallsIn(options.packs),
|
|
6648
|
+
inflectionsIn(options.packs)
|
|
6649
|
+
)
|
|
6650
|
+
]);
|
|
6651
|
+
emitRequireFacts(db, options.file, options.root, /* @__PURE__ */ new Set([options.file]));
|
|
6652
|
+
bindEvaluator(db, {
|
|
6653
|
+
files: [{ file: options.file, root: options.root }],
|
|
6654
|
+
definitions: new Map(methodDefinitionsIn(options.file, options.root))
|
|
6655
|
+
});
|
|
6656
|
+
(0, import_resolution8.addPackWords)(db, packWordsOf(options.packs));
|
|
6657
|
+
return db;
|
|
6519
6658
|
}
|
|
6520
6659
|
function alreadyDiscovered(seen, raw) {
|
|
6521
6660
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
6522
|
-
const
|
|
6661
|
+
const boundary = raw.boundaryBinding === null ? "" : (0, import_behavioral_ir5.boundaryKey)(raw.boundaryBinding) ?? "";
|
|
6662
|
+
const key = `${raw.identity.file}::${reported}::${raw.identity.range.start}::${boundary}`;
|
|
6523
6663
|
if (seen.has(key)) {
|
|
6524
6664
|
return true;
|
|
6525
6665
|
}
|
|
@@ -6586,7 +6726,7 @@ async function extractRubyProject(options) {
|
|
|
6586
6726
|
emitRequireFacts(db, file, root, known);
|
|
6587
6727
|
}
|
|
6588
6728
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6589
|
-
|
|
6729
|
+
(0, import_resolution8.addPackWords)(db, packWordsOf(options.packs));
|
|
6590
6730
|
return readDynamicNames(
|
|
6591
6731
|
db,
|
|
6592
6732
|
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
@@ -6810,7 +6950,7 @@ function findRubyFiles(root) {
|
|
|
6810
6950
|
}
|
|
6811
6951
|
|
|
6812
6952
|
// src/facts/why.ts
|
|
6813
|
-
var WITNESS_RULES = [...
|
|
6953
|
+
var WITNESS_RULES = [...import_resolution9.RESOLUTION_RULES, ...RUBY_RULES];
|
|
6814
6954
|
var METHOD_TYPES5 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6815
6955
|
function namedChildrenOf(node) {
|
|
6816
6956
|
return node.namedChildren.filter((child) => child !== null);
|
|
@@ -6859,6 +6999,10 @@ function displayNameOf(node) {
|
|
|
6859
6999
|
function width(node) {
|
|
6860
7000
|
return node.endIndex - node.startIndex;
|
|
6861
7001
|
}
|
|
7002
|
+
function askedKey(value) {
|
|
7003
|
+
const enclosing = enclosingMethod2(value.node);
|
|
7004
|
+
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
7005
|
+
}
|
|
6862
7006
|
var RubyWhySession = class {
|
|
6863
7007
|
root;
|
|
6864
7008
|
db = new import_datalog4.Database();
|
|
@@ -6931,10 +7075,10 @@ var RubyWhySession = class {
|
|
|
6931
7075
|
* which the caller says in its own words.
|
|
6932
7076
|
*/
|
|
6933
7077
|
explain(value, options = {}) {
|
|
6934
|
-
return (0,
|
|
7078
|
+
return (0, import_resolution9.explainResolvedKey)({
|
|
6935
7079
|
db: this.db,
|
|
6936
7080
|
rules: WITNESS_RULES,
|
|
6937
|
-
key:
|
|
7081
|
+
key: askedKey(value),
|
|
6938
7082
|
locate: (key) => this.locate(key),
|
|
6939
7083
|
displayPath: (key) => this.displayPath(key),
|
|
6940
7084
|
...options
|
|
@@ -7052,6 +7196,7 @@ async function rubyStubEvidence(options) {
|
|
|
7052
7196
|
emitValueFacts,
|
|
7053
7197
|
evaluatedValue,
|
|
7054
7198
|
extractRubyProject,
|
|
7199
|
+
factsForFile,
|
|
7055
7200
|
field,
|
|
7056
7201
|
findRubyFiles,
|
|
7057
7202
|
graphqlTypeNameFromQualified,
|