@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.js
CHANGED
|
@@ -327,7 +327,7 @@ function autoloadDirectories(root) {
|
|
|
327
327
|
|
|
328
328
|
// src/defineMethod.ts
|
|
329
329
|
import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
|
|
330
|
-
import {
|
|
330
|
+
import { hole as hole2, literalOf as literalOf3, piecesOf } from "@suss/values";
|
|
331
331
|
|
|
332
332
|
// src/values/evaluator.ts
|
|
333
333
|
import { nodeOfKey } from "@suss/resolution";
|
|
@@ -336,63 +336,56 @@ import { Evaluator, force, literalOf, text } from "@suss/values";
|
|
|
336
336
|
// src/facts/resolve.ts
|
|
337
337
|
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
338
338
|
import {
|
|
339
|
+
allocationSitesOf,
|
|
340
|
+
alsoSteps,
|
|
339
341
|
askResolution,
|
|
342
|
+
askResolutionUnder,
|
|
343
|
+
INSTANCE_STEP,
|
|
340
344
|
resolutionProgram,
|
|
345
|
+
resolutionUnderProgram,
|
|
341
346
|
writtenValueOf as sharedWrittenValueOf,
|
|
347
|
+
writtenValueUnder as sharedWrittenValueUnder,
|
|
342
348
|
VALUE_STEP
|
|
343
349
|
} from "@suss/resolution";
|
|
344
|
-
var RUBY_RULES = [
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
350
|
+
var RUBY_RULES = alsoSteps([
|
|
351
|
+
// Making one of a class is a call of the class, which the shared rules
|
|
352
|
+
// already say. Ruby writes the callee as `new` read off the constant, so
|
|
353
|
+
// what this adds is that the callee is the class.
|
|
348
354
|
rule(
|
|
349
|
-
"
|
|
350
|
-
[v("x"), v("cls"),
|
|
355
|
+
"hop",
|
|
356
|
+
[v("x"), v("cls"), INSTANCE_STEP],
|
|
351
357
|
[
|
|
352
358
|
lit("readsProperty", v("x"), v("o"), constant("new")),
|
|
353
359
|
lit("comesTo", v("o"), v("cls")),
|
|
354
360
|
lit("objectValue", v("cls"))
|
|
355
361
|
]
|
|
356
362
|
),
|
|
357
|
-
// The
|
|
358
|
-
//
|
|
359
|
-
//
|
|
363
|
+
// The same spelling again, for the caller asking where a class was
|
|
364
|
+
// made. `callsNamed` reaches a callee written as the name itself, and
|
|
365
|
+
// Ruby writes `new` off the constant instead.
|
|
360
366
|
rule(
|
|
361
|
-
"
|
|
362
|
-
[v("
|
|
367
|
+
"constructsNamed",
|
|
368
|
+
[v("r"), v("cls")],
|
|
363
369
|
[
|
|
364
|
-
lit("
|
|
365
|
-
lit("
|
|
366
|
-
lit("
|
|
367
|
-
|
|
368
|
-
lit("givesBackOne", v("n"), v("m"))
|
|
369
|
-
],
|
|
370
|
-
"declared finder"
|
|
371
|
-
),
|
|
372
|
-
// Ruby cannot refer to a method without running it, so
|
|
373
|
-
// `Settings.filters` is worth what the method returns. The shared
|
|
374
|
-
// rules hang that step on a `call` fact this spelling never gets.
|
|
375
|
-
rule(
|
|
376
|
-
"invokes",
|
|
377
|
-
[v("x"), v("f")],
|
|
378
|
-
[
|
|
379
|
-
lit("readsProperty", v("x"), v("o"), v("m")),
|
|
380
|
-
lit("comesTo", v("x"), v("f")),
|
|
381
|
-
lit("func", v("f"))
|
|
382
|
-
],
|
|
383
|
-
"property read runs a method"
|
|
370
|
+
lit("binds", v("o"), v("cls")),
|
|
371
|
+
lit("readsProperty", v("c"), v("o"), constant("new")),
|
|
372
|
+
lit("call", v("r"), v("c"))
|
|
373
|
+
]
|
|
384
374
|
),
|
|
385
375
|
// `%i[a b].freeze` is worth the list it was written as. The evaluator
|
|
386
376
|
// says the same in its row table, for a value it reads in one file.
|
|
387
377
|
...["freeze", "dup"].map(
|
|
388
378
|
(method) => rule(
|
|
389
|
-
"
|
|
390
|
-
[v("
|
|
391
|
-
[
|
|
379
|
+
"hop",
|
|
380
|
+
[v("r"), v("o"), VALUE_STEP],
|
|
381
|
+
[
|
|
382
|
+
lit("call", v("r"), v("c")),
|
|
383
|
+
lit("readsProperty", v("c"), v("o"), constant(method))
|
|
384
|
+
],
|
|
392
385
|
"hands back the receiver"
|
|
393
386
|
)
|
|
394
387
|
)
|
|
395
|
-
];
|
|
388
|
+
]);
|
|
396
389
|
var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
397
390
|
function resolveValues(db, keys) {
|
|
398
391
|
askResolution(db, keys, "wanted", RUBY_PROGRAM);
|
|
@@ -404,10 +397,125 @@ function writtenValueOf(db, key) {
|
|
|
404
397
|
resolveValues(db, [key]);
|
|
405
398
|
return sharedWrittenValueOf(db, key, (keys) => resolveValues(db, keys));
|
|
406
399
|
}
|
|
400
|
+
function writtenValueUnder(db, key, site) {
|
|
401
|
+
const outcome = askResolutionUnder(
|
|
402
|
+
db,
|
|
403
|
+
[[key, site]],
|
|
404
|
+
resolutionUnderProgram(RUBY_RULES)
|
|
405
|
+
);
|
|
406
|
+
if (outcome === "abandoned") {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
return sharedWrittenValueUnder(db, key, site);
|
|
410
|
+
}
|
|
411
|
+
function constructionSites(db, classKey) {
|
|
412
|
+
askResolution(db, [classKey], "wantedSites", RUBY_PROGRAM);
|
|
413
|
+
return allocationSitesOf(db, classKey);
|
|
414
|
+
}
|
|
407
415
|
|
|
408
416
|
// src/facts/values.ts
|
|
409
417
|
import { startsAtName, valueLeftByWrites } from "@suss/resolution";
|
|
410
418
|
|
|
419
|
+
// src/paths/bareCalls.ts
|
|
420
|
+
var PARAMETER_LIST_TYPES = [
|
|
421
|
+
"method_parameters",
|
|
422
|
+
"block_parameters",
|
|
423
|
+
"lambda_parameters",
|
|
424
|
+
"parameters"
|
|
425
|
+
];
|
|
426
|
+
function isFieldOf(parent, node, name) {
|
|
427
|
+
return field(parent, name)?.id === node.id;
|
|
428
|
+
}
|
|
429
|
+
function namedAt(parent, node) {
|
|
430
|
+
return isFieldOf(parent, node, "name");
|
|
431
|
+
}
|
|
432
|
+
function always() {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
var SPELLS_A_NAME = {
|
|
436
|
+
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
437
|
+
method: namedAt,
|
|
438
|
+
singleton_method: namedAt,
|
|
439
|
+
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
440
|
+
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
441
|
+
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
442
|
+
optional_parameter: namedAt,
|
|
443
|
+
splat_parameter: namedAt,
|
|
444
|
+
hash_splat_parameter: namedAt,
|
|
445
|
+
keyword_parameter: namedAt,
|
|
446
|
+
block_parameter: namedAt,
|
|
447
|
+
destructured_parameter: always,
|
|
448
|
+
left_assignment_list: always,
|
|
449
|
+
rest_assignment: always,
|
|
450
|
+
destructured_left_assignment: always,
|
|
451
|
+
exception_variable: always,
|
|
452
|
+
undef: always,
|
|
453
|
+
alias: always,
|
|
454
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
455
|
+
};
|
|
456
|
+
function addNamesUnder(node, names) {
|
|
457
|
+
if (node === null) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (node.type === "identifier") {
|
|
461
|
+
names.add(node.text);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
for (const child of bodyStatements(node)) {
|
|
465
|
+
addNamesUnder(child, names);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function parameterNames(node, names) {
|
|
469
|
+
for (const parameter of bodyStatements(node)) {
|
|
470
|
+
if (parameter.type === "identifier") {
|
|
471
|
+
names.add(parameter.text);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const declared = field(parameter, "name");
|
|
475
|
+
if (declared !== null && declared.type === "identifier") {
|
|
476
|
+
names.add(declared.text);
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
addNamesUnder(parameter, names);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function assignedNames(node, names) {
|
|
483
|
+
addNamesUnder(field(node, "left"), names);
|
|
484
|
+
}
|
|
485
|
+
var BINDS_A_NAME = {
|
|
486
|
+
assignment: assignedNames,
|
|
487
|
+
operator_assignment: assignedNames,
|
|
488
|
+
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
489
|
+
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
490
|
+
...Object.fromEntries(
|
|
491
|
+
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames])
|
|
492
|
+
)
|
|
493
|
+
};
|
|
494
|
+
function localNamesIn(definitionNode) {
|
|
495
|
+
const names = /* @__PURE__ */ new Set();
|
|
496
|
+
const visit = (node) => {
|
|
497
|
+
BINDS_A_NAME[node.type]?.(node, names);
|
|
498
|
+
for (const child of bodyStatements(node)) {
|
|
499
|
+
visit(child);
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
visit(definitionNode);
|
|
503
|
+
return names;
|
|
504
|
+
}
|
|
505
|
+
function spellsAName(node) {
|
|
506
|
+
const parent = node.parent;
|
|
507
|
+
if (parent === null) {
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
return SPELLS_A_NAME[parent.type]?.(parent, node) === true;
|
|
511
|
+
}
|
|
512
|
+
function isBareMethodCall(node, locals) {
|
|
513
|
+
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
return !spellsAName(node);
|
|
517
|
+
}
|
|
518
|
+
|
|
411
519
|
// src/facts/locals.ts
|
|
412
520
|
import { writesRunInOrder } from "@suss/resolution";
|
|
413
521
|
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
@@ -432,7 +540,7 @@ function parametersOf(owner) {
|
|
|
432
540
|
const params = field(owner, "parameters");
|
|
433
541
|
return params === null ? [] : bodyStatements(params);
|
|
434
542
|
}
|
|
435
|
-
function
|
|
543
|
+
function parameterNames2(owner) {
|
|
436
544
|
const names = /* @__PURE__ */ new Set();
|
|
437
545
|
for (const param of parametersOf(owner)) {
|
|
438
546
|
const name = paramNameOf(param);
|
|
@@ -480,6 +588,13 @@ function readForVariable(node) {
|
|
|
480
588
|
}
|
|
481
589
|
return targetsOf(pattern).map((target) => writeOf(target, null, node, false));
|
|
482
590
|
}
|
|
591
|
+
function readRescueVariable(node) {
|
|
592
|
+
const variable = field(node, "variable");
|
|
593
|
+
if (variable === null) {
|
|
594
|
+
return [];
|
|
595
|
+
}
|
|
596
|
+
return bodyStatements(variable).flatMap(targetsOf).map((target) => writeOf(target, null, node, false));
|
|
597
|
+
}
|
|
483
598
|
function readBlockParameters(node) {
|
|
484
599
|
const found = [];
|
|
485
600
|
for (const param of parametersOf(node)) {
|
|
@@ -494,6 +609,7 @@ var WRITE_READERS = {
|
|
|
494
609
|
assignment: readAssignment,
|
|
495
610
|
operator_assignment: readOperatorAssignment,
|
|
496
611
|
for: readForVariable,
|
|
612
|
+
rescue: readRescueVariable,
|
|
497
613
|
block: readBlockParameters,
|
|
498
614
|
do_block: readBlockParameters
|
|
499
615
|
};
|
|
@@ -539,7 +655,7 @@ function namesOfMethod(method) {
|
|
|
539
655
|
if (remembered !== void 0) {
|
|
540
656
|
return remembered;
|
|
541
657
|
}
|
|
542
|
-
const names = scopeNames(
|
|
658
|
+
const names = scopeNames(parameterNames2(method), field(method, "body"));
|
|
543
659
|
cache.byMethod.set(method, names);
|
|
544
660
|
return names;
|
|
545
661
|
}
|
|
@@ -561,10 +677,17 @@ function ownerOfName(node, name, enclosing) {
|
|
|
561
677
|
}
|
|
562
678
|
return enclosing !== null && names.locals.has(name) ? enclosing : null;
|
|
563
679
|
}
|
|
680
|
+
function isLocalName(node, name, enclosing) {
|
|
681
|
+
const names = enclosing === null ? namesOfFile(node.tree) : namesOfMethod(enclosing);
|
|
682
|
+
if (names.blockParams.has(name) && blockDeclaring(node, name) !== null) {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
return names.locals.has(name);
|
|
686
|
+
}
|
|
564
687
|
function blockDeclaring(node, name) {
|
|
565
688
|
let current = node.parent;
|
|
566
689
|
while (current !== null) {
|
|
567
|
-
if (BLOCK_TYPES2.has(current.type) &&
|
|
690
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames2(current).has(name)) {
|
|
568
691
|
return current;
|
|
569
692
|
}
|
|
570
693
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -620,6 +743,17 @@ function collectWrites(method, body) {
|
|
|
620
743
|
}
|
|
621
744
|
return [...groups.values()];
|
|
622
745
|
}
|
|
746
|
+
function instanceWritesRunInOrder(body, name, targets) {
|
|
747
|
+
return writesRunInOrder(
|
|
748
|
+
body,
|
|
749
|
+
name,
|
|
750
|
+
targets.map((target) => ({
|
|
751
|
+
at: target,
|
|
752
|
+
direct: target.parent?.parent?.id === body.id
|
|
753
|
+
})),
|
|
754
|
+
nameReads(new Set(targets.map((target) => target.id)))
|
|
755
|
+
);
|
|
756
|
+
}
|
|
623
757
|
function orderedWrite(write, scope) {
|
|
624
758
|
return {
|
|
625
759
|
at: write.at,
|
|
@@ -665,12 +799,28 @@ function readKey(filePath, written, enclosing) {
|
|
|
665
799
|
if (node.type !== "identifier" && node.type !== "constant") {
|
|
666
800
|
return nodeId(filePath, node);
|
|
667
801
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return isPropertyRead(parent) ? nodeId(filePath, parent) : nodeId(filePath, node);
|
|
802
|
+
if (isMethodOfReceiver(node) || isBareCall(node, enclosing)) {
|
|
803
|
+
return nodeId(filePath, node);
|
|
671
804
|
}
|
|
672
805
|
return nameKey(filePath, node, enclosing);
|
|
673
806
|
}
|
|
807
|
+
function isMethodOfReceiver(node) {
|
|
808
|
+
const parent = node.parent;
|
|
809
|
+
return parent !== null && parent.type === "call" && field(parent, "method")?.id === node.id && field(parent, "receiver") !== null;
|
|
810
|
+
}
|
|
811
|
+
function isReceiverOfCall(node) {
|
|
812
|
+
const parent = node.parent;
|
|
813
|
+
return parent !== null && parent.type === "call" && field(parent, "receiver")?.id === node.id;
|
|
814
|
+
}
|
|
815
|
+
function isBareCall(node, enclosing) {
|
|
816
|
+
if (node.type !== "identifier") {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
if (spellsAName(node) && !isReceiverOfCall(node)) {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
return !isLocalName(node, node.text, enclosing);
|
|
823
|
+
}
|
|
674
824
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
675
825
|
"string",
|
|
676
826
|
"integer",
|
|
@@ -699,11 +849,7 @@ function add(emitter, relation, ...tuple) {
|
|
|
699
849
|
emitter.db.add(relation, tuple);
|
|
700
850
|
}
|
|
701
851
|
function valueKey(emitter, written) {
|
|
702
|
-
|
|
703
|
-
if (value.type !== "identifier" && value.type !== "constant") {
|
|
704
|
-
return nodeId(emitter.filePath, value);
|
|
705
|
-
}
|
|
706
|
-
return nameKey(emitter.filePath, value, emitter.enclosing);
|
|
852
|
+
return readKey(emitter.filePath, written, emitter.enclosing);
|
|
707
853
|
}
|
|
708
854
|
function pairKeyText(key) {
|
|
709
855
|
if (key.type === "hash_key_symbol") {
|
|
@@ -717,35 +863,29 @@ function pairKeyText(key) {
|
|
|
717
863
|
}
|
|
718
864
|
return null;
|
|
719
865
|
}
|
|
720
|
-
function
|
|
721
|
-
|
|
722
|
-
(
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
function emitPropertyRead(emitter, node) {
|
|
726
|
-
const receiver = field(node, "receiver");
|
|
727
|
-
const method = field(node, "method");
|
|
728
|
-
if (receiver === null || method === null) {
|
|
729
|
-
return;
|
|
866
|
+
function calleeKeyOf(filePath, node, enclosing) {
|
|
867
|
+
if (isBareCall(node, enclosing)) {
|
|
868
|
+
return nameKey(filePath, node, enclosing);
|
|
730
869
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
method.text
|
|
737
|
-
);
|
|
870
|
+
const method = node.type === "call" ? field(node, "method") : null;
|
|
871
|
+
if (method === null) {
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
return field(node, "receiver") === null ? nameKey(filePath, method, enclosing) : nodeId(filePath, method);
|
|
738
875
|
}
|
|
739
876
|
function emitCall(emitter, call) {
|
|
740
877
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
741
|
-
const method = field(call, "method");
|
|
742
|
-
|
|
878
|
+
const method = call.type === "identifier" ? call : field(call, "method");
|
|
879
|
+
const calleeKey = calleeKeyOf(emitter.filePath, call, emitter.enclosing);
|
|
880
|
+
if (method === null || calleeKey === null) {
|
|
743
881
|
return;
|
|
744
882
|
}
|
|
745
883
|
const receiver = field(call, "receiver");
|
|
746
884
|
const callKey = nodeId(emitter.filePath, call);
|
|
747
|
-
const calleeKey = receiver === null ? valueKey(emitter, method) : nodeId(emitter.filePath, method);
|
|
748
885
|
add(emitter, "call", callKey, calleeKey);
|
|
886
|
+
if (!emitter.insideMethod) {
|
|
887
|
+
add(emitter, "callOutsideMethod", callKey);
|
|
888
|
+
}
|
|
749
889
|
if (receiver !== null) {
|
|
750
890
|
add(
|
|
751
891
|
emitter,
|
|
@@ -754,6 +894,8 @@ function emitCall(emitter, call) {
|
|
|
754
894
|
valueKey(emitter, receiver),
|
|
755
895
|
method.text
|
|
756
896
|
);
|
|
897
|
+
} else if (emitter.selfKey !== null) {
|
|
898
|
+
add(emitter, "readsProperty", calleeKey, emitter.selfKey, method.text);
|
|
757
899
|
}
|
|
758
900
|
const args = field(call, "arguments");
|
|
759
901
|
let position = 0;
|
|
@@ -878,9 +1020,7 @@ function emitExpressionFacts(emitter, node) {
|
|
|
878
1020
|
emitDynamicDefinition(emitter, node, []);
|
|
879
1021
|
walkExpressions(node, emitter, (child, turns) => {
|
|
880
1022
|
emitDynamicDefinition(emitter, child, turns);
|
|
881
|
-
if (
|
|
882
|
-
emitPropertyRead(emitter, child);
|
|
883
|
-
} else if (child.type === "call") {
|
|
1023
|
+
if (child.type === "call" || isBareCall(child, emitter.enclosing)) {
|
|
884
1024
|
emitCall(emitter, child);
|
|
885
1025
|
}
|
|
886
1026
|
if (ARRAY_TYPES.has(child.type)) {
|
|
@@ -936,28 +1076,52 @@ function collectInstanceWrite(emitter, node) {
|
|
|
936
1076
|
const operator = field(node, "operator")?.text;
|
|
937
1077
|
const value = node.type === "assignment" || WHOLE_VALUE_OPERATORS.has(operator ?? "") ? right : null;
|
|
938
1078
|
const written = collector.get(left.text) ?? [];
|
|
939
|
-
written.push(
|
|
940
|
-
describeWrite(emitter, {
|
|
1079
|
+
written.push({
|
|
1080
|
+
write: describeWrite(emitter, {
|
|
941
1081
|
name: left.text,
|
|
942
1082
|
target: left,
|
|
943
1083
|
value,
|
|
944
1084
|
at: node,
|
|
945
1085
|
fromParameter: false
|
|
946
|
-
})
|
|
947
|
-
|
|
1086
|
+
}),
|
|
1087
|
+
target: left
|
|
1088
|
+
});
|
|
948
1089
|
collector.set(left.text, written);
|
|
949
1090
|
}
|
|
1091
|
+
function settledWrites(writes, ordered) {
|
|
1092
|
+
const described = writes.map((written) => written.write);
|
|
1093
|
+
const settled = valueLeftByWrites(described, ordered);
|
|
1094
|
+
if (settled !== null) {
|
|
1095
|
+
return [settled];
|
|
1096
|
+
}
|
|
1097
|
+
const left = [];
|
|
1098
|
+
for (const write of described) {
|
|
1099
|
+
if (write.value !== null && !write.narrowsName) {
|
|
1100
|
+
left.push(write.value);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return left;
|
|
1104
|
+
}
|
|
950
1105
|
function emitInstanceWrites(emitter, classKey, collected) {
|
|
951
1106
|
for (const [name, writes] of collected) {
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
1107
|
+
for (const value of settledWrites(writes, false)) {
|
|
1108
|
+
add(emitter, "holdsProperty", classKey, name, value);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function emitInstanceStores(emitter, funcKey, body, collected) {
|
|
1113
|
+
for (const [name, writes] of collected) {
|
|
1114
|
+
const deciding = writes.filter((written) => !written.write.narrowsName);
|
|
1115
|
+
if (deciding.length === 0) {
|
|
955
1116
|
continue;
|
|
956
1117
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1118
|
+
const ordered = instanceWritesRunInOrder(
|
|
1119
|
+
body,
|
|
1120
|
+
name,
|
|
1121
|
+
deciding.map((written) => written.target)
|
|
1122
|
+
);
|
|
1123
|
+
for (const value of settledWrites(deciding, ordered)) {
|
|
1124
|
+
add(emitter, "storesProperty", funcKey, name, value);
|
|
961
1125
|
}
|
|
962
1126
|
}
|
|
963
1127
|
}
|
|
@@ -1017,7 +1181,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
1017
1181
|
add(inside, "returnsValue", funcKey, valueKey(inside, returned));
|
|
1018
1182
|
}
|
|
1019
1183
|
}
|
|
1020
|
-
if (child.type === "call") {
|
|
1184
|
+
if (child.type === "call" || isBareCall(child, method)) {
|
|
1021
1185
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1022
1186
|
}
|
|
1023
1187
|
});
|
|
@@ -1085,20 +1249,32 @@ function emitScopeWrites(emitter, method, body) {
|
|
|
1085
1249
|
}
|
|
1086
1250
|
}
|
|
1087
1251
|
function emitCandidates(emitter, key, group) {
|
|
1252
|
+
let unstated = false;
|
|
1253
|
+
let stated = 0;
|
|
1088
1254
|
for (const write of group.writes) {
|
|
1089
1255
|
if (write.value === null) {
|
|
1090
1256
|
add(emitter, "writesUnstated", key);
|
|
1257
|
+
unstated = true;
|
|
1091
1258
|
continue;
|
|
1092
1259
|
}
|
|
1093
1260
|
if (write.fromParameter) {
|
|
1261
|
+
unstated = true;
|
|
1094
1262
|
continue;
|
|
1095
1263
|
}
|
|
1096
1264
|
if (!describeWrite(emitter, write).narrowsName) {
|
|
1097
1265
|
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
1266
|
+
stated += 1;
|
|
1098
1267
|
}
|
|
1099
1268
|
}
|
|
1269
|
+
if (!unstated && stated > 0) {
|
|
1270
|
+
add(emitter, "writesAllStated", key);
|
|
1271
|
+
}
|
|
1100
1272
|
}
|
|
1101
1273
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1274
|
+
var INITIALIZE_METHOD = "initialize";
|
|
1275
|
+
function receiverKeyOf(filePath, method, classKey) {
|
|
1276
|
+
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
1277
|
+
}
|
|
1102
1278
|
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1103
1279
|
function mixedInConstants(body, callName) {
|
|
1104
1280
|
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
@@ -1126,6 +1302,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1126
1302
|
const within = {
|
|
1127
1303
|
...emitter,
|
|
1128
1304
|
selfKey: classKey,
|
|
1305
|
+
insideMethod: false,
|
|
1129
1306
|
instanceWrites: collected
|
|
1130
1307
|
};
|
|
1131
1308
|
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
@@ -1153,10 +1330,30 @@ function emitClassFacts(emitter, cls) {
|
|
|
1153
1330
|
emitExpressionFacts(within, statement);
|
|
1154
1331
|
continue;
|
|
1155
1332
|
}
|
|
1156
|
-
const
|
|
1333
|
+
const stored = /* @__PURE__ */ new Map();
|
|
1334
|
+
const receiverKey = receiverKeyOf(emitter.filePath, statement, classKey);
|
|
1335
|
+
if (receiverKey !== classKey) {
|
|
1336
|
+
add(emitter, "instanceOf", receiverKey, classKey);
|
|
1337
|
+
}
|
|
1338
|
+
const funcKey = emitMethodFacts(
|
|
1339
|
+
{
|
|
1340
|
+
...within,
|
|
1341
|
+
selfKey: receiverKey,
|
|
1342
|
+
insideMethod: receiverKey !== classKey,
|
|
1343
|
+
instanceWrites: stored
|
|
1344
|
+
},
|
|
1345
|
+
statement
|
|
1346
|
+
);
|
|
1157
1347
|
const name = field(statement, "name");
|
|
1158
1348
|
if (name !== null) {
|
|
1159
1349
|
add(emitter, "holdsProperty", classKey, name.text, funcKey);
|
|
1350
|
+
if (name.text === INITIALIZE_METHOD) {
|
|
1351
|
+
add(emitter, "initializes", classKey, funcKey);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
const methodBody = field(statement, "body");
|
|
1355
|
+
if (methodBody !== null) {
|
|
1356
|
+
emitInstanceStores(within, funcKey, methodBody, stored);
|
|
1160
1357
|
}
|
|
1161
1358
|
}
|
|
1162
1359
|
emitInstanceWrites(within, classKey, collected);
|
|
@@ -1168,6 +1365,7 @@ function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
|
1168
1365
|
filePath,
|
|
1169
1366
|
enclosing: null,
|
|
1170
1367
|
selfKey: null,
|
|
1368
|
+
insideMethod: false,
|
|
1171
1369
|
instanceWrites: null,
|
|
1172
1370
|
bodyBlocks
|
|
1173
1371
|
};
|
|
@@ -1248,7 +1446,7 @@ function rubyLowering(options) {
|
|
|
1248
1446
|
statement: statementOf,
|
|
1249
1447
|
siteOf,
|
|
1250
1448
|
functionOf,
|
|
1251
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1449
|
+
writtenTo: (node, site) => context === null ? null : context.writtenTo(node, site),
|
|
1252
1450
|
callable: (node) => {
|
|
1253
1451
|
if (!isInlinableCall(node)) {
|
|
1254
1452
|
return null;
|
|
@@ -1765,17 +1963,32 @@ function siteOf(node) {
|
|
|
1765
1963
|
current = parent;
|
|
1766
1964
|
}
|
|
1767
1965
|
}
|
|
1966
|
+
var BARE_SPLAT_TYPES = /* @__PURE__ */ new Set(["splat_parameter", "hash_splat_parameter"]);
|
|
1768
1967
|
function parametersOf2(parameters) {
|
|
1769
1968
|
if (parameters === null) {
|
|
1770
1969
|
return [];
|
|
1771
1970
|
}
|
|
1772
|
-
|
|
1971
|
+
const declared = [];
|
|
1972
|
+
let position = 0;
|
|
1973
|
+
for (const parameter of named(parameters)) {
|
|
1974
|
+
if (BARE_SPLAT_TYPES.has(parameter.type) && field(parameter, "name") === null) {
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1773
1977
|
if (parameter.type === "identifier") {
|
|
1774
|
-
|
|
1978
|
+
declared.push({ name: parameter.text, default: null, position });
|
|
1979
|
+
} else {
|
|
1980
|
+
const name = field(parameter, "name");
|
|
1981
|
+
if (name !== null) {
|
|
1982
|
+
declared.push({
|
|
1983
|
+
name: name.text,
|
|
1984
|
+
default: field(parameter, "value"),
|
|
1985
|
+
position
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1775
1988
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1989
|
+
position += 1;
|
|
1990
|
+
}
|
|
1991
|
+
return declared;
|
|
1779
1992
|
}
|
|
1780
1993
|
function functionOf(node) {
|
|
1781
1994
|
if (node.type === "program") {
|
|
@@ -2183,25 +2396,56 @@ var rubyRows = [
|
|
|
2183
2396
|
|
|
2184
2397
|
// src/values/evaluator.ts
|
|
2185
2398
|
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2399
|
+
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2186
2400
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2401
|
+
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2187
2402
|
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2188
2403
|
function bindEvaluator(db, nodes) {
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
);
|
|
2404
|
+
for (const entry of nodes.files) {
|
|
2405
|
+
filesByTree.set(entry.root.tree, entry.file);
|
|
2406
|
+
}
|
|
2407
|
+
const context = contextOver(db, nodes);
|
|
2408
|
+
contexts.set(db, context);
|
|
2409
|
+
evaluators.set(db, new Evaluator(rubyLowering({ context, rows: rubyRows })));
|
|
2195
2410
|
}
|
|
2196
|
-
function
|
|
2411
|
+
function writtenNodeOf(node, db, site) {
|
|
2412
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
2413
|
+
return context === void 0 ? null : context.writtenTo(node, site);
|
|
2414
|
+
}
|
|
2415
|
+
function evaluatedValue(node, db, bindings, site) {
|
|
2197
2416
|
const evaluator = evaluatorFor(node, db);
|
|
2198
2417
|
if (bindings === void 0) {
|
|
2199
|
-
return force(
|
|
2418
|
+
return force(
|
|
2419
|
+
site === void 0 ? evaluator.evaluate(node) : evaluator.evaluate(node, { site })
|
|
2420
|
+
);
|
|
2200
2421
|
}
|
|
2201
2422
|
const supplied = new Map(
|
|
2202
2423
|
[...bindings].map(([name, value]) => [name, text(value)])
|
|
2203
2424
|
);
|
|
2204
|
-
return force(
|
|
2425
|
+
return force(
|
|
2426
|
+
evaluator.evaluate(node, {
|
|
2427
|
+
bindings: supplied,
|
|
2428
|
+
...site === void 0 ? {} : { site }
|
|
2429
|
+
})
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
function constructionSitesOf(node, db) {
|
|
2433
|
+
const cls = enclosingClass(node);
|
|
2434
|
+
const file = cls === null ? void 0 : filesByTree.get(cls.tree);
|
|
2435
|
+
if (db === void 0 || cls === null || file === void 0) {
|
|
2436
|
+
return [];
|
|
2437
|
+
}
|
|
2438
|
+
return constructionSites(db, nodeId(file, cls));
|
|
2439
|
+
}
|
|
2440
|
+
function enclosingClass(node) {
|
|
2441
|
+
let current = node.parent;
|
|
2442
|
+
while (current !== null) {
|
|
2443
|
+
if (current.type === "class") {
|
|
2444
|
+
return current;
|
|
2445
|
+
}
|
|
2446
|
+
current = current.parent;
|
|
2447
|
+
}
|
|
2448
|
+
return null;
|
|
2205
2449
|
}
|
|
2206
2450
|
function stringValueOf(node, db, bindings) {
|
|
2207
2451
|
return literalOf(evaluatedValue(node, db, bindings));
|
|
@@ -2253,15 +2497,13 @@ function contextOver(db, nodes) {
|
|
|
2253
2497
|
}
|
|
2254
2498
|
const fileOf = (node) => filesByRoot.get(node.tree.rootNode.id)?.file ?? null;
|
|
2255
2499
|
return {
|
|
2256
|
-
writtenTo: (node) => {
|
|
2500
|
+
writtenTo: (node, site) => {
|
|
2257
2501
|
const file = fileOf(node);
|
|
2258
2502
|
if (file === null) {
|
|
2259
2503
|
return null;
|
|
2260
2504
|
}
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2263
|
-
readKey(file, node, enclosingMethod(node))
|
|
2264
|
-
);
|
|
2505
|
+
const key = readKey(file, node, enclosingMethod(node));
|
|
2506
|
+
const answer = site === void 0 ? writtenValueOf(db, key) : writtenValueUnder(db, key, site);
|
|
2265
2507
|
return answer === null ? null : nodeOfKey(rootsByFile, answer);
|
|
2266
2508
|
},
|
|
2267
2509
|
callable: (call) => {
|
|
@@ -2279,6 +2521,31 @@ function contextOver(db, nodes) {
|
|
|
2279
2521
|
};
|
|
2280
2522
|
}
|
|
2281
2523
|
|
|
2524
|
+
// src/values/literals.ts
|
|
2525
|
+
import { force as force2, literalOf as literalOf2 } from "@suss/values";
|
|
2526
|
+
function literalElementsIn(value) {
|
|
2527
|
+
if (value.kind !== "sequence") {
|
|
2528
|
+
return null;
|
|
2529
|
+
}
|
|
2530
|
+
const elements = [];
|
|
2531
|
+
for (const item of value.items) {
|
|
2532
|
+
const literal = literalOf2(force2(item.value));
|
|
2533
|
+
if (literal === null) {
|
|
2534
|
+
return null;
|
|
2535
|
+
}
|
|
2536
|
+
elements.push(literal);
|
|
2537
|
+
}
|
|
2538
|
+
return elements;
|
|
2539
|
+
}
|
|
2540
|
+
function literalElementsOf(node, db) {
|
|
2541
|
+
return literalElementsIn(evaluatedValue(node, db));
|
|
2542
|
+
}
|
|
2543
|
+
function namesOf(node, db) {
|
|
2544
|
+
const value = evaluatedValue(node, db);
|
|
2545
|
+
const single = literalOf2(value);
|
|
2546
|
+
return single === null ? literalElementsIn(value) : [single];
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2282
2549
|
// src/defineMethod.ts
|
|
2283
2550
|
var NOTHING = {
|
|
2284
2551
|
names: /* @__PURE__ */ new Set(),
|
|
@@ -2326,7 +2593,7 @@ function readOne(db, rootsByFile, nameKey2) {
|
|
|
2326
2593
|
const patterns = [];
|
|
2327
2594
|
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2328
2595
|
const value = node === null ? hole2(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2329
|
-
const name =
|
|
2596
|
+
const name = literalOf3(value);
|
|
2330
2597
|
if (name !== null) {
|
|
2331
2598
|
names.push(name);
|
|
2332
2599
|
continue;
|
|
@@ -2382,29 +2649,14 @@ function listsBehind(db, rootsByFile, nameKey2) {
|
|
|
2382
2649
|
0,
|
|
2383
2650
|
nameKey2
|
|
2384
2651
|
)) {
|
|
2385
|
-
const
|
|
2652
|
+
const node = nodeOfKey2(rootsByFile, String(overKey));
|
|
2653
|
+
const elements = node === null ? null : literalElementsOf(node, db);
|
|
2386
2654
|
if (elements !== null) {
|
|
2387
2655
|
found.push({ element: String(element), index: String(index), elements });
|
|
2388
2656
|
}
|
|
2389
2657
|
}
|
|
2390
2658
|
return found;
|
|
2391
2659
|
}
|
|
2392
|
-
function literalElementsOf(rootsByFile, key, db) {
|
|
2393
|
-
const node = nodeOfKey2(rootsByFile, key);
|
|
2394
|
-
const value = node === null ? null : evaluatedValue(node, db);
|
|
2395
|
-
if (value === null || value.kind !== "sequence") {
|
|
2396
|
-
return null;
|
|
2397
|
-
}
|
|
2398
|
-
const elements = [];
|
|
2399
|
-
for (const item of value.items) {
|
|
2400
|
-
const literal = literalOf2(force2(item.value));
|
|
2401
|
-
if (literal === null) {
|
|
2402
|
-
return null;
|
|
2403
|
-
}
|
|
2404
|
-
elements.push(literal);
|
|
2405
|
-
}
|
|
2406
|
-
return elements;
|
|
2407
|
-
}
|
|
2408
2660
|
|
|
2409
2661
|
// src/scope.ts
|
|
2410
2662
|
function qualifyConstantRef(node, nesting) {
|
|
@@ -2736,108 +2988,12 @@ import {
|
|
|
2736
2988
|
import { absentReading as absentReading2, unreadableReading as unreadableReading2 } from "@suss/extractor";
|
|
2737
2989
|
|
|
2738
2990
|
// src/clientCalls.ts
|
|
2739
|
-
import { namesNothing, restBinding } from "@suss/behavioral-ir";
|
|
2991
|
+
import { hasNameHole, namesNothing, restBinding } from "@suss/behavioral-ir";
|
|
2740
2992
|
import { pathOf } from "@suss/values";
|
|
2741
2993
|
|
|
2742
2994
|
// src/paths/effects.ts
|
|
2743
2995
|
import { enumerateOrDegrade, sharedGatingConditions } from "@suss/extractor";
|
|
2744
|
-
|
|
2745
|
-
// src/paths/bareCalls.ts
|
|
2746
|
-
var PARAMETER_LIST_TYPES = [
|
|
2747
|
-
"method_parameters",
|
|
2748
|
-
"block_parameters",
|
|
2749
|
-
"lambda_parameters",
|
|
2750
|
-
"parameters"
|
|
2751
|
-
];
|
|
2752
|
-
function isFieldOf(parent, node, name) {
|
|
2753
|
-
return field(parent, name)?.id === node.id;
|
|
2754
|
-
}
|
|
2755
|
-
function namedAt(parent, node) {
|
|
2756
|
-
return isFieldOf(parent, node, "name");
|
|
2757
|
-
}
|
|
2758
|
-
function always() {
|
|
2759
|
-
return true;
|
|
2760
|
-
}
|
|
2761
|
-
var SPELLS_A_NAME = {
|
|
2762
|
-
call: (parent, node) => isFieldOf(parent, node, "method") || isFieldOf(parent, node, "receiver"),
|
|
2763
|
-
method: namedAt,
|
|
2764
|
-
singleton_method: namedAt,
|
|
2765
|
-
assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2766
|
-
operator_assignment: (parent, node) => isFieldOf(parent, node, "left"),
|
|
2767
|
-
for: (parent, node) => isFieldOf(parent, node, "pattern"),
|
|
2768
|
-
optional_parameter: namedAt,
|
|
2769
|
-
splat_parameter: namedAt,
|
|
2770
|
-
hash_splat_parameter: namedAt,
|
|
2771
|
-
keyword_parameter: namedAt,
|
|
2772
|
-
block_parameter: namedAt,
|
|
2773
|
-
destructured_parameter: always,
|
|
2774
|
-
left_assignment_list: always,
|
|
2775
|
-
rest_assignment: always,
|
|
2776
|
-
destructured_left_assignment: always,
|
|
2777
|
-
exception_variable: always,
|
|
2778
|
-
undef: always,
|
|
2779
|
-
alias: always,
|
|
2780
|
-
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always]))
|
|
2781
|
-
};
|
|
2782
|
-
function addNamesUnder(node, names) {
|
|
2783
|
-
if (node === null) {
|
|
2784
|
-
return;
|
|
2785
|
-
}
|
|
2786
|
-
if (node.type === "identifier") {
|
|
2787
|
-
names.add(node.text);
|
|
2788
|
-
return;
|
|
2789
|
-
}
|
|
2790
|
-
for (const child of bodyStatements(node)) {
|
|
2791
|
-
addNamesUnder(child, names);
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
function parameterNames2(node, names) {
|
|
2795
|
-
for (const parameter of bodyStatements(node)) {
|
|
2796
|
-
if (parameter.type === "identifier") {
|
|
2797
|
-
names.add(parameter.text);
|
|
2798
|
-
continue;
|
|
2799
|
-
}
|
|
2800
|
-
const declared = field(parameter, "name");
|
|
2801
|
-
if (declared !== null && declared.type === "identifier") {
|
|
2802
|
-
names.add(declared.text);
|
|
2803
|
-
continue;
|
|
2804
|
-
}
|
|
2805
|
-
addNamesUnder(parameter, names);
|
|
2806
|
-
}
|
|
2807
|
-
}
|
|
2808
|
-
function assignedNames(node, names) {
|
|
2809
|
-
addNamesUnder(field(node, "left"), names);
|
|
2810
|
-
}
|
|
2811
|
-
var BINDS_A_NAME = {
|
|
2812
|
-
assignment: assignedNames,
|
|
2813
|
-
operator_assignment: assignedNames,
|
|
2814
|
-
for: (node, names) => addNamesUnder(field(node, "pattern"), names),
|
|
2815
|
-
exception_variable: (node, names) => addNamesUnder(node, names),
|
|
2816
|
-
...Object.fromEntries(
|
|
2817
|
-
PARAMETER_LIST_TYPES.map((type) => [type, parameterNames2])
|
|
2818
|
-
)
|
|
2819
|
-
};
|
|
2820
|
-
function localNamesIn(definitionNode) {
|
|
2821
|
-
const names = /* @__PURE__ */ new Set();
|
|
2822
|
-
const visit = (node) => {
|
|
2823
|
-
BINDS_A_NAME[node.type]?.(node, names);
|
|
2824
|
-
for (const child of bodyStatements(node)) {
|
|
2825
|
-
visit(child);
|
|
2826
|
-
}
|
|
2827
|
-
};
|
|
2828
|
-
visit(definitionNode);
|
|
2829
|
-
return names;
|
|
2830
|
-
}
|
|
2831
|
-
function isBareMethodCall(node, locals) {
|
|
2832
|
-
if (node.type !== "identifier" || locals.has(node.text)) {
|
|
2833
|
-
return false;
|
|
2834
|
-
}
|
|
2835
|
-
const parent = node.parent;
|
|
2836
|
-
if (parent === null) {
|
|
2837
|
-
return false;
|
|
2838
|
-
}
|
|
2839
|
-
return SPELLS_A_NAME[parent.type]?.(parent, node) !== true;
|
|
2840
|
-
}
|
|
2996
|
+
import { constantOf, literalOf as literalOf4 } from "@suss/values";
|
|
2841
2997
|
|
|
2842
2998
|
// src/paths/lowering.ts
|
|
2843
2999
|
var RAISE_NAMES = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
@@ -3125,23 +3281,21 @@ var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
|
3125
3281
|
"do_block",
|
|
3126
3282
|
"block"
|
|
3127
3283
|
]);
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
return
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
simple_symbol: (node) => ({ kind: "string", value: node.text.slice(1) })
|
|
3144
|
-
};
|
|
3284
|
+
function literalArgOf(node, facts) {
|
|
3285
|
+
const value = evaluatedValue(node, facts);
|
|
3286
|
+
const text2 = literalOf4(value);
|
|
3287
|
+
if (text2 !== null) {
|
|
3288
|
+
return { kind: "string", value: text2 };
|
|
3289
|
+
}
|
|
3290
|
+
const constant2 = constantOf(value);
|
|
3291
|
+
if (typeof constant2 === "number") {
|
|
3292
|
+
return { kind: "number", value: constant2 };
|
|
3293
|
+
}
|
|
3294
|
+
if (typeof constant2 === "boolean") {
|
|
3295
|
+
return { kind: "boolean", value: constant2 };
|
|
3296
|
+
}
|
|
3297
|
+
return null;
|
|
3298
|
+
}
|
|
3145
3299
|
function children2(node) {
|
|
3146
3300
|
return node.namedChildren.filter((child) => child !== null);
|
|
3147
3301
|
}
|
|
@@ -3225,23 +3379,27 @@ function calleeText(call) {
|
|
|
3225
3379
|
}
|
|
3226
3380
|
return receiver === null ? method : `${receiver.text}.${method}`;
|
|
3227
3381
|
}
|
|
3228
|
-
function argOf(node) {
|
|
3229
|
-
const literal =
|
|
3230
|
-
if (literal !== null
|
|
3382
|
+
function argOf(node, facts) {
|
|
3383
|
+
const literal = literalArgOf(node, facts);
|
|
3384
|
+
if (literal !== null) {
|
|
3231
3385
|
return literal;
|
|
3232
3386
|
}
|
|
3233
3387
|
if (node.type === "call" && !isArglessReceiverCall(node)) {
|
|
3234
|
-
return {
|
|
3388
|
+
return {
|
|
3389
|
+
kind: "call",
|
|
3390
|
+
callee: calleeText(node),
|
|
3391
|
+
args: argsOf(node, facts)
|
|
3392
|
+
};
|
|
3235
3393
|
}
|
|
3236
3394
|
return { kind: "identifier", name: node.text };
|
|
3237
3395
|
}
|
|
3238
|
-
function argsOf(call) {
|
|
3396
|
+
function argsOf(call, facts) {
|
|
3239
3397
|
const args = field(call, "arguments");
|
|
3240
3398
|
if (args === null) {
|
|
3241
3399
|
return [];
|
|
3242
3400
|
}
|
|
3243
3401
|
return children2(args).map(
|
|
3244
|
-
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child) : argOf(child)
|
|
3402
|
+
(child) => child.type === "pair" ? argOf(field(child, "value") ?? child, facts) : argOf(child, facts)
|
|
3245
3403
|
);
|
|
3246
3404
|
}
|
|
3247
3405
|
function enclosingStatement(call, body) {
|
|
@@ -3254,7 +3412,7 @@ function enclosingStatement(call, body) {
|
|
|
3254
3412
|
}
|
|
3255
3413
|
return current;
|
|
3256
3414
|
}
|
|
3257
|
-
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS) {
|
|
3415
|
+
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
3258
3416
|
const body = field(definitionNode, "body");
|
|
3259
3417
|
if (body === null) {
|
|
3260
3418
|
return [];
|
|
@@ -3294,7 +3452,7 @@ function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, kee
|
|
|
3294
3452
|
return {
|
|
3295
3453
|
type: "invocation",
|
|
3296
3454
|
callee: calleeText(call),
|
|
3297
|
-
args: argsOf(call),
|
|
3455
|
+
args: argsOf(call, facts),
|
|
3298
3456
|
async: false,
|
|
3299
3457
|
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
3300
3458
|
};
|
|
@@ -3309,7 +3467,7 @@ import {
|
|
|
3309
3467
|
unreadableReading,
|
|
3310
3468
|
writtenReading
|
|
3311
3469
|
} from "@suss/extractor";
|
|
3312
|
-
import { constantOf, literalOf as
|
|
3470
|
+
import { constantOf as constantOf2, literalOf as literalOf6 } from "@suss/values";
|
|
3313
3471
|
|
|
3314
3472
|
// src/paths/predicates.ts
|
|
3315
3473
|
var OPERATORS = {
|
|
@@ -3378,7 +3536,7 @@ function binaryOf(node) {
|
|
|
3378
3536
|
return opaqueOf(node);
|
|
3379
3537
|
}
|
|
3380
3538
|
function valueRefOf(node) {
|
|
3381
|
-
const literal =
|
|
3539
|
+
const literal = literalOf5(node);
|
|
3382
3540
|
if (literal !== null) {
|
|
3383
3541
|
return literal;
|
|
3384
3542
|
}
|
|
@@ -3391,7 +3549,7 @@ function valueRefOf(node) {
|
|
|
3391
3549
|
}
|
|
3392
3550
|
return { type: "unresolved", sourceText: node.text };
|
|
3393
3551
|
}
|
|
3394
|
-
function
|
|
3552
|
+
function literalOf5(node) {
|
|
3395
3553
|
if (node.type === "integer") {
|
|
3396
3554
|
const value = Number.parseInt(node.text, 10);
|
|
3397
3555
|
return Number.isNaN(value) ? null : { type: "literal", value };
|
|
@@ -3434,13 +3592,13 @@ function statusArgumentOf(call, declaration) {
|
|
|
3434
3592
|
}
|
|
3435
3593
|
return args.positional[declaration.statusArgument] ?? null;
|
|
3436
3594
|
}
|
|
3437
|
-
function statusNumberOf(node, names) {
|
|
3438
|
-
const value = evaluatedValue(node);
|
|
3439
|
-
const constant2 =
|
|
3595
|
+
function statusNumberOf(node, names, facts) {
|
|
3596
|
+
const value = evaluatedValue(node, facts);
|
|
3597
|
+
const constant2 = constantOf2(value);
|
|
3440
3598
|
if (typeof constant2 === "number") {
|
|
3441
3599
|
return constant2;
|
|
3442
3600
|
}
|
|
3443
|
-
const name =
|
|
3601
|
+
const name = literalOf6(value);
|
|
3444
3602
|
return name === null ? null : names[name] ?? null;
|
|
3445
3603
|
}
|
|
3446
3604
|
function declarationsByName(declarations) {
|
|
@@ -3484,12 +3642,12 @@ function collectReturns(node, found) {
|
|
|
3484
3642
|
}
|
|
3485
3643
|
return found;
|
|
3486
3644
|
}
|
|
3487
|
-
function readingOfCall(call, declaration, names) {
|
|
3645
|
+
function readingOfCall(call, declaration, names, facts) {
|
|
3488
3646
|
const argument = statusArgumentOf(call, declaration);
|
|
3489
3647
|
if (argument === null) {
|
|
3490
3648
|
return declaration.defaultStatusCode === void 0 ? absentReading : writtenReading(declaration.defaultStatusCode, rangeOf(call));
|
|
3491
3649
|
}
|
|
3492
|
-
const status = statusNumberOf(argument, names);
|
|
3650
|
+
const status = statusNumberOf(argument, names, facts);
|
|
3493
3651
|
if (status === null) {
|
|
3494
3652
|
return unreadableReading(
|
|
3495
3653
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
@@ -3633,7 +3791,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
3633
3791
|
const outcomes = [];
|
|
3634
3792
|
for (const terminal of terminals) {
|
|
3635
3793
|
const declaration = declarationOf(terminal, byName, locals);
|
|
3636
|
-
const reading = declaration === void 0 ? absentReading : readingOfCall(terminal, declaration, statusNames);
|
|
3794
|
+
const reading = declaration === void 0 ? absentReading : readingOfCall(terminal, declaration, statusNames, options.facts);
|
|
3637
3795
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
3638
3796
|
outcomes.push({
|
|
3639
3797
|
conditions: path7.map(conditionOf),
|
|
@@ -3667,11 +3825,9 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
3667
3825
|
continue;
|
|
3668
3826
|
}
|
|
3669
3827
|
for (const call of callsUnder(method)) {
|
|
3670
|
-
const request
|
|
3671
|
-
|
|
3672
|
-
continue;
|
|
3828
|
+
for (const request of requestCalls(call, pattern, options)) {
|
|
3829
|
+
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3673
3830
|
}
|
|
3674
|
-
units.push(clientUnit(method, name, request, pattern, pack, options));
|
|
3675
3831
|
}
|
|
3676
3832
|
}
|
|
3677
3833
|
return units;
|
|
@@ -3700,36 +3856,60 @@ function callsUnder(node, found = []) {
|
|
|
3700
3856
|
}
|
|
3701
3857
|
return found;
|
|
3702
3858
|
}
|
|
3703
|
-
function
|
|
3859
|
+
function requestCalls(call, pattern, options) {
|
|
3704
3860
|
const receiver = field(call, "receiver");
|
|
3705
3861
|
const called = field(call, "method")?.text;
|
|
3706
|
-
if (receiver === null || called === void 0) {
|
|
3707
|
-
return
|
|
3862
|
+
if (receiver === null || called === void 0 || !isLibraryReceiver(receiver, pattern, options)) {
|
|
3863
|
+
return [];
|
|
3708
3864
|
}
|
|
3709
|
-
const
|
|
3710
|
-
|
|
3711
|
-
|
|
3865
|
+
const read = (site) => requestCall(
|
|
3866
|
+
call,
|
|
3867
|
+
called,
|
|
3868
|
+
receiverPrefix(receiver, pattern, options, site),
|
|
3869
|
+
pattern,
|
|
3870
|
+
options,
|
|
3871
|
+
site
|
|
3872
|
+
);
|
|
3873
|
+
const plain = read();
|
|
3874
|
+
if (plain !== null && !hasNameHole(plain.path)) {
|
|
3875
|
+
return [plain];
|
|
3876
|
+
}
|
|
3877
|
+
const byBoundary = /* @__PURE__ */ new Map();
|
|
3878
|
+
for (const site of constructionSitesOf(call, options.facts)) {
|
|
3879
|
+
const stated = read(site);
|
|
3880
|
+
if (stated !== null) {
|
|
3881
|
+
byBoundary.set(`${stated.method} ${stated.path}`, stated);
|
|
3882
|
+
}
|
|
3712
3883
|
}
|
|
3884
|
+
if (byBoundary.size > 0) {
|
|
3885
|
+
return [...byBoundary.values()];
|
|
3886
|
+
}
|
|
3887
|
+
return plain === null ? [] : [plain];
|
|
3888
|
+
}
|
|
3889
|
+
function requestCall(call, called, prefix, pattern, options, site) {
|
|
3713
3890
|
const args = readCallArgs(field(call, "arguments"));
|
|
3714
3891
|
const verb = pattern.verbMethodNames[called];
|
|
3715
3892
|
if (verb !== void 0) {
|
|
3716
|
-
const path8 = urlIn(args, pattern, options);
|
|
3893
|
+
const path8 = urlIn(args, pattern, options, site);
|
|
3717
3894
|
return path8 === null ? null : { method: verb, path: prefix + path8 };
|
|
3718
3895
|
}
|
|
3719
3896
|
const sent = pattern.requestObject;
|
|
3720
3897
|
if (sent === void 0 || sent.attribute !== called) {
|
|
3721
3898
|
return null;
|
|
3722
3899
|
}
|
|
3723
|
-
const built = requestBuilt(args.positional[0],
|
|
3900
|
+
const built = requestBuilt(args.positional[0], sent, options, site);
|
|
3724
3901
|
if (built === null) {
|
|
3725
3902
|
return null;
|
|
3726
3903
|
}
|
|
3727
|
-
const path7 = pathAt(built.url, options);
|
|
3904
|
+
const path7 = pathAt(built.url, options, site);
|
|
3728
3905
|
return path7 === null ? null : { method: built.method, path: prefix + path7 };
|
|
3729
3906
|
}
|
|
3730
|
-
function requestBuilt(argument,
|
|
3731
|
-
|
|
3732
|
-
|
|
3907
|
+
function requestBuilt(argument, sent, options, site) {
|
|
3908
|
+
if (argument === void 0) {
|
|
3909
|
+
return null;
|
|
3910
|
+
}
|
|
3911
|
+
const written = writtenNodeOf(argument, options.facts, site) ?? argument;
|
|
3912
|
+
if (written.type !== "call") {
|
|
3733
3913
|
return null;
|
|
3734
3914
|
}
|
|
3735
3915
|
const requestClass = field(written, "receiver")?.text;
|
|
@@ -3740,28 +3920,18 @@ function requestBuilt(argument, method, sent) {
|
|
|
3740
3920
|
const args = readCallArgs(field(written, "arguments"));
|
|
3741
3921
|
return { method: verb, url: args.positional[sent.urlPosition] };
|
|
3742
3922
|
}
|
|
3743
|
-
function
|
|
3744
|
-
|
|
3745
|
-
return null;
|
|
3746
|
-
}
|
|
3747
|
-
const value = assignedValue(node.text, method);
|
|
3748
|
-
return value !== null && value.type === "call" ? value : null;
|
|
3923
|
+
function isLibraryReceiver(receiver, pattern, options) {
|
|
3924
|
+
return namesConstant(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
3749
3925
|
}
|
|
3750
|
-
function receiverPrefix(receiver,
|
|
3751
|
-
|
|
3752
|
-
return "";
|
|
3753
|
-
}
|
|
3754
|
-
if (receiver.type !== "identifier") {
|
|
3755
|
-
return null;
|
|
3756
|
-
}
|
|
3757
|
-
const built = builtBy(receiver.text, method, root, pattern);
|
|
3926
|
+
function receiverPrefix(receiver, pattern, options, site) {
|
|
3927
|
+
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
3758
3928
|
if (built === null) {
|
|
3759
|
-
return
|
|
3929
|
+
return "";
|
|
3760
3930
|
}
|
|
3761
3931
|
const args = readCallArgs(field(built, "arguments"));
|
|
3762
3932
|
const keyword = pattern.builderUrlKeyword;
|
|
3763
3933
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
3764
|
-
const path7 = base === void 0 ? null : pathOf(evaluatedValue(base, options.facts));
|
|
3934
|
+
const path7 = base === void 0 ? null : pathOf(evaluatedValue(base, options.facts, void 0, site));
|
|
3765
3935
|
return path7 === void 0 || path7 === null ? "" : trimmed(path7);
|
|
3766
3936
|
}
|
|
3767
3937
|
function trimmed(path7) {
|
|
@@ -3773,61 +3943,28 @@ function namesConstant(receiver, constantName2) {
|
|
|
3773
3943
|
}
|
|
3774
3944
|
return receiver.type === "scope_resolution" && compoundName(receiver) === constantName2;
|
|
3775
3945
|
}
|
|
3776
|
-
function
|
|
3946
|
+
function builderCallBehind(receiver, pattern, facts) {
|
|
3777
3947
|
const builders = pattern.receiverBuilders ?? [];
|
|
3778
3948
|
if (builders.length === 0) {
|
|
3779
3949
|
return null;
|
|
3780
3950
|
}
|
|
3781
|
-
const
|
|
3782
|
-
if (
|
|
3783
|
-
return null;
|
|
3784
|
-
}
|
|
3785
|
-
const receiver = field(value, "receiver");
|
|
3786
|
-
return receiver !== null && namesConstant(receiver, pattern.constantName) ? value : null;
|
|
3787
|
-
}
|
|
3788
|
-
function valueMethodNamedReturns(name, root) {
|
|
3789
|
-
const defined = methodDefinitions(root).find(
|
|
3790
|
-
(candidate) => field(candidate, "name")?.text === name
|
|
3791
|
-
);
|
|
3792
|
-
if (defined === void 0) {
|
|
3793
|
-
return null;
|
|
3794
|
-
}
|
|
3795
|
-
const body = field(defined, "body");
|
|
3796
|
-
const statements = body === null ? [] : body.namedChildren;
|
|
3797
|
-
const last = statements[statements.length - 1];
|
|
3798
|
-
if (last === void 0) {
|
|
3799
|
-
return null;
|
|
3800
|
-
}
|
|
3801
|
-
if (last.type === "operator_assignment" || last.type === "assignment") {
|
|
3802
|
-
return field(last, "right");
|
|
3803
|
-
}
|
|
3804
|
-
return last.type === "return" ? last.namedChildren[0] ?? null : last;
|
|
3805
|
-
}
|
|
3806
|
-
function assignedValue(name, method) {
|
|
3807
|
-
const body = field(method, "body");
|
|
3808
|
-
if (body === null) {
|
|
3951
|
+
const written = writtenNodeOf(receiver, facts);
|
|
3952
|
+
if (written === null || written.type !== "call" || !builders.includes(field(written, "method")?.text ?? "")) {
|
|
3809
3953
|
return null;
|
|
3810
3954
|
}
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
continue;
|
|
3814
|
-
}
|
|
3815
|
-
if (field(statement, "left")?.text === name) {
|
|
3816
|
-
return field(statement, "right");
|
|
3817
|
-
}
|
|
3818
|
-
}
|
|
3819
|
-
return null;
|
|
3955
|
+
const constant2 = field(written, "receiver");
|
|
3956
|
+
return constant2 !== null && namesConstant(constant2, pattern.constantName) ? written : null;
|
|
3820
3957
|
}
|
|
3821
|
-
function urlIn(args, pattern, options) {
|
|
3958
|
+
function urlIn(args, pattern, options, site) {
|
|
3822
3959
|
const keyword = pattern.url.keyword;
|
|
3823
3960
|
const written = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
3824
|
-
return pathAt(written, options);
|
|
3961
|
+
return pathAt(written, options, site);
|
|
3825
3962
|
}
|
|
3826
|
-
function pathAt(written, options) {
|
|
3963
|
+
function pathAt(written, options, site) {
|
|
3827
3964
|
if (written === void 0) {
|
|
3828
3965
|
return null;
|
|
3829
3966
|
}
|
|
3830
|
-
const path7 = pathOf(evaluatedValue(written, options.facts));
|
|
3967
|
+
const path7 = pathOf(evaluatedValue(written, options.facts, void 0, site));
|
|
3831
3968
|
return path7 === void 0 || namesNothing(path7) ? null : path7;
|
|
3832
3969
|
}
|
|
3833
3970
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
@@ -3850,15 +3987,15 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
3850
3987
|
recognition: pack.name
|
|
3851
3988
|
}),
|
|
3852
3989
|
parameters: [],
|
|
3853
|
-
branches: callerBranches(method, range),
|
|
3990
|
+
branches: callerBranches(method, range, options.facts),
|
|
3854
3991
|
...responseAccessors(pattern),
|
|
3855
3992
|
bodyContent: "statements",
|
|
3856
3993
|
dependencyCalls: [],
|
|
3857
3994
|
declaredContract: null
|
|
3858
3995
|
};
|
|
3859
3996
|
}
|
|
3860
|
-
function callerBranches(method, range) {
|
|
3861
|
-
const effects = invocationEffects(method);
|
|
3997
|
+
function callerBranches(method, range, facts) {
|
|
3998
|
+
const effects = invocationEffects(method, void 0, void 0, facts);
|
|
3862
3999
|
return returnPathBranches(method, effects) ?? [returnBranch(range, effects)];
|
|
3863
4000
|
}
|
|
3864
4001
|
function responseAccessors(pattern) {
|
|
@@ -4016,7 +4153,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4016
4153
|
}
|
|
4017
4154
|
const form = byName.get(called);
|
|
4018
4155
|
if (form !== void 0) {
|
|
4019
|
-
for (const declaration of declarationsOf(statement, form)) {
|
|
4156
|
+
for (const declaration of declarationsOf(statement, form, read.facts)) {
|
|
4020
4157
|
declared = [
|
|
4021
4158
|
...declared.filter((earlier) => !sameFilter(earlier, declaration)),
|
|
4022
4159
|
declaration
|
|
@@ -4026,7 +4163,7 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
4026
4163
|
}
|
|
4027
4164
|
const skipped = skipNames.get(called);
|
|
4028
4165
|
if (skipped !== void 0) {
|
|
4029
|
-
declared = applySkips(declared, skipsOf(statement, skipped));
|
|
4166
|
+
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
4030
4167
|
}
|
|
4031
4168
|
}
|
|
4032
4169
|
const resolved = [];
|
|
@@ -4063,14 +4200,14 @@ function filterReference(filter, displayPath) {
|
|
|
4063
4200
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
4064
4201
|
};
|
|
4065
4202
|
}
|
|
4066
|
-
function filterUnit(filter, pattern, displayPath, body) {
|
|
4203
|
+
function filterUnit(filter, pattern, displayPath, body, facts) {
|
|
4067
4204
|
const range = rangeOf(filter.method);
|
|
4068
4205
|
const branches = responseBranches(
|
|
4069
4206
|
filter.method,
|
|
4070
4207
|
pattern,
|
|
4071
4208
|
body.effects ?? [],
|
|
4072
4209
|
body.extraEffects,
|
|
4073
|
-
{ fallthrough: "handOn" }
|
|
4210
|
+
{ fallthrough: "handOn", facts }
|
|
4074
4211
|
);
|
|
4075
4212
|
return {
|
|
4076
4213
|
identity: {
|
|
@@ -4118,30 +4255,30 @@ function calledName(statement) {
|
|
|
4118
4255
|
}
|
|
4119
4256
|
return field(statement, "method")?.text ?? null;
|
|
4120
4257
|
}
|
|
4121
|
-
function declarationsOf(statement, filter) {
|
|
4258
|
+
function declarationsOf(statement, filter, facts) {
|
|
4122
4259
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4123
4260
|
const keywords = filter.actionKeywords;
|
|
4124
|
-
const only = keywords === void 0 ? null :
|
|
4125
|
-
const except = keywords === void 0 ? null :
|
|
4126
|
-
return methodNamesOf(args, filter).map((methodName) => ({
|
|
4261
|
+
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
4262
|
+
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
4263
|
+
return methodNamesOf(args, filter, facts).map((methodName) => ({
|
|
4127
4264
|
filter,
|
|
4128
4265
|
methodName,
|
|
4129
4266
|
only,
|
|
4130
4267
|
except: except ?? /* @__PURE__ */ new Set()
|
|
4131
4268
|
}));
|
|
4132
4269
|
}
|
|
4133
|
-
function methodNamesOf(args, filter) {
|
|
4270
|
+
function methodNamesOf(args, filter, facts) {
|
|
4134
4271
|
if (filter.methodFrom === "withKeyword") {
|
|
4135
4272
|
const named2 = args.keyword.with;
|
|
4136
|
-
const value = named2 === void 0 ? null :
|
|
4273
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
4137
4274
|
return value === null ? [] : [value];
|
|
4138
4275
|
}
|
|
4139
|
-
return args.positional.map((arg) =>
|
|
4276
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
4140
4277
|
}
|
|
4141
|
-
function skipsOf(statement, filterName) {
|
|
4278
|
+
function skipsOf(statement, filterName, facts) {
|
|
4142
4279
|
const args = readCallArgs(field(statement, "arguments"));
|
|
4143
|
-
const actions =
|
|
4144
|
-
return args.positional.map((arg) =>
|
|
4280
|
+
const actions = actionsUnder(args.keyword.only, facts);
|
|
4281
|
+
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
4145
4282
|
}
|
|
4146
4283
|
function sameFilter(one, other) {
|
|
4147
4284
|
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
@@ -4165,26 +4302,12 @@ function applySkips(declared, skips) {
|
|
|
4165
4302
|
}
|
|
4166
4303
|
return left;
|
|
4167
4304
|
}
|
|
4168
|
-
function
|
|
4305
|
+
function actionsUnder(node, facts) {
|
|
4169
4306
|
if (node === void 0) {
|
|
4170
4307
|
return null;
|
|
4171
4308
|
}
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
return /* @__PURE__ */ new Set([single]);
|
|
4175
|
-
}
|
|
4176
|
-
if (node.type !== "array" && node.type !== "symbol_array") {
|
|
4177
|
-
return null;
|
|
4178
|
-
}
|
|
4179
|
-
const names = /* @__PURE__ */ new Set();
|
|
4180
|
-
for (const child of bodyStatements(node)) {
|
|
4181
|
-
const bare = child.type === "bare_symbol" ? child.text : null;
|
|
4182
|
-
const name = symbolValue(child) ?? bare;
|
|
4183
|
-
if (name !== null) {
|
|
4184
|
-
names.add(name);
|
|
4185
|
-
}
|
|
4186
|
-
}
|
|
4187
|
-
return names;
|
|
4309
|
+
const found = namesOf(node, facts);
|
|
4310
|
+
return found === null ? null : new Set(found);
|
|
4188
4311
|
}
|
|
4189
4312
|
|
|
4190
4313
|
// src/storage.ts
|
|
@@ -4262,67 +4385,64 @@ function argumentsOf2(call) {
|
|
|
4262
4385
|
const args = field(call, "arguments");
|
|
4263
4386
|
return args === null ? [] : children3(args);
|
|
4264
4387
|
}
|
|
4265
|
-
function
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
function bareName(node) {
|
|
4269
|
-
return node.text.replace(/^:/, "").replace(/:$/, "");
|
|
4388
|
+
function recordFieldsOf(argument, facts) {
|
|
4389
|
+
const value = evaluatedValue(argument, facts);
|
|
4390
|
+
return value.kind === "record" ? [...value.fields.keys()] : [];
|
|
4270
4391
|
}
|
|
4271
|
-
function
|
|
4272
|
-
if (argument.type === "pair") {
|
|
4273
|
-
return [argument];
|
|
4274
|
-
}
|
|
4275
|
-
if (argument.type === "hash") {
|
|
4276
|
-
return children3(argument).filter((child) => child.type === "pair");
|
|
4277
|
-
}
|
|
4278
|
-
return [];
|
|
4279
|
-
}
|
|
4280
|
-
function keywordKeys(call) {
|
|
4392
|
+
function keywordKeys(call, facts) {
|
|
4281
4393
|
const keys = [];
|
|
4282
4394
|
for (const argument of argumentsOf2(call)) {
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4395
|
+
if (argument.type !== "pair") {
|
|
4396
|
+
keys.push(...recordFieldsOf(argument, facts));
|
|
4397
|
+
continue;
|
|
4398
|
+
}
|
|
4399
|
+
const key = field(argument, "key");
|
|
4400
|
+
const name = key === null ? null : keyNameOf(key);
|
|
4401
|
+
if (name !== null) {
|
|
4402
|
+
keys.push(name);
|
|
4288
4403
|
}
|
|
4289
4404
|
}
|
|
4290
4405
|
return keys;
|
|
4291
4406
|
}
|
|
4292
|
-
function
|
|
4293
|
-
return
|
|
4407
|
+
function keyNameOf(key) {
|
|
4408
|
+
return hashKeySymbolName(key) ?? symbolValue(key) ?? stringLiteralValue(key);
|
|
4294
4409
|
}
|
|
4295
|
-
function
|
|
4410
|
+
function hasPositionalArgument(call, facts) {
|
|
4411
|
+
return argumentsOf2(call).some(
|
|
4412
|
+
(argument) => argument.type !== "pair" && recordFieldsOf(argument, facts).length === 0
|
|
4413
|
+
);
|
|
4414
|
+
}
|
|
4415
|
+
function columnsAskedFor(call, pattern, facts) {
|
|
4296
4416
|
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4297
4417
|
return [];
|
|
4298
4418
|
}
|
|
4299
|
-
return argumentsOf2(call).
|
|
4419
|
+
return argumentsOf2(call).map((argument) => stringValueOf(argument, facts)).filter((column) => column !== null);
|
|
4300
4420
|
}
|
|
4301
|
-
function primaryKeySelector(call, pattern) {
|
|
4421
|
+
function primaryKeySelector(call, pattern, facts) {
|
|
4302
4422
|
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4303
4423
|
if (byPrimaryKey === void 0) {
|
|
4304
4424
|
return [];
|
|
4305
4425
|
}
|
|
4306
|
-
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call);
|
|
4426
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call, facts);
|
|
4307
4427
|
return picks ? [byPrimaryKey.column] : [];
|
|
4308
4428
|
}
|
|
4309
|
-
function selectorOf(call, pattern) {
|
|
4429
|
+
function selectorOf(call, pattern, facts) {
|
|
4310
4430
|
const picked = [];
|
|
4311
4431
|
for (const link of chainLinks(call)) {
|
|
4312
|
-
picked.push(...primaryKeySelector(link, pattern));
|
|
4432
|
+
picked.push(...primaryKeySelector(link, pattern, facts));
|
|
4313
4433
|
if (pattern.reads.includes(methodOf(link))) {
|
|
4314
|
-
picked.push(...keywordKeys(link));
|
|
4434
|
+
picked.push(...keywordKeys(link, facts));
|
|
4315
4435
|
}
|
|
4316
4436
|
}
|
|
4317
4437
|
return [...new Set(picked)];
|
|
4318
4438
|
}
|
|
4319
|
-
function fieldsOf(call, pattern, kind) {
|
|
4439
|
+
function fieldsOf(call, pattern, kind, facts) {
|
|
4320
4440
|
if (kind === "write") {
|
|
4321
|
-
return [...new Set(keywordKeys(call))];
|
|
4441
|
+
return [...new Set(keywordKeys(call, facts))];
|
|
4322
4442
|
}
|
|
4323
4443
|
return [
|
|
4324
4444
|
...new Set(
|
|
4325
|
-
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern))
|
|
4445
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern, facts))
|
|
4326
4446
|
)
|
|
4327
4447
|
];
|
|
4328
4448
|
}
|
|
@@ -4414,8 +4534,8 @@ function modelCallEffects(call, file, options, enclosing) {
|
|
|
4414
4534
|
target.container,
|
|
4415
4535
|
pattern,
|
|
4416
4536
|
kind,
|
|
4417
|
-
selectorOf(worked, pattern),
|
|
4418
|
-
fieldsOf(worked, pattern, kind)
|
|
4537
|
+
selectorOf(worked, pattern, options.facts),
|
|
4538
|
+
fieldsOf(worked, pattern, kind, options.facts)
|
|
4419
4539
|
)
|
|
4420
4540
|
];
|
|
4421
4541
|
}
|
|
@@ -4735,7 +4855,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4735
4855
|
filter,
|
|
4736
4856
|
pattern,
|
|
4737
4857
|
displayPath,
|
|
4738
|
-
bodyOfMethod(filter.method, filter.file, options)
|
|
4858
|
+
bodyOfMethod(filter.method, filter.file, options),
|
|
4859
|
+
options.facts
|
|
4739
4860
|
);
|
|
4740
4861
|
units.push(raw);
|
|
4741
4862
|
options.onReachSeed?.(raw, {
|
|
@@ -4826,7 +4947,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
4826
4947
|
method,
|
|
4827
4948
|
pattern,
|
|
4828
4949
|
body.effects ?? [],
|
|
4829
|
-
body.extraEffects
|
|
4950
|
+
body.extraEffects,
|
|
4951
|
+
{ facts: bodyRead.facts }
|
|
4830
4952
|
);
|
|
4831
4953
|
return {
|
|
4832
4954
|
identity: {
|
|
@@ -4956,7 +5078,8 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
4956
5078
|
const effects = invocationEffects(
|
|
4957
5079
|
method,
|
|
4958
5080
|
bodyRead.inheritedMethods,
|
|
4959
|
-
EVERY_ARGLESS_CALL
|
|
5081
|
+
EVERY_ARGLESS_CALL,
|
|
5082
|
+
bodyRead.facts
|
|
4960
5083
|
);
|
|
4961
5084
|
const storage = bodyRead.storage;
|
|
4962
5085
|
const extra = [
|
|
@@ -5694,6 +5817,7 @@ function parseWith(language, source) {
|
|
|
5694
5817
|
import fs3 from "fs";
|
|
5695
5818
|
import path4 from "path";
|
|
5696
5819
|
import {
|
|
5820
|
+
boundaryKey,
|
|
5697
5821
|
disambiguateSummaryIds,
|
|
5698
5822
|
linkCallsToSummaries,
|
|
5699
5823
|
placeArgTargets as placeArgTargets2,
|
|
@@ -5714,6 +5838,7 @@ import {
|
|
|
5714
5838
|
runDigest,
|
|
5715
5839
|
stampModuleImports
|
|
5716
5840
|
} from "@suss/extractor";
|
|
5841
|
+
import { addPackWords } from "@suss/resolution";
|
|
5717
5842
|
|
|
5718
5843
|
// src/diagnostics.ts
|
|
5719
5844
|
import {
|
|
@@ -6494,20 +6619,41 @@ function associationCallsIn(packs) {
|
|
|
6494
6619
|
)
|
|
6495
6620
|
);
|
|
6496
6621
|
}
|
|
6497
|
-
function
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
}
|
|
6622
|
+
function packWordsOf(packs) {
|
|
6623
|
+
return {
|
|
6624
|
+
givesBackOne: packs.flatMap(
|
|
6625
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6626
|
+
(pattern) => pattern.baseClasses.flatMap(
|
|
6627
|
+
(base) => pattern.givesBack.map((method) => ({ base, method }))
|
|
6628
|
+
)
|
|
6629
|
+
)
|
|
6630
|
+
)
|
|
6631
|
+
};
|
|
6632
|
+
}
|
|
6633
|
+
function factsForFile(options) {
|
|
6634
|
+
const db = new Database2();
|
|
6635
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6636
|
+
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6637
|
+
emitConstantBindings(db, [
|
|
6638
|
+
collectFileConstants(
|
|
6639
|
+
options.file,
|
|
6640
|
+
options.root,
|
|
6641
|
+
associationCallsIn(options.packs),
|
|
6642
|
+
inflectionsIn(options.packs)
|
|
6643
|
+
)
|
|
6644
|
+
]);
|
|
6645
|
+
emitRequireFacts(db, options.file, options.root, /* @__PURE__ */ new Set([options.file]));
|
|
6646
|
+
bindEvaluator(db, {
|
|
6647
|
+
files: [{ file: options.file, root: options.root }],
|
|
6648
|
+
definitions: new Map(methodDefinitionsIn(options.file, options.root))
|
|
6649
|
+
});
|
|
6650
|
+
addPackWords(db, packWordsOf(options.packs));
|
|
6651
|
+
return db;
|
|
6507
6652
|
}
|
|
6508
6653
|
function alreadyDiscovered(seen, raw) {
|
|
6509
6654
|
const reported = raw.identity.exportPath?.join(".") ?? raw.identity.name;
|
|
6510
|
-
const
|
|
6655
|
+
const boundary = raw.boundaryBinding === null ? "" : boundaryKey(raw.boundaryBinding) ?? "";
|
|
6656
|
+
const key = `${raw.identity.file}::${reported}::${raw.identity.range.start}::${boundary}`;
|
|
6511
6657
|
if (seen.has(key)) {
|
|
6512
6658
|
return true;
|
|
6513
6659
|
}
|
|
@@ -6574,7 +6720,7 @@ async function extractRubyProject(options) {
|
|
|
6574
6720
|
emitRequireFacts(db, file, root, known);
|
|
6575
6721
|
}
|
|
6576
6722
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6577
|
-
|
|
6723
|
+
addPackWords(db, packWordsOf(options.packs));
|
|
6578
6724
|
return readDynamicNames(
|
|
6579
6725
|
db,
|
|
6580
6726
|
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
@@ -6847,6 +6993,10 @@ function displayNameOf(node) {
|
|
|
6847
6993
|
function width(node) {
|
|
6848
6994
|
return node.endIndex - node.startIndex;
|
|
6849
6995
|
}
|
|
6996
|
+
function askedKey(value) {
|
|
6997
|
+
const enclosing = enclosingMethod2(value.node);
|
|
6998
|
+
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6999
|
+
}
|
|
6850
7000
|
var RubyWhySession = class {
|
|
6851
7001
|
root;
|
|
6852
7002
|
db = new Database3();
|
|
@@ -6922,7 +7072,7 @@ var RubyWhySession = class {
|
|
|
6922
7072
|
return explainResolvedKey({
|
|
6923
7073
|
db: this.db,
|
|
6924
7074
|
rules: WITNESS_RULES,
|
|
6925
|
-
key:
|
|
7075
|
+
key: askedKey(value),
|
|
6926
7076
|
locate: (key) => this.locate(key),
|
|
6927
7077
|
displayPath: (key) => this.displayPath(key),
|
|
6928
7078
|
...options
|
|
@@ -7039,6 +7189,7 @@ export {
|
|
|
7039
7189
|
emitValueFacts,
|
|
7040
7190
|
evaluatedValue,
|
|
7041
7191
|
extractRubyProject,
|
|
7192
|
+
factsForFile,
|
|
7042
7193
|
field,
|
|
7043
7194
|
findRubyFiles,
|
|
7044
7195
|
graphqlTypeNameFromQualified,
|