@suss/adapter-ruby 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +761 -223
- package/dist/index.d.cts +164 -13
- package/dist/index.d.ts +164 -13
- package/dist/index.js +745 -207
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -63,7 +63,52 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
63
63
|
"singleton_class"
|
|
64
64
|
]);
|
|
65
65
|
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
66
|
+
var INCLUDE_CALL = "include";
|
|
67
|
+
var PREPEND_CALL = "prepend";
|
|
66
68
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
69
|
+
var BLOCK_TYPES = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
70
|
+
var NO_BODY_BLOCKS = /* @__PURE__ */ new Map();
|
|
71
|
+
function isModuleBody(body) {
|
|
72
|
+
return body.parent?.type === "module";
|
|
73
|
+
}
|
|
74
|
+
function declaredBlockAt(statement, isModule, blocks) {
|
|
75
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const method = field(statement, "method");
|
|
79
|
+
const kind = method === null ? void 0 : blocks.get(method.text);
|
|
80
|
+
if (kind === void 0 || kind.moduleOnly && !isModule) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return field(statement, "block") === null ? null : kind;
|
|
84
|
+
}
|
|
85
|
+
function bodyStatementsRun(body, isModule, blocks) {
|
|
86
|
+
return bodyStatements(body).flatMap((statement) => {
|
|
87
|
+
if (declaredBlockAt(statement, isModule, blocks) === null) {
|
|
88
|
+
return [statement];
|
|
89
|
+
}
|
|
90
|
+
const block = field(statement, "block");
|
|
91
|
+
const inner = block === null ? null : field(block, "body");
|
|
92
|
+
return inner === null ? [] : bodyStatementsRun(inner, isModule, blocks);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function definedAtClassLevel(node, body, blocks) {
|
|
96
|
+
if (blocks.size === 0) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const isModule = isModuleBody(body);
|
|
100
|
+
let current = node.parent;
|
|
101
|
+
while (current !== null && current.id !== body.id) {
|
|
102
|
+
const parent = current.parent;
|
|
103
|
+
if (BLOCK_TYPES.has(current.type) && parent !== null) {
|
|
104
|
+
if (declaredBlockAt(parent, isModule, blocks)?.definesClassMethods) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
current = parent;
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
67
112
|
function runStatements(body, blockConfigures = () => false) {
|
|
68
113
|
const found = [];
|
|
69
114
|
const readsItsChildren = (node) => {
|
|
@@ -99,10 +144,10 @@ function stringLiteralValue(node) {
|
|
|
99
144
|
function symbolValue(node) {
|
|
100
145
|
return node.type === "simple_symbol" ? node.text.slice(1) : null;
|
|
101
146
|
}
|
|
102
|
-
function instanceMethodsByName(body) {
|
|
147
|
+
function instanceMethodsByName(body, blocks = NO_BODY_BLOCKS) {
|
|
103
148
|
const methods = /* @__PURE__ */ new Map();
|
|
104
149
|
for (const stmt of runStatements(body)) {
|
|
105
|
-
if (stmt.type !== "method") {
|
|
150
|
+
if (stmt.type !== "method" || definedAtClassLevel(stmt, body, blocks)) {
|
|
106
151
|
continue;
|
|
107
152
|
}
|
|
108
153
|
const name = field(stmt, "name")?.text;
|
|
@@ -164,10 +209,11 @@ function instanceMethodVisibility(body) {
|
|
|
164
209
|
}
|
|
165
210
|
return visibility;
|
|
166
211
|
}
|
|
167
|
-
function singletonMethodsByName(body) {
|
|
212
|
+
function singletonMethodsByName(body, blocks = NO_BODY_BLOCKS) {
|
|
168
213
|
const methods = /* @__PURE__ */ new Map();
|
|
169
214
|
for (const stmt of runStatements(body)) {
|
|
170
|
-
|
|
215
|
+
const classLevel = stmt.type === "method" && definedAtClassLevel(stmt, body, blocks);
|
|
216
|
+
if (stmt.type !== "singleton_method" && !classLevel) {
|
|
171
217
|
continue;
|
|
172
218
|
}
|
|
173
219
|
const name = field(stmt, "name")?.text;
|
|
@@ -280,7 +326,8 @@ function autoloadDirectories(root) {
|
|
|
280
326
|
}
|
|
281
327
|
|
|
282
328
|
// src/defineMethod.ts
|
|
283
|
-
import {
|
|
329
|
+
import { nodeOfKey as nodeOfKey2 } from "@suss/resolution";
|
|
330
|
+
import { force as force2, hole as hole2, literalOf as literalOf2, piecesOf } from "@suss/values";
|
|
284
331
|
|
|
285
332
|
// src/values/evaluator.ts
|
|
286
333
|
import { nodeOfKey } from "@suss/resolution";
|
|
@@ -321,6 +368,29 @@ var RUBY_RULES = [
|
|
|
321
368
|
lit("givesBackOne", v("n"), v("m"))
|
|
322
369
|
],
|
|
323
370
|
"declared finder"
|
|
371
|
+
),
|
|
372
|
+
// Ruby cannot refer to a method without running it, so
|
|
373
|
+
// `Settings.filters` is worth what the method returns. The shared
|
|
374
|
+
// rules hang that step on a `call` fact this spelling never gets.
|
|
375
|
+
rule(
|
|
376
|
+
"invokes",
|
|
377
|
+
[v("x"), v("f")],
|
|
378
|
+
[
|
|
379
|
+
lit("readsProperty", v("x"), v("o"), v("m")),
|
|
380
|
+
lit("comesTo", v("x"), v("f")),
|
|
381
|
+
lit("func", v("f"))
|
|
382
|
+
],
|
|
383
|
+
"property read runs a method"
|
|
384
|
+
),
|
|
385
|
+
// `%i[a b].freeze` is worth the list it was written as. The evaluator
|
|
386
|
+
// says the same in its row table, for a value it reads in one file.
|
|
387
|
+
...["freeze", "dup"].map(
|
|
388
|
+
(method) => rule(
|
|
389
|
+
"stepsTo",
|
|
390
|
+
[v("x"), v("o"), VALUE_STEP],
|
|
391
|
+
[lit("readsProperty", v("x"), v("o"), constant(method))],
|
|
392
|
+
"hands back the receiver"
|
|
393
|
+
)
|
|
324
394
|
)
|
|
325
395
|
];
|
|
326
396
|
var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
@@ -340,8 +410,8 @@ import { startsAtName, valueLeftByWrites } from "@suss/resolution";
|
|
|
340
410
|
|
|
341
411
|
// src/facts/locals.ts
|
|
342
412
|
import { writesRunInOrder } from "@suss/resolution";
|
|
343
|
-
var
|
|
344
|
-
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([...OWN_BODY_TYPES, ...
|
|
413
|
+
var BLOCK_TYPES2 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
414
|
+
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([...OWN_BODY_TYPES, ...BLOCK_TYPES2]);
|
|
345
415
|
var RUBY_NAME_TYPES = /* @__PURE__ */ new Set([
|
|
346
416
|
"identifier",
|
|
347
417
|
"instance_variable"
|
|
@@ -494,7 +564,7 @@ function ownerOfName(node, name, enclosing) {
|
|
|
494
564
|
function blockDeclaring(node, name) {
|
|
495
565
|
let current = node.parent;
|
|
496
566
|
while (current !== null) {
|
|
497
|
-
if (
|
|
567
|
+
if (BLOCK_TYPES2.has(current.type) && parameterNames(current).has(name)) {
|
|
498
568
|
return current;
|
|
499
569
|
}
|
|
500
570
|
if (OWN_BODY_TYPES.has(current.type)) {
|
|
@@ -744,17 +814,70 @@ function emitHash(emitter, hash) {
|
|
|
744
814
|
add(emitter, "holdsProperty", objectKey, keyText, valueKey(emitter, value));
|
|
745
815
|
}
|
|
746
816
|
}
|
|
747
|
-
|
|
817
|
+
var BLOCK_TYPES3 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
818
|
+
var LOOP_METHODS = /* @__PURE__ */ new Set(["each", "each_with_index", "map"]);
|
|
819
|
+
var DEFINE_METHOD_CALL = "define_method";
|
|
820
|
+
function loopTurnAt(emitter, block) {
|
|
821
|
+
const call = block.parent;
|
|
822
|
+
if (call?.type !== "call") {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
const receiver = field(call, "receiver");
|
|
826
|
+
const method = field(call, "method");
|
|
827
|
+
if (receiver === null || method === null || !LOOP_METHODS.has(method.text)) {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
const parameters = field(block, "parameters");
|
|
831
|
+
const names = parameters === null ? [] : children(parameters).map((p) => p.text);
|
|
832
|
+
const element = names[0];
|
|
833
|
+
if (element === void 0) {
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
element,
|
|
838
|
+
index: names[1] ?? "",
|
|
839
|
+
overKey: nodeId(emitter.filePath, receiver)
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
function emitDynamicDefinition(emitter, call, turns) {
|
|
843
|
+
if (emitter.selfKey === null || // A call inside a method runs when that method does, not at load time.
|
|
844
|
+
emitter.enclosing !== null || call.type !== "call" || field(call, "receiver") !== null || field(call, "method")?.text !== DEFINE_METHOD_CALL) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const args = field(call, "arguments");
|
|
848
|
+
const first = args === null ? void 0 : children(args)[0];
|
|
849
|
+
const nameKey2 = nodeId(emitter.filePath, first ?? call);
|
|
850
|
+
add(emitter, "definesMethodFrom", emitter.selfKey, nameKey2);
|
|
851
|
+
for (const turn of turns) {
|
|
852
|
+
add(
|
|
853
|
+
emitter,
|
|
854
|
+
"nameTurnsOn",
|
|
855
|
+
nameKey2,
|
|
856
|
+
turn.element,
|
|
857
|
+
turn.index,
|
|
858
|
+
turn.overKey
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function walkExpressions(node, emitter, visit, turns = []) {
|
|
748
863
|
for (const child of children(node)) {
|
|
749
864
|
if (OWN_BODY_TYPES.has(child.type)) {
|
|
750
865
|
continue;
|
|
751
866
|
}
|
|
752
|
-
visit(child);
|
|
753
|
-
|
|
867
|
+
visit(child, turns);
|
|
868
|
+
const opened = BLOCK_TYPES3.has(child.type) ? loopTurnAt(emitter, child) : null;
|
|
869
|
+
walkExpressions(
|
|
870
|
+
child,
|
|
871
|
+
emitter,
|
|
872
|
+
visit,
|
|
873
|
+
opened === null ? turns : [...turns, opened]
|
|
874
|
+
);
|
|
754
875
|
}
|
|
755
876
|
}
|
|
756
877
|
function emitExpressionFacts(emitter, node) {
|
|
757
|
-
|
|
878
|
+
emitDynamicDefinition(emitter, node, []);
|
|
879
|
+
walkExpressions(node, emitter, (child, turns) => {
|
|
880
|
+
emitDynamicDefinition(emitter, child, turns);
|
|
758
881
|
if (isPropertyRead(child)) {
|
|
759
882
|
emitPropertyRead(emitter, child);
|
|
760
883
|
} else if (child.type === "call") {
|
|
@@ -843,7 +966,20 @@ function implicitReturn(body) {
|
|
|
843
966
|
(child) => child.type !== "rescue" && child.type !== "ensure"
|
|
844
967
|
);
|
|
845
968
|
const last = statements[statements.length - 1];
|
|
846
|
-
|
|
969
|
+
if (last === void 0 || last.type === "return") {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
return assignedValueOf(last) ?? last;
|
|
973
|
+
}
|
|
974
|
+
function assignedValueOf(node) {
|
|
975
|
+
if (!ASSIGNMENT_TYPES.has(node.type)) {
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
const operator = field(node, "operator")?.text;
|
|
979
|
+
if (node.type !== "assignment" && !WHOLE_VALUE_OPERATORS.has(operator ?? "")) {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
return field(node, "right");
|
|
847
983
|
}
|
|
848
984
|
function emitMethodFacts(emitter, method) {
|
|
849
985
|
const funcKey = nodeId(emitter.filePath, method);
|
|
@@ -873,7 +1009,7 @@ function emitMethodFacts(emitter, method) {
|
|
|
873
1009
|
}
|
|
874
1010
|
};
|
|
875
1011
|
recordNested(body);
|
|
876
|
-
walkExpressions(body, (child) => {
|
|
1012
|
+
walkExpressions(body, inside, (child) => {
|
|
877
1013
|
if (child.type === "return") {
|
|
878
1014
|
const first = children(child)[0];
|
|
879
1015
|
const returned = first?.type === "argument_list" ? children(first)[0] : first;
|
|
@@ -963,23 +1099,37 @@ function emitCandidates(emitter, key, group) {
|
|
|
963
1099
|
}
|
|
964
1100
|
}
|
|
965
1101
|
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1102
|
+
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
1103
|
+
function mixedInConstants(body, callName) {
|
|
1104
|
+
return bareCallArgumentGroups(body, callName).flatMap((group) => [...group].reverse()).reverse().map(readThrough).filter((argument) => CONSTANT_REF_TYPES.has(argument.type));
|
|
1105
|
+
}
|
|
1106
|
+
function emitMixinFacts(emitter, classKey, body, callName) {
|
|
1107
|
+
for (const mixin of mixedInConstants(body, callName)) {
|
|
1108
|
+
add(emitter, "extends", classKey, valueKey(emitter, mixin));
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
966
1111
|
function emitClassFacts(emitter, cls) {
|
|
967
1112
|
const classKey = nodeId(emitter.filePath, cls);
|
|
968
1113
|
add(emitter, "objectValue", classKey);
|
|
1114
|
+
const body = field(cls, "body");
|
|
1115
|
+
if (body !== null) {
|
|
1116
|
+
emitMixinFacts(emitter, classKey, body, PREPEND_CALL);
|
|
1117
|
+
emitMixinFacts(emitter, classKey, body, INCLUDE_CALL);
|
|
1118
|
+
}
|
|
969
1119
|
const superclass = field(cls, "superclass");
|
|
970
1120
|
const base = superclass === null ? null : children(superclass)[0] ?? null;
|
|
971
1121
|
if (base !== null) {
|
|
972
1122
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
973
1123
|
add(emitter, "extendsNamed", classKey, base.text);
|
|
974
1124
|
}
|
|
975
|
-
const body = field(cls, "body");
|
|
976
1125
|
const collected = /* @__PURE__ */ new Map();
|
|
977
1126
|
const within = {
|
|
978
1127
|
...emitter,
|
|
979
1128
|
selfKey: classKey,
|
|
980
1129
|
instanceWrites: collected
|
|
981
1130
|
};
|
|
982
|
-
|
|
1131
|
+
const statements = body === null ? [] : bodyStatementsRun(body, cls.type === "module", emitter.bodyBlocks);
|
|
1132
|
+
for (const statement of statements) {
|
|
983
1133
|
if (statement.type === "assignment") {
|
|
984
1134
|
const left = field(statement, "left");
|
|
985
1135
|
const right = field(statement, "right");
|
|
@@ -1000,6 +1150,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1000
1150
|
continue;
|
|
1001
1151
|
}
|
|
1002
1152
|
if (!METHOD_TYPES.has(statement.type)) {
|
|
1153
|
+
emitExpressionFacts(within, statement);
|
|
1003
1154
|
continue;
|
|
1004
1155
|
}
|
|
1005
1156
|
const funcKey = emitMethodFacts(within, statement);
|
|
@@ -1011,13 +1162,14 @@ function emitClassFacts(emitter, cls) {
|
|
|
1011
1162
|
emitInstanceWrites(within, classKey, collected);
|
|
1012
1163
|
return classKey;
|
|
1013
1164
|
}
|
|
1014
|
-
function emitValueFacts(db, filePath, root) {
|
|
1165
|
+
function emitValueFacts(db, filePath, root, bodyBlocks = NO_BODY_BLOCKS) {
|
|
1015
1166
|
const emitter = {
|
|
1016
1167
|
db,
|
|
1017
1168
|
filePath,
|
|
1018
1169
|
enclosing: null,
|
|
1019
1170
|
selfKey: null,
|
|
1020
|
-
instanceWrites: null
|
|
1171
|
+
instanceWrites: null,
|
|
1172
|
+
bodyBlocks
|
|
1021
1173
|
};
|
|
1022
1174
|
const declaresName = (child, key) => {
|
|
1023
1175
|
const name = field(child, "name");
|
|
@@ -1057,7 +1209,7 @@ var OWN_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1057
1209
|
"module",
|
|
1058
1210
|
"singleton_class"
|
|
1059
1211
|
]);
|
|
1060
|
-
var
|
|
1212
|
+
var BLOCK_TYPES4 = /* @__PURE__ */ new Set(["block", "do_block"]);
|
|
1061
1213
|
var METHOD_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1062
1214
|
var NAME_TYPES = /* @__PURE__ */ new Set([
|
|
1063
1215
|
"identifier",
|
|
@@ -1566,7 +1718,7 @@ function isTailExpression(node) {
|
|
|
1566
1718
|
if (owner.type === "block" && owner.parent?.type === "lambda") {
|
|
1567
1719
|
return true;
|
|
1568
1720
|
}
|
|
1569
|
-
if (METHOD_TYPES2.has(owner.type) ||
|
|
1721
|
+
if (METHOD_TYPES2.has(owner.type) || BLOCK_TYPES4.has(owner.type)) {
|
|
1570
1722
|
return true;
|
|
1571
1723
|
}
|
|
1572
1724
|
if (owner.type === "when") {
|
|
@@ -1585,7 +1737,7 @@ function isRoot(node) {
|
|
|
1585
1737
|
if (OWN_ROOT_TYPES.has(node.type)) {
|
|
1586
1738
|
return true;
|
|
1587
1739
|
}
|
|
1588
|
-
return
|
|
1740
|
+
return BLOCK_TYPES4.has(node.type) && node.parent?.type === "call";
|
|
1589
1741
|
}
|
|
1590
1742
|
function isPathStatement(node, parent) {
|
|
1591
1743
|
if (node.type === "elsif") {
|
|
@@ -1639,7 +1791,7 @@ function functionOf(node) {
|
|
|
1639
1791
|
body: bodyOf(block)
|
|
1640
1792
|
};
|
|
1641
1793
|
}
|
|
1642
|
-
if (
|
|
1794
|
+
if (BLOCK_TYPES4.has(node.type)) {
|
|
1643
1795
|
return {
|
|
1644
1796
|
parameters: parametersOf2(field(node, "parameters")),
|
|
1645
1797
|
body: bodyOf(node)
|
|
@@ -2128,113 +2280,119 @@ function contextOver(db, nodes) {
|
|
|
2128
2280
|
}
|
|
2129
2281
|
|
|
2130
2282
|
// src/defineMethod.ts
|
|
2131
|
-
var
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
function
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
return NOTHING;
|
|
2283
|
+
var NOTHING = {
|
|
2284
|
+
names: /* @__PURE__ */ new Set(),
|
|
2285
|
+
patterns: [],
|
|
2286
|
+
unreadable: false
|
|
2287
|
+
};
|
|
2288
|
+
function couldBeDefined(defined, name) {
|
|
2289
|
+
return defined.unreadable || defined.patterns.some((pattern) => pattern.test(name));
|
|
2290
|
+
}
|
|
2291
|
+
function definedNamesOf(dynamic, classKey) {
|
|
2292
|
+
return dynamic?.get(classKey) ?? NOTHING;
|
|
2293
|
+
}
|
|
2294
|
+
function readDynamicNames(db, rootsByFile) {
|
|
2295
|
+
const byClass = /* @__PURE__ */ new Map();
|
|
2296
|
+
for (const [classKey, nameKey2] of db.facts("definesMethodFrom")) {
|
|
2297
|
+
const key = String(classKey);
|
|
2298
|
+
byClass.set(
|
|
2299
|
+
key,
|
|
2300
|
+
foldReading(
|
|
2301
|
+
byClass.get(key) ?? NOTHING,
|
|
2302
|
+
readOne(db, rootsByFile, String(nameKey2))
|
|
2303
|
+
)
|
|
2304
|
+
);
|
|
2154
2305
|
}
|
|
2155
|
-
const
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
const defined = namesDefinedBy(call);
|
|
2159
|
-
if (defined === null) {
|
|
2160
|
-
unreadable = true;
|
|
2161
|
-
continue;
|
|
2162
|
-
}
|
|
2163
|
-
for (const name of defined) {
|
|
2164
|
-
names.add(name);
|
|
2306
|
+
for (const [classKey, reading] of byClass) {
|
|
2307
|
+
for (const name of reading.names) {
|
|
2308
|
+
db.add("declaresName", [classKey, name]);
|
|
2165
2309
|
}
|
|
2166
2310
|
}
|
|
2167
|
-
return
|
|
2311
|
+
return byClass;
|
|
2168
2312
|
}
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
if (first === void 0) {
|
|
2173
|
-
return null;
|
|
2313
|
+
function foldReading(soFar, reading) {
|
|
2314
|
+
if (reading === null) {
|
|
2315
|
+
return { ...soFar, unreadable: true };
|
|
2174
2316
|
}
|
|
2317
|
+
return {
|
|
2318
|
+
names: /* @__PURE__ */ new Set([...soFar.names, ...reading.names]),
|
|
2319
|
+
patterns: [...soFar.patterns, ...reading.patterns],
|
|
2320
|
+
unreadable: soFar.unreadable
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
function readOne(db, rootsByFile, nameKey2) {
|
|
2324
|
+
const node = nodeOfKey2(rootsByFile, nameKey2);
|
|
2175
2325
|
const names = [];
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2326
|
+
const patterns = [];
|
|
2327
|
+
for (const bindings of loopTurns(db, rootsByFile, nameKey2)) {
|
|
2328
|
+
const value = node === null ? hole2(nameKey2) : evaluatedValue(node, db, bindings.size === 0 ? void 0 : bindings);
|
|
2329
|
+
const name = literalOf2(value);
|
|
2330
|
+
if (name !== null) {
|
|
2331
|
+
names.push(name);
|
|
2332
|
+
continue;
|
|
2333
|
+
}
|
|
2334
|
+
const pattern = patternOf(value);
|
|
2335
|
+
if (pattern === null) {
|
|
2183
2336
|
return null;
|
|
2184
2337
|
}
|
|
2185
|
-
|
|
2338
|
+
patterns.push(pattern);
|
|
2186
2339
|
}
|
|
2187
|
-
return names;
|
|
2340
|
+
return { names, patterns };
|
|
2188
2341
|
}
|
|
2189
|
-
function
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2342
|
+
function patternOf(value) {
|
|
2343
|
+
const pieces = piecesOf(value);
|
|
2344
|
+
let source = "^";
|
|
2345
|
+
let readSomething = false;
|
|
2346
|
+
for (const piece of pieces) {
|
|
2347
|
+
if (piece.kind === "hole") {
|
|
2348
|
+
source += "[\\s\\S]*";
|
|
2349
|
+
continue;
|
|
2350
|
+
}
|
|
2351
|
+
const options = piece.options.filter((option) => option.length > 0);
|
|
2352
|
+
if (options.length === 0) {
|
|
2194
2353
|
continue;
|
|
2195
2354
|
}
|
|
2355
|
+
readSomething = true;
|
|
2356
|
+
source += `(?:${options.map(escapeForPattern).join("|")})`;
|
|
2357
|
+
}
|
|
2358
|
+
return readSomething ? new RegExp(`${source}$`) : null;
|
|
2359
|
+
}
|
|
2360
|
+
function escapeForPattern(text2) {
|
|
2361
|
+
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2362
|
+
}
|
|
2363
|
+
function loopTurns(db, rootsByFile, nameKey2) {
|
|
2364
|
+
let turns = [/* @__PURE__ */ new Map()];
|
|
2365
|
+
for (const over of listsBehind(db, rootsByFile, nameKey2)) {
|
|
2196
2366
|
turns = turns.flatMap(
|
|
2197
|
-
(base) =>
|
|
2367
|
+
(base) => over.elements.map((value, position) => {
|
|
2368
|
+
const binding = new Map([...base, [over.element, value]]);
|
|
2369
|
+
if (over.index !== "") {
|
|
2370
|
+
binding.set(over.index, String(position));
|
|
2371
|
+
}
|
|
2372
|
+
return binding;
|
|
2373
|
+
})
|
|
2198
2374
|
);
|
|
2199
2375
|
}
|
|
2200
2376
|
return turns;
|
|
2201
2377
|
}
|
|
2202
|
-
function
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2378
|
+
function listsBehind(db, rootsByFile, nameKey2) {
|
|
2379
|
+
const found = [];
|
|
2380
|
+
for (const [, element, index, overKey] of db.lookup(
|
|
2381
|
+
"nameTurnsOn",
|
|
2382
|
+
0,
|
|
2383
|
+
nameKey2
|
|
2384
|
+
)) {
|
|
2385
|
+
const elements = literalElementsOf(rootsByFile, String(overKey), db);
|
|
2386
|
+
if (elements !== null) {
|
|
2387
|
+
found.push({ element: String(element), index: String(index), elements });
|
|
2209
2388
|
}
|
|
2210
|
-
current = parent;
|
|
2211
2389
|
}
|
|
2212
|
-
return
|
|
2390
|
+
return found;
|
|
2213
2391
|
}
|
|
2214
|
-
function
|
|
2215
|
-
const
|
|
2216
|
-
const
|
|
2217
|
-
|
|
2218
|
-
if (receiver === null || method === void 0 || !LOOP_METHODS.has(method)) {
|
|
2219
|
-
return null;
|
|
2220
|
-
}
|
|
2221
|
-
const parameters = field(block, "parameters");
|
|
2222
|
-
const [element, index] = parameters === null ? [] : bodyStatements(parameters).map((parameter) => parameter.text);
|
|
2223
|
-
const elements = element === void 0 ? null : literalElementsOf(receiver);
|
|
2224
|
-
if (element === void 0 || elements === null) {
|
|
2225
|
-
return null;
|
|
2226
|
-
}
|
|
2227
|
-
return elements.map((value, position) => {
|
|
2228
|
-
const binding = /* @__PURE__ */ new Map([[element, value]]);
|
|
2229
|
-
if (index !== void 0) {
|
|
2230
|
-
binding.set(index, String(position));
|
|
2231
|
-
}
|
|
2232
|
-
return binding;
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2235
|
-
function literalElementsOf(node) {
|
|
2236
|
-
const value = evaluatedValue(node);
|
|
2237
|
-
if (value.kind !== "sequence") {
|
|
2392
|
+
function literalElementsOf(rootsByFile, key, db) {
|
|
2393
|
+
const node = nodeOfKey2(rootsByFile, key);
|
|
2394
|
+
const value = node === null ? null : evaluatedValue(node, db);
|
|
2395
|
+
if (value === null || value.kind !== "sequence") {
|
|
2238
2396
|
return null;
|
|
2239
2397
|
}
|
|
2240
2398
|
const elements = [];
|
|
@@ -2374,8 +2532,6 @@ function graphqlTypeNameFromQualified(qualifiedName, convention) {
|
|
|
2374
2532
|
}
|
|
2375
2533
|
|
|
2376
2534
|
// src/ancestry.ts
|
|
2377
|
-
var INCLUDE_CALL = "include";
|
|
2378
|
-
var PREPEND_CALL = "prepend";
|
|
2379
2535
|
async function reachDefinition(qualifiedName, lookup) {
|
|
2380
2536
|
const filePath = resolveConstantFile(
|
|
2381
2537
|
lookup.root,
|
|
@@ -2502,7 +2658,7 @@ function superclassCandidatesOf(blocks) {
|
|
|
2502
2658
|
}
|
|
2503
2659
|
return [];
|
|
2504
2660
|
}
|
|
2505
|
-
function methodInAncestry(ancestry, name) {
|
|
2661
|
+
function methodInAncestry(ancestry, name, read = {}) {
|
|
2506
2662
|
for (const entry of ancestry) {
|
|
2507
2663
|
if (entry.type === "root") {
|
|
2508
2664
|
return { type: "none" };
|
|
@@ -2514,7 +2670,7 @@ function methodInAncestry(ancestry, name) {
|
|
|
2514
2670
|
cause: "unreadAncestor"
|
|
2515
2671
|
};
|
|
2516
2672
|
}
|
|
2517
|
-
const found = definitionIn(entry.blocks, name);
|
|
2673
|
+
const found = definitionIn(entry.blocks, name, read);
|
|
2518
2674
|
if (found.method !== null && found.block !== null) {
|
|
2519
2675
|
return { type: "found", method: found.method, block: found.block };
|
|
2520
2676
|
}
|
|
@@ -2528,7 +2684,7 @@ function methodInAncestry(ancestry, name) {
|
|
|
2528
2684
|
}
|
|
2529
2685
|
return { type: "none" };
|
|
2530
2686
|
}
|
|
2531
|
-
function definitionIn(blocks, name) {
|
|
2687
|
+
function definitionIn(blocks, name, read) {
|
|
2532
2688
|
let method = null;
|
|
2533
2689
|
let block = null;
|
|
2534
2690
|
let definedDynamically = false;
|
|
@@ -2538,14 +2694,17 @@ function definitionIn(blocks, name) {
|
|
|
2538
2694
|
if (body === null) {
|
|
2539
2695
|
continue;
|
|
2540
2696
|
}
|
|
2541
|
-
const found = instanceMethodsByName(body).get(name);
|
|
2697
|
+
const found = instanceMethodsByName(body, read.bodyBlocks).get(name);
|
|
2542
2698
|
if (found !== void 0) {
|
|
2543
2699
|
method = found;
|
|
2544
2700
|
block = candidate;
|
|
2545
2701
|
}
|
|
2546
|
-
const defined =
|
|
2702
|
+
const defined = definedNamesOf(
|
|
2703
|
+
read.dynamicNames,
|
|
2704
|
+
nodeId(candidate.file, candidate.info.node)
|
|
2705
|
+
);
|
|
2547
2706
|
definedDynamically ||= defined.names.has(name);
|
|
2548
|
-
unreadableDefine ||= defined
|
|
2707
|
+
unreadableDefine ||= couldBeDefined(defined, name);
|
|
2549
2708
|
}
|
|
2550
2709
|
return { method, block, definedDynamically, unreadableDefine };
|
|
2551
2710
|
}
|
|
@@ -2959,7 +3118,7 @@ function lowerRubyBody(body, terminals, responseCalls = []) {
|
|
|
2959
3118
|
|
|
2960
3119
|
// src/paths/effects.ts
|
|
2961
3120
|
var RAISE_NAMES2 = /* @__PURE__ */ new Set(["raise", "fail"]);
|
|
2962
|
-
var
|
|
3121
|
+
var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
2963
3122
|
"body_statement",
|
|
2964
3123
|
"then",
|
|
2965
3124
|
"else",
|
|
@@ -3088,7 +3247,7 @@ function argsOf(call) {
|
|
|
3088
3247
|
function enclosingStatement(call, body) {
|
|
3089
3248
|
let current = call;
|
|
3090
3249
|
while (current.parent !== null) {
|
|
3091
|
-
if (
|
|
3250
|
+
if (BLOCK_TYPES5.has(current.parent.type) || current.parent.id === body.id) {
|
|
3092
3251
|
return current;
|
|
3093
3252
|
}
|
|
3094
3253
|
current = current.parent;
|
|
@@ -3837,7 +3996,7 @@ function isDefaultedAt(node) {
|
|
|
3837
3996
|
}
|
|
3838
3997
|
|
|
3839
3998
|
// src/filters.ts
|
|
3840
|
-
function controllerFilters(pattern, ancestry) {
|
|
3999
|
+
function controllerFilters(pattern, ancestry, read = {}) {
|
|
3841
4000
|
const forms = pattern.filters ?? [];
|
|
3842
4001
|
if (forms.length === 0) {
|
|
3843
4002
|
return [];
|
|
@@ -3872,7 +4031,7 @@ function controllerFilters(pattern, ancestry) {
|
|
|
3872
4031
|
}
|
|
3873
4032
|
const resolved = [];
|
|
3874
4033
|
for (const declaration of declared) {
|
|
3875
|
-
const found = methodInAncestry(ancestry, declaration.methodName);
|
|
4034
|
+
const found = methodInAncestry(ancestry, declaration.methodName, read);
|
|
3876
4035
|
if (found.type !== "found") {
|
|
3877
4036
|
continue;
|
|
3878
4037
|
}
|
|
@@ -4076,28 +4235,98 @@ function modelPattern(constant2, file, options) {
|
|
|
4076
4235
|
(candidate) => reachesBase(options.facts, classKey, candidate.baseClasses)
|
|
4077
4236
|
);
|
|
4078
4237
|
}
|
|
4079
|
-
function
|
|
4080
|
-
const
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4238
|
+
function chainLinks(call) {
|
|
4239
|
+
const links = [];
|
|
4240
|
+
let current = call;
|
|
4241
|
+
while (current !== null && current.type === "call") {
|
|
4242
|
+
links.push(current);
|
|
4243
|
+
current = receiverOf(current);
|
|
4244
|
+
}
|
|
4245
|
+
return links;
|
|
4246
|
+
}
|
|
4247
|
+
function kindOfCall(pattern, method) {
|
|
4248
|
+
if (pattern.writes.includes(method)) {
|
|
4249
|
+
return "write";
|
|
4250
|
+
}
|
|
4251
|
+
if (pattern.reads.includes(method)) {
|
|
4252
|
+
return "read";
|
|
4253
|
+
}
|
|
4254
|
+
return void 0;
|
|
4255
|
+
}
|
|
4256
|
+
function someLibraryDefines(options, method) {
|
|
4257
|
+
return options.patterns.some(
|
|
4258
|
+
(pattern) => kindOfCall(pattern, method) !== void 0
|
|
4259
|
+
);
|
|
4260
|
+
}
|
|
4261
|
+
function argumentsOf2(call) {
|
|
4262
|
+
const args = field(call, "arguments");
|
|
4263
|
+
return args === null ? [] : children3(args);
|
|
4264
|
+
}
|
|
4265
|
+
function isKeywordArgument(argument) {
|
|
4266
|
+
return argument.type === "pair" || argument.type === "hash";
|
|
4267
|
+
}
|
|
4268
|
+
function bareName(node) {
|
|
4269
|
+
return node.text.replace(/^:/, "").replace(/:$/, "");
|
|
4270
|
+
}
|
|
4271
|
+
function pairsIn(argument) {
|
|
4272
|
+
if (argument.type === "pair") {
|
|
4273
|
+
return [argument];
|
|
4274
|
+
}
|
|
4275
|
+
if (argument.type === "hash") {
|
|
4276
|
+
return children3(argument).filter((child) => child.type === "pair");
|
|
4277
|
+
}
|
|
4278
|
+
return [];
|
|
4279
|
+
}
|
|
4280
|
+
function keywordKeys(call) {
|
|
4281
|
+
const keys = [];
|
|
4282
|
+
for (const argument of argumentsOf2(call)) {
|
|
4283
|
+
for (const pair of pairsIn(argument)) {
|
|
4284
|
+
const key = field(pair, "key");
|
|
4088
4285
|
if (key !== null) {
|
|
4089
|
-
|
|
4286
|
+
keys.push(bareName(key));
|
|
4090
4287
|
}
|
|
4091
4288
|
}
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4289
|
+
}
|
|
4290
|
+
return keys;
|
|
4291
|
+
}
|
|
4292
|
+
function hasPositionalArgument(call) {
|
|
4293
|
+
return argumentsOf2(call).some((argument) => !isKeywordArgument(argument));
|
|
4294
|
+
}
|
|
4295
|
+
function columnsAskedFor(call, pattern) {
|
|
4296
|
+
if (!(pattern.columnArguments ?? []).includes(methodOf(call))) {
|
|
4297
|
+
return [];
|
|
4298
|
+
}
|
|
4299
|
+
return argumentsOf2(call).filter((argument) => argument.type === "simple_symbol").map(bareName);
|
|
4300
|
+
}
|
|
4301
|
+
function primaryKeySelector(call, pattern) {
|
|
4302
|
+
const byPrimaryKey = pattern.byPrimaryKey;
|
|
4303
|
+
if (byPrimaryKey === void 0) {
|
|
4304
|
+
return [];
|
|
4305
|
+
}
|
|
4306
|
+
const picks = byPrimaryKey.methods.includes(methodOf(call)) && hasPositionalArgument(call);
|
|
4307
|
+
return picks ? [byPrimaryKey.column] : [];
|
|
4308
|
+
}
|
|
4309
|
+
function selectorOf(call, pattern) {
|
|
4310
|
+
const picked = [];
|
|
4311
|
+
for (const link of chainLinks(call)) {
|
|
4312
|
+
picked.push(...primaryKeySelector(link, pattern));
|
|
4313
|
+
if (pattern.reads.includes(methodOf(link))) {
|
|
4314
|
+
picked.push(...keywordKeys(link));
|
|
4095
4315
|
}
|
|
4096
|
-
}
|
|
4097
|
-
walk(node);
|
|
4316
|
+
}
|
|
4098
4317
|
return [...new Set(picked)];
|
|
4099
4318
|
}
|
|
4100
|
-
function
|
|
4319
|
+
function fieldsOf(call, pattern, kind) {
|
|
4320
|
+
if (kind === "write") {
|
|
4321
|
+
return [...new Set(keywordKeys(call))];
|
|
4322
|
+
}
|
|
4323
|
+
return [
|
|
4324
|
+
...new Set(
|
|
4325
|
+
chainLinks(call).flatMap((link) => columnsAskedFor(link, pattern))
|
|
4326
|
+
)
|
|
4327
|
+
];
|
|
4328
|
+
}
|
|
4329
|
+
function storageEffect(call, container, pattern, kind, selector, fields) {
|
|
4101
4330
|
const operation = methodOf(call);
|
|
4102
4331
|
return {
|
|
4103
4332
|
type: "interaction",
|
|
@@ -4111,35 +4340,12 @@ function storageEffect(call, container, pattern, kind, selector) {
|
|
|
4111
4340
|
interaction: {
|
|
4112
4341
|
class: "storage-access",
|
|
4113
4342
|
kind,
|
|
4114
|
-
fields
|
|
4343
|
+
fields,
|
|
4115
4344
|
operation,
|
|
4116
4345
|
...selector.length > 0 ? { selector } : {}
|
|
4117
4346
|
}
|
|
4118
4347
|
};
|
|
4119
4348
|
}
|
|
4120
|
-
function modelCallEffects(call, file, options) {
|
|
4121
|
-
const constant2 = rootConstant(call);
|
|
4122
|
-
if (constant2 === null) {
|
|
4123
|
-
return [];
|
|
4124
|
-
}
|
|
4125
|
-
const pattern = modelPattern(constant2, file, options);
|
|
4126
|
-
if (pattern === void 0) {
|
|
4127
|
-
return [];
|
|
4128
|
-
}
|
|
4129
|
-
const kind = pattern.writes.includes(methodOf(call)) ? "write" : "read";
|
|
4130
|
-
return [
|
|
4131
|
-
storageEffect(
|
|
4132
|
-
call,
|
|
4133
|
-
constantName(constant2),
|
|
4134
|
-
pattern,
|
|
4135
|
-
kind,
|
|
4136
|
-
selectorOf(call)
|
|
4137
|
-
)
|
|
4138
|
-
];
|
|
4139
|
-
}
|
|
4140
|
-
function writeMethods(patterns) {
|
|
4141
|
-
return new Set(patterns.flatMap((pattern) => pattern.writes));
|
|
4142
|
-
}
|
|
4143
4349
|
function classSettledOn(facts, key) {
|
|
4144
4350
|
askResolution2(facts, [key], "wanted", RUBY_PROGRAM);
|
|
4145
4351
|
const objects = new Set(
|
|
@@ -4156,27 +4362,64 @@ function declaredName(facts, classKey) {
|
|
|
4156
4362
|
function projectDeclares(facts, classKey, method) {
|
|
4157
4363
|
return facts.lookup("wantedDeclaredName", 0, classKey).some((row) => String(row[1]) === method);
|
|
4158
4364
|
}
|
|
4159
|
-
function
|
|
4365
|
+
function constantReceiver(constant2, file, facts) {
|
|
4366
|
+
const classKey = classBehind(facts, file, constant2);
|
|
4367
|
+
return classKey === void 0 ? void 0 : { classKey, container: constantName(constant2) };
|
|
4368
|
+
}
|
|
4369
|
+
function settledReceiver(receiver, file, facts, enclosing) {
|
|
4370
|
+
const classKey = classSettledOn(facts, readKey(file, receiver, enclosing));
|
|
4371
|
+
if (classKey === void 0) {
|
|
4372
|
+
return void 0;
|
|
4373
|
+
}
|
|
4374
|
+
const container = declaredName(facts, classKey);
|
|
4375
|
+
return container === void 0 ? void 0 : { classKey, container };
|
|
4376
|
+
}
|
|
4377
|
+
function receiverClass(call, file, options, enclosing) {
|
|
4378
|
+
const constant2 = rootConstant(call);
|
|
4379
|
+
if (constant2 !== null) {
|
|
4380
|
+
return constantReceiver(constant2, file, options.facts);
|
|
4381
|
+
}
|
|
4160
4382
|
const receiver = receiverOf(call);
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
return [];
|
|
4383
|
+
if (receiver === null) {
|
|
4384
|
+
return void 0;
|
|
4164
4385
|
}
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
)
|
|
4169
|
-
|
|
4386
|
+
return settledReceiver(receiver, file, options.facts, enclosing);
|
|
4387
|
+
}
|
|
4388
|
+
function libraryCallIn(call, options) {
|
|
4389
|
+
return chainLinks(call).find(
|
|
4390
|
+
(link) => someLibraryDefines(options, methodOf(link))
|
|
4391
|
+
) ?? null;
|
|
4392
|
+
}
|
|
4393
|
+
function modelCallEffects(call, file, options, enclosing) {
|
|
4394
|
+
const worked = libraryCallIn(call, options);
|
|
4395
|
+
if (worked === null) {
|
|
4170
4396
|
return [];
|
|
4171
4397
|
}
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
)
|
|
4175
|
-
if (pattern === void 0 || projectDeclares(options.facts, classKey, method)) {
|
|
4398
|
+
const method = methodOf(worked);
|
|
4399
|
+
const target = receiverClass(worked, file, options, enclosing);
|
|
4400
|
+
if (target === void 0) {
|
|
4176
4401
|
return [];
|
|
4177
4402
|
}
|
|
4178
|
-
const
|
|
4179
|
-
|
|
4403
|
+
for (const pattern of options.patterns) {
|
|
4404
|
+
const kind = kindOfCall(pattern, method);
|
|
4405
|
+
if (kind === void 0 || !reachesBase(options.facts, target.classKey, pattern.baseClasses)) {
|
|
4406
|
+
continue;
|
|
4407
|
+
}
|
|
4408
|
+
if (projectDeclares(options.facts, target.classKey, method)) {
|
|
4409
|
+
return [];
|
|
4410
|
+
}
|
|
4411
|
+
return [
|
|
4412
|
+
storageEffect(
|
|
4413
|
+
worked,
|
|
4414
|
+
target.container,
|
|
4415
|
+
pattern,
|
|
4416
|
+
kind,
|
|
4417
|
+
selectorOf(worked, pattern),
|
|
4418
|
+
fieldsOf(worked, pattern, kind)
|
|
4419
|
+
)
|
|
4420
|
+
];
|
|
4421
|
+
}
|
|
4422
|
+
return [];
|
|
4180
4423
|
}
|
|
4181
4424
|
function isLoader(node, loader) {
|
|
4182
4425
|
if (node === null) {
|
|
@@ -4214,7 +4457,7 @@ function loaderCallEffects(call, file, options) {
|
|
|
4214
4457
|
const pattern = modelPattern(argument, file, options);
|
|
4215
4458
|
if (pattern !== void 0) {
|
|
4216
4459
|
effects.push(
|
|
4217
|
-
storageEffect(call, constantName(argument), pattern, "read", [])
|
|
4460
|
+
storageEffect(call, constantName(argument), pattern, "read", [], [])
|
|
4218
4461
|
);
|
|
4219
4462
|
}
|
|
4220
4463
|
}
|
|
@@ -4222,22 +4465,21 @@ function loaderCallEffects(call, file, options) {
|
|
|
4222
4465
|
return effects;
|
|
4223
4466
|
}
|
|
4224
4467
|
function effectsOfCall(call, file, options, enclosing) {
|
|
4225
|
-
const onModel = modelCallEffects(call, file, options);
|
|
4226
|
-
|
|
4227
|
-
return onModel;
|
|
4228
|
-
}
|
|
4229
|
-
const onInstance = rootConstant(call) === null ? instanceWriteEffects(call, file, options, enclosing) : [];
|
|
4230
|
-
return onInstance.length > 0 ? onInstance : loaderCallEffects(call, file, options);
|
|
4468
|
+
const onModel = modelCallEffects(call, file, options, enclosing);
|
|
4469
|
+
return onModel.length > 0 ? onModel : loaderCallEffects(call, file, options);
|
|
4231
4470
|
}
|
|
4232
4471
|
function storageClaims(call, file, options, enclosing = null) {
|
|
4233
4472
|
return options.patterns.length > 0 && effectsOfCall(call, file, options, enclosing).length > 0;
|
|
4234
4473
|
}
|
|
4235
4474
|
function receiverKeysToAsk(chains, file, options, enclosing) {
|
|
4236
|
-
const writes = writeMethods(options.patterns);
|
|
4237
4475
|
const keys = [];
|
|
4238
4476
|
for (const call of chains) {
|
|
4239
|
-
const
|
|
4240
|
-
if (
|
|
4477
|
+
const worked = libraryCallIn(call, options);
|
|
4478
|
+
if (worked === null || rootConstant(worked) !== null) {
|
|
4479
|
+
continue;
|
|
4480
|
+
}
|
|
4481
|
+
const receiver = receiverOf(worked);
|
|
4482
|
+
if (receiver !== null) {
|
|
4241
4483
|
keys.push(readKey(file, receiver, enclosing));
|
|
4242
4484
|
}
|
|
4243
4485
|
}
|
|
@@ -4334,11 +4576,17 @@ function createFileCache(parse, readFile) {
|
|
|
4334
4576
|
}
|
|
4335
4577
|
};
|
|
4336
4578
|
}
|
|
4337
|
-
function fieldReadContext(pattern, cache, fileBlocks, bodyRead) {
|
|
4579
|
+
function fieldReadContext(pattern, cache, fileBlocks, bodyRead, facts) {
|
|
4338
4580
|
return {
|
|
4339
4581
|
pattern,
|
|
4340
4582
|
cache,
|
|
4341
4583
|
bodyRead,
|
|
4584
|
+
facts,
|
|
4585
|
+
read: {
|
|
4586
|
+
facts,
|
|
4587
|
+
bodyBlocks: bodyRead.bodyBlocks,
|
|
4588
|
+
dynamicNames: bodyRead.dynamicNames
|
|
4589
|
+
},
|
|
4342
4590
|
lookup: {
|
|
4343
4591
|
root: pattern.root,
|
|
4344
4592
|
pathConvention: pattern.pathConvention,
|
|
@@ -4421,7 +4669,13 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
4421
4669
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
4422
4670
|
return [];
|
|
4423
4671
|
}
|
|
4424
|
-
const ctx = fieldReadContext(
|
|
4672
|
+
const ctx = fieldReadContext(
|
|
4673
|
+
pattern,
|
|
4674
|
+
options.cache,
|
|
4675
|
+
fileBlocks,
|
|
4676
|
+
options,
|
|
4677
|
+
options.facts
|
|
4678
|
+
);
|
|
4425
4679
|
const ancestry = await ancestryOf(info.qualifiedName, ownBlocks, ctx.lookup);
|
|
4426
4680
|
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
4427
4681
|
return [];
|
|
@@ -4469,7 +4723,11 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4469
4723
|
])) {
|
|
4470
4724
|
return [];
|
|
4471
4725
|
}
|
|
4472
|
-
const filters = controllerFilters(pattern, ancestry
|
|
4726
|
+
const filters = controllerFilters(pattern, ancestry, {
|
|
4727
|
+
facts: options.facts,
|
|
4728
|
+
bodyBlocks: options.bodyBlocks,
|
|
4729
|
+
dynamicNames: options.dynamicNames
|
|
4730
|
+
});
|
|
4473
4731
|
const units = [];
|
|
4474
4732
|
for (const filter of filters) {
|
|
4475
4733
|
const displayPath = options.displayPathOf?.(filter.file) ?? filter.file;
|
|
@@ -4514,7 +4772,10 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4514
4772
|
});
|
|
4515
4773
|
};
|
|
4516
4774
|
const own = /* @__PURE__ */ new Set();
|
|
4517
|
-
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4775
|
+
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4776
|
+
ownBlocks,
|
|
4777
|
+
options.bodyBlocks
|
|
4778
|
+
)) {
|
|
4518
4779
|
own.add(actionName);
|
|
4519
4780
|
emitAction(actionName, method, block);
|
|
4520
4781
|
}
|
|
@@ -4524,7 +4785,8 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4524
4785
|
continue;
|
|
4525
4786
|
}
|
|
4526
4787
|
for (const [actionName, method, block] of publicInstanceMethods(
|
|
4527
|
-
entry.blocks
|
|
4788
|
+
entry.blocks,
|
|
4789
|
+
options.bodyBlocks
|
|
4528
4790
|
)) {
|
|
4529
4791
|
if (seen.has(actionName)) {
|
|
4530
4792
|
continue;
|
|
@@ -4538,14 +4800,17 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
4538
4800
|
}
|
|
4539
4801
|
return units;
|
|
4540
4802
|
}
|
|
4541
|
-
function publicInstanceMethods(blocks) {
|
|
4803
|
+
function publicInstanceMethods(blocks, bodyBlocks) {
|
|
4542
4804
|
const found = [];
|
|
4543
4805
|
for (const block of blocks) {
|
|
4544
4806
|
if (block.info.bodyNode === null) {
|
|
4545
4807
|
continue;
|
|
4546
4808
|
}
|
|
4547
4809
|
const visibility = instanceMethodVisibility(block.info.bodyNode);
|
|
4548
|
-
for (const [name, method] of instanceMethodsByName(
|
|
4810
|
+
for (const [name, method] of instanceMethodsByName(
|
|
4811
|
+
block.info.bodyNode,
|
|
4812
|
+
bodyBlocks
|
|
4813
|
+
)) {
|
|
4549
4814
|
if ((visibility.get(name) ?? "public") === "public") {
|
|
4550
4815
|
found.push([name, method, block]);
|
|
4551
4816
|
}
|
|
@@ -4808,7 +5073,7 @@ async function readFieldShape(symbol, callArgs, scope, ctx, ancestry, range) {
|
|
|
4808
5073
|
return {
|
|
4809
5074
|
contract: literalContract(callArgs, scope, ctx),
|
|
4810
5075
|
body: bodyFromLookup(
|
|
4811
|
-
methodInAncestry(ancestry, symbol),
|
|
5076
|
+
methodInAncestry(ancestry, symbol, ctx.read),
|
|
4812
5077
|
range,
|
|
4813
5078
|
"This field",
|
|
4814
5079
|
NO_METHOD_BEHIND_IT,
|
|
@@ -4853,7 +5118,7 @@ async function readWiredClass(ref, scope, ctx) {
|
|
|
4853
5118
|
return {
|
|
4854
5119
|
contract: readClassContract(ancestry, ctx.pattern),
|
|
4855
5120
|
body: bodyFromLookup(
|
|
4856
|
-
methodInAncestry(ancestry, ctx.pattern.resolverMethodName),
|
|
5121
|
+
methodInAncestry(ancestry, ctx.pattern.resolverMethodName, ctx.read),
|
|
4857
5122
|
range,
|
|
4858
5123
|
`This field's ${targetQualifiedName}`,
|
|
4859
5124
|
methodNotSettled(
|
|
@@ -5025,6 +5290,202 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
5025
5290
|
};
|
|
5026
5291
|
}
|
|
5027
5292
|
|
|
5293
|
+
// src/inflect.ts
|
|
5294
|
+
var SINGULAR_RULES = [
|
|
5295
|
+
[/s$/i, ""],
|
|
5296
|
+
[/(ss)$/i, "$1"],
|
|
5297
|
+
[/(n)ews$/i, "$1ews"],
|
|
5298
|
+
[/([ti])a$/i, "$1um"],
|
|
5299
|
+
[
|
|
5300
|
+
/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i,
|
|
5301
|
+
"$1sis"
|
|
5302
|
+
],
|
|
5303
|
+
[/(^analy)(sis|ses)$/i, "$1sis"],
|
|
5304
|
+
[/([^f])ves$/i, "$1fe"],
|
|
5305
|
+
[/(hive)s$/i, "$1"],
|
|
5306
|
+
[/(tive)s$/i, "$1"],
|
|
5307
|
+
[/([lr])ves$/i, "$1f"],
|
|
5308
|
+
[/([^aeiouy]|qu)ies$/i, "$1y"],
|
|
5309
|
+
[/(s)eries$/i, "$1eries"],
|
|
5310
|
+
[/(m)ovies$/i, "$1ovie"],
|
|
5311
|
+
[/(x|ch|ss|sh)es$/i, "$1"],
|
|
5312
|
+
[/^(m|l)ice$/i, "$1ouse"],
|
|
5313
|
+
[/(bus)(es)?$/i, "$1"],
|
|
5314
|
+
[/(o)es$/i, "$1"],
|
|
5315
|
+
[/(shoe)s$/i, "$1"],
|
|
5316
|
+
[/(cris|test)(is|es)$/i, "$1is"],
|
|
5317
|
+
[/^(a)x[ie]s$/i, "$1xis"],
|
|
5318
|
+
[/(octop|vir)(us|i)$/i, "$1us"],
|
|
5319
|
+
[/(alias|status)(es)?$/i, "$1"],
|
|
5320
|
+
[/^(ox)en/i, "$1"],
|
|
5321
|
+
[/(vert|ind)ices$/i, "$1ex"],
|
|
5322
|
+
[/(matr)ices$/i, "$1ix"],
|
|
5323
|
+
[/(quiz)zes$/i, "$1"],
|
|
5324
|
+
[/(database)s$/i, "$1"],
|
|
5325
|
+
// The words ActiveSupport calls irregular. It declares them after the
|
|
5326
|
+
// rules above, which is what puts them ahead in the search.
|
|
5327
|
+
[/(p)erson$/i, "$1erson"],
|
|
5328
|
+
[/(p)eople$/i, "$1erson"],
|
|
5329
|
+
[/(m)an$/i, "$1an"],
|
|
5330
|
+
[/(m)en$/i, "$1an"],
|
|
5331
|
+
[/(c)hild$/i, "$1hild"],
|
|
5332
|
+
[/(c)hildren$/i, "$1hild"],
|
|
5333
|
+
[/(s)ex$/i, "$1ex"],
|
|
5334
|
+
[/(s)exes$/i, "$1ex"],
|
|
5335
|
+
[/(m)ove$/i, "$1ove"],
|
|
5336
|
+
[/(m)oves$/i, "$1ove"],
|
|
5337
|
+
[/(z)ombie$/i, "$1ombie"],
|
|
5338
|
+
[/(z)ombies$/i, "$1ombie"]
|
|
5339
|
+
];
|
|
5340
|
+
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
5341
|
+
"equipment",
|
|
5342
|
+
"information",
|
|
5343
|
+
"rice",
|
|
5344
|
+
"money",
|
|
5345
|
+
"species",
|
|
5346
|
+
"series",
|
|
5347
|
+
"fish",
|
|
5348
|
+
"sheep",
|
|
5349
|
+
"jeans",
|
|
5350
|
+
"police"
|
|
5351
|
+
]);
|
|
5352
|
+
function singularize(word, project) {
|
|
5353
|
+
if (UNCOUNTABLE.has(word.toLowerCase()) || (project?.uncountable ?? []).some(
|
|
5354
|
+
(uncountable) => uncountable.toLowerCase() === word.toLowerCase()
|
|
5355
|
+
)) {
|
|
5356
|
+
return word;
|
|
5357
|
+
}
|
|
5358
|
+
const own = projectSingular(word, project);
|
|
5359
|
+
if (own !== null) {
|
|
5360
|
+
return own;
|
|
5361
|
+
}
|
|
5362
|
+
for (let at = SINGULAR_RULES.length - 1; at >= 0; at--) {
|
|
5363
|
+
const [pattern, replacement] = SINGULAR_RULES[at];
|
|
5364
|
+
if (pattern.test(word)) {
|
|
5365
|
+
return word.replace(pattern, replacement);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
return word;
|
|
5369
|
+
}
|
|
5370
|
+
function projectSingular(word, project) {
|
|
5371
|
+
const irregular = [...project?.irregular ?? []].reverse();
|
|
5372
|
+
for (const [plural, singular] of irregular) {
|
|
5373
|
+
if (plural.toLowerCase() === word.toLowerCase()) {
|
|
5374
|
+
return matchingCase(word, singular);
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
const rules = [...project?.singular ?? []].reverse();
|
|
5378
|
+
for (const [rule3, replacement] of rules) {
|
|
5379
|
+
const applied = applyRule(word, rule3, replacement);
|
|
5380
|
+
if (applied !== null) {
|
|
5381
|
+
return applied;
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
return null;
|
|
5385
|
+
}
|
|
5386
|
+
function applyRule(word, rule3, replacement) {
|
|
5387
|
+
const written = /^\/(.*)\/([a-z]*)$/s.exec(rule3);
|
|
5388
|
+
if (written === null) {
|
|
5389
|
+
return word.includes(rule3) ? word.replace(rule3, replacement) : null;
|
|
5390
|
+
}
|
|
5391
|
+
const flags = (written[2] ?? "").replace("m", "s").replace(/[^is]/g, "");
|
|
5392
|
+
const pattern = new RegExp(written[1] ?? "", flags);
|
|
5393
|
+
return pattern.test(word) ? word.replace(pattern, replacement.replace(/\\(\d)/g, "$$$1")) : null;
|
|
5394
|
+
}
|
|
5395
|
+
function matchingCase(word, replacement) {
|
|
5396
|
+
const first = word[0];
|
|
5397
|
+
if (first === void 0 || first !== first.toUpperCase()) {
|
|
5398
|
+
return replacement;
|
|
5399
|
+
}
|
|
5400
|
+
return replacement.charAt(0).toUpperCase() + replacement.slice(1);
|
|
5401
|
+
}
|
|
5402
|
+
function camelize(word, acronyms = []) {
|
|
5403
|
+
return word.split("_").map((part) => capitalize(part, acronyms)).join("");
|
|
5404
|
+
}
|
|
5405
|
+
function capitalize(part, acronyms) {
|
|
5406
|
+
if (part === "") {
|
|
5407
|
+
return "";
|
|
5408
|
+
}
|
|
5409
|
+
const acronym = acronyms.find(
|
|
5410
|
+
(known) => known.toLowerCase() === part.toLowerCase()
|
|
5411
|
+
);
|
|
5412
|
+
return acronym ?? part[0]?.toUpperCase() + part.slice(1);
|
|
5413
|
+
}
|
|
5414
|
+
function associationTargetName(name, plural, project) {
|
|
5415
|
+
return camelize(
|
|
5416
|
+
plural ? singularize(name, project) : name,
|
|
5417
|
+
project?.acronyms ?? []
|
|
5418
|
+
);
|
|
5419
|
+
}
|
|
5420
|
+
|
|
5421
|
+
// src/facts/associations.ts
|
|
5422
|
+
function declaredName2(argument) {
|
|
5423
|
+
if (argument === void 0) {
|
|
5424
|
+
return null;
|
|
5425
|
+
}
|
|
5426
|
+
if (argument.type === "simple_symbol") {
|
|
5427
|
+
return argument.text.slice(1);
|
|
5428
|
+
}
|
|
5429
|
+
return stringLiteralValue(argument);
|
|
5430
|
+
}
|
|
5431
|
+
function targetReference(key, written, nesting) {
|
|
5432
|
+
if (written.startsWith("::")) {
|
|
5433
|
+
return { key, written: written.slice(2), nesting: [] };
|
|
5434
|
+
}
|
|
5435
|
+
return { key, written, nesting };
|
|
5436
|
+
}
|
|
5437
|
+
function targetName(call, calls, name, plural, inflections) {
|
|
5438
|
+
const override = readCallArgs(field(call, "arguments")).keyword[calls.classNameKeyword];
|
|
5439
|
+
if (override === void 0) {
|
|
5440
|
+
return associationTargetName(name, plural, inflections);
|
|
5441
|
+
}
|
|
5442
|
+
return stringLiteralValue(override);
|
|
5443
|
+
}
|
|
5444
|
+
function associationsDeclaredIn(cls, classKey, nesting, calls, inflections) {
|
|
5445
|
+
const body = field(cls, "body");
|
|
5446
|
+
if (body === null || calls.length === 0) {
|
|
5447
|
+
return [];
|
|
5448
|
+
}
|
|
5449
|
+
const found = [];
|
|
5450
|
+
for (const statement of runStatements(body)) {
|
|
5451
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
5452
|
+
continue;
|
|
5453
|
+
}
|
|
5454
|
+
const method = field(statement, "method")?.text ?? "";
|
|
5455
|
+
for (const declaration of calls) {
|
|
5456
|
+
const plural = declaration.plural.includes(method);
|
|
5457
|
+
if (!plural && !declaration.singular.includes(method)) {
|
|
5458
|
+
continue;
|
|
5459
|
+
}
|
|
5460
|
+
const positional = readCallArgs(field(statement, "arguments")).positional;
|
|
5461
|
+
const name = declaredName2(positional[0]);
|
|
5462
|
+
if (name === null) {
|
|
5463
|
+
continue;
|
|
5464
|
+
}
|
|
5465
|
+
const written = targetName(
|
|
5466
|
+
statement,
|
|
5467
|
+
declaration,
|
|
5468
|
+
name,
|
|
5469
|
+
plural,
|
|
5470
|
+
inflections
|
|
5471
|
+
);
|
|
5472
|
+
if (written === null) {
|
|
5473
|
+
continue;
|
|
5474
|
+
}
|
|
5475
|
+
found.push({
|
|
5476
|
+
classKey,
|
|
5477
|
+
name,
|
|
5478
|
+
target: targetReference(
|
|
5479
|
+
`${classKey}#association:${name}`,
|
|
5480
|
+
written,
|
|
5481
|
+
nesting
|
|
5482
|
+
)
|
|
5483
|
+
});
|
|
5484
|
+
}
|
|
5485
|
+
}
|
|
5486
|
+
return found;
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5028
5489
|
// src/facts/constants.ts
|
|
5029
5490
|
function children4(node) {
|
|
5030
5491
|
return node.namedChildren.filter((child) => child !== null);
|
|
@@ -5059,20 +5520,32 @@ function isDeclaration(node, parent) {
|
|
|
5059
5520
|
}
|
|
5060
5521
|
return parent.type === "scope_resolution";
|
|
5061
5522
|
}
|
|
5062
|
-
function collectFileConstants(filePath, root) {
|
|
5523
|
+
function collectFileConstants(filePath, root, associationCalls = [], inflections) {
|
|
5063
5524
|
const definitions = [];
|
|
5064
5525
|
const references = [];
|
|
5526
|
+
const associations = [];
|
|
5065
5527
|
const walk = (node, parent, nesting) => {
|
|
5066
5528
|
if (NESTING_TYPES.has(node.type)) {
|
|
5067
5529
|
const name = field(node, "name");
|
|
5068
5530
|
const written = name === null ? null : writtenName(name);
|
|
5069
5531
|
if (written !== null) {
|
|
5532
|
+
const classKey = nodeId(filePath, node);
|
|
5070
5533
|
definitions.push({
|
|
5071
5534
|
qualifiedName: qualify(nesting, written),
|
|
5072
|
-
key:
|
|
5535
|
+
key: classKey,
|
|
5073
5536
|
kind: "declaration"
|
|
5074
5537
|
});
|
|
5075
5538
|
const inside = [...nesting, ...written.split("::")];
|
|
5539
|
+
for (const declared of associationsDeclaredIn(
|
|
5540
|
+
node,
|
|
5541
|
+
classKey,
|
|
5542
|
+
inside,
|
|
5543
|
+
associationCalls,
|
|
5544
|
+
inflections
|
|
5545
|
+
)) {
|
|
5546
|
+
associations.push(declared);
|
|
5547
|
+
references.push(declared.target);
|
|
5548
|
+
}
|
|
5076
5549
|
for (const child of children4(node)) {
|
|
5077
5550
|
walk(child, node, inside);
|
|
5078
5551
|
}
|
|
@@ -5104,7 +5577,7 @@ function collectFileConstants(filePath, root) {
|
|
|
5104
5577
|
}
|
|
5105
5578
|
};
|
|
5106
5579
|
walk(root, null, []);
|
|
5107
|
-
return { filePath, definitions, references };
|
|
5580
|
+
return { filePath, definitions, references, associations };
|
|
5108
5581
|
}
|
|
5109
5582
|
function emitConstantBindings(db, perFile) {
|
|
5110
5583
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -5128,6 +5601,13 @@ function emitConstantBindings(db, perFile) {
|
|
|
5128
5601
|
return found.length === 1 ? found[0].key : null;
|
|
5129
5602
|
};
|
|
5130
5603
|
for (const file of files) {
|
|
5604
|
+
for (const declared of file.associations) {
|
|
5605
|
+
db.add("declaresAssociation", [
|
|
5606
|
+
declared.classKey,
|
|
5607
|
+
declared.name,
|
|
5608
|
+
declared.target.key
|
|
5609
|
+
]);
|
|
5610
|
+
}
|
|
5131
5611
|
for (const reference of file.references) {
|
|
5132
5612
|
for (let depth = reference.nesting.length; depth >= 0; depth--) {
|
|
5133
5613
|
const candidate = qualify(
|
|
@@ -5289,6 +5769,35 @@ function requiredRelativePath(node) {
|
|
|
5289
5769
|
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
5290
5770
|
}
|
|
5291
5771
|
|
|
5772
|
+
// src/pack.ts
|
|
5773
|
+
function inflectionsIn(packs) {
|
|
5774
|
+
const pooled = {
|
|
5775
|
+
acronyms: [],
|
|
5776
|
+
irregular: [],
|
|
5777
|
+
uncountable: [],
|
|
5778
|
+
singular: []
|
|
5779
|
+
};
|
|
5780
|
+
for (const pack of packs) {
|
|
5781
|
+
pooled.acronyms?.push(...pack.inflections?.acronyms ?? []);
|
|
5782
|
+
pooled.irregular?.push(...pack.inflections?.irregular ?? []);
|
|
5783
|
+
pooled.uncountable?.push(...pack.inflections?.uncountable ?? []);
|
|
5784
|
+
pooled.singular?.push(...pack.inflections?.singular ?? []);
|
|
5785
|
+
}
|
|
5786
|
+
return pooled;
|
|
5787
|
+
}
|
|
5788
|
+
function bodyBlocksIn(packs) {
|
|
5789
|
+
const pooled = /* @__PURE__ */ new Map();
|
|
5790
|
+
for (const pack of packs) {
|
|
5791
|
+
for (const declared of pack.bodyBlocks ?? []) {
|
|
5792
|
+
pooled.set(declared.name, {
|
|
5793
|
+
moduleOnly: declared.moduleOnly ?? false,
|
|
5794
|
+
definesClassMethods: declared.definesClassMethods ?? false
|
|
5795
|
+
});
|
|
5796
|
+
}
|
|
5797
|
+
}
|
|
5798
|
+
return pooled;
|
|
5799
|
+
}
|
|
5800
|
+
|
|
5292
5801
|
// src/reach/closure.ts
|
|
5293
5802
|
import {
|
|
5294
5803
|
functionCallBinding,
|
|
@@ -5466,7 +5975,11 @@ function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
|
5466
5975
|
if (ancestry === void 0) {
|
|
5467
5976
|
return stop("outsideRun");
|
|
5468
5977
|
}
|
|
5469
|
-
const found = methodInAncestry(ancestry, methodName
|
|
5978
|
+
const found = methodInAncestry(ancestry, methodName, {
|
|
5979
|
+
facts: ctx.facts,
|
|
5980
|
+
bodyBlocks: ctx.bodyBlocks,
|
|
5981
|
+
dynamicNames: ctx.dynamicNames
|
|
5982
|
+
});
|
|
5470
5983
|
if (found.type === "found") {
|
|
5471
5984
|
return followed(reachedMethod(found.method, found.block, methodName));
|
|
5472
5985
|
}
|
|
@@ -5495,7 +6008,10 @@ function singletonMethodOn(qualifiedName, methodName, ctx) {
|
|
|
5495
6008
|
if (block.info.bodyNode === null) {
|
|
5496
6009
|
continue;
|
|
5497
6010
|
}
|
|
5498
|
-
const found = singletonMethodsByName(
|
|
6011
|
+
const found = singletonMethodsByName(
|
|
6012
|
+
block.info.bodyNode,
|
|
6013
|
+
ctx.bodyBlocks
|
|
6014
|
+
).get(methodName);
|
|
5499
6015
|
if (found !== void 0) {
|
|
5500
6016
|
return followed(reachedMethod(found, block, methodName));
|
|
5501
6017
|
}
|
|
@@ -5849,7 +6365,7 @@ function parameterName(param) {
|
|
|
5849
6365
|
|
|
5850
6366
|
// src/reach/context.ts
|
|
5851
6367
|
var METHOD_TYPES4 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
5852
|
-
async function buildReachContext(files, facts) {
|
|
6368
|
+
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
|
|
5853
6369
|
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
5854
6370
|
const classes = [];
|
|
5855
6371
|
const classNames = /* @__PURE__ */ new Map();
|
|
@@ -5923,7 +6439,9 @@ async function buildReachContext(files, facts) {
|
|
|
5923
6439
|
topLevelMethods,
|
|
5924
6440
|
facts,
|
|
5925
6441
|
classNames,
|
|
5926
|
-
definitions
|
|
6442
|
+
definitions,
|
|
6443
|
+
bodyBlocks,
|
|
6444
|
+
dynamicNames
|
|
5927
6445
|
};
|
|
5928
6446
|
}
|
|
5929
6447
|
function namedChildren(node) {
|
|
@@ -5969,6 +6487,13 @@ function inheritedMethodsIn(packs) {
|
|
|
5969
6487
|
}
|
|
5970
6488
|
return found;
|
|
5971
6489
|
}
|
|
6490
|
+
function associationCallsIn(packs) {
|
|
6491
|
+
return packs.flatMap(
|
|
6492
|
+
(pack) => (pack.storage ?? []).flatMap(
|
|
6493
|
+
(pattern) => pattern.associations === void 0 ? [] : [pattern.associations]
|
|
6494
|
+
)
|
|
6495
|
+
);
|
|
6496
|
+
}
|
|
5972
6497
|
function emitStorageFacts(db, packs) {
|
|
5973
6498
|
for (const pack of packs) {
|
|
5974
6499
|
for (const pattern of pack.storage ?? []) {
|
|
@@ -6023,6 +6548,9 @@ async function extractRubyProject(options) {
|
|
|
6023
6548
|
);
|
|
6024
6549
|
const parsed = [];
|
|
6025
6550
|
const definitions = /* @__PURE__ */ new Map();
|
|
6551
|
+
const associationCalls = associationCallsIn(options.packs);
|
|
6552
|
+
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6553
|
+
const inflections = inflectionsIn(options.packs);
|
|
6026
6554
|
for (const file of options.files) {
|
|
6027
6555
|
await timer.timeAsync("parse", async () => {
|
|
6028
6556
|
const root = await cache.get(file);
|
|
@@ -6030,14 +6558,16 @@ async function extractRubyProject(options) {
|
|
|
6030
6558
|
return;
|
|
6031
6559
|
}
|
|
6032
6560
|
parsed.push({ file, root });
|
|
6033
|
-
emitValueFacts(db, file, root);
|
|
6561
|
+
emitValueFacts(db, file, root, bodyBlocks);
|
|
6034
6562
|
for (const [key, method] of methodDefinitionsIn(file, root)) {
|
|
6035
6563
|
definitions.set(key, method);
|
|
6036
6564
|
}
|
|
6037
|
-
constants.push(
|
|
6565
|
+
constants.push(
|
|
6566
|
+
collectFileConstants(file, root, associationCalls, inflections)
|
|
6567
|
+
);
|
|
6038
6568
|
});
|
|
6039
6569
|
}
|
|
6040
|
-
timer.time("discover", () => {
|
|
6570
|
+
const dynamicNames = timer.time("discover", () => {
|
|
6041
6571
|
emitConstantBindings(db, constants);
|
|
6042
6572
|
const known = new Set(parsed.map(({ file }) => file));
|
|
6043
6573
|
for (const { file, root } of parsed) {
|
|
@@ -6045,6 +6575,10 @@ async function extractRubyProject(options) {
|
|
|
6045
6575
|
}
|
|
6046
6576
|
bindEvaluator(db, { files: parsed, definitions });
|
|
6047
6577
|
emitStorageFacts(db, options.packs);
|
|
6578
|
+
return readDynamicNames(
|
|
6579
|
+
db,
|
|
6580
|
+
new Map(parsed.map(({ file, root }) => [file, root]))
|
|
6581
|
+
);
|
|
6048
6582
|
});
|
|
6049
6583
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
6050
6584
|
const loaderPatterns = options.packs.flatMap((pack) => pack.loaders ?? []);
|
|
@@ -6052,7 +6586,7 @@ async function extractRubyProject(options) {
|
|
|
6052
6586
|
const inheritedMethods = inheritedMethodsIn(options.packs);
|
|
6053
6587
|
const reachContext = await timer.timeAsync(
|
|
6054
6588
|
"discover",
|
|
6055
|
-
() => buildReachContext(parsed, db)
|
|
6589
|
+
() => buildReachContext(parsed, db, bodyBlocks, dynamicNames)
|
|
6056
6590
|
);
|
|
6057
6591
|
const displayPathOf = (file) => options.workspaceRoot !== void 0 ? path4.relative(options.workspaceRoot, file) : file;
|
|
6058
6592
|
const seeds = [];
|
|
@@ -6072,6 +6606,8 @@ async function extractRubyProject(options) {
|
|
|
6072
6606
|
cache,
|
|
6073
6607
|
...storage === void 0 ? {} : { storage },
|
|
6074
6608
|
inheritedMethods,
|
|
6609
|
+
bodyBlocks,
|
|
6610
|
+
dynamicNames,
|
|
6075
6611
|
displayPathOf,
|
|
6076
6612
|
facts: db,
|
|
6077
6613
|
onReachSeed: (raw, seed) => seedByRaw.set(raw, seed)
|
|
@@ -6143,7 +6679,9 @@ async function extractRubyProject(options) {
|
|
|
6143
6679
|
context: reachContext,
|
|
6144
6680
|
displayPathOf,
|
|
6145
6681
|
...storage === void 0 ? {} : { storage },
|
|
6146
|
-
inheritedMethods
|
|
6682
|
+
inheritedMethods,
|
|
6683
|
+
bodyBlocks,
|
|
6684
|
+
dynamicNames
|
|
6147
6685
|
})
|
|
6148
6686
|
);
|
|
6149
6687
|
for (const entry of found) {
|