@suss/adapter-python 0.31.2 → 0.33.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/README.md +6 -4
- package/dist/index.cjs +865 -240
- package/dist/index.d.cts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +861 -235
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -97,6 +97,12 @@ var LATER_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
97
97
|
"lambda",
|
|
98
98
|
"class_definition"
|
|
99
99
|
]);
|
|
100
|
+
function isModule(node) {
|
|
101
|
+
return node.type === "module";
|
|
102
|
+
}
|
|
103
|
+
function runsAtModuleLoad(node) {
|
|
104
|
+
return !LATER_BODY_TYPES.has(node.type) && node.type !== "decorator";
|
|
105
|
+
}
|
|
100
106
|
function enclosingFunction(node) {
|
|
101
107
|
let current = node.parent;
|
|
102
108
|
while (current !== null) {
|
|
@@ -784,6 +790,15 @@ var import_resolution = require("@suss/resolution");
|
|
|
784
790
|
function resolveCalls(db, callKeys) {
|
|
785
791
|
(0, import_resolution.askResolution)(db, callKeys);
|
|
786
792
|
}
|
|
793
|
+
function resolveEnvObjects(db, objectKeys) {
|
|
794
|
+
(0, import_resolution.askResolution)(db, objectKeys, "wantedEnvObject");
|
|
795
|
+
}
|
|
796
|
+
function resolvedFunctions(db, key) {
|
|
797
|
+
return [
|
|
798
|
+
...db.lookup("wantedResolves", 0, key),
|
|
799
|
+
...db.lookup("wantedGivesBack", 0, key)
|
|
800
|
+
].map((row) => String(row[1]));
|
|
801
|
+
}
|
|
787
802
|
function writtenValueUnder(db, key, site) {
|
|
788
803
|
if ((0, import_resolution.askResolutionUnder)(db, [[key, site]]) === "abandoned") {
|
|
789
804
|
return null;
|
|
@@ -802,6 +817,13 @@ function writtenValuesOf(db, key) {
|
|
|
802
817
|
resolveCalls(db, [key]);
|
|
803
818
|
return (0, import_resolution.writtenValuesOf)(db, key, (keys) => resolveCalls(db, keys));
|
|
804
819
|
}
|
|
820
|
+
function settleWrittenValues(db, keys) {
|
|
821
|
+
if (keys.length === 0) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
resolveCalls(db, keys);
|
|
825
|
+
(0, import_resolution.writtenValuesByKey)(db, keys, (behind) => resolveCalls(db, behind));
|
|
826
|
+
}
|
|
805
827
|
function originsOf(db, nameKey2) {
|
|
806
828
|
(0, import_resolution.askResolution)(db, [nameKey2], "wantedOrigin");
|
|
807
829
|
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey2).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
@@ -992,31 +1014,46 @@ function emitCall(emitter, call) {
|
|
|
992
1014
|
if (!emitter.insideMethod) {
|
|
993
1015
|
add(emitter, "callOutsideMethod", callKey);
|
|
994
1016
|
}
|
|
1017
|
+
for (const argument of callArguments(call)) {
|
|
1018
|
+
if (argument.kind === "keyword") {
|
|
1019
|
+
add(
|
|
1020
|
+
emitter,
|
|
1021
|
+
"callKeywordArg",
|
|
1022
|
+
callKey,
|
|
1023
|
+
argument.name,
|
|
1024
|
+
valueKey(emitter, argument.node)
|
|
1025
|
+
);
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
add(
|
|
1029
|
+
emitter,
|
|
1030
|
+
"callArg",
|
|
1031
|
+
callKey,
|
|
1032
|
+
String(argument.position),
|
|
1033
|
+
valueKey(emitter, argument.node)
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
function callArguments(call) {
|
|
1038
|
+
const args = field(call, "arguments");
|
|
1039
|
+
if (args === null) {
|
|
1040
|
+
return [];
|
|
1041
|
+
}
|
|
1042
|
+
const written = [];
|
|
995
1043
|
let position = 0;
|
|
996
1044
|
for (const argument of children(args)) {
|
|
997
1045
|
if (argument.type === "keyword_argument") {
|
|
998
1046
|
const name = field(argument, "name");
|
|
999
1047
|
const value = field(argument, "value");
|
|
1000
1048
|
if (name !== null && value !== null) {
|
|
1001
|
-
|
|
1002
|
-
emitter,
|
|
1003
|
-
"callKeywordArg",
|
|
1004
|
-
callKey,
|
|
1005
|
-
name.text,
|
|
1006
|
-
valueKey(emitter, value)
|
|
1007
|
-
);
|
|
1049
|
+
written.push({ kind: "keyword", name: name.text, node: value });
|
|
1008
1050
|
}
|
|
1009
1051
|
continue;
|
|
1010
1052
|
}
|
|
1011
|
-
|
|
1012
|
-
emitter,
|
|
1013
|
-
"callArg",
|
|
1014
|
-
callKey,
|
|
1015
|
-
String(position),
|
|
1016
|
-
valueKey(emitter, argument)
|
|
1017
|
-
);
|
|
1053
|
+
written.push({ kind: "positional", position, node: argument });
|
|
1018
1054
|
position += 1;
|
|
1019
1055
|
}
|
|
1056
|
+
return written;
|
|
1020
1057
|
}
|
|
1021
1058
|
function valueKey(emitter, value) {
|
|
1022
1059
|
if (value.type !== "identifier") {
|
|
@@ -1083,9 +1120,51 @@ function walkExpressions(emitter, node, visit) {
|
|
|
1083
1120
|
walkExpressions(emitter, child, visit);
|
|
1084
1121
|
}
|
|
1085
1122
|
}
|
|
1123
|
+
var WRITTEN_KEY_TYPES = /* @__PURE__ */ new Set(["string", "integer", "concatenated_string"]);
|
|
1124
|
+
function emitKeyedRead(emitter, site, container, key) {
|
|
1125
|
+
if (WRITTEN_KEY_TYPES.has(key.type)) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
add(
|
|
1129
|
+
emitter,
|
|
1130
|
+
"readsKeyed",
|
|
1131
|
+
nodeId(emitter.filePath, site),
|
|
1132
|
+
valueKey(emitter, container),
|
|
1133
|
+
valueKey(emitter, key)
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
function singleSubscript(node) {
|
|
1137
|
+
const written = fields(node, "subscript");
|
|
1138
|
+
return written.length === 1 ? written[0] ?? null : null;
|
|
1139
|
+
}
|
|
1140
|
+
function mappingGetRead(call) {
|
|
1141
|
+
const callee = field(call, "function");
|
|
1142
|
+
if (callee === null || callee.type !== "attribute") {
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
const container = field(callee, "object");
|
|
1146
|
+
if (container === null || field(callee, "attribute")?.text !== "get") {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
const first = callArguments(call).find(
|
|
1150
|
+
(argument) => argument.kind === "positional" && argument.position === 0
|
|
1151
|
+
);
|
|
1152
|
+
return first === void 0 ? null : { container, key: first.node };
|
|
1153
|
+
}
|
|
1086
1154
|
function emitExpressionFact(emitter, child) {
|
|
1087
1155
|
if (child.type === "call") {
|
|
1088
1156
|
emitCall(emitter, child);
|
|
1157
|
+
const mapping = mappingGetRead(child);
|
|
1158
|
+
if (mapping !== null) {
|
|
1159
|
+
emitKeyedRead(emitter, child, mapping.container, mapping.key);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
if (child.type === "subscript") {
|
|
1163
|
+
const index = singleSubscript(child);
|
|
1164
|
+
const container = field(child, "value");
|
|
1165
|
+
if (index !== null && container !== null) {
|
|
1166
|
+
emitKeyedRead(emitter, child, container, index);
|
|
1167
|
+
}
|
|
1089
1168
|
}
|
|
1090
1169
|
if (child.type === "dictionary") {
|
|
1091
1170
|
emitDictionary(emitter, child);
|
|
@@ -1153,6 +1232,9 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1153
1232
|
return funcKey;
|
|
1154
1233
|
}
|
|
1155
1234
|
emitNestedDefinitions(inside, body);
|
|
1235
|
+
if (fn.type === "lambda") {
|
|
1236
|
+
emitExpressionFact(inside, body);
|
|
1237
|
+
}
|
|
1156
1238
|
let statesReturn = false;
|
|
1157
1239
|
const stores = /* @__PURE__ */ new Map();
|
|
1158
1240
|
walkExpressions(inside, body, (child) => {
|
|
@@ -2511,6 +2593,10 @@ function writtenNodeOf(node, db) {
|
|
|
2511
2593
|
const bound = db === void 0 ? void 0 : projects.get(db);
|
|
2512
2594
|
return bound?.context.writtenTo(node) ?? null;
|
|
2513
2595
|
}
|
|
2596
|
+
function resolutionKeyOf(node, db) {
|
|
2597
|
+
const bound = db === void 0 ? void 0 : projects.get(db);
|
|
2598
|
+
return bound?.keyOf(node) ?? null;
|
|
2599
|
+
}
|
|
2514
2600
|
var NOTHING_BUILT = { type: "noCall" };
|
|
2515
2601
|
function constructionBehind(node, db) {
|
|
2516
2602
|
if (db === void 0) {
|
|
@@ -2545,8 +2631,17 @@ function askWrittenValues(nodes, db) {
|
|
|
2545
2631
|
if (bound === void 0) {
|
|
2546
2632
|
return;
|
|
2547
2633
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2634
|
+
settleWrittenValues(db, resolutionKeysOf(nodes, bound));
|
|
2635
|
+
}
|
|
2636
|
+
function resolutionKeysOf(nodes, bound) {
|
|
2637
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2638
|
+
for (const node of nodes) {
|
|
2639
|
+
const key = bound.keyOf(node);
|
|
2640
|
+
if (key !== null) {
|
|
2641
|
+
keys.add(key);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return [...keys];
|
|
2550
2645
|
}
|
|
2551
2646
|
function evaluatedValue(node, db, site) {
|
|
2552
2647
|
const evaluator = evaluatorFor(node, db);
|
|
@@ -2614,7 +2709,7 @@ function projectOver(db, nodes) {
|
|
|
2614
2709
|
filesByRoot.set(entry.root.id, entry);
|
|
2615
2710
|
rootsByFile.set(entry.file, entry.root);
|
|
2616
2711
|
}
|
|
2617
|
-
const
|
|
2712
|
+
const keyOf3 = (node) => {
|
|
2618
2713
|
const entry = filesByRoot.get(node.tree.rootNode.id);
|
|
2619
2714
|
if (entry === void 0) {
|
|
2620
2715
|
return null;
|
|
@@ -2623,7 +2718,7 @@ function projectOver(db, nodes) {
|
|
|
2623
2718
|
};
|
|
2624
2719
|
const context = {
|
|
2625
2720
|
writtenTo: (node, site) => {
|
|
2626
|
-
const key =
|
|
2721
|
+
const key = keyOf3(node);
|
|
2627
2722
|
if (key === null) {
|
|
2628
2723
|
return null;
|
|
2629
2724
|
}
|
|
@@ -2632,17 +2727,17 @@ function projectOver(db, nodes) {
|
|
|
2632
2727
|
},
|
|
2633
2728
|
callable: (call) => {
|
|
2634
2729
|
const callee = field(call, "function");
|
|
2635
|
-
const key = callee === null ? null :
|
|
2730
|
+
const key = callee === null ? null : keyOf3(callee);
|
|
2636
2731
|
if (key === null) {
|
|
2637
2732
|
return null;
|
|
2638
2733
|
}
|
|
2639
2734
|
resolveCalls(db, [key]);
|
|
2640
|
-
const resolved = db.
|
|
2641
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2735
|
+
const resolved = db.lookup("wantedResolves", 0, key);
|
|
2736
|
+
const settled = resolved.length === 1 ? String(resolved[0]?.[1]) : void 0;
|
|
2642
2737
|
return settled === void 0 ? null : nodes.definitions.get(settled) ?? null;
|
|
2643
2738
|
}
|
|
2644
2739
|
};
|
|
2645
|
-
return { keyOf:
|
|
2740
|
+
return { keyOf: keyOf3, roots: rootsByFile, context };
|
|
2646
2741
|
}
|
|
2647
2742
|
|
|
2648
2743
|
// src/decorators.ts
|
|
@@ -2857,7 +2952,7 @@ function passesParametersThrough(def, inner) {
|
|
|
2857
2952
|
// src/discovery.ts
|
|
2858
2953
|
var import_behavioral_ir4 = require("@suss/behavioral-ir");
|
|
2859
2954
|
var import_extractor5 = require("@suss/extractor");
|
|
2860
|
-
var
|
|
2955
|
+
var import_values13 = require("@suss/values");
|
|
2861
2956
|
|
|
2862
2957
|
// src/clientCalls.ts
|
|
2863
2958
|
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
@@ -3270,6 +3365,18 @@ function bodyCalls(node, found = []) {
|
|
|
3270
3365
|
}
|
|
3271
3366
|
return found;
|
|
3272
3367
|
}
|
|
3368
|
+
function moduleLoadCalls(node, found = []) {
|
|
3369
|
+
for (const child of node.namedChildren) {
|
|
3370
|
+
if (child === null || !runsAtModuleLoad(child)) {
|
|
3371
|
+
continue;
|
|
3372
|
+
}
|
|
3373
|
+
if (child.type === "call") {
|
|
3374
|
+
found.push(child);
|
|
3375
|
+
}
|
|
3376
|
+
moduleLoadCalls(child, found);
|
|
3377
|
+
}
|
|
3378
|
+
return found;
|
|
3379
|
+
}
|
|
3273
3380
|
function enclosingStatement(call, body) {
|
|
3274
3381
|
let current = call;
|
|
3275
3382
|
while (current !== null && current.parent !== null) {
|
|
@@ -3294,15 +3401,18 @@ function argOf(node, facts) {
|
|
|
3294
3401
|
}
|
|
3295
3402
|
return { kind: "identifier", name: node.text };
|
|
3296
3403
|
}
|
|
3297
|
-
function
|
|
3404
|
+
function argumentNodes(call) {
|
|
3298
3405
|
const args = field(call, "arguments");
|
|
3299
3406
|
if (args === null) {
|
|
3300
3407
|
return [];
|
|
3301
3408
|
}
|
|
3302
3409
|
return args.namedChildren.filter((child) => child !== null).map(
|
|
3303
|
-
(child) => child.type === "keyword_argument" ?
|
|
3410
|
+
(child) => child.type === "keyword_argument" ? field(child, "value") ?? child : child
|
|
3304
3411
|
);
|
|
3305
3412
|
}
|
|
3413
|
+
function argsOf(call, facts) {
|
|
3414
|
+
return argumentNodes(call).map((node) => argOf(node, facts));
|
|
3415
|
+
}
|
|
3306
3416
|
function calleeText(call) {
|
|
3307
3417
|
return field(call, "function")?.text ?? call.text;
|
|
3308
3418
|
}
|
|
@@ -3311,7 +3421,40 @@ function invocationEffects(definitionNode, facts) {
|
|
|
3311
3421
|
if (body === null) {
|
|
3312
3422
|
return [];
|
|
3313
3423
|
}
|
|
3314
|
-
|
|
3424
|
+
return invocationEffectsIn(body, bodyCalls(body), facts);
|
|
3425
|
+
}
|
|
3426
|
+
function moduleLoadInvocationEffects(moduleNode, facts) {
|
|
3427
|
+
return invocationEffectsIn(moduleNode, moduleLoadCalls(moduleNode), facts);
|
|
3428
|
+
}
|
|
3429
|
+
var ASKED_ABOUT_TYPES = /* @__PURE__ */ new Set(["identifier", "attribute", "call"]);
|
|
3430
|
+
function askedNodesUnder(node, found = []) {
|
|
3431
|
+
if (ASKED_ABOUT_TYPES.has(node.type)) {
|
|
3432
|
+
found.push(node);
|
|
3433
|
+
}
|
|
3434
|
+
for (const child of node.namedChildren) {
|
|
3435
|
+
if (child !== null && !NESTED_DEFINITION_TYPES.has(child.type)) {
|
|
3436
|
+
askedNodesUnder(child, found);
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
return found;
|
|
3440
|
+
}
|
|
3441
|
+
function bodyValueNodes(definitionNode) {
|
|
3442
|
+
const body = field(definitionNode, "body");
|
|
3443
|
+
return body === null ? [] : askedNodesUnder(body);
|
|
3444
|
+
}
|
|
3445
|
+
function effectValueNodes(call, gating) {
|
|
3446
|
+
const found = argumentNodes(call).flatMap((node) => askedNodesUnder(node));
|
|
3447
|
+
for (const path6 of gating ?? []) {
|
|
3448
|
+
for (const condition of path6) {
|
|
3449
|
+
if (condition.expression !== null) {
|
|
3450
|
+
askedNodesUnder(condition.expression, found);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
return found;
|
|
3455
|
+
}
|
|
3456
|
+
function invocationEffectsIn(body, written, facts) {
|
|
3457
|
+
const calls = [...written].sort((a, b) => a.endIndex - b.endIndex);
|
|
3315
3458
|
if (calls.length === 0) {
|
|
3316
3459
|
return [];
|
|
3317
3460
|
}
|
|
@@ -3333,10 +3476,17 @@ function invocationEffects(definitionNode, facts) {
|
|
|
3333
3476
|
},
|
|
3334
3477
|
statements
|
|
3335
3478
|
);
|
|
3336
|
-
|
|
3479
|
+
const gatingOf = calls.map((call) => {
|
|
3337
3480
|
const statement = statementOf3.get(call.id);
|
|
3481
|
+
return statement === void 0 ? void 0 : enumerated.byTerminal.get(statement);
|
|
3482
|
+
});
|
|
3483
|
+
askWrittenValues(
|
|
3484
|
+
calls.flatMap((call, index) => effectValueNodes(call, gatingOf[index])),
|
|
3485
|
+
facts
|
|
3486
|
+
);
|
|
3487
|
+
return calls.map((call, index) => {
|
|
3338
3488
|
const conditions = (0, import_extractor3.sharedGatingConditions)(
|
|
3339
|
-
|
|
3489
|
+
gatingOf[index],
|
|
3340
3490
|
(condition) => predicateOf(condition, facts)
|
|
3341
3491
|
);
|
|
3342
3492
|
return {
|
|
@@ -3367,6 +3517,22 @@ function clientCallUnits(root, module2, pack, pattern, options) {
|
|
|
3367
3517
|
}
|
|
3368
3518
|
return units;
|
|
3369
3519
|
}
|
|
3520
|
+
function clientCallReceivers(root, module2) {
|
|
3521
|
+
const found = [];
|
|
3522
|
+
for (const definition of functionDefinitions(root)) {
|
|
3523
|
+
for (const call of bodyCalls(definition)) {
|
|
3524
|
+
const callee = field(call, "function");
|
|
3525
|
+
if (callee === null || originOf(callee, module2) !== null) {
|
|
3526
|
+
continue;
|
|
3527
|
+
}
|
|
3528
|
+
const object = field(callee, "object");
|
|
3529
|
+
if (object !== null && field(callee, "attribute") !== null) {
|
|
3530
|
+
found.push(object);
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
return found;
|
|
3535
|
+
}
|
|
3370
3536
|
function functionDefinitions(node, found = []) {
|
|
3371
3537
|
for (const child of children(node)) {
|
|
3372
3538
|
if (isFunction(child)) {
|
|
@@ -3575,14 +3741,22 @@ var import_behavioral_ir3 = require("@suss/behavioral-ir");
|
|
|
3575
3741
|
var import_extractor4 = require("@suss/extractor");
|
|
3576
3742
|
var PYTHON_ENV_RECOGNITION = "python-env";
|
|
3577
3743
|
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
3578
|
-
function envReadEffects(root, module2) {
|
|
3579
|
-
const
|
|
3744
|
+
function envReadEffects(root, module2, facts) {
|
|
3745
|
+
const slots = [];
|
|
3746
|
+
const readsHelpers = facts !== void 0 && sitesByDb.has(facts);
|
|
3580
3747
|
const startScope = module2.scopeFor.get(root.id) ?? module2.moduleScope;
|
|
3581
3748
|
(0, import_extractor4.walkDescendants)(root, startScope, {
|
|
3582
3749
|
at: (node, scope) => {
|
|
3583
|
-
const
|
|
3584
|
-
if (
|
|
3585
|
-
|
|
3750
|
+
const syntax = envReadSyntaxAt(node, scope);
|
|
3751
|
+
if (syntax !== null) {
|
|
3752
|
+
const name = stringLiteralValue(syntax.name);
|
|
3753
|
+
if (name !== null) {
|
|
3754
|
+
slots.push({ read: { name, defaulted: syntax.defaulted } });
|
|
3755
|
+
}
|
|
3756
|
+
return;
|
|
3757
|
+
}
|
|
3758
|
+
if (readsHelpers && node.type === "call") {
|
|
3759
|
+
slots.push({ call: node });
|
|
3586
3760
|
}
|
|
3587
3761
|
},
|
|
3588
3762
|
into: (node, scope) => {
|
|
@@ -3592,7 +3766,16 @@ function envReadEffects(root, module2) {
|
|
|
3592
3766
|
return module2.scopeFor.get(node.id) ?? scope;
|
|
3593
3767
|
}
|
|
3594
3768
|
});
|
|
3595
|
-
|
|
3769
|
+
const throughHelpers = helperReadsByCall(
|
|
3770
|
+
slots.flatMap((slot) => "call" in slot ? [slot.call] : []),
|
|
3771
|
+
facts
|
|
3772
|
+
);
|
|
3773
|
+
return slots.flatMap((slot) => {
|
|
3774
|
+
if ("read" in slot) {
|
|
3775
|
+
return [configReadEffect(slot.read)];
|
|
3776
|
+
}
|
|
3777
|
+
return throughHelpers.get(slot.call.id) ?? [];
|
|
3778
|
+
});
|
|
3596
3779
|
}
|
|
3597
3780
|
function configReadEffect(read) {
|
|
3598
3781
|
return {
|
|
@@ -3610,7 +3793,7 @@ function configReadEffect(read) {
|
|
|
3610
3793
|
}
|
|
3611
3794
|
};
|
|
3612
3795
|
}
|
|
3613
|
-
function
|
|
3796
|
+
function envReadSyntaxAt(node, scope) {
|
|
3614
3797
|
if (node.type === "subscript") {
|
|
3615
3798
|
return subscriptRead(node, scope);
|
|
3616
3799
|
}
|
|
@@ -3625,11 +3808,10 @@ function subscriptRead(node, scope) {
|
|
|
3625
3808
|
if (value === null || index === null || !isEnviron(value, scope)) {
|
|
3626
3809
|
return null;
|
|
3627
3810
|
}
|
|
3628
|
-
|
|
3629
|
-
if (name === null || isAssignedTo(node)) {
|
|
3811
|
+
if (isAssignedTo(node)) {
|
|
3630
3812
|
return null;
|
|
3631
3813
|
}
|
|
3632
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
3814
|
+
return { name: index, defaulted: isDefaultedAt(node) };
|
|
3633
3815
|
}
|
|
3634
3816
|
function isAssignedTo(node) {
|
|
3635
3817
|
const parent = node.parent;
|
|
@@ -3643,22 +3825,20 @@ function isAssignedTo(node) {
|
|
|
3643
3825
|
}
|
|
3644
3826
|
function callRead(node, scope) {
|
|
3645
3827
|
const callee = field(node, "function");
|
|
3646
|
-
|
|
3647
|
-
if (callee === null || args === null || !isEnvGetter(callee, scope)) {
|
|
3828
|
+
if (callee === null || !isEnvGetter(callee, scope)) {
|
|
3648
3829
|
return null;
|
|
3649
3830
|
}
|
|
3650
|
-
const
|
|
3651
|
-
|
|
3831
|
+
const written = callArguments(node);
|
|
3832
|
+
const name = written.find(
|
|
3833
|
+
(argument) => argument.kind === "positional" && argument.position === 0
|
|
3652
3834
|
);
|
|
3653
|
-
|
|
3654
|
-
if (name === null) {
|
|
3835
|
+
if (name === void 0) {
|
|
3655
3836
|
return null;
|
|
3656
3837
|
}
|
|
3657
|
-
const
|
|
3658
|
-
(
|
|
3838
|
+
const hasDefault = written.some(
|
|
3839
|
+
(argument) => argument.kind === "positional" && argument.position === 1 || argument.kind === "keyword" && argument.name === "default"
|
|
3659
3840
|
);
|
|
3660
|
-
|
|
3661
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
3841
|
+
return { name: name.node, defaulted: hasDefault || isDefaultedAt(node) };
|
|
3662
3842
|
}
|
|
3663
3843
|
function isEnvGetter(callee, scope) {
|
|
3664
3844
|
if (callee.type === "attribute") {
|
|
@@ -3715,9 +3895,197 @@ function isDefaultedAt(node) {
|
|
|
3715
3895
|
}
|
|
3716
3896
|
return false;
|
|
3717
3897
|
}
|
|
3898
|
+
function envFactsIn(filePath, root, module2) {
|
|
3899
|
+
const found = { sites: [], objects: [] };
|
|
3900
|
+
(0, import_extractor4.walkDescendants)(root, module2.moduleScope, {
|
|
3901
|
+
at: (node, scope) => {
|
|
3902
|
+
if (isEnviron(node, scope) && handsOnward(node)) {
|
|
3903
|
+
found.objects.push(keyOf(filePath, node));
|
|
3904
|
+
}
|
|
3905
|
+
const syntax = envReadSyntaxAt(node, scope);
|
|
3906
|
+
if (syntax === null || stringLiteralValue(syntax.name) !== null) {
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3909
|
+
found.sites.push({
|
|
3910
|
+
site: nodeId(filePath, node),
|
|
3911
|
+
nameKey: keyOf(filePath, syntax.name),
|
|
3912
|
+
defaulted: syntax.defaulted,
|
|
3913
|
+
functionKey: functionReadKey(filePath, node, scope)
|
|
3914
|
+
});
|
|
3915
|
+
},
|
|
3916
|
+
into: (node, scope) => module2.scopeFor.get(node.id) ?? scope
|
|
3917
|
+
});
|
|
3918
|
+
return found;
|
|
3919
|
+
}
|
|
3920
|
+
var keyOf = (filePath, node) => readKey(filePath, node, enclosingFunction(node));
|
|
3921
|
+
function functionReadKey(filePath, node, scope) {
|
|
3922
|
+
if (node.type !== "call") {
|
|
3923
|
+
return null;
|
|
3924
|
+
}
|
|
3925
|
+
const callee = field(node, "function");
|
|
3926
|
+
if (callee === null || isEnviron(field(callee, "object") ?? callee, scope)) {
|
|
3927
|
+
return null;
|
|
3928
|
+
}
|
|
3929
|
+
return keyOf(filePath, callee);
|
|
3930
|
+
}
|
|
3931
|
+
function handsOnward(node) {
|
|
3932
|
+
const parent = node.parent;
|
|
3933
|
+
if (parent === null) {
|
|
3934
|
+
return true;
|
|
3935
|
+
}
|
|
3936
|
+
if (parent.type === "subscript" && field(parent, "value")?.id === node.id) {
|
|
3937
|
+
return !writesTheKey(field(parent, "subscript"));
|
|
3938
|
+
}
|
|
3939
|
+
if (parent.type === "attribute" && field(parent, "object")?.id === node.id) {
|
|
3940
|
+
const call = parent.parent;
|
|
3941
|
+
return field(parent, "attribute")?.text === "get" && call !== null && call.type === "call" && !writesTheKey(firstArgumentOf(call));
|
|
3942
|
+
}
|
|
3943
|
+
return true;
|
|
3944
|
+
}
|
|
3945
|
+
var writesTheKey = (key) => key !== null && stringLiteralValue(key) !== null;
|
|
3946
|
+
function firstArgumentOf(call) {
|
|
3947
|
+
const first = callArguments(call).find(
|
|
3948
|
+
(argument) => argument.kind === "positional" && argument.position === 0
|
|
3949
|
+
);
|
|
3950
|
+
return first?.node ?? null;
|
|
3951
|
+
}
|
|
3952
|
+
var sitesByDb = /* @__PURE__ */ new WeakMap();
|
|
3953
|
+
function bindEnvFacts(db, found) {
|
|
3954
|
+
const sites = found.flatMap((one) => one.sites);
|
|
3955
|
+
const objects = found.flatMap((one) => one.objects);
|
|
3956
|
+
if (sites.length === 0 && objects.length === 0) {
|
|
3957
|
+
return;
|
|
3958
|
+
}
|
|
3959
|
+
let index = sitesByDb.get(db);
|
|
3960
|
+
if (index === void 0) {
|
|
3961
|
+
index = { byId: /* @__PURE__ */ new Map(), objects: /* @__PURE__ */ new Set(), sitesByParameter: null };
|
|
3962
|
+
sitesByDb.set(db, index);
|
|
3963
|
+
}
|
|
3964
|
+
for (const object of objects) {
|
|
3965
|
+
db.add("environmentObject", [object]);
|
|
3966
|
+
index.objects.add(object);
|
|
3967
|
+
}
|
|
3968
|
+
for (const site of sites) {
|
|
3969
|
+
index.byId.set(site.site, site);
|
|
3970
|
+
if (site.functionKey === null) {
|
|
3971
|
+
continue;
|
|
3972
|
+
}
|
|
3973
|
+
db.add("environmentObject", [site.functionKey]);
|
|
3974
|
+
db.add("readsKeyed", [site.site, site.functionKey, site.nameKey]);
|
|
3975
|
+
index.objects.add(site.functionKey);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
function sitesEachParameterNames(db, index) {
|
|
3979
|
+
if (index.sitesByParameter !== null) {
|
|
3980
|
+
return index.sitesByParameter;
|
|
3981
|
+
}
|
|
3982
|
+
resolveEnvObjects(db, [...index.objects]);
|
|
3983
|
+
const found = /* @__PURE__ */ new Map();
|
|
3984
|
+
for (const row of db.facts("wantedParamNamesEnv")) {
|
|
3985
|
+
const parameter = String(row[0]);
|
|
3986
|
+
const named = found.get(parameter) ?? [];
|
|
3987
|
+
named.push(String(row[1]));
|
|
3988
|
+
found.set(parameter, named);
|
|
3989
|
+
}
|
|
3990
|
+
index.sitesByParameter = found;
|
|
3991
|
+
return found;
|
|
3992
|
+
}
|
|
3993
|
+
function helperReadsByCall(calls, db) {
|
|
3994
|
+
const found = /* @__PURE__ */ new Map();
|
|
3995
|
+
const index = db === void 0 ? void 0 : sitesByDb.get(db);
|
|
3996
|
+
if (db === void 0 || index === void 0 || calls.length === 0) {
|
|
3997
|
+
return found;
|
|
3998
|
+
}
|
|
3999
|
+
const named = sitesEachParameterNames(db, index);
|
|
4000
|
+
if (named.size === 0) {
|
|
4001
|
+
return found;
|
|
4002
|
+
}
|
|
4003
|
+
const calleeKeys = /* @__PURE__ */ new Map();
|
|
4004
|
+
for (const call of calls) {
|
|
4005
|
+
const callee = field(call, "function");
|
|
4006
|
+
const key = callee === null ? null : resolutionKeyOf(callee, db);
|
|
4007
|
+
if (key !== null) {
|
|
4008
|
+
calleeKeys.set(call.id, key);
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
if (calleeKeys.size === 0) {
|
|
4012
|
+
return found;
|
|
4013
|
+
}
|
|
4014
|
+
resolveCalls(db, [...new Set(calleeKeys.values())]);
|
|
4015
|
+
for (const call of calls) {
|
|
4016
|
+
const key = calleeKeys.get(call.id);
|
|
4017
|
+
if (key === void 0) {
|
|
4018
|
+
continue;
|
|
4019
|
+
}
|
|
4020
|
+
const argumentAt2 = argumentsByParameter(db, call, key);
|
|
4021
|
+
if (argumentAt2.size === 0) {
|
|
4022
|
+
continue;
|
|
4023
|
+
}
|
|
4024
|
+
const reads = readsThroughHelper(db, call, argumentAt2, index.byId, named);
|
|
4025
|
+
if (reads.length > 0) {
|
|
4026
|
+
found.set(call.id, reads);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
return found;
|
|
4030
|
+
}
|
|
4031
|
+
function argumentsByParameter(db, call, calleeKey) {
|
|
4032
|
+
const written = callArguments(call);
|
|
4033
|
+
if (written.length === 0) {
|
|
4034
|
+
return /* @__PURE__ */ new Map();
|
|
4035
|
+
}
|
|
4036
|
+
const positional = /* @__PURE__ */ new Map();
|
|
4037
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4038
|
+
for (const argument of written) {
|
|
4039
|
+
if (argument.kind === "keyword") {
|
|
4040
|
+
byName.set(argument.name, argument.node);
|
|
4041
|
+
continue;
|
|
4042
|
+
}
|
|
4043
|
+
positional.set(String(argument.position), argument.node);
|
|
4044
|
+
}
|
|
4045
|
+
const argumentAt2 = /* @__PURE__ */ new Map();
|
|
4046
|
+
for (const callee of resolvedFunctions(db, calleeKey)) {
|
|
4047
|
+
for (const row of db.lookup("paramOf", 0, callee)) {
|
|
4048
|
+
const at = positional.get(String(row[1]));
|
|
4049
|
+
if (at !== void 0) {
|
|
4050
|
+
argumentAt2.set(String(row[2]), at);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
for (const row of db.lookup("paramNamed", 0, callee)) {
|
|
4054
|
+
const at = byName.get(String(row[1]));
|
|
4055
|
+
if (at !== void 0) {
|
|
4056
|
+
argumentAt2.set(String(row[2]), at);
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
return argumentAt2;
|
|
4061
|
+
}
|
|
4062
|
+
function readsThroughHelper(db, call, argumentAt2, siteById, named) {
|
|
4063
|
+
const reads = /* @__PURE__ */ new Map();
|
|
4064
|
+
for (const [parameter, argument] of argumentAt2) {
|
|
4065
|
+
const sites = named.get(parameter);
|
|
4066
|
+
if (sites === void 0) {
|
|
4067
|
+
continue;
|
|
4068
|
+
}
|
|
4069
|
+
const name = stringLiteralValue(argument) ?? stringValueOf(argument, db);
|
|
4070
|
+
if (name === null) {
|
|
4071
|
+
continue;
|
|
4072
|
+
}
|
|
4073
|
+
for (const id of sites) {
|
|
4074
|
+
const defaulted = siteById.get(id)?.defaulted ?? false;
|
|
4075
|
+
reads.set(name, (reads.get(name) ?? true) && defaulted);
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
if (reads.size === 0) {
|
|
4079
|
+
return [];
|
|
4080
|
+
}
|
|
4081
|
+
const defaultedAtCall = isDefaultedAt(call);
|
|
4082
|
+
return [...reads].map(
|
|
4083
|
+
([name, defaulted]) => configReadEffect({ name, defaulted: defaulted || defaultedAtCall })
|
|
4084
|
+
);
|
|
4085
|
+
}
|
|
3718
4086
|
|
|
3719
4087
|
// src/paths/raisedResponses.ts
|
|
3720
|
-
var
|
|
4088
|
+
var import_values9 = require("@suss/values");
|
|
3721
4089
|
var NESTED_DEFINITION_TYPES2 = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
3722
4090
|
function raisedResponses(body, options) {
|
|
3723
4091
|
if (body === null || options.calls.length === 0) {
|
|
@@ -3811,7 +4179,7 @@ function statusOf(declared, call, facts) {
|
|
|
3811
4179
|
if (written === null) {
|
|
3812
4180
|
return declared.defaultStatusCode === void 0 ? null : { type: "literal", value: declared.defaultStatusCode };
|
|
3813
4181
|
}
|
|
3814
|
-
const value = (0,
|
|
4182
|
+
const value = (0, import_values9.constantOf)(evaluatedValue(written, facts));
|
|
3815
4183
|
if (typeof value === "number") {
|
|
3816
4184
|
return { type: "literal", value };
|
|
3817
4185
|
}
|
|
@@ -3924,71 +4292,301 @@ function returnedBodyShape(statement) {
|
|
|
3924
4292
|
// src/rawSql.ts
|
|
3925
4293
|
var import_ir_core = require("@suss/ir-core");
|
|
3926
4294
|
var import_sql = require("@suss/sql");
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
4295
|
+
var import_values11 = require("@suss/values");
|
|
4296
|
+
|
|
4297
|
+
// src/receiverTypes.ts
|
|
4298
|
+
var UNDECLARED_MODULE = "builtins";
|
|
4299
|
+
function annotationTarget(annotation) {
|
|
4300
|
+
if (annotation.type === "type" && annotation.namedChildren[0]) {
|
|
4301
|
+
return annotationTarget(annotation.namedChildren[0]);
|
|
3930
4302
|
}
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
4303
|
+
if (annotation.type === "binary_operator") {
|
|
4304
|
+
const named = [field(annotation, "left"), field(annotation, "right")].find(
|
|
4305
|
+
(side) => side !== null && side.type !== "none"
|
|
4306
|
+
);
|
|
4307
|
+
return named === void 0 || named === null ? null : annotationTarget(named);
|
|
4308
|
+
}
|
|
4309
|
+
if (annotation.type === "generic_type") {
|
|
4310
|
+
const outer = annotation.namedChildren[0];
|
|
4311
|
+
const first = genericTypeArgs(annotation)[0];
|
|
4312
|
+
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
4313
|
+
return annotationTarget(first);
|
|
3936
4314
|
}
|
|
3937
|
-
|
|
3938
|
-
|
|
4315
|
+
return outer ?? null;
|
|
4316
|
+
}
|
|
4317
|
+
return annotation;
|
|
4318
|
+
}
|
|
4319
|
+
function typeNameOf(annotation) {
|
|
4320
|
+
const target = annotationTarget(annotation);
|
|
4321
|
+
if (target === null) {
|
|
4322
|
+
return null;
|
|
4323
|
+
}
|
|
4324
|
+
return target.type === "identifier" ? target.text : stringLiteralValue(target);
|
|
4325
|
+
}
|
|
4326
|
+
function firstInBody(node, read) {
|
|
4327
|
+
const here = read(node);
|
|
4328
|
+
if (here !== null) {
|
|
4329
|
+
return here;
|
|
4330
|
+
}
|
|
4331
|
+
for (const child of children(node)) {
|
|
4332
|
+
if (child.type === "function_definition" || child.type === "lambda") {
|
|
3939
4333
|
continue;
|
|
3940
4334
|
}
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
effects.push({
|
|
3945
|
-
type: "interaction",
|
|
3946
|
-
binding: (0, import_ir_core.storageBinding)({
|
|
3947
|
-
recognition: `python-${pattern.module}`,
|
|
3948
|
-
storageSystem: pattern.storageSystem,
|
|
3949
|
-
scope: "default",
|
|
3950
|
-
container: access.table
|
|
3951
|
-
}),
|
|
3952
|
-
callee: field(call, "function")?.text ?? "",
|
|
3953
|
-
interaction: {
|
|
3954
|
-
class: "storage-access",
|
|
3955
|
-
kind: access.kind,
|
|
3956
|
-
fields: access.fields,
|
|
3957
|
-
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
3958
|
-
operation: field(call, "function")?.text ?? ""
|
|
3959
|
-
}
|
|
3960
|
-
});
|
|
4335
|
+
const found = firstInBody(child, read);
|
|
4336
|
+
if (found !== null) {
|
|
4337
|
+
return found;
|
|
3961
4338
|
}
|
|
3962
4339
|
}
|
|
3963
|
-
return
|
|
4340
|
+
return null;
|
|
3964
4341
|
}
|
|
3965
|
-
function
|
|
4342
|
+
function statedBy(statement, name) {
|
|
4343
|
+
if (statement.type === "assignment") {
|
|
4344
|
+
const annotation = field(statement, "type");
|
|
4345
|
+
return field(statement, "left")?.text === name ? annotation : null;
|
|
4346
|
+
}
|
|
4347
|
+
if (statement.type === "as_pattern") {
|
|
4348
|
+
const alias = field(statement, "alias");
|
|
4349
|
+
const value = statement.namedChildren[0] ?? null;
|
|
4350
|
+
const callee = value?.type === "call" ? field(value, "function") : null;
|
|
4351
|
+
return alias?.text === name ? callee : null;
|
|
4352
|
+
}
|
|
4353
|
+
return null;
|
|
4354
|
+
}
|
|
4355
|
+
function statedTypeNode(name, from) {
|
|
4356
|
+
const fn = enclosingFunction(from);
|
|
4357
|
+
if (fn === null) {
|
|
4358
|
+
return null;
|
|
4359
|
+
}
|
|
4360
|
+
const params = field(fn, "parameters");
|
|
4361
|
+
for (const param of params === null ? [] : children(params)) {
|
|
4362
|
+
const info = parameterNameAndType(param);
|
|
4363
|
+
if (info?.name === name && info.typeNode !== null) {
|
|
4364
|
+
return info.typeNode;
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
const body = field(fn, "body");
|
|
4368
|
+
return body === null ? null : firstInBody(body, (statement) => statedBy(statement, name));
|
|
4369
|
+
}
|
|
4370
|
+
function statedTypeName(name, from) {
|
|
4371
|
+
const stated = statedTypeNode(name, from);
|
|
4372
|
+
return stated === null ? null : typeNameOf(stated);
|
|
4373
|
+
}
|
|
4374
|
+
function statedOrigins(stated, options) {
|
|
4375
|
+
const name = typeNameOf(stated);
|
|
4376
|
+
const byRule = name === null ? [] : originsOf(options.facts, `${options.filePath}#${name}`);
|
|
4377
|
+
const target = annotationTarget(stated);
|
|
4378
|
+
const imported = target === null ? null : originOf(target, moduleOf(target));
|
|
4379
|
+
if (imported === null || imported.module === UNDECLARED_MODULE) {
|
|
4380
|
+
return byRule;
|
|
4381
|
+
}
|
|
4382
|
+
return [...byRule, imported];
|
|
4383
|
+
}
|
|
4384
|
+
function receiverTypeOrigins(receiver, options) {
|
|
4385
|
+
const stated = statedTypeNode(receiver.text, receiver);
|
|
4386
|
+
if (stated !== null) {
|
|
4387
|
+
return statedOrigins(stated, options);
|
|
4388
|
+
}
|
|
4389
|
+
const built = constructionBehind(receiver, options.facts);
|
|
4390
|
+
if (built.type === "oneCall") {
|
|
4391
|
+
return [built.construction.origin];
|
|
4392
|
+
}
|
|
4393
|
+
const key = nameKeyIn(
|
|
4394
|
+
options.filePath,
|
|
4395
|
+
enclosingFunction(receiver),
|
|
4396
|
+
receiver.text
|
|
4397
|
+
);
|
|
4398
|
+
return subjectConstructions(options.facts, [key]).get(key)?.origins ?? [];
|
|
4399
|
+
}
|
|
4400
|
+
|
|
4401
|
+
// src/rawSql.ts
|
|
4402
|
+
function rawSqlEffects(calls, options) {
|
|
4403
|
+
return matchesIn(calls, options).flatMap(
|
|
4404
|
+
(match) => match.accesses.flatMap((access) => effectFor(match, access))
|
|
4405
|
+
);
|
|
4406
|
+
}
|
|
4407
|
+
function rawSqlCallIds(calls, options) {
|
|
4408
|
+
return new Set(matchesIn(calls, options).map((match) => match.call.id));
|
|
4409
|
+
}
|
|
4410
|
+
function matchesIn(calls, options) {
|
|
4411
|
+
if (options.patterns.length === 0 && (options.clients ?? []).length === 0) {
|
|
4412
|
+
return [];
|
|
4413
|
+
}
|
|
4414
|
+
const matched = [];
|
|
4415
|
+
for (const call of calls) {
|
|
4416
|
+
const one = importedFunctionMatch(call, options) ?? clientMatch(call, options);
|
|
4417
|
+
if (one !== null) {
|
|
4418
|
+
matched.push(one);
|
|
4419
|
+
}
|
|
4420
|
+
}
|
|
4421
|
+
return matched;
|
|
4422
|
+
}
|
|
4423
|
+
function effectFor(match, access) {
|
|
4424
|
+
const operation = field(match.call, "function")?.text ?? "";
|
|
4425
|
+
return [
|
|
4426
|
+
{
|
|
4427
|
+
type: "interaction",
|
|
4428
|
+
binding: (0, import_ir_core.storageBinding)({
|
|
4429
|
+
recognition: match.recognition,
|
|
4430
|
+
storageSystem: match.storageSystem,
|
|
4431
|
+
scope: access.qualifier[access.qualifier.length - 1] ?? NO_GROUP,
|
|
4432
|
+
container: access.table
|
|
4433
|
+
}),
|
|
4434
|
+
callee: operation,
|
|
4435
|
+
interaction: {
|
|
4436
|
+
class: "storage-access",
|
|
4437
|
+
kind: access.kind,
|
|
4438
|
+
fields: access.fields,
|
|
4439
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
4440
|
+
operation
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
];
|
|
4444
|
+
}
|
|
4445
|
+
var NO_GROUP = "default";
|
|
4446
|
+
function importedFunctionMatch(call, options) {
|
|
3966
4447
|
const callee = field(call, "function");
|
|
3967
4448
|
if (callee === null || callee.type !== "identifier") {
|
|
3968
|
-
return
|
|
4449
|
+
return null;
|
|
3969
4450
|
}
|
|
3970
4451
|
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
3971
4452
|
if (from === void 0) {
|
|
3972
|
-
return
|
|
4453
|
+
return null;
|
|
3973
4454
|
}
|
|
3974
4455
|
const module2 = String(from[1]);
|
|
3975
|
-
|
|
3976
|
-
(
|
|
4456
|
+
const pattern = options.patterns.find(
|
|
4457
|
+
(candidate) => candidate.module === module2 && candidate.functions.includes(callee.text)
|
|
3977
4458
|
);
|
|
3978
|
-
|
|
3979
|
-
|
|
4459
|
+
if (pattern === void 0) {
|
|
4460
|
+
return null;
|
|
4461
|
+
}
|
|
3980
4462
|
const args = field(call, "arguments");
|
|
3981
4463
|
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
3982
|
-
|
|
4464
|
+
const statement = first === null ? null : statementAt(first, void 0, options.facts);
|
|
4465
|
+
if (statement === null) {
|
|
3983
4466
|
return null;
|
|
3984
4467
|
}
|
|
3985
|
-
|
|
4468
|
+
return {
|
|
4469
|
+
call,
|
|
4470
|
+
recognition: `python-${pattern.module}`,
|
|
4471
|
+
storageSystem: pattern.storageSystem,
|
|
4472
|
+
accesses: (0, import_sql.readSqlAccess)(statement, { dialect: pattern.storageSystem })
|
|
4473
|
+
};
|
|
4474
|
+
}
|
|
4475
|
+
function clientMatch(call, options) {
|
|
4476
|
+
const callee = field(call, "function");
|
|
4477
|
+
if (callee === null || callee.type !== "attribute") {
|
|
4478
|
+
return null;
|
|
4479
|
+
}
|
|
4480
|
+
const method = field(callee, "attribute")?.text ?? "";
|
|
4481
|
+
const receiver = field(callee, "object");
|
|
4482
|
+
if (receiver === null) {
|
|
4483
|
+
return null;
|
|
4484
|
+
}
|
|
4485
|
+
for (const pattern of options.clients ?? []) {
|
|
4486
|
+
const accesses = accessesOf(call, method, pattern, options);
|
|
4487
|
+
if (accesses === null || !isClientOf(receiver, pattern, options)) {
|
|
4488
|
+
continue;
|
|
4489
|
+
}
|
|
4490
|
+
return {
|
|
4491
|
+
call,
|
|
4492
|
+
recognition: `python-${pattern.module}`,
|
|
4493
|
+
storageSystem: pattern.storageSystem,
|
|
4494
|
+
accesses
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
return null;
|
|
4498
|
+
}
|
|
4499
|
+
function accessesOf(call, method, pattern, options) {
|
|
4500
|
+
const dialect = pattern.dialect ?? pattern.storageSystem;
|
|
4501
|
+
for (const takes of pattern.statements ?? []) {
|
|
4502
|
+
if (takes.method !== method) {
|
|
4503
|
+
continue;
|
|
4504
|
+
}
|
|
4505
|
+
const written = argumentNode(call, takes);
|
|
4506
|
+
const statement = written === null ? null : statementAt(written, takes.path, options.facts);
|
|
4507
|
+
return statement === null ? [] : (0, import_sql.readSqlAccess)(statement, { dialect });
|
|
4508
|
+
}
|
|
4509
|
+
for (const takes of pattern.tables ?? []) {
|
|
4510
|
+
if (takes.method !== method) {
|
|
4511
|
+
continue;
|
|
4512
|
+
}
|
|
4513
|
+
const written = argumentNode(call, takes);
|
|
4514
|
+
const name = written === null ? null : stringValueOf(written, options.facts);
|
|
4515
|
+
const split = name === null ? null : (0, import_sql.splitQualifiedTable)(name);
|
|
4516
|
+
return split === null ? [] : [{ ...split, kind: takes.kind, fields: [], selector: [] }];
|
|
4517
|
+
}
|
|
4518
|
+
return null;
|
|
4519
|
+
}
|
|
4520
|
+
function isClientOf(receiver, pattern, options) {
|
|
4521
|
+
return receiverOrigins(receiver, options).some(
|
|
4522
|
+
(origin) => origin.module === pattern.module && pattern.clientTypes.includes(origin.name)
|
|
4523
|
+
);
|
|
4524
|
+
}
|
|
4525
|
+
function receiverOrigins(receiver, options) {
|
|
4526
|
+
if (receiver.type === "identifier") {
|
|
4527
|
+
return receiverTypeOrigins(receiver, options);
|
|
4528
|
+
}
|
|
4529
|
+
return receiver.type === "call" ? handedBack(receiver, options) : [];
|
|
4530
|
+
}
|
|
4531
|
+
function handedBack(call, options) {
|
|
4532
|
+
const callee = field(call, "function");
|
|
4533
|
+
if (callee === null || callee.type !== "attribute") {
|
|
4534
|
+
return [];
|
|
4535
|
+
}
|
|
4536
|
+
const method = field(callee, "attribute")?.text ?? "";
|
|
4537
|
+
const inner = field(callee, "object");
|
|
4538
|
+
if (inner === null) {
|
|
4539
|
+
return [];
|
|
4540
|
+
}
|
|
4541
|
+
const found = [];
|
|
4542
|
+
for (const pattern of options.clients ?? []) {
|
|
4543
|
+
const handoff = (pattern.handsBack ?? []).find(
|
|
4544
|
+
(one) => one.method === method
|
|
4545
|
+
);
|
|
4546
|
+
if (handoff !== void 0 && isClientOf(inner, pattern, options)) {
|
|
4547
|
+
found.push({ module: handoff.module, name: handoff.name });
|
|
4548
|
+
}
|
|
4549
|
+
}
|
|
4550
|
+
return found;
|
|
4551
|
+
}
|
|
4552
|
+
function argumentNode(call, at) {
|
|
4553
|
+
const args = field(call, "arguments");
|
|
4554
|
+
const written = args === null ? [] : children(args);
|
|
4555
|
+
const named = written.find(
|
|
4556
|
+
(one) => one.type === "keyword_argument" && at.keyword !== void 0 && field(one, "name")?.text === at.keyword
|
|
4557
|
+
);
|
|
4558
|
+
const value = named === void 0 ? null : field(named, "value");
|
|
4559
|
+
if (value !== null) {
|
|
4560
|
+
return value;
|
|
4561
|
+
}
|
|
4562
|
+
if (at.argument === void 0) {
|
|
4563
|
+
return null;
|
|
4564
|
+
}
|
|
4565
|
+
return written.filter((one) => one.type !== "keyword_argument")[at.argument] ?? null;
|
|
4566
|
+
}
|
|
4567
|
+
function statementAt(node, path6, facts) {
|
|
4568
|
+
const reached = valueAt(evaluatedValue(node, facts), path6 ?? []);
|
|
4569
|
+
const parts = reached === null ? null : literalParts(reached);
|
|
3986
4570
|
if (parts === null) {
|
|
3987
4571
|
return null;
|
|
3988
4572
|
}
|
|
3989
4573
|
const statement = (0, import_sql.sqlFromParts)(parts);
|
|
3990
4574
|
return statement.trim() === "" ? null : statement;
|
|
3991
4575
|
}
|
|
4576
|
+
function valueAt(value, path6) {
|
|
4577
|
+
let reached = value;
|
|
4578
|
+
for (const key of path6) {
|
|
4579
|
+
if (reached.kind !== "record") {
|
|
4580
|
+
return null;
|
|
4581
|
+
}
|
|
4582
|
+
const item = reached.fields.get(key);
|
|
4583
|
+
if (item === void 0) {
|
|
4584
|
+
return null;
|
|
4585
|
+
}
|
|
4586
|
+
reached = (0, import_values11.force)(item.value);
|
|
4587
|
+
}
|
|
4588
|
+
return reached;
|
|
4589
|
+
}
|
|
3992
4590
|
function literalParts(value) {
|
|
3993
4591
|
if (value.kind !== "string") {
|
|
3994
4592
|
return null;
|
|
@@ -4086,78 +4684,7 @@ function receiverName(chain) {
|
|
|
4086
4684
|
return null;
|
|
4087
4685
|
}
|
|
4088
4686
|
const object = field(callee, "object");
|
|
4089
|
-
return object !== null && object.type === "identifier" ? object
|
|
4090
|
-
}
|
|
4091
|
-
function typeNameOf(annotation) {
|
|
4092
|
-
if (annotation.type === "type" && annotation.namedChildren[0]) {
|
|
4093
|
-
return typeNameOf(annotation.namedChildren[0]);
|
|
4094
|
-
}
|
|
4095
|
-
if (annotation.type === "identifier") {
|
|
4096
|
-
return annotation.text;
|
|
4097
|
-
}
|
|
4098
|
-
const quoted = stringLiteralValue(annotation);
|
|
4099
|
-
if (quoted !== null) {
|
|
4100
|
-
return quoted;
|
|
4101
|
-
}
|
|
4102
|
-
if (annotation.type === "binary_operator") {
|
|
4103
|
-
const named = [field(annotation, "left"), field(annotation, "right")].find(
|
|
4104
|
-
(side) => side !== null && side.type !== "none"
|
|
4105
|
-
);
|
|
4106
|
-
return named === void 0 || named === null ? null : typeNameOf(named);
|
|
4107
|
-
}
|
|
4108
|
-
if (annotation.type === "generic_type") {
|
|
4109
|
-
const outer = annotation.namedChildren[0];
|
|
4110
|
-
const first = genericTypeArgs(annotation)[0];
|
|
4111
|
-
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
4112
|
-
return typeNameOf(first);
|
|
4113
|
-
}
|
|
4114
|
-
return outer?.type === "identifier" ? outer.text : null;
|
|
4115
|
-
}
|
|
4116
|
-
return null;
|
|
4117
|
-
}
|
|
4118
|
-
function statedTypeName(name, from) {
|
|
4119
|
-
const fn = enclosingFunction(from);
|
|
4120
|
-
if (fn === null) {
|
|
4121
|
-
return null;
|
|
4122
|
-
}
|
|
4123
|
-
const params = field(fn, "parameters");
|
|
4124
|
-
for (const param of params === null ? [] : children(params)) {
|
|
4125
|
-
const info = parameterNameAndType(param);
|
|
4126
|
-
if (info?.name === name && info.typeNode !== null) {
|
|
4127
|
-
return typeNameOf(info.typeNode);
|
|
4128
|
-
}
|
|
4129
|
-
}
|
|
4130
|
-
const body = field(fn, "body");
|
|
4131
|
-
return body === null ? null : firstInBody(body, (statement) => statedBy(statement, name));
|
|
4132
|
-
}
|
|
4133
|
-
function firstInBody(node, read) {
|
|
4134
|
-
const here = read(node);
|
|
4135
|
-
if (here !== null) {
|
|
4136
|
-
return here;
|
|
4137
|
-
}
|
|
4138
|
-
for (const child of children(node)) {
|
|
4139
|
-
if (child.type === "function_definition" || child.type === "lambda") {
|
|
4140
|
-
continue;
|
|
4141
|
-
}
|
|
4142
|
-
const found = firstInBody(child, read);
|
|
4143
|
-
if (found !== null) {
|
|
4144
|
-
return found;
|
|
4145
|
-
}
|
|
4146
|
-
}
|
|
4147
|
-
return null;
|
|
4148
|
-
}
|
|
4149
|
-
function statedBy(statement, name) {
|
|
4150
|
-
if (statement.type === "assignment") {
|
|
4151
|
-
const annotation = field(statement, "type");
|
|
4152
|
-
return field(statement, "left")?.text === name && annotation !== null ? typeNameOf(annotation) : null;
|
|
4153
|
-
}
|
|
4154
|
-
if (statement.type === "as_pattern") {
|
|
4155
|
-
const alias = field(statement, "alias");
|
|
4156
|
-
const value = statement.namedChildren[0] ?? null;
|
|
4157
|
-
const callee = value?.type === "call" ? field(value, "function") : null;
|
|
4158
|
-
return alias?.text === name && callee?.type === "identifier" ? callee.text : null;
|
|
4159
|
-
}
|
|
4160
|
-
return null;
|
|
4687
|
+
return object !== null && object.type === "identifier" ? object : null;
|
|
4161
4688
|
}
|
|
4162
4689
|
function builtType(call, options) {
|
|
4163
4690
|
const root = rootOf(call);
|
|
@@ -4205,7 +4732,7 @@ function typedReceiverPattern(options, chain) {
|
|
|
4205
4732
|
if (receiver === null) {
|
|
4206
4733
|
return void 0;
|
|
4207
4734
|
}
|
|
4208
|
-
for (const origin of receiverTypeOrigins(
|
|
4735
|
+
for (const origin of receiverTypeOrigins(receiver, options)) {
|
|
4209
4736
|
const pattern = queryTypePattern(options, origin.module, origin.name);
|
|
4210
4737
|
if (pattern !== void 0) {
|
|
4211
4738
|
return pattern;
|
|
@@ -4213,14 +4740,6 @@ function typedReceiverPattern(options, chain) {
|
|
|
4213
4740
|
}
|
|
4214
4741
|
return void 0;
|
|
4215
4742
|
}
|
|
4216
|
-
function receiverTypeOrigins(options, name, from) {
|
|
4217
|
-
const stated = statedTypeName(name, from);
|
|
4218
|
-
if (stated !== null) {
|
|
4219
|
-
return originsOf(options.facts, `${options.filePath}#${stated}`);
|
|
4220
|
-
}
|
|
4221
|
-
const key = nameKeyIn(options.filePath, enclosingFunction(from), name);
|
|
4222
|
-
return subjectConstructions(options.facts, [key]).get(key)?.origins ?? [];
|
|
4223
|
-
}
|
|
4224
4743
|
function queryTypePattern(options, module2, name) {
|
|
4225
4744
|
return options.patterns.find(
|
|
4226
4745
|
(pattern) => module2 === pattern.module && pattern.queryTypes.includes(name)
|
|
@@ -4342,7 +4861,7 @@ function classBehind(typeName, options) {
|
|
|
4342
4861
|
function isClassName(name) {
|
|
4343
4862
|
return /^[A-Z][A-Za-z0-9_]*$/.test(name) && name !== name.toUpperCase();
|
|
4344
4863
|
}
|
|
4345
|
-
function
|
|
4864
|
+
function effectFor2(pattern, chain, operation, options) {
|
|
4346
4865
|
const valueMethods = pattern.valueMethods ?? [];
|
|
4347
4866
|
const picked = selectorOf(chain, valueMethods);
|
|
4348
4867
|
return {
|
|
@@ -4452,7 +4971,7 @@ function storageCallIds(calls, options) {
|
|
|
4452
4971
|
}
|
|
4453
4972
|
function storageEffects(calls, options) {
|
|
4454
4973
|
return matchedChains(calls, options).flatMap(
|
|
4455
|
-
({ chain, pattern, operation }) => operation === null ? [] : [
|
|
4974
|
+
({ chain, pattern, operation }) => operation === null ? [] : [effectFor2(pattern, chain, operation, options)]
|
|
4456
4975
|
);
|
|
4457
4976
|
}
|
|
4458
4977
|
|
|
@@ -4487,8 +5006,10 @@ function decoratedStatements(root, module2, facts) {
|
|
|
4487
5006
|
}));
|
|
4488
5007
|
}
|
|
4489
5008
|
function discoverUnits(root, module2, options) {
|
|
5009
|
+
const decorated = decoratedStatements(root, module2, options.facts);
|
|
5010
|
+
askWrittenValues(clientCallReceivers(root, module2), options.facts);
|
|
4490
5011
|
return [
|
|
4491
|
-
...
|
|
5012
|
+
...decorated.flatMap(
|
|
4492
5013
|
(decoratedStatement) => decoratedUnits(decoratedStatement, module2, options)
|
|
4493
5014
|
),
|
|
4494
5015
|
...clientUnits(root, module2, options)
|
|
@@ -4584,7 +5105,7 @@ function readPathArgument(classification, options) {
|
|
|
4584
5105
|
const first = classification.args[0];
|
|
4585
5106
|
if (first !== void 0) {
|
|
4586
5107
|
const value = evaluatedValue(first.node, options.facts);
|
|
4587
|
-
const path6 = (0,
|
|
5108
|
+
const path6 = (0, import_values13.literalOf)(value) ?? (0, import_values13.pathOf)(value);
|
|
4588
5109
|
if (path6 !== void 0) {
|
|
4589
5110
|
return (0, import_extractor5.writtenReading)(path6, classification.range);
|
|
4590
5111
|
}
|
|
@@ -4831,11 +5352,11 @@ function statusFromReturnedValue(node, facts) {
|
|
|
4831
5352
|
return null;
|
|
4832
5353
|
}
|
|
4833
5354
|
const value = evaluatedValue(node, facts);
|
|
4834
|
-
const number = (0,
|
|
5355
|
+
const number = (0, import_values13.constantOf)(value);
|
|
4835
5356
|
if (typeof number === "number") {
|
|
4836
5357
|
return Number.isInteger(number) ? number : null;
|
|
4837
5358
|
}
|
|
4838
|
-
const text2 = (0,
|
|
5359
|
+
const text2 = (0, import_values13.literalOf)(value);
|
|
4839
5360
|
if (text2 === null) {
|
|
4840
5361
|
return null;
|
|
4841
5362
|
}
|
|
@@ -5178,7 +5699,12 @@ function buildRouteUnit(options) {
|
|
|
5178
5699
|
() => readReturnAnnotation(definitionNode, enclosingScope, ctx)
|
|
5179
5700
|
);
|
|
5180
5701
|
const effects = invocationEffects(definitionNode, options.facts);
|
|
5181
|
-
const extra = recognizedBodyEffects(
|
|
5702
|
+
const extra = recognizedBodyEffects(
|
|
5703
|
+
definitionNode,
|
|
5704
|
+
module2,
|
|
5705
|
+
storageLookup,
|
|
5706
|
+
options.facts
|
|
5707
|
+
);
|
|
5182
5708
|
const perTerminal = branchesPerTerminal({
|
|
5183
5709
|
definitionNode,
|
|
5184
5710
|
effects,
|
|
@@ -5263,28 +5789,35 @@ function unreadRoleReadings(parameters, range) {
|
|
|
5263
5789
|
)
|
|
5264
5790
|
];
|
|
5265
5791
|
}
|
|
5266
|
-
function recognizedBodyEffects(definitionNode, module2, storageLookup) {
|
|
5792
|
+
function recognizedBodyEffects(definitionNode, module2, storageLookup, facts) {
|
|
5267
5793
|
const storage = storageLookup === void 0 ? [] : [
|
|
5268
5794
|
...storageEffects(bodyCalls(definitionNode), {
|
|
5269
5795
|
...storageLookup,
|
|
5270
5796
|
filePath: storageLookup.factsPath
|
|
5271
5797
|
}),
|
|
5272
|
-
...rawSqlEffects(bodyCalls(definitionNode),
|
|
5273
|
-
facts: storageLookup.facts,
|
|
5274
|
-
filePath: storageLookup.factsPath,
|
|
5275
|
-
patterns: storageLookup.rawSql ?? []
|
|
5276
|
-
})
|
|
5798
|
+
...rawSqlEffects(bodyCalls(definitionNode), rawSqlOf(storageLookup))
|
|
5277
5799
|
];
|
|
5278
|
-
return [...envReadEffects(definitionNode, module2), ...storage];
|
|
5800
|
+
return [...envReadEffects(definitionNode, module2, facts), ...storage];
|
|
5801
|
+
}
|
|
5802
|
+
function rawSqlOf(storageLookup) {
|
|
5803
|
+
return {
|
|
5804
|
+
facts: storageLookup.facts,
|
|
5805
|
+
filePath: storageLookup.factsPath,
|
|
5806
|
+
patterns: storageLookup.rawSql ?? [],
|
|
5807
|
+
clients: storageLookup.sqlClients ?? []
|
|
5808
|
+
};
|
|
5279
5809
|
}
|
|
5280
|
-
function recognizedCallIds(
|
|
5810
|
+
function recognizedCallIds(calls, storageLookup) {
|
|
5281
5811
|
if (storageLookup === void 0) {
|
|
5282
5812
|
return /* @__PURE__ */ new Set();
|
|
5283
5813
|
}
|
|
5284
|
-
return
|
|
5285
|
-
...
|
|
5286
|
-
|
|
5287
|
-
|
|
5814
|
+
return /* @__PURE__ */ new Set([
|
|
5815
|
+
...storageCallIds(calls, {
|
|
5816
|
+
...storageLookup,
|
|
5817
|
+
filePath: storageLookup.factsPath
|
|
5818
|
+
}),
|
|
5819
|
+
...rawSqlCallIds(calls, rawSqlOf(storageLookup))
|
|
5820
|
+
]);
|
|
5288
5821
|
}
|
|
5289
5822
|
function bodyContentOf(bodyNode) {
|
|
5290
5823
|
for (const stmt of bodyStatements(bodyNode)) {
|
|
@@ -5717,11 +6250,14 @@ function resolveCallee2(call, site, ctx, read) {
|
|
|
5717
6250
|
const outcome = read?.outcomes.get(spelling.key) ?? (0, import_resolution5.calleeOutcomeOf)(ctx.facts, spelling.key);
|
|
5718
6251
|
return asCallee(outcome, site.owner, ctx);
|
|
5719
6252
|
}
|
|
5720
|
-
function
|
|
6253
|
+
function namedOutcomes(keys, ctx) {
|
|
6254
|
+
return (0, import_resolution5.calleeOutcomes)(ctx.facts, [...new Set(keys)]);
|
|
6255
|
+
}
|
|
6256
|
+
function functionNamed(nameKey2, ctx, read) {
|
|
5721
6257
|
if (assignedElsewhere(ctx.facts, nameKey2)) {
|
|
5722
6258
|
return null;
|
|
5723
6259
|
}
|
|
5724
|
-
const outcome = (0, import_resolution5.calleeOutcomeOf)(ctx.facts, nameKey2);
|
|
6260
|
+
const outcome = read?.get(nameKey2) ?? (0, import_resolution5.calleeOutcomeOf)(ctx.facts, nameKey2);
|
|
5725
6261
|
return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
|
|
5726
6262
|
}
|
|
5727
6263
|
function assignedElsewhere(facts, nameKey2) {
|
|
@@ -5929,6 +6465,7 @@ function reachedFunctions(seeds, options) {
|
|
|
5929
6465
|
const stopsByKey = /* @__PURE__ */ new Map();
|
|
5930
6466
|
const argTargetsByKey = /* @__PURE__ */ new Map();
|
|
5931
6467
|
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
6468
|
+
const callsByKey = /* @__PURE__ */ new Map();
|
|
5932
6469
|
const passedPositions = /* @__PURE__ */ new Set();
|
|
5933
6470
|
for (const seed of seeds) {
|
|
5934
6471
|
seedKeys.add(seed.key);
|
|
@@ -5955,12 +6492,21 @@ function reachedFunctions(seeds, options) {
|
|
|
5955
6492
|
bodies.flatMap((body) => body.written),
|
|
5956
6493
|
ctx
|
|
5957
6494
|
);
|
|
6495
|
+
const passed = namedOutcomes(
|
|
6496
|
+
bodies.flatMap(
|
|
6497
|
+
({ source, written }) => written.flatMap(({ call }) => passedNameKeys(source.file, call))
|
|
6498
|
+
),
|
|
6499
|
+
ctx
|
|
6500
|
+
);
|
|
5958
6501
|
for (const { key, source, body, written } of bodies) {
|
|
5959
6502
|
const scan = scanBody(
|
|
5960
6503
|
source,
|
|
5961
6504
|
ctx,
|
|
5962
|
-
recognizedCallIds(
|
|
5963
|
-
|
|
6505
|
+
recognizedCallIds(
|
|
6506
|
+
written.map(({ call }) => call),
|
|
6507
|
+
options.storageFor(source.file)
|
|
6508
|
+
),
|
|
6509
|
+
{ body, written, spellings, passed }
|
|
5964
6510
|
);
|
|
5965
6511
|
if (scan.stops.length > 0) {
|
|
5966
6512
|
stopsByKey.set(key, scan.stops);
|
|
@@ -5973,21 +6519,27 @@ function reachedFunctions(seeds, options) {
|
|
|
5973
6519
|
for (const position of scan.passedPositions) {
|
|
5974
6520
|
passedPositions.add(position);
|
|
5975
6521
|
}
|
|
6522
|
+
const called = [];
|
|
5976
6523
|
for (const target of scan.followed) {
|
|
5977
|
-
const calleeKey =
|
|
6524
|
+
const calleeKey = keyOf2(target);
|
|
5978
6525
|
if (!functionByKey.has(calleeKey)) {
|
|
5979
6526
|
functionByKey.set(calleeKey, target);
|
|
5980
6527
|
}
|
|
5981
6528
|
db.add("calls", [key, calleeKey]);
|
|
6529
|
+
called.push(calleeKey);
|
|
6530
|
+
}
|
|
6531
|
+
if (called.length > 0) {
|
|
6532
|
+
callsByKey.set(key, called);
|
|
5982
6533
|
}
|
|
5983
6534
|
}
|
|
5984
6535
|
}
|
|
6536
|
+
const reached = db.facts("reachable").map(([keyAtom]) => String(keyAtom)).filter((key) => !seedKeys.has(key));
|
|
6537
|
+
settleBodyValues(reached, functionByKey, options.facts);
|
|
5985
6538
|
const summaries = [];
|
|
5986
6539
|
const summariesByKey = /* @__PURE__ */ new Map();
|
|
5987
|
-
for (const
|
|
5988
|
-
const key = String(keyAtom);
|
|
6540
|
+
for (const key of reached) {
|
|
5989
6541
|
const target = functionByKey.get(key);
|
|
5990
|
-
if (
|
|
6542
|
+
if (target === void 0) {
|
|
5991
6543
|
continue;
|
|
5992
6544
|
}
|
|
5993
6545
|
const summary = (0, import_extractor7.assembleSummary)(libraryUnit(target, options), {
|
|
@@ -6012,10 +6564,11 @@ function reachedFunctions(seeds, options) {
|
|
|
6012
6564
|
stopsByKey,
|
|
6013
6565
|
argTargetsByKey,
|
|
6014
6566
|
parameterCallsByKey,
|
|
6567
|
+
callsByKey,
|
|
6015
6568
|
passedPositions
|
|
6016
6569
|
};
|
|
6017
6570
|
}
|
|
6018
|
-
function
|
|
6571
|
+
function keyOf2(target) {
|
|
6019
6572
|
return nodeId(target.file.file, target.node);
|
|
6020
6573
|
}
|
|
6021
6574
|
var EMPTY_SCAN = {
|
|
@@ -6028,15 +6581,53 @@ var EMPTY_SCAN = {
|
|
|
6028
6581
|
};
|
|
6029
6582
|
function bodyOf(source) {
|
|
6030
6583
|
const { file, node } = source;
|
|
6031
|
-
const body = field(node, "body");
|
|
6584
|
+
const body = isModule(node) ? node : field(node, "body");
|
|
6032
6585
|
if (body === null) {
|
|
6033
6586
|
return { body, written: [] };
|
|
6034
6587
|
}
|
|
6035
6588
|
return {
|
|
6036
6589
|
body,
|
|
6037
|
-
written: callsWritten(file, body, scopeAt2(file, node),
|
|
6590
|
+
written: callsWritten(file, body, scopeAt2(file, node), keyOf2(source), {
|
|
6591
|
+
descendsInto: isModule(node) ? runsAtModuleLoad : outsideNestedDef
|
|
6592
|
+
})
|
|
6038
6593
|
};
|
|
6039
6594
|
}
|
|
6595
|
+
function outsideNestedDef(node) {
|
|
6596
|
+
return node.type !== "function_definition";
|
|
6597
|
+
}
|
|
6598
|
+
function settleBodyValues(reached, functionByKey, facts) {
|
|
6599
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
6600
|
+
for (const key of reached) {
|
|
6601
|
+
const target = functionByKey.get(key);
|
|
6602
|
+
if (target === void 0) {
|
|
6603
|
+
continue;
|
|
6604
|
+
}
|
|
6605
|
+
const listed = byFile.get(target.file.file) ?? [];
|
|
6606
|
+
for (const node of bodyValueNodes(target.node)) {
|
|
6607
|
+
listed.push(node);
|
|
6608
|
+
}
|
|
6609
|
+
byFile.set(target.file.file, listed);
|
|
6610
|
+
}
|
|
6611
|
+
for (const nodes of byFile.values()) {
|
|
6612
|
+
askWrittenValues(nodes, facts);
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
function passedNameKeyOf(file, arg) {
|
|
6616
|
+
if (arg === null || arg.type !== "identifier") {
|
|
6617
|
+
return null;
|
|
6618
|
+
}
|
|
6619
|
+
return readKey(file.file, arg, enclosingFunction(arg));
|
|
6620
|
+
}
|
|
6621
|
+
function passedNameKeys(file, call) {
|
|
6622
|
+
const args = field(call, "arguments");
|
|
6623
|
+
if (args === null) {
|
|
6624
|
+
return [];
|
|
6625
|
+
}
|
|
6626
|
+
return args.namedChildren.flatMap((arg) => {
|
|
6627
|
+
const key = passedNameKeyOf(file, arg);
|
|
6628
|
+
return key === null ? [] : [key];
|
|
6629
|
+
});
|
|
6630
|
+
}
|
|
6040
6631
|
function scanBody(source, ctx, recognized, read) {
|
|
6041
6632
|
const followed = [];
|
|
6042
6633
|
const stops = [];
|
|
@@ -6057,17 +6648,15 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6057
6648
|
return;
|
|
6058
6649
|
}
|
|
6059
6650
|
args.namedChildren.forEach((arg, position) => {
|
|
6060
|
-
|
|
6651
|
+
const nameKey2 = passedNameKeyOf(file, arg);
|
|
6652
|
+
if (nameKey2 === null) {
|
|
6061
6653
|
return;
|
|
6062
6654
|
}
|
|
6063
|
-
const resolved = functionNamed(
|
|
6064
|
-
readKey(file.file, arg, enclosingFunction(arg)),
|
|
6065
|
-
ctx
|
|
6066
|
-
);
|
|
6655
|
+
const resolved = functionNamed(nameKey2, ctx, read.passed);
|
|
6067
6656
|
if (resolved === null) {
|
|
6068
6657
|
return;
|
|
6069
6658
|
}
|
|
6070
|
-
const key =
|
|
6659
|
+
const key = keyOf2(resolved);
|
|
6071
6660
|
if (!seen.has(key)) {
|
|
6072
6661
|
seen.add(key);
|
|
6073
6662
|
followed.push(resolved);
|
|
@@ -6092,7 +6681,7 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6092
6681
|
recordPassedArgs(
|
|
6093
6682
|
call,
|
|
6094
6683
|
callee,
|
|
6095
|
-
outcome.kind === "followed" ?
|
|
6684
|
+
outcome.kind === "followed" ? keyOf2(outcome.target) : null
|
|
6096
6685
|
);
|
|
6097
6686
|
if (outcome.kind === "stopped") {
|
|
6098
6687
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
@@ -6109,7 +6698,7 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6109
6698
|
}
|
|
6110
6699
|
return;
|
|
6111
6700
|
}
|
|
6112
|
-
const key =
|
|
6701
|
+
const key = keyOf2(outcome.target);
|
|
6113
6702
|
if (!seen.has(key)) {
|
|
6114
6703
|
seen.add(key);
|
|
6115
6704
|
followed.push(outcome.target);
|
|
@@ -6127,7 +6716,7 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6127
6716
|
passedPositions
|
|
6128
6717
|
};
|
|
6129
6718
|
}
|
|
6130
|
-
function callsWritten(file, body, outer, owner) {
|
|
6719
|
+
function callsWritten(file, body, outer, owner, limit) {
|
|
6131
6720
|
const found = [];
|
|
6132
6721
|
(0, import_extractor7.walkDescendants)(body, outer, {
|
|
6133
6722
|
at: (child, scope) => {
|
|
@@ -6135,7 +6724,7 @@ function callsWritten(file, body, outer, owner) {
|
|
|
6135
6724
|
found.push({ call: child, site: { file, scope, owner } });
|
|
6136
6725
|
}
|
|
6137
6726
|
},
|
|
6138
|
-
into: (child, scope) => child
|
|
6727
|
+
into: (child, scope) => limit.descendsInto(child) ? file.module.scopeFor.get(child.id) ?? scope : import_extractor7.SKIP_CHILDREN
|
|
6139
6728
|
});
|
|
6140
6729
|
return found;
|
|
6141
6730
|
}
|
|
@@ -6168,7 +6757,8 @@ function libraryUnit(target, options) {
|
|
|
6168
6757
|
const extra = recognizedBodyEffects(
|
|
6169
6758
|
node,
|
|
6170
6759
|
file.module,
|
|
6171
|
-
options.storageFor(file)
|
|
6760
|
+
options.storageFor(file),
|
|
6761
|
+
options.facts
|
|
6172
6762
|
);
|
|
6173
6763
|
const range = rangeOf(node);
|
|
6174
6764
|
return {
|
|
@@ -7749,7 +8339,8 @@ function wrapperUnit(target, declared, options) {
|
|
|
7749
8339
|
const extra = recognizedBodyEffects(
|
|
7750
8340
|
node,
|
|
7751
8341
|
file.module,
|
|
7752
|
-
options.storageFor(file)
|
|
8342
|
+
options.storageFor(file),
|
|
8343
|
+
options.facts
|
|
7753
8344
|
);
|
|
7754
8345
|
const delegate = (at) => ({
|
|
7755
8346
|
terminal: {
|
|
@@ -7907,6 +8498,7 @@ function factsForFile(options) {
|
|
|
7907
8498
|
files: [{ file: options.file, root: options.root, module: options.module }],
|
|
7908
8499
|
definitions
|
|
7909
8500
|
});
|
|
8501
|
+
bindEnvFacts(db, [envFactsIn(options.file, options.root, options.module)]);
|
|
7910
8502
|
(0, import_resolution7.addPackWords)(db, packWordsOf(options.packs));
|
|
7911
8503
|
return db;
|
|
7912
8504
|
}
|
|
@@ -7957,6 +8549,7 @@ async function extractPythonProject(options) {
|
|
|
7957
8549
|
);
|
|
7958
8550
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
7959
8551
|
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
8552
|
+
const sqlClients = options.packs.flatMap((pack) => pack.sqlClients ?? []);
|
|
7960
8553
|
const modelQueries = options.packs.flatMap((pack) => pack.models ?? []);
|
|
7961
8554
|
const discovers = options.packs.some((pack) => pack.discovery.length > 0);
|
|
7962
8555
|
const buildsReceivers = options.packs.some(
|
|
@@ -7964,7 +8557,15 @@ async function extractPythonProject(options) {
|
|
|
7964
8557
|
(client) => (client.receiverConstructors ?? []).length > 0
|
|
7965
8558
|
)
|
|
7966
8559
|
);
|
|
7967
|
-
const
|
|
8560
|
+
const envFacts = bound.map(
|
|
8561
|
+
(boundFile) => envFactsIn(boundFile.file, boundFile.root, boundFile.module)
|
|
8562
|
+
);
|
|
8563
|
+
const readsEnvThroughNames = envFacts.some(
|
|
8564
|
+
(one) => one.sites.length > 0 || one.objects.length > 0
|
|
8565
|
+
);
|
|
8566
|
+
const needsValues = discovers || mountsRouters || buildsReceivers || readsEnvThroughNames || storagePatterns.length > 0 || modelQueries.length > 0 || // A statement written as SQL is a value, and so is the client the
|
|
8567
|
+
// call that hands it over is read off.
|
|
8568
|
+
rawSqlPatterns.length > 0 || sqlClients.length > 0;
|
|
7968
8569
|
const definitions = /* @__PURE__ */ new Map();
|
|
7969
8570
|
timer.time("discover", () => {
|
|
7970
8571
|
for (const { file, root, module: moduleBinding } of bound) {
|
|
@@ -7976,6 +8577,7 @@ async function extractPythonProject(options) {
|
|
|
7976
8577
|
}
|
|
7977
8578
|
if (needsValues) {
|
|
7978
8579
|
bindEvaluator(db, { files: bound, definitions });
|
|
8580
|
+
bindEnvFacts(db, envFacts);
|
|
7979
8581
|
}
|
|
7980
8582
|
(0, import_resolution7.addPackWords)(db, packWordsOf(options.packs));
|
|
7981
8583
|
});
|
|
@@ -7991,13 +8593,14 @@ async function extractPythonProject(options) {
|
|
|
7991
8593
|
...mountsRouters ? { facts: db } : {}
|
|
7992
8594
|
})
|
|
7993
8595
|
);
|
|
7994
|
-
const storageFor = (file) => storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
|
|
8596
|
+
const storageFor = (file) => storagePatterns.length > 0 || rawSqlPatterns.length > 0 || sqlClients.length > 0 ? {
|
|
7995
8597
|
facts: db,
|
|
7996
8598
|
factsPath: file.file,
|
|
7997
8599
|
patterns: storagePatterns,
|
|
7998
8600
|
definitionAt: (key) => definitions.get(key),
|
|
7999
8601
|
couldMatch,
|
|
8000
|
-
rawSql: rawSqlPatterns
|
|
8602
|
+
rawSql: rawSqlPatterns,
|
|
8603
|
+
sqlClients
|
|
8001
8604
|
} : void 0;
|
|
8002
8605
|
const wrapperIndex = timer.time(
|
|
8003
8606
|
"discover",
|
|
@@ -8032,6 +8635,7 @@ async function extractPythonProject(options) {
|
|
|
8032
8635
|
}
|
|
8033
8636
|
const seeds = [];
|
|
8034
8637
|
const summariesBySeed = /* @__PURE__ */ new Map();
|
|
8638
|
+
const moduleRoots = [];
|
|
8035
8639
|
for (const boundFile of bound) {
|
|
8036
8640
|
const { file, root, module: moduleBinding } = boundFile;
|
|
8037
8641
|
const displayPath = displayPathOf(file);
|
|
@@ -8064,23 +8668,17 @@ async function extractPythonProject(options) {
|
|
|
8064
8668
|
}
|
|
8065
8669
|
const loadTimeReads = timer.time(
|
|
8066
8670
|
"discover",
|
|
8067
|
-
() => envReadEffects(root, moduleBinding)
|
|
8671
|
+
() => envReadEffects(root, moduleBinding, db)
|
|
8068
8672
|
);
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
}),
|
|
8079
|
-
{ gapHandling }
|
|
8080
|
-
)
|
|
8081
|
-
);
|
|
8082
|
-
summary.confidence = { source: "inferred_static", level: "low" };
|
|
8083
|
-
summaries.push(summary);
|
|
8673
|
+
const moduleKey = nodeId(file, root);
|
|
8674
|
+
if (!summariesBySeed.has(moduleKey)) {
|
|
8675
|
+
seeds.push({ key: moduleKey, file: boundFile, node: root });
|
|
8676
|
+
moduleRoots.push({
|
|
8677
|
+
boundFile,
|
|
8678
|
+
displayPath,
|
|
8679
|
+
key: moduleKey,
|
|
8680
|
+
loadTimeReads
|
|
8681
|
+
});
|
|
8084
8682
|
}
|
|
8085
8683
|
}
|
|
8086
8684
|
const reached = timer.time(
|
|
@@ -8094,16 +8692,19 @@ async function extractPythonProject(options) {
|
|
|
8094
8692
|
definitions
|
|
8095
8693
|
})
|
|
8096
8694
|
);
|
|
8695
|
+
const placeWhatItReached = (summary, key) => {
|
|
8696
|
+
if (gapHandling !== "silent") {
|
|
8697
|
+
summary.gaps.push(
|
|
8698
|
+
...(reached.stopsByKey.get(key) ?? []).map(import_behavioral_ir6.unfollowedCallGap)
|
|
8699
|
+
);
|
|
8700
|
+
}
|
|
8701
|
+
(0, import_behavioral_ir6.placeCalls)(summary, reached.targetsByKey.get(key));
|
|
8702
|
+
(0, import_behavioral_ir6.placeArgTargets)(summary, reached.argTargetsByKey.get(key));
|
|
8703
|
+
(0, import_behavioral_ir6.placeCalleeParameters)(summary, reached.parameterCallsByKey.get(key));
|
|
8704
|
+
};
|
|
8097
8705
|
for (const [key, owners] of summariesBySeed) {
|
|
8098
8706
|
for (const summary of owners) {
|
|
8099
|
-
|
|
8100
|
-
summary.gaps.push(
|
|
8101
|
-
...(reached.stopsByKey.get(key) ?? []).map(import_behavioral_ir6.unfollowedCallGap)
|
|
8102
|
-
);
|
|
8103
|
-
}
|
|
8104
|
-
(0, import_behavioral_ir6.placeCalls)(summary, reached.targetsByKey.get(key));
|
|
8105
|
-
(0, import_behavioral_ir6.placeArgTargets)(summary, reached.argTargetsByKey.get(key));
|
|
8106
|
-
(0, import_behavioral_ir6.placeCalleeParameters)(summary, reached.parameterCallsByKey.get(key));
|
|
8707
|
+
placeWhatItReached(summary, key);
|
|
8107
8708
|
}
|
|
8108
8709
|
}
|
|
8109
8710
|
if (gapHandling !== "silent") {
|
|
@@ -8113,6 +8714,30 @@ async function extractPythonProject(options) {
|
|
|
8113
8714
|
reached.passedPositions
|
|
8114
8715
|
);
|
|
8115
8716
|
}
|
|
8717
|
+
for (const { boundFile, displayPath, key, loadTimeReads } of moduleRoots) {
|
|
8718
|
+
const calledAtLoad = reached.callsByKey.get(key) ?? [];
|
|
8719
|
+
if (loadTimeReads.length === 0 && calledAtLoad.length === 0) {
|
|
8720
|
+
continue;
|
|
8721
|
+
}
|
|
8722
|
+
const summary = timer.time(
|
|
8723
|
+
"summarize",
|
|
8724
|
+
() => (0, import_extractor10.assembleSummary)(
|
|
8725
|
+
(0, import_extractor10.moduleInitStructure)({
|
|
8726
|
+
name: import_node_path3.default.basename(displayPath),
|
|
8727
|
+
file: displayPath,
|
|
8728
|
+
range: rangeOf(boundFile.root),
|
|
8729
|
+
effects: [
|
|
8730
|
+
...loadTimeReads,
|
|
8731
|
+
...moduleLoadInvocationEffects(boundFile.root, db).map(import_extractor10.effectToIR)
|
|
8732
|
+
]
|
|
8733
|
+
}),
|
|
8734
|
+
{ gapHandling }
|
|
8735
|
+
)
|
|
8736
|
+
);
|
|
8737
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
8738
|
+
placeWhatItReached(summary, key);
|
|
8739
|
+
summaries.push(summary);
|
|
8740
|
+
}
|
|
8116
8741
|
summaries.push(...reached.summaries);
|
|
8117
8742
|
const resolvedImports = resolvedImportsOf(db, displayPathOf);
|
|
8118
8743
|
(0, import_extractor10.stampModuleImports)(summaries, (file) => resolvedImports.get(file) ?? []);
|