@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.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,118 @@ 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
|
+
(0, import_resolution.askResolutionUnder)(db, [[key, site]], (0, import_resolution.resolutionUnderProgram)(RUBY_RULES));
|
|
472
|
+
return (0, import_resolution.writtenValueUnder)(db, key, site);
|
|
473
|
+
}
|
|
474
|
+
function constructionSites(db, classKey) {
|
|
475
|
+
(0, import_resolution.askResolution)(db, [classKey], "wantedSites", RUBY_PROGRAM);
|
|
476
|
+
return (0, import_resolution.allocationSitesOf)(db, classKey);
|
|
477
|
+
}
|
|
482
478
|
|
|
483
479
|
// src/facts/values.ts
|
|
484
480
|
var import_resolution3 = require("@suss/resolution");
|
|
485
481
|
|
|
482
|
+
// src/paths/bareCalls.ts
|
|
483
|
+
var PARAMETER_LIST_TYPES = [
|
|
484
|
+
"method_parameters",
|
|
485
|
+
"block_parameters",
|
|
486
|
+
"lambda_parameters",
|
|
487
|
+
"parameters"
|
|
488
|
+
];
|
|
489
|
+
function isFieldOf(parent, node, name) {
|
|
490
|
+
return field(parent, name)?.id === node.id;
|
|
491
|
+
}
|
|
492
|
+
function namedAt(parent, node) {
|
|
493
|
+
return isFieldOf(parent, node, "name");
|
|
494
|
+
}
|
|
495
|
+
function always() {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
var SPELLS_A_NAME = {
|
|
499
|
+
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
500
|
+
method: namedAt,
|
|
501
|
+
singleton_method: namedAt,
|
|
502
|
+
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
503
|
+
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
504
|
+
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
505
|
+
optional_parameter: namedAt,
|
|
506
|
+
splat_parameter: namedAt,
|
|
507
|
+
hash_splat_parameter: namedAt,
|
|
508
|
+
keyword_parameter: namedAt,
|
|
509
|
+
block_parameter: namedAt,
|
|
510
|
+
destructured_parameter: always,
|
|
511
|
+
left_assignment_list: always,
|
|
512
|
+
rest_assignment: always,
|
|
513
|
+
destructured_left_assignment: always,
|
|
514
|
+
exception_variable: always,
|
|
515
|
+
undef: always,
|
|
516
|
+
alias: always,
|
|
517
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
518
|
+
};
|
|
519
|
+
function addNamesUnder(node, names) {
|
|
520
|
+
if (node === null) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (node.type === "identifier") {
|
|
524
|
+
names.add(node.text);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
for (const child of bodyStatements(node)) {
|
|
528
|
+
addNamesUnder(child, names);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function parameterNames(node, names) {
|
|
532
|
+
for (const parameter of bodyStatements(node)) {
|
|
533
|
+
if (parameter.type === "identifier") {
|
|
534
|
+
names.add(parameter.text);
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
const declared = field(parameter, "name");
|
|
538
|
+
if (declared !== null && declared.type === "identifier") {
|
|
539
|
+
names.add(declared.text);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
addNamesUnder(parameter, names);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function assignedNames(node, names) {
|
|
546
|
+
addNamesUnder(field(node, "left"), names);
|
|
547
|
+
}
|
|
548
|
+
var BINDS_A_NAME = {
|
|
549
|
+
assignment: assignedNames,
|
|
550
|
+
operator_assignment: assignedNames,
|
|
551
|
+
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
552
|
+
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
553
|
+
...Object.fromEntries(
|
|
554
|
+
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
555
|
+
)
|
|
556
|
+
};
|
|
557
|
+
function localNamesIn(definitionNode) {
|
|
558
|
+
const names = /* @__PURE__ */ new Set();
|
|
559
|
+
const visit = (node) => {
|
|
560
|
+
BINDS_A_NAME[node.type]?.(node, names);
|
|
561
|
+
for (const child of bodyStatements(node)) {
|
|
562
|
+
visit(child);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
visit(definitionNode);
|
|
566
|
+
return names;
|
|
567
|
+
}
|
|
568
|
+
function spellsAName(node) {
|
|
569
|
+
const parent = node.parent;
|
|
570
|
+
if (parent === null) {
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
574
|
+
}
|
|
575
|
+
function isBareMethodCall(node, locals) {
|
|
576
|
+
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
return !spellsAName(node);
|
|
580
|
+
}
|
|
581
|
+
|
|
486
582
|
// src/facts/locals.ts
|
|
487
583
|
var import_resolution2 = require("@suss/resolution");
|
|
488
584
|
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
@@ -507,7 +603,7 @@ function parametersOf(owner) {
|
|
|
507
603
|
const params = field(owner, "parameters");
|
|
508
604
|
return params === null ? [] : bodyStatements(params);
|
|
509
605
|
}
|
|
510
|
-
function
|
|
606
|
+
function parameterNames2(owner) {
|
|
511
607
|
const names = /* @__PURE__ */ new Set();
|
|
512
608
|
for (const param of parametersOf(owner)) {
|
|
513
609
|
const name = paramNameOf(param);
|
|
@@ -555,6 +651,13 @@ function readForVariable(node) {
|
|
|
555
651
|
}
|
|
556
652
|
return targetsOf(pattern).map((target) => writeOf(target, null, node, false));
|
|
557
653
|
}
|
|
654
|
+
function readRescueVariable(node) {
|
|
655
|
+
const variable = field(node, "variable");
|
|
656
|
+
if (variable === null) {
|
|
657
|
+
return [];
|
|
658
|
+
}
|
|
659
|
+
return bodyStatements(variable).flatMap(targetsOf).map((target) => writeOf(target, null, node, false));
|
|
660
|
+
}
|
|
558
661
|
function readBlockParameters(node) {
|
|
559
662
|
const found = [];
|
|
560
663
|
for (const param of parametersOf(node)) {
|
|
@@ -569,6 +672,7 @@ var WRITE_READERS = {
|
|
|
569
672
|
assignment: readAssignment,
|
|
570
673
|
operator_assignment: readOperatorAssignment,
|
|
571
674
|
for: readForVariable,
|
|
675
|
+
rescue: readRescueVariable,
|
|
572
676
|
block: readBlockParameters,
|
|
573
677
|
do_block: readBlockParameters
|
|
574
678
|
};
|
|
@@ -614,7 +718,7 @@ function namesOfMethod(method) {
|
|
|
614
718
|
if (remembered !== void 0) {
|
|
615
719
|
return remembered;
|
|
616
720
|
}
|
|
617
|
-
const names = scopeNames(
|
|
721
|
+
const names = scopeNames(parameterNames2(method), field(method, "body"));
|
|
618
722
|
cache.byMethod.set(method, names);
|
|
619
723
|
return names;
|
|
620
724
|
}
|
|
@@ -636,10 +740,17 @@ function ownerOfName(node, name, enclosing) {
|
|
|
636
740
|
}
|
|
637
741
|
return enclosing !== null && names.locals.has(name) ? enclosing : null;
|
|
638
742
|
}
|
|
743
|
+
function isLocalName(node, name, enclosing) {
|
|
744
|
+
const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
|
|
745
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
return names.locals.has(name);
|
|
749
|
+
}
|
|
639
750
|
function blockDeclaring(node, name) {
|
|
640
751
|
let current = node.parent;
|
|
641
752
|
while (current !== null) {
|
|
642
|
-
if (BLOCK_TYPES2.has(current.type) &&
|
|
753
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames2(current).has(name)) {
|
|
643
754
|
return current;
|
|
644
755
|
}
|
|
645
756
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -695,6 +806,17 @@ function collectWrites(method, body) {
|
|
|
695
806
|
}
|
|
696
807
|
return [...groups.values()];
|
|
697
808
|
}
|
|
809
|
+
function instanceWritesRunInOrder(body, name, targets) {
|
|
810
|
+
return (0, import_resolution2.writesRunInOrder)(
|
|
811
|
+
body,
|
|
812
|
+
name,
|
|
813
|
+
targets.map((target) => ({
|
|
814
|
+
at: target,
|
|
815
|
+
direct: target.parent?.parent?.id === body.id
|
|
816
|
+
})),
|
|
817
|
+
nameReads(new Set(targets.map((target) => target.id)))
|
|
818
|
+
);
|
|
819
|
+
}
|
|
698
820
|
function orderedWrite(write, scope) {
|
|
699
821
|
return {
|
|
700
822
|
at: write.at,
|
|
@@ -740,12 +862,28 @@ function readKey(filePath, written, enclosing) {
|
|
|
740
862
|
if (node.type !== "identifier" && node.type !== "constant") {
|
|
741
863
|
return nodeId(filePath, node);
|
|
742
864
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
865
|
+
if (isMethodOfReceiver(node) || isBareCall(node, enclosing)) {
|
|
866
|
+
return nodeId(filePath, node);
|
|
746
867
|
}
|
|
747
868
|
return nameKey(filePath, node, enclosing);
|
|
748
869
|
}
|
|
870
|
+
function isMethodOfReceiver(node) {
|
|
871
|
+
const parent = node.parent;
|
|
872
|
+
return parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null;
|
|
873
|
+
}
|
|
874
|
+
function isReceiverOfCall(node) {
|
|
875
|
+
const parent = node.parent;
|
|
876
|
+
return parent !== null && parent.type === "call" && field(parent, "receiver")?.id === node.id;
|
|
877
|
+
}
|
|
878
|
+
function isBareCall(node, enclosing) {
|
|
879
|
+
if (node.type !== "identifier") {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
if (spellsAName(node) && !isReceiverOfCall(node)) {
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
return !isLocalName(node, node.text, enclosing);
|
|
886
|
+
}
|
|
749
887
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
750
888
|
"string",
|
|
751
889
|
"integer",
|
|
@@ -774,11 +912,7 @@ function add(emitter, relation, ...tuple) {
|
|
|
774
912
|
emitter.db.add(relation, tuple);
|
|
775
913
|
}
|
|
776
914
|
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);
|
|
915
|
+
return readKey(emitter.filePath, written, emitter.enclosing);
|
|
782
916
|
}
|
|
783
917
|
function pairKeyText(key) {
|
|
784
918
|
if (key.type === "hash_key_symbol") {
|
|
@@ -792,35 +926,29 @@ function pairKeyText(key) {
|
|
|
792
926
|
}
|
|
793
927
|
return null;
|
|
794
928
|
}
|
|
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;
|
|
929
|
+
function calleeKeyOf(filePath, node, enclosing) {
|
|
930
|
+
if (isBareCall(node, enclosing)) {
|
|
931
|
+
return nameKey(filePath, node, enclosing);
|
|
805
932
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
method.text
|
|
812
|
-
);
|
|
933
|
+
const method = node.type === "call" ? field(node, "method") : null;
|
|
934
|
+
if (method === null) {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
813
938
|
}
|
|
814
939
|
function emitCall(emitter, call) {
|
|
815
940
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
816
|
-
const method = field(call, "method");
|
|
817
|
-
|
|
941
|
+
const method = call.type === "identifier" ? call : field(call, "method");
|
|
942
|
+
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
943
|
+
if (method === null || calleeKey === null) {
|
|
818
944
|
return;
|
|
819
945
|
}
|
|
820
946
|
const receiver = field(call, "receiver");
|
|
821
947
|
const callKey = nodeId(emitter.filePath, call);
|
|
822
|
-
const calleeKey = receiver === null ? valueKey(emitter, method) : nodeId(emitter.filePath, method);
|
|
823
948
|
add(emitter, "call", callKey, calleeKey);
|
|
949
|
+
if (!emitter.insideMethod) {
|
|
950
|
+
add(emitter, "callOutsideMethod", callKey);
|
|
951
|
+
}
|
|
824
952
|
if (receiver !== null) {
|
|
825
953
|
add(
|
|
826
954
|
emitter,
|
|
@@ -829,6 +957,8 @@ function emitCall(emitter, call) {
|
|
|
829
957
|
valueKey(emitter, receiver),
|
|
830
958
|
method.text
|
|
831
959
|
);
|
|
960
|
+
} else if (emitter.selfKey !== null) {
|
|
961
|
+
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
832
962
|
}
|
|
833
963
|
const args = field(call, "arguments");
|
|
834
964
|
let position = 0;
|
|
@@ -953,9 +1083,7 @@ function emitExpressionFacts(emitter, node) {
|
|
|
953
1083
|
emitDynamicDefinition(emitter, node, []);
|
|
954
1084
|
walkExpressions(node, emitter, (child, turns) => {
|
|
955
1085
|
emitDynamicDefinition(emitter, child, turns);
|
|
956
|
-
if (
|
|
957
|
-
emitPropertyRead(emitter, child);
|
|
958
|
-
} else if (child.type === "call") {
|
|
1086
|
+
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
959
1087
|
emitCall(emitter, child);
|
|
960
1088
|
}
|
|
961
1089
|
if (ARRAY_TYPES.has(child.type)) {
|
|
@@ -1011,28 +1139,52 @@ function collectInstanceWrite(emitter, node) {
|
|
|
1011
1139
|
const operator = field(node, "operator")?.text;
|
|
1012
1140
|
const value = node.type === "assignment" || WHOLE_VALUE_OPERATORS.has(operator ?? "") ? right : null;
|
|
1013
1141
|
const written = collector.get(left.text) ?? [];
|
|
1014
|
-
written.push(
|
|
1015
|
-
describeWrite(emitter, {
|
|
1142
|
+
written.push({
|
|
1143
|
+
write: describeWrite(emitter, {
|
|
1016
1144
|
name: left.text,
|
|
1017
1145
|
target: left,
|
|
1018
1146
|
value,
|
|
1019
1147
|
at: node,
|
|
1020
1148
|
fromParameter: false
|
|
1021
|
-
})
|
|
1022
|
-
|
|
1149
|
+
}),
|
|
1150
|
+
target: left
|
|
1151
|
+
});
|
|
1023
1152
|
collector.set(left.text, written);
|
|
1024
1153
|
}
|
|
1154
|
+
function settledWrites(writes, ordered) {
|
|
1155
|
+
const described = writes.map((written) => written.write);
|
|
1156
|
+
const settled = (0, import_resolution3.valueLeftByWrites)(described, ordered);
|
|
1157
|
+
if (settled !== null) {
|
|
1158
|
+
return [settled];
|
|
1159
|
+
}
|
|
1160
|
+
const left = [];
|
|
1161
|
+
for (const write of described) {
|
|
1162
|
+
if (write.value !== null && !write.narrowsName) {
|
|
1163
|
+
left.push(write.value);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return left;
|
|
1167
|
+
}
|
|
1025
1168
|
function emitInstanceWrites(emitter, classKey, collected) {
|
|
1026
1169
|
for (const [name, writes] of collected) {
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1170
|
+
for (const value of settledWrites(writes, false)) {
|
|
1171
|
+
add(emitter, "holdsProperty", classKey, name, value);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function emitInstanceStores(emitter, funcKey, body, collected) {
|
|
1176
|
+
for (const [name, writes] of collected) {
|
|
1177
|
+
const deciding = writes.filter((written) => !written.write.narrowsName);
|
|
1178
|
+
if (deciding.length === 0) {
|
|
1030
1179
|
continue;
|
|
1031
1180
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1181
|
+
const ordered = instanceWritesRunInOrder(
|
|
1182
|
+
body,
|
|
1183
|
+
name,
|
|
1184
|
+
deciding.map((written) => written.target)
|
|
1185
|
+
);
|
|
1186
|
+
for (const value of settledWrites(deciding, ordered)) {
|
|
1187
|
+
add(emitter, "storesProperty", funcKey, name, value);
|
|
1036
1188
|
}
|
|
1037
1189
|
}
|
|
1038
1190
|
}
|
|
@@ -1092,7 +1244,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1092
1244
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
1093
1245
|
}
|
|
1094
1246
|
}
|
|
1095
|
-
if (child.type === "call") {
|
|
1247
|
+
if (child.type === "call" || isBareCall(child, method)) {
|
|
1096
1248
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1097
1249
|
}
|
|
1098
1250
|
});
|
|
@@ -1160,20 +1312,32 @@ function emitScopeWrites(emitter, method, body) {
|
|
|
1160
1312
|
}
|
|
1161
1313
|
}
|
|
1162
1314
|
function emitCandidates(emitter, key, group) {
|
|
1315
|
+
let unstated = false;
|
|
1316
|
+
let stated = 0;
|
|
1163
1317
|
for (const write of group.writes) {
|
|
1164
1318
|
if (write.value === null) {
|
|
1165
1319
|
add(emitter, "writesUnstated", key);
|
|
1320
|
+
unstated = true;
|
|
1166
1321
|
continue;
|
|
1167
1322
|
}
|
|
1168
1323
|
if (write.fromParameter) {
|
|
1324
|
+
unstated = true;
|
|
1169
1325
|
continue;
|
|
1170
1326
|
}
|
|
1171
1327
|
if (!describeWrite(emitter, write).narrowsName) {
|
|
1172
1328
|
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
1329
|
+
stated += 1;
|
|
1173
1330
|
}
|
|
1174
1331
|
}
|
|
1332
|
+
if (!unstated && stated > 0) {
|
|
1333
|
+
add(emitter, "writesAllStated", key);
|
|
1334
|
+
}
|
|
1175
1335
|
}
|
|
1176
1336
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1337
|
+
var INITIALIZE_METHOD = "initialize";
|
|
1338
|
+
function receiverKeyOf(filePath, method, classKey) {
|
|
1339
|
+
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
1340
|
+
}
|
|
1177
1341
|
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1178
1342
|
function mixedInConstants(body, callName) {
|
|
1179
1343
|
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
@@ -1201,6 +1365,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1201
1365
|
const within = {
|
|
1202
1366
|
...emitter,
|
|
1203
1367
|
selfKey: classKey,
|
|
1368
|
+
insideMethod: false,
|
|
1204
1369
|
instanceWrites: collected
|
|
1205
1370
|
};
|
|
1206
1371
|
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
@@ -1228,10 +1393,30 @@ function emitClassFacts(emitter, cls) {
|
|
|
1228
1393
|
emitExpressionFacts(within, statement);
|
|
1229
1394
|
continue;
|
|
1230
1395
|
}
|
|
1231
|
-
const
|
|
1396
|
+
const stored = /* @__PURE__ */ new Map();
|
|
1397
|
+
const receiverKey = receiverKeyOf(emitter.filePath, statement, classKey);
|
|
1398
|
+
if (receiverKey !== classKey) {
|
|
1399
|
+
add(emitter, "instanceOf", receiverKey, classKey);
|
|
1400
|
+
}
|
|
1401
|
+
const funcKey = emitMethodFacts(
|
|
1402
|
+
{
|
|
1403
|
+
...within,
|
|
1404
|
+
selfKey: receiverKey,
|
|
1405
|
+
insideMethod: receiverKey !== classKey,
|
|
1406
|
+
instanceWrites: stored
|
|
1407
|
+
},
|
|
1408
|
+
statement
|
|
1409
|
+
);
|
|
1232
1410
|
const name = field(statement, "name");
|
|
1233
1411
|
if (name !== null) {
|
|
1234
1412
|
add(emitter, "holdsProperty", classKey, name.text, funcKey);
|
|
1413
|
+
if (name.text === INITIALIZE_METHOD) {
|
|
1414
|
+
add(emitter, "initializes", classKey, funcKey);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
const methodBody = field(statement, "body");
|
|
1418
|
+
if (methodBody !== null) {
|
|
1419
|
+
emitInstanceStores(within, funcKey, methodBody, stored);
|
|
1235
1420
|
}
|
|
1236
1421
|
}
|
|
1237
1422
|
emitInstanceWrites(within, classKey, collected);
|
|
@@ -1243,6 +1428,7 @@ function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
|
1243
1428
|
filePath,
|
|
1244
1429
|
enclosing: null,
|
|
1245
1430
|
selfKey: null,
|
|
1431
|
+
insideMethod: false,
|
|
1246
1432
|
instanceWrites: null,
|
|
1247
1433
|
bodyBlocks
|
|
1248
1434
|
};
|
|
@@ -1323,7 +1509,7 @@ function rubyLowering(options) {
|
|
|
1323
1509
|
statement: statementOf,
|
|
1324
1510
|
siteOf,
|
|
1325
1511
|
functionOf,
|
|
1326
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1512
|
+
writtenTo: (node, site) => context === null ? null : context.writtenTo(node, site),
|
|
1327
1513
|
callable: (node) => {
|
|
1328
1514
|
if (!isInlinableCall(node)) {
|
|
1329
1515
|
return null;
|
|
@@ -1840,17 +2026,32 @@ function siteOf(node) {
|
|
|
1840
2026
|
current = parent;
|
|
1841
2027
|
}
|
|
1842
2028
|
}
|
|
2029
|
+
var BARE_SPLAT_TYPES = /* @__PURE__ */ new Set(["splat_parameter", "hash_splat_parameter"]);
|
|
1843
2030
|
function parametersOf2(parameters) {
|
|
1844
2031
|
if (parameters === null) {
|
|
1845
2032
|
return [];
|
|
1846
2033
|
}
|
|
1847
|
-
|
|
2034
|
+
const declared = [];
|
|
2035
|
+
let position = 0;
|
|
2036
|
+
for (const parameter of named(parameters)) {
|
|
2037
|
+
if (BARE_SPLAT_TYPES.has(parameter.type) && field(parameter, "name") === null) {
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
1848
2040
|
if (parameter.type === "identifier") {
|
|
1849
|
-
|
|
2041
|
+
declared.push({ name: parameter.text, default: null, position });
|
|
2042
|
+
} else {
|
|
2043
|
+
const name = field(parameter, "name");
|
|
2044
|
+
if (name !== null) {
|
|
2045
|
+
declared.push({
|
|
2046
|
+
name: name.text,
|
|
2047
|
+
default: field(parameter, "value"),
|
|
2048
|
+
position
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
1850
2051
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
2052
|
+
position += 1;
|
|
2053
|
+
}
|
|
2054
|
+
return declared;
|
|
1854
2055
|
}
|
|
1855
2056
|
function functionOf(node) {
|
|
1856
2057
|
if (node.type === "program") {
|
|
@@ -2240,25 +2441,56 @@ var rubyRows = [
|
|
|
2240
2441
|
|
|
2241
2442
|
// src/values/evaluator.ts
|
|
2242
2443
|
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2444
|
+
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2243
2445
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2446
|
+
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2244
2447
|
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2245
2448
|
function bindEvaluator(db, nodes) {
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
);
|
|
2449
|
+
for (const entry of nodes.files) {
|
|
2450
|
+
filesByTree.set(entry.root.tree, entry.file);
|
|
2451
|
+
}
|
|
2452
|
+
const context = contextOver(db, nodes);
|
|
2453
|
+
contexts.set(db, context);
|
|
2454
|
+
evaluators.set(db, new import_values2.Evaluator(rubyLowering({ context, rows: rubyRows })));
|
|
2252
2455
|
}
|
|
2253
|
-
function
|
|
2456
|
+
function writtenNodeOf(node, db, site) {
|
|
2457
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2458
|
+
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2459
|
+
}
|
|
2460
|
+
function evaluatedValue(node, db, bindings, site) {
|
|
2254
2461
|
const evaluator = evaluatorFor(node, db);
|
|
2255
2462
|
if (bindings === void 0) {
|
|
2256
|
-
return (0, import_values2.force)(
|
|
2463
|
+
return (0, import_values2.force)(
|
|
2464
|
+
site === void 0 ? evaluator.evaluate(node) : evaluator.evaluate(node, { site })
|
|
2465
|
+
);
|
|
2257
2466
|
}
|
|
2258
2467
|
const supplied = new Map(
|
|
2259
2468
|
[...bindings].map(([name, value]) => [name, (0, import_values2.text)(value)])
|
|
2260
2469
|
);
|
|
2261
|
-
return (0, import_values2.force)(
|
|
2470
|
+
return (0, import_values2.force)(
|
|
2471
|
+
evaluator.evaluate(node, {
|
|
2472
|
+
bindings: supplied,
|
|
2473
|
+
...site === void 0 ? {} : { site }
|
|
2474
|
+
})
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2477
|
+
function constructionSitesOf(node, db) {
|
|
2478
|
+
const cls = enclosingClass(node);
|
|
2479
|
+
const file = cls === null ? void 0 : filesByTree.get(cls.tree);
|
|
2480
|
+
if (db === void 0 || cls === null || file === void 0) {
|
|
2481
|
+
return [];
|
|
2482
|
+
}
|
|
2483
|
+
return constructionSites(db, nodeId(file, cls));
|
|
2484
|
+
}
|
|
2485
|
+
function enclosingClass(node) {
|
|
2486
|
+
let current = node.parent;
|
|
2487
|
+
while (current !== null) {
|
|
2488
|
+
if (current.type === "class") {
|
|
2489
|
+
return current;
|
|
2490
|
+
}
|
|
2491
|
+
current = current.parent;
|
|
2492
|
+
}
|
|
2493
|
+
return null;
|
|
2262
2494
|
}
|
|
2263
2495
|
function stringValueOf(node, db, bindings) {
|
|
2264
2496
|
return (0, import_values2.literalOf)(evaluatedValue(node, db, bindings));
|
|
@@ -2310,15 +2542,13 @@ function contextOver(db, nodes) {
|
|
|
2310
2542
|
}
|
|
2311
2543
|
const fileOf = (node) => filesByRoot.get(node.tree.rootNode.id)?.file ?? null;
|
|
2312
2544
|
return {
|
|
2313
|
-
writtenTo: (node) => {
|
|
2545
|
+
writtenTo: (node, site) => {
|
|
2314
2546
|
const file = fileOf(node);
|
|
2315
2547
|
if (file === null) {
|
|
2316
2548
|
return null;
|
|
2317
2549
|
}
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
readKey(file, node, enclosingMethod(node))
|
|
2321
|
-
);
|
|
2550
|
+
const key = readKey(file, node, enclosingMethod(node));
|
|
2551
|
+
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2322
2552
|
return answer === null ? null : (0, import_resolution4.nodeOfKey)(rootsByFile, answer);
|
|
2323
2553
|
},
|
|
2324
2554
|
callable: (call) => {
|
|
@@ -2336,6 +2566,31 @@ function contextOver(db, nodes) {
|
|
|
2336
2566
|
};
|
|
2337
2567
|
}
|
|
2338
2568
|
|
|
2569
|
+
// src/values/literals.ts
|
|
2570
|
+
var import_values4 = require("@suss/values");
|
|
2571
|
+
function literalElementsIn(value) {
|
|
2572
|
+
if (value.kind !== "sequence") {
|
|
2573
|
+
return null;
|
|
2574
|
+
}
|
|
2575
|
+
const elements = [];
|
|
2576
|
+
for (const item of value.items) {
|
|
2577
|
+
const literal = (0, import_values4.literalOf)((0, import_values4.force)(item.value));
|
|
2578
|
+
if (literal === null) {
|
|
2579
|
+
return null;
|
|
2580
|
+
}
|
|
2581
|
+
elements.push(literal);
|
|
2582
|
+
}
|
|
2583
|
+
return elements;
|
|
2584
|
+
}
|
|
2585
|
+
function literalElementsOf(node, db) {
|
|
2586
|
+
return literalElementsIn(evaluatedValue(node, db));
|
|
2587
|
+
}
|
|
2588
|
+
function namesOf(node, db) {
|
|
2589
|
+
const value = evaluatedValue(node, db);
|
|
2590
|
+
const single = (0, import_values4.literalOf)(value);
|
|
2591
|
+
return single === null ? literalElementsIn(value) : [single];
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2339
2594
|
// src/defineMethod.ts
|
|
2340
2595
|
var NOTHING = {
|
|
2341
2596
|
names: /* @__PURE__ */ new Set(),
|
|
@@ -2382,8 +2637,8 @@ function readOne(db, rootsByFile, nameKey2) {
|
|
|
2382
2637
|
const names = [];
|
|
2383
2638
|
const patterns = [];
|
|
2384
2639
|
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2385
|
-
const value = node === null ? (0,
|
|
2386
|
-
const name = (0,
|
|
2640
|
+
const value = node === null ? (0, import_values5.hole)(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2641
|
+
const name = (0, import_values5.literalOf)(value);
|
|
2387
2642
|
if (name !== null) {
|
|
2388
2643
|
names.push(name);
|
|
2389
2644
|
continue;
|
|
@@ -2397,7 +2652,7 @@ function readOne(db, rootsByFile, nameKey2) {
|
|
|
2397
2652
|
return { names, patterns };
|
|
2398
2653
|
}
|
|
2399
2654
|
function patternOf(value) {
|
|
2400
|
-
const pieces = (0,
|
|
2655
|
+
const pieces = (0, import_values5.piecesOf)(value);
|
|
2401
2656
|
let source = "^";
|
|
2402
2657
|
let readSomething = false;
|
|
2403
2658
|
for (const piece of pieces) {
|
|
@@ -2439,29 +2694,14 @@ function listsBehind(db, rootsByFile, nameKey2) {
|
|
|
2439
2694
|
0,
|
|
2440
2695
|
nameKey2
|
|
2441
2696
|
)) {
|
|
2442
|
-
const
|
|
2697
|
+
const node = (0, import_resolution5.nodeOfKey)(rootsByFile, String(overKey));
|
|
2698
|
+
const elements = node === null ? null : literalElementsOf(node, db);
|
|
2443
2699
|
if (elements !== null) {
|
|
2444
2700
|
found.push({ element: String(element), index: String(index), elements });
|
|
2445
2701
|
}
|
|
2446
2702
|
}
|
|
2447
2703
|
return found;
|
|
2448
2704
|
}
|
|
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
2705
|
|
|
2466
2706
|
// src/scope.ts
|
|
2467
2707
|
function qualifyConstantRef(node, nesting) {
|
|
@@ -2790,107 +3030,11 @@ var import_extractor5 = require("@suss/extractor");
|
|
|
2790
3030
|
|
|
2791
3031
|
// src/clientCalls.ts
|
|
2792
3032
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
2793
|
-
var
|
|
3033
|
+
var import_values9 = require("@suss/values");
|
|
2794
3034
|
|
|
2795
3035
|
// src/paths/effects.ts
|
|
2796
3036
|
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
|
-
}
|
|
3037
|
+
var import_values7 = require("@suss/values");
|
|
2894
3038
|
|
|
2895
3039
|
// src/paths/lowering.ts
|
|
2896
3040
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -3178,23 +3322,21 @@ var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
|
3178
3322
|
"do_block",
|
|
3179
3323
|
"block"
|
|
3180
3324
|
]);
|
|
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
|
-
};
|
|
3325
|
+
function literalArgOf(node, facts) {
|
|
3326
|
+
const value = evaluatedValue(node, facts);
|
|
3327
|
+
const text2 = (0, import_values7.literalOf)(value);
|
|
3328
|
+
if (text2 !== null) {
|
|
3329
|
+
return { kind: "string", value: text2 };
|
|
3330
|
+
}
|
|
3331
|
+
const constant2 = (0, import_values7.constantOf)(value);
|
|
3332
|
+
if (typeof constant2 === "number") {
|
|
3333
|
+
return { kind: "number", value: constant2 };
|
|
3334
|
+
}
|
|
3335
|
+
if (typeof constant2 === "boolean") {
|
|
3336
|
+
return { kind: "boolean", value: constant2 };
|
|
3337
|
+
}
|
|
3338
|
+
return null;
|
|
3339
|
+
}
|
|
3198
3340
|
function children2(node) {
|
|
3199
3341
|
return node.namedChildren.filter((child) => child !== null);
|
|
3200
3342
|
}
|
|
@@ -3278,23 +3420,27 @@ function calleeText(call) {
|
|
|
3278
3420
|
}
|
|
3279
3421
|
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
3280
3422
|
}
|
|
3281
|
-
function argOf(node) {
|
|
3282
|
-
const literal =
|
|
3283
|
-
if (literal !== null
|
|
3423
|
+
function argOf(node, facts) {
|
|
3424
|
+
const literal = literalArgOf(node, facts);
|
|
3425
|
+
if (literal !== null) {
|
|
3284
3426
|
return literal;
|
|
3285
3427
|
}
|
|
3286
3428
|
if (node.type === "call" && !isArglessReceiverCall(node)) {
|
|
3287
|
-
return {
|
|
3429
|
+
return {
|
|
3430
|
+
kind: "call",
|
|
3431
|
+
callee: calleeText(node),
|
|
3432
|
+
args: argsOf(node, facts)
|
|
3433
|
+
};
|
|
3288
3434
|
}
|
|
3289
3435
|
return { kind: "identifier", name: node.text };
|
|
3290
3436
|
}
|
|
3291
|
-
function argsOf(call) {
|
|
3437
|
+
function argsOf(call, facts) {
|
|
3292
3438
|
const args = field(call, "arguments");
|
|
3293
3439
|
if (args === null) {
|
|
3294
3440
|
return [];
|
|
3295
3441
|
}
|
|
3296
3442
|
return children2(args).map(
|
|
3297
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3443
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child, facts) : argOf(child, facts)
|
|
3298
3444
|
);
|
|
3299
3445
|
}
|
|
3300
3446
|
function enclosingStatement(call, body) {
|
|
@@ -3307,7 +3453,7 @@ function enclosingStatement(call, body) {
|
|
|
3307
3453
|
}
|
|
3308
3454
|
return current;
|
|
3309
3455
|
}
|
|
3310
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS) {
|
|
3456
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3311
3457
|
const body = field(definitionNode, "body");
|
|
3312
3458
|
if (body === null) {
|
|
3313
3459
|
return [];
|
|
@@ -3347,7 +3493,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3347
3493
|
return {
|
|
3348
3494
|
type: "invocation",
|
|
3349
3495
|
callee: calleeText(call),
|
|
3350
|
-
args: argsOf(call),
|
|
3496
|
+
args: argsOf(call, facts),
|
|
3351
3497
|
async: false,
|
|
3352
3498
|
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3353
3499
|
};
|
|
@@ -3356,7 +3502,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3356
3502
|
|
|
3357
3503
|
// src/responseStatus.ts
|
|
3358
3504
|
var import_extractor3 = require("@suss/extractor");
|
|
3359
|
-
var
|
|
3505
|
+
var import_values8 = require("@suss/values");
|
|
3360
3506
|
|
|
3361
3507
|
// src/paths/predicates.ts
|
|
3362
3508
|
var OPERATORS = {
|
|
@@ -3425,7 +3571,7 @@ function binaryOf(node) {
|
|
|
3425
3571
|
return opaqueOf(node);
|
|
3426
3572
|
}
|
|
3427
3573
|
function valueRefOf(node) {
|
|
3428
|
-
const literal =
|
|
3574
|
+
const literal = literalOf5(node);
|
|
3429
3575
|
if (literal !== null) {
|
|
3430
3576
|
return literal;
|
|
3431
3577
|
}
|
|
@@ -3438,7 +3584,7 @@ function valueRefOf(node) {
|
|
|
3438
3584
|
}
|
|
3439
3585
|
return { type: "unresolved", sourceText: node.text };
|
|
3440
3586
|
}
|
|
3441
|
-
function
|
|
3587
|
+
function literalOf5(node) {
|
|
3442
3588
|
if (node.type === "integer") {
|
|
3443
3589
|
const value = Number.parseInt(node.text, 10);
|
|
3444
3590
|
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
@@ -3481,13 +3627,13 @@ function statusArgumentOf(call, declaration) {
|
|
|
3481
3627
|
}
|
|
3482
3628
|
return args.positional[declaration.statusArgument] ?? null;
|
|
3483
3629
|
}
|
|
3484
|
-
function statusNumberOf(node, names) {
|
|
3485
|
-
const value = evaluatedValue(node);
|
|
3486
|
-
const constant2 = (0,
|
|
3630
|
+
function statusNumberOf(node, names, facts) {
|
|
3631
|
+
const value = evaluatedValue(node, facts);
|
|
3632
|
+
const constant2 = (0, import_values8.constantOf)(value);
|
|
3487
3633
|
if (typeof constant2 === "number") {
|
|
3488
3634
|
return constant2;
|
|
3489
3635
|
}
|
|
3490
|
-
const name = (0,
|
|
3636
|
+
const name = (0, import_values8.literalOf)(value);
|
|
3491
3637
|
return name === null ? null : names[name] ?? null;
|
|
3492
3638
|
}
|
|
3493
3639
|
function declarationsByName(declarations) {
|
|
@@ -3531,12 +3677,12 @@ function collectReturns(node, found) {
|
|
|
3531
3677
|
}
|
|
3532
3678
|
return found;
|
|
3533
3679
|
}
|
|
3534
|
-
function readingOfCall(call, declaration, names) {
|
|
3680
|
+
function readingOfCall(call, declaration, names, facts) {
|
|
3535
3681
|
const argument = statusArgumentOf(call, declaration);
|
|
3536
3682
|
if (argument === null) {
|
|
3537
3683
|
return declaration.defaultStatusCode === void 0 ? import_extractor3.absentReading : (0, import_extractor3.writtenReading)(declaration.defaultStatusCode, rangeOf(call));
|
|
3538
3684
|
}
|
|
3539
|
-
const status = statusNumberOf(argument, names);
|
|
3685
|
+
const status = statusNumberOf(argument, names, facts);
|
|
3540
3686
|
if (status === null) {
|
|
3541
3687
|
return (0, import_extractor3.unreadableReading)(
|
|
3542
3688
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
@@ -3680,7 +3826,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
3680
3826
|
const outcomes = [];
|
|
3681
3827
|
for (const terminal of terminals) {
|
|
3682
3828
|
const declaration = declarationOf(terminal, byName, locals);
|
|
3683
|
-
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
3829
|
+
const reading = declaration === void 0 ? import_extractor3.absentReading : readingOfCall(terminal, declaration, statusNames, options.facts);
|
|
3684
3830
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
3685
3831
|
outcomes.push({
|
|
3686
3832
|
conditions: path7.map(conditionOf),
|
|
@@ -3714,11 +3860,9 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3714
3860
|
continue;
|
|
3715
3861
|
}
|
|
3716
3862
|
for (const call of callsUnder(method)) {
|
|
3717
|
-
const request
|
|
3718
|
-
|
|
3719
|
-
continue;
|
|
3863
|
+
for (const request of requestCalls(call, pattern, options)) {
|
|
3864
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3720
3865
|
}
|
|
3721
|
-
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3722
3866
|
}
|
|
3723
3867
|
}
|
|
3724
3868
|
return units;
|
|
@@ -3747,36 +3891,60 @@ function callsUnder(node, found = []) {
|
|
|
3747
3891
|
}
|
|
3748
3892
|
return found;
|
|
3749
3893
|
}
|
|
3750
|
-
function
|
|
3894
|
+
function requestCalls(call, pattern, options) {
|
|
3751
3895
|
const receiver = field(call, "receiver");
|
|
3752
3896
|
const called = field(call, "method")?.text;
|
|
3753
|
-
if (receiver === null || called === void 0) {
|
|
3754
|
-
return
|
|
3897
|
+
if (receiver === null || called === void 0 || !isLibraryReceiver(receiver, pattern, options)) {
|
|
3898
|
+
return [];
|
|
3755
3899
|
}
|
|
3756
|
-
const
|
|
3757
|
-
|
|
3758
|
-
|
|
3900
|
+
const read = (site) => requestCall(
|
|
3901
|
+
call,
|
|
3902
|
+
called,
|
|
3903
|
+
receiverPrefix(receiver, pattern, options, site),
|
|
3904
|
+
pattern,
|
|
3905
|
+
options,
|
|
3906
|
+
site
|
|
3907
|
+
);
|
|
3908
|
+
const plain = read();
|
|
3909
|
+
if (plain !== null && !(0, import_behavioral_ir.hasNameHole)(plain.path)) {
|
|
3910
|
+
return [plain];
|
|
3911
|
+
}
|
|
3912
|
+
const byBoundary = /* @__PURE__ */ new Map();
|
|
3913
|
+
for (const site of constructionSitesOf(call, options.facts)) {
|
|
3914
|
+
const stated = read(site);
|
|
3915
|
+
if (stated !== null) {
|
|
3916
|
+
byBoundary.set(`${stated.method} ${stated.path}`, stated);
|
|
3917
|
+
}
|
|
3759
3918
|
}
|
|
3919
|
+
if (byBoundary.size > 0) {
|
|
3920
|
+
return [...byBoundary.values()];
|
|
3921
|
+
}
|
|
3922
|
+
return plain === null ? [] : [plain];
|
|
3923
|
+
}
|
|
3924
|
+
function requestCall(call, called, prefix, pattern, options, site) {
|
|
3760
3925
|
const args = readCallArgs(field(call, "arguments"));
|
|
3761
3926
|
const verb = pattern.verbMethodNames[called];
|
|
3762
3927
|
if (verb !== void 0) {
|
|
3763
|
-
const path8 = urlIn(args, pattern, options);
|
|
3928
|
+
const path8 = urlIn(args, pattern, options, site);
|
|
3764
3929
|
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
3765
3930
|
}
|
|
3766
3931
|
const sent = pattern.requestObject;
|
|
3767
3932
|
if (sent === void 0 || sent.attribute !== called) {
|
|
3768
3933
|
return null;
|
|
3769
3934
|
}
|
|
3770
|
-
const built = requestBuilt(args.positional[0],
|
|
3935
|
+
const built = requestBuilt(args.positional[0], sent, options, site);
|
|
3771
3936
|
if (built === null) {
|
|
3772
3937
|
return null;
|
|
3773
3938
|
}
|
|
3774
|
-
const path7 = pathAt(built.url, options);
|
|
3939
|
+
const path7 = pathAt(built.url, options, site);
|
|
3775
3940
|
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3776
3941
|
}
|
|
3777
|
-
function requestBuilt(argument,
|
|
3778
|
-
|
|
3779
|
-
|
|
3942
|
+
function requestBuilt(argument, sent, options, site) {
|
|
3943
|
+
if (argument === void 0) {
|
|
3944
|
+
return null;
|
|
3945
|
+
}
|
|
3946
|
+
const written = writtenNodeOf(argument, options.facts, site) ?? argument;
|
|
3947
|
+
if (written.type !== "call") {
|
|
3780
3948
|
return null;
|
|
3781
3949
|
}
|
|
3782
3950
|
const requestClass = field(written, "receiver")?.text;
|
|
@@ -3787,28 +3955,18 @@ function requestBuilt(argument, method, sent) {
|
|
|
3787
3955
|
const args = readCallArgs(field(written, "arguments"));
|
|
3788
3956
|
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3789
3957
|
}
|
|
3790
|
-
function
|
|
3791
|
-
|
|
3792
|
-
return null;
|
|
3793
|
-
}
|
|
3794
|
-
const value = assignedValue(node.text, method);
|
|
3795
|
-
return value !== null && value.type === "call" ? value : null;
|
|
3958
|
+
function isLibraryReceiver(receiver, pattern, options) {
|
|
3959
|
+
return namesConstant(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
3796
3960
|
}
|
|
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);
|
|
3961
|
+
function receiverPrefix(receiver, pattern, options, site) {
|
|
3962
|
+
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
3805
3963
|
if (built === null) {
|
|
3806
|
-
return
|
|
3964
|
+
return "";
|
|
3807
3965
|
}
|
|
3808
3966
|
const args = readCallArgs(field(built, "arguments"));
|
|
3809
3967
|
const keyword = pattern.builderUrlKeyword;
|
|
3810
3968
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
3811
|
-
const path7 = base === void 0 ? null : (0,
|
|
3969
|
+
const path7 = base === void 0 ? null : (0, import_values9.pathOf)(evaluatedValue(base, options.facts, void 0, site));
|
|
3812
3970
|
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3813
3971
|
}
|
|
3814
3972
|
function trimmed(path7) {
|
|
@@ -3820,61 +3978,28 @@ function namesConstant(receiver, constantName2) {
|
|
|
3820
3978
|
}
|
|
3821
3979
|
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
3822
3980
|
}
|
|
3823
|
-
function
|
|
3981
|
+
function builderCallBehind(receiver, pattern, facts) {
|
|
3824
3982
|
const builders = pattern.receiverBuilders ?? [];
|
|
3825
3983
|
if (builders.length === 0) {
|
|
3826
3984
|
return null;
|
|
3827
3985
|
}
|
|
3828
|
-
const
|
|
3829
|
-
if (
|
|
3986
|
+
const written = writtenNodeOf(receiver, facts);
|
|
3987
|
+
if (written === null || written.type !== "call" || !builders.includes(field(written, "method")?.text ?? "")) {
|
|
3830
3988
|
return null;
|
|
3831
3989
|
}
|
|
3832
|
-
const
|
|
3833
|
-
return
|
|
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;
|
|
3990
|
+
const constant2 = field(written, "receiver");
|
|
3991
|
+
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3852
3992
|
}
|
|
3853
|
-
function
|
|
3854
|
-
const body = field(method, "body");
|
|
3855
|
-
if (body === null) {
|
|
3856
|
-
return null;
|
|
3857
|
-
}
|
|
3858
|
-
for (const statement of runStatements(body)) {
|
|
3859
|
-
if (statement.type !== "assignment") {
|
|
3860
|
-
continue;
|
|
3861
|
-
}
|
|
3862
|
-
if (field(statement, "left")?.text === name) {
|
|
3863
|
-
return field(statement, "right");
|
|
3864
|
-
}
|
|
3865
|
-
}
|
|
3866
|
-
return null;
|
|
3867
|
-
}
|
|
3868
|
-
function urlIn(args, pattern, options) {
|
|
3993
|
+
function urlIn(args, pattern, options, site) {
|
|
3869
3994
|
const keyword = pattern.url.keyword;
|
|
3870
3995
|
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3871
|
-
return pathAt(written, options);
|
|
3996
|
+
return pathAt(written, options, site);
|
|
3872
3997
|
}
|
|
3873
|
-
function pathAt(written, options) {
|
|
3998
|
+
function pathAt(written, options, site) {
|
|
3874
3999
|
if (written === void 0) {
|
|
3875
4000
|
return null;
|
|
3876
4001
|
}
|
|
3877
|
-
const path7 = (0,
|
|
4002
|
+
const path7 = (0, import_values9.pathOf)(evaluatedValue(written, options.facts, void 0, site));
|
|
3878
4003
|
return path7 === void 0 || (0, import_behavioral_ir.namesNothing)(path7) ? null : path7;
|
|
3879
4004
|
}
|
|
3880
4005
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
@@ -3897,15 +4022,15 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
3897
4022
|
recognition: pack.name
|
|
3898
4023
|
}),
|
|
3899
4024
|
parameters: [],
|
|
3900
|
-
branches: callerBranches(method, range),
|
|
4025
|
+
branches: callerBranches(method, range, options.facts),
|
|
3901
4026
|
...responseAccessors(pattern),
|
|
3902
4027
|
bodyContent: "statements",
|
|
3903
4028
|
dependencyCalls: [],
|
|
3904
4029
|
declaredContract: null
|
|
3905
4030
|
};
|
|
3906
4031
|
}
|
|
3907
|
-
function callerBranches(method, range) {
|
|
3908
|
-
const effects = invocationEffects(method);
|
|
4032
|
+
function callerBranches(method, range, facts) {
|
|
4033
|
+
const effects = invocationEffects(method, void 0, void 0, facts);
|
|
3909
4034
|
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3910
4035
|
}
|
|
3911
4036
|
function responseAccessors(pattern) {
|
|
@@ -4063,7 +4188,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4063
4188
|
}
|
|
4064
4189
|
const form = byName.get(called);
|
|
4065
4190
|
if (form !== void 0) {
|
|
4066
|
-
for (const declaration of declarationsOf(statement, form)) {
|
|
4191
|
+
for (const declaration of declarationsOf(statement, form, read.facts)) {
|
|
4067
4192
|
declared = [
|
|
4068
4193
|
...declared.filter((earlier) => !sameFilter(earlier, declaration)),
|
|
4069
4194
|
declaration
|
|
@@ -4073,7 +4198,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4073
4198
|
}
|
|
4074
4199
|
const skipped = skipNames.get(called);
|
|
4075
4200
|
if (skipped !== void 0) {
|
|
4076
|
-
declared = applySkips(declared, skipsOf(statement, skipped));
|
|
4201
|
+
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
4077
4202
|
}
|
|
4078
4203
|
}
|
|
4079
4204
|
const resolved = [];
|
|
@@ -4110,14 +4235,14 @@ function filterReference(filter, displayPath) {
|
|
|
4110
4235
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
4111
4236
|
};
|
|
4112
4237
|
}
|
|
4113
|
-
function filterUnit(filter, pattern, displayPath, body) {
|
|
4238
|
+
function filterUnit(filter, pattern, displayPath, body, facts) {
|
|
4114
4239
|
const range = rangeOf(filter.method);
|
|
4115
4240
|
const branches = responseBranches(
|
|
4116
4241
|
filter.method,
|
|
4117
4242
|
pattern,
|
|
4118
4243
|
body.effects ?? [],
|
|
4119
4244
|
body.extraEffects,
|
|
4120
|
-
{ fallthrough: "handOn" }
|
|
4245
|
+
{ fallthrough: "handOn", facts }
|
|
4121
4246
|
);
|
|
4122
4247
|
return {
|
|
4123
4248
|
identity: {
|
|
@@ -4165,30 +4290,30 @@ function calledName(statement) {
|
|
|
4165
4290
|
}
|
|
4166
4291
|
return field(statement, "method")?.text ?? null;
|
|
4167
4292
|
}
|
|
4168
|
-
function declarationsOf(statement, filter) {
|
|
4293
|
+
function declarationsOf(statement, filter, facts) {
|
|
4169
4294
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4170
4295
|
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) => ({
|
|
4296
|
+
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
4297
|
+
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
4298
|
+
return methodNamesOf(args, filter, facts).map((methodName) => ({
|
|
4174
4299
|
filter,
|
|
4175
4300
|
methodName,
|
|
4176
4301
|
only,
|
|
4177
4302
|
except: except ?? /* @__PURE__ */ new Set()
|
|
4178
4303
|
}));
|
|
4179
4304
|
}
|
|
4180
|
-
function methodNamesOf(args, filter) {
|
|
4305
|
+
function methodNamesOf(args, filter, facts) {
|
|
4181
4306
|
if (filter.methodFrom === "withKeyword") {
|
|
4182
4307
|
const named2 = args.keyword.with;
|
|
4183
|
-
const value = named2 === void 0 ? null :
|
|
4308
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
4184
4309
|
return value === null ? [] : [value];
|
|
4185
4310
|
}
|
|
4186
|
-
return args.positional.map((arg) =>
|
|
4311
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
4187
4312
|
}
|
|
4188
|
-
function skipsOf(statement, filterName) {
|
|
4313
|
+
function skipsOf(statement, filterName, facts) {
|
|
4189
4314
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4190
|
-
const actions =
|
|
4191
|
-
return args.positional.map((arg) =>
|
|
4315
|
+
const actions = actionsUnder(args.keyword.only, facts);
|
|
4316
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
4192
4317
|
}
|
|
4193
4318
|
function sameFilter(one, other) {
|
|
4194
4319
|
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
@@ -4212,26 +4337,12 @@ function applySkips(declared, skips) {
|
|
|
4212
4337
|
}
|
|
4213
4338
|
return left;
|
|
4214
4339
|
}
|
|
4215
|
-
function
|
|
4340
|
+
function actionsUnder(node, facts) {
|
|
4216
4341
|
if (node === void 0) {
|
|
4217
4342
|
return null;
|
|
4218
4343
|
}
|
|
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;
|
|
4344
|
+
const found = namesOf(node, facts);
|
|
4345
|
+
return found === null ? null : new Set(found);
|
|
4235
4346
|
}
|
|
4236
4347
|
|
|
4237
4348
|
// src/storage.ts
|
|
@@ -4309,67 +4420,64 @@ function argumentsOf2(call) {
|
|
|
4309
4420
|
const args = field(call, "arguments");
|
|
4310
4421
|
return args === null ? [] : children3(args);
|
|
4311
4422
|
}
|
|
4312
|
-
function
|
|
4313
|
-
|
|
4423
|
+
function recordFieldsOf(argument, facts) {
|
|
4424
|
+
const value = evaluatedValue(argument, facts);
|
|
4425
|
+
return value.kind === "record" ? [...value.fields.keys()] : [];
|
|
4314
4426
|
}
|
|
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) {
|
|
4427
|
+
function keywordKeys(call, facts) {
|
|
4328
4428
|
const keys = [];
|
|
4329
4429
|
for (const argument of argumentsOf2(call)) {
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4430
|
+
if (argument.type !== "pair") {
|
|
4431
|
+
keys.push(...recordFieldsOf(argument, facts));
|
|
4432
|
+
continue;
|
|
4433
|
+
}
|
|
4434
|
+
const key = field(argument, "key");
|
|
4435
|
+
const name = key === null ? null : keyNameOf(key);
|
|
4436
|
+
if (name !== null) {
|
|
4437
|
+
keys.push(name);
|
|
4335
4438
|
}
|
|
4336
4439
|
}
|
|
4337
4440
|
return keys;
|
|
4338
4441
|
}
|
|
4339
|
-
function
|
|
4340
|
-
return
|
|
4442
|
+
function keyNameOf(key) {
|
|
4443
|
+
return hashKeySymbolName(key) ?? symbolValue(key) ?? stringLiteralValue(key);
|
|
4341
4444
|
}
|
|
4342
|
-
function
|
|
4445
|
+
function hasPositionalArgument(call, facts) {
|
|
4446
|
+
return argumentsOf2(call).some(
|
|
4447
|
+
(argument) => argument.type !== "pair" && recordFieldsOf(argument, facts).length === 0
|
|
4448
|
+
);
|
|
4449
|
+
}
|
|
4450
|
+
function columnsAskedFor(call, pattern, facts) {
|
|
4343
4451
|
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4344
4452
|
return [];
|
|
4345
4453
|
}
|
|
4346
|
-
return argumentsOf2(call).
|
|
4454
|
+
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
4347
4455
|
}
|
|
4348
|
-
function primaryKeySelector(call, pattern) {
|
|
4456
|
+
function primaryKeySelector(call, pattern, facts) {
|
|
4349
4457
|
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4350
4458
|
if (byPrimaryKey === void 0) {
|
|
4351
4459
|
return [];
|
|
4352
4460
|
}
|
|
4353
|
-
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call);
|
|
4461
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
|
|
4354
4462
|
return picks ? [byPrimaryKey.column] : [];
|
|
4355
4463
|
}
|
|
4356
|
-
function selectorOf(call, pattern) {
|
|
4464
|
+
function selectorOf(call, pattern, facts) {
|
|
4357
4465
|
const picked = [];
|
|
4358
4466
|
for (const link of chainLinks(call)) {
|
|
4359
|
-
picked.push(...primaryKeySelector(link, pattern));
|
|
4467
|
+
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4360
4468
|
if (pattern.reads.includes(methodOf(link))) {
|
|
4361
|
-
picked.push(...keywordKeys(link));
|
|
4469
|
+
picked.push(...keywordKeys(link, facts));
|
|
4362
4470
|
}
|
|
4363
4471
|
}
|
|
4364
4472
|
return [...new Set(picked)];
|
|
4365
4473
|
}
|
|
4366
|
-
function fieldsOf(call, pattern, kind) {
|
|
4474
|
+
function fieldsOf(call, pattern, kind, facts) {
|
|
4367
4475
|
if (kind === "write") {
|
|
4368
|
-
return [...new Set(keywordKeys(call))];
|
|
4476
|
+
return [...new Set(keywordKeys(call, facts))];
|
|
4369
4477
|
}
|
|
4370
4478
|
return [
|
|
4371
4479
|
...new Set(
|
|
4372
|
-
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern))
|
|
4480
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern, facts))
|
|
4373
4481
|
)
|
|
4374
4482
|
];
|
|
4375
4483
|
}
|
|
@@ -4461,8 +4569,8 @@ function modelCallEffects(call, file, options, enclosing) {
|
|
|
4461
4569
|
target.container,
|
|
4462
4570
|
pattern,
|
|
4463
4571
|
kind,
|
|
4464
|
-
selectorOf(worked, pattern),
|
|
4465
|
-
fieldsOf(worked, pattern, kind)
|
|
4572
|
+
selectorOf(worked, pattern, options.facts),
|
|
4573
|
+
fieldsOf(worked, pattern, kind, options.facts)
|
|
4466
4574
|
)
|
|
4467
4575
|
];
|
|
4468
4576
|
}
|
|
@@ -4782,7 +4890,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4782
4890
|
filter,
|
|
4783
4891
|
pattern,
|
|
4784
4892
|
displayPath,
|
|
4785
|
-
bodyOfMethod(filter.method, filter.file, options)
|
|
4893
|
+
bodyOfMethod(filter.method, filter.file, options),
|
|
4894
|
+
options.facts
|
|
4786
4895
|
);
|
|
4787
4896
|
units.push(raw);
|
|
4788
4897
|
options.onReachSeed?.(raw, {
|
|
@@ -4873,7 +4982,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
4873
4982
|
method,
|
|
4874
4983
|
pattern,
|
|
4875
4984
|
body.effects ?? [],
|
|
4876
|
-
body.extraEffects
|
|
4985
|
+
body.extraEffects,
|
|
4986
|
+
{ facts: bodyRead.facts }
|
|
4877
4987
|
);
|
|
4878
4988
|
return {
|
|
4879
4989
|
identity: {
|
|
@@ -5003,7 +5113,8 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
5003
5113
|
const effects = invocationEffects(
|
|
5004
5114
|
method,
|
|
5005
5115
|
bodyRead.inheritedMethods,
|
|
5006
|
-
EVERY_ARGLESS_CALL
|
|
5116
|
+
EVERY_ARGLESS_CALL,
|
|
5117
|
+
bodyRead.facts
|
|
5007
5118
|
);
|
|
5008
5119
|
const storage = bodyRead.storage;
|
|
5009
5120
|
const extra = [
|
|
@@ -5679,7 +5790,7 @@ function emitConstantBindings(db, perFile) {
|
|
|
5679
5790
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
5680
5791
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
5681
5792
|
var import_datalog4 = require("@suss/datalog");
|
|
5682
|
-
var
|
|
5793
|
+
var import_resolution9 = require("@suss/resolution");
|
|
5683
5794
|
|
|
5684
5795
|
// src/parser.ts
|
|
5685
5796
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -5743,6 +5854,7 @@ var import_node_path4 = __toESM(require("path"), 1);
|
|
|
5743
5854
|
var import_behavioral_ir5 = require("@suss/behavioral-ir");
|
|
5744
5855
|
var import_datalog3 = require("@suss/datalog");
|
|
5745
5856
|
var import_extractor10 = require("@suss/extractor");
|
|
5857
|
+
var import_resolution8 = require("@suss/resolution");
|
|
5746
5858
|
|
|
5747
5859
|
// src/diagnostics.ts
|
|
5748
5860
|
var import_extractor6 = require("@suss/extractor");
|
|
@@ -6506,20 +6618,41 @@ function associationCallsIn(packs) {
|
|
|
6506
6618
|
)
|
|
6507
6619
|
);
|
|
6508
6620
|
}
|
|
6509
|
-
function
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
}
|
|
6621
|
+
function packWordsOf(packs) {
|
|
6622
|
+
return {
|
|
6623
|
+
givesBackOne: packs.flatMap(
|
|
6624
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6625
|
+
(pattern) => pattern.baseClasses.flatMap(
|
|
6626
|
+
(base) => pattern.givesBack.map((method) => ({ base, method }))
|
|
6627
|
+
)
|
|
6628
|
+
)
|
|
6629
|
+
)
|
|
6630
|
+
};
|
|
6631
|
+
}
|
|
6632
|
+
function factsForFile(options) {
|
|
6633
|
+
const db = new import_datalog3.Database();
|
|
6634
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6635
|
+
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6636
|
+
emitConstantBindings(db, [
|
|
6637
|
+
collectFileConstants(
|
|
6638
|
+
options.file,
|
|
6639
|
+
options.root,
|
|
6640
|
+
associationCallsIn(options.packs),
|
|
6641
|
+
inflectionsIn(options.packs)
|
|
6642
|
+
)
|
|
6643
|
+
]);
|
|
6644
|
+
emitRequireFacts(db, options.file, options.root, /* @__PURE__ */ new Set([options.file]));
|
|
6645
|
+
bindEvaluator(db, {
|
|
6646
|
+
files: [{ file: options.file, root: options.root }],
|
|
6647
|
+
definitions: new Map(methodDefinitionsIn(options.file, options.root))
|
|
6648
|
+
});
|
|
6649
|
+
(0, import_resolution8.addPackWords)(db, packWordsOf(options.packs));
|
|
6650
|
+
return db;
|
|
6519
6651
|
}
|
|
6520
6652
|
function alreadyDiscovered(seen, raw) {
|
|
6521
6653
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
6522
|
-
const
|
|
6654
|
+
const boundary = raw.boundaryBinding === null ? "" : (0, import_behavioral_ir5.boundaryKey)(raw.boundaryBinding) ?? "";
|
|
6655
|
+
const key = `${raw.identity.file}::${reported}::${raw.identity.range.start}::${boundary}`;
|
|
6523
6656
|
if (seen.has(key)) {
|
|
6524
6657
|
return true;
|
|
6525
6658
|
}
|
|
@@ -6586,7 +6719,7 @@ async function extractRubyProject(options) {
|
|
|
6586
6719
|
emitRequireFacts(db, file, root, known);
|
|
6587
6720
|
}
|
|
6588
6721
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6589
|
-
|
|
6722
|
+
(0, import_resolution8.addPackWords)(db, packWordsOf(options.packs));
|
|
6590
6723
|
return readDynamicNames(
|
|
6591
6724
|
db,
|
|
6592
6725
|
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
@@ -6810,7 +6943,7 @@ function findRubyFiles(root) {
|
|
|
6810
6943
|
}
|
|
6811
6944
|
|
|
6812
6945
|
// src/facts/why.ts
|
|
6813
|
-
var WITNESS_RULES = [...
|
|
6946
|
+
var WITNESS_RULES = [...import_resolution9.RESOLUTION_RULES, ...RUBY_RULES];
|
|
6814
6947
|
var METHOD_TYPES5 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6815
6948
|
function namedChildrenOf(node) {
|
|
6816
6949
|
return node.namedChildren.filter((child) => child !== null);
|
|
@@ -6859,6 +6992,10 @@ function displayNameOf(node) {
|
|
|
6859
6992
|
function width(node) {
|
|
6860
6993
|
return node.endIndex - node.startIndex;
|
|
6861
6994
|
}
|
|
6995
|
+
function askedKey(value) {
|
|
6996
|
+
const enclosing = enclosingMethod2(value.node);
|
|
6997
|
+
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6998
|
+
}
|
|
6862
6999
|
var RubyWhySession = class {
|
|
6863
7000
|
root;
|
|
6864
7001
|
db = new import_datalog4.Database();
|
|
@@ -6931,10 +7068,10 @@ var RubyWhySession = class {
|
|
|
6931
7068
|
* which the caller says in its own words.
|
|
6932
7069
|
*/
|
|
6933
7070
|
explain(value, options = {}) {
|
|
6934
|
-
return (0,
|
|
7071
|
+
return (0, import_resolution9.explainResolvedKey)({
|
|
6935
7072
|
db: this.db,
|
|
6936
7073
|
rules: WITNESS_RULES,
|
|
6937
|
-
key:
|
|
7074
|
+
key: askedKey(value),
|
|
6938
7075
|
locate: (key) => this.locate(key),
|
|
6939
7076
|
displayPath: (key) => this.displayPath(key),
|
|
6940
7077
|
...options
|
|
@@ -7052,6 +7189,7 @@ async function rubyStubEvidence(options) {
|
|
|
7052
7189
|
emitValueFacts,
|
|
7053
7190
|
evaluatedValue,
|
|
7054
7191
|
extractRubyProject,
|
|
7192
|
+
factsForFile,
|
|
7055
7193
|
field,
|
|
7056
7194
|
findRubyFiles,
|
|
7057
7195
|
graphqlTypeNameFromQualified,
|