@suss/adapter-ruby 0.34.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1442 -365
- package/dist/index.d.cts +86 -13
- package/dist/index.d.ts +86 -13
- package/dist/index.js +1345 -261
- 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
|
},
|
|
@@ -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 {
|
|
@@ -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;
|
|
@@ -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,71 @@ 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
|
+
]);
|
|
568
713
|
var CONSTRUCTOR = "new";
|
|
569
714
|
|
|
570
715
|
// src/facts/resolve.ts
|
|
@@ -612,7 +757,13 @@ function resolveEnvObjects(db, objects) {
|
|
|
612
757
|
}
|
|
613
758
|
function writtenValueOf(db, key) {
|
|
614
759
|
resolveValues(db, [key]);
|
|
615
|
-
return sharedWrittenValueOf(
|
|
760
|
+
return sharedWrittenValueOf(
|
|
761
|
+
db,
|
|
762
|
+
key,
|
|
763
|
+
(keys) => resolveValues(db, keys),
|
|
764
|
+
void 0,
|
|
765
|
+
answersFromTestsLeftOut(key, isTestFile)
|
|
766
|
+
);
|
|
616
767
|
}
|
|
617
768
|
function writtenValueUnder(db, key, site) {
|
|
618
769
|
const program = resolutionUnderProgram(RUBY_RULES);
|
|
@@ -652,7 +803,7 @@ function isFieldOf(parent, node, name) {
|
|
|
652
803
|
function namedAt(parent, node) {
|
|
653
804
|
return isFieldOf(parent, node, "name");
|
|
654
805
|
}
|
|
655
|
-
function
|
|
806
|
+
function always2() {
|
|
656
807
|
return true;
|
|
657
808
|
}
|
|
658
809
|
var SPELLS_A_NAME = {
|
|
@@ -667,14 +818,14 @@ var SPELLS_A_NAME = {
|
|
|
667
818
|
hash_splat_parameter: namedAt,
|
|
668
819
|
keyword_parameter: namedAt,
|
|
669
820
|
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,
|
|
821
|
+
destructured_parameter: always2,
|
|
822
|
+
left_assignment_list: always2,
|
|
823
|
+
rest_assignment: always2,
|
|
824
|
+
destructured_left_assignment: always2,
|
|
825
|
+
exception_variable: always2,
|
|
826
|
+
undef: always2,
|
|
827
|
+
alias: always2,
|
|
828
|
+
...Object.fromEntries(PARAMETER_LIST_TYPES.map((type) => [type, always2]))
|
|
678
829
|
};
|
|
679
830
|
function addNamesUnder(node, names) {
|
|
680
831
|
if (node === null) {
|
|
@@ -1493,9 +1644,7 @@ function emitKeyedElement(emitter, node, parent) {
|
|
|
1493
1644
|
if (object === null || isWriteTarget(node, parent)) {
|
|
1494
1645
|
return;
|
|
1495
1646
|
}
|
|
1496
|
-
const index = node.
|
|
1497
|
-
(child) => child !== null && child.id !== object.id
|
|
1498
|
-
);
|
|
1647
|
+
const index = children(node).find((child) => child.id !== object.id);
|
|
1499
1648
|
if (index !== void 0) {
|
|
1500
1649
|
emitKeyedRead(emitter, node, object, index, node);
|
|
1501
1650
|
emitWrittenKeyRead(emitter, node, object, index);
|
|
@@ -1536,17 +1685,17 @@ function fallbackBranchesOf(node, type, enclosing) {
|
|
|
1536
1685
|
}
|
|
1537
1686
|
return [left, right].filter((side) => !raises(side, enclosing));
|
|
1538
1687
|
}
|
|
1539
|
-
var
|
|
1688
|
+
var RAISING_METHODS2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
1540
1689
|
function raises(written, enclosing) {
|
|
1541
1690
|
const node = readThrough(written);
|
|
1542
1691
|
if (node.type === "identifier") {
|
|
1543
|
-
return
|
|
1692
|
+
return RAISING_METHODS2.has(node.text) && isBareCall(node, enclosing);
|
|
1544
1693
|
}
|
|
1545
1694
|
if (node.type !== "call") {
|
|
1546
1695
|
return false;
|
|
1547
1696
|
}
|
|
1548
1697
|
const receiver = field(node, "receiver");
|
|
1549
|
-
return
|
|
1698
|
+
return RAISING_METHODS2.has(field(node, "method")?.text ?? "") && (receiver === null || receiver.text === "Kernel");
|
|
1550
1699
|
}
|
|
1551
1700
|
function emitExpressionFacts(emitter, node) {
|
|
1552
1701
|
emitDynamicDefinition(emitter, node, node.type, []);
|
|
@@ -2502,9 +2651,7 @@ var EXPRESSION_TYPES = {
|
|
|
2502
2651
|
),
|
|
2503
2652
|
unary: (node) => {
|
|
2504
2653
|
const operand2 = field(node, "operand");
|
|
2505
|
-
const operator = node.
|
|
2506
|
-
(child) => child !== null && !child.isNamed
|
|
2507
|
-
);
|
|
2654
|
+
const operator = allChildren(node).find((child) => !child.isNamed);
|
|
2508
2655
|
if (operand2 === null || operator === void 0) {
|
|
2509
2656
|
return OPAQUE;
|
|
2510
2657
|
}
|
|
@@ -2706,8 +2853,25 @@ function statementOf(node) {
|
|
|
2706
2853
|
if (appending !== null) {
|
|
2707
2854
|
return appending;
|
|
2708
2855
|
}
|
|
2856
|
+
if (isRaiseCall(node) && !rescuedInSameFunction(node)) {
|
|
2857
|
+
return { kind: "throw" };
|
|
2858
|
+
}
|
|
2709
2859
|
return isTailExpression(node) ? { kind: "return", value: node } : { kind: "expression", value: node };
|
|
2710
2860
|
}
|
|
2861
|
+
var RESCUING_TYPES = /* @__PURE__ */ new Set(["begin", "body_statement"]);
|
|
2862
|
+
var UNRESCUED_PARTS = /* @__PURE__ */ new Set(["rescue", "else", "ensure"]);
|
|
2863
|
+
function rescuedInSameFunction(node) {
|
|
2864
|
+
let child = node;
|
|
2865
|
+
let parent = node.parent;
|
|
2866
|
+
while (parent !== null && !isRoot(parent)) {
|
|
2867
|
+
if (RESCUING_TYPES.has(parent.type) && !UNRESCUED_PARTS.has(child.type) && children(parent).some((part) => part.type === "rescue")) {
|
|
2868
|
+
return true;
|
|
2869
|
+
}
|
|
2870
|
+
child = parent;
|
|
2871
|
+
parent = parent.parent;
|
|
2872
|
+
}
|
|
2873
|
+
return false;
|
|
2874
|
+
}
|
|
2711
2875
|
function appendingStatement(node) {
|
|
2712
2876
|
if (node.type !== "binary" || field(node, "operator")?.text !== "<<") {
|
|
2713
2877
|
return null;
|
|
@@ -3382,6 +3546,10 @@ function writtenNodeOf(node, db, site) {
|
|
|
3382
3546
|
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
3383
3547
|
return context === void 0 ? null : context.writtenTo(node, site);
|
|
3384
3548
|
}
|
|
3549
|
+
function calledDefinition(call, db) {
|
|
3550
|
+
const context = db === void 0 ? void 0 : contexts.get(db);
|
|
3551
|
+
return context === void 0 ? null : context.callable(call);
|
|
3552
|
+
}
|
|
3385
3553
|
function askWrittenValues(nodes, db) {
|
|
3386
3554
|
if (db === void 0 || !contexts.has(db)) {
|
|
3387
3555
|
return;
|
|
@@ -3444,7 +3612,7 @@ function methodDefinitionsIn(file, root) {
|
|
|
3444
3612
|
if (METHOD_TYPES.has(node.type)) {
|
|
3445
3613
|
found.set(nodeId(file, node), node);
|
|
3446
3614
|
}
|
|
3447
|
-
for (const child of node
|
|
3615
|
+
for (const child of children(node)) {
|
|
3448
3616
|
if (child !== null) {
|
|
3449
3617
|
visit(child);
|
|
3450
3618
|
}
|
|
@@ -3770,12 +3938,27 @@ function graphqlTypeNameFromQualified(qualifiedName, convention) {
|
|
|
3770
3938
|
|
|
3771
3939
|
// src/ancestry.ts
|
|
3772
3940
|
async function reachDefinition(qualifiedName, lookup) {
|
|
3941
|
+
const segments = qualifiedName.split("::");
|
|
3942
|
+
for (let length = segments.length; length > 0; length--) {
|
|
3943
|
+
const found = await definedInFileOf(
|
|
3944
|
+
segments.slice(0, length).join("::"),
|
|
3945
|
+
qualifiedName,
|
|
3946
|
+
lookup
|
|
3947
|
+
);
|
|
3948
|
+
if (found !== null) {
|
|
3949
|
+
return found;
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
return null;
|
|
3953
|
+
}
|
|
3954
|
+
async function definedInFileOf(fileConstant, qualifiedName, lookup) {
|
|
3773
3955
|
const filePath = resolveConstantFile(
|
|
3774
3956
|
lookup.root,
|
|
3775
|
-
|
|
3957
|
+
fileConstant,
|
|
3776
3958
|
lookup.pathConvention,
|
|
3777
3959
|
lookup.acronyms,
|
|
3778
|
-
lookup.constantFiles
|
|
3960
|
+
lookup.constantFiles,
|
|
3961
|
+
lookup.autoloadRoots
|
|
3779
3962
|
);
|
|
3780
3963
|
if (filePath === null) {
|
|
3781
3964
|
return null;
|
|
@@ -3920,7 +4103,7 @@ function superclassCandidatesOf(blocks) {
|
|
|
3920
4103
|
return [];
|
|
3921
4104
|
}
|
|
3922
4105
|
function methodInAncestry(ancestry, name, read = {}) {
|
|
3923
|
-
for (const entry of ancestry) {
|
|
4106
|
+
for (const [index, entry] of ancestry.entries()) {
|
|
3924
4107
|
if (entry.type === "root") {
|
|
3925
4108
|
return { type: "none" };
|
|
3926
4109
|
}
|
|
@@ -3928,7 +4111,8 @@ function methodInAncestry(ancestry, name, read = {}) {
|
|
|
3928
4111
|
return {
|
|
3929
4112
|
type: "unsettled",
|
|
3930
4113
|
reason: `inherited from ${entry.name}, which this run did not read`,
|
|
3931
|
-
cause: "unreadAncestor"
|
|
4114
|
+
cause: "unreadAncestor",
|
|
4115
|
+
definedLater: definedInProjectAfter(ancestry, index, name, read)
|
|
3932
4116
|
};
|
|
3933
4117
|
}
|
|
3934
4118
|
const found = definitionIn(entry.blocks, name, read);
|
|
@@ -3945,6 +4129,18 @@ function methodInAncestry(ancestry, name, read = {}) {
|
|
|
3945
4129
|
}
|
|
3946
4130
|
return { type: "none" };
|
|
3947
4131
|
}
|
|
4132
|
+
function definedInProjectAfter(ancestry, index, name, read) {
|
|
4133
|
+
return ancestry.slice(index + 1).some(
|
|
4134
|
+
(entry) => entry.type !== "root" && entry.type !== "unfollowed" && definitionIn(entry.blocks, name, read).method !== null
|
|
4135
|
+
);
|
|
4136
|
+
}
|
|
4137
|
+
function methodPastUnreadAncestors(ancestry, name, read = {}) {
|
|
4138
|
+
return methodInAncestry(
|
|
4139
|
+
ancestry.filter((entry) => entry.type !== "unfollowed"),
|
|
4140
|
+
name,
|
|
4141
|
+
read
|
|
4142
|
+
);
|
|
4143
|
+
}
|
|
3948
4144
|
function definitionIn(blocks, name, read) {
|
|
3949
4145
|
let method = null;
|
|
3950
4146
|
let block = null;
|
|
@@ -3994,11 +4190,17 @@ import {
|
|
|
3994
4190
|
graphqlResolverBinding,
|
|
3995
4191
|
restBinding as restBinding2
|
|
3996
4192
|
} from "@suss/behavioral-ir";
|
|
3997
|
-
import { absentReading as
|
|
4193
|
+
import { absentReading as absentReading3, unreadableReading as unreadableReading2 } from "@suss/extractor";
|
|
3998
4194
|
|
|
3999
4195
|
// src/clientCalls.ts
|
|
4000
|
-
import {
|
|
4001
|
-
|
|
4196
|
+
import {
|
|
4197
|
+
hasNameHole,
|
|
4198
|
+
namesNothing,
|
|
4199
|
+
restBinding,
|
|
4200
|
+
statusMembersOf,
|
|
4201
|
+
testsStatus
|
|
4202
|
+
} from "@suss/behavioral-ir";
|
|
4203
|
+
import { hostOf, pathOf } from "@suss/values";
|
|
4002
4204
|
|
|
4003
4205
|
// src/paths/effects.ts
|
|
4004
4206
|
import { enumerateOrDegrade, sharedGatingConditions } from "@suss/extractor";
|
|
@@ -4025,7 +4227,7 @@ function isRaise(node) {
|
|
|
4025
4227
|
return false;
|
|
4026
4228
|
}
|
|
4027
4229
|
const receiver = field(node, "receiver");
|
|
4028
|
-
const method = field(node, "method") ?? node.
|
|
4230
|
+
const method = field(node, "method") ?? node.namedChild(0) ?? null;
|
|
4029
4231
|
return receiver === null && method !== null && RAISE_NAMES.has(method.text);
|
|
4030
4232
|
}
|
|
4031
4233
|
function exitOf(node) {
|
|
@@ -4037,7 +4239,7 @@ function exitOf(node) {
|
|
|
4037
4239
|
function exitKindOf(node) {
|
|
4038
4240
|
let sawReturn = false;
|
|
4039
4241
|
const raises2 = (current) => {
|
|
4040
|
-
for (const child of current
|
|
4242
|
+
for (const child of children(current)) {
|
|
4041
4243
|
if (child === null || CAPTURES_RETURN.has(child.type)) {
|
|
4042
4244
|
continue;
|
|
4043
4245
|
}
|
|
@@ -4071,13 +4273,52 @@ function callHeaderOf(call, block) {
|
|
|
4071
4273
|
function blockStatements(block) {
|
|
4072
4274
|
return block === null ? [] : children(block);
|
|
4073
4275
|
}
|
|
4276
|
+
function attachedBlockOf(call) {
|
|
4277
|
+
return children(call).find(
|
|
4278
|
+
(child) => child !== null && (child.type === "do_block" || child.type === "block")
|
|
4279
|
+
) ?? null;
|
|
4280
|
+
}
|
|
4281
|
+
function soleBlockParameter(block) {
|
|
4282
|
+
const names = children(field(block, "parameters")).filter(
|
|
4283
|
+
(child) => child.type === "identifier"
|
|
4284
|
+
);
|
|
4285
|
+
return names.length === 1 ? names[0].text : null;
|
|
4286
|
+
}
|
|
4287
|
+
function blockBodyStatements(block) {
|
|
4288
|
+
const body = field(block, "body");
|
|
4289
|
+
if (body !== null) {
|
|
4290
|
+
return children(body);
|
|
4291
|
+
}
|
|
4292
|
+
return children(block).filter((child) => child.type !== "block_parameters");
|
|
4293
|
+
}
|
|
4294
|
+
function armBody(block) {
|
|
4295
|
+
return field(block, "body") ?? block;
|
|
4296
|
+
}
|
|
4297
|
+
function formatArmOf(statement, parameter) {
|
|
4298
|
+
if (statement.type !== "call") {
|
|
4299
|
+
return null;
|
|
4300
|
+
}
|
|
4301
|
+
const receiver = field(statement, "receiver");
|
|
4302
|
+
const format = field(statement, "method")?.text;
|
|
4303
|
+
if (receiver?.text !== parameter || format === void 0) {
|
|
4304
|
+
return null;
|
|
4305
|
+
}
|
|
4306
|
+
const block = attachedBlockOf(statement);
|
|
4307
|
+
return {
|
|
4308
|
+
format,
|
|
4309
|
+
header: block === null ? statement.text : callHeaderOf(statement, block).sourceText,
|
|
4310
|
+
block
|
|
4311
|
+
};
|
|
4312
|
+
}
|
|
4074
4313
|
var Lowerer = class {
|
|
4075
|
-
constructor(terminals, responseCalls) {
|
|
4314
|
+
constructor(terminals, responseCalls, formatSwitch) {
|
|
4076
4315
|
this.terminals = terminals;
|
|
4077
4316
|
this.responseCalls = responseCalls;
|
|
4317
|
+
this.formatSwitch = formatSwitch;
|
|
4078
4318
|
}
|
|
4079
4319
|
terminals;
|
|
4080
4320
|
responseCalls;
|
|
4321
|
+
formatSwitch;
|
|
4081
4322
|
terminalsByStmt = /* @__PURE__ */ new Map();
|
|
4082
4323
|
terminalHome = new NodeMap();
|
|
4083
4324
|
attachTerminals(statement, found) {
|
|
@@ -4108,7 +4349,7 @@ var Lowerer = class {
|
|
|
4108
4349
|
found.push(own);
|
|
4109
4350
|
return found;
|
|
4110
4351
|
}
|
|
4111
|
-
for (const child of node
|
|
4352
|
+
for (const child of children(node)) {
|
|
4112
4353
|
if (child !== null && !OWN_BODY_TYPES.has(child.type)) {
|
|
4113
4354
|
this.responsesIn(child, found);
|
|
4114
4355
|
}
|
|
@@ -4167,29 +4408,22 @@ var Lowerer = class {
|
|
|
4167
4408
|
};
|
|
4168
4409
|
}
|
|
4169
4410
|
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(
|
|
4411
|
+
const rescues2 = children(node).filter((child) => child.type === "rescue");
|
|
4412
|
+
const ensureClause = children(node).find((child) => child.type === "ensure") ?? null;
|
|
4413
|
+
const tryBody = children(node).filter(
|
|
4177
4414
|
(child) => child !== null && child.type !== "rescue" && child.type !== "ensure" && child.type !== "else"
|
|
4178
4415
|
);
|
|
4179
4416
|
return {
|
|
4180
4417
|
kind: "try",
|
|
4181
4418
|
tryBody: tryBody.map((statement) => this.lower(statement)),
|
|
4182
|
-
catchBody:
|
|
4419
|
+
catchBody: rescues2.length > 0 ? rescues2.flatMap((clause) => this.lowerBlock(field(clause, "body"))) : null,
|
|
4183
4420
|
finallyBody: ensureClause === null ? null : this.lowerBlock(ensureClause),
|
|
4184
4421
|
exitKind: exitKindOf(node)
|
|
4185
4422
|
};
|
|
4186
4423
|
}
|
|
4187
4424
|
lowerCase(node) {
|
|
4188
4425
|
const groups = [];
|
|
4189
|
-
for (const child of node
|
|
4190
|
-
if (child === null) {
|
|
4191
|
-
continue;
|
|
4192
|
-
}
|
|
4426
|
+
for (const child of children(node)) {
|
|
4193
4427
|
if (child.type === "when") {
|
|
4194
4428
|
groups.push({
|
|
4195
4429
|
condition: handleOf(field(child, "pattern")),
|
|
@@ -4207,6 +4441,47 @@ var Lowerer = class {
|
|
|
4207
4441
|
}
|
|
4208
4442
|
return { kind: "switch", groups, exitKind: exitKindOf(node) };
|
|
4209
4443
|
}
|
|
4444
|
+
/**
|
|
4445
|
+
* `respond_to do |format| ... end` runs one arm per request, picked by
|
|
4446
|
+
* the format the request accepts, so it lowers to a switch with an arm
|
|
4447
|
+
* per `format.x`. The arm that accepts every format is the default.
|
|
4448
|
+
* Without one, a request no arm accepts makes the library raise. Null
|
|
4449
|
+
* when the block contains anything besides those arms.
|
|
4450
|
+
*/
|
|
4451
|
+
lowerFormatSwitch(node, block) {
|
|
4452
|
+
const declared = this.formatSwitch;
|
|
4453
|
+
if (declared === void 0 || node.type !== "call" || field(node, "receiver") !== null || field(node, "method")?.text !== declared.name) {
|
|
4454
|
+
return null;
|
|
4455
|
+
}
|
|
4456
|
+
const parameter = soleBlockParameter(block);
|
|
4457
|
+
if (parameter === null) {
|
|
4458
|
+
return null;
|
|
4459
|
+
}
|
|
4460
|
+
const arms = blockBodyStatements(block).map(
|
|
4461
|
+
(statement) => formatArmOf(statement, parameter)
|
|
4462
|
+
);
|
|
4463
|
+
if (arms.length === 0 || arms.some((arm) => arm === null)) {
|
|
4464
|
+
return null;
|
|
4465
|
+
}
|
|
4466
|
+
const groups = arms.map(
|
|
4467
|
+
(arm) => ({
|
|
4468
|
+
condition: arm.format === declared.matchesEvery ? null : { sourceText: arm.header, expression: null },
|
|
4469
|
+
hasTrailingBreak: true,
|
|
4470
|
+
body: arm.block === null ? [] : this.lowerBlock(armBody(arm.block))
|
|
4471
|
+
})
|
|
4472
|
+
);
|
|
4473
|
+
const named2 = groups.filter((group) => group.condition !== null);
|
|
4474
|
+
const fallback2 = groups.find((group) => group.condition === null) ?? {
|
|
4475
|
+
condition: null,
|
|
4476
|
+
hasTrailingBreak: true,
|
|
4477
|
+
body: [{ kind: "exit", exit: "throw", exitKind: "throw" }]
|
|
4478
|
+
};
|
|
4479
|
+
return {
|
|
4480
|
+
kind: "switch",
|
|
4481
|
+
groups: [...named2, fallback2],
|
|
4482
|
+
exitKind: exitKindOf(node)
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4210
4485
|
lower(node) {
|
|
4211
4486
|
const exit = exitOf(node);
|
|
4212
4487
|
if (exit !== null) {
|
|
@@ -4236,10 +4511,12 @@ var Lowerer = class {
|
|
|
4236
4511
|
[header]
|
|
4237
4512
|
);
|
|
4238
4513
|
}
|
|
4239
|
-
const attachedBlock = node
|
|
4240
|
-
|
|
4241
|
-
)
|
|
4242
|
-
|
|
4514
|
+
const attachedBlock = attachedBlockOf(node);
|
|
4515
|
+
const formatSwitch = attachedBlock === null ? null : this.lowerFormatSwitch(node, attachedBlock);
|
|
4516
|
+
if (formatSwitch !== null) {
|
|
4517
|
+
return this.attach(formatSwitch, [node]);
|
|
4518
|
+
}
|
|
4519
|
+
if (node.type === "call" && attachedBlock !== null) {
|
|
4243
4520
|
return this.attach(
|
|
4244
4521
|
{
|
|
4245
4522
|
kind: "loop",
|
|
@@ -4266,10 +4543,11 @@ var Lowerer = class {
|
|
|
4266
4543
|
return this.attach({ kind: "opaque", exitKind: exitKindOf(node) }, [node]);
|
|
4267
4544
|
}
|
|
4268
4545
|
};
|
|
4269
|
-
function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
4546
|
+
function lowerRubyBody(body, terminals, responseCalls = [], formatSwitch) {
|
|
4270
4547
|
const lowerer = new Lowerer(
|
|
4271
4548
|
new Map(terminals.map((terminal) => [terminal.id, terminal])),
|
|
4272
|
-
new NodeSet(responseCalls)
|
|
4549
|
+
new NodeSet(responseCalls),
|
|
4550
|
+
formatSwitch
|
|
4273
4551
|
);
|
|
4274
4552
|
return {
|
|
4275
4553
|
statements: lowerer.lowerBlock(body),
|
|
@@ -4430,6 +4708,14 @@ function enclosingStatement(call, body) {
|
|
|
4430
4708
|
return current;
|
|
4431
4709
|
}
|
|
4432
4710
|
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
4711
|
+
return invocationSites(
|
|
4712
|
+
definitionNode,
|
|
4713
|
+
inherited,
|
|
4714
|
+
keepsArglessCall,
|
|
4715
|
+
facts
|
|
4716
|
+
).map((site) => site.effect);
|
|
4717
|
+
}
|
|
4718
|
+
function invocationSites(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
4433
4719
|
const read = methodBody(definitionNode);
|
|
4434
4720
|
if (read === null) {
|
|
4435
4721
|
return [];
|
|
@@ -4442,7 +4728,7 @@ function moduleScopeInvocationEffects(root, inherited = NO_INHERITED_METHODS, ke
|
|
|
4442
4728
|
inherited,
|
|
4443
4729
|
keepsArglessCall,
|
|
4444
4730
|
facts
|
|
4445
|
-
);
|
|
4731
|
+
).map((site) => site.effect);
|
|
4446
4732
|
}
|
|
4447
4733
|
function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
4448
4734
|
const body = read.body;
|
|
@@ -4476,25 +4762,18 @@ function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
|
4476
4762
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
4477
4763
|
);
|
|
4478
4764
|
return {
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4765
|
+
call,
|
|
4766
|
+
effect: {
|
|
4767
|
+
type: "invocation",
|
|
4768
|
+
callee: calleeText(call),
|
|
4769
|
+
args: argsOf(call, facts),
|
|
4770
|
+
async: false,
|
|
4771
|
+
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
4772
|
+
}
|
|
4484
4773
|
};
|
|
4485
4774
|
});
|
|
4486
4775
|
}
|
|
4487
4776
|
|
|
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
4777
|
// src/paths/predicates.ts
|
|
4499
4778
|
var OPERATORS = {
|
|
4500
4779
|
"==": "eq",
|
|
@@ -4513,11 +4792,11 @@ var opaqueOf = (node) => ({
|
|
|
4513
4792
|
var JOINING = /* @__PURE__ */ new Set(["&&", "||", "and", "or"]);
|
|
4514
4793
|
function predicateOf(node, inputs) {
|
|
4515
4794
|
if (node.type === "parenthesized_statements") {
|
|
4516
|
-
const inner = node.
|
|
4795
|
+
const inner = node.namedChild(0);
|
|
4517
4796
|
return inner == null ? opaqueOf(node) : predicateOf(inner, inputs);
|
|
4518
4797
|
}
|
|
4519
4798
|
if (node.type === "unary" && field(node, "operator")?.text === "!") {
|
|
4520
|
-
const operand2 = field(node, "operand") ?? node.
|
|
4799
|
+
const operand2 = field(node, "operand") ?? node.namedChild(0);
|
|
4521
4800
|
return operand2 == null ? opaqueOf(node) : { type: "negation", operand: predicateOf(operand2, inputs) };
|
|
4522
4801
|
}
|
|
4523
4802
|
if (node.type === "binary") {
|
|
@@ -4543,11 +4822,11 @@ function predicateOf(node, inputs) {
|
|
|
4543
4822
|
}
|
|
4544
4823
|
function operandsOf(node) {
|
|
4545
4824
|
if (node.type === "parenthesized_statements") {
|
|
4546
|
-
const inner = node.
|
|
4825
|
+
const inner = node.namedChild(0);
|
|
4547
4826
|
return inner == null ? [] : operandsOf(inner);
|
|
4548
4827
|
}
|
|
4549
4828
|
if (node.type === "unary" && field(node, "operator")?.text === "!") {
|
|
4550
|
-
const operand2 = field(node, "operand") ?? node.
|
|
4829
|
+
const operand2 = field(node, "operand") ?? node.namedChild(0);
|
|
4551
4830
|
return operand2 == null ? [] : operandsOf(operand2);
|
|
4552
4831
|
}
|
|
4553
4832
|
if (node.type === "binary") {
|
|
@@ -4643,6 +4922,321 @@ function calledMethod(node) {
|
|
|
4643
4922
|
return field(node, "method")?.text ?? null;
|
|
4644
4923
|
}
|
|
4645
4924
|
|
|
4925
|
+
// src/responseHandOff.ts
|
|
4926
|
+
import { helperPathsOnVariables, splitAtHandOffs } from "@suss/extractor";
|
|
4927
|
+
|
|
4928
|
+
// src/exceptionClasses.ts
|
|
4929
|
+
var ROOT_EXCEPTION = "Exception";
|
|
4930
|
+
var RUBY_EXCEPTION_PARENTS = {
|
|
4931
|
+
NoMemoryError: ROOT_EXCEPTION,
|
|
4932
|
+
ScriptError: ROOT_EXCEPTION,
|
|
4933
|
+
LoadError: "ScriptError",
|
|
4934
|
+
NotImplementedError: "ScriptError",
|
|
4935
|
+
SyntaxError: "ScriptError",
|
|
4936
|
+
SecurityError: ROOT_EXCEPTION,
|
|
4937
|
+
SignalException: ROOT_EXCEPTION,
|
|
4938
|
+
Interrupt: "SignalException",
|
|
4939
|
+
StandardError: ROOT_EXCEPTION,
|
|
4940
|
+
SystemExit: ROOT_EXCEPTION,
|
|
4941
|
+
SystemStackError: ROOT_EXCEPTION,
|
|
4942
|
+
ArgumentError: "StandardError",
|
|
4943
|
+
UncaughtThrowError: "ArgumentError",
|
|
4944
|
+
EncodingError: "StandardError",
|
|
4945
|
+
FiberError: "StandardError",
|
|
4946
|
+
IOError: "StandardError",
|
|
4947
|
+
EOFError: "IOError",
|
|
4948
|
+
IndexError: "StandardError",
|
|
4949
|
+
KeyError: "IndexError",
|
|
4950
|
+
StopIteration: "IndexError",
|
|
4951
|
+
ClosedQueueError: "StopIteration",
|
|
4952
|
+
LocalJumpError: "StandardError",
|
|
4953
|
+
NameError: "StandardError",
|
|
4954
|
+
NoMethodError: "NameError",
|
|
4955
|
+
RangeError: "StandardError",
|
|
4956
|
+
FloatDomainError: "RangeError",
|
|
4957
|
+
RegexpError: "StandardError",
|
|
4958
|
+
RuntimeError: "StandardError",
|
|
4959
|
+
FrozenError: "RuntimeError",
|
|
4960
|
+
SystemCallError: "StandardError",
|
|
4961
|
+
ThreadError: "StandardError",
|
|
4962
|
+
TypeError: "StandardError",
|
|
4963
|
+
ZeroDivisionError: "StandardError"
|
|
4964
|
+
};
|
|
4965
|
+
var MESSAGE_ONLY_EXCEPTION = "RuntimeError";
|
|
4966
|
+
var ExceptionReader = class {
|
|
4967
|
+
constructor(lookup, library) {
|
|
4968
|
+
this.library = library;
|
|
4969
|
+
this.walking = {
|
|
4970
|
+
...lookup,
|
|
4971
|
+
ancestryRootClassNames: [
|
|
4972
|
+
...Object.keys(library),
|
|
4973
|
+
...Object.keys(RUBY_EXCEPTION_PARENTS),
|
|
4974
|
+
ROOT_EXCEPTION
|
|
4975
|
+
]
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
library;
|
|
4979
|
+
walking;
|
|
4980
|
+
known = /* @__PURE__ */ new Map();
|
|
4981
|
+
read(ref) {
|
|
4982
|
+
const key = [ref.text, ...ref.candidates].join("\0");
|
|
4983
|
+
let found = this.known.get(key);
|
|
4984
|
+
if (found === void 0) {
|
|
4985
|
+
found = readException(ref, this.walking, this.library);
|
|
4986
|
+
this.known.set(key, found);
|
|
4987
|
+
}
|
|
4988
|
+
return found;
|
|
4989
|
+
}
|
|
4990
|
+
};
|
|
4991
|
+
function listedException(name, library) {
|
|
4992
|
+
const fromLibrary = library[name];
|
|
4993
|
+
if (fromLibrary !== void 0) {
|
|
4994
|
+
return {
|
|
4995
|
+
name,
|
|
4996
|
+
ancestors: fromLibrary.ancestors,
|
|
4997
|
+
incomplete: false,
|
|
4998
|
+
inheritableByUnread: false
|
|
4999
|
+
};
|
|
5000
|
+
}
|
|
5001
|
+
const fromRuby = rubyAncestors(name);
|
|
5002
|
+
return fromRuby === null ? null : {
|
|
5003
|
+
name,
|
|
5004
|
+
ancestors: fromRuby,
|
|
5005
|
+
incomplete: false,
|
|
5006
|
+
inheritableByUnread: true
|
|
5007
|
+
};
|
|
5008
|
+
}
|
|
5009
|
+
function unreadException(name) {
|
|
5010
|
+
return {
|
|
5011
|
+
name,
|
|
5012
|
+
ancestors: [ROOT_EXCEPTION],
|
|
5013
|
+
incomplete: true,
|
|
5014
|
+
inheritableByUnread: true
|
|
5015
|
+
};
|
|
5016
|
+
}
|
|
5017
|
+
function classesOf(exception) {
|
|
5018
|
+
return [exception.name, ...exception.ancestors];
|
|
5019
|
+
}
|
|
5020
|
+
async function readException(ref, lookup, library) {
|
|
5021
|
+
for (const candidate of ref.candidates) {
|
|
5022
|
+
const known = listedException(candidate, library);
|
|
5023
|
+
if (known !== null) {
|
|
5024
|
+
return known;
|
|
5025
|
+
}
|
|
5026
|
+
const defined = await reachConstant([candidate], lookup);
|
|
5027
|
+
if (defined !== null) {
|
|
5028
|
+
return projectException(defined, lookup, library);
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
return unreadException(ref.candidates.at(-1) ?? ref.text);
|
|
5032
|
+
}
|
|
5033
|
+
async function projectException(defined, lookup, library) {
|
|
5034
|
+
const chain = await ancestryOf(defined.name, defined.blocks, lookup);
|
|
5035
|
+
const ancestors = [];
|
|
5036
|
+
let incomplete = false;
|
|
5037
|
+
for (const entry of chain) {
|
|
5038
|
+
if (entry.type === "bodies" && entry.name === defined.name) {
|
|
5039
|
+
continue;
|
|
5040
|
+
}
|
|
5041
|
+
ancestors.push(entry.name);
|
|
5042
|
+
if (entry.type === "root") {
|
|
5043
|
+
ancestors.push(
|
|
5044
|
+
...listedException(entry.name, library)?.ancestors ?? []
|
|
5045
|
+
);
|
|
5046
|
+
}
|
|
5047
|
+
if (entry.type === "unfollowed") {
|
|
5048
|
+
incomplete = true;
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
if (incomplete && !ancestors.includes(ROOT_EXCEPTION)) {
|
|
5052
|
+
ancestors.push(ROOT_EXCEPTION);
|
|
5053
|
+
}
|
|
5054
|
+
return {
|
|
5055
|
+
name: defined.name,
|
|
5056
|
+
ancestors,
|
|
5057
|
+
incomplete,
|
|
5058
|
+
inheritableByUnread: false
|
|
5059
|
+
};
|
|
5060
|
+
}
|
|
5061
|
+
function rubyAncestors(name) {
|
|
5062
|
+
if (name === ROOT_EXCEPTION) {
|
|
5063
|
+
return [];
|
|
5064
|
+
}
|
|
5065
|
+
if (RUBY_EXCEPTION_PARENTS[name] === void 0) {
|
|
5066
|
+
return null;
|
|
5067
|
+
}
|
|
5068
|
+
const ancestors = [];
|
|
5069
|
+
for (let parent = RUBY_EXCEPTION_PARENTS[name]; parent !== void 0; parent = RUBY_EXCEPTION_PARENTS[parent]) {
|
|
5070
|
+
ancestors.push(parent);
|
|
5071
|
+
}
|
|
5072
|
+
return ancestors;
|
|
5073
|
+
}
|
|
5074
|
+
|
|
5075
|
+
// src/raises.ts
|
|
5076
|
+
var RESCUING_BODIES = /* @__PURE__ */ new Set(["begin", "body_statement"]);
|
|
5077
|
+
var AFTER_CATCH = /* @__PURE__ */ new Set(["rescue", "else", "ensure"]);
|
|
5078
|
+
function escapingRaises(method) {
|
|
5079
|
+
const body = field(method, "body");
|
|
5080
|
+
if (body === null) {
|
|
5081
|
+
return [];
|
|
5082
|
+
}
|
|
5083
|
+
const found = [];
|
|
5084
|
+
const visit = (node) => {
|
|
5085
|
+
for (const child of children(node)) {
|
|
5086
|
+
if (child === null || OWN_BODY_TYPES.has(child.type)) {
|
|
5087
|
+
continue;
|
|
5088
|
+
}
|
|
5089
|
+
if (isRaise(child) && !caughtLocally(child, method)) {
|
|
5090
|
+
found.push(child);
|
|
5091
|
+
continue;
|
|
5092
|
+
}
|
|
5093
|
+
visit(child);
|
|
5094
|
+
}
|
|
5095
|
+
};
|
|
5096
|
+
visit(body);
|
|
5097
|
+
return found;
|
|
5098
|
+
}
|
|
5099
|
+
function caughtLocally(raise, method) {
|
|
5100
|
+
let child = raise;
|
|
5101
|
+
for (let parent = raise.parent; parent !== null && parent.id !== method.id; parent = parent.parent) {
|
|
5102
|
+
if (rescues(parent, child)) {
|
|
5103
|
+
return true;
|
|
5104
|
+
}
|
|
5105
|
+
child = parent;
|
|
5106
|
+
}
|
|
5107
|
+
return false;
|
|
5108
|
+
}
|
|
5109
|
+
function rescues(parent, child) {
|
|
5110
|
+
if (parent.type === "rescue_modifier") {
|
|
5111
|
+
return field(parent, "body")?.id === child.id;
|
|
5112
|
+
}
|
|
5113
|
+
return RESCUING_BODIES.has(parent.type) && !AFTER_CATCH.has(child.type) && children(parent).some((one) => one?.type === "rescue");
|
|
5114
|
+
}
|
|
5115
|
+
function raisedClassRef(call, nesting) {
|
|
5116
|
+
const first = readCallArgs(field(call, "arguments")).positional[0];
|
|
5117
|
+
if (first === void 0) {
|
|
5118
|
+
return null;
|
|
5119
|
+
}
|
|
5120
|
+
if (first.type === "string") {
|
|
5121
|
+
return {
|
|
5122
|
+
text: MESSAGE_ONLY_EXCEPTION,
|
|
5123
|
+
candidates: [MESSAGE_ONLY_EXCEPTION]
|
|
5124
|
+
};
|
|
5125
|
+
}
|
|
5126
|
+
const constant2 = constructedClass(first) ?? first;
|
|
5127
|
+
const candidates = constantRefCandidates(constant2, nesting);
|
|
5128
|
+
return candidates.length === 0 ? null : { text: constant2.text, candidates };
|
|
5129
|
+
}
|
|
5130
|
+
function constructedClass(node) {
|
|
5131
|
+
if (node.type !== "call" || field(node, "method")?.text !== "new") {
|
|
5132
|
+
return null;
|
|
5133
|
+
}
|
|
5134
|
+
return field(node, "receiver");
|
|
5135
|
+
}
|
|
5136
|
+
|
|
5137
|
+
// src/responseHandOff.ts
|
|
5138
|
+
function raiseEndings(method) {
|
|
5139
|
+
return escapingRaises(method).map((call) => ({
|
|
5140
|
+
call,
|
|
5141
|
+
terminal: raiseTerminal(call)
|
|
5142
|
+
}));
|
|
5143
|
+
}
|
|
5144
|
+
function raiseTerminal(call) {
|
|
5145
|
+
const [first, second] = readCallArgs(field(call, "arguments")).positional;
|
|
5146
|
+
const message = first === void 0 ? null : stringLiteralValue(first);
|
|
5147
|
+
return {
|
|
5148
|
+
kind: "throw",
|
|
5149
|
+
statusCode: null,
|
|
5150
|
+
body: null,
|
|
5151
|
+
exceptionType: raisedClassRef(call, [])?.text ?? null,
|
|
5152
|
+
message: message ?? (second === void 0 ? null : stringLiteralValue(second)),
|
|
5153
|
+
component: null,
|
|
5154
|
+
renderTree: null,
|
|
5155
|
+
delegateTarget: null,
|
|
5156
|
+
emitEvent: null,
|
|
5157
|
+
location: rangeOf(call)
|
|
5158
|
+
};
|
|
5159
|
+
}
|
|
5160
|
+
function throughResponseHelpers(branches, sites, options) {
|
|
5161
|
+
const handOffs = [];
|
|
5162
|
+
for (const { call, effect } of sites) {
|
|
5163
|
+
const helper = helperBranchesAt(call, options);
|
|
5164
|
+
if (helper !== null) {
|
|
5165
|
+
handOffs.push({
|
|
5166
|
+
helper,
|
|
5167
|
+
preconditions: effect.preconditions,
|
|
5168
|
+
line: rangeOf(call).start,
|
|
5169
|
+
at: rangeOf(call)
|
|
5170
|
+
});
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
return handOffs.length === 0 ? [...branches] : branches.flatMap((branch) => splitAtHandOffs(branch, handOffs));
|
|
5174
|
+
}
|
|
5175
|
+
function helperBranchesAt(call, options) {
|
|
5176
|
+
if (call.type !== "call") {
|
|
5177
|
+
return null;
|
|
5178
|
+
}
|
|
5179
|
+
const args = readCallArgs(field(call, "arguments"));
|
|
5180
|
+
const handedAt = args.positional.flatMap(
|
|
5181
|
+
(argument, position) => argument.type === "identifier" && refersToResponse(argument, options) ? [{ position, variable: argument.text }] : []
|
|
5182
|
+
);
|
|
5183
|
+
const handedAs = Object.entries(args.keyword).flatMap(
|
|
5184
|
+
([name, argument]) => argument.type === "identifier" && refersToResponse(argument, options) ? [{ name, variable: argument.text }] : []
|
|
5185
|
+
);
|
|
5186
|
+
if (handedAt.length === 0 && handedAs.length === 0) {
|
|
5187
|
+
return null;
|
|
5188
|
+
}
|
|
5189
|
+
const helper = calledDefinition(call, options.facts);
|
|
5190
|
+
if (helper === null) {
|
|
5191
|
+
return null;
|
|
5192
|
+
}
|
|
5193
|
+
const passed = /* @__PURE__ */ new Map();
|
|
5194
|
+
const names = parametersOf(helper).map(
|
|
5195
|
+
(parameter) => paramNameOf(parameter)?.text ?? null
|
|
5196
|
+
);
|
|
5197
|
+
for (const { position, variable } of handedAt) {
|
|
5198
|
+
const name = names[position];
|
|
5199
|
+
if (name !== void 0 && name !== null) {
|
|
5200
|
+
passed.set(name, variable);
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
5203
|
+
for (const { name, variable } of handedAs) {
|
|
5204
|
+
if (names.includes(name)) {
|
|
5205
|
+
passed.set(name, variable);
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
return passed.size === 0 ? null : helperPathsOnVariables(options.readHelper(helper), passed);
|
|
5209
|
+
}
|
|
5210
|
+
function refersToResponse(name, options) {
|
|
5211
|
+
const written = writtenNodeOf(name, options.facts);
|
|
5212
|
+
if (written === null) {
|
|
5213
|
+
return false;
|
|
5214
|
+
}
|
|
5215
|
+
if (sameNode(written, options.request)) {
|
|
5216
|
+
return true;
|
|
5217
|
+
}
|
|
5218
|
+
const member = written.type === "call" ? field(written, "method") : null;
|
|
5219
|
+
const receiver = written.type === "call" ? field(written, "receiver") : null;
|
|
5220
|
+
if (member === null || receiver?.type !== "identifier" || !options.bodyMethods.includes(member.text)) {
|
|
5221
|
+
return false;
|
|
5222
|
+
}
|
|
5223
|
+
const parsedFrom = writtenNodeOf(receiver, options.facts);
|
|
5224
|
+
return parsedFrom !== null && sameNode(parsedFrom, options.request);
|
|
5225
|
+
}
|
|
5226
|
+
function sameNode(a, b) {
|
|
5227
|
+
return a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.tree === b.tree;
|
|
5228
|
+
}
|
|
5229
|
+
|
|
5230
|
+
// src/responseStatus.ts
|
|
5231
|
+
import {
|
|
5232
|
+
absentReading as absentReading2,
|
|
5233
|
+
enumerateOrDegrade as enumerateOrDegrade2,
|
|
5234
|
+
guardsHoldOn,
|
|
5235
|
+
unreadableReading,
|
|
5236
|
+
writtenReading as writtenReading2
|
|
5237
|
+
} from "@suss/extractor";
|
|
5238
|
+
import { constantOf as constantOf3, literalOf as literalOf7 } from "@suss/values";
|
|
5239
|
+
|
|
4646
5240
|
// src/provenance.ts
|
|
4647
5241
|
import { sourceRefsOf } from "@suss/extractor";
|
|
4648
5242
|
import { constantOf as constantOf2, literalOf as literalOf6 } from "@suss/values";
|
|
@@ -4710,7 +5304,7 @@ function pickedSource(call, loader) {
|
|
|
4710
5304
|
return null;
|
|
4711
5305
|
}
|
|
4712
5306
|
const args = field(pick, "arguments");
|
|
4713
|
-
const given = args === null ? void 0 : args.
|
|
5307
|
+
const given = args === null ? void 0 : args.namedChild(source.at);
|
|
4714
5308
|
return given?.type === "constant" || given?.type === "scope_resolution" ? given : null;
|
|
4715
5309
|
}
|
|
4716
5310
|
|
|
@@ -5355,10 +5949,7 @@ function recognizeMethodStorage(method, file, options) {
|
|
|
5355
5949
|
return { effects, slots };
|
|
5356
5950
|
}
|
|
5357
5951
|
function callsUnder(node, found = []) {
|
|
5358
|
-
for (const child of node
|
|
5359
|
-
if (child === null) {
|
|
5360
|
-
continue;
|
|
5361
|
-
}
|
|
5952
|
+
for (const child of children(node)) {
|
|
5362
5953
|
if (child.type === "call") {
|
|
5363
5954
|
found.push(child);
|
|
5364
5955
|
}
|
|
@@ -5566,6 +6157,111 @@ function literalAt(key, facts) {
|
|
|
5566
6157
|
return constant2 !== void 0 ? constant2 : literalOf6(value) ?? void 0;
|
|
5567
6158
|
}
|
|
5568
6159
|
|
|
6160
|
+
// src/raisedStatuses.ts
|
|
6161
|
+
function rescuedBy(handlers, exception) {
|
|
6162
|
+
if (handlers === "eachRoute") {
|
|
6163
|
+
return true;
|
|
6164
|
+
}
|
|
6165
|
+
const classes = new Set(classesOf(exception));
|
|
6166
|
+
return handlers.some(
|
|
6167
|
+
(caught) => caught.someUnread || caught.classes.some(
|
|
6168
|
+
(one) => classes.has(one.name) || exception.incomplete && one.inheritableByUnread
|
|
6169
|
+
)
|
|
6170
|
+
);
|
|
6171
|
+
}
|
|
6172
|
+
function raisedTerminal(pattern, exception, handlers, location) {
|
|
6173
|
+
const libraryStatus = exception === null ? void 0 : pattern.libraryExceptions?.[exception.name]?.status;
|
|
6174
|
+
const status = exception === null || rescuedBy(handlers, exception) ? void 0 : libraryStatus;
|
|
6175
|
+
return {
|
|
6176
|
+
kind: status === void 0 ? "throw" : "response",
|
|
6177
|
+
statusCode: status === void 0 ? null : { type: "literal", value: status },
|
|
6178
|
+
body: null,
|
|
6179
|
+
exceptionType: status === void 0 ? exception?.name ?? null : null,
|
|
6180
|
+
...status === void 0 && exception !== null ? {
|
|
6181
|
+
exceptionAncestry: {
|
|
6182
|
+
ancestors: exception.ancestors,
|
|
6183
|
+
incomplete: exception.incomplete
|
|
6184
|
+
}
|
|
6185
|
+
} : {},
|
|
6186
|
+
...status === void 0 && libraryStatus !== void 0 ? { statusWhenUncaught: libraryStatus } : {},
|
|
6187
|
+
message: null,
|
|
6188
|
+
component: null,
|
|
6189
|
+
renderTree: null,
|
|
6190
|
+
delegateTarget: null,
|
|
6191
|
+
emitEvent: null,
|
|
6192
|
+
location
|
|
6193
|
+
};
|
|
6194
|
+
}
|
|
6195
|
+
function raisedStatusBranches(pattern, storage, effects, handlers, location) {
|
|
6196
|
+
const operations = new Set(
|
|
6197
|
+
(effects ?? []).flatMap(
|
|
6198
|
+
(effect) => effect.type === "interaction" && effect.interaction.class === "storage-access" && effect.interaction.operation !== void 0 ? [effect.interaction.operation] : []
|
|
6199
|
+
)
|
|
6200
|
+
);
|
|
6201
|
+
const raised = new Set(
|
|
6202
|
+
storage.flatMap(
|
|
6203
|
+
(one) => (one.raises ?? []).flatMap(
|
|
6204
|
+
(raise) => raise.methods.some((method) => operations.has(method)) ? [raise.exception] : []
|
|
6205
|
+
)
|
|
6206
|
+
)
|
|
6207
|
+
);
|
|
6208
|
+
return [...raised].flatMap((name) => {
|
|
6209
|
+
const exception = listedException(name, pattern.libraryExceptions ?? {}) ?? unreadException(name);
|
|
6210
|
+
const terminal = raisedTerminal(pattern, exception, handlers, location);
|
|
6211
|
+
if (terminal.kind === "throw" && !rescuedBy(handlers, exception)) {
|
|
6212
|
+
return [];
|
|
6213
|
+
}
|
|
6214
|
+
return [
|
|
6215
|
+
{
|
|
6216
|
+
conditions: [
|
|
6217
|
+
{
|
|
6218
|
+
sourceText: `${name} raised`,
|
|
6219
|
+
structured: null,
|
|
6220
|
+
polarity: "positive",
|
|
6221
|
+
source: "earlyThrow"
|
|
6222
|
+
}
|
|
6223
|
+
],
|
|
6224
|
+
terminal,
|
|
6225
|
+
effects: [],
|
|
6226
|
+
location,
|
|
6227
|
+
isDefault: false
|
|
6228
|
+
}
|
|
6229
|
+
];
|
|
6230
|
+
});
|
|
6231
|
+
}
|
|
6232
|
+
|
|
6233
|
+
// src/responseBody.ts
|
|
6234
|
+
import { absentReading, writtenReading } from "@suss/extractor";
|
|
6235
|
+
import { shapeOfValue } from "@suss/values";
|
|
6236
|
+
var BODY_KINDS = /* @__PURE__ */ new Set([
|
|
6237
|
+
"record",
|
|
6238
|
+
"sequence",
|
|
6239
|
+
"unbounded"
|
|
6240
|
+
]);
|
|
6241
|
+
function serializedValue(node, serializers) {
|
|
6242
|
+
const receiver = field(node, "receiver");
|
|
6243
|
+
const method = field(node, "method")?.text;
|
|
6244
|
+
if (node.type !== "call" || receiver === null || method === void 0 || !serializers.has(method) || field(node, "arguments") !== null) {
|
|
6245
|
+
return node;
|
|
6246
|
+
}
|
|
6247
|
+
return serializedValue(receiver, serializers);
|
|
6248
|
+
}
|
|
6249
|
+
function bodyReadingOfCall(call, declaration, facts) {
|
|
6250
|
+
if (declaration.bodyKeyword === void 0 || call.type !== "call") {
|
|
6251
|
+
return absentReading;
|
|
6252
|
+
}
|
|
6253
|
+
const argument = readCallArgs(field(call, "arguments")).keyword[declaration.bodyKeyword];
|
|
6254
|
+
if (argument === void 0) {
|
|
6255
|
+
return absentReading;
|
|
6256
|
+
}
|
|
6257
|
+
const body = serializedValue(
|
|
6258
|
+
argument,
|
|
6259
|
+
new Set(declaration.bodySerializers ?? [])
|
|
6260
|
+
);
|
|
6261
|
+
const value = evaluatedValue(body, facts);
|
|
6262
|
+
return BODY_KINDS.has(value.kind) ? writtenReading(shapeOfValue(value), rangeOf(argument)) : absentReading;
|
|
6263
|
+
}
|
|
6264
|
+
|
|
5569
6265
|
// src/responseStatus.ts
|
|
5570
6266
|
function statusArgumentOf(call, declaration) {
|
|
5571
6267
|
const args = readCallArgs(field(call, "arguments"));
|
|
@@ -5578,7 +6274,11 @@ function statusArgumentOf(call, declaration) {
|
|
|
5578
6274
|
}
|
|
5579
6275
|
return args.positional[declaration.statusArgument] ?? null;
|
|
5580
6276
|
}
|
|
5581
|
-
function statusNumberOf(node, names, facts) {
|
|
6277
|
+
function statusNumberOf(node, names, facts, bound = /* @__PURE__ */ new Map()) {
|
|
6278
|
+
const passed = node.type === "identifier" ? bound.get(node.text) : void 0;
|
|
6279
|
+
if (passed !== void 0) {
|
|
6280
|
+
return passed;
|
|
6281
|
+
}
|
|
5582
6282
|
const value = evaluatedValue(node, facts);
|
|
5583
6283
|
const constant2 = constantOf3(value);
|
|
5584
6284
|
if (typeof constant2 === "number") {
|
|
@@ -5602,21 +6302,42 @@ function declarationOf(node, byName, locals) {
|
|
|
5602
6302
|
const name = field(node, "method")?.text;
|
|
5603
6303
|
return name === void 0 ? void 0 : byName.get(name);
|
|
5604
6304
|
}
|
|
5605
|
-
function
|
|
5606
|
-
|
|
6305
|
+
function responderOf(node, responders) {
|
|
6306
|
+
const declaration = declarationOf(node, responders.byName, responders.locals);
|
|
6307
|
+
if (declaration !== void 0) {
|
|
6308
|
+
return { kind: "declared", declaration };
|
|
6309
|
+
}
|
|
6310
|
+
const name = receiverlessName(node, responders.locals);
|
|
6311
|
+
const reading = name === null || responders.helper === void 0 ? null : responders.helper(name, node);
|
|
6312
|
+
return reading === null ? void 0 : { kind: "helper", reading };
|
|
6313
|
+
}
|
|
6314
|
+
function receiverlessName(node, locals) {
|
|
6315
|
+
if (node.type === "identifier") {
|
|
6316
|
+
return isBareMethodCall(node, locals) ? node.text : null;
|
|
6317
|
+
}
|
|
6318
|
+
if (node.type !== "call" || field(node, "receiver") !== null) {
|
|
6319
|
+
return null;
|
|
6320
|
+
}
|
|
6321
|
+
return field(node, "method")?.text ?? null;
|
|
6322
|
+
}
|
|
6323
|
+
function readingOfResponder(call, responder, names, options) {
|
|
6324
|
+
return responder.kind === "helper" ? responder.reading : readingOfCall(call, responder.declaration, names, options);
|
|
6325
|
+
}
|
|
6326
|
+
function collectResponseCalls(node, responders, found) {
|
|
6327
|
+
for (const child of children(node)) {
|
|
5607
6328
|
if (child === null || OWN_BODY_TYPES.has(child.type)) {
|
|
5608
6329
|
continue;
|
|
5609
6330
|
}
|
|
5610
|
-
if (
|
|
6331
|
+
if (responderOf(child, responders) !== void 0) {
|
|
5611
6332
|
found.push(child);
|
|
5612
6333
|
continue;
|
|
5613
6334
|
}
|
|
5614
|
-
collectResponseCalls(child,
|
|
6335
|
+
collectResponseCalls(child, responders, found);
|
|
5615
6336
|
}
|
|
5616
6337
|
return found;
|
|
5617
6338
|
}
|
|
5618
6339
|
function collectReturns(node, found) {
|
|
5619
|
-
for (const child of node
|
|
6340
|
+
for (const child of children(node)) {
|
|
5620
6341
|
if (child === null || OWN_BODY_TYPES.has(child.type)) {
|
|
5621
6342
|
continue;
|
|
5622
6343
|
}
|
|
@@ -5628,19 +6349,24 @@ function collectReturns(node, found) {
|
|
|
5628
6349
|
}
|
|
5629
6350
|
return found;
|
|
5630
6351
|
}
|
|
5631
|
-
function readingOfCall(call, declaration, names,
|
|
6352
|
+
function readingOfCall(call, declaration, names, options) {
|
|
5632
6353
|
const argument = statusArgumentOf(call, declaration);
|
|
5633
6354
|
if (argument === null) {
|
|
5634
|
-
return declaration.defaultStatusCode === void 0 ?
|
|
6355
|
+
return declaration.defaultStatusCode === void 0 ? absentReading2 : writtenReading2(declaration.defaultStatusCode, rangeOf(call));
|
|
5635
6356
|
}
|
|
5636
|
-
const status = statusNumberOf(
|
|
6357
|
+
const status = statusNumberOf(
|
|
6358
|
+
argument,
|
|
6359
|
+
names,
|
|
6360
|
+
options.facts,
|
|
6361
|
+
options.boundStatuses
|
|
6362
|
+
);
|
|
5637
6363
|
if (status === null) {
|
|
5638
6364
|
return unreadableReading(
|
|
5639
6365
|
"This response writes a status that does not settle on a number here, so this outcome claims none",
|
|
5640
6366
|
rangeOf(argument)
|
|
5641
6367
|
);
|
|
5642
6368
|
}
|
|
5643
|
-
return
|
|
6369
|
+
return writtenReading2(status, rangeOf(argument));
|
|
5644
6370
|
}
|
|
5645
6371
|
function effectsReaching(effects, conditions) {
|
|
5646
6372
|
return effects.filter(
|
|
@@ -5669,6 +6395,45 @@ function conditionInputs(method, paths, facts) {
|
|
|
5669
6395
|
);
|
|
5670
6396
|
return operands.length === 0 ? void 0 : guardInputs(operands, { facts, unit: method });
|
|
5671
6397
|
}
|
|
6398
|
+
function boundStatusArguments(helper, call, pattern, facts) {
|
|
6399
|
+
const parameters = children(field(helper, "parameters")).filter(
|
|
6400
|
+
(child) => child.type === "identifier"
|
|
6401
|
+
);
|
|
6402
|
+
const args = call.type === "call" ? readCallArgs(field(call, "arguments")).positional : [];
|
|
6403
|
+
const bound = /* @__PURE__ */ new Map();
|
|
6404
|
+
parameters.forEach((parameter, index) => {
|
|
6405
|
+
const arg = args[index];
|
|
6406
|
+
const status = arg === void 0 ? null : statusNumberOf(arg, pattern.statusCodeNames ?? {}, facts);
|
|
6407
|
+
if (status !== null) {
|
|
6408
|
+
bound.set(parameter.text, status);
|
|
6409
|
+
}
|
|
6410
|
+
});
|
|
6411
|
+
return bound;
|
|
6412
|
+
}
|
|
6413
|
+
function helperResponse(helper, pattern, facts, boundStatuses = /* @__PURE__ */ new Map()) {
|
|
6414
|
+
const branches = responseBranches(helper, pattern, [], void 0, {
|
|
6415
|
+
fallthrough: "handOn",
|
|
6416
|
+
facts,
|
|
6417
|
+
boundStatuses
|
|
6418
|
+
});
|
|
6419
|
+
if (branches === null || branches.some((branch) => branch.terminal.kind !== "response")) {
|
|
6420
|
+
return null;
|
|
6421
|
+
}
|
|
6422
|
+
const statuses = new Set(
|
|
6423
|
+
branches.map((branch) => {
|
|
6424
|
+
const reading = branch.statusCodeReading?.reading;
|
|
6425
|
+
if (reading?.kind === "absent") {
|
|
6426
|
+
return branch.statusCodeReading?.libraryDefault ?? null;
|
|
6427
|
+
}
|
|
6428
|
+
return reading?.kind === "written" ? reading.value : null;
|
|
6429
|
+
})
|
|
6430
|
+
);
|
|
6431
|
+
const [only] = [...statuses];
|
|
6432
|
+
return statuses.size === 1 && typeof only === "number" ? writtenReading2(only, rangeOf(helper)) : unreadableReading(
|
|
6433
|
+
"This response is sent by a helper whose status does not settle on one number here, so this outcome claims none",
|
|
6434
|
+
rangeOf(helper)
|
|
6435
|
+
);
|
|
6436
|
+
}
|
|
5672
6437
|
function branchOf(outcome, pattern, effects, extraEffects, options) {
|
|
5673
6438
|
if (outcome.fellThrough === true && options.fallthrough === "handOn") {
|
|
5674
6439
|
return handsOnBranch(outcome, effects, extraEffects);
|
|
@@ -5691,6 +6456,7 @@ function branchOf(outcome, pattern, effects, extraEffects, options) {
|
|
|
5691
6456
|
reading: outcome.reading,
|
|
5692
6457
|
libraryDefault: pattern.defaultStatusCode
|
|
5693
6458
|
},
|
|
6459
|
+
...outcome.body === void 0 ? {} : { bodyShapeReading: { reading: outcome.body } },
|
|
5694
6460
|
effects: effectsReaching(effects, outcome.conditions),
|
|
5695
6461
|
...extraEffects === void 0 ? {} : { extraEffects },
|
|
5696
6462
|
location: outcome.location,
|
|
@@ -5718,20 +6484,26 @@ function handsOnBranch(outcome, effects, extraEffects) {
|
|
|
5718
6484
|
isDefault: outcome.conditions.length === 0
|
|
5719
6485
|
};
|
|
5720
6486
|
}
|
|
5721
|
-
function returnPathBranches(method, effects, exits = [], facts) {
|
|
6487
|
+
function returnPathBranches(method, effects, exits = [], facts, keepsArms, raises2 = []) {
|
|
5722
6488
|
const body = field(method, "body");
|
|
5723
6489
|
if (body === null) {
|
|
5724
6490
|
return null;
|
|
5725
6491
|
}
|
|
5726
6492
|
const returns = collectReturns(body, []);
|
|
5727
6493
|
const exitCalls = exits.map((exit) => exit.call);
|
|
5728
|
-
const
|
|
6494
|
+
const terminals = [
|
|
6495
|
+
...returns,
|
|
6496
|
+
...exitCalls,
|
|
6497
|
+
...raises2.map((raise) => raise.call)
|
|
6498
|
+
];
|
|
6499
|
+
const lowered = lowerRubyBody(body, terminals, exitCalls);
|
|
5729
6500
|
const enumerated = enumerateOrDegrade2(
|
|
5730
6501
|
{
|
|
5731
6502
|
statements: lowered.statements,
|
|
5732
|
-
terminalsByStmt: lowered.terminalsByStmt
|
|
6503
|
+
terminalsByStmt: lowered.terminalsByStmt,
|
|
6504
|
+
...keepsArms === void 0 ? {} : { keepsArms }
|
|
5733
6505
|
},
|
|
5734
|
-
|
|
6506
|
+
terminals
|
|
5735
6507
|
);
|
|
5736
6508
|
const inputs = conditionInputs(
|
|
5737
6509
|
method,
|
|
@@ -5759,8 +6531,8 @@ function returnPathBranches(method, effects, exits = [], facts) {
|
|
|
5759
6531
|
returnTerminal(rangeOf(statement))
|
|
5760
6532
|
);
|
|
5761
6533
|
}
|
|
5762
|
-
for (const
|
|
5763
|
-
push(enumerated.byTerminal.get(
|
|
6534
|
+
for (const ending of [...exits, ...raises2]) {
|
|
6535
|
+
push(enumerated.byTerminal.get(ending.call) ?? [], ending.terminal);
|
|
5764
6536
|
}
|
|
5765
6537
|
push(enumerated.fallthrough, returnTerminal(rangeOf(method)));
|
|
5766
6538
|
return branches.length === 0 ? null : branches;
|
|
@@ -5785,13 +6557,22 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
5785
6557
|
if (declarations.length === 0 || body === null) {
|
|
5786
6558
|
return null;
|
|
5787
6559
|
}
|
|
5788
|
-
const
|
|
5789
|
-
|
|
5790
|
-
|
|
6560
|
+
const responders = {
|
|
6561
|
+
byName: declarationsByName(declarations),
|
|
6562
|
+
locals: localNamesIn(method),
|
|
6563
|
+
helper: options.respondingHelper
|
|
6564
|
+
};
|
|
6565
|
+
const responses = collectResponseCalls(body, responders, []);
|
|
5791
6566
|
const returns = collectReturns(body, []);
|
|
5792
|
-
const
|
|
6567
|
+
const raises2 = options.raises === void 0 ? [] : escapingRaises(method);
|
|
6568
|
+
const lowered = lowerRubyBody(
|
|
6569
|
+
body,
|
|
6570
|
+
[...returns, ...raises2],
|
|
6571
|
+
responses,
|
|
6572
|
+
pattern.formatSwitch
|
|
6573
|
+
);
|
|
5793
6574
|
const bareReturns = returns.filter(
|
|
5794
|
-
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node,
|
|
6575
|
+
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, responders, []).length === 0
|
|
5795
6576
|
);
|
|
5796
6577
|
const terminals = [...responses, ...bareReturns];
|
|
5797
6578
|
const enumerated = enumerateOrDegrade2(
|
|
@@ -5799,7 +6580,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
5799
6580
|
statements: lowered.statements,
|
|
5800
6581
|
terminalsByStmt: lowered.terminalsByStmt
|
|
5801
6582
|
},
|
|
5802
|
-
terminals
|
|
6583
|
+
[...terminals, ...raises2]
|
|
5803
6584
|
);
|
|
5804
6585
|
const statusNames = pattern.statusCodeNames ?? {};
|
|
5805
6586
|
const inputs = conditionInputs(
|
|
@@ -5810,30 +6591,58 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
5810
6591
|
const conditionsOf = (path7) => path7.map((condition) => conditionOf(condition, inputs));
|
|
5811
6592
|
const outcomes = [];
|
|
5812
6593
|
for (const terminal of terminals) {
|
|
5813
|
-
const
|
|
5814
|
-
const reading =
|
|
6594
|
+
const responder = responderOf(terminal, responders);
|
|
6595
|
+
const reading = responder === void 0 ? absentReading2 : readingOfResponder(terminal, responder, statusNames, options);
|
|
6596
|
+
const body2 = responder?.kind === "declared" ? bodyReadingOfCall(terminal, responder.declaration, options.facts) : void 0;
|
|
5815
6597
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
5816
6598
|
outcomes.push({
|
|
5817
6599
|
conditions: conditionsOf(path7),
|
|
5818
6600
|
reading,
|
|
5819
|
-
|
|
6601
|
+
...body2 === void 0 ? {} : { body: body2 },
|
|
6602
|
+
location: rangeOf(terminal),
|
|
6603
|
+
// A bare `return` wrote no response, so a filter that takes it
|
|
6604
|
+
// hands the request on the same as one that reaches its end.
|
|
6605
|
+
...responder === void 0 ? { fellThrough: true } : {}
|
|
5820
6606
|
});
|
|
5821
6607
|
}
|
|
5822
6608
|
}
|
|
5823
6609
|
for (const path7 of enumerated.fallthrough) {
|
|
5824
6610
|
outcomes.push({
|
|
5825
6611
|
conditions: conditionsOf(path7),
|
|
5826
|
-
reading:
|
|
6612
|
+
reading: absentReading2,
|
|
5827
6613
|
location: rangeOf(method),
|
|
5828
6614
|
fellThrough: true
|
|
5829
6615
|
});
|
|
5830
6616
|
}
|
|
5831
|
-
|
|
6617
|
+
const read = options.raises;
|
|
6618
|
+
const raised = raises2.flatMap((call) => {
|
|
6619
|
+
const terminal = raisedTerminal(
|
|
6620
|
+
pattern,
|
|
6621
|
+
read?.classes.get(call) ?? null,
|
|
6622
|
+
read?.handlers ?? [],
|
|
6623
|
+
rangeOf(call)
|
|
6624
|
+
);
|
|
6625
|
+
return (enumerated.byTerminal.get(call) ?? []).map((path7) => {
|
|
6626
|
+
const conditions = conditionsOf(path7);
|
|
6627
|
+
return {
|
|
6628
|
+
conditions,
|
|
6629
|
+
terminal,
|
|
6630
|
+
effects: effectsReaching(effects, conditions),
|
|
6631
|
+
...extraEffects === void 0 ? {} : { extraEffects },
|
|
6632
|
+
location: terminal.location,
|
|
6633
|
+
isDefault: false
|
|
6634
|
+
};
|
|
6635
|
+
});
|
|
6636
|
+
});
|
|
6637
|
+
if (outcomes.length === 0 && raised.length === 0) {
|
|
5832
6638
|
return null;
|
|
5833
6639
|
}
|
|
5834
|
-
return
|
|
5835
|
-
(
|
|
5836
|
-
|
|
6640
|
+
return [
|
|
6641
|
+
...outcomes.map(
|
|
6642
|
+
(outcome) => branchOf(outcome, pattern, effects, extraEffects, options)
|
|
6643
|
+
),
|
|
6644
|
+
...raised
|
|
6645
|
+
];
|
|
5837
6646
|
}
|
|
5838
6647
|
|
|
5839
6648
|
// src/clientCalls.ts
|
|
@@ -5846,7 +6655,16 @@ function clientCallUnits(root, pack, pattern, options) {
|
|
|
5846
6655
|
}
|
|
5847
6656
|
for (const call of callsUnder2(method)) {
|
|
5848
6657
|
for (const request of requestCalls(call, pattern, options)) {
|
|
5849
|
-
units.push(
|
|
6658
|
+
units.push(
|
|
6659
|
+
clientUnit(
|
|
6660
|
+
method,
|
|
6661
|
+
name,
|
|
6662
|
+
{ ...request, call },
|
|
6663
|
+
pattern,
|
|
6664
|
+
pack,
|
|
6665
|
+
options
|
|
6666
|
+
)
|
|
6667
|
+
);
|
|
5850
6668
|
}
|
|
5851
6669
|
}
|
|
5852
6670
|
}
|
|
@@ -5904,10 +6722,7 @@ function requestArgument(call, called, pattern) {
|
|
|
5904
6722
|
return pattern.requestObject?.attribute === called ? args.positional[0] : void 0;
|
|
5905
6723
|
}
|
|
5906
6724
|
function methodDefinitions(node, found = []) {
|
|
5907
|
-
for (const child of node
|
|
5908
|
-
if (child === null) {
|
|
5909
|
-
continue;
|
|
5910
|
-
}
|
|
6725
|
+
for (const child of children(node)) {
|
|
5911
6726
|
if (child.type === "method" || child.type === "singleton_method") {
|
|
5912
6727
|
found.push(child);
|
|
5913
6728
|
}
|
|
@@ -5916,10 +6731,7 @@ function methodDefinitions(node, found = []) {
|
|
|
5916
6731
|
return found;
|
|
5917
6732
|
}
|
|
5918
6733
|
function callsUnder2(node, found = []) {
|
|
5919
|
-
for (const child of node
|
|
5920
|
-
if (child === null) {
|
|
5921
|
-
continue;
|
|
5922
|
-
}
|
|
6734
|
+
for (const child of children(node)) {
|
|
5923
6735
|
if (child.type === "call") {
|
|
5924
6736
|
found.push(child);
|
|
5925
6737
|
}
|
|
@@ -5936,7 +6748,7 @@ function requestCalls(call, pattern, options) {
|
|
|
5936
6748
|
const read = (site) => requestCall(
|
|
5937
6749
|
call,
|
|
5938
6750
|
called,
|
|
5939
|
-
|
|
6751
|
+
receiverBase(receiver, pattern, options, site),
|
|
5940
6752
|
pattern,
|
|
5941
6753
|
options,
|
|
5942
6754
|
site
|
|
@@ -5949,7 +6761,10 @@ function requestCalls(call, pattern, options) {
|
|
|
5949
6761
|
for (const site of constructionSitesOf(call, options.facts)) {
|
|
5950
6762
|
const stated = read(site);
|
|
5951
6763
|
if (stated !== null) {
|
|
5952
|
-
byBoundary.set(
|
|
6764
|
+
byBoundary.set(
|
|
6765
|
+
`${stated.method} ${stated.host ?? ""} ${stated.path}`,
|
|
6766
|
+
stated
|
|
6767
|
+
);
|
|
5953
6768
|
}
|
|
5954
6769
|
}
|
|
5955
6770
|
if (byBoundary.size > 0) {
|
|
@@ -5957,12 +6772,12 @@ function requestCalls(call, pattern, options) {
|
|
|
5957
6772
|
}
|
|
5958
6773
|
return plain === null ? [] : [plain];
|
|
5959
6774
|
}
|
|
5960
|
-
function requestCall(call, called,
|
|
6775
|
+
function requestCall(call, called, base, pattern, options, site) {
|
|
5961
6776
|
const args = readCallArgs(field(call, "arguments"));
|
|
5962
6777
|
const verb = pattern.verbMethodNames[called];
|
|
5963
6778
|
if (verb !== void 0) {
|
|
5964
|
-
const
|
|
5965
|
-
return
|
|
6779
|
+
const target2 = targetAt(urlNodeIn(args, pattern), options, site);
|
|
6780
|
+
return target2 === null ? null : { method: verb, ...underBase(base, target2) };
|
|
5966
6781
|
}
|
|
5967
6782
|
const sent = pattern.requestObject;
|
|
5968
6783
|
if (sent === void 0 || sent.attribute !== called) {
|
|
@@ -5972,8 +6787,14 @@ function requestCall(call, called, prefix, pattern, options, site) {
|
|
|
5972
6787
|
if (built === null) {
|
|
5973
6788
|
return null;
|
|
5974
6789
|
}
|
|
5975
|
-
const
|
|
5976
|
-
return
|
|
6790
|
+
const target = targetAt(built.url, options, site);
|
|
6791
|
+
return target === null ? null : { method: built.method, ...underBase(base, target) };
|
|
6792
|
+
}
|
|
6793
|
+
function underBase(base, target) {
|
|
6794
|
+
if (base === null || target.host !== void 0) {
|
|
6795
|
+
return target;
|
|
6796
|
+
}
|
|
6797
|
+
return { path: base.path + target.path, host: base.host };
|
|
5977
6798
|
}
|
|
5978
6799
|
function requestBuilt(argument, sent, options, site) {
|
|
5979
6800
|
if (argument === void 0) {
|
|
@@ -5994,16 +6815,19 @@ function requestBuilt(argument, sent, options, site) {
|
|
|
5994
6815
|
function isLibraryReceiver(receiver, pattern, options) {
|
|
5995
6816
|
return namesConstant2(receiver, pattern.constantName) || builderCallBehind(receiver, pattern, options.facts) !== null;
|
|
5996
6817
|
}
|
|
5997
|
-
function
|
|
6818
|
+
function receiverBase(receiver, pattern, options, site) {
|
|
5998
6819
|
const built = builderCallBehind(receiver, pattern, options.facts);
|
|
5999
6820
|
if (built === null) {
|
|
6000
|
-
return
|
|
6821
|
+
return null;
|
|
6001
6822
|
}
|
|
6002
6823
|
const args = readCallArgs(field(built, "arguments"));
|
|
6003
6824
|
const keyword = pattern.builderUrlKeyword;
|
|
6004
6825
|
const base = (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[0];
|
|
6005
|
-
|
|
6006
|
-
|
|
6826
|
+
if (base === void 0) {
|
|
6827
|
+
return null;
|
|
6828
|
+
}
|
|
6829
|
+
const value = evaluatedValue(base, options.facts, void 0, site);
|
|
6830
|
+
return { path: trimmed(pathOf(value) ?? ""), host: hostOf(value) };
|
|
6007
6831
|
}
|
|
6008
6832
|
function trimmed(path7) {
|
|
6009
6833
|
return path7 === "/" ? "" : path7.replace(/\/+$/, "");
|
|
@@ -6026,22 +6850,32 @@ function builderCallBehind(receiver, pattern, facts) {
|
|
|
6026
6850
|
const constant2 = field(written, "receiver");
|
|
6027
6851
|
return constant2 !== null && namesConstant2(constant2, pattern.constantName) ? written : null;
|
|
6028
6852
|
}
|
|
6029
|
-
function urlIn(args, pattern, options, site) {
|
|
6030
|
-
return pathAt(urlNodeIn(args, pattern), options, site);
|
|
6031
|
-
}
|
|
6032
6853
|
function urlNodeIn(args, pattern) {
|
|
6033
6854
|
const keyword = pattern.url.keyword;
|
|
6034
6855
|
return (keyword === void 0 ? void 0 : args.keyword[keyword]) ?? args.positional[pattern.url.position];
|
|
6035
6856
|
}
|
|
6036
|
-
function
|
|
6857
|
+
function targetAt(written, options, site) {
|
|
6037
6858
|
if (written === void 0) {
|
|
6038
6859
|
return null;
|
|
6039
6860
|
}
|
|
6040
|
-
const
|
|
6041
|
-
|
|
6861
|
+
const value = evaluatedValue(written, options.facts, void 0, site);
|
|
6862
|
+
const path7 = pathOf(value);
|
|
6863
|
+
return path7 === void 0 || namesNothing(path7) ? null : { path: path7, host: hostOf(value) };
|
|
6042
6864
|
}
|
|
6043
6865
|
function clientUnit(method, name, request, pattern, pack, options) {
|
|
6044
6866
|
const range = rangeOf(method);
|
|
6867
|
+
const accessors = responseAccessors(pattern);
|
|
6868
|
+
const statusMembers = statusMembersOf(accessors);
|
|
6869
|
+
const reading = {
|
|
6870
|
+
facts: options.facts,
|
|
6871
|
+
statusMembers,
|
|
6872
|
+
handOff: {
|
|
6873
|
+
request: request.call,
|
|
6874
|
+
bodyMethods: accessors.bodyAccessors ?? [],
|
|
6875
|
+
facts: options.facts,
|
|
6876
|
+
readHelper: (helper) => callerBranches(helper, { facts: options.facts, statusMembers })
|
|
6877
|
+
}
|
|
6878
|
+
};
|
|
6045
6879
|
return {
|
|
6046
6880
|
identity: {
|
|
6047
6881
|
name,
|
|
@@ -6057,21 +6891,30 @@ function clientUnit(method, name, request, pattern, pack, options) {
|
|
|
6057
6891
|
transport: pack.protocol,
|
|
6058
6892
|
method: request.method,
|
|
6059
6893
|
path: request.path,
|
|
6894
|
+
host: request.host,
|
|
6060
6895
|
recognition: pack.name
|
|
6061
6896
|
}),
|
|
6062
6897
|
parameters: [],
|
|
6063
|
-
branches: callerBranches(method,
|
|
6064
|
-
...
|
|
6898
|
+
branches: callerBranches(method, reading),
|
|
6899
|
+
...accessors,
|
|
6065
6900
|
bodyContent: "statements",
|
|
6066
6901
|
dependencyCalls: [],
|
|
6067
6902
|
declaredContract: null
|
|
6068
6903
|
};
|
|
6069
6904
|
}
|
|
6070
|
-
function callerBranches(method,
|
|
6071
|
-
const
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6905
|
+
function callerBranches(method, reading) {
|
|
6906
|
+
const sites = invocationSites(method, void 0, void 0, reading.facts);
|
|
6907
|
+
const effects = sites.map((site) => site.effect);
|
|
6908
|
+
const keepsArms = (condition) => testsStatus(predicateOf(condition), reading.statusMembers);
|
|
6909
|
+
const found = returnPathBranches(
|
|
6910
|
+
method,
|
|
6911
|
+
effects,
|
|
6912
|
+
[],
|
|
6913
|
+
reading.facts,
|
|
6914
|
+
keepsArms,
|
|
6915
|
+
raiseEndings(method)
|
|
6916
|
+
) ?? [returnBranch(rangeOf(method), effects)];
|
|
6917
|
+
return reading.handOff === void 0 ? found : throughResponseHelpers(found, sites, reading.handOff);
|
|
6075
6918
|
}
|
|
6076
6919
|
function responseAccessors(pattern) {
|
|
6077
6920
|
const response = pattern.response;
|
|
@@ -6082,7 +6925,8 @@ function responseAccessors(pattern) {
|
|
|
6082
6925
|
...response.body === void 0 ? {} : { bodyAccessors: response.body },
|
|
6083
6926
|
...response.statusCode === void 0 ? {} : { statusAccessors: response.statusCode },
|
|
6084
6927
|
...response.success === void 0 ? {} : { successAccessors: response.success },
|
|
6085
|
-
...response.failureDelivery === void 0 ? {} : { failureDelivery: response.failureDelivery }
|
|
6928
|
+
...response.failureDelivery === void 0 ? {} : { failureDelivery: response.failureDelivery },
|
|
6929
|
+
...response.redirectDelivery === void 0 ? {} : { redirectDelivery: response.redirectDelivery }
|
|
6086
6930
|
};
|
|
6087
6931
|
}
|
|
6088
6932
|
function returnBranch(range, effects) {
|
|
@@ -6133,9 +6977,7 @@ function elementSite(node) {
|
|
|
6133
6977
|
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
6134
6978
|
return null;
|
|
6135
6979
|
}
|
|
6136
|
-
const index = node.
|
|
6137
|
-
(child) => child !== null && child.id !== object.id
|
|
6138
|
-
);
|
|
6980
|
+
const index = children(node).find((child) => child.id !== object.id);
|
|
6139
6981
|
if (index === void 0) {
|
|
6140
6982
|
return null;
|
|
6141
6983
|
}
|
|
@@ -6316,7 +7158,7 @@ function followsExitWhenAbsent(parent, child, subject) {
|
|
|
6316
7158
|
if (!STATEMENT_LIST_TYPES.has(parent.type)) {
|
|
6317
7159
|
return false;
|
|
6318
7160
|
}
|
|
6319
|
-
const statements = parent
|
|
7161
|
+
const statements = children(parent);
|
|
6320
7162
|
const at = statements.findIndex((statement) => statement?.id === child.id);
|
|
6321
7163
|
return statements.slice(0, at).some(
|
|
6322
7164
|
(statement) => statement !== null && leavesWhenAbsent(statement, subject)
|
|
@@ -6368,7 +7210,7 @@ function continuationRaises(from) {
|
|
|
6368
7210
|
let parent = current.parent;
|
|
6369
7211
|
while (parent !== null) {
|
|
6370
7212
|
if (STATEMENT_LIST_TYPES.has(parent.type)) {
|
|
6371
|
-
const statements = parent
|
|
7213
|
+
const statements = children(parent);
|
|
6372
7214
|
const at = statements.findIndex((one) => one?.id === current.id);
|
|
6373
7215
|
const exit = firstExit(statements.slice(at + 1));
|
|
6374
7216
|
if (exit !== null) {
|
|
@@ -6402,20 +7244,13 @@ function firstExit(statements) {
|
|
|
6402
7244
|
return null;
|
|
6403
7245
|
}
|
|
6404
7246
|
function exitOf2(statement) {
|
|
6405
|
-
if (
|
|
7247
|
+
if (isRaiseCall(statement)) {
|
|
6406
7248
|
return "raise";
|
|
6407
7249
|
}
|
|
6408
7250
|
if (LEAVING_TYPES.has(statement.type)) {
|
|
6409
7251
|
return "leave";
|
|
6410
7252
|
}
|
|
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 ?? "");
|
|
7253
|
+
return STATEMENT_LIST_TYPES.has(statement.type) ? firstExit(children(statement)) : null;
|
|
6419
7254
|
}
|
|
6420
7255
|
function isPresentWhen(test, outcome, subject) {
|
|
6421
7256
|
const expression = peelParens(test);
|
|
@@ -6483,7 +7318,7 @@ function isNilCheckOn(call, receiver) {
|
|
|
6483
7318
|
return call.type === "call" && field(call, "method")?.text === "nil?" && field(call, "receiver")?.id === receiver.id;
|
|
6484
7319
|
}
|
|
6485
7320
|
function peelParens(node) {
|
|
6486
|
-
const inner = node.type === "parenthesized_statements" && node.namedChildCount === 1 ? node.
|
|
7321
|
+
const inner = node.type === "parenthesized_statements" && node.namedChildCount === 1 ? node.namedChild(0) : null;
|
|
6487
7322
|
return inner === null || inner === void 0 ? node : peelParens(inner);
|
|
6488
7323
|
}
|
|
6489
7324
|
function climbParens(node) {
|
|
@@ -6528,7 +7363,7 @@ var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method",
|
|
|
6528
7363
|
function envReadEffects(root, facts) {
|
|
6529
7364
|
const helpers = helperFactsOf(facts);
|
|
6530
7365
|
const slots = [];
|
|
6531
|
-
walkDescendants2(root, null, {
|
|
7366
|
+
walkDescendants2(root, null, children, {
|
|
6532
7367
|
at: (node) => {
|
|
6533
7368
|
const read = envReadAt(node);
|
|
6534
7369
|
if (read !== null) {
|
|
@@ -6608,7 +7443,7 @@ function namedParameters(db) {
|
|
|
6608
7443
|
return named2;
|
|
6609
7444
|
}
|
|
6610
7445
|
function emitEnvFacts(db, file, root) {
|
|
6611
|
-
walkDescendants2(root, null, {
|
|
7446
|
+
walkDescendants2(root, null, children, {
|
|
6612
7447
|
at: (node) => {
|
|
6613
7448
|
if (isEnv(node) && handsOnward(node)) {
|
|
6614
7449
|
db.add("environmentObject", [
|
|
@@ -6736,6 +7571,7 @@ function envSiteAt(node) {
|
|
|
6736
7571
|
}
|
|
6737
7572
|
|
|
6738
7573
|
// src/filters.ts
|
|
7574
|
+
var RESCUES_NOTHING = { refs: [], someUnread: false };
|
|
6739
7575
|
function controllerFilters(pattern, ancestry, read = {}) {
|
|
6740
7576
|
const forms = pattern.filters ?? [];
|
|
6741
7577
|
if (forms.length === 0) {
|
|
@@ -6749,18 +7585,29 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
6749
7585
|
}
|
|
6750
7586
|
}
|
|
6751
7587
|
let declared = [];
|
|
6752
|
-
|
|
7588
|
+
const inlineSeen = /* @__PURE__ */ new Map();
|
|
7589
|
+
for (const { block, statement } of inheritedStatements(ancestry)) {
|
|
6753
7590
|
const called = calledName(statement);
|
|
6754
7591
|
if (called === null) {
|
|
6755
7592
|
continue;
|
|
6756
7593
|
}
|
|
6757
7594
|
const form = byName.get(called);
|
|
6758
7595
|
if (form !== void 0) {
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
7596
|
+
const inlineKey = `${block.file}\0${block.info.qualifiedName}\0${form.name}`;
|
|
7597
|
+
const ordinal = inlineSeen.get(inlineKey) ?? 0;
|
|
7598
|
+
const declarations = declarationsOf(
|
|
7599
|
+
statement,
|
|
7600
|
+
form,
|
|
7601
|
+
block,
|
|
7602
|
+
read.facts,
|
|
7603
|
+
ordinal
|
|
7604
|
+
);
|
|
7605
|
+
if (declarations.some((one) => one.inline !== void 0)) {
|
|
7606
|
+
inlineSeen.set(inlineKey, ordinal + 1);
|
|
7607
|
+
}
|
|
7608
|
+
for (const declaration of declarations) {
|
|
7609
|
+
const earlier = declaration.filter.onThrow === true ? void 0 : declared.find((one) => sameFilter(one, declaration));
|
|
7610
|
+
declared = [...declared.filter((one) => one !== earlier), declaration];
|
|
6764
7611
|
}
|
|
6765
7612
|
continue;
|
|
6766
7613
|
}
|
|
@@ -6771,62 +7618,105 @@ function controllerFilters(pattern, ancestry, read = {}) {
|
|
|
6771
7618
|
}
|
|
6772
7619
|
const resolved = [];
|
|
6773
7620
|
for (const declaration of declared) {
|
|
6774
|
-
const
|
|
6775
|
-
|
|
7621
|
+
const written = writtenAt(
|
|
7622
|
+
declaration,
|
|
7623
|
+
filterMethod(ancestry, declaration.methodName, read)
|
|
7624
|
+
);
|
|
7625
|
+
if (written === null) {
|
|
6776
7626
|
continue;
|
|
6777
7627
|
}
|
|
6778
7628
|
resolved.push({
|
|
6779
7629
|
filter: declaration.filter,
|
|
6780
|
-
|
|
6781
|
-
method: found.method,
|
|
6782
|
-
file: found.block.file,
|
|
6783
|
-
enclosingQualifiedName: found.block.info.qualifiedName,
|
|
7630
|
+
...written,
|
|
6784
7631
|
only: declaration.only,
|
|
6785
|
-
except: declaration.except
|
|
7632
|
+
except: declaration.except,
|
|
7633
|
+
rescues: declaration.rescues
|
|
6786
7634
|
});
|
|
6787
7635
|
}
|
|
6788
7636
|
return [
|
|
6789
7637
|
...resolved.filter((one) => one.filter.onThrow !== true),
|
|
6790
|
-
...resolved.filter((one) => one.filter.onThrow === true)
|
|
7638
|
+
...resolved.filter((one) => one.filter.onThrow === true).reverse()
|
|
6791
7639
|
];
|
|
6792
7640
|
}
|
|
7641
|
+
function writtenAt(declaration, found) {
|
|
7642
|
+
if (found.type === "found") {
|
|
7643
|
+
return {
|
|
7644
|
+
methodName: declaration.methodName,
|
|
7645
|
+
nameKind: "binding",
|
|
7646
|
+
method: found.method,
|
|
7647
|
+
file: found.block.file,
|
|
7648
|
+
enclosingQualifiedName: found.block.info.qualifiedName,
|
|
7649
|
+
nesting: found.block.info.bodyNesting
|
|
7650
|
+
};
|
|
7651
|
+
}
|
|
7652
|
+
const inline = declaration.inline;
|
|
7653
|
+
if (inline === void 0) {
|
|
7654
|
+
return null;
|
|
7655
|
+
}
|
|
7656
|
+
return {
|
|
7657
|
+
methodName: inline.label,
|
|
7658
|
+
nameKind: "label",
|
|
7659
|
+
method: inline.block,
|
|
7660
|
+
file: inline.declaredIn.file,
|
|
7661
|
+
enclosingQualifiedName: inline.declaredIn.info.qualifiedName,
|
|
7662
|
+
nesting: inline.declaredIn.info.bodyNesting
|
|
7663
|
+
};
|
|
7664
|
+
}
|
|
7665
|
+
function filterMethod(ancestry, name, read) {
|
|
7666
|
+
const found = methodInAncestry(ancestry, name, read);
|
|
7667
|
+
if (found.type === "unsettled" && found.cause === "unreadAncestor") {
|
|
7668
|
+
return methodPastUnreadAncestors(ancestry, name, read);
|
|
7669
|
+
}
|
|
7670
|
+
return found;
|
|
7671
|
+
}
|
|
6793
7672
|
function filterCoversAction(filter, actionName) {
|
|
6794
7673
|
if (filter.except.has(actionName)) {
|
|
6795
7674
|
return false;
|
|
6796
7675
|
}
|
|
6797
7676
|
return filter.only === null || filter.only.has(actionName);
|
|
6798
7677
|
}
|
|
6799
|
-
function filterReference(filter, displayPath) {
|
|
7678
|
+
function filterReference(filter, displayPath, caught) {
|
|
6800
7679
|
return {
|
|
6801
7680
|
file: displayPath,
|
|
6802
7681
|
name: filter.methodName,
|
|
6803
|
-
...filter.filter.onThrow === true ? { onThrow: true } : {}
|
|
7682
|
+
...filter.filter.onThrow === true ? { onThrow: true } : {},
|
|
7683
|
+
...caught === void 0 ? {} : {
|
|
7684
|
+
catches: caught.classes.map((one) => one.name),
|
|
7685
|
+
...caught.classes.some((one) => one.inheritableByUnread) ? { mayCatchUnreadClasses: true } : {},
|
|
7686
|
+
...caught.someUnread ? { mayCatchAny: true } : {}
|
|
7687
|
+
}
|
|
6804
7688
|
};
|
|
6805
7689
|
}
|
|
6806
|
-
function filterUnit(filter, pattern, displayPath, body,
|
|
7690
|
+
function filterUnit(filter, pattern, displayPath, body, reading = {}) {
|
|
6807
7691
|
const range = rangeOf(filter.method);
|
|
7692
|
+
const { respondingHelper, raises: raises2 } = reading;
|
|
6808
7693
|
const branches = responseBranches(
|
|
6809
7694
|
filter.method,
|
|
6810
7695
|
pattern,
|
|
6811
7696
|
body.effects ?? [],
|
|
6812
7697
|
body.extraEffects,
|
|
6813
|
-
{
|
|
7698
|
+
{
|
|
7699
|
+
fallthrough: "handOn",
|
|
7700
|
+
facts: reading.facts,
|
|
7701
|
+
...respondingHelper === void 0 ? {} : { respondingHelper },
|
|
7702
|
+
...raises2 === void 0 ? {} : { raises: raises2 }
|
|
7703
|
+
}
|
|
6814
7704
|
);
|
|
6815
7705
|
return {
|
|
6816
7706
|
identity: {
|
|
6817
7707
|
name: filter.methodName,
|
|
6818
|
-
nameKind:
|
|
7708
|
+
nameKind: filter.nameKind,
|
|
6819
7709
|
kind: "middleware",
|
|
6820
7710
|
file: displayPath,
|
|
6821
7711
|
range,
|
|
6822
7712
|
span: spanOf(filter.method),
|
|
6823
|
-
exportName: filter.methodName,
|
|
7713
|
+
exportName: filter.nameKind === "binding" ? filter.methodName : null,
|
|
6824
7714
|
exportPath: [filter.enclosingQualifiedName, filter.methodName]
|
|
6825
7715
|
},
|
|
6826
7716
|
boundaryBinding: null,
|
|
6827
7717
|
parameters: [],
|
|
6828
7718
|
branches: withSlotSources(
|
|
6829
|
-
branches ?? [handsOn(range, body)],
|
|
7719
|
+
[...branches ?? [handsOn(range, body)], ...reading.raised ?? []],
|
|
6830
7720
|
body.provenance
|
|
6831
7721
|
),
|
|
6832
7722
|
bodyContent: body.bodyContent ?? "absent",
|
|
@@ -6861,25 +7751,92 @@ function calledName(statement) {
|
|
|
6861
7751
|
}
|
|
6862
7752
|
return field(statement, "method")?.text ?? null;
|
|
6863
7753
|
}
|
|
6864
|
-
function declarationsOf(statement, filter, facts) {
|
|
7754
|
+
function declarationsOf(statement, filter, declaredIn, facts, inlineOrdinal) {
|
|
6865
7755
|
const args = readCallArgs(field(statement, "arguments"));
|
|
6866
7756
|
const keywords = filter.actionKeywords;
|
|
6867
7757
|
const only = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.include], facts);
|
|
6868
7758
|
const except = keywords === void 0 ? null : actionsUnder(args.keyword[keywords.exclude], facts);
|
|
6869
|
-
|
|
7759
|
+
const rescues2 = filter.onThrow === true ? rescuedClasses(args, declaredIn.info.bodyNesting, facts) : RESCUES_NOTHING;
|
|
7760
|
+
const inline = inlineHandler(
|
|
7761
|
+
statement,
|
|
7762
|
+
args,
|
|
7763
|
+
filter,
|
|
7764
|
+
declaredIn,
|
|
7765
|
+
inlineOrdinal
|
|
7766
|
+
);
|
|
7767
|
+
return methodNamesOf(args, filter, facts, inline).map((methodName) => ({
|
|
6870
7768
|
filter,
|
|
6871
7769
|
methodName,
|
|
6872
7770
|
only,
|
|
6873
|
-
except: except ?? /* @__PURE__ */ new Set()
|
|
7771
|
+
except: except ?? /* @__PURE__ */ new Set(),
|
|
7772
|
+
rescues: rescues2,
|
|
7773
|
+
...inline === void 0 ? {} : { inline }
|
|
6874
7774
|
}));
|
|
6875
7775
|
}
|
|
6876
|
-
function
|
|
7776
|
+
function inlineHandler(statement, args, filter, declaredIn, ordinal) {
|
|
7777
|
+
const block = blockInPlaceOfMethod(statement, args, filter);
|
|
7778
|
+
if (block === null || blockStatements2(block).length === 0) {
|
|
7779
|
+
return void 0;
|
|
7780
|
+
}
|
|
7781
|
+
return { block, label: `${filter.name}#${ordinal}`, declaredIn };
|
|
7782
|
+
}
|
|
7783
|
+
function rescuedClasses(args, nesting, facts) {
|
|
7784
|
+
const refs = [];
|
|
7785
|
+
let someUnread = args.positional.length === 0;
|
|
7786
|
+
for (const arg of args.positional) {
|
|
7787
|
+
const candidates = constantRefCandidates(arg, nesting);
|
|
7788
|
+
const named2 = candidates.length === 0 ? stringValueOf(arg, facts) : null;
|
|
7789
|
+
if (candidates.length === 0 && named2 === null) {
|
|
7790
|
+
someUnread = true;
|
|
7791
|
+
continue;
|
|
7792
|
+
}
|
|
7793
|
+
refs.push({
|
|
7794
|
+
text: arg.text,
|
|
7795
|
+
candidates: named2 === null ? candidates : [named2]
|
|
7796
|
+
});
|
|
7797
|
+
}
|
|
7798
|
+
return { refs, someUnread };
|
|
7799
|
+
}
|
|
7800
|
+
function methodNamesOf(args, filter, facts, inline) {
|
|
7801
|
+
const named2 = filter.methodFrom === "withKeyword" ? withKeywordName(args, facts) : symbolArgumentNames(args, facts);
|
|
7802
|
+
if (named2.length > 0 || inline === void 0) {
|
|
7803
|
+
return named2;
|
|
7804
|
+
}
|
|
7805
|
+
return [blockHandlerName(inline.block) ?? inline.label];
|
|
7806
|
+
}
|
|
7807
|
+
function withKeywordName(args, facts) {
|
|
7808
|
+
const named2 = args.keyword.with;
|
|
7809
|
+
const value = named2 === void 0 ? null : stringValueOf(named2, facts);
|
|
7810
|
+
return value === null ? [] : [value];
|
|
7811
|
+
}
|
|
7812
|
+
function blockInPlaceOfMethod(statement, args, filter) {
|
|
6877
7813
|
if (filter.methodFrom === "withKeyword") {
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
7814
|
+
return args.keyword.with === void 0 ? field(statement, "block") : null;
|
|
7815
|
+
}
|
|
7816
|
+
const lambda = args.positional.find((arg) => arg.type === "lambda");
|
|
7817
|
+
if (lambda !== void 0) {
|
|
7818
|
+
return field(lambda, "body");
|
|
6881
7819
|
}
|
|
6882
|
-
return
|
|
7820
|
+
return args.positional.length === 0 ? field(statement, "block") : null;
|
|
7821
|
+
}
|
|
7822
|
+
function blockStatements2(block) {
|
|
7823
|
+
return children(field(block, "body")).filter(
|
|
7824
|
+
(child) => child.type !== "comment"
|
|
7825
|
+
);
|
|
7826
|
+
}
|
|
7827
|
+
function blockHandlerName(block) {
|
|
7828
|
+
const statements = blockStatements2(block);
|
|
7829
|
+
const only = statements.length === 1 ? statements[0] : void 0;
|
|
7830
|
+
if (only === void 0) {
|
|
7831
|
+
return null;
|
|
7832
|
+
}
|
|
7833
|
+
const parameters = new Set(
|
|
7834
|
+
children(field(block, "parameters")).map((parameter) => parameter.text)
|
|
7835
|
+
);
|
|
7836
|
+
if (only.type === "identifier") {
|
|
7837
|
+
return parameters.has(only.text) ? null : only.text;
|
|
7838
|
+
}
|
|
7839
|
+
return calledName(only);
|
|
6883
7840
|
}
|
|
6884
7841
|
function symbolArgumentNames(args, facts) {
|
|
6885
7842
|
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null);
|
|
@@ -6890,13 +7847,16 @@ function skipsOf(statement, filterName, facts) {
|
|
|
6890
7847
|
return args.positional.map((arg) => stringValueOf(arg, facts)).filter((name) => name !== null).map((methodName) => ({ filterName, methodName, actions }));
|
|
6891
7848
|
}
|
|
6892
7849
|
function sameFilter(one, other) {
|
|
6893
|
-
return one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
7850
|
+
return namedBySymbol(one) && namedBySymbol(other) && one.filter.name === other.filter.name && one.methodName === other.methodName;
|
|
7851
|
+
}
|
|
7852
|
+
function namedBySymbol(declaration) {
|
|
7853
|
+
return declaration.inline === void 0;
|
|
6894
7854
|
}
|
|
6895
7855
|
function applySkips(declared, skips) {
|
|
6896
7856
|
const left = [];
|
|
6897
7857
|
for (const declaration of declared) {
|
|
6898
7858
|
const reaching = skips.filter(
|
|
6899
|
-
(skip) => skip.filterName === declaration.filter.name && skip.methodName === declaration.methodName
|
|
7859
|
+
(skip) => namedBySymbol(declaration) && skip.filterName === declaration.filter.name && skip.methodName === declaration.methodName
|
|
6900
7860
|
);
|
|
6901
7861
|
if (reaching.some((skip) => skip.actions === null)) {
|
|
6902
7862
|
continue;
|
|
@@ -6939,7 +7899,7 @@ var STREAM_METHODS = /* @__PURE__ */ new Set(["puts", "print", "write", "<<"]);
|
|
|
6939
7899
|
var DEFERRED_BODY_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
6940
7900
|
function streamWriteEffects(root) {
|
|
6941
7901
|
const found = [];
|
|
6942
|
-
walkDescendants3(root, null, {
|
|
7902
|
+
walkDescendants3(root, null, children, {
|
|
6943
7903
|
at: (node) => {
|
|
6944
7904
|
const write = node.type === "call" ? streamWriteAt(node) : null;
|
|
6945
7905
|
if (write !== null) {
|
|
@@ -7030,7 +7990,7 @@ function writtenShapeOf(node) {
|
|
|
7030
7990
|
return hashShape(node);
|
|
7031
7991
|
}
|
|
7032
7992
|
if (node.type === "array") {
|
|
7033
|
-
const first = node.
|
|
7993
|
+
const first = children(node).find((child) => child !== null);
|
|
7034
7994
|
return {
|
|
7035
7995
|
type: "array",
|
|
7036
7996
|
items: first ? writtenShapeOf(first) : { type: "unknown" }
|
|
@@ -7040,7 +8000,7 @@ function writtenShapeOf(node) {
|
|
|
7040
8000
|
}
|
|
7041
8001
|
function hashShape(node) {
|
|
7042
8002
|
const properties = {};
|
|
7043
|
-
for (const pair of node
|
|
8003
|
+
for (const pair of children(node)) {
|
|
7044
8004
|
if (pair?.type !== "pair") {
|
|
7045
8005
|
continue;
|
|
7046
8006
|
}
|
|
@@ -7403,14 +8363,14 @@ var NodesSeen = class {
|
|
|
7403
8363
|
};
|
|
7404
8364
|
function exampleRun(example, pattern, shared) {
|
|
7405
8365
|
const byName = /* @__PURE__ */ new Map();
|
|
7406
|
-
const
|
|
8366
|
+
const always3 = [];
|
|
7407
8367
|
for (const group of withInlineIncludes(example.groups, pattern, shared)) {
|
|
7408
8368
|
for (const one of groupBlocks(group, pattern)) {
|
|
7409
8369
|
for (const name of one.names) {
|
|
7410
8370
|
byName.set(name, one.block);
|
|
7411
8371
|
}
|
|
7412
8372
|
if (one.always) {
|
|
7413
|
-
|
|
8373
|
+
always3.push(one.block);
|
|
7414
8374
|
}
|
|
7415
8375
|
}
|
|
7416
8376
|
}
|
|
@@ -7429,7 +8389,7 @@ function exampleRun(example, pattern, shared) {
|
|
|
7429
8389
|
}
|
|
7430
8390
|
};
|
|
7431
8391
|
include(example.block);
|
|
7432
|
-
for (const block of
|
|
8392
|
+
for (const block of always3) {
|
|
7433
8393
|
include(block);
|
|
7434
8394
|
}
|
|
7435
8395
|
const valueNames = /* @__PURE__ */ new Set([
|
|
@@ -8074,6 +9034,7 @@ function fieldReadContext(pattern, cache, fileBlocks, bodyRead, facts) {
|
|
|
8074
9034
|
},
|
|
8075
9035
|
lookup: {
|
|
8076
9036
|
root: pattern.root,
|
|
9037
|
+
autoloadRoots: pattern.autoloadRoots ?? [],
|
|
8077
9038
|
pathConvention: pattern.pathConvention,
|
|
8078
9039
|
acronyms: pattern.acronyms ?? [],
|
|
8079
9040
|
ancestryRootClassNames: pattern.ancestryRootClassNames,
|
|
@@ -8229,6 +9190,7 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8229
9190
|
}
|
|
8230
9191
|
const lookup = {
|
|
8231
9192
|
root: pattern.root,
|
|
9193
|
+
autoloadRoots: pattern.autoloadRoots ?? [],
|
|
8232
9194
|
pathConvention: pattern.pathConvention,
|
|
8233
9195
|
acronyms: pattern.acronyms ?? [],
|
|
8234
9196
|
ancestryRootClassNames: pattern.ancestryRootClassNames,
|
|
@@ -8267,15 +9229,41 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8267
9229
|
options.facts,
|
|
8268
9230
|
options.storage
|
|
8269
9231
|
);
|
|
9232
|
+
const respondingHelper = respondingHelpersOf(ancestry, pattern, options);
|
|
9233
|
+
const exceptions = new ExceptionReader(
|
|
9234
|
+
lookup,
|
|
9235
|
+
pattern.libraryExceptions ?? {}
|
|
9236
|
+
);
|
|
9237
|
+
const caught = await handlerClasses(filters, exceptions);
|
|
9238
|
+
const handlersAround = (actionName) => filters.filter((filter) => filterCoversAction(filter, actionName)).flatMap((filter) => {
|
|
9239
|
+
const classes = caught.get(filter);
|
|
9240
|
+
return classes === void 0 ? [] : [classes];
|
|
9241
|
+
});
|
|
8270
9242
|
for (const filter of filters) {
|
|
8271
9243
|
const displayPath = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
8272
|
-
const
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
9244
|
+
const body = bodyOfMethod(filter.method, filter.file, options);
|
|
9245
|
+
const handler = filter.filter.onThrow === true;
|
|
9246
|
+
const raw = filterUnit(filter, pattern, displayPath, body, {
|
|
9247
|
+
facts: options.facts,
|
|
9248
|
+
respondingHelper,
|
|
9249
|
+
...handler ? {} : {
|
|
9250
|
+
raises: {
|
|
9251
|
+
classes: await raisedClasses(
|
|
9252
|
+
filter.method,
|
|
9253
|
+
filter.nesting,
|
|
9254
|
+
exceptions
|
|
9255
|
+
),
|
|
9256
|
+
handlers: "eachRoute"
|
|
9257
|
+
},
|
|
9258
|
+
raised: raisedStatusBranches(
|
|
9259
|
+
pattern,
|
|
9260
|
+
options.storage?.patterns ?? [],
|
|
9261
|
+
body.extraEffects,
|
|
9262
|
+
"eachRoute",
|
|
9263
|
+
rangeOf(filter.method)
|
|
9264
|
+
)
|
|
9265
|
+
}
|
|
9266
|
+
});
|
|
8279
9267
|
units.push(raw);
|
|
8280
9268
|
options.onReachSeed?.(raw, {
|
|
8281
9269
|
file: filter.file,
|
|
@@ -8283,38 +9271,71 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
8283
9271
|
enclosingQualifiedName: filter.enclosingQualifiedName
|
|
8284
9272
|
});
|
|
8285
9273
|
}
|
|
8286
|
-
const emitAction = (actionName, method, block) => {
|
|
9274
|
+
const emitAction = async (actionName, method, block, routes) => {
|
|
8287
9275
|
const around = filters.filter((filter) => filterCoversAction(filter, actionName)).map(
|
|
8288
9276
|
(filter) => filterReference(
|
|
8289
9277
|
filter,
|
|
8290
|
-
options.displayPathOf?.(filter.file) ?? filter.file
|
|
9278
|
+
options.displayPathOf?.(filter.file) ?? filter.file,
|
|
9279
|
+
caught.get(filter)
|
|
8291
9280
|
)
|
|
8292
9281
|
);
|
|
8293
|
-
const
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
9282
|
+
const raises2 = {
|
|
9283
|
+
classes: await raisedClasses(method, block.info.bodyNesting, exceptions),
|
|
9284
|
+
handlers: handlersAround(actionName)
|
|
9285
|
+
};
|
|
9286
|
+
for (const route of routes.length === 0 ? [null] : routes) {
|
|
9287
|
+
const raw = buildControllerActionUnit(
|
|
9288
|
+
pack,
|
|
9289
|
+
pattern,
|
|
9290
|
+
info.qualifiedName,
|
|
9291
|
+
actionName,
|
|
9292
|
+
route,
|
|
9293
|
+
method,
|
|
9294
|
+
{
|
|
9295
|
+
display: block.file === (options.absoluteFile ?? options.filePath) ? options.filePath : options.displayPathOf?.(block.file) ?? block.file,
|
|
9296
|
+
absolute: block.file
|
|
9297
|
+
},
|
|
9298
|
+
options,
|
|
9299
|
+
around,
|
|
9300
|
+
respondingHelper,
|
|
9301
|
+
raises2
|
|
9302
|
+
);
|
|
9303
|
+
units.push(raw);
|
|
9304
|
+
options.onReachSeed?.(raw, {
|
|
9305
|
+
file: block.file,
|
|
9306
|
+
node: method,
|
|
9307
|
+
enclosingQualifiedName: block.info.qualifiedName
|
|
9308
|
+
});
|
|
9309
|
+
}
|
|
8312
9310
|
};
|
|
8313
9311
|
for (const [actionName, method, block] of actions) {
|
|
8314
|
-
|
|
9312
|
+
const routes = pattern.routesFor(info.qualifiedName, actionName);
|
|
9313
|
+
await emitAction(actionName, method, block, routes);
|
|
8315
9314
|
}
|
|
8316
9315
|
return units;
|
|
8317
9316
|
}
|
|
9317
|
+
async function handlerClasses(filters, exceptions) {
|
|
9318
|
+
const caught = /* @__PURE__ */ new Map();
|
|
9319
|
+
for (const filter of filters) {
|
|
9320
|
+
if (filter.filter.onThrow !== true) {
|
|
9321
|
+
continue;
|
|
9322
|
+
}
|
|
9323
|
+
const classes = [];
|
|
9324
|
+
for (const ref of filter.rescues.refs) {
|
|
9325
|
+
classes.push(await exceptions.read(ref));
|
|
9326
|
+
}
|
|
9327
|
+
caught.set(filter, { classes, someUnread: filter.rescues.someUnread });
|
|
9328
|
+
}
|
|
9329
|
+
return caught;
|
|
9330
|
+
}
|
|
9331
|
+
async function raisedClasses(method, nesting, exceptions) {
|
|
9332
|
+
const classes = new NodeMap();
|
|
9333
|
+
for (const call of escapingRaises(method)) {
|
|
9334
|
+
const ref = raisedClassRef(call, nesting);
|
|
9335
|
+
classes.set(call, ref === null ? null : await exceptions.read(ref));
|
|
9336
|
+
}
|
|
9337
|
+
return classes;
|
|
9338
|
+
}
|
|
8318
9339
|
function controllerActions(pattern, info, ownBlocks, ancestry, options) {
|
|
8319
9340
|
const actions = publicInstanceMethods(ownBlocks, options.bodyBlocks);
|
|
8320
9341
|
const seen = new Set(actions.map(([actionName]) => actionName));
|
|
@@ -8331,7 +9352,7 @@ function controllerActions(pattern, info, ownBlocks, ancestry, options) {
|
|
|
8331
9352
|
continue;
|
|
8332
9353
|
}
|
|
8333
9354
|
seen.add(actionName);
|
|
8334
|
-
if (pattern.
|
|
9355
|
+
if (pattern.routesFor(info.qualifiedName, actionName).length > 0) {
|
|
8335
9356
|
actions.push(action);
|
|
8336
9357
|
}
|
|
8337
9358
|
}
|
|
@@ -8356,16 +9377,52 @@ function publicInstanceMethods(blocks, bodyBlocks) {
|
|
|
8356
9377
|
}
|
|
8357
9378
|
return found;
|
|
8358
9379
|
}
|
|
8359
|
-
function
|
|
9380
|
+
function respondingHelpersOf(ancestry, pattern, options) {
|
|
9381
|
+
const methods = /* @__PURE__ */ new Map();
|
|
9382
|
+
const known = /* @__PURE__ */ new Map();
|
|
9383
|
+
const declared = new Set(
|
|
9384
|
+
(pattern.responseStatusCalls ?? []).map((call) => call.name)
|
|
9385
|
+
);
|
|
9386
|
+
const methodNamed = (name) => {
|
|
9387
|
+
if (!methods.has(name)) {
|
|
9388
|
+
const found = methodInAncestry(ancestry, name, {
|
|
9389
|
+
facts: options.facts,
|
|
9390
|
+
bodyBlocks: options.bodyBlocks,
|
|
9391
|
+
dynamicNames: options.dynamicNames
|
|
9392
|
+
});
|
|
9393
|
+
methods.set(name, found.type === "found" ? found.method : null);
|
|
9394
|
+
}
|
|
9395
|
+
return methods.get(name) ?? null;
|
|
9396
|
+
};
|
|
9397
|
+
return (name, call) => {
|
|
9398
|
+
const method = declared.has(name) ? null : methodNamed(name);
|
|
9399
|
+
if (method === null) {
|
|
9400
|
+
return null;
|
|
9401
|
+
}
|
|
9402
|
+
const bound = boundStatusArguments(method, call, pattern, options.facts);
|
|
9403
|
+
const key = `${name}(${[...bound].join(",")})`;
|
|
9404
|
+
if (!known.has(key)) {
|
|
9405
|
+
known.set(key, helperResponse(method, pattern, options.facts, bound));
|
|
9406
|
+
}
|
|
9407
|
+
return known.get(key) ?? null;
|
|
9408
|
+
};
|
|
9409
|
+
}
|
|
9410
|
+
function servesFormat(pattern, route) {
|
|
9411
|
+
return pattern.optionalFormat === true && route.formatless !== true && !route.path.endsWith("/");
|
|
9412
|
+
}
|
|
9413
|
+
function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actionName, route, method, file, bodyRead, wrappers, respondingHelper, raises2) {
|
|
8360
9414
|
const range = rangeOf(method);
|
|
8361
|
-
const route = pattern.routeFor(controllerQualifiedName, actionName);
|
|
8362
9415
|
const body = bodyOfMethod(method, file.absolute, bodyRead);
|
|
8363
9416
|
const perResponse = responseBranches(
|
|
8364
9417
|
method,
|
|
8365
9418
|
pattern,
|
|
8366
9419
|
body.effects ?? [],
|
|
8367
9420
|
body.extraEffects,
|
|
8368
|
-
{
|
|
9421
|
+
{
|
|
9422
|
+
facts: bodyRead.facts,
|
|
9423
|
+
respondingHelper,
|
|
9424
|
+
raises: raises2
|
|
9425
|
+
}
|
|
8369
9426
|
);
|
|
8370
9427
|
return {
|
|
8371
9428
|
identity: {
|
|
@@ -8382,7 +9439,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
8382
9439
|
transport: pack.protocol,
|
|
8383
9440
|
method: route.method,
|
|
8384
9441
|
path: route.path,
|
|
8385
|
-
recognition: pack.name
|
|
9442
|
+
recognition: pack.name,
|
|
9443
|
+
optionalFormat: servesFormat(pattern, route)
|
|
8386
9444
|
}),
|
|
8387
9445
|
parameters: [],
|
|
8388
9446
|
branches: withSlotSources(
|
|
@@ -8402,7 +9460,7 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
8402
9460
|
location: range
|
|
8403
9461
|
},
|
|
8404
9462
|
statusCodeReading: {
|
|
8405
|
-
reading:
|
|
9463
|
+
reading: absentReading3,
|
|
8406
9464
|
libraryDefault: pattern.defaultStatusCode
|
|
8407
9465
|
},
|
|
8408
9466
|
effects: body.effects ?? [],
|
|
@@ -8412,6 +9470,14 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
8412
9470
|
}
|
|
8413
9471
|
],
|
|
8414
9472
|
body.provenance
|
|
9473
|
+
).concat(
|
|
9474
|
+
raisedStatusBranches(
|
|
9475
|
+
pattern,
|
|
9476
|
+
bodyRead.storage?.patterns ?? [],
|
|
9477
|
+
body.extraEffects,
|
|
9478
|
+
raises2.handlers,
|
|
9479
|
+
range
|
|
9480
|
+
)
|
|
8415
9481
|
),
|
|
8416
9482
|
bodyContent: body.bodyContent ?? "absent",
|
|
8417
9483
|
dependencyCalls: [],
|
|
@@ -9311,7 +10377,7 @@ var EXIT_METHODS = /* @__PURE__ */ new Map([
|
|
|
9311
10377
|
var DEFERRED_BODY_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
9312
10378
|
function exitSites(root) {
|
|
9313
10379
|
const found = [];
|
|
9314
|
-
walkDescendants4(root, null, {
|
|
10380
|
+
walkDescendants4(root, null, children, {
|
|
9315
10381
|
at: (node) => {
|
|
9316
10382
|
const site = node.type === "call" ? exitSiteAt(node) : null;
|
|
9317
10383
|
if (site !== null) {
|
|
@@ -9428,7 +10494,7 @@ function eventsOf(args, called, callbacks, db) {
|
|
|
9428
10494
|
import path3 from "path";
|
|
9429
10495
|
import { SKIP_CHILDREN as SKIP_CHILDREN5, walkDescendants as walkDescendants5 } from "@suss/extractor";
|
|
9430
10496
|
function emitRequireFacts(db, filePath, root, known) {
|
|
9431
|
-
walkDescendants5(root, null, {
|
|
10497
|
+
walkDescendants5(root, null, children, {
|
|
9432
10498
|
at: (node) => {
|
|
9433
10499
|
const target = requiredRelativePath(node);
|
|
9434
10500
|
if (target === null) {
|
|
@@ -9469,6 +10535,7 @@ import {
|
|
|
9469
10535
|
placeCalls,
|
|
9470
10536
|
ReceiverClasses,
|
|
9471
10537
|
recordParameterGaps,
|
|
10538
|
+
StopList,
|
|
9472
10539
|
TargetPlacements,
|
|
9473
10540
|
unfollowedCallGap,
|
|
9474
10541
|
worthRecording
|
|
@@ -9510,7 +10577,7 @@ function optionParserFlagReads(method, parameterNames3) {
|
|
|
9510
10577
|
return reads;
|
|
9511
10578
|
}
|
|
9512
10579
|
function walkBody2(body, at) {
|
|
9513
|
-
walkDescendants6(body, null, {
|
|
10580
|
+
walkDescendants6(body, null, children, {
|
|
9514
10581
|
at,
|
|
9515
10582
|
into: (node) => DEFERRED_BODY_TYPES4.has(node.type) ? SKIP_CHILDREN6 : null
|
|
9516
10583
|
});
|
|
@@ -9526,7 +10593,7 @@ function flagsDeclaredIn(construction) {
|
|
|
9526
10593
|
return [];
|
|
9527
10594
|
}
|
|
9528
10595
|
const flags = [];
|
|
9529
|
-
walkDescendants6(block, null, {
|
|
10596
|
+
walkDescendants6(block, null, children, {
|
|
9530
10597
|
at: (node) => {
|
|
9531
10598
|
if (node.type !== "call" || field(node, "method")?.text !== "on") {
|
|
9532
10599
|
return;
|
|
@@ -9604,6 +10671,11 @@ var UNSETTLED_ON_SELF = {
|
|
|
9604
10671
|
reason: "noDeclaration",
|
|
9605
10672
|
matchByName: true
|
|
9606
10673
|
};
|
|
10674
|
+
var SELF_CALL_IN_MIXIN = {
|
|
10675
|
+
kind: "stopped",
|
|
10676
|
+
reason: "selfCallInMixin",
|
|
10677
|
+
matchByName: true
|
|
10678
|
+
};
|
|
9607
10679
|
function followed(target) {
|
|
9608
10680
|
return { kind: "followed", target };
|
|
9609
10681
|
}
|
|
@@ -9792,22 +10864,27 @@ function leavesRoomForATopLevelMethod(reason) {
|
|
|
9792
10864
|
return reason === "noDeclaration" || reason === "outsideRun";
|
|
9793
10865
|
}
|
|
9794
10866
|
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
|
-
}
|
|
10867
|
+
const onClass = site.enclosingQualifiedName === null ? null : methodOnSelf(site, site.enclosingQualifiedName, methodName, ctx);
|
|
10868
|
+
if (onClass !== null && (onClass.kind === "followed" || !leavesRoomForATopLevelMethod(onClass.reason))) {
|
|
10869
|
+
return onClass;
|
|
9805
10870
|
}
|
|
10871
|
+
const readWholeAncestry = onClass?.kind === "stopped" && onClass.reason === "noDeclaration";
|
|
9806
10872
|
const topLevel = resolveTopLevelName(methodName, ctx);
|
|
9807
10873
|
if (topLevel.kind === "followed" || topLevel.reason !== "noDeclaration") {
|
|
9808
10874
|
return topLevel;
|
|
9809
10875
|
}
|
|
9810
|
-
|
|
10876
|
+
if (declaredForTheOtherSelf(site, methodName, ctx)) {
|
|
10877
|
+
return NO_DECLARATION;
|
|
10878
|
+
}
|
|
10879
|
+
return readWholeAncestry && !OBJECT_METHODS.has(methodName) && inModuleInstanceMethod(site, ctx) ? SELF_CALL_IN_MIXIN : UNSETTLED_ON_SELF;
|
|
10880
|
+
}
|
|
10881
|
+
function inModuleInstanceMethod(site, ctx) {
|
|
10882
|
+
const qualifiedName = site.enclosingQualifiedName;
|
|
10883
|
+
if (qualifiedName === null || site.method === null || inClassMethod(site, ctx)) {
|
|
10884
|
+
return false;
|
|
10885
|
+
}
|
|
10886
|
+
const own = ctx.ancestries.get(qualifiedName)?.[0];
|
|
10887
|
+
return own?.type === "bodies" && own.blocks.every((block) => block.info.kind === "module");
|
|
9811
10888
|
}
|
|
9812
10889
|
function declaredForTheOtherSelf(site, methodName, ctx) {
|
|
9813
10890
|
const qualifiedName = site.enclosingQualifiedName;
|
|
@@ -9850,12 +10927,16 @@ function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
|
9850
10927
|
return followed(reachedMethod(found.method, found.block, methodName));
|
|
9851
10928
|
}
|
|
9852
10929
|
if (found.type === "unsettled") {
|
|
9853
|
-
return stop(
|
|
9854
|
-
found.cause === "dynamicDefine" ? "definedAtLoadTime" : "outsideRun"
|
|
9855
|
-
);
|
|
10930
|
+
return stop(unsettledReason(found));
|
|
9856
10931
|
}
|
|
9857
10932
|
return NO_DECLARATION;
|
|
9858
10933
|
}
|
|
10934
|
+
function unsettledReason(found) {
|
|
10935
|
+
if (found.cause === "dynamicDefine") {
|
|
10936
|
+
return "definedAtLoadTime";
|
|
10937
|
+
}
|
|
10938
|
+
return found.definedLater === true ? "behindUnreadAncestor" : "outsideRun";
|
|
10939
|
+
}
|
|
9859
10940
|
function reachedMethod(node, block, name) {
|
|
9860
10941
|
return {
|
|
9861
10942
|
file: block.file,
|
|
@@ -10251,7 +11332,7 @@ function scanBody(source, ctx, options, read) {
|
|
|
10251
11332
|
);
|
|
10252
11333
|
const ownParameters = positionalParameters(source.node).map((p) => p.name);
|
|
10253
11334
|
const followed2 = [];
|
|
10254
|
-
const stops =
|
|
11335
|
+
const stops = new StopList();
|
|
10255
11336
|
const placements = new TargetPlacements();
|
|
10256
11337
|
const receiverClasses = new ReceiverClasses();
|
|
10257
11338
|
const parameterCalls = [];
|
|
@@ -10335,11 +11416,13 @@ function scanBody(source, ctx, options, read) {
|
|
|
10335
11416
|
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
10336
11417
|
);
|
|
10337
11418
|
if (outcome.kind === "stopped") {
|
|
10338
|
-
const stopKey = `${outcome.reason}:${callee}`;
|
|
10339
11419
|
const claimed = options.storage !== void 0 && storageClaims(call, callSite.file, options.storage, callSite.method);
|
|
10340
|
-
if (!
|
|
10341
|
-
|
|
10342
|
-
|
|
11420
|
+
if (!claimed && worthRecording(outcome.reason)) {
|
|
11421
|
+
stops.add({
|
|
11422
|
+
callee,
|
|
11423
|
+
reason: outcome.reason,
|
|
11424
|
+
...resultUsedAsValue(call) ? { usedAsValue: true } : {}
|
|
11425
|
+
});
|
|
10343
11426
|
}
|
|
10344
11427
|
if (outcome.reason === "callerSupplied" && !parameterCallsSeen.has(callee)) {
|
|
10345
11428
|
const receiverName = field(call, "receiver")?.text;
|
|
@@ -10362,7 +11445,7 @@ function scanBody(source, ctx, options, read) {
|
|
|
10362
11445
|
}
|
|
10363
11446
|
return {
|
|
10364
11447
|
followed: followed2,
|
|
10365
|
-
stops,
|
|
11448
|
+
stops: stops.stops,
|
|
10366
11449
|
targets: placements.targets,
|
|
10367
11450
|
argTargets: placements.argTargets,
|
|
10368
11451
|
parameterCalls,
|
|
@@ -10836,7 +11919,7 @@ import { literalOf as literalOf10 } from "@suss/values";
|
|
|
10836
11919
|
function factoriesIn(packs) {
|
|
10837
11920
|
return packs.flatMap((pack) => pack.factories ?? []);
|
|
10838
11921
|
}
|
|
10839
|
-
function emitFactoryFacts(db, files,
|
|
11922
|
+
function emitFactoryFacts(db, files, isTestFile2, patterns, inflections) {
|
|
10840
11923
|
for (const pattern of patterns) {
|
|
10841
11924
|
const definitions = /* @__PURE__ */ new Map();
|
|
10842
11925
|
for (const { file, root } of files) {
|
|
@@ -10844,7 +11927,7 @@ function emitFactoryFacts(db, files, isTestFile, patterns, inflections) {
|
|
|
10844
11927
|
}
|
|
10845
11928
|
const classes = new FactoryClasses(db, definitions, inflections);
|
|
10846
11929
|
for (const { file, root } of files) {
|
|
10847
|
-
if (!
|
|
11930
|
+
if (!isTestFile2(file)) {
|
|
10848
11931
|
continue;
|
|
10849
11932
|
}
|
|
10850
11933
|
for (const [call, name] of buildsIn(db, root, pattern)) {
|
|
@@ -12080,7 +13163,7 @@ function requireTarget(node) {
|
|
|
12080
13163
|
}
|
|
12081
13164
|
function requireSitesIn(root, file) {
|
|
12082
13165
|
const found = [];
|
|
12083
|
-
walkDescendants7(root, null, {
|
|
13166
|
+
walkDescendants7(root, null, children, {
|
|
12084
13167
|
at: (node) => {
|
|
12085
13168
|
const target = requireTarget(node);
|
|
12086
13169
|
if (target !== null) {
|
|
@@ -12130,6 +13213,7 @@ export {
|
|
|
12130
13213
|
bindEvaluator,
|
|
12131
13214
|
bodyStatements,
|
|
12132
13215
|
booleanLiteralValue,
|
|
13216
|
+
children,
|
|
12133
13217
|
collectFileConstants,
|
|
12134
13218
|
createConstantFileCache,
|
|
12135
13219
|
createFileCache,
|