@suss/adapter-ruby 0.31.1 → 0.32.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 +188 -45
- package/dist/index.js +188 -45
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -144,6 +144,17 @@ var OWN_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
144
144
|
"singleton_class"
|
|
145
145
|
]);
|
|
146
146
|
var NESTING_TYPES = /* @__PURE__ */ new Set(["class", "module"]);
|
|
147
|
+
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
148
|
+
function enclosingMethod(node) {
|
|
149
|
+
let current = node.parent;
|
|
150
|
+
while (current !== null) {
|
|
151
|
+
if (METHOD_TYPES.has(current.type)) {
|
|
152
|
+
return current;
|
|
153
|
+
}
|
|
154
|
+
current = current.parent;
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
147
158
|
var INCLUDE_CALL = "include";
|
|
148
159
|
var PREPEND_CALL = "prepend";
|
|
149
160
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
@@ -460,8 +471,11 @@ var RUBY_PROGRAM = (0, import_resolution.resolutionProgram)(RUBY_RULES);
|
|
|
460
471
|
function resolveValues(db, keys) {
|
|
461
472
|
(0, import_resolution.askResolution)(db, keys, "wanted", RUBY_PROGRAM);
|
|
462
473
|
}
|
|
474
|
+
function resolveEnvSites(db, sites) {
|
|
475
|
+
(0, import_resolution.askResolution)(db, sites, "wantedEnvSite", RUBY_PROGRAM);
|
|
476
|
+
}
|
|
463
477
|
function resolvedFunctions(db, key) {
|
|
464
|
-
return db.
|
|
478
|
+
return db.lookup("wantedResolves", 0, key).map((row) => String(row[1]));
|
|
465
479
|
}
|
|
466
480
|
function writtenValueOf(db, key) {
|
|
467
481
|
resolveValues(db, [key]);
|
|
@@ -1340,7 +1354,6 @@ function emitCandidates(emitter, key, group) {
|
|
|
1340
1354
|
add(emitter, "writesAllStated", key);
|
|
1341
1355
|
}
|
|
1342
1356
|
}
|
|
1343
|
-
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1344
1357
|
var INITIALIZE_METHOD = "initialize";
|
|
1345
1358
|
function receiverKeyOf(filePath, method, classKey) {
|
|
1346
1359
|
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
@@ -1388,7 +1401,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1388
1401
|
left.text,
|
|
1389
1402
|
valueKey(emitter, right)
|
|
1390
1403
|
);
|
|
1391
|
-
emitExpressionFacts(
|
|
1404
|
+
emitExpressionFacts(within, statement);
|
|
1392
1405
|
}
|
|
1393
1406
|
continue;
|
|
1394
1407
|
}
|
|
@@ -2451,7 +2464,6 @@ var evaluators = /* @__PURE__ */ new WeakMap();
|
|
|
2451
2464
|
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2452
2465
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2453
2466
|
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2454
|
-
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2455
2467
|
function bindEvaluator(db, nodes) {
|
|
2456
2468
|
for (const entry of nodes.files) {
|
|
2457
2469
|
filesByTree.set(entry.root.tree, entry.file);
|
|
@@ -2505,7 +2517,7 @@ function stringValueOf(node, db, bindings) {
|
|
|
2505
2517
|
function methodDefinitionsIn(file, root) {
|
|
2506
2518
|
const found = /* @__PURE__ */ new Map();
|
|
2507
2519
|
const visit = (node) => {
|
|
2508
|
-
if (
|
|
2520
|
+
if (METHOD_TYPES.has(node.type)) {
|
|
2509
2521
|
found.set(nodeId(file, node), node);
|
|
2510
2522
|
}
|
|
2511
2523
|
for (const child of node.namedChildren) {
|
|
@@ -2530,16 +2542,6 @@ function evaluatorFor(node, db) {
|
|
|
2530
2542
|
}
|
|
2531
2543
|
return local;
|
|
2532
2544
|
}
|
|
2533
|
-
function enclosingMethod(node) {
|
|
2534
|
-
let current = node.parent;
|
|
2535
|
-
while (current !== null) {
|
|
2536
|
-
if (METHOD_TYPES3.has(current.type)) {
|
|
2537
|
-
return current;
|
|
2538
|
-
}
|
|
2539
|
-
current = current.parent;
|
|
2540
|
-
}
|
|
2541
|
-
return null;
|
|
2542
|
-
}
|
|
2543
2545
|
function contextOver(db, nodes) {
|
|
2544
2546
|
const filesByRoot = /* @__PURE__ */ new Map();
|
|
2545
2547
|
const rootsByFile = /* @__PURE__ */ new Map();
|
|
@@ -4077,19 +4079,155 @@ function returnBranch(range, effects) {
|
|
|
4077
4079
|
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
4078
4080
|
var import_extractor4 = require("@suss/extractor");
|
|
4079
4081
|
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
4082
|
+
var ENV_DEFAULTED = "rbEnvDefaulted";
|
|
4080
4083
|
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
4081
|
-
function envReadEffects(root) {
|
|
4082
|
-
const
|
|
4084
|
+
function envReadEffects(root, facts) {
|
|
4085
|
+
const helpers = helperFactsOf(facts);
|
|
4086
|
+
const slots = [];
|
|
4083
4087
|
(0, import_extractor4.walkDescendants)(root, null, {
|
|
4084
4088
|
at: (node) => {
|
|
4085
4089
|
const read = envReadAt(node);
|
|
4086
4090
|
if (read !== null) {
|
|
4087
|
-
|
|
4091
|
+
slots.push({ read });
|
|
4092
|
+
return;
|
|
4093
|
+
}
|
|
4094
|
+
if (helpers !== null && node.type === "call") {
|
|
4095
|
+
slots.push({ call: node });
|
|
4088
4096
|
}
|
|
4089
4097
|
},
|
|
4090
4098
|
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? import_extractor4.SKIP_CHILDREN : null
|
|
4091
4099
|
});
|
|
4092
|
-
|
|
4100
|
+
const calls = slots.flatMap((slot) => "call" in slot ? [slot.call] : []);
|
|
4101
|
+
const throughHelpers = helpers === null ? new NodeMap() : helperReads(calls, helpers);
|
|
4102
|
+
return slots.flatMap((slot) => {
|
|
4103
|
+
if ("read" in slot) {
|
|
4104
|
+
return [configReadEffect(slot.read)];
|
|
4105
|
+
}
|
|
4106
|
+
return (throughHelpers.get(slot.call) ?? []).map(configReadEffect);
|
|
4107
|
+
});
|
|
4108
|
+
}
|
|
4109
|
+
function helperFactsOf(facts) {
|
|
4110
|
+
if (facts === void 0) {
|
|
4111
|
+
return null;
|
|
4112
|
+
}
|
|
4113
|
+
const named2 = namedParameters(facts.db);
|
|
4114
|
+
if (named2.size === 0) {
|
|
4115
|
+
return null;
|
|
4116
|
+
}
|
|
4117
|
+
return { ...facts, named: named2 };
|
|
4118
|
+
}
|
|
4119
|
+
var namedByDb = /* @__PURE__ */ new WeakMap();
|
|
4120
|
+
function namedParameters(db) {
|
|
4121
|
+
const memo = namedByDb.get(db);
|
|
4122
|
+
if (memo !== void 0) {
|
|
4123
|
+
return memo;
|
|
4124
|
+
}
|
|
4125
|
+
const named2 = /* @__PURE__ */ new Map();
|
|
4126
|
+
namedByDb.set(db, named2);
|
|
4127
|
+
const sites = db.facts("readsEnvNamed").map((row) => String(row[0]));
|
|
4128
|
+
if (sites.length === 0) {
|
|
4129
|
+
return named2;
|
|
4130
|
+
}
|
|
4131
|
+
resolveEnvSites(db, sites);
|
|
4132
|
+
for (const row of db.facts("wantedParamNamesEnv")) {
|
|
4133
|
+
const found = named2.get(String(row[0]));
|
|
4134
|
+
if (found === void 0) {
|
|
4135
|
+
named2.set(String(row[0]), [String(row[1])]);
|
|
4136
|
+
continue;
|
|
4137
|
+
}
|
|
4138
|
+
found.push(String(row[1]));
|
|
4139
|
+
}
|
|
4140
|
+
return named2;
|
|
4141
|
+
}
|
|
4142
|
+
function emitEnvNameFacts(db, file, root) {
|
|
4143
|
+
for (const site of envNameSites(root)) {
|
|
4144
|
+
const key = nodeId(file, site.read);
|
|
4145
|
+
db.add("readsEnvNamed", [
|
|
4146
|
+
key,
|
|
4147
|
+
readKey(file, site.name, enclosingMethod(site.name))
|
|
4148
|
+
]);
|
|
4149
|
+
if (site.defaulted) {
|
|
4150
|
+
db.add(ENV_DEFAULTED, [key]);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
function envNameSites(root) {
|
|
4155
|
+
const sites = [];
|
|
4156
|
+
(0, import_extractor4.walkDescendants)(root, null, {
|
|
4157
|
+
at: (node) => {
|
|
4158
|
+
const site = envSiteAt(node);
|
|
4159
|
+
if (site !== null && stringLiteralValue(site.name) === null) {
|
|
4160
|
+
sites.push(site);
|
|
4161
|
+
}
|
|
4162
|
+
},
|
|
4163
|
+
into: () => null
|
|
4164
|
+
});
|
|
4165
|
+
return sites;
|
|
4166
|
+
}
|
|
4167
|
+
function helperReads(calls, facts) {
|
|
4168
|
+
const { db, file, named: named2 } = facts;
|
|
4169
|
+
const callees = new NodeMap();
|
|
4170
|
+
for (const call of calls) {
|
|
4171
|
+
const key = calleeKeyOf(file, call, enclosingMethod(call));
|
|
4172
|
+
if (key !== null) {
|
|
4173
|
+
callees.set(call, key);
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
const reads = new NodeMap();
|
|
4177
|
+
if (callees.size === 0) {
|
|
4178
|
+
return reads;
|
|
4179
|
+
}
|
|
4180
|
+
resolveValues(
|
|
4181
|
+
db,
|
|
4182
|
+
[...callees].map(([, callee]) => callee)
|
|
4183
|
+
);
|
|
4184
|
+
for (const [call, callee] of callees) {
|
|
4185
|
+
const args = namingArguments(db, named2, call, callee);
|
|
4186
|
+
if (args.length === 0) {
|
|
4187
|
+
continue;
|
|
4188
|
+
}
|
|
4189
|
+
const found = readsAtCall(db, call, args);
|
|
4190
|
+
if (found.length > 0) {
|
|
4191
|
+
reads.set(call, found);
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
return reads;
|
|
4195
|
+
}
|
|
4196
|
+
function namingArguments(db, named2, call, callee) {
|
|
4197
|
+
const { positional, keyword } = readCallArgs(field(call, "arguments"));
|
|
4198
|
+
const found = [];
|
|
4199
|
+
for (const func of resolvedFunctions(db, callee)) {
|
|
4200
|
+
for (const row of db.lookup("paramOf", 0, func)) {
|
|
4201
|
+
const sites = named2.get(String(row[2]));
|
|
4202
|
+
const argument = positional[Number(row[1])];
|
|
4203
|
+
if (sites !== void 0 && argument !== void 0) {
|
|
4204
|
+
found.push({ sites, argument });
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
for (const row of db.lookup("paramNamed", 0, func)) {
|
|
4208
|
+
const sites = named2.get(String(row[2]));
|
|
4209
|
+
const argument = keyword[String(row[1])];
|
|
4210
|
+
if (sites !== void 0 && argument !== void 0) {
|
|
4211
|
+
found.push({ sites, argument });
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
return found;
|
|
4216
|
+
}
|
|
4217
|
+
function readsAtCall(db, call, args) {
|
|
4218
|
+
const defaultedAtCall = isDefaultedAt(call);
|
|
4219
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4220
|
+
for (const { sites, argument } of args) {
|
|
4221
|
+
const name = stringLiteralValue(argument) ?? stringValueOf(argument, db);
|
|
4222
|
+
if (name === null) {
|
|
4223
|
+
continue;
|
|
4224
|
+
}
|
|
4225
|
+
for (const site of sites) {
|
|
4226
|
+
const defaulted = defaultedAtCall || db.lookup(ENV_DEFAULTED, 0, site).length > 0;
|
|
4227
|
+
byName.set(name, (byName.get(name) ?? true) && defaulted);
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
return [...byName].map(([name, defaulted]) => ({ name, defaulted }));
|
|
4093
4231
|
}
|
|
4094
4232
|
function configReadEffect(read) {
|
|
4095
4233
|
return {
|
|
@@ -4108,15 +4246,23 @@ function configReadEffect(read) {
|
|
|
4108
4246
|
};
|
|
4109
4247
|
}
|
|
4110
4248
|
function envReadAt(node) {
|
|
4249
|
+
const site = envSiteAt(node);
|
|
4250
|
+
if (site === null) {
|
|
4251
|
+
return null;
|
|
4252
|
+
}
|
|
4253
|
+
const name = stringLiteralValue(site.name);
|
|
4254
|
+
return name === null ? null : { name, defaulted: site.defaulted };
|
|
4255
|
+
}
|
|
4256
|
+
function envSiteAt(node) {
|
|
4111
4257
|
if (node.type === "element_reference") {
|
|
4112
|
-
return
|
|
4258
|
+
return elementSite(node);
|
|
4113
4259
|
}
|
|
4114
4260
|
if (node.type === "call") {
|
|
4115
|
-
return
|
|
4261
|
+
return fetchSite(node);
|
|
4116
4262
|
}
|
|
4117
4263
|
return null;
|
|
4118
4264
|
}
|
|
4119
|
-
function
|
|
4265
|
+
function elementSite(node) {
|
|
4120
4266
|
const object = field(node, "object");
|
|
4121
4267
|
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
4122
4268
|
return null;
|
|
@@ -4124,24 +4270,23 @@ function elementRead(node) {
|
|
|
4124
4270
|
const index = node.namedChildren.find(
|
|
4125
4271
|
(child) => child !== null && child.id !== object.id
|
|
4126
4272
|
);
|
|
4127
|
-
|
|
4128
|
-
if (name === null) {
|
|
4273
|
+
if (index === void 0) {
|
|
4129
4274
|
return null;
|
|
4130
4275
|
}
|
|
4131
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
4276
|
+
return { read: node, name: index, defaulted: isDefaultedAt(node) };
|
|
4132
4277
|
}
|
|
4133
|
-
function
|
|
4278
|
+
function fetchSite(node) {
|
|
4134
4279
|
const receiver = field(node, "receiver");
|
|
4135
4280
|
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
4136
4281
|
return null;
|
|
4137
4282
|
}
|
|
4138
4283
|
const { positional } = readCallArgs(field(node, "arguments"));
|
|
4139
|
-
const name = positional[0]
|
|
4140
|
-
if (name ===
|
|
4284
|
+
const name = positional[0];
|
|
4285
|
+
if (name === void 0) {
|
|
4141
4286
|
return null;
|
|
4142
4287
|
}
|
|
4143
4288
|
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
4144
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
4289
|
+
return { read: node, name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
4145
4290
|
}
|
|
4146
4291
|
function isEnv(node) {
|
|
4147
4292
|
if (node.type === "constant") {
|
|
@@ -5124,8 +5269,12 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
5124
5269
|
bodyRead.facts
|
|
5125
5270
|
);
|
|
5126
5271
|
const storage = bodyRead.storage;
|
|
5272
|
+
const facts = bodyRead.facts;
|
|
5127
5273
|
const extra = [
|
|
5128
|
-
...envReadEffects(
|
|
5274
|
+
...envReadEffects(
|
|
5275
|
+
method,
|
|
5276
|
+
facts === void 0 ? void 0 : { db: facts, file }
|
|
5277
|
+
),
|
|
5129
5278
|
...storage === void 0 ? [] : storageEffects(callsUnder2(method), file, storage, method)
|
|
5130
5279
|
];
|
|
5131
5280
|
return {
|
|
@@ -6495,7 +6644,7 @@ function parameterName(param) {
|
|
|
6495
6644
|
}
|
|
6496
6645
|
|
|
6497
6646
|
// src/reach/context.ts
|
|
6498
|
-
var
|
|
6647
|
+
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6499
6648
|
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
|
|
6500
6649
|
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
6501
6650
|
const classes = [];
|
|
@@ -6506,7 +6655,7 @@ async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dyna
|
|
|
6506
6655
|
classes.push({ file, info });
|
|
6507
6656
|
classNames.set(nodeId(file, info.node), info.qualifiedName);
|
|
6508
6657
|
for (const statement of namedChildren(info.bodyNode)) {
|
|
6509
|
-
if (
|
|
6658
|
+
if (METHOD_TYPES3.has(statement.type)) {
|
|
6510
6659
|
classOfMethod.set(nodeId(file, statement), info.qualifiedName);
|
|
6511
6660
|
}
|
|
6512
6661
|
}
|
|
@@ -6640,6 +6789,7 @@ function factsForFile(options) {
|
|
|
6640
6789
|
const db = new import_datalog3.Database();
|
|
6641
6790
|
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6642
6791
|
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6792
|
+
emitEnvNameFacts(db, options.file, options.root);
|
|
6643
6793
|
emitConstantBindings(db, [
|
|
6644
6794
|
collectFileConstants(
|
|
6645
6795
|
options.file,
|
|
@@ -6711,6 +6861,7 @@ async function extractRubyProject(options) {
|
|
|
6711
6861
|
}
|
|
6712
6862
|
parsed.push({ file, root });
|
|
6713
6863
|
emitValueFacts(db, file, root, bodyBlocks);
|
|
6864
|
+
emitEnvNameFacts(db, file, root);
|
|
6714
6865
|
for (const [key, method] of methodDefinitionsIn(file, root)) {
|
|
6715
6866
|
definitions.set(key, method);
|
|
6716
6867
|
}
|
|
@@ -6788,7 +6939,10 @@ async function extractRubyProject(options) {
|
|
|
6788
6939
|
});
|
|
6789
6940
|
}
|
|
6790
6941
|
}
|
|
6791
|
-
const loadTimeReads = timer.time(
|
|
6942
|
+
const loadTimeReads = timer.time(
|
|
6943
|
+
"discover",
|
|
6944
|
+
() => envReadEffects(root, { db, file })
|
|
6945
|
+
);
|
|
6792
6946
|
if (loadTimeReads.length > 0) {
|
|
6793
6947
|
const summary = timer.time(
|
|
6794
6948
|
"summarize",
|
|
@@ -6951,20 +7105,9 @@ function findRubyFiles(root) {
|
|
|
6951
7105
|
|
|
6952
7106
|
// src/facts/why.ts
|
|
6953
7107
|
var WITNESS_RULES = [...import_resolution9.RESOLUTION_RULES, ...RUBY_RULES];
|
|
6954
|
-
var METHOD_TYPES5 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6955
7108
|
function namedChildrenOf(node) {
|
|
6956
7109
|
return node.namedChildren.filter((child) => child !== null);
|
|
6957
7110
|
}
|
|
6958
|
-
function enclosingMethod2(node) {
|
|
6959
|
-
let current = node.parent;
|
|
6960
|
-
while (current !== null) {
|
|
6961
|
-
if (METHOD_TYPES5.has(current.type)) {
|
|
6962
|
-
return current;
|
|
6963
|
-
}
|
|
6964
|
-
current = current.parent;
|
|
6965
|
-
}
|
|
6966
|
-
return null;
|
|
6967
|
-
}
|
|
6968
7111
|
function indexFile(file, root, locations) {
|
|
6969
7112
|
const walk = (node) => {
|
|
6970
7113
|
locations.set(nodeId(file, node), { file, node });
|
|
@@ -7000,7 +7143,7 @@ function width(node) {
|
|
|
7000
7143
|
return node.endIndex - node.startIndex;
|
|
7001
7144
|
}
|
|
7002
7145
|
function askedKey(value) {
|
|
7003
|
-
const enclosing =
|
|
7146
|
+
const enclosing = enclosingMethod(value.node);
|
|
7004
7147
|
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
7005
7148
|
}
|
|
7006
7149
|
var RubyWhySession = class {
|
package/dist/index.js
CHANGED
|
@@ -63,6 +63,17 @@ 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 METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
67
|
+
function enclosingMethod(node) {
|
|
68
|
+
let current = node.parent;
|
|
69
|
+
while (current !== null) {
|
|
70
|
+
if (METHOD_TYPES.has(current.type)) {
|
|
71
|
+
return current;
|
|
72
|
+
}
|
|
73
|
+
current = current.parent;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
66
77
|
var INCLUDE_CALL = "include";
|
|
67
78
|
var PREPEND_CALL = "prepend";
|
|
68
79
|
var ARGUMENT_LIST_TYPE = "argument_list";
|
|
@@ -390,8 +401,11 @@ var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
|
390
401
|
function resolveValues(db, keys) {
|
|
391
402
|
askResolution(db, keys, "wanted", RUBY_PROGRAM);
|
|
392
403
|
}
|
|
404
|
+
function resolveEnvSites(db, sites) {
|
|
405
|
+
askResolution(db, sites, "wantedEnvSite", RUBY_PROGRAM);
|
|
406
|
+
}
|
|
393
407
|
function resolvedFunctions(db, key) {
|
|
394
|
-
return db.
|
|
408
|
+
return db.lookup("wantedResolves", 0, key).map((row) => String(row[1]));
|
|
395
409
|
}
|
|
396
410
|
function writtenValueOf(db, key) {
|
|
397
411
|
resolveValues(db, [key]);
|
|
@@ -1270,7 +1284,6 @@ function emitCandidates(emitter, key, group) {
|
|
|
1270
1284
|
add(emitter, "writesAllStated", key);
|
|
1271
1285
|
}
|
|
1272
1286
|
}
|
|
1273
|
-
var METHOD_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
1274
1287
|
var INITIALIZE_METHOD = "initialize";
|
|
1275
1288
|
function receiverKeyOf(filePath, method, classKey) {
|
|
1276
1289
|
return method.type === "singleton_method" ? classKey : `${nodeId(filePath, method)}#self`;
|
|
@@ -1318,7 +1331,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1318
1331
|
left.text,
|
|
1319
1332
|
valueKey(emitter, right)
|
|
1320
1333
|
);
|
|
1321
|
-
emitExpressionFacts(
|
|
1334
|
+
emitExpressionFacts(within, statement);
|
|
1322
1335
|
}
|
|
1323
1336
|
continue;
|
|
1324
1337
|
}
|
|
@@ -2399,7 +2412,6 @@ var evaluators = /* @__PURE__ */ new WeakMap();
|
|
|
2399
2412
|
var contexts = /* @__PURE__ */ new WeakMap();
|
|
2400
2413
|
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2401
2414
|
var filesByTree = /* @__PURE__ */ new WeakMap();
|
|
2402
|
-
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
2403
2415
|
function bindEvaluator(db, nodes) {
|
|
2404
2416
|
for (const entry of nodes.files) {
|
|
2405
2417
|
filesByTree.set(entry.root.tree, entry.file);
|
|
@@ -2453,7 +2465,7 @@ function stringValueOf(node, db, bindings) {
|
|
|
2453
2465
|
function methodDefinitionsIn(file, root) {
|
|
2454
2466
|
const found = /* @__PURE__ */ new Map();
|
|
2455
2467
|
const visit = (node) => {
|
|
2456
|
-
if (
|
|
2468
|
+
if (METHOD_TYPES.has(node.type)) {
|
|
2457
2469
|
found.set(nodeId(file, node), node);
|
|
2458
2470
|
}
|
|
2459
2471
|
for (const child of node.namedChildren) {
|
|
@@ -2478,16 +2490,6 @@ function evaluatorFor(node, db) {
|
|
|
2478
2490
|
}
|
|
2479
2491
|
return local;
|
|
2480
2492
|
}
|
|
2481
|
-
function enclosingMethod(node) {
|
|
2482
|
-
let current = node.parent;
|
|
2483
|
-
while (current !== null) {
|
|
2484
|
-
if (METHOD_TYPES3.has(current.type)) {
|
|
2485
|
-
return current;
|
|
2486
|
-
}
|
|
2487
|
-
current = current.parent;
|
|
2488
|
-
}
|
|
2489
|
-
return null;
|
|
2490
|
-
}
|
|
2491
2493
|
function contextOver(db, nodes) {
|
|
2492
2494
|
const filesByRoot = /* @__PURE__ */ new Map();
|
|
2493
2495
|
const rootsByFile = /* @__PURE__ */ new Map();
|
|
@@ -4035,19 +4037,155 @@ function returnBranch(range, effects) {
|
|
|
4035
4037
|
import { runtimeConfigBinding } from "@suss/behavioral-ir";
|
|
4036
4038
|
import { SKIP_CHILDREN as SKIP_CHILDREN2, walkDescendants as walkDescendants2 } from "@suss/extractor";
|
|
4037
4039
|
var RUBY_ENV_RECOGNITION = "ruby-env";
|
|
4040
|
+
var ENV_DEFAULTED = "rbEnvDefaulted";
|
|
4038
4041
|
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
4039
|
-
function envReadEffects(root) {
|
|
4040
|
-
const
|
|
4042
|
+
function envReadEffects(root, facts) {
|
|
4043
|
+
const helpers = helperFactsOf(facts);
|
|
4044
|
+
const slots = [];
|
|
4041
4045
|
walkDescendants2(root, null, {
|
|
4042
4046
|
at: (node) => {
|
|
4043
4047
|
const read = envReadAt(node);
|
|
4044
4048
|
if (read !== null) {
|
|
4045
|
-
|
|
4049
|
+
slots.push({ read });
|
|
4050
|
+
return;
|
|
4051
|
+
}
|
|
4052
|
+
if (helpers !== null && node.type === "call") {
|
|
4053
|
+
slots.push({ call: node });
|
|
4046
4054
|
}
|
|
4047
4055
|
},
|
|
4048
4056
|
into: (node) => DEFERRED_BODY_TYPES.has(node.type) ? SKIP_CHILDREN2 : null
|
|
4049
4057
|
});
|
|
4050
|
-
|
|
4058
|
+
const calls = slots.flatMap((slot) => "call" in slot ? [slot.call] : []);
|
|
4059
|
+
const throughHelpers = helpers === null ? new NodeMap() : helperReads(calls, helpers);
|
|
4060
|
+
return slots.flatMap((slot) => {
|
|
4061
|
+
if ("read" in slot) {
|
|
4062
|
+
return [configReadEffect(slot.read)];
|
|
4063
|
+
}
|
|
4064
|
+
return (throughHelpers.get(slot.call) ?? []).map(configReadEffect);
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
function helperFactsOf(facts) {
|
|
4068
|
+
if (facts === void 0) {
|
|
4069
|
+
return null;
|
|
4070
|
+
}
|
|
4071
|
+
const named2 = namedParameters(facts.db);
|
|
4072
|
+
if (named2.size === 0) {
|
|
4073
|
+
return null;
|
|
4074
|
+
}
|
|
4075
|
+
return { ...facts, named: named2 };
|
|
4076
|
+
}
|
|
4077
|
+
var namedByDb = /* @__PURE__ */ new WeakMap();
|
|
4078
|
+
function namedParameters(db) {
|
|
4079
|
+
const memo = namedByDb.get(db);
|
|
4080
|
+
if (memo !== void 0) {
|
|
4081
|
+
return memo;
|
|
4082
|
+
}
|
|
4083
|
+
const named2 = /* @__PURE__ */ new Map();
|
|
4084
|
+
namedByDb.set(db, named2);
|
|
4085
|
+
const sites = db.facts("readsEnvNamed").map((row) => String(row[0]));
|
|
4086
|
+
if (sites.length === 0) {
|
|
4087
|
+
return named2;
|
|
4088
|
+
}
|
|
4089
|
+
resolveEnvSites(db, sites);
|
|
4090
|
+
for (const row of db.facts("wantedParamNamesEnv")) {
|
|
4091
|
+
const found = named2.get(String(row[0]));
|
|
4092
|
+
if (found === void 0) {
|
|
4093
|
+
named2.set(String(row[0]), [String(row[1])]);
|
|
4094
|
+
continue;
|
|
4095
|
+
}
|
|
4096
|
+
found.push(String(row[1]));
|
|
4097
|
+
}
|
|
4098
|
+
return named2;
|
|
4099
|
+
}
|
|
4100
|
+
function emitEnvNameFacts(db, file, root) {
|
|
4101
|
+
for (const site of envNameSites(root)) {
|
|
4102
|
+
const key = nodeId(file, site.read);
|
|
4103
|
+
db.add("readsEnvNamed", [
|
|
4104
|
+
key,
|
|
4105
|
+
readKey(file, site.name, enclosingMethod(site.name))
|
|
4106
|
+
]);
|
|
4107
|
+
if (site.defaulted) {
|
|
4108
|
+
db.add(ENV_DEFAULTED, [key]);
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
function envNameSites(root) {
|
|
4113
|
+
const sites = [];
|
|
4114
|
+
walkDescendants2(root, null, {
|
|
4115
|
+
at: (node) => {
|
|
4116
|
+
const site = envSiteAt(node);
|
|
4117
|
+
if (site !== null && stringLiteralValue(site.name) === null) {
|
|
4118
|
+
sites.push(site);
|
|
4119
|
+
}
|
|
4120
|
+
},
|
|
4121
|
+
into: () => null
|
|
4122
|
+
});
|
|
4123
|
+
return sites;
|
|
4124
|
+
}
|
|
4125
|
+
function helperReads(calls, facts) {
|
|
4126
|
+
const { db, file, named: named2 } = facts;
|
|
4127
|
+
const callees = new NodeMap();
|
|
4128
|
+
for (const call of calls) {
|
|
4129
|
+
const key = calleeKeyOf(file, call, enclosingMethod(call));
|
|
4130
|
+
if (key !== null) {
|
|
4131
|
+
callees.set(call, key);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
const reads = new NodeMap();
|
|
4135
|
+
if (callees.size === 0) {
|
|
4136
|
+
return reads;
|
|
4137
|
+
}
|
|
4138
|
+
resolveValues(
|
|
4139
|
+
db,
|
|
4140
|
+
[...callees].map(([, callee]) => callee)
|
|
4141
|
+
);
|
|
4142
|
+
for (const [call, callee] of callees) {
|
|
4143
|
+
const args = namingArguments(db, named2, call, callee);
|
|
4144
|
+
if (args.length === 0) {
|
|
4145
|
+
continue;
|
|
4146
|
+
}
|
|
4147
|
+
const found = readsAtCall(db, call, args);
|
|
4148
|
+
if (found.length > 0) {
|
|
4149
|
+
reads.set(call, found);
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4152
|
+
return reads;
|
|
4153
|
+
}
|
|
4154
|
+
function namingArguments(db, named2, call, callee) {
|
|
4155
|
+
const { positional, keyword } = readCallArgs(field(call, "arguments"));
|
|
4156
|
+
const found = [];
|
|
4157
|
+
for (const func of resolvedFunctions(db, callee)) {
|
|
4158
|
+
for (const row of db.lookup("paramOf", 0, func)) {
|
|
4159
|
+
const sites = named2.get(String(row[2]));
|
|
4160
|
+
const argument = positional[Number(row[1])];
|
|
4161
|
+
if (sites !== void 0 && argument !== void 0) {
|
|
4162
|
+
found.push({ sites, argument });
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
for (const row of db.lookup("paramNamed", 0, func)) {
|
|
4166
|
+
const sites = named2.get(String(row[2]));
|
|
4167
|
+
const argument = keyword[String(row[1])];
|
|
4168
|
+
if (sites !== void 0 && argument !== void 0) {
|
|
4169
|
+
found.push({ sites, argument });
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
return found;
|
|
4174
|
+
}
|
|
4175
|
+
function readsAtCall(db, call, args) {
|
|
4176
|
+
const defaultedAtCall = isDefaultedAt(call);
|
|
4177
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4178
|
+
for (const { sites, argument } of args) {
|
|
4179
|
+
const name = stringLiteralValue(argument) ?? stringValueOf(argument, db);
|
|
4180
|
+
if (name === null) {
|
|
4181
|
+
continue;
|
|
4182
|
+
}
|
|
4183
|
+
for (const site of sites) {
|
|
4184
|
+
const defaulted = defaultedAtCall || db.lookup(ENV_DEFAULTED, 0, site).length > 0;
|
|
4185
|
+
byName.set(name, (byName.get(name) ?? true) && defaulted);
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
return [...byName].map(([name, defaulted]) => ({ name, defaulted }));
|
|
4051
4189
|
}
|
|
4052
4190
|
function configReadEffect(read) {
|
|
4053
4191
|
return {
|
|
@@ -4066,15 +4204,23 @@ function configReadEffect(read) {
|
|
|
4066
4204
|
};
|
|
4067
4205
|
}
|
|
4068
4206
|
function envReadAt(node) {
|
|
4207
|
+
const site = envSiteAt(node);
|
|
4208
|
+
if (site === null) {
|
|
4209
|
+
return null;
|
|
4210
|
+
}
|
|
4211
|
+
const name = stringLiteralValue(site.name);
|
|
4212
|
+
return name === null ? null : { name, defaulted: site.defaulted };
|
|
4213
|
+
}
|
|
4214
|
+
function envSiteAt(node) {
|
|
4069
4215
|
if (node.type === "element_reference") {
|
|
4070
|
-
return
|
|
4216
|
+
return elementSite(node);
|
|
4071
4217
|
}
|
|
4072
4218
|
if (node.type === "call") {
|
|
4073
|
-
return
|
|
4219
|
+
return fetchSite(node);
|
|
4074
4220
|
}
|
|
4075
4221
|
return null;
|
|
4076
4222
|
}
|
|
4077
|
-
function
|
|
4223
|
+
function elementSite(node) {
|
|
4078
4224
|
const object = field(node, "object");
|
|
4079
4225
|
if (object === null || !isEnv(object) || isAssignedTo(node)) {
|
|
4080
4226
|
return null;
|
|
@@ -4082,24 +4228,23 @@ function elementRead(node) {
|
|
|
4082
4228
|
const index = node.namedChildren.find(
|
|
4083
4229
|
(child) => child !== null && child.id !== object.id
|
|
4084
4230
|
);
|
|
4085
|
-
|
|
4086
|
-
if (name === null) {
|
|
4231
|
+
if (index === void 0) {
|
|
4087
4232
|
return null;
|
|
4088
4233
|
}
|
|
4089
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
4234
|
+
return { read: node, name: index, defaulted: isDefaultedAt(node) };
|
|
4090
4235
|
}
|
|
4091
|
-
function
|
|
4236
|
+
function fetchSite(node) {
|
|
4092
4237
|
const receiver = field(node, "receiver");
|
|
4093
4238
|
if (receiver === null || !isEnv(receiver) || field(node, "method")?.text !== "fetch") {
|
|
4094
4239
|
return null;
|
|
4095
4240
|
}
|
|
4096
4241
|
const { positional } = readCallArgs(field(node, "arguments"));
|
|
4097
|
-
const name = positional[0]
|
|
4098
|
-
if (name ===
|
|
4242
|
+
const name = positional[0];
|
|
4243
|
+
if (name === void 0) {
|
|
4099
4244
|
return null;
|
|
4100
4245
|
}
|
|
4101
4246
|
const hasDefault = positional.length > 1 || field(node, "block") !== null;
|
|
4102
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
4247
|
+
return { read: node, name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
4103
4248
|
}
|
|
4104
4249
|
function isEnv(node) {
|
|
4105
4250
|
if (node.type === "constant") {
|
|
@@ -5082,8 +5227,12 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
5082
5227
|
bodyRead.facts
|
|
5083
5228
|
);
|
|
5084
5229
|
const storage = bodyRead.storage;
|
|
5230
|
+
const facts = bodyRead.facts;
|
|
5085
5231
|
const extra = [
|
|
5086
|
-
...envReadEffects(
|
|
5232
|
+
...envReadEffects(
|
|
5233
|
+
method,
|
|
5234
|
+
facts === void 0 ? void 0 : { db: facts, file }
|
|
5235
|
+
),
|
|
5087
5236
|
...storage === void 0 ? [] : storageEffects(callsUnder2(method), file, storage, method)
|
|
5088
5237
|
];
|
|
5089
5238
|
return {
|
|
@@ -6489,7 +6638,7 @@ function parameterName(param) {
|
|
|
6489
6638
|
}
|
|
6490
6639
|
|
|
6491
6640
|
// src/reach/context.ts
|
|
6492
|
-
var
|
|
6641
|
+
var METHOD_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6493
6642
|
async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dynamicNames = /* @__PURE__ */ new Map()) {
|
|
6494
6643
|
const blocksByQualifiedName = /* @__PURE__ */ new Map();
|
|
6495
6644
|
const classes = [];
|
|
@@ -6500,7 +6649,7 @@ async function buildReachContext(files, facts, bodyBlocks = NO_BODY_BLOCKS, dyna
|
|
|
6500
6649
|
classes.push({ file, info });
|
|
6501
6650
|
classNames.set(nodeId(file, info.node), info.qualifiedName);
|
|
6502
6651
|
for (const statement of namedChildren(info.bodyNode)) {
|
|
6503
|
-
if (
|
|
6652
|
+
if (METHOD_TYPES3.has(statement.type)) {
|
|
6504
6653
|
classOfMethod.set(nodeId(file, statement), info.qualifiedName);
|
|
6505
6654
|
}
|
|
6506
6655
|
}
|
|
@@ -6634,6 +6783,7 @@ function factsForFile(options) {
|
|
|
6634
6783
|
const db = new Database2();
|
|
6635
6784
|
const bodyBlocks = bodyBlocksIn(options.packs);
|
|
6636
6785
|
emitValueFacts(db, options.file, options.root, bodyBlocks);
|
|
6786
|
+
emitEnvNameFacts(db, options.file, options.root);
|
|
6637
6787
|
emitConstantBindings(db, [
|
|
6638
6788
|
collectFileConstants(
|
|
6639
6789
|
options.file,
|
|
@@ -6705,6 +6855,7 @@ async function extractRubyProject(options) {
|
|
|
6705
6855
|
}
|
|
6706
6856
|
parsed.push({ file, root });
|
|
6707
6857
|
emitValueFacts(db, file, root, bodyBlocks);
|
|
6858
|
+
emitEnvNameFacts(db, file, root);
|
|
6708
6859
|
for (const [key, method] of methodDefinitionsIn(file, root)) {
|
|
6709
6860
|
definitions.set(key, method);
|
|
6710
6861
|
}
|
|
@@ -6782,7 +6933,10 @@ async function extractRubyProject(options) {
|
|
|
6782
6933
|
});
|
|
6783
6934
|
}
|
|
6784
6935
|
}
|
|
6785
|
-
const loadTimeReads = timer.time(
|
|
6936
|
+
const loadTimeReads = timer.time(
|
|
6937
|
+
"discover",
|
|
6938
|
+
() => envReadEffects(root, { db, file })
|
|
6939
|
+
);
|
|
6786
6940
|
if (loadTimeReads.length > 0) {
|
|
6787
6941
|
const summary = timer.time(
|
|
6788
6942
|
"summarize",
|
|
@@ -6945,20 +7099,9 @@ function findRubyFiles(root) {
|
|
|
6945
7099
|
|
|
6946
7100
|
// src/facts/why.ts
|
|
6947
7101
|
var WITNESS_RULES = [...RESOLUTION_RULES, ...RUBY_RULES];
|
|
6948
|
-
var METHOD_TYPES5 = /* @__PURE__ */ new Set(["method", "singleton_method"]);
|
|
6949
7102
|
function namedChildrenOf(node) {
|
|
6950
7103
|
return node.namedChildren.filter((child) => child !== null);
|
|
6951
7104
|
}
|
|
6952
|
-
function enclosingMethod2(node) {
|
|
6953
|
-
let current = node.parent;
|
|
6954
|
-
while (current !== null) {
|
|
6955
|
-
if (METHOD_TYPES5.has(current.type)) {
|
|
6956
|
-
return current;
|
|
6957
|
-
}
|
|
6958
|
-
current = current.parent;
|
|
6959
|
-
}
|
|
6960
|
-
return null;
|
|
6961
|
-
}
|
|
6962
7105
|
function indexFile(file, root, locations) {
|
|
6963
7106
|
const walk = (node) => {
|
|
6964
7107
|
locations.set(nodeId(file, node), { file, node });
|
|
@@ -6994,7 +7137,7 @@ function width(node) {
|
|
|
6994
7137
|
return node.endIndex - node.startIndex;
|
|
6995
7138
|
}
|
|
6996
7139
|
function askedKey(value) {
|
|
6997
|
-
const enclosing =
|
|
7140
|
+
const enclosing = enclosingMethod(value.node);
|
|
6998
7141
|
return calleeKeyOf(value.file, value.node, enclosing) ?? readKey(value.file, value.node, enclosing);
|
|
6999
7142
|
}
|
|
7000
7143
|
var RubyWhySession = class {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/adapter-ruby",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Read a Ruby codebase with suss and check what each Rails action or graphql-ruby resolver returns against the code that calls it.",
|
|
6
6
|
"keywords": [
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@suss/behavioral-ir": "0.
|
|
53
|
-
"@suss/datalog": "0.
|
|
54
|
-
"@suss/extractor": "0.
|
|
55
|
-
"@suss/ir-core": "0.
|
|
56
|
-
"@suss/resolution": "0.
|
|
57
|
-
"@suss/values": "0.
|
|
52
|
+
"@suss/behavioral-ir": "0.32.0",
|
|
53
|
+
"@suss/datalog": "0.32.0",
|
|
54
|
+
"@suss/extractor": "0.32.0",
|
|
55
|
+
"@suss/ir-core": "0.32.0",
|
|
56
|
+
"@suss/resolution": "0.32.0",
|
|
57
|
+
"@suss/values": "0.32.0",
|
|
58
58
|
"web-tree-sitter": "^0.26.11"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|