@suss/adapter-ruby 0.34.0 → 0.35.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/dist/index.cjs +1995 -504
- package/dist/index.d.cts +91 -19
- package/dist/index.d.ts +91 -19
- package/dist/index.js +1919 -412
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -19,7 +19,29 @@ function field(node, name) {
|
|
|
19
19
|
return node.childForFieldName(name);
|
|
20
20
|
}
|
|
21
21
|
function children(node) {
|
|
22
|
-
|
|
22
|
+
if (node === null || node === void 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const found = [];
|
|
26
|
+
const count = node.namedChildCount;
|
|
27
|
+
for (let index = 0; index < count; index++) {
|
|
28
|
+
const child = node.namedChild(index);
|
|
29
|
+
if (child !== null) {
|
|
30
|
+
found.push(child);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return found;
|
|
34
|
+
}
|
|
35
|
+
function allChildren(node) {
|
|
36
|
+
const found = [];
|
|
37
|
+
const count = node.childCount;
|
|
38
|
+
for (let index = 0; index < count; index++) {
|
|
39
|
+
const child = node.child(index);
|
|
40
|
+
if (child !== null) {
|
|
41
|
+
found.push(child);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return found;
|
|
23
45
|
}
|
|
24
46
|
function bodyStatements(body) {
|
|
25
47
|
return children(body);
|
|
@@ -81,6 +103,13 @@ var MODULE_SCOPE_STOPS = /* @__PURE__ */ new Set([
|
|
|
81
103
|
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
82
104
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
83
105
|
var LAMBDA_TYPE = "lambda";
|
|
106
|
+
var RAISING_METHODS = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
107
|
+
function isRaiseCall(node) {
|
|
108
|
+
if (node.type === "identifier") {
|
|
109
|
+
return RAISING_METHODS.has(node.text);
|
|
110
|
+
}
|
|
111
|
+
return node.type === "call" && field(node, "receiver") === null && RAISING_METHODS.has(field(node, "method")?.text ?? "");
|
|
112
|
+
}
|
|
84
113
|
function enclosingDefinition(node) {
|
|
85
114
|
return definitionsAround.of(node);
|
|
86
115
|
}
|
|
@@ -156,7 +185,7 @@ function runStatements(body, blockConfigures = () => false) {
|
|
|
156
185
|
}
|
|
157
186
|
return !(node.type === "call" && blockConfigures(node));
|
|
158
187
|
};
|
|
159
|
-
walkDescendants(body, null, {
|
|
188
|
+
walkDescendants(body, null, children, {
|
|
160
189
|
at: (node) => {
|
|
161
190
|
found.push(node);
|
|
162
191
|
},
|
|
@@ -242,8 +271,8 @@ var VISIBILITY_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
242
271
|
"protected",
|
|
243
272
|
"public"
|
|
244
273
|
]);
|
|
245
|
-
function visibilityKeyword(
|
|
246
|
-
return VISIBILITY_KEYWORDS.has(
|
|
274
|
+
function visibilityKeyword(text3) {
|
|
275
|
+
return VISIBILITY_KEYWORDS.has(text3) ? text3 : null;
|
|
247
276
|
}
|
|
248
277
|
function setVisibility(visibility, name, keyword) {
|
|
249
278
|
if (keyword === "public") {
|
|
@@ -443,6 +472,51 @@ function readCallArgs(argumentList) {
|
|
|
443
472
|
}
|
|
444
473
|
return { positional, keyword };
|
|
445
474
|
}
|
|
475
|
+
var CONTROL_OPERATORS = /* @__PURE__ */ new Set([
|
|
476
|
+
"and",
|
|
477
|
+
"or",
|
|
478
|
+
"&&",
|
|
479
|
+
"||"
|
|
480
|
+
]);
|
|
481
|
+
var HANDS_ON = /* @__PURE__ */ new Set(["return", "next", "break"]);
|
|
482
|
+
var isChild = (fieldName) => (parent, child) => field(parent, fieldName)?.id === child.id;
|
|
483
|
+
var always = () => true;
|
|
484
|
+
var asCondition = isChild("condition");
|
|
485
|
+
var VALUE_USED_UNDER = {
|
|
486
|
+
assignment: isChild("right"),
|
|
487
|
+
operator_assignment: isChild("right"),
|
|
488
|
+
call: isChild("receiver"),
|
|
489
|
+
element_reference: always,
|
|
490
|
+
argument_list: (parent) => !HANDS_ON.has(parent.parent?.type ?? ""),
|
|
491
|
+
pair: always,
|
|
492
|
+
array: always,
|
|
493
|
+
hash: always,
|
|
494
|
+
interpolation: always,
|
|
495
|
+
unary: always,
|
|
496
|
+
binary: (parent) => !CONTROL_OPERATORS.has(field(parent, "operator")?.text ?? ""),
|
|
497
|
+
if: asCondition,
|
|
498
|
+
unless: asCondition,
|
|
499
|
+
while: asCondition,
|
|
500
|
+
until: asCondition,
|
|
501
|
+
if_modifier: asCondition,
|
|
502
|
+
unless_modifier: asCondition,
|
|
503
|
+
while_modifier: asCondition,
|
|
504
|
+
until_modifier: asCondition,
|
|
505
|
+
conditional: asCondition,
|
|
506
|
+
case: isChild("value")
|
|
507
|
+
};
|
|
508
|
+
function resultUsedAsValue(node) {
|
|
509
|
+
let child = node;
|
|
510
|
+
let parent = node.parent;
|
|
511
|
+
while (parent !== null && parent.type === "parenthesized_statements" && parent.namedChildCount === 1) {
|
|
512
|
+
child = parent;
|
|
513
|
+
parent = parent.parent;
|
|
514
|
+
}
|
|
515
|
+
if (parent === null) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
return VALUE_USED_UNDER[parent.type]?.(parent, child) ?? false;
|
|
519
|
+
}
|
|
446
520
|
var NodeSet = class extends IdSet {
|
|
447
521
|
};
|
|
448
522
|
var NodeMap = class extends IdMap {
|
|
@@ -468,8 +542,8 @@ function lowerAcronyms(word, acronyms) {
|
|
|
468
542
|
(_match, before, acronym) => `${before === void 0 ? "" : "_"}${acronym.toLowerCase()}`
|
|
469
543
|
);
|
|
470
544
|
}
|
|
471
|
-
function escapeRegExp(
|
|
472
|
-
return
|
|
545
|
+
function escapeRegExp(text3) {
|
|
546
|
+
return text3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
473
547
|
}
|
|
474
548
|
var PATH_CONVENTIONS = {
|
|
475
549
|
railsUnderscore: underscoreConstantPath
|
|
@@ -477,19 +551,23 @@ var PATH_CONVENTIONS = {
|
|
|
477
551
|
function createConstantFileCache() {
|
|
478
552
|
return { autoloadDirectories: /* @__PURE__ */ new Map(), resolved: /* @__PURE__ */ new Map() };
|
|
479
553
|
}
|
|
480
|
-
function resolveConstantFile(root, qualifiedName, convention, acronyms = [], cache = createConstantFileCache()) {
|
|
554
|
+
function resolveConstantFile(root, qualifiedName, convention, acronyms = [], cache = createConstantFileCache(), autoloadRoots = []) {
|
|
481
555
|
const relative = `${PATH_CONVENTIONS[convention](qualifiedName, acronyms)}.rb`;
|
|
482
|
-
const key =
|
|
556
|
+
const key = [root, ...autoloadRoots, relative].join("\0");
|
|
483
557
|
const known = cache.resolved.get(key);
|
|
484
558
|
if (known !== void 0) {
|
|
485
559
|
return known;
|
|
486
560
|
}
|
|
487
|
-
const found = firstExisting(root, relative, cache);
|
|
561
|
+
const found = firstExisting(root, relative, cache, autoloadRoots);
|
|
488
562
|
cache.resolved.set(key, found);
|
|
489
563
|
return found;
|
|
490
564
|
}
|
|
491
|
-
function firstExisting(root, relative, cache) {
|
|
492
|
-
for (const candidate of [
|
|
565
|
+
function firstExisting(root, relative, cache, autoloadRoots) {
|
|
566
|
+
for (const candidate of [
|
|
567
|
+
root,
|
|
568
|
+
...autoloadDirectoriesOf(root, cache),
|
|
569
|
+
...autoloadRoots
|
|
570
|
+
]) {
|
|
493
571
|
const file = path.join(candidate, relative);
|
|
494
572
|
if (fs.existsSync(file)) {
|
|
495
573
|
return file;
|
|
@@ -528,7 +606,7 @@ function isDirectory(directory) {
|
|
|
528
606
|
}
|
|
529
607
|
|
|
530
608
|
// src/defineMethod.ts
|
|
531
|
-
import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
|
|
609
|
+
import { nodeOfKey as nodeOfKey2, spanOfKey } from "@suss/resolution";
|
|
532
610
|
import { hole as hole2, literalOf as literalOf3, piecesOf } from "@suss/values";
|
|
533
611
|
|
|
534
612
|
// src/values/evaluator.ts
|
|
@@ -536,10 +614,12 @@ import { nodeOfKey, noteKeyRead, writtenValuesByKey } from "@suss/resolution";
|
|
|
536
614
|
import { Evaluator, force, literalOf, text } from "@suss/values";
|
|
537
615
|
|
|
538
616
|
// src/facts/resolve.ts
|
|
617
|
+
import { isTestFile } from "@suss/behavioral-ir";
|
|
539
618
|
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
540
619
|
import {
|
|
541
620
|
allocationSitesOf,
|
|
542
621
|
alsoSteps,
|
|
622
|
+
answersFromTestsLeftOut,
|
|
543
623
|
askResolution,
|
|
544
624
|
askResolutionUnder,
|
|
545
625
|
askSources,
|
|
@@ -565,6 +645,80 @@ var CONVERTING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
565
645
|
"String",
|
|
566
646
|
"Float"
|
|
567
647
|
]);
|
|
648
|
+
var OBJECT_METHODS = /* @__PURE__ */ new Set([
|
|
649
|
+
"Array",
|
|
650
|
+
"Float",
|
|
651
|
+
"Hash",
|
|
652
|
+
"Integer",
|
|
653
|
+
"String",
|
|
654
|
+
"abort",
|
|
655
|
+
"binding",
|
|
656
|
+
"block_given?",
|
|
657
|
+
"caller",
|
|
658
|
+
"catch",
|
|
659
|
+
"class",
|
|
660
|
+
"clone",
|
|
661
|
+
"define_singleton_method",
|
|
662
|
+
"dup",
|
|
663
|
+
"eql?",
|
|
664
|
+
"equal?",
|
|
665
|
+
"eval",
|
|
666
|
+
"exit",
|
|
667
|
+
"extend",
|
|
668
|
+
"fail",
|
|
669
|
+
"format",
|
|
670
|
+
"freeze",
|
|
671
|
+
"frozen?",
|
|
672
|
+
"hash",
|
|
673
|
+
"inspect",
|
|
674
|
+
"instance_of?",
|
|
675
|
+
"instance_variable_defined?",
|
|
676
|
+
"instance_variable_get",
|
|
677
|
+
"instance_variable_set",
|
|
678
|
+
"instance_variables",
|
|
679
|
+
"is_a?",
|
|
680
|
+
"itself",
|
|
681
|
+
"kind_of?",
|
|
682
|
+
"lambda",
|
|
683
|
+
"loop",
|
|
684
|
+
"method",
|
|
685
|
+
"methods",
|
|
686
|
+
"nil?",
|
|
687
|
+
"object_id",
|
|
688
|
+
"p",
|
|
689
|
+
"pp",
|
|
690
|
+
"print",
|
|
691
|
+
"printf",
|
|
692
|
+
"proc",
|
|
693
|
+
"public_send",
|
|
694
|
+
"puts",
|
|
695
|
+
"raise",
|
|
696
|
+
"rand",
|
|
697
|
+
"require",
|
|
698
|
+
"require_relative",
|
|
699
|
+
"respond_to?",
|
|
700
|
+
"send",
|
|
701
|
+
"singleton_class",
|
|
702
|
+
"sleep",
|
|
703
|
+
"sprintf",
|
|
704
|
+
"srand",
|
|
705
|
+
"system",
|
|
706
|
+
"tap",
|
|
707
|
+
"then",
|
|
708
|
+
"throw",
|
|
709
|
+
"to_s",
|
|
710
|
+
"warn",
|
|
711
|
+
"yield_self"
|
|
712
|
+
]);
|
|
713
|
+
var SENDING_METHODS = /* @__PURE__ */ new Set([
|
|
714
|
+
"send",
|
|
715
|
+
"public_send",
|
|
716
|
+
"__send__"
|
|
717
|
+
]);
|
|
718
|
+
var METHOD_REFERENCE = "method";
|
|
719
|
+
var INVOKE_METHOD = "call";
|
|
720
|
+
var BLOCK_WRAPPERS = /* @__PURE__ */ new Set(["proc", "lambda"]);
|
|
721
|
+
var PROC_CLASS = "Proc";
|
|
568
722
|
var CONSTRUCTOR = "new";
|
|
569
723
|
|
|
570
724
|
// src/facts/resolve.ts
|
|
@@ -612,7 +766,13 @@ function resolveEnvObjects(db, objects) {
|
|
|
612
766
|
}
|
|
613
767
|
function writtenValueOf(db, key) {
|
|
614
768
|
resolveValues(db, [key]);
|
|
615
|
-
return sharedWrittenValueOf(
|
|
769
|
+
return sharedWrittenValueOf(
|
|
770
|
+
db,
|
|
771
|
+
key,
|
|
772
|
+
(keys) => resolveValues(db, keys),
|
|
773
|
+
void 0,
|
|
774
|
+
answersFromTestsLeftOut(key, isTestFile)
|
|
775
|
+
);
|
|
616
776
|
}
|
|
617
777
|
function writtenValueUnder(db, key, site) {
|
|
618
778
|
const program = resolutionUnderProgram(RUBY_RULES);
|
|
@@ -630,12 +790,16 @@ function constructionSites(db, classKey) {
|
|
|
630
790
|
|
|
631
791
|
// src/facts/values.ts
|
|
632
792
|
import {
|
|
793
|
+
addEntryRows,
|
|
633
794
|
addPackWords,
|
|
795
|
+
addTemplateRows,
|
|
634
796
|
classMemberName,
|
|
635
797
|
NAMED_STORE_NAME,
|
|
636
798
|
RECEIVER_STORE_NAME,
|
|
799
|
+
SPREAD_ENTRY,
|
|
637
800
|
startsAtName,
|
|
638
801
|
UNPLACED_STORE_NAME,
|
|
802
|
+
VALUE_ENTRY,
|
|
639
803
|
valueLeftByWrites
|
|
640
804
|
} from "@suss/resolution";
|
|
641
805
|
|
|
@@ -652,7 +816,7 @@ function isFieldOf(parent, node, name) {
|
|
|
652
816
|
function namedAt(parent, node) {
|
|
653
817
|
return isFieldOf(parent, node, "name");
|
|
654
818
|
}
|
|
655
|
-
function
|
|
819
|
+
function always2() {
|
|
656
820
|
return true;
|
|
657
821
|
}
|
|
658
822
|
var SPELLS_A_NAME = {
|
|
@@ -667,14 +831,14 @@ var SPELLS_A_NAME = {
|
|
|
667
831
|
hash_splat_parameter: namedAt,
|
|
668
832
|
keyword_parameter: namedAt,
|
|
669
833
|
block_parameter: namedAt,
|
|
670
|
-
destructured_parameter:
|
|
671
|
-
left_assignment_list:
|
|
672
|
-
rest_assignment:
|
|
673
|
-
destructured_left_assignment:
|
|
674
|
-
exception_variable:
|
|
675
|
-
undef:
|
|
676
|
-
alias:
|
|
677
|
-
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type,
|
|
834
|
+
destructured_parameter: always2,
|
|
835
|
+
left_assignment_list: always2,
|
|
836
|
+
rest_assignment: always2,
|
|
837
|
+
destructured_left_assignment: always2,
|
|
838
|
+
exception_variable: always2,
|
|
839
|
+
undef: always2,
|
|
840
|
+
alias: always2,
|
|
841
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always2]))
|
|
678
842
|
};
|
|
679
843
|
function addNamesUnder(node, names) {
|
|
680
844
|
if (node === null) {
|
|
@@ -1168,6 +1332,9 @@ function add(emitter, relation, ...tuple) {
|
|
|
1168
1332
|
}
|
|
1169
1333
|
emitter.db.add(relation, tuple);
|
|
1170
1334
|
}
|
|
1335
|
+
function rowsOf(emitter) {
|
|
1336
|
+
return (relation, row) => add(emitter, relation, ...row);
|
|
1337
|
+
}
|
|
1171
1338
|
function valueKey(emitter, written, parent) {
|
|
1172
1339
|
return readKey(emitter.filePath, written, emitter.enclosing, parent);
|
|
1173
1340
|
}
|
|
@@ -1251,6 +1418,97 @@ function emitCall(emitter, site) {
|
|
|
1251
1418
|
}
|
|
1252
1419
|
emitCallArguments(emitter, site);
|
|
1253
1420
|
emitConversion(emitter, site);
|
|
1421
|
+
emitSentChoice(emitter, site);
|
|
1422
|
+
emitMethodReference(emitter, site);
|
|
1423
|
+
emitBlockAsProc(emitter, site);
|
|
1424
|
+
}
|
|
1425
|
+
function emitBlockAsProc(emitter, site) {
|
|
1426
|
+
const { method, receiver, node } = site;
|
|
1427
|
+
const name = method?.text;
|
|
1428
|
+
const wraps = receiver === null ? name !== void 0 && BLOCK_WRAPPERS.has(name) : name === CONSTRUCTOR && receiver.text === PROC_CLASS;
|
|
1429
|
+
const block = field(node, "block");
|
|
1430
|
+
if (!wraps || block === null) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
const blockKey = nodeId(emitter.filePath, block);
|
|
1434
|
+
add(emitter, "func", blockKey);
|
|
1435
|
+
add(emitter, "binds", site.key, blockKey);
|
|
1436
|
+
for (const call of callsWrittenIn(block)) {
|
|
1437
|
+
add(emitter, "makesCall", blockKey, nodeId(emitter.filePath, call));
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
function callsWrittenIn(node, found = []) {
|
|
1441
|
+
for (const child of children(node)) {
|
|
1442
|
+
if (OWN_BODY_TYPES.has(child.type)) {
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (child.type === "call") {
|
|
1446
|
+
found.push(child);
|
|
1447
|
+
}
|
|
1448
|
+
callsWrittenIn(child, found);
|
|
1449
|
+
}
|
|
1450
|
+
return found;
|
|
1451
|
+
}
|
|
1452
|
+
function emitSentChoice(emitter, site) {
|
|
1453
|
+
const { method, receiver, node } = site;
|
|
1454
|
+
if (method === null || !SENDING_METHODS.has(method.text)) {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
const objectKey = receiver === null ? emitter.selfKey : valueKey(emitter, receiver, node);
|
|
1458
|
+
const [name] = readCallArgs(field(node, "arguments")).positional;
|
|
1459
|
+
if (objectKey === null || name === void 0) {
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
add(emitter, "callsChosenEntry", site.key);
|
|
1463
|
+
const written = symbolValue(name) ?? stringLiteralValue(name);
|
|
1464
|
+
if (written !== null) {
|
|
1465
|
+
for (const spelled of namesReadAs(emitter, receiver, objectKey, written)) {
|
|
1466
|
+
add(emitter, "choosesNamedEntry", site.key, objectKey, spelled);
|
|
1467
|
+
}
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
const ends = writtenEnds(name);
|
|
1471
|
+
if (ends === null) {
|
|
1472
|
+
add(emitter, "choosesEntry", site.key, objectKey);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
for (const start of namesReadAs(emitter, receiver, objectKey, ends.start)) {
|
|
1476
|
+
add(emitter, "choosesEntryLike", site.key, objectKey, start, ends.end);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function writtenEnds(node) {
|
|
1480
|
+
if (node.type !== "string" && node.type !== "delimited_symbol") {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
const parts = children(node);
|
|
1484
|
+
const written = (part) => part.type === "string_content";
|
|
1485
|
+
const firstComputed = parts.findIndex((part) => !written(part));
|
|
1486
|
+
if (firstComputed === -1) {
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
const lastComputed = parts.length - 1 - [...parts].reverse().findIndex((part) => !written(part));
|
|
1490
|
+
const start = parts.slice(0, firstComputed).map((part) => part.text).join("");
|
|
1491
|
+
const end = parts.slice(lastComputed + 1).map((part) => part.text).join("");
|
|
1492
|
+
return start === "" && end === "" ? null : { start, end };
|
|
1493
|
+
}
|
|
1494
|
+
function emitMethodReference(emitter, site) {
|
|
1495
|
+
const { method, receiver, node } = site;
|
|
1496
|
+
if (method?.text !== METHOD_REFERENCE || receiver !== null || emitter.selfKey === null) {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const [name] = readCallArgs(field(node, "arguments")).positional;
|
|
1500
|
+
const written = name === void 0 ? null : symbolValue(name) ?? stringLiteralValue(name);
|
|
1501
|
+
if (written === null) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
for (const spelled of namesReadAs(
|
|
1505
|
+
emitter,
|
|
1506
|
+
receiver,
|
|
1507
|
+
emitter.selfKey,
|
|
1508
|
+
written
|
|
1509
|
+
)) {
|
|
1510
|
+
add(emitter, "readsProperty", site.key, emitter.selfKey, spelled);
|
|
1511
|
+
}
|
|
1254
1512
|
}
|
|
1255
1513
|
function emitConversion(emitter, site) {
|
|
1256
1514
|
const { method, receiver, node } = site;
|
|
@@ -1324,7 +1582,6 @@ var SELF_READS = {
|
|
|
1324
1582
|
function classSpelling(name) {
|
|
1325
1583
|
return name === CONSTRUCTOR ? name : classMemberName(name);
|
|
1326
1584
|
}
|
|
1327
|
-
var INVOKE_METHOD = "call";
|
|
1328
1585
|
function invokedValueOf(call) {
|
|
1329
1586
|
if (call.type !== "call") {
|
|
1330
1587
|
return null;
|
|
@@ -1370,6 +1627,7 @@ function emitArray(emitter, array) {
|
|
|
1370
1627
|
const objectKey = nodeId(emitter.filePath, array);
|
|
1371
1628
|
add(emitter, "objectValue", objectKey);
|
|
1372
1629
|
let position = 0;
|
|
1630
|
+
const entries = [];
|
|
1373
1631
|
for (const element of children(array)) {
|
|
1374
1632
|
add(
|
|
1375
1633
|
emitter,
|
|
@@ -1379,12 +1637,24 @@ function emitArray(emitter, array) {
|
|
|
1379
1637
|
valueKey(emitter, element, array)
|
|
1380
1638
|
);
|
|
1381
1639
|
position += 1;
|
|
1640
|
+
entries.push(writtenEntry(emitter, element, array));
|
|
1382
1641
|
}
|
|
1642
|
+
addEntryRows(rowsOf(emitter), objectKey, entries);
|
|
1643
|
+
}
|
|
1644
|
+
var SPREAD_TYPES = /* @__PURE__ */ new Set(["splat_argument", "hash_splat_argument"]);
|
|
1645
|
+
function writtenEntry(emitter, entry, parent) {
|
|
1646
|
+
const unpacked = SPREAD_TYPES.has(entry.type) ? children(entry)[0] : void 0;
|
|
1647
|
+
return unpacked === void 0 ? { key: valueKey(emitter, entry, parent), form: VALUE_ENTRY } : { key: valueKey(emitter, unpacked, entry), form: SPREAD_ENTRY };
|
|
1383
1648
|
}
|
|
1384
1649
|
function emitHash(emitter, hash) {
|
|
1385
1650
|
const objectKey = nodeId(emitter.filePath, hash);
|
|
1386
1651
|
add(emitter, "objectValue", objectKey);
|
|
1652
|
+
const entries = [];
|
|
1387
1653
|
for (const pair of children(hash)) {
|
|
1654
|
+
if (SPREAD_TYPES.has(pair.type)) {
|
|
1655
|
+
entries.push(writtenEntry(emitter, pair, hash));
|
|
1656
|
+
continue;
|
|
1657
|
+
}
|
|
1388
1658
|
if (pair.type !== "pair") {
|
|
1389
1659
|
continue;
|
|
1390
1660
|
}
|
|
@@ -1393,6 +1663,7 @@ function emitHash(emitter, hash) {
|
|
|
1393
1663
|
if (key === null || value === null) {
|
|
1394
1664
|
continue;
|
|
1395
1665
|
}
|
|
1666
|
+
entries.push(writtenEntry(emitter, value, pair));
|
|
1396
1667
|
const keyText = pairKeyText(key);
|
|
1397
1668
|
if (keyText === null) {
|
|
1398
1669
|
add(emitter, "holdsUnderKey", objectKey, valueKey(emitter, value, pair));
|
|
@@ -1406,32 +1677,66 @@ function emitHash(emitter, hash) {
|
|
|
1406
1677
|
valueKey(emitter, value, pair)
|
|
1407
1678
|
);
|
|
1408
1679
|
}
|
|
1680
|
+
addEntryRows(rowsOf(emitter), objectKey, entries);
|
|
1409
1681
|
}
|
|
1410
1682
|
var BLOCK_TYPES3 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1411
1683
|
var LOOP_METHODS = /* @__PURE__ */ new Set(["each", "each_with_index", "map"]);
|
|
1412
|
-
var
|
|
1413
|
-
function
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1417
|
-
const receiver = field(call, "receiver");
|
|
1418
|
-
const method = field(call, "method");
|
|
1684
|
+
var COUNTING_LOOP = "each_with_index";
|
|
1685
|
+
function emitLoopVariables(emitter, block, call) {
|
|
1686
|
+
const receiver = call.type === "call" ? field(call, "receiver") : null;
|
|
1687
|
+
const method = receiver === null ? null : field(call, "method");
|
|
1419
1688
|
if (receiver === null || method === null || !LOOP_METHODS.has(method.text)) {
|
|
1420
|
-
return
|
|
1689
|
+
return;
|
|
1421
1690
|
}
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1691
|
+
const variables = children(field(block, "parameters")).filter(
|
|
1692
|
+
(variable) => variable.type !== "block_parameter"
|
|
1693
|
+
);
|
|
1694
|
+
const counting = method.text === COUNTING_LOOP;
|
|
1695
|
+
const [first, position] = variables;
|
|
1696
|
+
if (first === void 0 || counting && variables.length > 2) {
|
|
1697
|
+
return;
|
|
1427
1698
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1699
|
+
const taken = counting || variables.length === 1 ? elementTakenBy(first) : partsTakenBy(variables);
|
|
1700
|
+
const blockKey = nodeId(emitter.filePath, block);
|
|
1701
|
+
const over = valueKey(emitter, receiver, call);
|
|
1702
|
+
for (const variable of taken) {
|
|
1703
|
+
const key = `${blockKey}#${variable.name}`;
|
|
1704
|
+
if (variable.part === null) {
|
|
1705
|
+
add(emitter, "elementOf", key, over);
|
|
1706
|
+
continue;
|
|
1707
|
+
}
|
|
1708
|
+
add(emitter, "elementPartOf", key, over, variable.part);
|
|
1709
|
+
}
|
|
1710
|
+
const [anchor] = taken;
|
|
1711
|
+
if (counting && position?.type === "identifier" && anchor !== void 0) {
|
|
1712
|
+
add(
|
|
1713
|
+
emitter,
|
|
1714
|
+
"positionOf",
|
|
1715
|
+
`${blockKey}#${position.text}`,
|
|
1716
|
+
`${blockKey}#${anchor.name}`
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
function elementTakenBy(variable) {
|
|
1721
|
+
if (variable.type === "identifier") {
|
|
1722
|
+
return [{ name: variable.text, part: null }];
|
|
1723
|
+
}
|
|
1724
|
+
return variable.type === "destructured_parameter" ? partsTakenBy(children(variable)) : [];
|
|
1725
|
+
}
|
|
1726
|
+
function partsTakenBy(variables) {
|
|
1727
|
+
const parts = [];
|
|
1728
|
+
for (const [position, variable] of variables.entries()) {
|
|
1729
|
+
if (variable.type === "splat_parameter") {
|
|
1730
|
+
break;
|
|
1731
|
+
}
|
|
1732
|
+
if (variable.type === "identifier") {
|
|
1733
|
+
parts.push({ name: variable.text, part: String(position) });
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
return parts;
|
|
1433
1737
|
}
|
|
1434
|
-
|
|
1738
|
+
var DEFINE_METHOD_CALL = "define_method";
|
|
1739
|
+
function emitDynamicDefinition(emitter, call, type) {
|
|
1435
1740
|
if (emitter.selfKey === null || // A call inside a method runs when the method runs, not when the class loads.
|
|
1436
1741
|
emitter.enclosing !== null || type !== "call" || field(call, "receiver") !== null || field(call, "method")?.text !== DEFINE_METHOD_CALL) {
|
|
1437
1742
|
return;
|
|
@@ -1440,33 +1745,16 @@ function emitDynamicDefinition(emitter, call, type, turns) {
|
|
|
1440
1745
|
const first = args === null ? void 0 : children(args)[0];
|
|
1441
1746
|
const nameKey2 = nodeId(emitter.filePath, first ?? call);
|
|
1442
1747
|
add(emitter, "definesMethodFrom", emitter.selfKey, nameKey2);
|
|
1443
|
-
for (const turn of turns) {
|
|
1444
|
-
add(
|
|
1445
|
-
emitter,
|
|
1446
|
-
"nameTurnsOn",
|
|
1447
|
-
nameKey2,
|
|
1448
|
-
turn.element,
|
|
1449
|
-
turn.index,
|
|
1450
|
-
turn.overKey
|
|
1451
|
-
);
|
|
1452
|
-
}
|
|
1453
1748
|
}
|
|
1454
|
-
function walkExpressions(node,
|
|
1749
|
+
function walkExpressions(node, visit, nested = null) {
|
|
1455
1750
|
for (const child of children(node)) {
|
|
1456
1751
|
const type = child.type;
|
|
1457
1752
|
if (OWN_BODY_TYPES.has(type)) {
|
|
1458
1753
|
nested?.push(child);
|
|
1459
1754
|
continue;
|
|
1460
1755
|
}
|
|
1461
|
-
visit(child, type, node
|
|
1462
|
-
|
|
1463
|
-
walkExpressions(
|
|
1464
|
-
child,
|
|
1465
|
-
emitter,
|
|
1466
|
-
visit,
|
|
1467
|
-
nested,
|
|
1468
|
-
opened === null ? turns : [...turns, opened]
|
|
1469
|
-
);
|
|
1756
|
+
visit(child, type, node);
|
|
1757
|
+
walkExpressions(child, visit, nested);
|
|
1470
1758
|
}
|
|
1471
1759
|
}
|
|
1472
1760
|
var WRITTEN_KEY_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -1480,22 +1768,23 @@ function emitKeyedRead(emitter, site, container, key, keyParent) {
|
|
|
1480
1768
|
if (WRITTEN_KEY_TYPES.has(key.type)) {
|
|
1481
1769
|
return;
|
|
1482
1770
|
}
|
|
1771
|
+
const siteKey = nodeId(emitter.filePath, site);
|
|
1772
|
+
const containerKey = valueKey(emitter, container, site);
|
|
1483
1773
|
add(
|
|
1484
1774
|
emitter,
|
|
1485
1775
|
"readsKeyed",
|
|
1486
|
-
|
|
1487
|
-
|
|
1776
|
+
siteKey,
|
|
1777
|
+
containerKey,
|
|
1488
1778
|
valueKey(emitter, key, keyParent)
|
|
1489
1779
|
);
|
|
1780
|
+
add(emitter, "choosesEntry", siteKey, containerKey);
|
|
1490
1781
|
}
|
|
1491
1782
|
function emitKeyedElement(emitter, node, parent) {
|
|
1492
1783
|
const object = field(node, "object");
|
|
1493
1784
|
if (object === null || isWriteTarget(node, parent)) {
|
|
1494
1785
|
return;
|
|
1495
1786
|
}
|
|
1496
|
-
const index = node.
|
|
1497
|
-
(child) => child !== null && child.id !== object.id
|
|
1498
|
-
);
|
|
1787
|
+
const index = children(node).find((child) => child.id !== object.id);
|
|
1499
1788
|
if (index !== void 0) {
|
|
1500
1789
|
emitKeyedRead(emitter, node, object, index, node);
|
|
1501
1790
|
emitWrittenKeyRead(emitter, node, object, index);
|
|
@@ -1536,27 +1825,27 @@ function fallbackBranchesOf(node, type, enclosing) {
|
|
|
1536
1825
|
}
|
|
1537
1826
|
return [left, right].filter((side) => !raises(side, enclosing));
|
|
1538
1827
|
}
|
|
1539
|
-
var
|
|
1828
|
+
var RAISING_METHODS2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
1540
1829
|
function raises(written, enclosing) {
|
|
1541
1830
|
const node = readThrough(written);
|
|
1542
1831
|
if (node.type === "identifier") {
|
|
1543
|
-
return
|
|
1832
|
+
return RAISING_METHODS2.has(node.text) && isBareCall(node, enclosing);
|
|
1544
1833
|
}
|
|
1545
1834
|
if (node.type !== "call") {
|
|
1546
1835
|
return false;
|
|
1547
1836
|
}
|
|
1548
1837
|
const receiver = field(node, "receiver");
|
|
1549
|
-
return
|
|
1838
|
+
return RAISING_METHODS2.has(field(node, "method")?.text ?? "") && (receiver === null || receiver.text === "Kernel");
|
|
1550
1839
|
}
|
|
1551
1840
|
function emitExpressionFacts(emitter, node) {
|
|
1552
|
-
emitDynamicDefinition(emitter, node, node.type
|
|
1553
|
-
walkExpressions(node,
|
|
1841
|
+
emitDynamicDefinition(emitter, node, node.type);
|
|
1842
|
+
walkExpressions(node, (child, type, parent) => {
|
|
1554
1843
|
const site = callSiteAt(emitter, child, type, parent);
|
|
1555
|
-
emitExpressionFact(emitter, child, type, parent,
|
|
1844
|
+
emitExpressionFact(emitter, child, type, parent, site);
|
|
1556
1845
|
});
|
|
1557
1846
|
}
|
|
1558
|
-
function emitExpressionFact(emitter, child, type, parent,
|
|
1559
|
-
emitDynamicDefinition(emitter, child, type
|
|
1847
|
+
function emitExpressionFact(emitter, child, type, parent, site) {
|
|
1848
|
+
emitDynamicDefinition(emitter, child, type);
|
|
1560
1849
|
if (site !== null) {
|
|
1561
1850
|
emitCall(emitter, site);
|
|
1562
1851
|
emitKeyedFetch(emitter, site);
|
|
@@ -1597,6 +1886,11 @@ function emitExpressionFact(emitter, child, type, parent, turns, site) {
|
|
|
1597
1886
|
}
|
|
1598
1887
|
if (BLOCK_TYPES3.has(type)) {
|
|
1599
1888
|
emitBlockValue(emitter, child);
|
|
1889
|
+
emitLoopVariables(emitter, child, parent);
|
|
1890
|
+
}
|
|
1891
|
+
const pieces = TEMPLATE_PIECES[type]?.(emitter, child) ?? [];
|
|
1892
|
+
if (pieces.length > 0) {
|
|
1893
|
+
addTemplateRows(rowsOf(emitter), nodeId(emitter.filePath, child), pieces);
|
|
1600
1894
|
}
|
|
1601
1895
|
for (const branch of BRANCHES_OF[type]?.(child) ?? []) {
|
|
1602
1896
|
add(
|
|
@@ -1616,6 +1910,29 @@ function emitExpressionFact(emitter, child, type, parent, turns, site) {
|
|
|
1616
1910
|
emitParameterDefault(emitter, child);
|
|
1617
1911
|
}
|
|
1618
1912
|
}
|
|
1913
|
+
function interpolatedPieces(emitter, string) {
|
|
1914
|
+
const parts = children(string);
|
|
1915
|
+
if (!parts.some((part) => part.type === "interpolation")) {
|
|
1916
|
+
return [];
|
|
1917
|
+
}
|
|
1918
|
+
return parts.flatMap((part) => {
|
|
1919
|
+
if (part.type === "string_content") {
|
|
1920
|
+
return [{ text: part.text }];
|
|
1921
|
+
}
|
|
1922
|
+
const expression = part.type === "interpolation" ? children(part)[0] : void 0;
|
|
1923
|
+
return expression === void 0 ? [] : [{ hole: valueKey(emitter, expression, part) }];
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
function adjacentPieces(emitter, joined2) {
|
|
1927
|
+
return children(joined2).map((part) => ({
|
|
1928
|
+
hole: valueKey(emitter, part, joined2)
|
|
1929
|
+
}));
|
|
1930
|
+
}
|
|
1931
|
+
var TEMPLATE_PIECES = {
|
|
1932
|
+
string: interpolatedPieces,
|
|
1933
|
+
delimited_symbol: interpolatedPieces,
|
|
1934
|
+
chained_string: adjacentPieces
|
|
1935
|
+
};
|
|
1619
1936
|
function emitBlockArgument(emitter, argument) {
|
|
1620
1937
|
const [handed] = children(argument);
|
|
1621
1938
|
if (handed !== void 0) {
|
|
@@ -2038,11 +2355,10 @@ function emitMethodFacts(emitter, method) {
|
|
|
2038
2355
|
const collecting = { ...inside, heldRows: expressionFacts };
|
|
2039
2356
|
walkExpressions(
|
|
2040
2357
|
body,
|
|
2041
|
-
|
|
2042
|
-
(child, type, parent, turns) => {
|
|
2358
|
+
(child, type, parent) => {
|
|
2043
2359
|
const site = callSiteAt(inside, child, type, parent);
|
|
2044
2360
|
emitBodyFact(bodyFacts, inside, funcKey, child, type, site);
|
|
2045
|
-
emitExpressionFact(collecting, child, type, parent,
|
|
2361
|
+
emitExpressionFact(collecting, child, type, parent, site);
|
|
2046
2362
|
},
|
|
2047
2363
|
nested
|
|
2048
2364
|
);
|
|
@@ -2502,9 +2818,7 @@ var EXPRESSION_TYPES = {
|
|
|
2502
2818
|
),
|
|
2503
2819
|
unary: (node) => {
|
|
2504
2820
|
const operand2 = field(node, "operand");
|
|
2505
|
-
const operator = node.
|
|
2506
|
-
(child) => child !== null && !child.isNamed
|
|
2507
|
-
);
|
|
2821
|
+
const operator = allChildren(node).find((child) => !child.isNamed);
|
|
2508
2822
|
if (operand2 === null || operator === void 0) {
|
|
2509
2823
|
return OPAQUE;
|
|
2510
2824
|
}
|
|
@@ -2706,8 +3020,25 @@ function statementOf(node) {
|
|
|
2706
3020
|
if (appending !== null) {
|
|
2707
3021
|
return appending;
|
|
2708
3022
|
}
|
|
3023
|
+
if (isRaiseCall(node) && !rescuedInSameFunction(node)) {
|
|
3024
|
+
return { kind: "throw" };
|
|
3025
|
+
}
|
|
2709
3026
|
return isTailExpression(node) ? { kind: "return", value: node } : { kind: "expression", value: node };
|
|
2710
3027
|
}
|
|
3028
|
+
var RESCUING_TYPES = /* @__PURE__ */ new Set(["begin", "body_statement"]);
|
|
3029
|
+
var UNRESCUED_PARTS = /* @__PURE__ */ new Set(["rescue", "else", "ensure"]);
|
|
3030
|
+
function rescuedInSameFunction(node) {
|
|
3031
|
+
let child = node;
|
|
3032
|
+
let parent = node.parent;
|
|
3033
|
+
while (parent !== null && !isRoot(parent)) {
|
|
3034
|
+
if (RESCUING_TYPES.has(parent.type) && !UNRESCUED_PARTS.has(child.type) && children(parent).some((part) => part.type === "rescue")) {
|
|
3035
|
+
return true;
|
|
3036
|
+
}
|
|
3037
|
+
child = parent;
|
|
3038
|
+
parent = parent.parent;
|
|
3039
|
+
}
|
|
3040
|
+
return false;
|
|
3041
|
+
}
|
|
2711
3042
|
function appendingStatement(node) {
|
|
2712
3043
|
if (node.type !== "binary" || field(node, "operator")?.text !== "<<") {
|
|
2713
3044
|
return null;
|
|
@@ -3382,6 +3713,10 @@ function writtenNodeOf(node, db, site) {
|
|
|
3382
3713
|
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
3383
3714
|
return context === void 0 ? null : context.writtenTo(node, site);
|
|
3384
3715
|
}
|
|
3716
|
+
function calledDefinition(call, db) {
|
|
3717
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
3718
|
+
return context === void 0 ? null : context.callable(call);
|
|
3719
|
+
}
|
|
3385
3720
|
function askWrittenValues(nodes, db) {
|
|
3386
3721
|
if (db === void 0 || !contexts.has(db)) {
|
|
3387
3722
|
return;
|
|
@@ -3444,7 +3779,7 @@ function methodDefinitionsIn(file, root) {
|
|
|
3444
3779
|
if (METHOD_TYPES.has(node.type)) {
|
|
3445
3780
|
found.set(nodeId(file, node), node);
|
|
3446
3781
|
}
|
|
3447
|
-
for (const child of node
|
|
3782
|
+
for (const child of children(node)) {
|
|
3448
3783
|
if (child !== null) {
|
|
3449
3784
|
visit(child);
|
|
3450
3785
|
}
|
|
@@ -3507,7 +3842,7 @@ function contextOver(db, nodes) {
|
|
|
3507
3842
|
}
|
|
3508
3843
|
|
|
3509
3844
|
// src/values/literals.ts
|
|
3510
|
-
import { force as force2, literalOf as literalOf2 } from "@suss/values";
|
|
3845
|
+
import { force as force2, literalOf as literalOf2, sequence, text as text2 } from "@suss/values";
|
|
3511
3846
|
function literalElementsIn(value) {
|
|
3512
3847
|
if (value.kind !== "sequence") {
|
|
3513
3848
|
return null;
|
|
@@ -3522,8 +3857,24 @@ function literalElementsIn(value) {
|
|
|
3522
3857
|
}
|
|
3523
3858
|
return elements;
|
|
3524
3859
|
}
|
|
3525
|
-
function
|
|
3526
|
-
|
|
3860
|
+
function loopElementsOf(node, db) {
|
|
3861
|
+
const value = evaluatedValue(node, db);
|
|
3862
|
+
if (value.kind === "sequence") {
|
|
3863
|
+
return value.items.map((item) => elementOfList(force2(item.value)));
|
|
3864
|
+
}
|
|
3865
|
+
if (value.kind === "record" && !value.open) {
|
|
3866
|
+
return [...value.fields].map(([key, item]) => {
|
|
3867
|
+
const parts = [text2(key), force2(item.value)];
|
|
3868
|
+
return { whole: sequence(parts), parts };
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
return null;
|
|
3872
|
+
}
|
|
3873
|
+
function elementOfList(whole) {
|
|
3874
|
+
return {
|
|
3875
|
+
whole,
|
|
3876
|
+
parts: whole.kind === "sequence" ? whole.items.map((item) => force2(item.value)) : [whole]
|
|
3877
|
+
};
|
|
3527
3878
|
}
|
|
3528
3879
|
function namesOf(node, db) {
|
|
3529
3880
|
const value = evaluatedValue(node, db);
|
|
@@ -3545,13 +3896,16 @@ function definedNamesOf(dynamic, classKey) {
|
|
|
3545
3896
|
}
|
|
3546
3897
|
function readDynamicNames(db, rootsByFile) {
|
|
3547
3898
|
const byClass = /* @__PURE__ */ new Map();
|
|
3548
|
-
|
|
3899
|
+
const definitions = db.facts("definesMethodFrom");
|
|
3900
|
+
const loops = definitions.length === 0 ? /* @__PURE__ */ new Map() : loopsByFile(db);
|
|
3901
|
+
for (const [classKey, nameKey2] of definitions) {
|
|
3549
3902
|
const key = String(classKey);
|
|
3903
|
+
const around = loopsAround(loops, key, String(nameKey2));
|
|
3550
3904
|
byClass.set(
|
|
3551
3905
|
key,
|
|
3552
3906
|
foldReading(
|
|
3553
3907
|
byClass.get(key) ?? NOTHING,
|
|
3554
|
-
readOne(db, rootsByFile, String(nameKey2))
|
|
3908
|
+
readOne(db, rootsByFile, String(nameKey2), around)
|
|
3555
3909
|
)
|
|
3556
3910
|
);
|
|
3557
3911
|
}
|
|
@@ -3572,11 +3926,11 @@ function foldReading(soFar, reading) {
|
|
|
3572
3926
|
unreadable: soFar.unreadable
|
|
3573
3927
|
};
|
|
3574
3928
|
}
|
|
3575
|
-
function readOne(db, rootsByFile, nameKey2) {
|
|
3929
|
+
function readOne(db, rootsByFile, nameKey2, loops) {
|
|
3576
3930
|
const node = nodeOfKey2(rootsByFile, nameKey2);
|
|
3577
3931
|
const names = [];
|
|
3578
3932
|
const patterns = [];
|
|
3579
|
-
for (const bindings of loopTurns(db, rootsByFile,
|
|
3933
|
+
for (const bindings of loopTurns(db, rootsByFile, loops)) {
|
|
3580
3934
|
const value = node === null ? hole2(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
3581
3935
|
const name = literalOf3(value);
|
|
3582
3936
|
if (name !== null) {
|
|
@@ -3609,38 +3963,105 @@ function patternOf(value) {
|
|
|
3609
3963
|
}
|
|
3610
3964
|
return readSomething ? new RegExp(`${source}$`) : null;
|
|
3611
3965
|
}
|
|
3612
|
-
function escapeForPattern(
|
|
3613
|
-
return
|
|
3966
|
+
function escapeForPattern(text3) {
|
|
3967
|
+
return text3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3614
3968
|
}
|
|
3615
|
-
function loopTurns(db, rootsByFile,
|
|
3969
|
+
function loopTurns(db, rootsByFile, loops) {
|
|
3616
3970
|
let turns = [/* @__PURE__ */ new Map()];
|
|
3617
|
-
for (const
|
|
3971
|
+
for (const loop of loops) {
|
|
3972
|
+
const bindings = bindingsOf(db, rootsByFile, loop);
|
|
3973
|
+
if (bindings === null) {
|
|
3974
|
+
continue;
|
|
3975
|
+
}
|
|
3618
3976
|
turns = turns.flatMap(
|
|
3619
|
-
(base) =>
|
|
3620
|
-
const binding = new Map([...base, [over.element, value]]);
|
|
3621
|
-
if (over.index !== "") {
|
|
3622
|
-
binding.set(over.index, String(position));
|
|
3623
|
-
}
|
|
3624
|
-
return binding;
|
|
3625
|
-
})
|
|
3977
|
+
(base) => bindings.map((binding) => new Map([...base, ...binding]))
|
|
3626
3978
|
);
|
|
3627
3979
|
}
|
|
3628
3980
|
return turns;
|
|
3629
3981
|
}
|
|
3630
|
-
function
|
|
3631
|
-
const
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
)
|
|
3637
|
-
const
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3982
|
+
function bindingsOf(db, rootsByFile, loop) {
|
|
3983
|
+
const node = collectionNode(rootsByFile, loop);
|
|
3984
|
+
const elements = node === null ? null : loopElementsOf(node, db);
|
|
3985
|
+
if (elements === null) {
|
|
3986
|
+
return null;
|
|
3987
|
+
}
|
|
3988
|
+
const bound = [...loop.variables].flatMap(([name, takes]) => {
|
|
3989
|
+
const values = elements.map(
|
|
3990
|
+
(element, position) => GIVEN[takes.kind](element, position, takes)
|
|
3991
|
+
);
|
|
3992
|
+
return values.every((value) => value !== null) ? [{ name, values }] : [];
|
|
3993
|
+
});
|
|
3994
|
+
if (bound.length === 0) {
|
|
3995
|
+
return null;
|
|
3996
|
+
}
|
|
3997
|
+
return elements.map(
|
|
3998
|
+
(_, position) => new Map(bound.map(({ name, values }) => [name, values[position] ?? ""]))
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
var GIVEN = {
|
|
4002
|
+
element: (element) => literalOf3(element.whole),
|
|
4003
|
+
part: (element, _, takes) => {
|
|
4004
|
+
const part = takes.kind === "part" ? element.parts[takes.part] : void 0;
|
|
4005
|
+
return part === void 0 ? null : literalOf3(part);
|
|
4006
|
+
},
|
|
4007
|
+
position: (_, position) => String(position)
|
|
4008
|
+
};
|
|
4009
|
+
function collectionNode(rootsByFile, loop) {
|
|
4010
|
+
const written = nodeOfKey2(rootsByFile, loop.over);
|
|
4011
|
+
if (written !== null) {
|
|
4012
|
+
return written;
|
|
4013
|
+
}
|
|
4014
|
+
const call = nodeOfKey2(rootsByFile, loop.block.key)?.parent ?? null;
|
|
4015
|
+
return call === null ? null : field(call, "receiver");
|
|
4016
|
+
}
|
|
4017
|
+
function blockVariableOf(key) {
|
|
4018
|
+
const at = key.lastIndexOf("#");
|
|
4019
|
+
const span = at < 0 ? null : spanOfKey(key.slice(0, at));
|
|
4020
|
+
return span === null ? null : { block: { ...span, key: key.slice(0, at) }, name: key.slice(at + 1) };
|
|
4021
|
+
}
|
|
4022
|
+
function loopsByFile(db) {
|
|
4023
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
4024
|
+
const note = (key, over, takes) => {
|
|
4025
|
+
const variable = blockVariableOf(String(key));
|
|
4026
|
+
if (variable === null) {
|
|
4027
|
+
return;
|
|
3641
4028
|
}
|
|
4029
|
+
const known = blocks.get(variable.block.key);
|
|
4030
|
+
const loop = known ?? {
|
|
4031
|
+
block: variable.block,
|
|
4032
|
+
over: String(over),
|
|
4033
|
+
variables: /* @__PURE__ */ new Map()
|
|
4034
|
+
};
|
|
4035
|
+
loop.variables.set(variable.name, takes);
|
|
4036
|
+
blocks.set(variable.block.key, loop);
|
|
4037
|
+
};
|
|
4038
|
+
for (const [element, over] of db.facts("elementOf")) {
|
|
4039
|
+
note(element, over, { kind: "element" });
|
|
3642
4040
|
}
|
|
3643
|
-
|
|
4041
|
+
for (const [element, over, part] of db.facts("elementPartOf")) {
|
|
4042
|
+
note(element, over, { kind: "part", part: Number(part) });
|
|
4043
|
+
}
|
|
4044
|
+
for (const [position, element] of db.facts("positionOf")) {
|
|
4045
|
+
const block = blockVariableOf(String(element))?.block.key;
|
|
4046
|
+
if (block !== void 0 && blocks.has(block)) {
|
|
4047
|
+
note(position, blocks.get(block)?.over, { kind: "position" });
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
4051
|
+
for (const loop of blocks.values()) {
|
|
4052
|
+
byFile.set(loop.block.file, [...byFile.get(loop.block.file) ?? [], loop]);
|
|
4053
|
+
}
|
|
4054
|
+
return byFile;
|
|
4055
|
+
}
|
|
4056
|
+
function loopsAround(loops, classKey, nameKey2) {
|
|
4057
|
+
const cls = spanOfKey(classKey);
|
|
4058
|
+
const name = spanOfKey(nameKey2);
|
|
4059
|
+
if (cls === null || name === null) {
|
|
4060
|
+
return [];
|
|
4061
|
+
}
|
|
4062
|
+
return (loops.get(name.file) ?? []).filter(
|
|
4063
|
+
({ block }) => cls.start <= block.start && block.end <= cls.end && block.start <= name.start && name.end <= block.end
|
|
4064
|
+
).sort((a, b) => a.block.start - b.block.start);
|
|
3644
4065
|
}
|
|
3645
4066
|
|
|
3646
4067
|
// src/scope.ts
|
|
@@ -3770,12 +4191,27 @@ function graphqlTypeNameFromQualified(qualifiedName, convention) {
|
|
|
3770
4191
|
|
|
3771
4192
|
// src/ancestry.ts
|
|
3772
4193
|
async function reachDefinition(qualifiedName, lookup) {
|
|
4194
|
+
const segments = qualifiedName.split("::");
|
|
4195
|
+
for (let length = segments.length; length > 0; length--) {
|
|
4196
|
+
const found = await definedInFileOf(
|
|
4197
|
+
segments.slice(0, length).join("::"),
|
|
4198
|
+
qualifiedName,
|
|
4199
|
+
lookup
|
|
4200
|
+
);
|
|
4201
|
+
if (found !== null) {
|
|
4202
|
+
return found;
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
return null;
|
|
4206
|
+
}
|
|
4207
|
+
async function definedInFileOf(fileConstant, qualifiedName, lookup) {
|
|
3773
4208
|
const filePath = resolveConstantFile(
|
|
3774
4209
|
lookup.root,
|
|
3775
|
-
|
|
4210
|
+
fileConstant,
|
|
3776
4211
|
lookup.pathConvention,
|
|
3777
4212
|
lookup.acronyms,
|
|
3778
|
-
lookup.constantFiles
|
|
4213
|
+
lookup.constantFiles,
|
|
4214
|
+
lookup.autoloadRoots
|
|
3779
4215
|
);
|
|
3780
4216
|
if (filePath === null) {
|
|
3781
4217
|
return null;
|
|
@@ -3920,7 +4356,7 @@ function superclassCandidatesOf(blocks) {
|
|
|
3920
4356
|
return [];
|
|
3921
4357
|
}
|
|
3922
4358
|
function methodInAncestry(ancestry, name, read = {}) {
|
|
3923
|
-
for (const entry of ancestry) {
|
|
4359
|
+
for (const [index, entry] of ancestry.entries()) {
|
|
3924
4360
|
if (entry.type === "root") {
|
|
3925
4361
|
return { type: "none" };
|
|
3926
4362
|
}
|
|
@@ -3928,7 +4364,8 @@ function methodInAncestry(ancestry, name, read = {}) {
|
|
|
3928
4364
|
return {
|
|
3929
4365
|
type: "unsettled",
|
|
3930
4366
|
reason: `inherited from ${entry.name}, which this run did not read`,
|
|
3931
|
-
cause: "unreadAncestor"
|
|
4367
|
+
cause: "unreadAncestor",
|
|
4368
|
+
definedLater: definedInProjectAfter(ancestry, index, name, read)
|
|
3932
4369
|
};
|
|
3933
4370
|
}
|
|
3934
4371
|
const found = definitionIn(entry.blocks, name, read);
|
|
@@ -3945,6 +4382,18 @@ function methodInAncestry(ancestry, name, read = {}) {
|
|
|
3945
4382
|
}
|
|
3946
4383
|
return { type: "none" };
|
|
3947
4384
|
}
|
|
4385
|
+
function definedInProjectAfter(ancestry, index, name, read) {
|
|
4386
|
+
return ancestry.slice(index + 1).some(
|
|
4387
|
+
(entry) => entry.type !== "root" && entry.type !== "unfollowed" && definitionIn(entry.blocks, name, read).method !== null
|
|
4388
|
+
);
|
|
4389
|
+
}
|
|
4390
|
+
function methodPastUnreadAncestors(ancestry, name, read = {}) {
|
|
4391
|
+
return methodInAncestry(
|
|
4392
|
+
ancestry.filter((entry) => entry.type !== "unfollowed"),
|
|
4393
|
+
name,
|
|
4394
|
+
read
|
|
4395
|
+
);
|
|
4396
|
+
}
|
|
3948
4397
|
function definitionIn(blocks, name, read) {
|
|
3949
4398
|
let method = null;
|
|
3950
4399
|
let block = null;
|
|
@@ -3994,11 +4443,17 @@ import {
|
|
|
3994
4443
|
graphqlResolverBinding,
|
|
3995
4444
|
restBinding as restBinding2
|
|
3996
4445
|
} from "@suss/behavioral-ir";
|
|
3997
|
-
import { absentReading as
|
|
4446
|
+
import { absentReading as absentReading3, unreadableReading as unreadableReading2 } from "@suss/extractor";
|
|
3998
4447
|
|
|
3999
4448
|
// src/clientCalls.ts
|
|
4000
|
-
import {
|
|
4001
|
-
|
|
4449
|
+
import {
|
|
4450
|
+
hasNameHole,
|
|
4451
|
+
namesNothing,
|
|
4452
|
+
restBinding,
|
|
4453
|
+
statusMembersOf,
|
|
4454
|
+
testsStatus
|
|
4455
|
+
} from "@suss/behavioral-ir";
|
|
4456
|
+
import { hostOf, pathOf } from "@suss/values";
|
|
4002
4457
|
|
|
4003
4458
|
// src/paths/effects.ts
|
|
4004
4459
|
import { enumerateOrDegrade, sharedGatingConditions } from "@suss/extractor";
|
|
@@ -4025,7 +4480,7 @@ function isRaise(node) {
|
|
|
4025
4480
|
return false;
|
|
4026
4481
|
}
|
|
4027
4482
|
const receiver = field(node, "receiver");
|
|
4028
|
-
const method = field(node, "method") ?? node.
|
|
4483
|
+
const method = field(node, "method") ?? node.namedChild(0) ?? null;
|
|
4029
4484
|
return receiver === null && method !== null && RAISE_NAMES.has(method.text);
|
|
4030
4485
|
}
|
|
4031
4486
|
function exitOf(node) {
|
|
@@ -4037,7 +4492,7 @@ function exitOf(node) {
|
|
|
4037
4492
|
function exitKindOf(node) {
|
|
4038
4493
|
let sawReturn = false;
|
|
4039
4494
|
const raises2 = (current) => {
|
|
4040
|
-
for (const child of current
|
|
4495
|
+
for (const child of children(current)) {
|
|
4041
4496
|
if (child === null || CAPTURES_RETURN.has(child.type)) {
|
|
4042
4497
|
continue;
|
|
4043
4498
|
}
|
|
@@ -4071,13 +4526,52 @@ function callHeaderOf(call, block) {
|
|
|
4071
4526
|
function blockStatements(block) {
|
|
4072
4527
|
return block === null ? [] : children(block);
|
|
4073
4528
|
}
|
|
4529
|
+
function attachedBlockOf(call) {
|
|
4530
|
+
return children(call).find(
|
|
4531
|
+
(child) => child !== null && (child.type === "do_block" || child.type === "block")
|
|
4532
|
+
) ?? null;
|
|
4533
|
+
}
|
|
4534
|
+
function soleBlockParameter(block) {
|
|
4535
|
+
const names = children(field(block, "parameters")).filter(
|
|
4536
|
+
(child) => child.type === "identifier"
|
|
4537
|
+
);
|
|
4538
|
+
return names.length === 1 ? names[0].text : null;
|
|
4539
|
+
}
|
|
4540
|
+
function blockBodyStatements(block) {
|
|
4541
|
+
const body = field(block, "body");
|
|
4542
|
+
if (body !== null) {
|
|
4543
|
+
return children(body);
|
|
4544
|
+
}
|
|
4545
|
+
return children(block).filter((child) => child.type !== "block_parameters");
|
|
4546
|
+
}
|
|
4547
|
+
function armBody(block) {
|
|
4548
|
+
return field(block, "body") ?? block;
|
|
4549
|
+
}
|
|
4550
|
+
function formatArmOf(statement, parameter) {
|
|
4551
|
+
if (statement.type !== "call") {
|
|
4552
|
+
return null;
|
|
4553
|
+
}
|
|
4554
|
+
const receiver = field(statement, "receiver");
|
|
4555
|
+
const format = field(statement, "method")?.text;
|
|
4556
|
+
if (receiver?.text !== parameter || format === void 0) {
|
|
4557
|
+
return null;
|
|
4558
|
+
}
|
|
4559
|
+
const block = attachedBlockOf(statement);
|
|
4560
|
+
return {
|
|
4561
|
+
format,
|
|
4562
|
+
header: block === null ? statement.text : callHeaderOf(statement, block).sourceText,
|
|
4563
|
+
block
|
|
4564
|
+
};
|
|
4565
|
+
}
|
|
4074
4566
|
var Lowerer = class {
|
|
4075
|
-
constructor(terminals, responseCalls) {
|
|
4567
|
+
constructor(terminals, responseCalls, formatSwitch) {
|
|
4076
4568
|
this.terminals = terminals;
|
|
4077
4569
|
this.responseCalls = responseCalls;
|
|
4570
|
+
this.formatSwitch = formatSwitch;
|
|
4078
4571
|
}
|
|
4079
4572
|
terminals;
|
|
4080
4573
|
responseCalls;
|
|
4574
|
+
formatSwitch;
|
|
4081
4575
|
terminalsByStmt = /* @__PURE__ */ new Map();
|
|
4082
4576
|
terminalHome = new NodeMap();
|
|
4083
4577
|
attachTerminals(statement, found) {
|
|
@@ -4108,7 +4602,7 @@ var Lowerer = class {
|
|
|
4108
4602
|
found.push(own);
|
|
4109
4603
|
return found;
|
|
4110
4604
|
}
|
|
4111
|
-
for (const child of node
|
|
4605
|
+
for (const child of children(node)) {
|
|
4112
4606
|
if (child !== null && !OWN_BODY_TYPES.has(child.type)) {
|
|
4113
4607
|
this.responsesIn(child, found);
|
|
4114
4608
|
}
|
|
@@ -4167,29 +4661,22 @@ var Lowerer = class {
|
|
|
4167
4661
|
};
|
|
4168
4662
|
}
|
|
4169
4663
|
lowerBegin(node) {
|
|
4170
|
-
const
|
|
4171
|
-
|
|
4172
|
-
)
|
|
4173
|
-
const ensureClause = node.namedChildren.find(
|
|
4174
|
-
(child) => child !== null && child.type === "ensure"
|
|
4175
|
-
) ?? null;
|
|
4176
|
-
const tryBody = node.namedChildren.filter(
|
|
4664
|
+
const rescues2 = children(node).filter((child) => child.type === "rescue");
|
|
4665
|
+
const ensureClause = children(node).find((child) => child.type === "ensure") ?? null;
|
|
4666
|
+
const tryBody = children(node).filter(
|
|
4177
4667
|
(child) => child !== null && child.type !== "rescue" && child.type !== "ensure" && child.type !== "else"
|
|
4178
4668
|
);
|
|
4179
4669
|
return {
|
|
4180
4670
|
kind: "try",
|
|
4181
4671
|
tryBody: tryBody.map((statement) => this.lower(statement)),
|
|
4182
|
-
catchBody:
|
|
4672
|
+
catchBody: rescues2.length > 0 ? rescues2.flatMap((clause) => this.lowerBlock(field(clause, "body"))) : null,
|
|
4183
4673
|
finallyBody: ensureClause === null ? null : this.lowerBlock(ensureClause),
|
|
4184
4674
|
exitKind: exitKindOf(node)
|
|
4185
4675
|
};
|
|
4186
4676
|
}
|
|
4187
4677
|
lowerCase(node) {
|
|
4188
4678
|
const groups = [];
|
|
4189
|
-
for (const child of node
|
|
4190
|
-
if (child === null) {
|
|
4191
|
-
continue;
|
|
4192
|
-
}
|
|
4679
|
+
for (const child of children(node)) {
|
|
4193
4680
|
if (child.type === "when") {
|
|
4194
4681
|
groups.push({
|
|
4195
4682
|
condition: handleOf(field(child, "pattern")),
|
|
@@ -4207,6 +4694,47 @@ var Lowerer = class {
|
|
|
4207
4694
|
}
|
|
4208
4695
|
return { kind: "switch", groups, exitKind: exitKindOf(node) };
|
|
4209
4696
|
}
|
|
4697
|
+
/**
|
|
4698
|
+
* `respond_to do |format| ... end` runs one arm per request, picked by
|
|
4699
|
+
* the format the request accepts, so it lowers to a switch with an arm
|
|
4700
|
+
* per `format.x`. The arm that accepts every format is the default.
|
|
4701
|
+
* Without one, a request no arm accepts makes the library raise. Null
|
|
4702
|
+
* when the block contains anything besides those arms.
|
|
4703
|
+
*/
|
|
4704
|
+
lowerFormatSwitch(node, block) {
|
|
4705
|
+
const declared = this.formatSwitch;
|
|
4706
|
+
if (declared === void 0 || node.type !== "call" || field(node, "receiver") !== null || field(node, "method")?.text !== declared.name) {
|
|
4707
|
+
return null;
|
|
4708
|
+
}
|
|
4709
|
+
const parameter = soleBlockParameter(block);
|
|
4710
|
+
if (parameter === null) {
|
|
4711
|
+
return null;
|
|
4712
|
+
}
|
|
4713
|
+
const arms = blockBodyStatements(block).map(
|
|
4714
|
+
(statement) => formatArmOf(statement, parameter)
|
|
4715
|
+
);
|
|
4716
|
+
if (arms.length === 0 || arms.some((arm) => arm === null)) {
|
|
4717
|
+
return null;
|
|
4718
|
+
}
|
|
4719
|
+
const groups = arms.map(
|
|
4720
|
+
(arm) => ({
|
|
4721
|
+
condition: arm.format === declared.matchesEvery ? null : { sourceText: arm.header, expression: null },
|
|
4722
|
+
hasTrailingBreak: true,
|
|
4723
|
+
body: arm.block === null ? [] : this.lowerBlock(armBody(arm.block))
|
|
4724
|
+
})
|
|
4725
|
+
);
|
|
4726
|
+
const named2 = groups.filter((group) => group.condition !== null);
|
|
4727
|
+
const fallback2 = groups.find((group) => group.condition === null) ?? {
|
|
4728
|
+
condition: null,
|
|
4729
|
+
hasTrailingBreak: true,
|
|
4730
|
+
body: [{ kind: "exit", exit: "throw", exitKind: "throw" }]
|
|
4731
|
+
};
|
|
4732
|
+
return {
|
|
4733
|
+
kind: "switch",
|
|
4734
|
+
groups: [...named2, fallback2],
|
|
4735
|
+
exitKind: exitKindOf(node)
|
|
4736
|
+
};
|
|
4737
|
+
}
|
|
4210
4738
|
lower(node) {
|
|
4211
4739
|
const exit = exitOf(node);
|
|
4212
4740
|
if (exit !== null) {
|
|
@@ -4236,10 +4764,12 @@ var Lowerer = class {
|
|
|
4236
4764
|
[header]
|
|
4237
4765
|
);
|
|
4238
4766
|
}
|
|
4239
|
-
const attachedBlock = node
|
|
4240
|
-
|
|
4241
|
-
)
|
|
4242
|
-
|
|
4767
|
+
const attachedBlock = attachedBlockOf(node);
|
|
4768
|
+
const formatSwitch = attachedBlock === null ? null : this.lowerFormatSwitch(node, attachedBlock);
|
|
4769
|
+
if (formatSwitch !== null) {
|
|
4770
|
+
return this.attach(formatSwitch, [node]);
|
|
4771
|
+
}
|
|
4772
|
+
if (node.type === "call" && attachedBlock !== null) {
|
|
4243
4773
|
return this.attach(
|
|
4244
4774
|
{
|
|
4245
4775
|
kind: "loop",
|
|
@@ -4266,10 +4796,11 @@ var Lowerer = class {
|
|
|
4266
4796
|
return this.attach({ kind: "opaque", exitKind: exitKindOf(node) }, [node]);
|
|
4267
4797
|
}
|
|
4268
4798
|
};
|
|
4269
|
-
function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
4799
|
+
function lowerRubyBody(body, terminals, responseCalls = [], formatSwitch) {
|
|
4270
4800
|
const lowerer = new Lowerer(
|
|
4271
4801
|
new Map(terminals.map((terminal) => [terminal.id, terminal])),
|
|
4272
|
-
new NodeSet(responseCalls)
|
|
4802
|
+
new NodeSet(responseCalls),
|
|
4803
|
+
formatSwitch
|
|
4273
4804
|
);
|
|
4274
4805
|
return {
|
|
4275
4806
|
statements: lowerer.lowerBlock(body),
|
|
@@ -4289,9 +4820,9 @@ var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
|
4289
4820
|
]);
|
|
4290
4821
|
function literalArgOf(node, facts) {
|
|
4291
4822
|
const value = evaluatedValue(node, facts);
|
|
4292
|
-
const
|
|
4293
|
-
if (
|
|
4294
|
-
return { kind: "string", value:
|
|
4823
|
+
const text3 = literalOf4(value);
|
|
4824
|
+
if (text3 !== null) {
|
|
4825
|
+
return { kind: "string", value: text3 };
|
|
4295
4826
|
}
|
|
4296
4827
|
const constant2 = constantOf(value);
|
|
4297
4828
|
if (typeof constant2 === "number") {
|
|
@@ -4357,7 +4888,7 @@ function isCall(node, locals, inherited) {
|
|
|
4357
4888
|
}
|
|
4358
4889
|
function collectCalls(node, read, inherited, found) {
|
|
4359
4890
|
for (const child of children(node)) {
|
|
4360
|
-
if (read.stops.has(child.type)) {
|
|
4891
|
+
if (read.stops.has(child.type) && !isLambdaArgument(child)) {
|
|
4361
4892
|
continue;
|
|
4362
4893
|
}
|
|
4363
4894
|
if (isCall(child, read.locals, inherited)) {
|
|
@@ -4367,6 +4898,9 @@ function collectCalls(node, read, inherited, found) {
|
|
|
4367
4898
|
}
|
|
4368
4899
|
return found;
|
|
4369
4900
|
}
|
|
4901
|
+
function isLambdaArgument(node) {
|
|
4902
|
+
return node.type === LAMBDA_TYPE && node.parent?.type === "argument_list";
|
|
4903
|
+
}
|
|
4370
4904
|
function methodBody(definitionNode) {
|
|
4371
4905
|
const body = field(definitionNode, "body");
|
|
4372
4906
|
if (body === null) {
|
|
@@ -4430,6 +4964,14 @@ function enclosingStatement(call, body) {
|
|
|
4430
4964
|
return current;
|
|
4431
4965
|
}
|
|
4432
4966
|
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
4967
|
+
return invocationSites(
|
|
4968
|
+
definitionNode,
|
|
4969
|
+
inherited,
|
|
4970
|
+
keepsArglessCall,
|
|
4971
|
+
facts
|
|
4972
|
+
).map((site) => site.effect);
|
|
4973
|
+
}
|
|
4974
|
+
function invocationSites(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
4433
4975
|
const read = methodBody(definitionNode);
|
|
4434
4976
|
if (read === null) {
|
|
4435
4977
|
return [];
|
|
@@ -4442,7 +4984,7 @@ function moduleScopeInvocationEffects(root, inherited = NO_INHERITED_METHODS, ke
|
|
|
4442
4984
|
inherited,
|
|
4443
4985
|
keepsArglessCall,
|
|
4444
4986
|
facts
|
|
4445
|
-
);
|
|
4987
|
+
).map((site) => site.effect);
|
|
4446
4988
|
}
|
|
4447
4989
|
function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
4448
4990
|
const body = read.body;
|
|
@@ -4476,25 +5018,18 @@ function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
|
4476
5018
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
4477
5019
|
);
|
|
4478
5020
|
return {
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
5021
|
+
call,
|
|
5022
|
+
effect: {
|
|
5023
|
+
type: "invocation",
|
|
5024
|
+
callee: calleeText(call),
|
|
5025
|
+
args: argsOf(call, facts),
|
|
5026
|
+
async: false,
|
|
5027
|
+
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
5028
|
+
}
|
|
4484
5029
|
};
|
|
4485
5030
|
});
|
|
4486
5031
|
}
|
|
4487
5032
|
|
|
4488
|
-
// src/responseStatus.ts
|
|
4489
|
-
import {
|
|
4490
|
-
absentReading,
|
|
4491
|
-
enumerateOrDegrade as enumerateOrDegrade2,
|
|
4492
|
-
guardsHoldOn,
|
|
4493
|
-
unreadableReading,
|
|
4494
|
-
writtenReading
|
|
4495
|
-
} from "@suss/extractor";
|
|
4496
|
-
import { constantOf as constantOf3, literalOf as literalOf7 } from "@suss/values";
|
|
4497
|
-
|
|
4498
5033
|
// src/paths/predicates.ts
|
|
4499
5034
|
var OPERATORS = {
|
|
4500
5035
|
"==": "eq",
|
|
@@ -4513,11 +5048,11 @@ var opaqueOf = (node) => ({
|
|
|
4513
5048
|
var JOINING = /* @__PURE__ */ new Set(["&&", "||", "and", "or"]);
|
|
4514
5049
|
function predicateOf(node, inputs) {
|
|
4515
5050
|
if (node.type === "parenthesized_statements") {
|
|
4516
|
-
const inner = node.
|
|
5051
|
+
const inner = node.namedChild(0);
|
|
4517
5052
|
return inner == null ? opaqueOf(node) : predicateOf(inner, inputs);
|
|
4518
5053
|
}
|
|
4519
5054
|
if (node.type === "unary" && field(node, "operator")?.text === "!") {
|
|
4520
|
-
const operand2 = field(node, "operand") ?? node.
|
|
5055
|
+
const operand2 = field(node, "operand") ?? node.namedChild(0);
|
|
4521
5056
|
return operand2 == null ? opaqueOf(node) : { type: "negation", operand: predicateOf(operand2, inputs) };
|
|
4522
5057
|
}
|
|
4523
5058
|
if (node.type === "binary") {
|
|
@@ -4543,11 +5078,11 @@ function predicateOf(node, inputs) {
|
|
|
4543
5078
|
}
|
|
4544
5079
|
function operandsOf(node) {
|
|
4545
5080
|
if (node.type === "parenthesized_statements") {
|
|
4546
|
-
const inner = node.
|
|
5081
|
+
const inner = node.namedChild(0);
|
|
4547
5082
|
return inner == null ? [] : operandsOf(inner);
|
|
4548
5083
|
}
|
|
4549
5084
|
if (node.type === "unary" && field(node, "operator")?.text === "!") {
|
|
4550
|
-
const operand2 = field(node, "operand") ?? node.
|
|
5085
|
+
const operand2 = field(node, "operand") ?? node.namedChild(0);
|
|
4551
5086
|
return operand2 == null ? [] : operandsOf(operand2);
|
|
4552
5087
|
}
|
|
4553
5088
|
if (node.type === "binary") {
|
|
@@ -4643,6 +5178,321 @@ function calledMethod(node) {
|
|
|
4643
5178
|
return field(node, "method")?.text ?? null;
|
|
4644
5179
|
}
|
|
4645
5180
|
|
|
5181
|
+
// src/responseHandOff.ts
|
|
5182
|
+
import { helperPathsOnVariables, splitAtHandOffs } from "@suss/extractor";
|
|
5183
|
+
|
|
5184
|
+
// src/exceptionClasses.ts
|
|
5185
|
+
var ROOT_EXCEPTION = "Exception";
|
|
5186
|
+
var RUBY_EXCEPTION_PARENTS = {
|
|
5187
|
+
NoMemoryError: ROOT_EXCEPTION,
|
|
5188
|
+
ScriptError: ROOT_EXCEPTION,
|
|
5189
|
+
LoadError: "ScriptError",
|
|
5190
|
+
NotImplementedError: "ScriptError",
|
|
5191
|
+
SyntaxError: "ScriptError",
|
|
5192
|
+
SecurityError: ROOT_EXCEPTION,
|
|
5193
|
+
SignalException: ROOT_EXCEPTION,
|
|
5194
|
+
Interrupt: "SignalException",
|
|
5195
|
+
StandardError: ROOT_EXCEPTION,
|
|
5196
|
+
SystemExit: ROOT_EXCEPTION,
|
|
5197
|
+
SystemStackError: ROOT_EXCEPTION,
|
|
5198
|
+
ArgumentError: "StandardError",
|
|
5199
|
+
UncaughtThrowError: "ArgumentError",
|
|
5200
|
+
EncodingError: "StandardError",
|
|
5201
|
+
FiberError: "StandardError",
|
|
5202
|
+
IOError: "StandardError",
|
|
5203
|
+
EOFError: "IOError",
|
|
5204
|
+
IndexError: "StandardError",
|
|
5205
|
+
KeyError: "IndexError",
|
|
5206
|
+
StopIteration: "IndexError",
|
|
5207
|
+
ClosedQueueError: "StopIteration",
|
|
5208
|
+
LocalJumpError: "StandardError",
|
|
5209
|
+
NameError: "StandardError",
|
|
5210
|
+
NoMethodError: "NameError",
|
|
5211
|
+
RangeError: "StandardError",
|
|
5212
|
+
FloatDomainError: "RangeError",
|
|
5213
|
+
RegexpError: "StandardError",
|
|
5214
|
+
RuntimeError: "StandardError",
|
|
5215
|
+
FrozenError: "RuntimeError",
|
|
5216
|
+
SystemCallError: "StandardError",
|
|
5217
|
+
ThreadError: "StandardError",
|
|
5218
|
+
TypeError: "StandardError",
|
|
5219
|
+
ZeroDivisionError: "StandardError"
|
|
5220
|
+
};
|
|
5221
|
+
var MESSAGE_ONLY_EXCEPTION = "RuntimeError";
|
|
5222
|
+
var ExceptionReader = class {
|
|
5223
|
+
constructor(lookup, library) {
|
|
5224
|
+
this.library = library;
|
|
5225
|
+
this.walking = {
|
|
5226
|
+
...lookup,
|
|
5227
|
+
ancestryRootClassNames: [
|
|
5228
|
+
...Object.keys(library),
|
|
5229
|
+
...Object.keys(RUBY_EXCEPTION_PARENTS),
|
|
5230
|
+
ROOT_EXCEPTION
|
|
5231
|
+
]
|
|
5232
|
+
};
|
|
5233
|
+
}
|
|
5234
|
+
library;
|
|
5235
|
+
walking;
|
|
5236
|
+
known = /* @__PURE__ */ new Map();
|
|
5237
|
+
read(ref) {
|
|
5238
|
+
const key = [ref.text, ...ref.candidates].join("\0");
|
|
5239
|
+
let found = this.known.get(key);
|
|
5240
|
+
if (found === void 0) {
|
|
5241
|
+
found = readException(ref, this.walking, this.library);
|
|
5242
|
+
this.known.set(key, found);
|
|
5243
|
+
}
|
|
5244
|
+
return found;
|
|
5245
|
+
}
|
|
5246
|
+
};
|
|
5247
|
+
function listedException(name, library) {
|
|
5248
|
+
const fromLibrary = library[name];
|
|
5249
|
+
if (fromLibrary !== void 0) {
|
|
5250
|
+
return {
|
|
5251
|
+
name,
|
|
5252
|
+
ancestors: fromLibrary.ancestors,
|
|
5253
|
+
incomplete: false,
|
|
5254
|
+
inheritableByUnread: false
|
|
5255
|
+
};
|
|
5256
|
+
}
|
|
5257
|
+
const fromRuby = rubyAncestors(name);
|
|
5258
|
+
return fromRuby === null ? null : {
|
|
5259
|
+
name,
|
|
5260
|
+
ancestors: fromRuby,
|
|
5261
|
+
incomplete: false,
|
|
5262
|
+
inheritableByUnread: true
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
function unreadException(name) {
|
|
5266
|
+
return {
|
|
5267
|
+
name,
|
|
5268
|
+
ancestors: [ROOT_EXCEPTION],
|
|
5269
|
+
incomplete: true,
|
|
5270
|
+
inheritableByUnread: true
|
|
5271
|
+
};
|
|
5272
|
+
}
|
|
5273
|
+
function classesOf(exception) {
|
|
5274
|
+
return [exception.name, ...exception.ancestors];
|
|
5275
|
+
}
|
|
5276
|
+
async function readException(ref, lookup, library) {
|
|
5277
|
+
for (const candidate of ref.candidates) {
|
|
5278
|
+
const known = listedException(candidate, library);
|
|
5279
|
+
if (known !== null) {
|
|
5280
|
+
return known;
|
|
5281
|
+
}
|
|
5282
|
+
const defined = await reachConstant([candidate], lookup);
|
|
5283
|
+
if (defined !== null) {
|
|
5284
|
+
return projectException(defined, lookup, library);
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
return unreadException(ref.candidates.at(-1) ?? ref.text);
|
|
5288
|
+
}
|
|
5289
|
+
async function projectException(defined, lookup, library) {
|
|
5290
|
+
const chain = await ancestryOf(defined.name, defined.blocks, lookup);
|
|
5291
|
+
const ancestors = [];
|
|
5292
|
+
let incomplete = false;
|
|
5293
|
+
for (const entry of chain) {
|
|
5294
|
+
if (entry.type === "bodies" && entry.name === defined.name) {
|
|
5295
|
+
continue;
|
|
5296
|
+
}
|
|
5297
|
+
ancestors.push(entry.name);
|
|
5298
|
+
if (entry.type === "root") {
|
|
5299
|
+
ancestors.push(
|
|
5300
|
+
...listedException(entry.name, library)?.ancestors ?? []
|
|
5301
|
+
);
|
|
5302
|
+
}
|
|
5303
|
+
if (entry.type === "unfollowed") {
|
|
5304
|
+
incomplete = true;
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
if (incomplete && !ancestors.includes(ROOT_EXCEPTION)) {
|
|
5308
|
+
ancestors.push(ROOT_EXCEPTION);
|
|
5309
|
+
}
|
|
5310
|
+
return {
|
|
5311
|
+
name: defined.name,
|
|
5312
|
+
ancestors,
|
|
5313
|
+
incomplete,
|
|
5314
|
+
inheritableByUnread: false
|
|
5315
|
+
};
|
|
5316
|
+
}
|
|
5317
|
+
function rubyAncestors(name) {
|
|
5318
|
+
if (name === ROOT_EXCEPTION) {
|
|
5319
|
+
return [];
|
|
5320
|
+
}
|
|
5321
|
+
if (RUBY_EXCEPTION_PARENTS[name] === void 0) {
|
|
5322
|
+
return null;
|
|
5323
|
+
}
|
|
5324
|
+
const ancestors = [];
|
|
5325
|
+
for (let parent = RUBY_EXCEPTION_PARENTS[name]; parent !== void 0; parent = RUBY_EXCEPTION_PARENTS[parent]) {
|
|
5326
|
+
ancestors.push(parent);
|
|
5327
|
+
}
|
|
5328
|
+
return ancestors;
|
|
5329
|
+
}
|
|
5330
|
+
|
|
5331
|
+
// src/raises.ts
|
|
5332
|
+
var RESCUING_BODIES = /* @__PURE__ */ new Set(["begin", "body_statement"]);
|
|
5333
|
+
var AFTER_CATCH = /* @__PURE__ */ new Set(["rescue", "else", "ensure"]);
|
|
5334
|
+
function escapingRaises(method) {
|
|
5335
|
+
const body = field(method, "body");
|
|
5336
|
+
if (body === null) {
|
|
5337
|
+
return [];
|
|
5338
|
+
}
|
|
5339
|
+
const found = [];
|
|
5340
|
+
const visit = (node) => {
|
|
5341
|
+
for (const child of children(node)) {
|
|
5342
|
+
if (child === null || OWN_BODY_TYPES.has(child.type)) {
|
|
5343
|
+
continue;
|
|
5344
|
+
}
|
|
5345
|
+
if (isRaise(child) && !caughtLocally(child, method)) {
|
|
5346
|
+
found.push(child);
|
|
5347
|
+
continue;
|
|
5348
|
+
}
|
|
5349
|
+
visit(child);
|
|
5350
|
+
}
|
|
5351
|
+
};
|
|
5352
|
+
visit(body);
|
|
5353
|
+
return found;
|
|
5354
|
+
}
|
|
5355
|
+
function caughtLocally(raise, method) {
|
|
5356
|
+
let child = raise;
|
|
5357
|
+
for (let parent = raise.parent; parent !== null && parent.id !== method.id; parent = parent.parent) {
|
|
5358
|
+
if (rescues(parent, child)) {
|
|
5359
|
+
return true;
|
|
5360
|
+
}
|
|
5361
|
+
child = parent;
|
|
5362
|
+
}
|
|
5363
|
+
return false;
|
|
5364
|
+
}
|
|
5365
|
+
function rescues(parent, child) {
|
|
5366
|
+
if (parent.type === "rescue_modifier") {
|
|
5367
|
+
return field(parent, "body")?.id === child.id;
|
|
5368
|
+
}
|
|
5369
|
+
return RESCUING_BODIES.has(parent.type) && !AFTER_CATCH.has(child.type) && children(parent).some((one) => one?.type === "rescue");
|
|
5370
|
+
}
|
|
5371
|
+
function raisedClassRef(call, nesting) {
|
|
5372
|
+
const first = readCallArgs(field(call, "arguments")).positional[0];
|
|
5373
|
+
if (first === void 0) {
|
|
5374
|
+
return null;
|
|
5375
|
+
}
|
|
5376
|
+
if (first.type === "string") {
|
|
5377
|
+
return {
|
|
5378
|
+
text: MESSAGE_ONLY_EXCEPTION,
|
|
5379
|
+
candidates: [MESSAGE_ONLY_EXCEPTION]
|
|
5380
|
+
};
|
|
5381
|
+
}
|
|
5382
|
+
const constant2 = constructedClass(first) ?? first;
|
|
5383
|
+
const candidates = constantRefCandidates(constant2, nesting);
|
|
5384
|
+
return candidates.length === 0 ? null : { text: constant2.text, candidates };
|
|
5385
|
+
}
|
|
5386
|
+
function constructedClass(node) {
|
|
5387
|
+
if (node.type !== "call" || field(node, "method")?.text !== "new") {
|
|
5388
|
+
return null;
|
|
5389
|
+
}
|
|
5390
|
+
return field(node, "receiver");
|
|
5391
|
+
}
|
|
5392
|
+
|
|
5393
|
+
// src/responseHandOff.ts
|
|
5394
|
+
function raiseEndings(method) {
|
|
5395
|
+
return escapingRaises(method).map((call) => ({
|
|
5396
|
+
call,
|
|
5397
|
+
terminal: raiseTerminal(call)
|
|
5398
|
+
}));
|
|
5399
|
+
}
|
|
5400
|
+
function raiseTerminal(call) {
|
|
5401
|
+
const [first, second] = readCallArgs(field(call, "arguments")).positional;
|
|
5402
|
+
const message = first === void 0 ? null : stringLiteralValue(first);
|
|
5403
|
+
return {
|
|
5404
|
+
kind: "throw",
|
|
5405
|
+
statusCode: null,
|
|
5406
|
+
body: null,
|
|
5407
|
+
exceptionType: raisedClassRef(call, [])?.text ?? null,
|
|
5408
|
+
message: message ?? (second === void 0 ? null : stringLiteralValue(second)),
|
|
5409
|
+
component: null,
|
|
5410
|
+
renderTree: null,
|
|
5411
|
+
delegateTarget: null,
|
|
5412
|
+
emitEvent: null,
|
|
5413
|
+
location: rangeOf(call)
|
|
5414
|
+
};
|
|
5415
|
+
}
|
|
5416
|
+
function throughResponseHelpers(branches, sites, options) {
|
|
5417
|
+
const handOffs = [];
|
|
5418
|
+
for (const { call, effect } of sites) {
|
|
5419
|
+
const helper = helperBranchesAt(call, options);
|
|
5420
|
+
if (helper !== null) {
|
|
5421
|
+
handOffs.push({
|
|
5422
|
+
helper,
|
|
5423
|
+
preconditions: effect.preconditions,
|
|
5424
|
+
line: rangeOf(call).start,
|
|
5425
|
+
at: rangeOf(call)
|
|
5426
|
+
});
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
return handOffs.length === 0 ? [...branches] : branches.flatMap((branch) => splitAtHandOffs(branch, handOffs));
|
|
5430
|
+
}
|
|
5431
|
+
function helperBranchesAt(call, options) {
|
|
5432
|
+
if (call.type !== "call") {
|
|
5433
|
+
return null;
|
|
5434
|
+
}
|
|
5435
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
5436
|
+
const handedAt = args.positional.flatMap(
|
|
5437
|
+
(argument, position) => argument.type === "identifier" && refersToResponse(argument, options) ? [{ position, variable: argument.text }] : []
|
|
5438
|
+
);
|
|
5439
|
+
const handedAs = Object.entries(args.keyword).flatMap(
|
|
5440
|
+
([name, argument]) => argument.type === "identifier" && refersToResponse(argument, options) ? [{ name, variable: argument.text }] : []
|
|
5441
|
+
);
|
|
5442
|
+
if (handedAt.length === 0 && handedAs.length === 0) {
|
|
5443
|
+
return null;
|
|
5444
|
+
}
|
|
5445
|
+
const helper = calledDefinition(call, options.facts);
|
|
5446
|
+
if (helper === null) {
|
|
5447
|
+
return null;
|
|
5448
|
+
}
|
|
5449
|
+
const passed = /* @__PURE__ */ new Map();
|
|
5450
|
+
const names = parametersOf(helper).map(
|
|
5451
|
+
(parameter) => paramNameOf(parameter)?.text ?? null
|
|
5452
|
+
);
|
|
5453
|
+
for (const { position, variable } of handedAt) {
|
|
5454
|
+
const name = names[position];
|
|
5455
|
+
if (name !== void 0 && name !== null) {
|
|
5456
|
+
passed.set(name, variable);
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
for (const { name, variable } of handedAs) {
|
|
5460
|
+
if (names.includes(name)) {
|
|
5461
|
+
passed.set(name, variable);
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
return passed.size === 0 ? null : helperPathsOnVariables(options.readHelper(helper), passed);
|
|
5465
|
+
}
|
|
5466
|
+
function refersToResponse(name, options) {
|
|
5467
|
+
const written = writtenNodeOf(name, options.facts);
|
|
5468
|
+
if (written === null) {
|
|
5469
|
+
return false;
|
|
5470
|
+
}
|
|
5471
|
+
if (sameNode(written, options.request)) {
|
|
5472
|
+
return true;
|
|
5473
|
+
}
|
|
5474
|
+
const member = written.type === "call" ? field(written, "method") : null;
|
|
5475
|
+
const receiver = written.type === "call" ? field(written, "receiver") : null;
|
|
5476
|
+
if (member === null || receiver?.type !== "identifier" || !options.bodyMethods.includes(member.text)) {
|
|
5477
|
+
return false;
|
|
5478
|
+
}
|
|
5479
|
+
const parsedFrom = writtenNodeOf(receiver, options.facts);
|
|
5480
|
+
return parsedFrom !== null && sameNode(parsedFrom, options.request);
|
|
5481
|
+
}
|
|
5482
|
+
function sameNode(a, b) {
|
|
5483
|
+
return a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.tree === b.tree;
|
|
5484
|
+
}
|
|
5485
|
+
|
|
5486
|
+
// src/responseStatus.ts
|
|
5487
|
+
import {
|
|
5488
|
+
absentReading as absentReading2,
|
|
5489
|
+
enumerateOrDegrade as enumerateOrDegrade2,
|
|
5490
|
+
guardsHoldOn,
|
|
5491
|
+
unreadableReading,
|
|
5492
|
+
writtenReading as writtenReading2
|
|
5493
|
+
} from "@suss/extractor";
|
|
5494
|
+
import { constantOf as constantOf3, literalOf as literalOf7 } from "@suss/values";
|
|
5495
|
+
|
|
4646
5496
|
// src/provenance.ts
|
|
4647
5497
|
import { sourceRefsOf } from "@suss/extractor";
|
|
4648
5498
|
import { constantOf as constantOf2, literalOf as literalOf6 } from "@suss/values";
|
|
@@ -4710,7 +5560,7 @@ function pickedSource(call, loader) {
|
|
|
4710
5560
|
return null;
|
|
4711
5561
|
}
|
|
4712
5562
|
const args = field(pick, "arguments");
|
|
4713
|
-
const given = args === null ? void 0 : args.
|
|
5563
|
+
const given = args === null ? void 0 : args.namedChild(source.at);
|
|
4714
5564
|
return given?.type === "constant" || given?.type === "scope_resolution" ? given : null;
|
|
4715
5565
|
}
|
|
4716
5566
|
|
|
@@ -5355,10 +6205,7 @@ function recognizeMethodStorage(method, file, options) {
|
|
|
5355
6205
|
return { effects, slots };
|
|
5356
6206
|
}
|
|
5357
6207
|
function callsUnder(node, found = []) {
|
|
5358
|
-
for (const child of node
|
|
5359
|
-
if (child === null) {
|
|
5360
|
-
continue;
|
|
5361
|
-
}
|
|
6208
|
+
for (const child of children(node)) {
|
|
5362
6209
|
if (child.type === "call") {
|
|
5363
6210
|
found.push(child);
|
|
5364
6211
|
}
|
|
@@ -5566,6 +6413,111 @@ function literalAt(key, facts) {
|
|
|
5566
6413
|
return constant2 !== void 0 ? constant2 : literalOf6(value) ?? void 0;
|
|
5567
6414
|
}
|
|
5568
6415
|
|
|
6416
|
+
// src/raisedStatuses.ts
|
|
6417
|
+
function rescuedBy(handlers, exception) {
|
|
6418
|
+
if (handlers === "eachRoute") {
|
|
6419
|
+
return true;
|
|
6420
|
+
}
|
|
6421
|
+
const classes = new Set(classesOf(exception));
|
|
6422
|
+
return handlers.some(
|
|
6423
|
+
(caught) => caught.someUnread || caught.classes.some(
|
|
6424
|
+
(one) => classes.has(one.name) || exception.incomplete && one.inheritableByUnread
|
|
6425
|
+
)
|
|
6426
|
+
);
|
|
6427
|
+
}
|
|
6428
|
+
function raisedTerminal(pattern, exception, handlers, location) {
|
|
6429
|
+
const libraryStatus = exception === null ? void 0 : pattern.libraryExceptions?.[exception.name]?.status;
|
|
6430
|
+
const status = exception === null || rescuedBy(handlers, exception) ? void 0 : libraryStatus;
|
|
6431
|
+
return {
|
|
6432
|
+
kind: status === void 0 ? "throw" : "response",
|
|
6433
|
+
statusCode: status === void 0 ? null : { type: "literal", value: status },
|
|
6434
|
+
body: null,
|
|
6435
|
+
exceptionType: status === void 0 ? exception?.name ?? null : null,
|
|
6436
|
+
...status === void 0 && exception !== null ? {
|
|
6437
|
+
exceptionAncestry: {
|
|
6438
|
+
ancestors: exception.ancestors,
|
|
6439
|
+
incomplete: exception.incomplete
|
|
6440
|
+
}
|
|
6441
|
+
} : {},
|
|
6442
|
+
...status === void 0 && libraryStatus !== void 0 ? { statusWhenUncaught: libraryStatus } : {},
|
|
6443
|
+
message: null,
|
|
6444
|
+
component: null,
|
|
6445
|
+
renderTree: null,
|
|
6446
|
+
delegateTarget: null,
|
|
6447
|
+
emitEvent: null,
|
|
6448
|
+
location
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
function raisedStatusBranches(pattern, storage, effects, handlers, location) {
|
|
6452
|
+
const operations = new Set(
|
|
6453
|
+
(effects ?? []).flatMap(
|
|
6454
|
+
(effect) => effect.type === "interaction" && effect.interaction.class === "storage-access" && effect.interaction.operation !== void 0 ? [effect.interaction.operation] : []
|
|
6455
|
+
)
|
|
6456
|
+
);
|
|
6457
|
+
const raised = new Set(
|
|
6458
|
+
storage.flatMap(
|
|
6459
|
+
(one) => (one.raises ?? []).flatMap(
|
|
6460
|
+
(raise) => raise.methods.some((method) => operations.has(method)) ? [raise.exception] : []
|
|
6461
|
+
)
|
|
6462
|
+
)
|
|
6463
|
+
);
|
|
6464
|
+
return [...raised].flatMap((name) => {
|
|
6465
|
+
const exception = listedException(name, pattern.libraryExceptions ?? {}) ?? unreadException(name);
|
|
6466
|
+
const terminal = raisedTerminal(pattern, exception, handlers, location);
|
|
6467
|
+
if (terminal.kind === "throw" && !rescuedBy(handlers, exception)) {
|
|
6468
|
+
return [];
|
|
6469
|
+
}
|
|
6470
|
+
return [
|
|
6471
|
+
{
|
|
6472
|
+
conditions: [
|
|
6473
|
+
{
|
|
6474
|
+
sourceText: `${name} raised`,
|
|
6475
|
+
structured: null,
|
|
6476
|
+
polarity: "positive",
|
|
6477
|
+
source: "earlyThrow"
|
|
6478
|
+
}
|
|
6479
|
+
],
|
|
6480
|
+
terminal,
|
|
6481
|
+
effects: [],
|
|
6482
|
+
location,
|
|
6483
|
+
isDefault: false
|
|
6484
|
+
}
|
|
6485
|
+
];
|
|
6486
|
+
});
|
|
6487
|
+
}
|
|
6488
|
+
|
|
6489
|
+
// src/responseBody.ts
|
|
6490
|
+
import { absentReading, writtenReading } from "@suss/extractor";
|
|
6491
|
+
import { shapeOfValue } from "@suss/values";
|
|
6492
|
+
var BODY_KINDS = /* @__PURE__ */ new Set([
|
|
6493
|
+
"record",
|
|
6494
|
+
"sequence",
|
|
6495
|
+
"unbounded"
|
|
6496
|
+
]);
|
|
6497
|
+
function serializedValue(node, serializers) {
|
|
6498
|
+
const receiver = field(node, "receiver");
|
|
6499
|
+
const method = field(node, "method")?.text;
|
|
6500
|
+
if (node.type !== "call" || receiver === null || method === void 0 || !serializers.has(method) || field(node, "arguments") !== null) {
|
|
6501
|
+
return node;
|
|
6502
|
+
}
|
|
6503
|
+
return serializedValue(receiver, serializers);
|
|
6504
|
+
}
|
|
6505
|
+
function bodyReadingOfCall(call, declaration, facts) {
|
|
6506
|
+
if (declaration.bodyKeyword === void 0 || call.type !== "call") {
|
|
6507
|
+
return absentReading;
|
|
6508
|
+
}
|
|
6509
|
+
const argument = readCallArgs(field(call, "arguments")).keyword[declaration.bodyKeyword];
|
|
6510
|
+
if (argument === void 0) {
|
|
6511
|
+
return absentReading;
|
|
6512
|
+
}
|
|
6513
|
+
const body = serializedValue(
|
|
6514
|
+
argument,
|
|
6515
|
+
new Set(declaration.bodySerializers ?? [])
|
|
6516
|
+
);
|
|
6517
|
+
const value = evaluatedValue(body, facts);
|
|
6518
|
+
return BODY_KINDS.has(value.kind) ? writtenReading(shapeOfValue(value), rangeOf(argument)) : absentReading;
|
|
6519
|
+
}
|
|
6520
|
+
|
|
5569
6521
|
// src/responseStatus.ts
|
|
5570
6522
|
function statusArgumentOf(call, declaration) {
|
|
5571
6523
|
const args = readCallArgs(field(call, "arguments"));
|
|
@@ -5578,7 +6530,11 @@ function statusArgumentOf(call, declaration) {
|
|
|
5578
6530
|
}
|
|
5579
6531
|
return args.positional[declaration.statusArgument] ?? null;
|
|
5580
6532
|
}
|
|
5581
|
-
function statusNumberOf(node, names, facts) {
|
|
6533
|
+
function statusNumberOf(node, names, facts, bound = /* @__PURE__ */ new Map()) {
|
|
6534
|
+
const passed = node.type === "identifier" ? bound.get(node.text) : void 0;
|
|
6535
|
+
if (passed !== void 0) {
|
|
6536
|
+
return passed;
|
|
6537
|
+
}
|
|
5582
6538
|
const value = evaluatedValue(node, facts);
|
|
5583
6539
|
const constant2 = constantOf3(value);
|
|
5584
6540
|
if (typeof constant2 === "number") {
|
|
@@ -5602,21 +6558,42 @@ function declarationOf(node, byName, locals) {
|
|
|
5602
6558
|
const name = field(node, "method")?.text;
|
|
5603
6559
|
return name === void 0 ? void 0 : byName.get(name);
|
|
5604
6560
|
}
|
|
5605
|
-
function
|
|
5606
|
-
|
|
6561
|
+
function responderOf(node, responders) {
|
|
6562
|
+
const declaration = declarationOf(node, responders.byName, responders.locals);
|
|
6563
|
+
if (declaration !== void 0) {
|
|
6564
|
+
return { kind: "declared", declaration };
|
|
6565
|
+
}
|
|
6566
|
+
const name = receiverlessName(node, responders.locals);
|
|
6567
|
+
const reading = name === null || responders.helper === void 0 ? null : responders.helper(name, node);
|
|
6568
|
+
return reading === null ? void 0 : { kind: "helper", reading };
|
|
6569
|
+
}
|
|
6570
|
+
function receiverlessName(node, locals) {
|
|
6571
|
+
if (node.type === "identifier") {
|
|
6572
|
+
return isBareMethodCall(node, locals) ? node.text : null;
|
|
6573
|
+
}
|
|
6574
|
+
if (node.type !== "call" || field(node, "receiver") !== null) {
|
|
6575
|
+
return null;
|
|
6576
|
+
}
|
|
6577
|
+
return field(node, "method")?.text ?? null;
|
|
6578
|
+
}
|
|
6579
|
+
function readingOfResponder(call, responder, names, options) {
|
|
6580
|
+
return responder.kind === "helper" ? responder.reading : readingOfCall(call, responder.declaration, names, options);
|
|
6581
|
+
}
|
|
6582
|
+
function collectResponseCalls(node, responders, found) {
|
|
6583
|
+
for (const child of children(node)) {
|
|
5607
6584
|
if (child === null || OWN_BODY_TYPES.has(child.type)) {
|
|
5608
6585
|
continue;
|
|
5609
6586
|
}
|
|
5610
|
-
if (
|
|
6587
|
+
if (responderOf(child, responders) !== void 0) {
|
|
5611
6588
|
found.push(child);
|
|
5612
6589
|
continue;
|
|
5613
6590
|
}
|
|
5614
|
-
collectResponseCalls(child,
|
|
6591
|
+
collectResponseCalls(child, responders, found);
|
|
5615
6592
|
}
|
|
5616
6593
|
return found;
|
|
5617
6594
|
}
|
|
5618
6595
|
function collectReturns(node, found) {
|
|
5619
|
-
for (const child of node
|
|
6596
|
+
for (const child of children(node)) {
|
|
5620
6597
|
if (child === null || OWN_BODY_TYPES.has(child.type)) {
|
|
5621
6598
|
continue;
|
|
5622
6599
|
}
|
|
@@ -5628,19 +6605,24 @@ function collectReturns(node, found) {
|
|
|
5628
6605
|
}
|
|
5629
6606
|
return found;
|
|
5630
6607
|
}
|
|
5631
|
-
function readingOfCall(call, declaration, names,
|
|
6608
|
+
function readingOfCall(call, declaration, names, options) {
|
|
5632
6609
|
const argument = statusArgumentOf(call, declaration);
|
|
5633
6610
|
if (argument === null) {
|
|
5634
|
-
return declaration.defaultStatusCode === void 0 ?
|
|
6611
|
+
return declaration.defaultStatusCode === void 0 ? absentReading2 : writtenReading2(declaration.defaultStatusCode, rangeOf(call));
|
|
5635
6612
|
}
|
|
5636
|
-
const status = statusNumberOf(
|
|
6613
|
+
const status = statusNumberOf(
|
|
6614
|
+
argument,
|
|
6615
|
+
names,
|
|
6616
|
+
options.facts,
|
|
6617
|
+
options.boundStatuses
|
|
6618
|
+
);
|
|
5637
6619
|
if (status === null) {
|
|
5638
6620
|
return unreadableReading(
|
|
5639
6621
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
5640
6622
|
rangeOf(argument)
|
|
5641
6623
|
);
|
|
5642
6624
|
}
|
|
5643
|
-
return
|
|
6625
|
+
return writtenReading2(status, rangeOf(argument));
|
|
5644
6626
|
}
|
|
5645
6627
|
function effectsReaching(effects, conditions) {
|
|
5646
6628
|
return effects.filter(
|
|
@@ -5669,6 +6651,45 @@ function conditionInputs(method, paths, facts) {
|
|
|
5669
6651
|
);
|
|
5670
6652
|
return operands.length === 0 ? void 0 : guardInputs(operands, { facts, unit: method });
|
|
5671
6653
|
}
|
|
6654
|
+
function boundStatusArguments(helper, call, pattern, facts) {
|
|
6655
|
+
const parameters = children(field(helper, "parameters")).filter(
|
|
6656
|
+
(child) => child.type === "identifier"
|
|
6657
|
+
);
|
|
6658
|
+
const args = call.type === "call" ? readCallArgs(field(call, "arguments")).positional : [];
|
|
6659
|
+
const bound = /* @__PURE__ */ new Map();
|
|
6660
|
+
parameters.forEach((parameter, index) => {
|
|
6661
|
+
const arg = args[index];
|
|
6662
|
+
const status = arg === void 0 ? null : statusNumberOf(arg, pattern.statusCodeNames ?? {}, facts);
|
|
6663
|
+
if (status !== null) {
|
|
6664
|
+
bound.set(parameter.text, status);
|
|
6665
|
+
}
|
|
6666
|
+
});
|
|
6667
|
+
return bound;
|
|
6668
|
+
}
|
|
6669
|
+
function helperResponse(helper, pattern, facts, boundStatuses = /* @__PURE__ */ new Map()) {
|
|
6670
|
+
const branches = responseBranches(helper, pattern, [], void 0, {
|
|
6671
|
+
fallthrough: "handOn",
|
|
6672
|
+
facts,
|
|
6673
|
+
boundStatuses
|
|
6674
|
+
});
|
|
6675
|
+
if (branches === null || branches.some((branch) => branch.terminal.kind !== "response")) {
|
|
6676
|
+
return null;
|
|
6677
|
+
}
|
|
6678
|
+
const statuses = new Set(
|
|
6679
|
+
branches.map((branch) => {
|
|
6680
|
+
const reading = branch.statusCodeReading?.reading;
|
|
6681
|
+
if (reading?.kind === "absent") {
|
|
6682
|
+
return branch.statusCodeReading?.libraryDefault ?? null;
|
|
6683
|
+
}
|
|
6684
|
+
return reading?.kind === "written" ? reading.value : null;
|
|
6685
|
+
})
|
|
6686
|
+
);
|
|
6687
|
+
const [only] = [...statuses];
|
|
6688
|
+
return statuses.size === 1 && typeof only === "number" ? writtenReading2(only, rangeOf(helper)) : unreadableReading(
|
|
6689
|
+
"This response is sent by a helper whose status does not settle on one number here, so this outcome claims none",
|
|
6690
|
+
rangeOf(helper)
|
|
6691
|
+
);
|
|
6692
|
+
}
|
|
5672
6693
|
function branchOf(outcome, pattern, effects, extraEffects, options) {
|
|
5673
6694
|
if (outcome.fellThrough === true && options.fallthrough === "handOn") {
|
|
5674
6695
|
return handsOnBranch(outcome, effects, extraEffects);
|
|
@@ -5691,6 +6712,7 @@ function branchOf(outcome, pattern, effects, extraEffects, options) {
|
|
|
5691
6712
|
reading: outcome.reading,
|
|
5692
6713
|
libraryDefault: pattern.defaultStatusCode
|
|
5693
6714
|
},
|
|
6715
|
+
...outcome.body === void 0 ? {} : { bodyShapeReading: { reading: outcome.body } },
|
|
5694
6716
|
effects: effectsReaching(effects, outcome.conditions),
|
|
5695
6717
|
...extraEffects === void 0 ? {} : { extraEffects },
|
|
5696
6718
|
location: outcome.location,
|
|
@@ -5718,20 +6740,26 @@ function handsOnBranch(outcome, effects, extraEffects) {
|
|
|
5718
6740
|
isDefault: outcome.conditions.length === 0
|
|
5719
6741
|
};
|
|
5720
6742
|
}
|
|
5721
|
-
function returnPathBranches(method, effects, exits = [], facts) {
|
|
6743
|
+
function returnPathBranches(method, effects, exits = [], facts, keepsArms, raises2 = []) {
|
|
5722
6744
|
const body = field(method, "body");
|
|
5723
6745
|
if (body === null) {
|
|
5724
6746
|
return null;
|
|
5725
6747
|
}
|
|
5726
6748
|
const returns = collectReturns(body, []);
|
|
5727
6749
|
const exitCalls = exits.map((exit) => exit.call);
|
|
5728
|
-
const
|
|
6750
|
+
const terminals = [
|
|
6751
|
+
...returns,
|
|
6752
|
+
...exitCalls,
|
|
6753
|
+
...raises2.map((raise) => raise.call)
|
|
6754
|
+
];
|
|
6755
|
+
const lowered = lowerRubyBody(body, terminals, exitCalls);
|
|
5729
6756
|
const enumerated = enumerateOrDegrade2(
|
|
5730
6757
|
{
|
|
5731
6758
|
statements: lowered.statements,
|
|
5732
|
-
terminalsByStmt: lowered.terminalsByStmt
|
|
6759
|
+
terminalsByStmt: lowered.terminalsByStmt,
|
|
6760
|
+
...keepsArms === void 0 ? {} : { keepsArms }
|
|
5733
6761
|
},
|
|
5734
|
-
|
|
6762
|
+
terminals
|
|
5735
6763
|
);
|
|
5736
6764
|
const inputs = conditionInputs(
|
|
5737
6765
|
method,
|
|
@@ -5759,8 +6787,8 @@ function returnPathBranches(method, effects, exits = [], facts) {
|
|
|
5759
6787
|
returnTerminal(rangeOf(statement))
|
|
5760
6788
|
);
|
|
5761
6789
|
}
|
|
5762
|
-
for (const
|
|
5763
|
-
push(enumerated.byTerminal.get(
|
|
6790
|
+
for (const ending of [...exits, ...raises2]) {
|
|
6791
|
+
push(enumerated.byTerminal.get(ending.call) ?? [], ending.terminal);
|
|
5764
6792
|
}
|
|
5765
6793
|
push(enumerated.fallthrough, returnTerminal(rangeOf(method)));
|
|
5766
6794
|
return branches.length === 0 ? null : branches;
|
|
@@ -5785,13 +6813,22 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
5785
6813
|
if (declarations.length === 0 || body === null) {
|
|
5786
6814
|
return null;
|
|
5787
6815
|
}
|
|
5788
|
-
const
|
|
5789
|
-
|
|
5790
|
-
|
|
6816
|
+
const responders = {
|
|
6817
|
+
byName: declarationsByName(declarations),
|
|
6818
|
+
locals: localNamesIn(method),
|
|
6819
|
+
helper: options.respondingHelper
|
|
6820
|
+
};
|
|
6821
|
+
const responses = collectResponseCalls(body, responders, []);
|
|
5791
6822
|
const returns = collectReturns(body, []);
|
|
5792
|
-
const
|
|
6823
|
+
const raises2 = options.raises === void 0 ? [] : escapingRaises(method);
|
|
6824
|
+
const lowered = lowerRubyBody(
|
|
6825
|
+
body,
|
|
6826
|
+
[...returns, ...raises2],
|
|
6827
|
+
responses,
|
|
6828
|
+
pattern.formatSwitch
|
|
6829
|
+
);
|
|
5793
6830
|
const bareReturns = returns.filter(
|
|
5794
|
-
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node,
|
|
6831
|
+
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, responders, []).length === 0
|
|
5795
6832
|
);
|
|
5796
6833
|
const terminals = [...responses, ...bareReturns];
|
|
5797
6834
|
const enumerated = enumerateOrDegrade2(
|
|
@@ -5799,7 +6836,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
5799
6836
|
statements: lowered.statements,
|
|
5800
6837
|
terminalsByStmt: lowered.terminalsByStmt
|
|
5801
6838
|
},
|
|
5802
|
-
terminals
|
|
6839
|
+
[...terminals, ...raises2]
|
|
5803
6840
|
);
|
|
5804
6841
|
const statusNames = pattern.statusCodeNames ?? {};
|
|
5805
6842
|
const inputs = conditionInputs(
|
|
@@ -5810,30 +6847,58 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
5810
6847
|
const conditionsOf = (path7) => path7.map((condition) => conditionOf(condition, inputs));
|
|
5811
6848
|
const outcomes = [];
|
|
5812
6849
|
for (const terminal of terminals) {
|
|
5813
|
-
const
|
|
5814
|
-
const reading =
|
|
6850
|
+
const responder = responderOf(terminal, responders);
|
|
6851
|
+
const reading = responder === void 0 ? absentReading2 : readingOfResponder(terminal, responder, statusNames, options);
|
|
6852
|
+
const body2 = responder?.kind === "declared" ? bodyReadingOfCall(terminal, responder.declaration, options.facts) : void 0;
|
|
5815
6853
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
5816
6854
|
outcomes.push({
|
|
5817
6855
|
conditions: conditionsOf(path7),
|
|
5818
6856
|
reading,
|
|
5819
|
-
|
|
6857
|
+
...body2 === void 0 ? {} : { body: body2 },
|
|
6858
|
+
location: rangeOf(terminal),
|
|
6859
|
+
// A bare `return` wrote no response, so a filter that takes it
|
|
6860
|
+
// hands the request on the same as one that reaches its end.
|
|
6861
|
+
...responder === void 0 ? { fellThrough: true } : {}
|
|
5820
6862
|
});
|
|
5821
6863
|
}
|
|
5822
6864
|
}
|
|
5823
6865
|
for (const path7 of enumerated.fallthrough) {
|
|
5824
6866
|
outcomes.push({
|
|
5825
6867
|
conditions: conditionsOf(path7),
|
|
5826
|
-
reading:
|
|
6868
|
+
reading: absentReading2,
|
|
5827
6869
|
location: rangeOf(method),
|
|
5828
6870
|
fellThrough: true
|
|
5829
6871
|
});
|
|
5830
|
-
}
|
|
5831
|
-
|
|
6872
|
+
}
|
|
6873
|
+
const read = options.raises;
|
|
6874
|
+
const raised = raises2.flatMap((call) => {
|
|
6875
|
+
const terminal = raisedTerminal(
|
|
6876
|
+
pattern,
|
|
6877
|
+
read?.classes.get(call) ?? null,
|
|
6878
|
+
read?.handlers ?? [],
|
|
6879
|
+
rangeOf(call)
|
|
6880
|
+
);
|
|
6881
|
+
return (enumerated.byTerminal.get(call) ?? []).map((path7) => {
|
|
6882
|
+
const conditions = conditionsOf(path7);
|
|
6883
|
+
return {
|
|
6884
|
+
conditions,
|
|
6885
|
+
terminal,
|
|
6886
|
+
effects: effectsReaching(effects, conditions),
|
|
6887
|
+
...extraEffects === void 0 ? {} : { extraEffects },
|
|
6888
|
+
location: terminal.location,
|
|
6889
|
+
isDefault: false
|
|
6890
|
+
};
|
|
6891
|
+
});
|
|
6892
|
+
});
|
|
6893
|
+
if (outcomes.length === 0 && raised.length === 0) {
|
|
5832
6894
|
return null;
|
|
5833
6895
|
}
|
|
5834
|
-
return
|
|
5835
|
-
(
|
|
5836
|
-
|
|
6896
|
+
return [
|
|
6897
|
+
...outcomes.map(
|
|
6898
|
+
(outcome) => branchOf(outcome, pattern, effects, extraEffects, options)
|
|
6899
|
+
),
|
|
6900
|
+
...raised
|
|
6901
|
+
];
|
|
5837
6902
|
}
|
|
5838
6903
|
|
|
5839
6904
|
// src/clientCalls.ts
|
|
@@ -5846,7 +6911,16 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
5846
6911
|
}
|
|
5847
6912
|
for (const call of callsUnder2(method)) {
|
|
5848
6913
|
for (const request of requestCalls(call, pattern, options)) {
|
|
5849
|
-
units.push(
|
|
6914
|
+
units.push(
|
|
6915
|
+
clientUnit(
|
|
6916
|
+
method,
|
|
6917
|
+
name,
|
|
6918
|
+
{ ...request, call },
|
|
6919
|
+
pattern,
|
|
6920
|
+
pack,
|
|
6921
|
+
options
|
|
6922
|
+
)
|
|
6923
|
+
);
|
|
5850
6924
|
}
|
|
5851
6925
|
}
|
|
5852
6926
|
}
|
|
@@ -5904,10 +6978,7 @@ function requestArgument(call, called, pattern) {
|
|
|
5904
6978
|
return pattern.requestObject?.attribute === called ? args.positional[0] : void 0;
|
|
5905
6979
|
}
|
|
5906
6980
|
function methodDefinitions(node, found = []) {
|
|
5907
|
-
for (const child of node
|
|
5908
|
-
if (child === null) {
|
|
5909
|
-
continue;
|
|
5910
|
-
}
|
|
6981
|
+
for (const child of children(node)) {
|
|
5911
6982
|
if (child.type === "method" || child.type === "singleton_method") {
|
|
5912
6983
|
found.push(child);
|
|
5913
6984
|
}
|
|
@@ -5916,10 +6987,7 @@ function methodDefinitions(node, found = []) {
|
|
|
5916
6987
|
return found;
|
|
5917
6988
|
}
|
|
5918
6989
|
function callsUnder2(node, found = []) {
|
|
5919
|
-
for (const child of node
|
|
5920
|
-
if (child === null) {
|
|
5921
|
-
continue;
|
|
5922
|
-
}
|
|
6990
|
+
for (const child of children(node)) {
|
|
5923
6991
|
if (child.type === "call") {
|
|
5924
6992
|
found.push(child);
|
|
5925
6993
|
}
|
|
@@ -5936,7 +7004,7 @@ function requestCalls(call, pattern, options) {
|
|
|
5936
7004
|
const read = (site) => requestCall(
|
|
5937
7005
|
call,
|
|
5938
7006
|
called,
|
|
5939
|
-
|
|
7007
|
+
receiverBase(receiver, pattern, options, site),
|
|
5940
7008
|
pattern,
|
|
5941
7009
|
options,
|
|
5942
7010
|
site
|
|
@@ -5949,7 +7017,10 @@ function requestCalls(call, pattern, options) {
|
|
|
5949
7017
|
for (const site of constructionSitesOf(call, options.facts)) {
|
|
5950
7018
|
const stated = read(site);
|
|
5951
7019
|
if (stated !== null) {
|
|
5952
|
-
byBoundary.set(
|
|
7020
|
+
byBoundary.set(
|
|
7021
|
+
`${stated.method} ${stated.host ?? ""} ${stated.path}`,
|
|
7022
|
+
stated
|
|
7023
|
+
);
|
|
5953
7024
|
}
|
|
5954
7025
|
}
|
|
5955
7026
|
if (byBoundary.size > 0) {
|
|
@@ -5957,12 +7028,12 @@ function requestCalls(call, pattern, options) {
|
|
|
5957
7028
|
}
|
|
5958
7029
|
return plain === null ? [] : [plain];
|
|
5959
7030
|
}
|
|
5960
|
-
function requestCall(call, called,
|
|
7031
|
+
function requestCall(call, called, base, pattern, options, site) {
|
|
5961
7032
|
const args = readCallArgs(field(call, "arguments"));
|
|
5962
7033
|
const verb = pattern.verbMethodNames[called];
|
|
5963
7034
|
if (verb !== void 0) {
|
|
5964
|
-
const
|
|
5965
|
-
return
|
|
7035
|
+
const target2 = targetAt(urlNodeIn(args, pattern), options, site);
|
|
7036
|
+
return target2 === null ? null : { method: verb, ...underBase(base, target2) };
|
|
5966
7037
|
}
|
|
5967
7038
|
const sent = pattern.requestObject;
|
|
5968
7039
|
if (sent === void 0 || sent.attribute !== called) {
|
|
@@ -5972,8 +7043,14 @@ function requestCall(call, called, prefix, pattern, options, site) {
|
|
|
5972
7043
|
if (built === null) {
|
|
5973
7044
|
return null;
|
|
5974
7045
|
}
|
|
5975
|
-
const
|
|
5976
|
-
return
|
|
7046
|
+
const target = targetAt(built.url, options, site);
|
|
7047
|
+
return target === null ? null : { method: built.method, ...underBase(base, target) };
|
|
7048
|
+
}
|
|
7049
|
+
function underBase(base, target) {
|
|
7050
|
+
if (base === null || target.host !== void 0) {
|
|
7051
|
+
return target;
|
|
7052
|
+
}
|
|
7053
|
+
return { path: base.path + target.path, host: base.host };
|
|
5977
7054
|
}
|
|
5978
7055
|
function requestBuilt(argument, sent, options, site) {
|
|
5979
7056
|
if (argument === void 0) {
|
|
@@ -5994,16 +7071,19 @@ function requestBuilt(argument, sent, options, site) {
|
|
|
5994
7071
|
function isLibraryReceiver(receiver, pattern, options) {
|
|
5995
7072
|
return namesConstant2(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
5996
7073
|
}
|
|
5997
|
-
function
|
|
7074
|
+
function receiverBase(receiver, pattern, options, site) {
|
|
5998
7075
|
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
5999
7076
|
if (built === null) {
|
|
6000
|
-
return
|
|
7077
|
+
return null;
|
|
6001
7078
|
}
|
|
6002
7079
|
const args = readCallArgs(field(built, "arguments"));
|
|
6003
7080
|
const keyword = pattern.builderUrlKeyword;
|
|
6004
7081
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
6005
|
-
|
|
6006
|
-
|
|
7082
|
+
if (base === void 0) {
|
|
7083
|
+
return null;
|
|
7084
|
+
}
|
|
7085
|
+
const value = evaluatedValue(base, options.facts, void 0, site);
|
|
7086
|
+
return { path: trimmed(pathOf(value) ?? ""), host: hostOf(value) };
|
|
6007
7087
|
}
|
|
6008
7088
|
function trimmed(path7) {
|
|
6009
7089
|
return path7 === "/" ? "" : path7.replace(/\/+$/, "");
|
|
@@ -6026,22 +7106,32 @@ function builderCallBehind(receiver, pattern, facts) {
|
|
|
6026
7106
|
const constant2 = field(written, "receiver");
|
|
6027
7107
|
return constant2 !== null && namesConstant2(constant2, pattern.constantName) ? written : null;
|
|
6028
7108
|
}
|
|
6029
|
-
function urlIn(args, pattern, options, site) {
|
|
6030
|
-
return pathAt(urlNodeIn(args, pattern), options, site);
|
|
6031
|
-
}
|
|
6032
7109
|
function urlNodeIn(args, pattern) {
|
|
6033
7110
|
const keyword = pattern.url.keyword;
|
|
6034
7111
|
return (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
6035
7112
|
}
|
|
6036
|
-
function
|
|
7113
|
+
function targetAt(written, options, site) {
|
|
6037
7114
|
if (written === void 0) {
|
|
6038
7115
|
return null;
|
|
6039
7116
|
}
|
|
6040
|
-
const
|
|
6041
|
-
|
|
7117
|
+
const value = evaluatedValue(written, options.facts, void 0, site);
|
|
7118
|
+
const path7 = pathOf(value);
|
|
7119
|
+
return path7 === void 0 || namesNothing(path7) ? null : { path: path7, host: hostOf(value) };
|
|
6042
7120
|
}
|
|
6043
7121
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
6044
7122
|
const range = rangeOf(method);
|
|
7123
|
+
const accessors = responseAccessors(pattern);
|
|
7124
|
+
const statusMembers = statusMembersOf(accessors);
|
|
7125
|
+
const reading = {
|
|
7126
|
+
facts: options.facts,
|
|
7127
|
+
statusMembers,
|
|
7128
|
+
handOff: {
|
|
7129
|
+
request: request.call,
|
|
7130
|
+
bodyMethods: accessors.bodyAccessors ?? [],
|
|
7131
|
+
facts: options.facts,
|
|
7132
|
+
readHelper: (helper) => callerBranches(helper, { facts: options.facts, statusMembers })
|
|
7133
|
+
}
|
|
7134
|
+
};
|
|
6045
7135
|
return {
|
|
6046
7136
|
identity: {
|
|
6047
7137
|
name,
|
|
@@ -6057,21 +7147,30 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
6057
7147
|
transport: pack.protocol,
|
|
6058
7148
|
method: request.method,
|
|
6059
7149
|
path: request.path,
|
|
7150
|
+
host: request.host,
|
|
6060
7151
|
recognition: pack.name
|
|
6061
7152
|
}),
|
|
6062
7153
|
parameters: [],
|
|
6063
|
-
branches: callerBranches(method,
|
|
6064
|
-
...
|
|
7154
|
+
branches: callerBranches(method, reading),
|
|
7155
|
+
...accessors,
|
|
6065
7156
|
bodyContent: "statements",
|
|
6066
7157
|
dependencyCalls: [],
|
|
6067
7158
|
declaredContract: null
|
|
6068
7159
|
};
|
|
6069
7160
|
}
|
|
6070
|
-
function callerBranches(method,
|
|
6071
|
-
const
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
7161
|
+
function callerBranches(method, reading) {
|
|
7162
|
+
const sites = invocationSites(method, void 0, void 0, reading.facts);
|
|
7163
|
+
const effects = sites.map((site) => site.effect);
|
|
7164
|
+
const keepsArms = (condition) => testsStatus(predicateOf(condition), reading.statusMembers);
|
|
7165
|
+
const found = returnPathBranches(
|
|
7166
|
+
method,
|
|
7167
|
+
effects,
|
|
7168
|
+
[],
|
|
7169
|
+
reading.facts,
|
|
7170
|
+
keepsArms,
|
|
7171
|
+
raiseEndings(method)
|
|
7172
|
+
) ?? [returnBranch(rangeOf(method), effects)];
|
|
7173
|
+
return reading.handOff === void 0 ? found : throughResponseHelpers(found, sites, reading.handOff);
|
|
6075
7174
|
}
|
|
6076
7175
|
function responseAccessors(pattern) {
|
|
6077
7176
|
const response = pattern.response;
|
|
@@ -6082,7 +7181,8 @@ function responseAccessors(pattern) {
|
|
|
6082
7181
|
...response.body === void 0 ? {} : { bodyAccessors: response.body },
|
|
6083
7182
|
...response.statusCode === void 0 ? {} : { statusAccessors: response.statusCode },
|
|
6084
7183
|
...response.success === void 0 ? {} : { successAccessors: response.success },
|
|
6085
|
-
...response.failureDelivery === void 0 ? {} : { failureDelivery: response.failureDelivery }
|
|
7184
|
+
...response.failureDelivery === void 0 ? {} : { failureDelivery: response.failureDelivery },
|
|
7185
|
+
...response.redirectDelivery === void 0 ? {} : { redirectDelivery: response.redirectDelivery }
|
|
6086
7186
|
};
|
|
6087
7187
|
}
|
|
6088
7188
|
function returnBranch(range, effects) {
|
|
@@ -6133,9 +7233,7 @@ function elementSite(node) {
|
|
|
6133
7233
|
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
6134
7234
|
return null;
|
|
6135
7235
|
}
|
|
6136
|
-
const index = node.
|
|
6137
|
-
(child) => child !== null && child.id !== object.id
|
|
6138
|
-
);
|
|
7236
|
+
const index = children(node).find((child) => child.id !== object.id);
|
|
6139
7237
|
if (index === void 0) {
|
|
6140
7238
|
return null;
|
|
6141
7239
|
}
|
|
@@ -6316,7 +7414,7 @@ function followsExitWhenAbsent(parent, child, subject) {
|
|
|
6316
7414
|
if (!STATEMENT_LIST_TYPES.has(parent.type)) {
|
|
6317
7415
|
return false;
|
|
6318
7416
|
}
|
|
6319
|
-
const statements = parent
|
|
7417
|
+
const statements = children(parent);
|
|
6320
7418
|
const at = statements.findIndex((statement) => statement?.id === child.id);
|
|
6321
7419
|
return statements.slice(0, at).some(
|
|
6322
7420
|
(statement) => statement !== null && leavesWhenAbsent(statement, subject)
|
|
@@ -6368,7 +7466,7 @@ function continuationRaises(from) {
|
|
|
6368
7466
|
let parent = current.parent;
|
|
6369
7467
|
while (parent !== null) {
|
|
6370
7468
|
if (STATEMENT_LIST_TYPES.has(parent.type)) {
|
|
6371
|
-
const statements = parent
|
|
7469
|
+
const statements = children(parent);
|
|
6372
7470
|
const at = statements.findIndex((one) => one?.id === current.id);
|
|
6373
7471
|
const exit = firstExit(statements.slice(at + 1));
|
|
6374
7472
|
if (exit !== null) {
|
|
@@ -6402,20 +7500,13 @@ function firstExit(statements) {
|
|
|
6402
7500
|
return null;
|
|
6403
7501
|
}
|
|
6404
7502
|
function exitOf2(statement) {
|
|
6405
|
-
if (
|
|
7503
|
+
if (isRaiseCall(statement)) {
|
|
6406
7504
|
return "raise";
|
|
6407
7505
|
}
|
|
6408
7506
|
if (LEAVING_TYPES.has(statement.type)) {
|
|
6409
7507
|
return "leave";
|
|
6410
7508
|
}
|
|
6411
|
-
return STATEMENT_LIST_TYPES.has(statement.type) ? firstExit(statement
|
|
6412
|
-
}
|
|
6413
|
-
var RAISING_METHODS2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
6414
|
-
function isRaise3(node) {
|
|
6415
|
-
if (node.type === "identifier") {
|
|
6416
|
-
return RAISING_METHODS2.has(node.text);
|
|
6417
|
-
}
|
|
6418
|
-
return node.type === "call" && field(node, "receiver") === null && RAISING_METHODS2.has(field(node, "method")?.text ?? "");
|
|
7509
|
+
return STATEMENT_LIST_TYPES.has(statement.type) ? firstExit(children(statement)) : null;
|
|
6419
7510
|
}
|
|
6420
7511
|
function isPresentWhen(test, outcome, subject) {
|
|
6421
7512
|
const expression = peelParens(test);
|
|
@@ -6483,7 +7574,7 @@ function isNilCheckOn(call, receiver) {
|
|
|
6483
7574
|
return call.type === "call" && field(call, "method")?.text === "nil?" && field(call, "receiver")?.id === receiver.id;
|
|
6484
7575
|
}
|
|
6485
7576
|
function peelParens(node) {
|
|
6486
|
-
const inner = node.type === "parenthesized_statements" && node.namedChildCount === 1 ? node.
|
|
7577
|
+
const inner = node.type === "parenthesized_statements" && node.namedChildCount === 1 ? node.namedChild(0) : null;
|
|
6487
7578
|
return inner === null || inner === void 0 ? node : peelParens(inner);
|
|
6488
7579
|
}
|
|
6489
7580
|
function climbParens(node) {
|
|
@@ -6528,7 +7619,7 @@ var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method",
|
|
|
6528
7619
|
function envReadEffects(root, facts) {
|
|
6529
7620
|
const helpers = helperFactsOf(facts);
|
|
6530
7621
|
const slots = [];
|
|
6531
|
-
walkDescendants2(root, null, {
|
|
7622
|
+
walkDescendants2(root, null, children, {
|
|
6532
7623
|
at: (node) => {
|
|
6533
7624
|
const read = envReadAt(node);
|
|
6534
7625
|
if (read !== null) {
|
|
@@ -6608,7 +7699,7 @@ function namedParameters(db) {
|
|
|
6608
7699
|
return named2;
|
|
6609
7700
|
}
|
|
6610
7701
|
function emitEnvFacts(db, file, root) {
|
|
6611
|
-
walkDescendants2(root, null, {
|
|
7702
|
+
walkDescendants2(root, null, children, {
|
|
6612
7703
|
at: (node) => {
|
|
6613
7704
|
if (isEnv(node) && handsOnward(node)) {
|
|
6614
7705
|
db.add("environmentObject", [
|
|
@@ -6736,7 +7827,8 @@ function envSiteAt(node) {
|
|
|
6736
7827
|
}
|
|
6737
7828
|
|
|
6738
7829
|
// src/filters.ts
|
|
6739
|
-
|
|
7830
|
+
var RESCUES_NOTHING = { refs: [], someUnread: false };
|
|
7831
|
+
function controllerFilterChain(pattern, ancestry, read = {}) {
|
|
6740
7832
|
const forms = pattern.filters ?? [];
|
|
6741
7833
|
if (forms.length === 0) {
|
|
6742
7834
|
return [];
|
|
@@ -6749,18 +7841,29 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
6749
7841
|
}
|
|
6750
7842
|
}
|
|
6751
7843
|
let declared = [];
|
|
6752
|
-
|
|
7844
|
+
const inlineSeen = /* @__PURE__ */ new Map();
|
|
7845
|
+
for (const { block, statement } of inheritedStatements(ancestry)) {
|
|
6753
7846
|
const called = calledName(statement);
|
|
6754
7847
|
if (called === null) {
|
|
6755
7848
|
continue;
|
|
6756
7849
|
}
|
|
6757
7850
|
const form = byName.get(called);
|
|
6758
7851
|
if (form !== void 0) {
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
7852
|
+
const inlineKey = `${block.file}\0${block.info.qualifiedName}\0${form.name}`;
|
|
7853
|
+
const ordinal = inlineSeen.get(inlineKey) ?? 0;
|
|
7854
|
+
const declarations = declarationsOf(
|
|
7855
|
+
statement,
|
|
7856
|
+
form,
|
|
7857
|
+
block,
|
|
7858
|
+
read.facts,
|
|
7859
|
+
ordinal
|
|
7860
|
+
);
|
|
7861
|
+
if (declarations.some((one) => one.inline !== void 0)) {
|
|
7862
|
+
inlineSeen.set(inlineKey, ordinal + 1);
|
|
7863
|
+
}
|
|
7864
|
+
for (const declaration of declarations) {
|
|
7865
|
+
const earlier = declaration.filter.onThrow === true ? void 0 : declared.find((one) => sameFilter(one, declaration));
|
|
7866
|
+
declared = [...declared.filter((one) => one !== earlier), declaration];
|
|
6764
7867
|
}
|
|
6765
7868
|
continue;
|
|
6766
7869
|
}
|
|
@@ -6769,26 +7872,78 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
6769
7872
|
declared = applySkips(declared, skipsOf(statement, skipped, read.facts));
|
|
6770
7873
|
}
|
|
6771
7874
|
}
|
|
6772
|
-
const resolved =
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
7875
|
+
const resolved = declared.map(
|
|
7876
|
+
(declaration) => linkFor(
|
|
7877
|
+
declaration,
|
|
7878
|
+
writtenAt(
|
|
7879
|
+
declaration,
|
|
7880
|
+
filterMethod(ancestry, declaration.methodName, read)
|
|
7881
|
+
)
|
|
7882
|
+
)
|
|
7883
|
+
);
|
|
7884
|
+
return [
|
|
7885
|
+
...resolved.filter((one) => !runsAfterARaise(one)),
|
|
7886
|
+
...resolved.filter(runsAfterARaise).reverse()
|
|
7887
|
+
];
|
|
7888
|
+
}
|
|
7889
|
+
function linkFor(declaration, written) {
|
|
7890
|
+
if (written === null) {
|
|
7891
|
+
return {
|
|
7892
|
+
unread: {
|
|
7893
|
+
filter: declaration.filter,
|
|
7894
|
+
methodName: declaration.methodName,
|
|
7895
|
+
declaredIn: declaration.declaredIn,
|
|
7896
|
+
only: declaration.only,
|
|
7897
|
+
except: declaration.except
|
|
7898
|
+
}
|
|
7899
|
+
};
|
|
7900
|
+
}
|
|
7901
|
+
return {
|
|
7902
|
+
read: {
|
|
6779
7903
|
filter: declaration.filter,
|
|
7904
|
+
...written,
|
|
7905
|
+
only: declaration.only,
|
|
7906
|
+
except: declaration.except,
|
|
7907
|
+
rescues: declaration.rescues
|
|
7908
|
+
}
|
|
7909
|
+
};
|
|
7910
|
+
}
|
|
7911
|
+
function filterOf(link) {
|
|
7912
|
+
return "read" in link ? link.read : link.unread;
|
|
7913
|
+
}
|
|
7914
|
+
function runsAfterARaise(link) {
|
|
7915
|
+
return filterOf(link).filter.onThrow === true;
|
|
7916
|
+
}
|
|
7917
|
+
function writtenAt(declaration, found) {
|
|
7918
|
+
if (found.type === "found") {
|
|
7919
|
+
return {
|
|
6780
7920
|
methodName: declaration.methodName,
|
|
7921
|
+
nameKind: "binding",
|
|
6781
7922
|
method: found.method,
|
|
6782
7923
|
file: found.block.file,
|
|
6783
7924
|
enclosingQualifiedName: found.block.info.qualifiedName,
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
});
|
|
7925
|
+
nesting: found.block.info.bodyNesting
|
|
7926
|
+
};
|
|
6787
7927
|
}
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
7928
|
+
const inline = declaration.inline;
|
|
7929
|
+
if (inline === void 0) {
|
|
7930
|
+
return null;
|
|
7931
|
+
}
|
|
7932
|
+
return {
|
|
7933
|
+
methodName: inline.label,
|
|
7934
|
+
nameKind: "label",
|
|
7935
|
+
method: inline.block,
|
|
7936
|
+
file: inline.declaredIn.file,
|
|
7937
|
+
enclosingQualifiedName: inline.declaredIn.info.qualifiedName,
|
|
7938
|
+
nesting: inline.declaredIn.info.bodyNesting
|
|
7939
|
+
};
|
|
7940
|
+
}
|
|
7941
|
+
function filterMethod(ancestry, name, read) {
|
|
7942
|
+
const found = methodInAncestry(ancestry, name, read);
|
|
7943
|
+
if (found.type === "unsettled" && found.cause === "unreadAncestor") {
|
|
7944
|
+
return methodPastUnreadAncestors(ancestry, name, read);
|
|
7945
|
+
}
|
|
7946
|
+
return found;
|
|
6792
7947
|
}
|
|
6793
7948
|
function filterCoversAction(filter, actionName) {
|
|
6794
7949
|
if (filter.except.has(actionName)) {
|
|
@@ -6796,37 +7951,55 @@ function filterCoversAction(filter, actionName) {
|
|
|
6796
7951
|
}
|
|
6797
7952
|
return filter.only === null || filter.only.has(actionName);
|
|
6798
7953
|
}
|
|
6799
|
-
function filterReference(filter, displayPath) {
|
|
7954
|
+
function filterReference(filter, displayPath, caught) {
|
|
7955
|
+
return {
|
|
7956
|
+
file: displayPath,
|
|
7957
|
+
name: filter.methodName,
|
|
7958
|
+
...filter.filter.onThrow === true ? { onThrow: true } : {},
|
|
7959
|
+
...caught === void 0 ? {} : {
|
|
7960
|
+
catches: caught.classes.map((one) => one.name),
|
|
7961
|
+
...caught.classes.some((one) => one.inheritableByUnread) ? { mayCatchUnreadClasses: true } : {},
|
|
7962
|
+
...caught.someUnread ? { mayCatchAny: true } : {}
|
|
7963
|
+
}
|
|
7964
|
+
};
|
|
7965
|
+
}
|
|
7966
|
+
function unreadFilterReference(filter, displayPath) {
|
|
6800
7967
|
return {
|
|
6801
7968
|
file: displayPath,
|
|
6802
7969
|
name: filter.methodName,
|
|
6803
7970
|
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
6804
7971
|
};
|
|
6805
7972
|
}
|
|
6806
|
-
function filterUnit(filter, pattern, displayPath, body,
|
|
7973
|
+
function filterUnit(filter, pattern, displayPath, body, reading = {}) {
|
|
6807
7974
|
const range = rangeOf(filter.method);
|
|
7975
|
+
const { respondingHelper, raises: raises2 } = reading;
|
|
6808
7976
|
const branches = responseBranches(
|
|
6809
7977
|
filter.method,
|
|
6810
7978
|
pattern,
|
|
6811
7979
|
body.effects ?? [],
|
|
6812
7980
|
body.extraEffects,
|
|
6813
|
-
{
|
|
7981
|
+
{
|
|
7982
|
+
fallthrough: "handOn",
|
|
7983
|
+
facts: reading.facts,
|
|
7984
|
+
...respondingHelper === void 0 ? {} : { respondingHelper },
|
|
7985
|
+
...raises2 === void 0 ? {} : { raises: raises2 }
|
|
7986
|
+
}
|
|
6814
7987
|
);
|
|
6815
7988
|
return {
|
|
6816
7989
|
identity: {
|
|
6817
7990
|
name: filter.methodName,
|
|
6818
|
-
nameKind:
|
|
7991
|
+
nameKind: filter.nameKind,
|
|
6819
7992
|
kind: "middleware",
|
|
6820
7993
|
file: displayPath,
|
|
6821
7994
|
range,
|
|
6822
7995
|
span: spanOf(filter.method),
|
|
6823
|
-
exportName: filter.methodName,
|
|
7996
|
+
exportName: filter.nameKind === "binding" ? filter.methodName : null,
|
|
6824
7997
|
exportPath: [filter.enclosingQualifiedName, filter.methodName]
|
|
6825
7998
|
},
|
|
6826
7999
|
boundaryBinding: null,
|
|
6827
8000
|
parameters: [],
|
|
6828
8001
|
branches: withSlotSources(
|
|
6829
|
-
branches ?? [handsOn(range, body)],
|
|
8002
|
+
[...branches ?? [handsOn(range, body)], ...reading.raised ?? []],
|
|
6830
8003
|
body.provenance
|
|
6831
8004
|
),
|
|
6832
8005
|
bodyContent: body.bodyContent ?? "absent",
|
|
@@ -6861,25 +8034,93 @@ function calledName(statement) {
|
|
|
6861
8034
|
}
|
|
6862
8035
|
return field(statement, "method")?.text ?? null;
|
|
6863
8036
|
}
|
|
6864
|
-
function declarationsOf(statement, filter, facts) {
|
|
8037
|
+
function declarationsOf(statement, filter, declaredIn, facts, inlineOrdinal) {
|
|
6865
8038
|
const args = readCallArgs(field(statement, "arguments"));
|
|
6866
8039
|
const keywords = filter.actionKeywords;
|
|
6867
8040
|
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
6868
8041
|
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
6869
|
-
|
|
8042
|
+
const rescues2 = filter.onThrow === true ? rescuedClasses(args, declaredIn.info.bodyNesting, facts) : RESCUES_NOTHING;
|
|
8043
|
+
const inline = inlineHandler(
|
|
8044
|
+
statement,
|
|
8045
|
+
args,
|
|
8046
|
+
filter,
|
|
8047
|
+
declaredIn,
|
|
8048
|
+
inlineOrdinal
|
|
8049
|
+
);
|
|
8050
|
+
return methodNamesOf(args, filter, facts, inline).map((methodName) => ({
|
|
6870
8051
|
filter,
|
|
6871
8052
|
methodName,
|
|
6872
8053
|
only,
|
|
6873
|
-
except: except ?? /* @__PURE__ */ new Set()
|
|
8054
|
+
except: except ?? /* @__PURE__ */ new Set(),
|
|
8055
|
+
rescues: rescues2,
|
|
8056
|
+
declaredIn: declaredIn.file,
|
|
8057
|
+
...inline === void 0 ? {} : { inline }
|
|
6874
8058
|
}));
|
|
6875
8059
|
}
|
|
6876
|
-
function
|
|
8060
|
+
function inlineHandler(statement, args, filter, declaredIn, ordinal) {
|
|
8061
|
+
const block = blockInPlaceOfMethod(statement, args, filter);
|
|
8062
|
+
if (block === null || blockStatements2(block).length === 0) {
|
|
8063
|
+
return void 0;
|
|
8064
|
+
}
|
|
8065
|
+
return { block, label: `${filter.name}#${ordinal}`, declaredIn };
|
|
8066
|
+
}
|
|
8067
|
+
function rescuedClasses(args, nesting, facts) {
|
|
8068
|
+
const refs = [];
|
|
8069
|
+
let someUnread = args.positional.length === 0;
|
|
8070
|
+
for (const arg of args.positional) {
|
|
8071
|
+
const candidates = constantRefCandidates(arg, nesting);
|
|
8072
|
+
const named2 = candidates.length === 0 ? stringValueOf(arg, facts) : null;
|
|
8073
|
+
if (candidates.length === 0 && named2 === null) {
|
|
8074
|
+
someUnread = true;
|
|
8075
|
+
continue;
|
|
8076
|
+
}
|
|
8077
|
+
refs.push({
|
|
8078
|
+
text: arg.text,
|
|
8079
|
+
candidates: named2 === null ? candidates : [named2]
|
|
8080
|
+
});
|
|
8081
|
+
}
|
|
8082
|
+
return { refs, someUnread };
|
|
8083
|
+
}
|
|
8084
|
+
function methodNamesOf(args, filter, facts, inline) {
|
|
8085
|
+
const named2 = filter.methodFrom === "withKeyword" ? withKeywordName(args, facts) : symbolArgumentNames(args, facts);
|
|
8086
|
+
if (named2.length > 0 || inline === void 0) {
|
|
8087
|
+
return named2;
|
|
8088
|
+
}
|
|
8089
|
+
return [blockHandlerName(inline.block) ?? inline.label];
|
|
8090
|
+
}
|
|
8091
|
+
function withKeywordName(args, facts) {
|
|
8092
|
+
const named2 = args.keyword.with;
|
|
8093
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
8094
|
+
return value === null ? [] : [value];
|
|
8095
|
+
}
|
|
8096
|
+
function blockInPlaceOfMethod(statement, args, filter) {
|
|
6877
8097
|
if (filter.methodFrom === "withKeyword") {
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
8098
|
+
return args.keyword.with === void 0 ? field(statement, "block") : null;
|
|
8099
|
+
}
|
|
8100
|
+
const lambda = args.positional.find((arg) => arg.type === "lambda");
|
|
8101
|
+
if (lambda !== void 0) {
|
|
8102
|
+
return field(lambda, "body");
|
|
8103
|
+
}
|
|
8104
|
+
return args.positional.length === 0 ? field(statement, "block") : null;
|
|
8105
|
+
}
|
|
8106
|
+
function blockStatements2(block) {
|
|
8107
|
+
return children(field(block, "body")).filter(
|
|
8108
|
+
(child) => child.type !== "comment"
|
|
8109
|
+
);
|
|
8110
|
+
}
|
|
8111
|
+
function blockHandlerName(block) {
|
|
8112
|
+
const statements = blockStatements2(block);
|
|
8113
|
+
const only = statements.length === 1 ? statements[0] : void 0;
|
|
8114
|
+
if (only === void 0) {
|
|
8115
|
+
return null;
|
|
8116
|
+
}
|
|
8117
|
+
const parameters = new Set(
|
|
8118
|
+
children(field(block, "parameters")).map((parameter) => parameter.text)
|
|
8119
|
+
);
|
|
8120
|
+
if (only.type === "identifier") {
|
|
8121
|
+
return parameters.has(only.text) ? null : only.text;
|
|
6881
8122
|
}
|
|
6882
|
-
return
|
|
8123
|
+
return calledName(only);
|
|
6883
8124
|
}
|
|
6884
8125
|
function symbolArgumentNames(args, facts) {
|
|
6885
8126
|
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
@@ -6890,13 +8131,16 @@ function skipsOf(statement, filterName, facts) {
|
|
|
6890
8131
|
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
6891
8132
|
}
|
|
6892
8133
|
function sameFilter(one, other) {
|
|
6893
|
-
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
8134
|
+
return namedBySymbol(one) && namedBySymbol(other) && one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
8135
|
+
}
|
|
8136
|
+
function namedBySymbol(declaration) {
|
|
8137
|
+
return declaration.inline === void 0;
|
|
6894
8138
|
}
|
|
6895
8139
|
function applySkips(declared, skips) {
|
|
6896
8140
|
const left = [];
|
|
6897
8141
|
for (const declaration of declared) {
|
|
6898
8142
|
const reaching = skips.filter(
|
|
6899
|
-
(skip) => skip.filterName === declaration.filter.name && skip.methodName === declaration.methodName
|
|
8143
|
+
(skip) => namedBySymbol(declaration) && skip.filterName === declaration.filter.name && skip.methodName === declaration.methodName
|
|
6900
8144
|
);
|
|
6901
8145
|
if (reaching.some((skip) => skip.actions === null)) {
|
|
6902
8146
|
continue;
|
|
@@ -6939,7 +8183,7 @@ var STREAM_METHODS = /* @__PURE__ */ new Set(["puts", "print", "write", "<<"]);
|
|
|
6939
8183
|
var DEFERRED_BODY_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
6940
8184
|
function streamWriteEffects(root) {
|
|
6941
8185
|
const found = [];
|
|
6942
|
-
walkDescendants3(root, null, {
|
|
8186
|
+
walkDescendants3(root, null, children, {
|
|
6943
8187
|
at: (node) => {
|
|
6944
8188
|
const write = node.type === "call" ? streamWriteAt(node) : null;
|
|
6945
8189
|
if (write !== null) {
|
|
@@ -7030,7 +8274,7 @@ function writtenShapeOf(node) {
|
|
|
7030
8274
|
return hashShape(node);
|
|
7031
8275
|
}
|
|
7032
8276
|
if (node.type === "array") {
|
|
7033
|
-
const first = node.
|
|
8277
|
+
const first = children(node).find((child) => child !== null);
|
|
7034
8278
|
return {
|
|
7035
8279
|
type: "array",
|
|
7036
8280
|
items: first ? writtenShapeOf(first) : { type: "unknown" }
|
|
@@ -7040,7 +8284,7 @@ function writtenShapeOf(node) {
|
|
|
7040
8284
|
}
|
|
7041
8285
|
function hashShape(node) {
|
|
7042
8286
|
const properties = {};
|
|
7043
|
-
for (const pair of node
|
|
8287
|
+
for (const pair of children(node)) {
|
|
7044
8288
|
if (pair?.type !== "pair") {
|
|
7045
8289
|
continue;
|
|
7046
8290
|
}
|
|
@@ -7403,14 +8647,14 @@ var NodesSeen = class {
|
|
|
7403
8647
|
};
|
|
7404
8648
|
function exampleRun(example, pattern, shared) {
|
|
7405
8649
|
const byName = /* @__PURE__ */ new Map();
|
|
7406
|
-
const
|
|
8650
|
+
const always3 = [];
|
|
7407
8651
|
for (const group of withInlineIncludes(example.groups, pattern, shared)) {
|
|
7408
8652
|
for (const one of groupBlocks(group, pattern)) {
|
|
7409
8653
|
for (const name of one.names) {
|
|
7410
8654
|
byName.set(name, one.block);
|
|
7411
8655
|
}
|
|
7412
8656
|
if (one.always) {
|
|
7413
|
-
|
|
8657
|
+
always3.push(one.block);
|
|
7414
8658
|
}
|
|
7415
8659
|
}
|
|
7416
8660
|
}
|
|
@@ -7429,7 +8673,7 @@ function exampleRun(example, pattern, shared) {
|
|
|
7429
8673
|
}
|
|
7430
8674
|
};
|
|
7431
8675
|
include(example.block);
|
|
7432
|
-
for (const block of
|
|
8676
|
+
for (const block of always3) {
|
|
7433
8677
|
include(block);
|
|
7434
8678
|
}
|
|
7435
8679
|
const valueNames = /* @__PURE__ */ new Set([
|
|
@@ -7802,8 +9046,8 @@ function moduleOfConstant(node, options) {
|
|
|
7802
9046
|
const files = [...new Set(keys.map(fileOfKey))];
|
|
7803
9047
|
return files.length === 1 ? options.displayPathOf(files[0]) : null;
|
|
7804
9048
|
}
|
|
7805
|
-
function oneLine(
|
|
7806
|
-
return
|
|
9049
|
+
function oneLine(text3) {
|
|
9050
|
+
return text3.replace(/\s*\n\s*/g, " ").trim();
|
|
7807
9051
|
}
|
|
7808
9052
|
|
|
7809
9053
|
// src/testClasses.ts
|
|
@@ -8074,6 +9318,7 @@ function fieldReadContext(pattern, cache, fileBlocks, bodyRead, facts) {
|
|
|
8074
9318
|
},
|
|
8075
9319
|
lookup: {
|
|
8076
9320
|
root: pattern.root,
|
|
9321
|
+
autoloadRoots: pattern.autoloadRoots ?? [],
|
|
8077
9322
|
pathConvention: pattern.pathConvention,
|
|
8078
9323
|
acronyms: pattern.acronyms ?? [],
|
|
8079
9324
|
ancestryRootClassNames: pattern.ancestryRootClassNames,
|
|
@@ -8229,6 +9474,7 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8229
9474
|
}
|
|
8230
9475
|
const lookup = {
|
|
8231
9476
|
root: pattern.root,
|
|
9477
|
+
autoloadRoots: pattern.autoloadRoots ?? [],
|
|
8232
9478
|
pathConvention: pattern.pathConvention,
|
|
8233
9479
|
acronyms: pattern.acronyms ?? [],
|
|
8234
9480
|
ancestryRootClassNames: pattern.ancestryRootClassNames,
|
|
@@ -8243,11 +9489,12 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8243
9489
|
])) {
|
|
8244
9490
|
return [];
|
|
8245
9491
|
}
|
|
8246
|
-
const
|
|
9492
|
+
const chain = controllerFilterChain(pattern, ancestry, {
|
|
8247
9493
|
facts: options.facts,
|
|
8248
9494
|
bodyBlocks: options.bodyBlocks,
|
|
8249
9495
|
dynamicNames: options.dynamicNames
|
|
8250
9496
|
});
|
|
9497
|
+
const filters = chain.flatMap((link) => "read" in link ? [link.read] : []);
|
|
8251
9498
|
const units = [];
|
|
8252
9499
|
const actions = controllerActions(
|
|
8253
9500
|
pattern,
|
|
@@ -8267,15 +9514,41 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8267
9514
|
options.facts,
|
|
8268
9515
|
options.storage
|
|
8269
9516
|
);
|
|
9517
|
+
const respondingHelper = respondingHelpersOf(ancestry, pattern, options);
|
|
9518
|
+
const exceptions = new ExceptionReader(
|
|
9519
|
+
lookup,
|
|
9520
|
+
pattern.libraryExceptions ?? {}
|
|
9521
|
+
);
|
|
9522
|
+
const caught = await handlerClasses(filters, exceptions);
|
|
9523
|
+
const handlersAround = (actionName) => filters.filter((filter) => filterCoversAction(filter, actionName)).flatMap((filter) => {
|
|
9524
|
+
const classes = caught.get(filter);
|
|
9525
|
+
return classes === void 0 ? [] : [classes];
|
|
9526
|
+
});
|
|
8270
9527
|
for (const filter of filters) {
|
|
8271
|
-
const
|
|
8272
|
-
const
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
9528
|
+
const displayPath2 = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
9529
|
+
const body = bodyOfMethod(filter.method, filter.file, options);
|
|
9530
|
+
const handler = filter.filter.onThrow === true;
|
|
9531
|
+
const raw = filterUnit(filter, pattern, displayPath2, body, {
|
|
9532
|
+
facts: options.facts,
|
|
9533
|
+
respondingHelper,
|
|
9534
|
+
...handler ? {} : {
|
|
9535
|
+
raises: {
|
|
9536
|
+
classes: await raisedClasses(
|
|
9537
|
+
filter.method,
|
|
9538
|
+
filter.nesting,
|
|
9539
|
+
exceptions
|
|
9540
|
+
),
|
|
9541
|
+
handlers: "eachRoute"
|
|
9542
|
+
},
|
|
9543
|
+
raised: raisedStatusBranches(
|
|
9544
|
+
pattern,
|
|
9545
|
+
options.storage?.patterns ?? [],
|
|
9546
|
+
body.extraEffects,
|
|
9547
|
+
"eachRoute",
|
|
9548
|
+
rangeOf(filter.method)
|
|
9549
|
+
)
|
|
9550
|
+
}
|
|
9551
|
+
});
|
|
8279
9552
|
units.push(raw);
|
|
8280
9553
|
options.onReachSeed?.(raw, {
|
|
8281
9554
|
file: filter.file,
|
|
@@ -8283,38 +9556,71 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8283
9556
|
enclosingQualifiedName: filter.enclosingQualifiedName
|
|
8284
9557
|
});
|
|
8285
9558
|
}
|
|
8286
|
-
const
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
const
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
9559
|
+
const displayPath = (file) => options.displayPathOf?.(file) ?? file;
|
|
9560
|
+
const referenceTo = (link) => "read" in link ? filterReference(
|
|
9561
|
+
link.read,
|
|
9562
|
+
displayPath(link.read.file),
|
|
9563
|
+
caught.get(link.read)
|
|
9564
|
+
) : unreadFilterReference(link.unread, displayPath(link.unread.declaredIn));
|
|
9565
|
+
const emitAction = async (actionName, method, block, routes) => {
|
|
9566
|
+
const around = chain.filter((link) => filterCoversAction(filterOf(link), actionName)).map((link) => referenceTo(link));
|
|
9567
|
+
const raises2 = {
|
|
9568
|
+
classes: await raisedClasses(method, block.info.bodyNesting, exceptions),
|
|
9569
|
+
handlers: handlersAround(actionName)
|
|
9570
|
+
};
|
|
9571
|
+
for (const route of routes.length === 0 ? [null] : routes) {
|
|
9572
|
+
const raw = buildControllerActionUnit(
|
|
9573
|
+
pack,
|
|
9574
|
+
pattern,
|
|
9575
|
+
info.qualifiedName,
|
|
9576
|
+
actionName,
|
|
9577
|
+
route,
|
|
9578
|
+
method,
|
|
9579
|
+
{
|
|
9580
|
+
display: block.file === (options.absoluteFile ?? options.filePath) ? options.filePath : options.displayPathOf?.(block.file) ?? block.file,
|
|
9581
|
+
absolute: block.file
|
|
9582
|
+
},
|
|
9583
|
+
options,
|
|
9584
|
+
around,
|
|
9585
|
+
respondingHelper,
|
|
9586
|
+
raises2
|
|
9587
|
+
);
|
|
9588
|
+
units.push(raw);
|
|
9589
|
+
options.onReachSeed?.(raw, {
|
|
9590
|
+
file: block.file,
|
|
9591
|
+
node: method,
|
|
9592
|
+
enclosingQualifiedName: block.info.qualifiedName
|
|
9593
|
+
});
|
|
9594
|
+
}
|
|
8312
9595
|
};
|
|
8313
9596
|
for (const [actionName, method, block] of actions) {
|
|
8314
|
-
|
|
9597
|
+
const routes = pattern.routesFor(info.qualifiedName, actionName);
|
|
9598
|
+
await emitAction(actionName, method, block, routes);
|
|
8315
9599
|
}
|
|
8316
9600
|
return units;
|
|
8317
9601
|
}
|
|
9602
|
+
async function handlerClasses(filters, exceptions) {
|
|
9603
|
+
const caught = /* @__PURE__ */ new Map();
|
|
9604
|
+
for (const filter of filters) {
|
|
9605
|
+
if (filter.filter.onThrow !== true) {
|
|
9606
|
+
continue;
|
|
9607
|
+
}
|
|
9608
|
+
const classes = [];
|
|
9609
|
+
for (const ref of filter.rescues.refs) {
|
|
9610
|
+
classes.push(await exceptions.read(ref));
|
|
9611
|
+
}
|
|
9612
|
+
caught.set(filter, { classes, someUnread: filter.rescues.someUnread });
|
|
9613
|
+
}
|
|
9614
|
+
return caught;
|
|
9615
|
+
}
|
|
9616
|
+
async function raisedClasses(method, nesting, exceptions) {
|
|
9617
|
+
const classes = new NodeMap();
|
|
9618
|
+
for (const call of escapingRaises(method)) {
|
|
9619
|
+
const ref = raisedClassRef(call, nesting);
|
|
9620
|
+
classes.set(call, ref === null ? null : await exceptions.read(ref));
|
|
9621
|
+
}
|
|
9622
|
+
return classes;
|
|
9623
|
+
}
|
|
8318
9624
|
function controllerActions(pattern, info, ownBlocks, ancestry, options) {
|
|
8319
9625
|
const actions = publicInstanceMethods(ownBlocks, options.bodyBlocks);
|
|
8320
9626
|
const seen = new Set(actions.map(([actionName]) => actionName));
|
|
@@ -8331,7 +9637,7 @@ function controllerActions(pattern, info, ownBlocks, ancestry, options) {
|
|
|
8331
9637
|
continue;
|
|
8332
9638
|
}
|
|
8333
9639
|
seen.add(actionName);
|
|
8334
|
-
if (pattern.
|
|
9640
|
+
if (pattern.routesFor(info.qualifiedName, actionName).length > 0) {
|
|
8335
9641
|
actions.push(action);
|
|
8336
9642
|
}
|
|
8337
9643
|
}
|
|
@@ -8356,16 +9662,52 @@ function publicInstanceMethods(blocks, bodyBlocks) {
|
|
|
8356
9662
|
}
|
|
8357
9663
|
return found;
|
|
8358
9664
|
}
|
|
8359
|
-
function
|
|
9665
|
+
function respondingHelpersOf(ancestry, pattern, options) {
|
|
9666
|
+
const methods = /* @__PURE__ */ new Map();
|
|
9667
|
+
const known = /* @__PURE__ */ new Map();
|
|
9668
|
+
const declared = new Set(
|
|
9669
|
+
(pattern.responseStatusCalls ?? []).map((call) => call.name)
|
|
9670
|
+
);
|
|
9671
|
+
const methodNamed = (name) => {
|
|
9672
|
+
if (!methods.has(name)) {
|
|
9673
|
+
const found = methodInAncestry(ancestry, name, {
|
|
9674
|
+
facts: options.facts,
|
|
9675
|
+
bodyBlocks: options.bodyBlocks,
|
|
9676
|
+
dynamicNames: options.dynamicNames
|
|
9677
|
+
});
|
|
9678
|
+
methods.set(name, found.type === "found" ? found.method : null);
|
|
9679
|
+
}
|
|
9680
|
+
return methods.get(name) ?? null;
|
|
9681
|
+
};
|
|
9682
|
+
return (name, call) => {
|
|
9683
|
+
const method = declared.has(name) ? null : methodNamed(name);
|
|
9684
|
+
if (method === null) {
|
|
9685
|
+
return null;
|
|
9686
|
+
}
|
|
9687
|
+
const bound = boundStatusArguments(method, call, pattern, options.facts);
|
|
9688
|
+
const key = `${name}(${[...bound].join(",")})`;
|
|
9689
|
+
if (!known.has(key)) {
|
|
9690
|
+
known.set(key, helperResponse(method, pattern, options.facts, bound));
|
|
9691
|
+
}
|
|
9692
|
+
return known.get(key) ?? null;
|
|
9693
|
+
};
|
|
9694
|
+
}
|
|
9695
|
+
function servesFormat(pattern, route) {
|
|
9696
|
+
return pattern.optionalFormat === true && route.formatless !== true && !route.path.endsWith("/");
|
|
9697
|
+
}
|
|
9698
|
+
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, route, method, file, bodyRead, wrappers, respondingHelper, raises2) {
|
|
8360
9699
|
const range = rangeOf(method);
|
|
8361
|
-
const route = pattern.routeFor(controllerQualifiedName, actionName);
|
|
8362
9700
|
const body = bodyOfMethod(method, file.absolute, bodyRead);
|
|
8363
9701
|
const perResponse = responseBranches(
|
|
8364
9702
|
method,
|
|
8365
9703
|
pattern,
|
|
8366
9704
|
body.effects ?? [],
|
|
8367
9705
|
body.extraEffects,
|
|
8368
|
-
{
|
|
9706
|
+
{
|
|
9707
|
+
facts: bodyRead.facts,
|
|
9708
|
+
respondingHelper,
|
|
9709
|
+
raises: raises2
|
|
9710
|
+
}
|
|
8369
9711
|
);
|
|
8370
9712
|
return {
|
|
8371
9713
|
identity: {
|
|
@@ -8382,7 +9724,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
8382
9724
|
transport: pack.protocol,
|
|
8383
9725
|
method: route.method,
|
|
8384
9726
|
path: route.path,
|
|
8385
|
-
recognition: pack.name
|
|
9727
|
+
recognition: pack.name,
|
|
9728
|
+
optionalFormat: servesFormat(pattern, route)
|
|
8386
9729
|
}),
|
|
8387
9730
|
parameters: [],
|
|
8388
9731
|
branches: withSlotSources(
|
|
@@ -8402,7 +9745,7 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
8402
9745
|
location: range
|
|
8403
9746
|
},
|
|
8404
9747
|
statusCodeReading: {
|
|
8405
|
-
reading:
|
|
9748
|
+
reading: absentReading3,
|
|
8406
9749
|
libraryDefault: pattern.defaultStatusCode
|
|
8407
9750
|
},
|
|
8408
9751
|
effects: body.effects ?? [],
|
|
@@ -8412,6 +9755,14 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
8412
9755
|
}
|
|
8413
9756
|
],
|
|
8414
9757
|
body.provenance
|
|
9758
|
+
).concat(
|
|
9759
|
+
raisedStatusBranches(
|
|
9760
|
+
pattern,
|
|
9761
|
+
bodyRead.storage?.patterns ?? [],
|
|
9762
|
+
body.extraEffects,
|
|
9763
|
+
raises2.handlers,
|
|
9764
|
+
range
|
|
9765
|
+
)
|
|
8415
9766
|
),
|
|
8416
9767
|
bodyContent: body.bodyContent ?? "absent",
|
|
8417
9768
|
dependencyCalls: [],
|
|
@@ -9241,6 +10592,7 @@ import {
|
|
|
9241
10592
|
placeArgTargets as placeArgTargets2,
|
|
9242
10593
|
placeCalleeParameters as placeCalleeParameters2,
|
|
9243
10594
|
placeCalls as placeCalls2,
|
|
10595
|
+
placePossibleCalls as placePossibleCalls2,
|
|
9244
10596
|
placeReceiverClasses,
|
|
9245
10597
|
recordParameterGaps as recordParameterGaps2,
|
|
9246
10598
|
summaryIdFromParts,
|
|
@@ -9252,11 +10604,13 @@ import {
|
|
|
9252
10604
|
composeWrappers,
|
|
9253
10605
|
createCacheLayer,
|
|
9254
10606
|
createTimer,
|
|
10607
|
+
extractionCacheDirFor,
|
|
9255
10608
|
extractionConfigStamp,
|
|
9256
10609
|
KeptParses,
|
|
9257
10610
|
matchesTestFileName as matchesTestFileName3,
|
|
9258
10611
|
moduleInitStructure,
|
|
9259
10612
|
noopTimer,
|
|
10613
|
+
ownersWhereWritten,
|
|
9260
10614
|
runDigest,
|
|
9261
10615
|
stampModuleImports,
|
|
9262
10616
|
stampModules
|
|
@@ -9311,7 +10665,7 @@ var EXIT_METHODS = /* @__PURE__ */ new Map([
|
|
|
9311
10665
|
var DEFERRED_BODY_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
9312
10666
|
function exitSites(root) {
|
|
9313
10667
|
const found = [];
|
|
9314
|
-
walkDescendants4(root, null, {
|
|
10668
|
+
walkDescendants4(root, null, children, {
|
|
9315
10669
|
at: (node) => {
|
|
9316
10670
|
const site = node.type === "call" ? exitSiteAt(node) : null;
|
|
9317
10671
|
if (site !== null) {
|
|
@@ -9428,7 +10782,7 @@ function eventsOf(args, called, callbacks, db) {
|
|
|
9428
10782
|
import path3 from "path";
|
|
9429
10783
|
import { SKIP_CHILDREN as SKIP_CHILDREN5, walkDescendants as walkDescendants5 } from "@suss/extractor";
|
|
9430
10784
|
function emitRequireFacts(db, filePath, root, known) {
|
|
9431
|
-
walkDescendants5(root, null, {
|
|
10785
|
+
walkDescendants5(root, null, children, {
|
|
9432
10786
|
at: (node) => {
|
|
9433
10787
|
const target = requiredRelativePath(node);
|
|
9434
10788
|
if (target === null) {
|
|
@@ -9467,8 +10821,10 @@ import {
|
|
|
9467
10821
|
placeArgTargets,
|
|
9468
10822
|
placeCalleeParameters,
|
|
9469
10823
|
placeCalls,
|
|
10824
|
+
placePossibleCalls,
|
|
9470
10825
|
ReceiverClasses,
|
|
9471
10826
|
recordParameterGaps,
|
|
10827
|
+
StopList,
|
|
9472
10828
|
TargetPlacements,
|
|
9473
10829
|
unfollowedCallGap,
|
|
9474
10830
|
worthRecording
|
|
@@ -9510,7 +10866,7 @@ function optionParserFlagReads(method, parameterNames3) {
|
|
|
9510
10866
|
return reads;
|
|
9511
10867
|
}
|
|
9512
10868
|
function walkBody2(body, at) {
|
|
9513
|
-
walkDescendants6(body, null, {
|
|
10869
|
+
walkDescendants6(body, null, children, {
|
|
9514
10870
|
at,
|
|
9515
10871
|
into: (node) => DEFERRED_BODY_TYPES4.has(node.type) ? SKIP_CHILDREN6 : null
|
|
9516
10872
|
});
|
|
@@ -9526,7 +10882,7 @@ function flagsDeclaredIn(construction) {
|
|
|
9526
10882
|
return [];
|
|
9527
10883
|
}
|
|
9528
10884
|
const flags = [];
|
|
9529
|
-
walkDescendants6(block, null, {
|
|
10885
|
+
walkDescendants6(block, null, children, {
|
|
9530
10886
|
at: (node) => {
|
|
9531
10887
|
if (node.type !== "call" || field(node, "method")?.text !== "on") {
|
|
9532
10888
|
return;
|
|
@@ -9541,8 +10897,8 @@ function flagsDeclaredIn(construction) {
|
|
|
9541
10897
|
return flags;
|
|
9542
10898
|
}
|
|
9543
10899
|
function flagOf(args) {
|
|
9544
|
-
const written = args.map((arg) => stringLiteralValue(arg)).filter((
|
|
9545
|
-
const chosen = written.find((
|
|
10900
|
+
const written = args.map((arg) => stringLiteralValue(arg)).filter((text3) => text3 !== null && text3.startsWith("-"));
|
|
10901
|
+
const chosen = written.find((text3) => text3.startsWith("--")) ?? written[0] ?? null;
|
|
9546
10902
|
if (chosen === null) {
|
|
9547
10903
|
return null;
|
|
9548
10904
|
}
|
|
@@ -9584,10 +10940,9 @@ import {
|
|
|
9584
10940
|
calleeOutcomeOf,
|
|
9585
10941
|
calleeOutcomes,
|
|
9586
10942
|
classMemberName as classMemberName3,
|
|
9587
|
-
couldBeSettled
|
|
10943
|
+
couldBeSettled,
|
|
10944
|
+
dispatchOutcomes
|
|
9588
10945
|
} from "@suss/resolution";
|
|
9589
|
-
var DYNAMIC_SEND_NAMES = /* @__PURE__ */ new Set(["send", "public_send", "__send__"]);
|
|
9590
|
-
var INVOKES_RECEIVER = "call";
|
|
9591
10946
|
var STOP_FOR = {
|
|
9592
10947
|
severalSources: "multipleSources",
|
|
9593
10948
|
outsideRun: "outsideRun",
|
|
@@ -9604,6 +10959,11 @@ var UNSETTLED_ON_SELF = {
|
|
|
9604
10959
|
reason: "noDeclaration",
|
|
9605
10960
|
matchByName: true
|
|
9606
10961
|
};
|
|
10962
|
+
var SELF_CALL_IN_MIXIN = {
|
|
10963
|
+
kind: "stopped",
|
|
10964
|
+
reason: "selfCallInMixin",
|
|
10965
|
+
matchByName: true
|
|
10966
|
+
};
|
|
9607
10967
|
function followed(target) {
|
|
9608
10968
|
return { kind: "followed", target };
|
|
9609
10969
|
}
|
|
@@ -9665,6 +11025,57 @@ function loaderSourceCallee(call, site, ctx) {
|
|
|
9665
11025
|
}
|
|
9666
11026
|
return null;
|
|
9667
11027
|
}
|
|
11028
|
+
function dispatchedCalls(calls, ctx) {
|
|
11029
|
+
const byKey = new Map(
|
|
11030
|
+
calls.map((one) => [nodeId(one.site.file, one.call), one])
|
|
11031
|
+
);
|
|
11032
|
+
const found = /* @__PURE__ */ new Map();
|
|
11033
|
+
const outcomes = dispatchOutcomes(ctx.facts, [...byKey.keys()], RUBY_PROGRAM);
|
|
11034
|
+
for (const [key, outcome] of outcomes) {
|
|
11035
|
+
const asked = byKey.get(key);
|
|
11036
|
+
if (asked === void 0) {
|
|
11037
|
+
continue;
|
|
11038
|
+
}
|
|
11039
|
+
const fits = sendFits(asked.call, asked.site, ctx);
|
|
11040
|
+
const targets = /* @__PURE__ */ new Map();
|
|
11041
|
+
for (const fn of outcome.functions) {
|
|
11042
|
+
for (const target of reachedThrough(fn, outcome.through, ctx)) {
|
|
11043
|
+
if (fits(target)) {
|
|
11044
|
+
targets.set(nodeId(target.file, target.node), target);
|
|
11045
|
+
}
|
|
11046
|
+
}
|
|
11047
|
+
}
|
|
11048
|
+
found.set(asked.call.id, {
|
|
11049
|
+
targets: [...targets.values()],
|
|
11050
|
+
certain: outcome.certain && outcome.functions.every((fn) => ctx.definitions.has(fn)),
|
|
11051
|
+
unresolved: outcome.unresolved
|
|
11052
|
+
});
|
|
11053
|
+
}
|
|
11054
|
+
return found;
|
|
11055
|
+
}
|
|
11056
|
+
function reachedThrough(key, through, ctx) {
|
|
11057
|
+
const own = ctx.definitions.get(key);
|
|
11058
|
+
if (own !== void 0) {
|
|
11059
|
+
return [own];
|
|
11060
|
+
}
|
|
11061
|
+
return (through.get(key) ?? []).flatMap((inner) => {
|
|
11062
|
+
const target = ctx.definitions.get(inner);
|
|
11063
|
+
return target === void 0 ? [] : [target];
|
|
11064
|
+
});
|
|
11065
|
+
}
|
|
11066
|
+
function sendFits(call, site, ctx) {
|
|
11067
|
+
const method = calleeMethodName(call);
|
|
11068
|
+
if (method === void 0 || !SENDING_METHODS.has(method)) {
|
|
11069
|
+
return () => true;
|
|
11070
|
+
}
|
|
11071
|
+
const receiver = field(call, "receiver");
|
|
11072
|
+
const toClass = receiver === null || receiver.type === "self" ? inClassMethod(site, ctx) : readsOffClassItself(
|
|
11073
|
+
receiver,
|
|
11074
|
+
readKey(site.file, receiver, site.method),
|
|
11075
|
+
classLocalsFor(call, site)
|
|
11076
|
+
);
|
|
11077
|
+
return (target) => definesClassMethod(target.node, ctx.bodyBlocks) === toClass;
|
|
11078
|
+
}
|
|
9668
11079
|
function outcomeFor(key, ctx, read) {
|
|
9669
11080
|
return read?.outcomes.get(key) ?? calleeOutcomeOf(ctx.facts, key, RUBY_PROGRAM);
|
|
9670
11081
|
}
|
|
@@ -9678,14 +11089,16 @@ function readsAsACall(call, site, ctx, read) {
|
|
|
9678
11089
|
return false;
|
|
9679
11090
|
}
|
|
9680
11091
|
const outcome = outcomeFor(spelling.key, ctx, read);
|
|
9681
|
-
return outcome.kind === "function" || outcome.kind === "object"
|
|
11092
|
+
return outcome.kind === "function" || outcome.kind === "object" || objectsBehind(ctx.facts, spelling.key).some(
|
|
11093
|
+
(key) => ctx.classNames.has(key)
|
|
11094
|
+
);
|
|
9682
11095
|
}
|
|
9683
11096
|
function spellingFor2(call, site) {
|
|
9684
11097
|
const methodName = calleeMethodName(call);
|
|
9685
11098
|
if (methodName === void 0) {
|
|
9686
11099
|
return { kind: "stopped", reason: "noDeclaration" };
|
|
9687
11100
|
}
|
|
9688
|
-
if (
|
|
11101
|
+
if (SENDING_METHODS.has(methodName)) {
|
|
9689
11102
|
return { kind: "stopped", reason: "unsettledValue" };
|
|
9690
11103
|
}
|
|
9691
11104
|
const receiver = field(call, "receiver");
|
|
@@ -9731,7 +11144,7 @@ function asCallee(spelling, outcome, site, ctx) {
|
|
|
9731
11144
|
return methodOnObject(spelling, objectKey, ctx);
|
|
9732
11145
|
}
|
|
9733
11146
|
if (outcome.kind === "function") {
|
|
9734
|
-
return spelling.method ===
|
|
11147
|
+
return spelling.method === INVOKE_METHOD ? functionCallee(outcome.key, ctx) : NO_DECLARATION;
|
|
9735
11148
|
}
|
|
9736
11149
|
return stop(STOP_FOR[outcome.kind] ?? "noDeclaration");
|
|
9737
11150
|
}
|
|
@@ -9775,7 +11188,7 @@ function scopeOn(qualifiedName, method, ctx) {
|
|
|
9775
11188
|
return null;
|
|
9776
11189
|
}
|
|
9777
11190
|
function fromCaller(spelling, parameterKey, site) {
|
|
9778
|
-
if (!parameterKey.startsWith(`${site.owner}#`) || spelling.method !==
|
|
11191
|
+
if (!parameterKey.startsWith(`${site.owner}#`) || spelling.method !== INVOKE_METHOD) {
|
|
9779
11192
|
return stop("unsettledValue");
|
|
9780
11193
|
}
|
|
9781
11194
|
return stop("callerSupplied");
|
|
@@ -9792,22 +11205,27 @@ function leavesRoomForATopLevelMethod(reason) {
|
|
|
9792
11205
|
return reason === "noDeclaration" || reason === "outsideRun";
|
|
9793
11206
|
}
|
|
9794
11207
|
function resolveImplicitSelf(methodName, site, ctx) {
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
site.enclosingQualifiedName,
|
|
9799
|
-
methodName,
|
|
9800
|
-
ctx
|
|
9801
|
-
);
|
|
9802
|
-
if (onClass.kind === "followed" || !leavesRoomForATopLevelMethod(onClass.reason)) {
|
|
9803
|
-
return onClass;
|
|
9804
|
-
}
|
|
11208
|
+
const onClass = site.enclosingQualifiedName === null ? null : methodOnSelf(site, site.enclosingQualifiedName, methodName, ctx);
|
|
11209
|
+
if (onClass !== null && (onClass.kind === "followed" || !leavesRoomForATopLevelMethod(onClass.reason))) {
|
|
11210
|
+
return onClass;
|
|
9805
11211
|
}
|
|
11212
|
+
const readWholeAncestry = onClass?.kind === "stopped" && onClass.reason === "noDeclaration";
|
|
9806
11213
|
const topLevel = resolveTopLevelName(methodName, ctx);
|
|
9807
11214
|
if (topLevel.kind === "followed" || topLevel.reason !== "noDeclaration") {
|
|
9808
11215
|
return topLevel;
|
|
9809
11216
|
}
|
|
9810
|
-
|
|
11217
|
+
if (declaredForTheOtherSelf(site, methodName, ctx)) {
|
|
11218
|
+
return NO_DECLARATION;
|
|
11219
|
+
}
|
|
11220
|
+
return readWholeAncestry && !OBJECT_METHODS.has(methodName) && inModuleInstanceMethod(site, ctx) ? SELF_CALL_IN_MIXIN : UNSETTLED_ON_SELF;
|
|
11221
|
+
}
|
|
11222
|
+
function inModuleInstanceMethod(site, ctx) {
|
|
11223
|
+
const qualifiedName = site.enclosingQualifiedName;
|
|
11224
|
+
if (qualifiedName === null || site.method === null || inClassMethod(site, ctx)) {
|
|
11225
|
+
return false;
|
|
11226
|
+
}
|
|
11227
|
+
const own = ctx.ancestries.get(qualifiedName)?.[0];
|
|
11228
|
+
return own?.type === "bodies" && own.blocks.every((block) => block.info.kind === "module");
|
|
9811
11229
|
}
|
|
9812
11230
|
function declaredForTheOtherSelf(site, methodName, ctx) {
|
|
9813
11231
|
const qualifiedName = site.enclosingQualifiedName;
|
|
@@ -9850,12 +11268,16 @@ function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
|
9850
11268
|
return followed(reachedMethod(found.method, found.block, methodName));
|
|
9851
11269
|
}
|
|
9852
11270
|
if (found.type === "unsettled") {
|
|
9853
|
-
return stop(
|
|
9854
|
-
found.cause === "dynamicDefine" ? "definedAtLoadTime" : "outsideRun"
|
|
9855
|
-
);
|
|
11271
|
+
return stop(unsettledReason(found));
|
|
9856
11272
|
}
|
|
9857
11273
|
return NO_DECLARATION;
|
|
9858
11274
|
}
|
|
11275
|
+
function unsettledReason(found) {
|
|
11276
|
+
if (found.cause === "dynamicDefine") {
|
|
11277
|
+
return "definedAtLoadTime";
|
|
11278
|
+
}
|
|
11279
|
+
return found.definedLater === true ? "behindUnreadAncestor" : "outsideRun";
|
|
11280
|
+
}
|
|
9859
11281
|
function reachedMethod(node, block, name) {
|
|
9860
11282
|
return {
|
|
9861
11283
|
file: block.file,
|
|
@@ -9902,6 +11324,7 @@ async function reachedFunctions(seeds, options) {
|
|
|
9902
11324
|
const targetsByKey = /* @__PURE__ */ new Map();
|
|
9903
11325
|
const stopsByKey = /* @__PURE__ */ new Map();
|
|
9904
11326
|
const argTargetsByKey = /* @__PURE__ */ new Map();
|
|
11327
|
+
const possibleTargetsByKey = /* @__PURE__ */ new Map();
|
|
9905
11328
|
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
9906
11329
|
const propertyReadsByKey = /* @__PURE__ */ new Map();
|
|
9907
11330
|
const receiverClassesByKey = /* @__PURE__ */ new Map();
|
|
@@ -9960,6 +11383,7 @@ async function reachedFunctions(seeds, options) {
|
|
|
9960
11383
|
}
|
|
9961
11384
|
targetsByKey.set(key, scan.targets);
|
|
9962
11385
|
argTargetsByKey.set(key, scan.argTargets);
|
|
11386
|
+
possibleTargetsByKey.set(key, scan.possibleTargets);
|
|
9963
11387
|
if (scan.propertyReads.size > 0) {
|
|
9964
11388
|
propertyReadsByKey.set(key, scan.propertyReads);
|
|
9965
11389
|
}
|
|
@@ -10023,6 +11447,7 @@ async function reachedFunctions(seeds, options) {
|
|
|
10023
11447
|
}
|
|
10024
11448
|
placeCalls(summary2, targetsByKey.get(key));
|
|
10025
11449
|
placeArgTargets(summary2, argTargetsByKey.get(key));
|
|
11450
|
+
placePossibleCalls(summary2, possibleTargetsByKey.get(key));
|
|
10026
11451
|
placeCalleeParameters(summary2, parameterCallsByKey.get(key));
|
|
10027
11452
|
return summary2;
|
|
10028
11453
|
};
|
|
@@ -10045,6 +11470,7 @@ async function reachedFunctions(seeds, options) {
|
|
|
10045
11470
|
targetsByKey,
|
|
10046
11471
|
stopsByKey,
|
|
10047
11472
|
argTargetsByKey,
|
|
11473
|
+
possibleTargetsByKey,
|
|
10048
11474
|
parameterCallsByKey,
|
|
10049
11475
|
propertyReadsByKey,
|
|
10050
11476
|
receiverClassesByKey,
|
|
@@ -10074,36 +11500,70 @@ function batchedReads(round, options) {
|
|
|
10074
11500
|
const bodies = round.flatMap(
|
|
10075
11501
|
(entry) => "source" in entry ? [{ key: entry.key, ...bodyOf2(entry.source, options) }] : []
|
|
10076
11502
|
);
|
|
10077
|
-
const
|
|
10078
|
-
|
|
10079
|
-
|
|
11503
|
+
const written = bodies.flatMap((body) => body.written);
|
|
11504
|
+
const spellings = calleeSpellings(written, options.context);
|
|
11505
|
+
const dispatched = dispatchedAmong(written, spellings, options.context);
|
|
11506
|
+
return new Map(
|
|
11507
|
+
bodies.map((body) => [body.key, { ...body, spellings, dispatched }])
|
|
11508
|
+
);
|
|
11509
|
+
}
|
|
11510
|
+
function dispatchedAmong(written, spellings, ctx) {
|
|
11511
|
+
return dispatchedCalls(
|
|
11512
|
+
written.filter(({ call, site }) => {
|
|
11513
|
+
if (!mayRunAnEntry(call)) {
|
|
11514
|
+
return false;
|
|
11515
|
+
}
|
|
11516
|
+
const outcome = resolveCallee(call, site, ctx, spellings);
|
|
11517
|
+
return outcome.kind === "stopped" && outcome.reason !== "callerSupplied";
|
|
11518
|
+
}),
|
|
11519
|
+
ctx
|
|
10080
11520
|
);
|
|
10081
|
-
|
|
11521
|
+
}
|
|
11522
|
+
function mayRunAnEntry(call) {
|
|
11523
|
+
const sent = calleeMethodName(call);
|
|
11524
|
+
if (sent !== void 0 && SENDING_METHODS.has(sent)) {
|
|
11525
|
+
return true;
|
|
11526
|
+
}
|
|
11527
|
+
if (call.type !== "call" || field(call, "receiver") === null) {
|
|
11528
|
+
return false;
|
|
11529
|
+
}
|
|
11530
|
+
const method = field(call, "method");
|
|
11531
|
+
return method === null || method.text === INVOKE_METHOD;
|
|
10082
11532
|
}
|
|
10083
11533
|
function scanOnce(source, key, options, read, charges) {
|
|
10084
11534
|
const ledger = options.ledger;
|
|
10085
11535
|
if (ledger === void 0) {
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
11536
|
+
return scanBody(
|
|
11537
|
+
source,
|
|
11538
|
+
options.context,
|
|
11539
|
+
options,
|
|
11540
|
+
read ?? ownReads(source, options)
|
|
11541
|
+
);
|
|
10091
11542
|
}
|
|
10092
11543
|
return ledger.charging(chargeOf(charges, key), () => {
|
|
10093
11544
|
forgetEvaluations(options.context.facts);
|
|
10094
11545
|
ledger.readFile(source.file);
|
|
10095
|
-
const
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
11546
|
+
const scan = scanBody(
|
|
11547
|
+
source,
|
|
11548
|
+
options.context,
|
|
11549
|
+
options,
|
|
11550
|
+
ownReads(source, options)
|
|
11551
|
+
);
|
|
10101
11552
|
for (const target of scan.followed) {
|
|
10102
11553
|
ledger.touchKey(keyOf(target));
|
|
10103
11554
|
}
|
|
10104
11555
|
return scan;
|
|
10105
11556
|
});
|
|
10106
11557
|
}
|
|
11558
|
+
function ownReads(source, options) {
|
|
11559
|
+
const body = bodyOf2(source, options);
|
|
11560
|
+
const spellings = calleeSpellings(body.written, options.context);
|
|
11561
|
+
return {
|
|
11562
|
+
...body,
|
|
11563
|
+
spellings,
|
|
11564
|
+
dispatched: dispatchedAmong(body.written, spellings, options.context)
|
|
11565
|
+
};
|
|
11566
|
+
}
|
|
10107
11567
|
function storedScan(scan) {
|
|
10108
11568
|
return {
|
|
10109
11569
|
followed: scan.followed.map((target) => ({
|
|
@@ -10119,6 +11579,10 @@ function storedScan(scan) {
|
|
|
10119
11579
|
callee,
|
|
10120
11580
|
[...byPosition]
|
|
10121
11581
|
]),
|
|
11582
|
+
possibleTargets: [...scan.possibleTargets].map(([callee, places]) => [
|
|
11583
|
+
callee,
|
|
11584
|
+
[...places]
|
|
11585
|
+
]),
|
|
10122
11586
|
parameterCalls: [...scan.parameterCalls],
|
|
10123
11587
|
passedPositions: [...scan.passedPositions],
|
|
10124
11588
|
propertyReads: [...scan.propertyReads],
|
|
@@ -10136,6 +11600,7 @@ function scanFromStored(stored, functionFor) {
|
|
|
10136
11600
|
new Map(byPosition)
|
|
10137
11601
|
])
|
|
10138
11602
|
),
|
|
11603
|
+
possibleTargets: new Map(stored.possibleTargets),
|
|
10139
11604
|
parameterCalls: stored.parameterCalls,
|
|
10140
11605
|
passedPositions: new Set(stored.passedPositions),
|
|
10141
11606
|
propertyReads: new Set(stored.propertyReads),
|
|
@@ -10164,6 +11629,7 @@ var EMPTY_SCAN = {
|
|
|
10164
11629
|
stops: [],
|
|
10165
11630
|
targets: /* @__PURE__ */ new Map(),
|
|
10166
11631
|
argTargets: /* @__PURE__ */ new Map(),
|
|
11632
|
+
possibleTargets: /* @__PURE__ */ new Map(),
|
|
10167
11633
|
parameterCalls: [],
|
|
10168
11634
|
passedPositions: /* @__PURE__ */ new Set(),
|
|
10169
11635
|
propertyReads: /* @__PURE__ */ new Set(),
|
|
@@ -10185,7 +11651,7 @@ function bodyOf2(source, options) {
|
|
|
10185
11651
|
const file = run?.fileOf(call) ?? source.file;
|
|
10186
11652
|
return file === source.file ? site : { ...site, file, method: enclosingDefinition(call) };
|
|
10187
11653
|
};
|
|
10188
|
-
const written =
|
|
11654
|
+
const written = callsWrittenIn2(source.node, run, options);
|
|
10189
11655
|
const calls = callsReported(
|
|
10190
11656
|
written,
|
|
10191
11657
|
(call) => mightReadAsACall(call, siteFor(call), options.context)
|
|
@@ -10199,7 +11665,7 @@ function bodyOf2(source, options) {
|
|
|
10199
11665
|
written: calls.map((call) => ({ call, site: siteFor(call) }))
|
|
10200
11666
|
};
|
|
10201
11667
|
}
|
|
10202
|
-
function
|
|
11668
|
+
function callsWrittenIn2(node, run, options) {
|
|
10203
11669
|
if (run !== null) {
|
|
10204
11670
|
return exampleCalls(run, options.inheritedMethods).filter(
|
|
10205
11671
|
(call) => !run.isRunnerCall(call)
|
|
@@ -10251,7 +11717,7 @@ function scanBody(source, ctx, options, read) {
|
|
|
10251
11717
|
);
|
|
10252
11718
|
const ownParameters = positionalParameters(source.node).map((p) => p.name);
|
|
10253
11719
|
const followed2 = [];
|
|
10254
|
-
const stops =
|
|
11720
|
+
const stops = new StopList();
|
|
10255
11721
|
const placements = new TargetPlacements();
|
|
10256
11722
|
const receiverClasses = new ReceiverClasses();
|
|
10257
11723
|
const parameterCalls = [];
|
|
@@ -10266,6 +11732,12 @@ function scanBody(source, ctx, options, read) {
|
|
|
10266
11732
|
}
|
|
10267
11733
|
const argNodes = bodyStatements(args);
|
|
10268
11734
|
for (let position = 0; position < argNodes.length; position += 1) {
|
|
11735
|
+
if (argNodes[position]?.type === LAMBDA_TYPE) {
|
|
11736
|
+
if (calleeKey !== null) {
|
|
11737
|
+
passedPositions.add(`${calleeKey}#${position}`);
|
|
11738
|
+
}
|
|
11739
|
+
continue;
|
|
11740
|
+
}
|
|
10269
11741
|
const symbol = methodReferenceSymbol(argNodes[position]);
|
|
10270
11742
|
if (symbol === null) {
|
|
10271
11743
|
continue;
|
|
@@ -10303,11 +11775,35 @@ function scanBody(source, ctx, options, read) {
|
|
|
10303
11775
|
span: spanOf(target.node)
|
|
10304
11776
|
});
|
|
10305
11777
|
};
|
|
11778
|
+
const follow = (target) => {
|
|
11779
|
+
const key = keyOf(target);
|
|
11780
|
+
if (!seen.has(key)) {
|
|
11781
|
+
seen.add(key);
|
|
11782
|
+
followed2.push(target);
|
|
11783
|
+
}
|
|
11784
|
+
};
|
|
10306
11785
|
for (const call of calls) {
|
|
10307
11786
|
const callee = calleeText(call);
|
|
10308
11787
|
const callSite = read.siteFor(call);
|
|
10309
11788
|
const predicate = read.predicateOf(call);
|
|
10310
|
-
const
|
|
11789
|
+
const settled = predicate === null ? resolveCallee(call, callSite, ctx, read.spellings) : resolveMethodOn(predicate.target, predicate.method, callSite, ctx);
|
|
11790
|
+
const dispatched = settled.kind === "stopped" ? read.dispatched.get(call.id) : void 0;
|
|
11791
|
+
const [only] = dispatched?.targets ?? [];
|
|
11792
|
+
if (dispatched !== void 0 && only !== void 0 && !dispatched.certain) {
|
|
11793
|
+
dispatched.targets.forEach(follow);
|
|
11794
|
+
placements.placePossible(
|
|
11795
|
+
callee,
|
|
11796
|
+
dispatched.targets.map((target) => ({
|
|
11797
|
+
file: displayPathOf(target.file),
|
|
11798
|
+
span: spanOf(target.node)
|
|
11799
|
+
}))
|
|
11800
|
+
);
|
|
11801
|
+
if (dispatched.unresolved > 0) {
|
|
11802
|
+
stops.add({ callee, reason: "unsettledValue" });
|
|
11803
|
+
}
|
|
11804
|
+
continue;
|
|
11805
|
+
}
|
|
11806
|
+
const outcome = only === void 0 ? settled : { kind: "followed", target: only };
|
|
10311
11807
|
if (options.storage !== void 0) {
|
|
10312
11808
|
for (const callback of callbacksReached(
|
|
10313
11809
|
call,
|
|
@@ -10335,11 +11831,13 @@ function scanBody(source, ctx, options, read) {
|
|
|
10335
11831
|
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
10336
11832
|
);
|
|
10337
11833
|
if (outcome.kind === "stopped") {
|
|
10338
|
-
const stopKey = `${outcome.reason}:${callee}`;
|
|
10339
11834
|
const claimed = options.storage !== void 0 && storageClaims(call, callSite.file, options.storage, callSite.method);
|
|
10340
|
-
if (!
|
|
10341
|
-
|
|
10342
|
-
|
|
11835
|
+
if (!claimed && worthRecording(outcome.reason)) {
|
|
11836
|
+
stops.add({
|
|
11837
|
+
callee,
|
|
11838
|
+
reason: outcome.reason,
|
|
11839
|
+
...resultUsedAsValue(call) ? { usedAsValue: true } : {}
|
|
11840
|
+
});
|
|
10343
11841
|
}
|
|
10344
11842
|
if (outcome.reason === "callerSupplied" && !parameterCallsSeen.has(callee)) {
|
|
10345
11843
|
const receiverName = field(call, "receiver")?.text;
|
|
@@ -10354,17 +11852,14 @@ function scanBody(source, ctx, options, read) {
|
|
|
10354
11852
|
if (isArglessReceiverCall(call)) {
|
|
10355
11853
|
keptArgless.add(call.id);
|
|
10356
11854
|
}
|
|
10357
|
-
|
|
10358
|
-
if (!seen.has(key)) {
|
|
10359
|
-
seen.add(key);
|
|
10360
|
-
followed2.push(outcome.target);
|
|
10361
|
-
}
|
|
11855
|
+
follow(outcome.target);
|
|
10362
11856
|
}
|
|
10363
11857
|
return {
|
|
10364
11858
|
followed: followed2,
|
|
10365
|
-
stops,
|
|
11859
|
+
stops: stops.stops,
|
|
10366
11860
|
targets: placements.targets,
|
|
10367
11861
|
argTargets: placements.argTargets,
|
|
11862
|
+
possibleTargets: placements.possibleTargets,
|
|
10368
11863
|
parameterCalls,
|
|
10369
11864
|
passedPositions,
|
|
10370
11865
|
propertyReads: propertyReadsAmong(read.argless, calls, keptArgless),
|
|
@@ -10396,9 +11891,9 @@ function propertyReadsAmong(argless, made, keptArgless) {
|
|
|
10396
11891
|
);
|
|
10397
11892
|
const reads = /* @__PURE__ */ new Set();
|
|
10398
11893
|
for (const call of argless) {
|
|
10399
|
-
const
|
|
10400
|
-
if (!kept.has(
|
|
10401
|
-
reads.add(
|
|
11894
|
+
const text3 = calleeText(call);
|
|
11895
|
+
if (!kept.has(text3)) {
|
|
11896
|
+
reads.add(text3);
|
|
10402
11897
|
}
|
|
10403
11898
|
}
|
|
10404
11899
|
return reads;
|
|
@@ -10836,7 +12331,7 @@ import { literalOf as literalOf10 } from "@suss/values";
|
|
|
10836
12331
|
function factoriesIn(packs) {
|
|
10837
12332
|
return packs.flatMap((pack) => pack.factories ?? []);
|
|
10838
12333
|
}
|
|
10839
|
-
function emitFactoryFacts(db, files,
|
|
12334
|
+
function emitFactoryFacts(db, files, isTestFile2, patterns, inflections) {
|
|
10840
12335
|
for (const pattern of patterns) {
|
|
10841
12336
|
const definitions = /* @__PURE__ */ new Map();
|
|
10842
12337
|
for (const { file, root } of files) {
|
|
@@ -10844,7 +12339,7 @@ function emitFactoryFacts(db, files, isTestFile, patterns, inflections) {
|
|
|
10844
12339
|
}
|
|
10845
12340
|
const classes = new FactoryClasses(db, definitions, inflections);
|
|
10846
12341
|
for (const { file, root } of files) {
|
|
10847
|
-
if (!
|
|
12342
|
+
if (!isTestFile2(file)) {
|
|
10848
12343
|
continue;
|
|
10849
12344
|
}
|
|
10850
12345
|
for (const [call, name] of buildsIn(db, root, pattern)) {
|
|
@@ -11338,7 +12833,7 @@ function replaysCleanly(units, discovered) {
|
|
|
11338
12833
|
async function extractRubyProject(options) {
|
|
11339
12834
|
const timer = options.onTiming !== void 0 ? createTimer() : noopTimer();
|
|
11340
12835
|
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
11341
|
-
options.cacheDir === null ? null : options.cacheDir ?? (options.projectRoot !== void 0 ?
|
|
12836
|
+
options.cacheDir === null ? null : options.cacheDir ?? (options.projectRoot !== void 0 ? extractionCacheDirFor(options.projectRoot) : null)
|
|
11342
12837
|
);
|
|
11343
12838
|
const layer = createCacheLayer(cacheDir);
|
|
11344
12839
|
const packsDigest = `${adapterStamp.packsDigest(
|
|
@@ -11359,10 +12854,13 @@ async function extractRubyProject(options) {
|
|
|
11359
12854
|
"cache.lookup",
|
|
11360
12855
|
() => layer.lookup(input)
|
|
11361
12856
|
);
|
|
12857
|
+
const composed = (stored) => composeWrappers(stored, {
|
|
12858
|
+
gapHandling: options.gapHandling ?? "permissive"
|
|
12859
|
+
});
|
|
11362
12860
|
if (lookup.kind === "hit") {
|
|
11363
12861
|
options.onCacheDiagnostic?.(lookup.diagnostic);
|
|
11364
12862
|
options.onTiming?.(timer.report());
|
|
11365
|
-
return { summaries: lookup.summaries, facts: new Database2() };
|
|
12863
|
+
return { summaries: composed(lookup.summaries), facts: new Database2() };
|
|
11366
12864
|
}
|
|
11367
12865
|
const plan = lookup.diagnostic.missReason === "files-changed" ? await timer.timeAsync("cache.plan", () => layer.plan(input)) : null;
|
|
11368
12866
|
if (plan !== null && plan.changed.size === 0 && plan.removed.size === 0) {
|
|
@@ -11372,7 +12870,7 @@ async function extractRubyProject(options) {
|
|
|
11372
12870
|
}
|
|
11373
12871
|
options.onCacheDiagnostic?.({ kind: "hit" });
|
|
11374
12872
|
options.onTiming?.(timer.report());
|
|
11375
|
-
return { summaries: plan.allSummaries(), facts: new Database2() };
|
|
12873
|
+
return { summaries: composed(plan.allSummaries()), facts: new Database2() };
|
|
11376
12874
|
}
|
|
11377
12875
|
const cache = { timer, cacheDir, layer, input };
|
|
11378
12876
|
const records = plan === null ? null : recordsToReplay(plan.roots, plan.changed, plan.removed);
|
|
@@ -11733,6 +13231,7 @@ async function runRuby(options, cache, previous) {
|
|
|
11733
13231
|
}
|
|
11734
13232
|
placeCalls2(summary, reached.targetsByKey.get(key));
|
|
11735
13233
|
placeArgTargets2(summary, reached.argTargetsByKey.get(key));
|
|
13234
|
+
placePossibleCalls2(summary, reached.possibleTargetsByKey.get(key));
|
|
11736
13235
|
placeCalleeParameters2(summary, reached.parameterCallsByKey.get(key));
|
|
11737
13236
|
placeReceiverClasses(summary, reached.receiverClassesByKey.get(key));
|
|
11738
13237
|
}
|
|
@@ -11779,16 +13278,20 @@ async function runRuby(options, cache, previous) {
|
|
|
11779
13278
|
);
|
|
11780
13279
|
return composeWrappers(summaries, { gapHandling });
|
|
11781
13280
|
});
|
|
13281
|
+
const walkedFiles = new Map(
|
|
13282
|
+
parsed.map(({ file }) => [displayPathOf(file), file])
|
|
13283
|
+
);
|
|
11782
13284
|
await timer.timeAsync("cache.write", async () => {
|
|
11783
|
-
if (cacheDir === null ||
|
|
13285
|
+
if (cacheDir === null || summaries.length === 0) {
|
|
11784
13286
|
return;
|
|
11785
13287
|
}
|
|
11786
13288
|
try {
|
|
11787
13289
|
await cache.layer.write(
|
|
11788
13290
|
cache.input,
|
|
11789
|
-
|
|
13291
|
+
summaries,
|
|
11790
13292
|
log === null || ledger === null || overBudget ? void 0 : rubyAttribution({
|
|
11791
|
-
|
|
13293
|
+
summaries,
|
|
13294
|
+
walkedFileOf: (locationFile) => walkedFiles.get(locationFile),
|
|
11792
13295
|
discoveries,
|
|
11793
13296
|
reached,
|
|
11794
13297
|
reuse,
|
|
@@ -11863,7 +13366,10 @@ function rubyAttribution(args) {
|
|
|
11863
13366
|
facts: args.facts.get(file) ?? { own: "", joined: "" }
|
|
11864
13367
|
}
|
|
11865
13368
|
}));
|
|
11866
|
-
return {
|
|
13369
|
+
return {
|
|
13370
|
+
roots,
|
|
13371
|
+
owners: ownersWhereWritten(args.summaries, args.walkedFileOf)
|
|
13372
|
+
};
|
|
11867
13373
|
}
|
|
11868
13374
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["vendor", "node_modules", "tmp", ".git"]);
|
|
11869
13375
|
function findRubyFiles(root) {
|
|
@@ -11937,8 +13443,8 @@ function displayNameOf(node) {
|
|
|
11937
13443
|
if (node.type === "class" || node.type === "module") {
|
|
11938
13444
|
return field(node, "name")?.text ?? node.text;
|
|
11939
13445
|
}
|
|
11940
|
-
const
|
|
11941
|
-
return
|
|
13446
|
+
const text3 = node.text.split("\n")[0].trim();
|
|
13447
|
+
return text3.length > 40 ? `${text3.slice(0, 40)}...` : text3;
|
|
11942
13448
|
}
|
|
11943
13449
|
function width(node) {
|
|
11944
13450
|
return node.endIndex - node.startIndex;
|
|
@@ -11968,14 +13474,14 @@ var RubyWhySession = class {
|
|
|
11968
13474
|
* The smallest expression on `line` of `file` whose text is exactly
|
|
11969
13475
|
* `text`, or null.
|
|
11970
13476
|
*/
|
|
11971
|
-
findExpression(file, line,
|
|
13477
|
+
findExpression(file, line, text3) {
|
|
11972
13478
|
const root = this.rootOf(file);
|
|
11973
13479
|
if (root === null) {
|
|
11974
13480
|
return null;
|
|
11975
13481
|
}
|
|
11976
13482
|
let found = null;
|
|
11977
13483
|
const visit = (node) => {
|
|
11978
|
-
if (node.startPosition.row + 1 === line && node.text ===
|
|
13484
|
+
if (node.startPosition.row + 1 === line && node.text === text3) {
|
|
11979
13485
|
if (found === null || width(node) <= width(found)) {
|
|
11980
13486
|
found = node;
|
|
11981
13487
|
}
|
|
@@ -12080,7 +13586,7 @@ function requireTarget(node) {
|
|
|
12080
13586
|
}
|
|
12081
13587
|
function requireSitesIn(root, file) {
|
|
12082
13588
|
const found = [];
|
|
12083
|
-
walkDescendants7(root, null, {
|
|
13589
|
+
walkDescendants7(root, null, children, {
|
|
12084
13590
|
at: (node) => {
|
|
12085
13591
|
const target = requireTarget(node);
|
|
12086
13592
|
if (target !== null) {
|
|
@@ -12130,6 +13636,7 @@ export {
|
|
|
12130
13636
|
bindEvaluator,
|
|
12131
13637
|
bodyStatements,
|
|
12132
13638
|
booleanLiteralValue,
|
|
13639
|
+
children,
|
|
12133
13640
|
collectFileConstants,
|
|
12134
13641
|
createConstantFileCache,
|
|
12135
13642
|
createFileCache,
|